Lua dzVents - Solar Data: Azimuth Altitude Lux
The forumtopic of this script is here : https://domoticz.com/forum/viewtopic.php?f=72&t=35541
Purpose
The purpose of this script is to calculate in real-time some usefull solar data without any hardware sensor :
· Azimuth : Angle between the Sun and the north, in degree. (north vector and the perpendicular projection of the sun down onto the horizon)
· Altitude : Angle of the Sun with the horizon, in degree.
· Lux : The illuminance, equal to one lumen per square metre. Taking account of the real time cloud layer.
· Radiation : The amount of Watt per Meter2
· and many more in the script but not exposed.
The calculation for your Geographical location (altitude, latitude, longitude) is based on the theoretical radiations of the sun according to its position in the sky at the moment, plus the real-time cloud layer and the real-time Atmospheric pressure.
What is it For ?
· Automation of roller blinds and venetian blind both to optimise the heating and to prevent dazzle.
· Manage your light according to the Luminosity
· Use the real civil twilight dawn and dusk (-6 deg altitude) instead of sunrise and sunset.
· Some calculation about photovoltaic production ?
· Automate the heating of swimming pool
· Personally I use a roller shutter to wake me up instead of an alarm clock, with a progressive opening of the roller to avoid glare.
History
The script had a lot of contributors.
Neutrino and I (jmleglise) have adapted to Domoticz a work of Sébastien Joly on an another automation system. Then it was migrate to dzvents.
In the beginning, the script was standalone and requested its data directly via API from a weather service providers. So a lot of forks appeared to adapt it, each time a provider disappeared: weather Underground => darksky => openweatherMap. The last big improvement was to base the input data only on domoticz devices. So It is up to you to feed the input data with the source you want. It is far better. The script is now source independent. And it remains easy because domoticz provide some weather provider in the Hardware section.
I have clean up the mess, merged again the forks, took the best of each of them and pay tribute to their contributors.
Here is the ultimate version. And please, let's avoid forks in the future. If you improve the script, don't do it like a savage. Propose it on the forum and wait for it to be accepted. Otherwise keep it to yourself.
Documentation
You can have a look to the very interesting documentations of the formulae :
· www.domotique-info.fr/2015/09/ou-est-le-soleil-pour-votre-homecenter-2/
· www.plevenon-meteo.info/technique/theorie/enso/ensoleillement.html
· en.wikipedia.org/wiki/Azimuth
Known limitations :
- Under 1° of Altitude of the sun, the formulae reach their limit of precision. So I have fixed in hard the theoretical value of the Lux at dawn/dusk defined by the Civil twilight : "when the Sun is between 0 and -6 degrees below the horizon, there is enough light for objects to be distinguishable. The sky is bright, even when cloudy."
- Originally, the script took the cloud cover from www.ogimet.com. To my knowledge, it is the only site in the world that gives access (for free) to synop data provided by all the weather stations in the world. Since then, we have abandoned this and now we get the cloudiness on the weather services integrated in domoticz. But openweatherMap seems to be much less performant on the coverage. The 2 services should be compared and perhaps come back to ogimet.
Installation instructions
The script
The last version of the script is here : https://github.com/jmleglise/mylittle-domoticz/blob/master/LuaScript/SolarData and below :
Put it in an event : Setup/More Options / Events / + / dzvents
Create Virtual device for Output :
The script calculates 4 data. But all are optional. Create the ones you want and enter their idx in the script. If you don't want them, just put nil instead of the idx.
E.g. :
local idxRadiation = nil
LUX : Virtual sensor type "Lux", name as you want.
Sun Azimuth : Virtual sensor type "custom type" / Y axis = "degrees". You can choose the icon , by editing it in the dashboard.
Sun Altitude : Virtual sensor type "custom type" / Y axis = "degrees". You can choose the icon , by editing it in the dashboard.
Sun Radiation : Virtual sensor type "custom type" Y axis = "Watt/m2". You can choose the icon , by editing it in the dashboard.
Device for the data Input :
The script needs a barometer. (This must be a device of type 'barometer'.) and the cloud cover. (This must be a device of type percentage 0% - 100%).
You can use either :
- a domoticz weather provider. For example: openweatherMap.
The devices are created automatically. Found them in the Setup/devices menu to get their idx.
- your physical devices.
- or your own data sources. (example ogimet for cloud cover)
Note the idx and write them in the script.
FYI, in my case (french city near Paris) , the OpenweatherMap 's cloudiness is really bad compared to the data return by ogimet. Above : OWM = 0% cloud. Ogimet=88% ...
Other parameters
local altitude = 27 -- Meters above sea level of your location. (Integer) Can be found from coordinates on https://www.advancedconverter.com/map-tools/find-altitude-by-coordinates
local latitude = nil -- Keep nil if you have defined your lat. and long. in the settings. Otherwise you can overwrite it here. E.g. something like 51.748485
local longitude = nil -- idem. E.g.something like 5.629728.
local logToFile = false -- Set to true if you also want to log to a file. It might get big by time.
local tmpLogFile = '/tmp/logSun.txt' -- Logging to a file if specified
Enjoy!
I hope you will enjoy it and please, share the uses you will make of it in this forumtopic : https://domoticz.com/forum/viewtopic.php?f=72&t=35541
Source Code
Dzvents Version
--[[
This script calculate in real-time some usefull solar data without any hardware sensor :
· Azimuth : Angle between the Sun and the north, in degree. (north vector and the perpendicular projection of the sun down onto the horizon)
· Altitude : Angle of the Sun with the horizon, in degree.
· Lux : The illuminance, equal to one lumen per square metre. Taking account of the real time cloud layer.
-- Installation & Documentation -----------------------------------------------------------
https://www.domoticz.com/wiki/index.php?title=Lua_dzVents_-_Solar_Data:_Azimuth_Altitude_Lux
-- Prerequisits -----------------------------------------------------------
2 sensors with Cloud Cover and Pressure updated
Requires Domoticz v3.8551 or later.
Work with lua 5.3 too.
No Platform dependent; Linux & Windows
-- Contributors ----------------------------------------------------------------
V1.0 - Sébastien Joly - Great original work
V1.1 - Neutrino - Adaptation to Domoticz
V1.2 - Jmleglise - An acceptable approximation of the lux below 1° altitude for Dawn and dusk + translation + several changes to be more userfriendly.
V1.3 - Jmleglise - No update of the Lux data when <=0 to get the sunset and sunrise with lastUpdate
V1.4 - Jmleglise - use the API instead of updateDevice to update the data of the virtual sensor to be able of using devicechanged['Lux'] in our scripts. (Due to a bug in Domoticz that doesn't catch the devicechanged event of the virtual sensor)
V1.5 - xces - UTC time calculation.
- waaren - pow function for lua 5.3 compatibility
V2.4 - BakSeeDaa - Converted to dzVents and changed quite many things.
V2.41 - Oredin - Use Dark Sky API instead of WU API
V3.0 - Bram Vreugdenhil/Hestia - Weather Api independent. Use OpenWeathermaps devices or your own sensors
V3.1 - Jmleglise - Merge the different fork. Some clean Up, comment and wiki.
]]--
local scriptName = 'solarData'
local scriptVersion = '3.1'
-- Variables to customize ------------------------------------------------
-- Devices for Input Data
local idxCloudCover = 684 -- device holding cloudcoverage in percentage. Ex : 60% (for E.g. a device from Openweathermap or darksky)
local idxBarometer = 675 -- Barometer device. E.g. a device from Openweathermap that contains value in hPa: like a Temp+Humidity+Baro device with : -0.5 C, 59 %, 1023.0 hPa (for E.g. a device from Openweathermap or your own hardware sensor)
-- Devices for Output Data (can be nil if you dont want some data)------------------------------------------
local idxSolarAzimuth = 111 -- Your device. A Virtual customer sensor for Solar Azimuth
local idxSolarAltitude = 112 -- Your device. A Virtual customer sensor for Solar Altitude
local idxRadiation = 130 -- Your device. A Virtual customer sensor for Solar Radiation (in Watt/m2)
local idxLux = 72 -- Your device. A Virtual customer sensor for Solar Lux
-- Other parameters -----------------------------------------------------
local intervalMins = 5 -- The interval of running this script. No need to be faster than the data source. (For example it is 10 min)
local altitude = 27 -- Meters above sea level of your location. (Integer) Can be found from coordinates on https://www.advancedconverter.com/map-tools/find-altitude-by-coordinates
local latitude = nil -- Keep nil if you have defined your lat. and long. in the settings. Otherwise you can overwrite it here. E.g. something like 51.748485
local longitude = nil -- idem. E.g.something like 5.629728.
local logToFile = true -- Set to true if you also want to log to a file. It might get big by time.
local tmpLogFile = '/tmp/logSun.txt' -- Logging to a file if specified
-- Don't make any changes below this line (Except for setting logging level) ----------------------------------------------------------------------
return {
active = true,
logging = {
-- level = domoticz.LOG_DEBUG, -- Uncomment to override the dzVents global logging setting
marker = scriptName..' '..scriptVersion
},
on = {
-- devices = {'testSwitch'}, -- a switch for testing w/o waiting minutes
timer = {'every '..tostring(intervalMins)..' minutes'} -- There is no more limit to worry about as there is no API called
},
execute = function(domoticz, device)
local function leapYear(year)
return year%4==0 and (year%100~=0 or year%400==0)
end
function math.pow(x, y) -- Function math.pow(x, y) has been deprecated in Lua 5.3.
return x^y
end
if latitude == nil then
latitude = domoticz.settings.location.latitude
end
if longitude == nil then
longitude = domoticz.settings.location.longitude
end
local arbitraryTwilightLux = 6.32 -- W/m² egal 800 Lux (the theoritical value is 4.74 but I have more accurate result with 6.32...)
local constantSolarRadiation = 1361 -- Solar Constant W/m²
local relativePressure = domoticz.devices(idxBarometer).barometer
local year = os.date('%Y')
local numOfDay = os.date('%j')
local nbDaysInYear = (leapYear(year) and 366 or 365)
local angularSpeed = 360/365.25
local declination = math.deg(math.asin(0.3978 * math.sin(math.rad(angularSpeed) *(numOfDay - (81 - 2 * math.sin((math.rad(angularSpeed) * (numOfDay - 2))))))))
local timeDecimal = (os.date('!%H') + os.date('!%M') / 60) -- Coordinated Universal Time (UTC)
local solarHour = timeDecimal + (4 * longitude / 60 ) -- The solar Hour
local hourlyAngle = 15 * ( 12 - solarHour ) -- hourly Angle of the sun
local sunAltitude = math.deg(math.asin(math.sin(math.rad(latitude))* math.sin(math.rad(declination)) + math.cos(math.rad(latitude)) * math.cos(math.rad(declination)) * math.cos(math.rad(hourlyAngle))))-- the height of the sun in degree, compared with the horizon
local sunAzimuth = math.acos((math.sin(math.rad(declination)) - math.sin(math.rad(latitude)) * math.sin(math.rad(sunAltitude))) / (math.cos(math.rad(latitude)) * math.cos(math.rad(sunAltitude) ))) * 180 / math.pi -- deviation of the sun from the North, in degree
local sinAzimuth = (math.cos(math.rad(declination)) * math.sin(math.rad(hourlyAngle))) / math.cos(math.rad(sunAltitude))
if(sinAzimuth<0) then sunAzimuth=360-sunAzimuth end
local sunstrokeDuration = math.deg(2/15 * math.acos(- math.tan(math.rad(latitude)) * math.tan(math.rad(declination)))) -- duration of sunstroke in the day . Not used in this calculation.
local RadiationAtm = constantSolarRadiation * (1 +0.034 * math.cos( math.rad( 360 * numOfDay / nbDaysInYear ))) -- Sun radiation (in W/m²) in the entrance of atmosphere.
-- Coefficient of mitigation M
local absolutePressure = relativePressure - domoticz.utils.round((altitude/ 8.3),1) -- hPa
local sinusSunAltitude = math.sin(math.rad(sunAltitude))
local M0 = math.sqrt(1229 + math.pow(614 * sinusSunAltitude,2)) - 614 * sinusSunAltitude
local M = M0 * relativePressure/absolutePressure
domoticz.log('', domoticz.LOG_INFO)
domoticz.log('================== '..scriptName..' V'..scriptVersion..' ==================', domoticz.LOG_INFO)
domoticz.log('Altitude:'..tostring(altitude)..', latitude: ' .. latitude .. ', longitude: ' .. longitude, domoticz.LOG_INFO)
domoticz.log('Angular Speed = ' .. angularSpeed .. ' per day', domoticz.LOG_DEBUG)
domoticz.log('Declination = ' .. declination .. '°', domoticz.LOG_DEBUG)
domoticz.log('Universal Coordinated Time (UTC) '.. timeDecimal ..' H.dd', domoticz.LOG_DEBUG)
domoticz.log('Solar Hour '.. solarHour ..' H.dd', domoticz.LOG_DEBUG)
domoticz.log('Altitude of the sun = ' .. sunAltitude .. '°', domoticz.LOG_INFO)
domoticz.log('Angular hourly = '.. hourlyAngle .. '°', domoticz.LOG_DEBUG)
domoticz.log('Azimuth of the sun = ' .. sunAzimuth .. '°', domoticz.LOG_INFO)
domoticz.log('Duration of the sun stroke of the day = ' .. domoticz.utils.round(sunstrokeDuration,2) ..' H.dd', domoticz.LOG_DEBUG)
domoticz.log('Radiation max in atmosphere = ' .. domoticz.utils.round(RadiationAtm,2) .. ' W/m²', domoticz.LOG_DEBUG)
domoticz.log('Local relative pressure = ' .. relativePressure .. ' hPa', domoticz.LOG_DEBUG)
domoticz.log('Absolute pressure in atmosphere = ' .. absolutePressure .. ' hPa', domoticz.LOG_DEBUG)
domoticz.log('Coefficient of mitigation M = ' .. M ..' M0 = '..M0, domoticz.LOG_DEBUG)
domoticz.log('', domoticz.LOG_INFO)
-- In meteorology, an okta is a unit of measurement used to describe the amount of cloud cover
-- at any given location such as a weather station. Sky conditions are estimated in terms of how many
-- eighths of the sky are covered in cloud, ranging from 0 oktas (completely clear sky) through to 8 oktas
-- (completely overcast). In addition, in the synop code there is an extra cloud cover indicator '9'
-- indicating that the sky is totally obscured (i.e. hidden from view),
-- usually due to dense fog or heavy snow.
Cloudpercentage = domoticz.devices(idxCloudCover).percentage
okta = Cloudpercentage/12.5
local Kc = 1-0.75*math.pow(okta/8,3.4) -- Factor of mitigation for the cloud layer
local directRadiation, scatteredRadiation, totalRadiation, Lux, weightedLux
if sunAltitude > 1 then -- Below 1° of Altitude , the formulae reach their limit of precision.
directRadiation = RadiationAtm * math.pow(0.6,M) * sinusSunAltitude
scatteredRadiation = RadiationAtm * (0.271 - 0.294 * math.pow(0.6,M)) * sinusSunAltitude
totalRadiation = scatteredRadiation + directRadiation
Lux = totalRadiation / 0.0079 -- Radiation in Lux. 1 Lux = 0,0079 W/m²
weightedLux = Lux * Kc -- radiation of the Sun with the cloud layer
elseif sunAltitude <= 1 and sunAltitude >= -7 then -- apply theoretical Lux of twilight
directRadiation = 0
scatteredRadiation = 0
arbitraryTwilightLux=arbitraryTwilightLux-(1-sunAltitude)/8*arbitraryTwilightLux
totalRadiation = scatteredRadiation + directRadiation + arbitraryTwilightLux
Lux = totalRadiation / 0.0079 -- Radiation in Lux. 1 Lux = 0,0079 W/m²
weightedLux = Lux * Kc -- radiation of the Sun with the cloud layer
elseif sunAltitude < -7 then -- no management of nautical and astronomical twilight...
directRadiation = 0
scatteredRadiation = 0
totalRadiation = 0
Lux = 0
weightedLux = 0 -- Lux for the nautic twilight should be around 3,2. I prefer to get 0.
end
totalRadiation=totalRadiation*Kc
domoticz.log('Okta = '..okta.. ' Cloud coverage = ' ..Cloudpercentage .. '%', domoticz.LOG_INFO)
domoticz.log('Kc = ' .. Kc, domoticz.LOG_DEBUG)
domoticz.log('Direct Radiation = '.. domoticz.utils.round(directRadiation,2) ..' W/m²', domoticz.LOG_INFO)
domoticz.log('Scattered Radiation = '.. domoticz.utils.round(scatteredRadiation,2) ..' W/m²', domoticz.LOG_DEBUG)
domoticz.log('Total radiation = ' .. domoticz.utils.round(totalRadiation,2) ..' W/m²', domoticz.LOG_INFO)
domoticz.log('Total Radiation in lux = '.. domoticz.utils.round(Lux,2)..' Lux', domoticz.LOG_DEBUG)
domoticz.log('Total weighted lux = '.. domoticz.utils.round(weightedLux,2)..' Lux', domoticz.LOG_INFO)
-- No update if Lux is already 0. So lastUpdate of the Lux sensor will keep the time when Lux has reached 0.
-- (Kind of timeofday['SunsetInMinutes'])
if idxLux and domoticz.devices(idxLux).lux + domoticz.utils.round(weightedLux, 0) > 0 then
domoticz.devices(idxLux).updateLux(domoticz.utils.round(weightedLux,0))
end
if idxSolarAzimuth then
domoticz.devices(idxSolarAzimuth).updateCustomSensor(domoticz.utils.round(sunAzimuth,0))
end
if idxSolarAltitude then
domoticz.devices(idxSolarAltitude).updateCustomSensor(domoticz.utils.round(sunAltitude,0))
end
-- No update if radiation is already 0. See LUX
if idxRadiation and (domoticz.devices(idxRadiation).rawData[1] + domoticz.utils.round(totalRadiation, 2) > 0) then
domoticz.devices(idxRadiation).updateCustomSensor(domoticz.utils.round(totalRadiation,2))
end
if logToFile then
local logDebug = os.date('%Y-%m-%d %H:%M:%S',os.time())
logDebug=logDebug..' Azimuth:' .. sunAzimuth .. ' Altitude:' .. sunAltitude
logDebug=logDebug..' Okta:' .. okta..' KC:'.. Kc
logDebug=logDebug..' Direct:'..directRadiation..' inDirect:'..scatteredRadiation..' TotalRadiation:'..totalRadiation..' LuxCloud:'.. domoticz.utils.round(weightedLux,2)
os.execute('echo '..logDebug..' >>'..tmpLogFile) -- compatible Linux & Windows
end
end
}
Plain Lua Version
And for Archive, one of the last Lua version
--[[ Virtual Lux sensor and other real-time solar data
~/domoticz/var/scripts/lua/script_time_SolarSensor.lua
-- Autors ----------------------------------------------------------------
V1.0 - Sébastien Joly - Great original work
V1.1 - Neutrino - Adaptation to Domoticz
V1.2 - Jmleglise - An acceptable approximation of the lux below 1° altitude for Dawn and dusk + translation + several changes to be more userfriendly.
V1.3 - Jmleglise - No update of the Lux data when <=0 to get the sunset and sunrise with lastUpdate
V1.4 - use the API instead of updateDevice to update the data of the virtual sensor to be able of using devicechanged['Lux'] in our scripts. (Due to a bug in Domoticz that doesn't catch the devicechanged event of the virtual sensor)
V1.5 - Marco Pietersen - Reverted use of API, introduced WU dependency
]]--
-- Variables to customize ------------------------------------------------
local latitude = 52.134028 -- your home
local longitude = 5.420183 -- your home
local altitude = 27 -- Your home altitude (get from Weather Underground or other source)
local wu_deviceName='Outside Temperature' -- Your Weather Underground device name that shows the Barometer value
local idxLux ='16' -- Your virtual Lux Device ID
local idxSolarAzimuth ='7' -- Your virtual Azimuth Device ID
local idxSolarAltitude ='8' -- Your virtual Solar Altitude Device ID
local idxUserVarOcta='1' -- Your user variable ID , named octa
local WMOID = '06260' -- Your nearest SYNOP Station for ogimet. Very important !
local DEBUG = 0 -- 0 , 1 for domoticz log , 2 for file log
-- Below, edit at your own risk ------------------------------------------
function leapYear(year)
return year%4==0 and (year%100~=0 or year%400==0)
end
function split(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
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
commandArray = {}
time = os.date("*t")
if ((time.min % 5)==0) then -- Run every 5 minutes.
local arbitraryTwilightLux=6.32 -- W/m² egal 800 Lux (the theoritical value is 4.74 but I have more accurate result with 6.32...)
local constantSolarRadiation = 1361 -- Solar Constant W/m²
if (uservariables['octa'] == nil) then print("Error : Did you create the Uservariable octa ?") end
sWeatherTemp, sWeatherHumidity, sHumFeelsLike, sWeatherPressure = otherdevices_svalues[wu_deviceName]:match("([^;]+);([^;]+);([^;]+);([^;]+);([^;]+)")
sWeatherTemp = tonumber(sWeatherTemp)
sWeatherHumidity = tonumber(sWeatherHumidity)
relativePressure = tonumber(sWeatherPressure)
if (DEBUG == 1) then print('WU Script Parsed Temp=' .. sWeatherTemp .. ' Humidity=' .. sWeatherHumidity .. ' Pressure=' .. relativePressure) end
----------------------------------
local year = os.date("%Y")
local numOfDay = os.date("%j")
if leapYear(year) == true then
nbDaysInYear = 366 -- How many days in the year ?
else
nbDaysInYear = 365
end
angularSpeed = 360/365.25
local Declinaison = math.deg(math.asin(0.3978 * math.sin(math.rad(angularSpeed) *(numOfDay - (81 - 2 * math.sin((math.rad(angularSpeed) * (numOfDay - 2))))))))
timeDecimal = (os.date("!%H") + os.date("!%M") / 60) -- Coordinated Universal Time (UTC)
solarHour = timeDecimal + (4 * longitude / 60 ) -- The solar Hour
hourlyAngle = 15 * ( 12 - solarHour ) -- hourly Angle of the sun
sunAltitude = math.deg(math.asin(math.sin(math.rad(latitude))* math.sin(math.rad(Declinaison)) + math.cos(math.rad(latitude)) * math.cos(math.rad(Declinaison)) * math.cos(math.rad(hourlyAngle))))-- the height of the sun in degree, compared with the horizon
local azimuth = math.acos((math.sin(math.rad(Declinaison)) - math.sin(math.rad(latitude)) * math.sin(math.rad(sunAltitude))) / (math.cos(math.rad(latitude)) * math.cos(math.rad(sunAltitude) ))) * 180 / math.pi -- deviation of the sun from the North, in degree
local sinAzimuth = (math.cos(math.rad(Declinaison)) * math.sin(math.rad(hourlyAngle))) / math.cos(math.rad(sunAltitude))
if(sinAzimuth<0) then azimuth=360-azimuth end
sunstrokeDuration = math.deg(2/15 * math.acos(- math.tan(math.rad(latitude)) * math.tan(math.rad(Declinaison)))) -- duration of sunstroke in the day . Not used in this calculation.
RadiationAtm = constantSolarRadiation * (1 +0.034 * math.cos( math.rad( 360 * numOfDay / nbDaysInYear ))) -- Sun radiation (in W/m²) in the entrance of atmosphere.
-- Coefficient of mitigation M
absolutePressure = relativePressure - round((altitude/ 8.3),1) -- hPa
sinusSunAltitude = math.sin(math.rad(sunAltitude))
M0 = math.sqrt(1229 + math.pow(614 * sinusSunAltitude,2)) - 614 * sinusSunAltitude
M = M0 * relativePressure/absolutePressure
if (DEBUG == 1) then
print('<b style="color:Blue"============== SUN LOG ==================</b>')
print(os.date("%Y-%m-%d %H:%M:%S", os.time()))
print("latitude:" .. latitude .. ", longitude:" .. longitude)
print("Home altitude = " .. tostring(altitude) .. " m")
print("number Of Day = " .. numOfDay)
if nbDaysInYear==366 then
print(year .." is a leap year !")
else
print(year.." is not a leap year")
end
print("Angular Speed = " .. angularSpeed .. " per day")
print("Declinaison = " .. Declinaison .. "°")
print("Universel Coordinated Time (UTC)".. timeDecimal .." H.dd")
print("Solar Hour ".. solarHour .." H.dd")
print("Altitude of the sun = " .. sunAltitude .. "°")
print("Angular hourly = ".. hourlyAngle .. "°")
print("Azimuth of the sun = " .. azimuth .. "°")
print("Duration of the sunstroke of the day = " .. round(sunstrokeDuration,2) .." H.dd") -- not used
print("Radiation max in atmosphere = " .. round(RadiationAtm,2) .. " W/m²")
print("Local relative pressure = " .. relativePressure .. " hPa")
print("Absolute pressure in atmosphere = " .. absolutePressure .. " hPa")
print("Coefficient of mitigation M = " .. M .." M0:"..M0)
end
-- Get SYNOP message from Ogimet web site
hourUTCminus1 = os.date("!%H")-1
if string.len(hourUTCminus1) == 1 then
hourUTCminus1 = "0" .. hourUTCminus1
end
UTC = os.date("%Y%m%d").. hourUTCminus1.."00" -- os.date("!%M")
if (DEBUG == 1) then
--local WMOID = jsonLocation.current_observation.display_location.wmo
end
cmd='curl "http://www.ogimet.com/cgi-bin/getsynop?block='..WMOID..'&begin='..UTC..'"'
if(DEBUG == 1) then print(cmd) end
local ogimet=assert(io.popen(cmd))
local synop = ogimet:read('*all')
ogimet:close()
if synop ~= nil
then
rslt = split(synop,",")
CodeStation = rslt[1]
rslt = split(synop, " "..CodeStation.. " ")
Trame = string.gsub(rslt[2], "=", "")
Trame = CodeStation .." ".. Trame
rslt = split(Trame, " ")
Octa = string.sub(rslt[3], 1, 1) -- 3rd char is the cloud layer. 0=no cloud , 1-8= cloudy from 1 to 8 max , 9 =Fog , / = no data
if(DEBUG == 1) then
print('ogimet:'..synop )
print('ogimet Octa: '..Octa )
end
if Octa == "/" then Octa = uservariables['octa'] end -- not defined ? take the previous value
if Octa == "9" then Octa = 8 end
else
Octa = uservariables['octa']
print('SolarSensor did not receive ogimet data. Reverting to last known value:'..Octa)
end
--os.execute('curl "http://127.0.0.1:8081/json.htm?type=command¶m=updateuservariable&idx='..idxUserVarOcta..'&vname=octa&vtype=0&vvalue='..tostring(Octa)..'"')
commandArray[#commandArray + 1] = {['Variable:octa'] = tostring(Octa)}
Kc=1-0.75*math.pow(Octa/8,3.4) -- Factor of mitigation for the cloud layer
if sunAltitude > 1 then -- Below 1° of Altitude , the formulae reach their limit of precision.
directRadiation = RadiationAtm * math.pow(0.6,M) * sinusSunAltitude
scatteredRadiation = RadiationAtm * (0.271 - 0.294 * math.pow(0.6,M)) * sinusSunAltitude
totalRadiation = scatteredRadiation + directRadiation
Lux = totalRadiation / 0.0079 -- Radiation in Lux. 1 Lux = 0,0079 W/m²
weightedLux = Lux * Kc -- radiation of the Sun with the cloud layer
elseif sunAltitude <= 1 and sunAltitude >= -7 then -- apply theoretical Lux of twilight
directRadiation = 0
scatteredRadiation = 0
arbitraryTwilightLux=arbitraryTwilightLux-(1-sunAltitude)/8*arbitraryTwilightLux
totalRadiation = scatteredRadiation + directRadiation + arbitraryTwilightLux
Lux = totalRadiation / 0.0079 -- Radiation in Lux. 1 Lux = 0,0079 W/m²
weightedLux = Lux * Kc -- radiation of the Sun with the cloud layer
elseif sunAltitude < -7 then -- no management of nautical and astronomical twilight...
directRadiation = 0
scatteredRadiation = 0
totalRadiation = 0
Lux = 0
weightedLux = 0 -- should be around 3,2 Lux for the nautic twilight. Nevertheless.
end
if (DEBUG == 1) then
print("Station SYNOP = " .. WMOID)
print( Octa .. " Octa")
print("Kc = " .. Kc)
print("Direct Radiation = ".. round(directRadiation,2) .." W/m²")
print("Scattered Radiation = ".. round(scatteredRadiation,2) .." W/m²")
print("Total radiation = " .. round(totalRadiation,2) .." W/m²")
print("Total Radiation in lux = ".. round(Lux,2).." Lux")
print("and at last, Total weighted lux = ".. round(weightedLux,2).." Lux")
end
if tonumber(otherdevices_svalues['Lux'])+round(weightedLux,0)>0 -- No update if Lux is already 0. So lastUpdate of the Lux sensor will keep the time when Lux has reached 0. (Kind of timeofday['SunsetInMinutes'])
then
commandArray[#commandArray + 1] = {['UpdateDevice'] = idxLux..'|0|'..tostring(round(weightedLux,0))} -- THis form is not recommended. due to limitation of the eventsystem of Domoticz
end
commandArray[#commandArray + 1] = {['UpdateDevice'] = idxSolarAzimuth..'|0|'..tostring(round(azimuth,0))}
commandArray[#commandArray + 1] = {['UpdateDevice'] = idxSolarAltitude..'|0|'..tostring(round(sunAltitude,0))}
if (DEBUG == 2) then
logDebug=os.date("%Y-%m-%d %H:%M:%S",os.time())
logDebug=logDebug.." Azimuth:" .. azimuth .. " Height:" .. sunAltitude
logDebug=logDebug.." Octa:" .. Octa.." KC:".. Kc
logDebug=logDebug.." Direct:"..directRadiation.." inDirect:"..scatteredRadiation.." TotalRadiation:"..totalRadiation.." LuxCloud:".. round(weightedLux,2)
os.execute('echo '..logDebug..' >>logSun.txt') -- compatible Linux & Windows
end
end
return commandArray