I've writen a DzVents script to get values from DarkSky, with pressure and cloud cover => all put in dz device
It works fine except for the rain because rain sensors are a strange thing in Domoticz - I work on it when I time and when it's raining
I also have rain expectation for the next 1h, 12 and 24h (for a future watering system)
Code: Select all
--[[ Script to get some values from darksky API and calculate rain expectation for next hours
Meteo_V0R9d
from https://www.domoticz.com/forum/viewtopic.php?t=22090 (Cloud cover from darksky by aleph0)
to read: https://darksky.net/dev/docs
extract from several emails with DarkSky support to understand their data:
<<precipProbability is the probability of any precipitation at all in the corresponding hour,
while *precipIntensity* is the expected amount of precipitation.
Technically *precipIntensity* is a rate, but it is per hour, so the number is also the expected amount for the hour for which it's reported>>
<<for the next 24 hours, you simply want to sum precipIntensity for each hour,
since it is the expected precipitation amount per hour for those hours>>
<<Regarding the probabilities for "current" conditions:
think of the probabilities as representing our uncertainty about events rather than a direct probability of an event occurring.
Because there are not stations at every location in the world and their data is not perfect, even historical observations have some uncertainty>>
]]--
local debug=0
local DOMAIN = 'Meteo'
local TIME_INTERVAL = 'every 10 minutes' -- 'every ...'
local coord='48.444444,2.233379' -- latitude,longitude
local api_key='xxxxxxxxxxxxxxxxxxxxxxxxxxx' -- darkSky secret key
-- Devices to update with API result
local dev_temp_hum_baro = 299 -- dummy receive Temp+Hum+Baro / Type Temp + Humidity + Baro
local dev_temperature = 300 -- dummy receive temperature / Type Temp
local dev_rain = 301 -- dummy receive rain in mm / Type Rain
local dev_rainExp = 366 -- dummy receive rain in mm / Type Rain
local dev_humidity = 302 -- dummy receive humidity / Type Humidity
local dev_barometer = 303 -- dummy receive pressure / SubType Barometer
local dev_wind = 298 -- dummy receive wind measures (Vent+Temp+Ressentie) / Type Wind
-- to choose between custom and %. With custom you could choose the icon!
local dev_cloudCoverPerCent = 339 -- or nill / dummy receive cloudCover / SubType Percentage -- to choose between custom and %
local dev_cloudCover = 309 -- or nill / dummy receive cloudCover / SubType Custom Sensor with unit = % -- With custom you could choose the icon!
local dev_uvIndex = 306 -- dummy receive uvIndex / Type UV
local dev_visibility = 310 -- dummy receive visibility / SubType Visibility
local dev_ozone = 308 -- dummy receive ozone / SubType Custom Sensor
-- Devices to update with calculation with API result
local dev_rain1h = 317 -- dummy receive 1h expected rain in mm / Custom sensor (mm)
local dev_rain12h = 318 -- dummy receive 12h expected rain in mm / Custom sensor (mm)
local dev_rain24h = 319 -- dummy receive 24h expected rain in mm / Custom sensor (mm)
return {
active = true,
logging = {
level = domoticz.LOG_ERROR, -- Select one of LOG_DEBUG, LOG_INFO, LOG_ERROR, LOG_FORCE to override system log level
marker = DOMAIN},
on = {
-- devices = {203}, -- a switch for testing w/o waiting minutes
timer = {TIME_INTERVAL},
httpResponses = {'getDarkSkyForecast'}
},
data = {
lastRain = {initial=nill },
lastRainExp = {initial=nill }
},
execute = function(dz, triggerObject)
-- /// Functions start \\\
local function string2Epoch(dateString) -- seconds from epoch based on stringdate (used by string2Date) -- Assuming a date pattern like: yyyy-mm-dd
local pattern = '(%d+)-(%d+)-(%d+)'
local runyear, runmonth, runday= dateString:match(pattern)
local convertedTimestamp = os.time({year = runyear, month = runmonth, day = runday})
return convertedTimestamp
end
local function string2Date(str,fmt) -- convert string from json into datevalue
if fmt then
return os.date(fmt,string2Epoch(str))
end
return os.date(' %A %d %B, %Y',string2Epoch(str)):gsub(' 0',' ')
end
local function round(num, dec)
if num == 0 then
return 0
else
local mult = 10^(dec or 0)
return math.floor(num * mult + 0.5) / mult
end
end
local function humidity_status(humidity_val) -- humidity in %
local status = dz.HUM_NORMAL
if humidity_val > 69 then status = dz.HUM_WET
elseif humidity_val < 30 then status = dz.HUM_DRY
elseif humidity_val < 45 then status = dz.HUM_NORMAL
else status = dz.HUM_COMFORTABLE
end
return status
end
local function THBForecast_calc(icon_val)
local THBForecast_val = dz.BARO_NOINFO
if icon_val == "" or icon_val == "wind" then THBForecast_val = dz.BARO_NOINFO
elseif icon_val == "clear-day" or icon_val == "clear-night" then THBForecast_val = dz.BARO_SUNNY
elseif icon_val == "partly-cloudy-day" or icon_val == "partly-cloudy-night" then THBForecast_val = dz.BARO_PARTLYCLOUDY
elseif icon_val == "sleet" or icon_val == "fog" or icon_val == "cloudy" then THBForecast_val = dz.BARO_CLOUDY
elseif icon_val == "rain" or icon == "snow" then THBForecast_val=dz.BARO_RAIN
end
if debug == 1 then
print('icon_val: ' .. icon_val)
print('THBForecast_val: ' .. THBForecast_val)
end
return THBForecast_val
end
local function baroForecast_calc(icon_val) -- to improve
local baroForecast_val = dz.BARO_NOINFO
if icon_val == "wind" then baroForecast_val = dz.BARO_UNSTABLE
elseif icon_val == "clear-day" or icon_val == "clear-night" then baroForecast_val = dz.BARO_SUNNY
elseif icon_val == "partly-cloudy-day" or icon_val == "partly-cloudy-night" then baroForecast_val = dz.BARO_STABLE
elseif icon_val == "sleet" or icon_val == "fog" or icon_val == "cloudy" then baroForecast_val = dz.BARO_CLOUDY
elseif icon_val == "rain" or icon == "snow" then baroForecast_val=dz.BARO_UNSTABLE
end
if debug == 1 then
print('icon_val: ' .. icon_val)
print('baroForecast_val: ' .. baroForecast_val)
end
return baroForecast_val
end
local function TempChill_calc(Temp_val, WindSpeed_kmh_val) -- https://fr.wikipedia.org/wiki/Température_ressentie#Froid_ressenti
local TempChill_val = Temp_val
if (Temp_val ~= nil and WindSpeed_kmh_val ~= nil and Temp_val <= 10) then
if (WindSpeed_kmh_val > 4.8) then
local Pow_WindSpeed = math.pow(WindSpeed_kmh_val, 0.16)
local Pow_WindSpeed = math.pow(WindSpeed_kmh_val, 0.16)
TempChill_val = 13.12 + 0.6215 * Temp_val + (0.3965 * Temp_val - 11.37) * Pow_WindSpeed
elseif WindSpeed_kmh_val >= 0 then
TempChill_val = Temp_val + 0.2 * (0.1345 * Temp_val - 1.59) * WindSpeed_kmh_val
end
end
return round(TempChill_val,1)
end
local function windDirection_calc(windBearing_val) -- Wind direction calculation
if (windBearing_val < 11.25) then wind_direction_val = 'N'
elseif (windBearing_val >= 11.25 and windBearing_val < 33.75) then wind_direction_val = 'NNE'
elseif (windBearing_val >= 33.75 and windBearing_val < 56.25) then wind_direction_val = 'NE'
elseif (windBearing_val >= 56.25 and windBearing_val < 78.75) then wind_direction_val = 'ENE'
elseif (windBearing_val >= 78.75 and windBearing_val < 101.25) then wind_direction_val = 'E'
elseif (windBearing_val >= 101.25 and windBearing_val < 123.75) then wind_direction_val = 'ESE'
elseif (windBearing_val >= 123.75 and windBearing_val < 146.25) then wind_direction_val = 'SE'
elseif (windBearing_val >= 146.25 and windBearing_val < 168.75) then wind_direction_val = 'SSE'
elseif (windBearing_val >= 168.75 and windBearing_val < 191.25) then wind_direction_val = 'S'
elseif (windBearing_val >= 191.25 and windBearing_val < 213.75) then wind_direction_val = 'SSW'
elseif (windBearing_val >= 213.75 and windBearing_val < 236.25) then wind_direction_val = 'SW'
elseif (windBearing_val >= 236.25 and windBearing_val < 258.75) then wind_direction_val = 'WSW'
elseif (windBearing_val >= 258.75 and windBearing_val < 281.25) then wind_direction_val = 'W'
elseif (windBearing_val >= 281.25 and windBearing_val < 303.75) then wind_direction_val = 'WNW'
elseif (windBearing_val >= 303.75 and windBearing_val < 326.25) then wind_direction_val = 'NW'
elseif (windBearing_val >= 326.25 and windBearing_val < 348.75) then wind_direction_val = 'NNW'
else wind_direction_val = 'N'
end
return wind_direction_val
end
-- \\\ Functions end ///
if (triggerObject.isDevice or triggerObject.isTimer) then -- triggerObject.isDevic for testing only
local darkSkyUrl = 'https://api.darksky.net/forecast/'..api_key..'/'..coord..'?lang=fr&units=si&exclude=minutely,daily' -- alerts to get sources, nearest-station, units
dz.log('*** Request url', dz.LOG_FORCE)
if debug==1 then
print('url: ' .. darkSkyUrl)
end
dz.openURL({url = darkSkyUrl, method = 'GET', callback = 'getDarkSkyForecast'})
elseif (triggerObject.isHTTPResponse) then
dz.log('*** Response...', dz.LOG_FORCE)
if (triggerObject.ok) then
if debug==1 then
print('triggerObject ok')
print('trigger: ' .. triggerObject.trigger)
print('callback: ' .. triggerObject.callback)
print('statusCode: ' .. triggerObject.statusCode)
end
if (triggerObject.isJSON) then
local currently_time = os.date('%x %X', triggerObject.json.currently.time)
if debug==1 then
print ('JSON :-)')
print ('latitude: ' ..triggerObject.json.latitude)
print ('longitude: ' ..triggerObject.json.longitude)
print ('timezone: ' ..triggerObject.json.timezone)
print('time UNIX: '.. triggerObject.json.currently.time)
print('time: '.. currently_time)
end
--[[ to do once to know - need to add flag
print('nearest-station: '.. triggerObject.json.flags["nearest-station"])
print('units: '.. triggerObject.json.flags.units)
local sourceId
for i in pairs(triggerObject.json.flags.sources) do
print('sources '.. i .. ": " .. triggerObject.json.flags.sources[i])
end
]]--
local nillCount = 0 -- to count nill values from dark Sky
local nillCountH = 0 -- to count nill values from dark Sky (hourly data)
-- Variables to calcule rain forecast
local hourlyPrecipIntensity = {initial={}}
local hourlyPrecipExpectation = {initial={}}
local hourlyTime= {initial={}}
local h1PrecipExpectation = 0
local h12PrecipExpectation = 0
local h24PrecipExpectation = 0
local h1PrecipMax = 0
local h12PrecipMax = 0
local h24PrecipMax = 0
-- Retrieve values from dz in case of nill values from json
local dz_rainRate = dz.devices(dev_rain).rainRate
local dz_rain = dz.devices(dev_rain).rain
local dz_rainExp = dz.devices(dev_rainExp).rain
if dz.data.lastRain == nill then
dz.data.lastRain = dz_rain
if debug == 2 then
print('dz.data.lastRain nill: ' .. dz.data.lastRain)
end
else
if debug == 2 then
print('dz.data.lastRain NOT nill: ' .. dz.data.lastRain)
end
end
if dz.data.lastRainExp == nill then
dz.data.lastRainExp = dz_rainExp
if debug == 2 then
print('dz.data.lastRainExp nill: ' .. dz.data.lastRainExp)
end
else
if debug == 2 then
print('dz.data.lastRainExp NOT nill: ' .. dz.data.lastRainExp)
end
end
local dz_humidity = dz.devices(dev_humidity).humidity
local dz_temperature = dz.devices(dev_temperature).temperature
local dz_dewPoint = dz.devices(dev_temp_hum_baro).dewPoint
local dz_pressure = dz.devices(dev_barometer).barometer
local dz_baroForecast = dz.devices(dev_barometer).forecast
local dz_THBForecast = dz.devices(dev_barometer).forecast
local dz_windBearing = dz.devices(dev_wind).direction
local dz_windSpeed_ms = dz.devices(dev_wind).speed
local dz_windGust_ms = dz.devices(dev_wind).gust
local dz_windChill = dz.devices(dev_wind).chill
if debug==1 then
print(' ')
print('dz values from devices')
print('dz_rainRate: ' .. dz_rainRate)
print('dz_rain: ' .. dz_rain)
print('dz_humidity: ' .. dz_humidity)
print('dz_temperature: ' .. dz_temperature)
print('dz_dewPoint: ' .. dz_dewPoint)
print('dz_pressure: ' .. dz_pressure)
print('dz_baroForecast: ' .. dz_baroForecast)
print('dz_THBForecast: ' .. dz_THBForecast)
print('dz_windBearing: ' .. dz_windBearing)
print('dz_windSpeed_ms: ' .. dz_windSpeed_ms)
print('dz_windGust_ms: ' .. dz_windGust_ms)
print('dz_windChill: ' .. dz_windChill)
print(' ')
print('Dark Sky values')
print('precipIntensity: ' .. triggerObject.json.currently.precipIntensity)
print('precipProbability: ' .. triggerObject.json.currently.precipProbability)
print('humidity: ' .. triggerObject.json.currently.humidity)
print('temperature: ' .. triggerObject.json.currently.temperature)
print('pressure: ' .. triggerObject.json.currently.pressure)
print('icon: ' .. triggerObject.json.currently.icon)
print('windSpeed: ' .. triggerObject.json.currently.windSpeed)
print('windBearing: ' .. triggerObject.json.currently.windBearing)
print('windGust: ' .. triggerObject.json.currently.windGust)
print('windChill: ' .. triggerObject.json.currently.apparentTemperature)
print('dewPoint : ' .. triggerObject.json.currently.dewPoint)
print('cloudCover: ' .. triggerObject.json.currently.cloudCover)
print('uvIndex: ' .. triggerObject.json.currently.uvIndex)
print('visibility: ' .. triggerObject.json.currently.visibility)
print('ozone: ' .. triggerObject.json.currently.ozone)
end
-- get with values from json and if not nill, update the devices with Dark Sky values ; if nill keep dz values
local hum_status = '' --
local Time = require('Time')
local now = Time()
local nowSecondsSinceMidnight = now.secondsSinceMidnight
if triggerObject.json.currently.precipIntensity ~= nill then
local lastRainUpdateInSec = dz.devices(dev_rain).lastUpdate.secondsAgo
local lastRainExpUpdateInSec = dz.devices(dev_rainExp).lastUpdate.secondsAgo
-- rain sensor is the total rain of the day
-- precipIntensity is for 1 hour, so 3600 seconds, for the last update I took the part for the elapsed time)
if lastRainUpdateInSec <= nowSecondsSinceMidnight then -- the last update was yesterday
dz.data.lastRain = dz.data.lastRain + triggerObject.json.currently.precipIntensity / 3600 * lastRainUpdateInSec
else dz.data.lastRain = triggerObject.json.currently.precipIntensity / 3600 * lastRainUpdateInSec
end
dz_rain = round(dz.data.lastRain, 1)
if triggerObject.json.currently.precipProbability ~= nill then
if lastRainExpUpdateInSec <= nowSecondsSinceMidnight then -- the last update was yesterday
dz.data.lastRainExp = dz.data.lastRainExp + triggerObject.json.currently.precipIntensity / 3600 * lastRainExpUpdateInSec * triggerObject.json.currently.precipProbability
else dz.data.lastRainExp = triggerObject.json.currently.precipIntensity / 3600 * lastRainExpUpdateInSec * triggerObject.json.currently.precipProbability
end
dz_rainExp = round(dz.data.lastRainExp, 1)
else nillCount = nillCount + 1
end
if debug == 2 then
print('secondsSinceMidnight: ' .. nowSecondsSinceMidnight)
print('rain last update: ' .. lastRainUpdateInSec)
print("precipIntensity: " .. triggerObject.json.currently.precipIntensity)
print('precipProbability: ' .. triggerObject.json.currently.precipProbability)
print("lastRain: " .. dz.data.lastRain)
print('dz_rain: ' .. dz_rain)
print("lastRainExp: " .. dz.data.lastRainExp)
print('dz_rainExp: ' .. dz_rainExp)
end
if triggerObject.json.currently.precipIntensity ~= nill then
dz_rainRate = round(triggerObject.json.currently.precipIntensity * 100,0)
else nillCount = nillCount + 1
end
else nillCount = nillCount + 1
end -- else dz value is kept
dz.devices(dev_rain).updateRain(dz_rainRate,dz_rain)
dz.devices(dev_rainExp).updateRain(dz_rainRate,dz_rainExp)
if triggerObject.json.currently.humidity ~= nill then
dz_humidity = triggerObject.json.currently.humidity*100
local hum_status = humidity_status(dz_humidity) dz.devices(dev_humidity).updateHumidity(dz_humidity, hum_status)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.temperature ~= nill then
dz_temperature = round(triggerObject.json.currently.temperature,1) dz.devices(dev_temperature).updateTemperature(dz_temperature)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.pressure ~= nill then
dz_pressure = round(triggerObject.json.currently.pressure,0)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.icon ~= nill then
dz_baroForecast = baroForecast_calc(triggerObject.json.currently.icon)
else nillCount = nillCount + 1
end
dz.devices(dev_barometer).updateBarometer(dz_pressure, dz_baroForecast)
if triggerObject.json.currently.windBearing ~= nill then
dz_windBearing = triggerObject.json.currently.windBearing
else nillCount = nillCount + 1
end
local wind_direction = windDirection_calc(dz_windBearing)
if debug==1 then
print ('dz_windBearing: ' .. dz_windBearing)
print('wind_direction: ' .. wind_direction)
end
if triggerObject.json.currently.windGust ~= nill then
dz_windGust_ms = round(triggerObject.json.currently.windGust,1)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.apparentTemperature ~= nill then
tempChill = round(triggerObject.json.currently.apparentTemperature,1)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.icon ~= nill then
dz_THBForecast = THBForecast_calc(triggerObject.json.currently.icon)
else nillCount = nillCount + 1
end
dz.devices(dev_wind).updateWind(dz_windBearing, wind_direction, dz_windSpeed_ms, dz_windGust_ms, dz_temperature,tempChill)
dz.devices(dev_temp_hum_baro).updateTempHumBaro(dz_temperature,dz_humidity,hum_status,dz_pressure,dz_THBForecast)
if triggerObject.json.currently.dewPoint ~= nill then
dz_dewPoint = round(triggerObject.json.currently.dewPoint,1)
dz.devices(dev_temp_hum_baro).dewPoint = triggerObject.json.currently.dewPoint -- doesn't work !? :-(
else nillCount = nillCount + 1
end
if triggerObject.json.currently.cloudCover ~= nill then
if dev_cloudCover ~= nill then
dz.devices(dev_cloudCover).updateCustomSensor(triggerObject.json.currently.cloudCover*100)
end
if dev_cloudCoverPerCent ~= nill then
dz.devices(dev_cloudCoverPerCent).updatePercentage(triggerObject.json.currently.cloudCover*100)
end
else nillCount = nillCount + 1
end
if triggerObject.json.currently.uvIndex ~= nill then
dz.devices(dev_uvIndex).updateUV(triggerObject.json.currently.uvIndex)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.visibility ~= nill then
dz.devices(dev_visibility).updateVisibility(triggerObject.json.currently .visibility)
else nillCount = nillCount + 1
end
if triggerObject.json.currently.ozone ~= nill then
dz.devices(dev_ozone).updateCustomSensor(round(triggerObject.json.currently.ozone))
else nillCount = nillCount + 1
end
if debug==1 then
print(' ')
print('dz values from devices')
print('dz_rainRate: ' .. dz_rainRate)
print('dz_rain: ' .. dz_rain)
print('dz_humidity: ' .. dz_humidity)
print('dz_temperature: ' .. dz_temperature)
print('dz_dewPoint: ' .. dz_dewPoint)
print('dz_pressure: ' .. dz_pressure)
print('dz_baroForecast: ' .. dz_baroForecast)
print('dz_THBForecast: ' .. dz_THBForecast)
print('dz_windBearing: ' .. dz_windBearing)
print('dz_windSpeed_ms: ' .. dz_windSpeed_ms)
print('dz_windGust_ms: ' .. dz_windGust_ms)
print('dz_windChill: ' .. dz_windChill)
print(' ')
end
-- Rain forecast calculation --
for i = 1, 25 do -- walk the hourly result for 24 hours (maxi = 49)
if triggerObject.json.hourly.data[i].precipIntensity ~= nill then
hourlyPrecipIntensity[i] = triggerObject.json.hourly.data[i].precipIntensity
hourlyPrecipExpectation[i] = hourlyPrecipIntensity[i]
if i == 1 then -- for the next hour
h1PrecipExpectation = hourlyPrecipExpectation[i]
h1PrecipMax = triggerObject.json.hourly.data[i].precipIntensity
end
if i < 13 then -- for the next 12 hours
h12PrecipExpectation = h12PrecipExpectation + hourlyPrecipExpectation[i]
if h12PrecipMax < triggerObject.json.hourly.data[i].precipIntensity then
h12PrecipMax = triggerObject.json.hourly.data[i].precipIntensity
end
end
h24PrecipExpectation = h24PrecipExpectation + hourlyPrecipExpectation[i]
if h24PrecipMax < triggerObject.json.hourly.data[i].precipIntensity then
h24PrecipMax = triggerObject.json.hourly.data[i].precipIntensity
end
if triggerObject.json.hourly.data[i] ~= nill then
hourlyTime[i] = os.date('%x %X', triggerObject.json.hourly.data[i].time)
else hourlyTime[i] = 0
nillCountH = nillCountH + 1
hourlyPrecipExpectation[i] = 0
end
else nillCountH = nillCountH + 1
hourlyPrecipIntensity[i] = 0
hourlyPrecipExpectation[i] = 0
end
if debug==1 then
print( i .. ' ' .. hourlyTime[i] .. ' precipIntensity: ' .. hourlyPrecipIntensity[i] .. ' expectation: ' .. hourlyPrecipExpectation[i])
end
end
h1PrecipMax = round((h1PrecipMax*100),-1)
h12PrecipMax = round((h12PrecipMax*100),-1)
h24PrecipMax = round((h24PrecipMax*100),-1)
h1PrecipExpectation = round(h1PrecipExpectation,1)
h12PrecipExpectation = round(h12PrecipExpectation,1)
h24PrecipExpectation = round(h24PrecipExpectation,1)
if debug==1 then
print('h1PrecipMax: ' .. h1PrecipMax .. ' h1PrecipExpectation: ' .. h1PrecipExpectation)
print('h12PrecipMax: ' .. h12PrecipMax .. ' h12PrecipExpectation: ' .. h12PrecipExpectation)
print('h24PrecipMax: ' .. h24PrecipMax .. ' h24PrecipExpectation: ' .. h24PrecipExpectation)
end
dz.devices(dev_rain1h).updateCustomSensor(h1PrecipExpectation)
dz.devices(dev_rain12h).updateCustomSensor(h12PrecipExpectation)
dz.devices(dev_rain24h).updateCustomSensor(h24PrecipExpectation)
if debug==1 then
print('h1PrecipExpectation rounded: ' .. h1PrecipExpectation)
print('h12PrecipExpectation rounded: ' .. h12PrecipExpectation)
print('h24PrecipExpectation rounded: ' .. h24PrecipExpectation)
end
if nillCount == 0 and nillCountH == 0 then
dz.log('Meteo Dark Sky end OK', dz.LOG_FORCE)
else dz.log('Meteo Dark Sky end KO : ' .. nillCount .. ' / ' .. nillCountH, dz.LOG_ERROR) -- number of values not present in the jason
end
else dz.log('NO JSON ???' , dz.LOG_ERROR)
end
else dz.log('Response KO !!!' , dz.LOG_ERROR)
end
else dz.log('Error triggerObject !!!' , dz.LOG_ERROR)
end
end
}