As Darksky, thanks to Apple, stopped its service by April 1st, I have been seriously looking for an alternative.
I knew that a PR for Visual Crossing had been posted on Github as a replacement for Darksky weather, but decided to look further in order to minimize the risk that this service would closed down as well in the future or that it would turn into a paid service.
I looked to various services and finally selected Open Meteo. See: https://open-meteo.com/
Advantages
1. Open Source.
2. No API key needed
3. Multiple Data Sources.
4. Free for non-commercial use.
5. Data limit 10.000 calls/day
6, Configurable http request to receive dedicated response.
7. No plug-ins or scripts in Domoticz. Just free configuration of virtual sensors in Node-RED.
8. Large amount of data
Temp tab:
You are free to use the (virtual) sensors of your choice. In the example (Temperature tab) above I used a combined Temperature, Humidity and Barometer sensor (THB), but also individual sensor for Temperature, Humidity and Barometer. Other combinations, such as TempHum or TempBar are also possible.
Weather tab:
See the Node-RED flow below:
The upper stream requests the LAT/LON co-ordinates from Domoticz. It is initiated during start of the flow
You might want to consider it to change that and not use the location of your Home address but e.g your personal location, that you receive from your smartphone. These location is stored in 2 global variables and used in the lower stream.
The Inject node injects a command every 10 minutes but feel free to change it, as long as you stay under 10,000 calls/day.
The "Change" node sets several parameters, to be used in the HTTP requests.
The "Function" node is slightly modified, but based on the post in viewtopic.php?p=263147&hilit=weather#p263147
at 09 Dec 2020, 16:57.
I tested this flow for approx 2 weeks and no issues were discovered. So in my opinion, it is ready to share it with the community.
The complete Node-RED flow, you will find below:
Code: Select all
[
{
"id": "4d2122f74f3e3752",
"type": "tab",
"label": "Open Meteo",
"disabled": false,
"info": "",
"env": []
},
{
"id": "5c0992b908348937",
"type": "inject",
"z": "4d2122f74f3e3752",
"name": "Request Home GPS",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "home_GPS",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 180,
"wires": [
[
"714e4cf506f868f1"
]
]
},
{
"id": "714e4cf506f868f1",
"type": "http request",
"z": "4d2122f74f3e3752",
"name": "HTTP Domoticz",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://192.168.10.50:8080/json.htm?type=settings",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 500,
"y": 180,
"wires": [
[
"9fb35f4b716ee86d",
"a61b6e9fc691a88b"
]
]
},
{
"id": "9fb35f4b716ee86d",
"type": "debug",
"z": "4d2122f74f3e3752",
"name": "debug 163",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload.Location",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 710,
"y": 140,
"wires": []
},
{
"id": "8ec742414f97ae75",
"type": "comment",
"z": "4d2122f74f3e3752",
"name": "Read Lat/Lon from Domoticz",
"info": "",
"x": 400,
"y": 120,
"wires": []
},
{
"id": "643ab3f67da319a5",
"type": "inject",
"z": "4d2122f74f3e3752",
"name": "Inject",
"props": [],
"repeat": "600",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"x": 230,
"y": 280,
"wires": [
[
"458f6d595493e6f7"
]
]
},
{
"id": "0f132cd63669ed9a",
"type": "http request",
"z": "4d2122f74f3e3752",
"name": "Request Open Meteo",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "https://api.open-meteo.com/v1/forecast?latitude={{{lat}}}&longitude={{{lon}}}¤t_weather={{{cw}}}&windspeed_unit={{{wind_set}}}&forecast_days={{{fc_days}}}&hourly={{{hourly}}}&daily={{{daily}}}&timezone={{{tz}}}",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 740,
"y": 280,
"wires": [
[
"9bad3ec41889ed8a",
"a7129cdafdb18bde"
]
]
},
{
"id": "0c1dcd4e5a7b64bd",
"type": "debug",
"z": "4d2122f74f3e3752",
"name": "debug 179",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1210,
"y": 240,
"wires": []
},
{
"id": "458f6d595493e6f7",
"type": "change",
"z": "4d2122f74f3e3752",
"name": "Set Open Meteo parameters",
"rules": [
{
"t": "set",
"p": "lat",
"pt": "msg",
"to": "lat",
"tot": "flow"
},
{
"t": "set",
"p": "lon",
"pt": "msg",
"to": "lon",
"tot": "flow"
},
{
"t": "set",
"p": "cw",
"pt": "msg",
"to": "true",
"tot": "bool"
},
{
"t": "set",
"p": "hourly",
"pt": "msg",
"to": "temperature_2m,relativehumidity_2m,windspeed_10m,dewpoint_2m,apparent_temperature,pressure_msl,cloudcover,winddirection_10m,windgusts_10m,direct_radiation,precipitation,precipitation_probability,rain,visibility",
"tot": "str"
},
{
"t": "set",
"p": "daily",
"pt": "msg",
"to": "uv_index_max,uv_index_clear_sky_max,winddirection_10m_dominant",
"tot": "str"
},
{
"t": "set",
"p": "tz",
"pt": "msg",
"to": "auto",
"tot": "str"
},
{
"t": "set",
"p": "fc_days",
"pt": "msg",
"to": "1",
"tot": "num"
},
{
"t": "set",
"p": "wind_set",
"pt": "msg",
"to": "ms",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 460,
"y": 280,
"wires": [
[
"0f132cd63669ed9a"
]
]
},
{
"id": "a61b6e9fc691a88b",
"type": "change",
"z": "4d2122f74f3e3752",
"name": "",
"rules": [
{
"t": "set",
"p": "lat",
"pt": "flow",
"to": "payload.Location.Latitude",
"tot": "msg"
},
{
"t": "set",
"p": "lon",
"pt": "flow",
"to": "payload.Location.Longitude",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 720,
"y": 180,
"wires": [
[]
]
},
{
"id": "229d06c564f6fd56",
"type": "comment",
"z": "4d2122f74f3e3752",
"name": "Open-Meteo",
"info": "See: https://open-meteo.com/",
"x": 710,
"y": 240,
"wires": []
},
{
"id": "a7129cdafdb18bde",
"type": "function",
"z": "4d2122f74f3e3752",
"name": "Prepare Domoticz",
"func": "// This function determines the comfort level for Domoticz based on the humidity.\n\nfunction comfortlevel(hum) {\n \nswitch (true) {\n case hum > 70: { hum_stat = 3 } break;\n case hum < 30: { hum_stat = 2 } break;\n case hum >= 30 && hum <= 45: { hum_stat = 0 } break;\n case hum > 45 && hum <= 70: { hum_stat = 1 } break;\n default: { hum_stat = 0 } break;\n}\nreturn hum_stat;\n}\n\n// This function determines the prediction (barometer forecast) for Domoticz based on the barometer pressure.\n\nfunction bar_forecast(pres) {\n\nswitch (true) {\n case pres > 1030: { bar_for = 1 } break;\n case pres > 1010 && pres <= 1030: { bar_for = 2 } break;\n case pres > 990 && pres <= 1010: { bar_for = 3 } break;\n case pres > 970 && pres <= 990: { bar_for = 4 } break;\n default: { bar_for = 0 } break;\n}\nreturn bar_for;\n}\n// This function determines the wind direction in abbreviated text for Domoticz based on the average bearing.\n\nfunction winddir(avgb) {\n\nswitch (true) {\n case avgb >= 0 && avgb <= 11.25: { dir = \"N\" } break;\n case avgb >= 11.26 && avgb <= 33.75: { dir = \"NNE\" } break;\n case avgb >= 33.76 && avgb <= 56.25: { dir = \"NE\" } break;\n case avgb >= 56.26 && avgb <= 78.75: { dir = \"ENE\" } break;\n case avgb >= 78.76 && avgb <= 101.25: { dir = \"E\" } break;\n case avgb >= 101.26 && avgb <= 123.75: { dir = \"ESE\" } break;\n case avgb >= 123.76 && avgb <= 146.25: { dir = \"SE\" } break;\n case avgb >= 146.26 && avgb <= 168.75: { dir = \"SSE\" } break;\n case avgb >= 168.76 && avgb <= 191.25: { dir = \"S\" } break;\n case avgb >= 191.26 && avgb <= 213.75: { dir = \"SSW\" } break;\n case avgb >= 213.76 && avgb <= 236.25: { dir = \"SW\" } break;\n case avgb >= 236.26 && avgb <= 258.75: { dir = \"WSW\" } break;\n case avgb >= 258.76 && avgb <= 281.25: { dir = \"W\" } break;\n case avgb >= 281.26 && avgb <= 303.75: { dir = \"WNW\" } break;\n case avgb >= 303.76 && avgb <= 326.25: { dir = \"NW\" } break;\n case avgb >= 326.26 && avgb <= 348.75: { dir = \"NNW\" } break;\n case avgb >= 348.76 && avgb <= 359.99: { dir = \"N\" } break;\n default: { dir = \"?\" } break;\n}\nreturn dir;\n}\n\nlet hrs = parseInt(msg.payload.current_weather.time.split(\"T\")[1].split(\":\")[0]);\n\nlet ba = msg.payload.hourly.pressure_msl[hrs]; //barometer pressure\nlet pr = bar_forecast(ba); // prediction (barometer forecast)\nlet tm = msg.payload.current_weather.temperature; // temperature\nlet hu = msg.payload.hourly.relativehumidity_2m[hrs]; // humidity\nlet lv = comfortlevel(hu); // comfort level\nlet rt = (msg.payload.hourly.precipitation[hrs] * 100).toFixed(); // rain rate\nlet fl = msg.payload.hourly.precipitation[hrs]; // rain fall\nlet sr = msg.payload.hourly.direct_radiation[hrs]; // solar radiation\nlet uv = msg.payload.daily.uv_index_max[0]; // UV radiation\nlet wd = msg.payload.current_weather.winddirection; // wind bearing\nlet sp = (msg.payload.current_weather.windspeed * 10).toFixed(1); // wind speed in 0.1 m/s\nlet gu = (msg.payload.hourly.windgusts_10m[hrs] * 10).toFixed(1); // wind gust in 0.1 m/s\nlet ch = msg.payload.hourly.apparent_temperature[hrs]; // chill temperature\nlet avgbr = msg.payload.daily.winddirection_10m_dominant[0]; // average daily dominant bearing\n//let dr = winddir(avgbr); // dominant average daily wind direction in abbreviated text; Alternative, to be used instead of wd\nlet dr = winddir(wd); // wind direction in abbreviated text; Alternative, to be used instead of avgbr\n//let txt = msg.payload.forecast; // weather forecast text, if available\nlet dp = msg.payload.hourly.dewpoint_2m[hrs]; // dew point\nlet vis = msg.payload.hourly.visibility[hrs]/1000;\nlet cc = msg.payload.hourly.cloudcover[hrs];\n\nlet msg1 = {};\nlet msg2 = {};\nlet msg3 = {};\nlet msg4 = {};\nlet msg5 = {};\nlet msg6 = {};\nlet msg7 = {};\nlet msg8 = {};\nlet msg9 = {};\nlet msg10 = {};\nlet msg11 = {};\nlet msg12 = {};\nlet msg13 = {};\nlet msg14 = {};\nlet msg15 = {};\n\nmsg1.payload = {\"command\":\"udevice\", \"idx\":20, \"svalue\":tm.toString()}; // To be used for Temperature sensor\nmsg2.payload = {\"command\":\"udevice\", \"idx\":21, \"nvalue\":hu, \"svalue\":lv.toString()}; // To be used for Humidity sensor\nmsg3.payload = {\"command\":\"udevice\", \"idx\":22, \"svalue\":ba.toString() + \";\" + pr.toString()}; // To be used for Barometer sensor\n//msg4.payload = {\"command\":\"udevice\", \"idx\":33, \"svalue\":tm.toString() + \";\" + hu.toString() + \";\" + lv.toString()}; // To be used for Temperature + Humidity sensor\nmsg5.payload = {\"command\":\"udevice\", \"idx\":11, \"svalue\":tm.toString() + \";\" + hu.toString() + \";\" + lv.toString() + \";\" + ba.toString() + \";\" + pr.toString()}; // To be used for Temperature + Humidity + Barometer sensor\n//msg6.payload = {\"command\":\"udevice\", \"idx\":35, \"svalue\":tm.toString() + \";\" + ba.toString() + \";\" + pr.toString()}; // To be used for Temperature + Barometer sensor\nmsg7.payload = {\"command\":\"udevice\", \"idx\":14, \"svalue\":rt.toString() + \";\" + fl.toString()}; // To be used for Rain sensor\nmsg8.payload = {\"command\":\"udevice\", \"idx\":12, \"svalue\":wd.toString() + \";\" + dr.toString() + \";\" + sp.toString() + \";\" + gu.toString() + \";\" + tm.toString() + \";\" + ch.toString()}; // To be used for Wind sensor\n//msg9.payload = {\"command\":\"udevice\", \"idx\":38, \"svalue\":wd.toString() + \";\" + dr.toString() + \";\" + sp.toString() + \";\" + gu.toString() + \";\" + tm.toString() + \";\" + ch.toString()}; // To be used for Wind + Temperature + Chill sensor\nmsg10.payload = {\"command\":\"udevice\", \"idx\":13, \"svalue\":uv.toString() + \";\" + \"0\"}; // To be used for UV sensor\nmsg11.payload = {\"command\":\"udevice\", \"idx\":17, \"svalue\":sr.toString()}; // To be used for Solar radiation sensor\n//msg12.payload = {\"command\":\"udevice\", \"idx\":41, \"svalue\":\"txt\"}; // To be used for Text sensor, if short weather text is available\nmsg13.payload = {\"command\":\"udevice\", \"idx\":18, \"svalue\":dp.toString()}; // To be used for Dewpoint temperature sensor\nmsg14.payload = {\"command\":\"udevice\", \"idx\": 15, \"svalue\": vis.toString()}; // To be used for Visibility sensor\nmsg15.payload = {\"command\":\"udevice\", \"idx\": 16, \"svalue\": cc.toString()}; // To be used for Cloud Cover sensor\n\n//return [[msg1,msg2,msg3,msg4,msg5,msg6,msg7,msg8,msg9,msg10,msg11,msg12,msg13,msg14],msg15];\nreturn [[msg1,msg2,msg3,msg5,msg7,msg8,msg10,msg11,msg13,msg14,msg15]]\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 990,
"y": 280,
"wires": [
[
"0c1dcd4e5a7b64bd",
"cd4943f82070c026"
]
]
},
{
"id": "9bad3ec41889ed8a",
"type": "debug",
"z": "4d2122f74f3e3752",
"name": "debug 180",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 970,
"y": 240,
"wires": []
},
{
"id": "cd4943f82070c026",
"type": "mqtt out",
"z": "4d2122f74f3e3752",
"name": "To Domoticz",
"topic": "domoticz/in",
"qos": "0",
"retain": "false",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "108bbff65a6cdbf3",
"x": 1210,
"y": 280,
"wires": []
},
{
"id": "108bbff65a6cdbf3",
"type": "mqtt-broker",
"name": "",
"broker": "192.168.10.51",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]
Do not forget to insert your own IDX numbers and the configuration of your MQTT server.
In case of questions, do not hesitate to ask.
Regards