-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsolardata.dzevents
217 lines (188 loc) · 12.1 KB
/
solardata.dzevents
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
--[[
Prerequisits
==================================
Requires Domoticz v3.8551 or later
Platform independent
CHANGE LOG: See http://www.domoticz.com/forum/viewtopic.php?t=19220
Virtual Lux sensor and other real-time solar data
-- Authors ----------------------------------------------------------------
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 - xces - UTC time calculation.
V2.0 - BakSeeDaa - Converted to dzVents and changed quite many things.
V2.4.1-DarkSky - oredin - Use Dark Sky API instead of WU API
]]--
-- Variables to customize ------------------------------------------------
local city = 'Paris' -- Only for log purpose
local countryCode = 'FR' -- Only for log purpose
local idxSolarAzimuth = nil -- (Integer) Virtual Azimuth Device ID
local idxSolarAltitude = nil -- (Integer) Your virtual Solar Altitude Device ID
local dsAPIkey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' -- Dark Sky API Key
local logToFile = false -- (Boolean) Set to true if you also wish to log to a file. It might get big by time.
local tmpLogFile = '/tmp/logSun.txt'-- Logging to a file if specified
local fetchIntervalDayMins = 15 -- Day time scraping interval. Never set this below 4 if you have a free WU API account.
local fetchIntervalNightMins = 30 -- Night time scraping interval.
-- Optional Domoticz devices
local idxLux = nil -- (Integer) Domoticz virtual Lux device ID
local idxCloudCover = nil -- (Integer) Domoticz Cloud Cover (PERCENTAGE TYPE) sensor device ID
local idxPressure = nil -- (Integer) Your Pressure Device ID. if you don't have one, the pressure value will be retrieve from Dark Sky API
-- (You don't have to configure these if you accept the values returned by the weather observation station)
local latitude = nil -- Latitude. (Decimal number) Decimal Degrees. E.g. something like 51.748485
local longitude = nil -- Longitude. (Decimal number) Decimal Degrees. E.g.something like 5.629728.
local altitude = nil -- Altitude. (Integer) Meters above sea level.
-- Please don't make any changes below this line (Except for setting logging level)
local scriptName = 'solarData'
local scriptVersion = '2.4.1-DarkSky'
return {
active = true,
logging = {
-- level = domoticz.LOG_DEBUG, -- Uncomment to override the dzVents global logging setting
marker = scriptName..' '..scriptVersion
},
on = {
timer = {
'every '..tostring(fetchIntervalDayMins)..' minutes at daytime',
'every '..tostring(fetchIntervalNightMins)..' minutes at nighttime',
},
httpResponses = {
scriptName..'DS',
},
},
data = {
lastOkta = {initial=0}
},
execute = function(domoticz, item)
if item.isTimer then
local url = 'https://api.darksky.net/forecast/'..dsAPIkey..'/'..latitude..','..longitude..'?exclude=minutely,hourly,daily,alerts,flags&lang=fr&units=ca'
domoticz.log('Requesting new weather data from Dark Sky API...', domoticz.LOG_DEBUG)
domoticz.log('URL used: '..url, domoticz.LOG_DEBUG)
domoticz.openURL({url = url, method = 'GET', callback = scriptName..'DS'}).afterSec(10)
end
if not item.isHTTPResponse then return end
local response = item
if response.trigger ~= scriptName..'DS' then return end
domoticz.log('Dark Sky API json data has been received', domoticz.LOG_DEBUG)
local function leapYear(year)
return year%4==0 and (year%100~=0 or year%400==0)
end
local dsAPIData = response.json
if not dsAPIData then
domoticz.log('Could not find any dsAPIData in the DS API response', domoticz.LOG_ERROR)
return
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²
-- In case of that latitude, longitude and altitude has not been defined in the configuration,
-- we simply use the values that is returned for the current observation location.
-- Reading longitude, latitude and altitude from the observation_location instead of from
-- display_location. API documentation is not so clear about what display_location is.
if not latitude then
domoticz.log('You must change the latitude value !', domoticz.LOG_ERROR)
return
end
if not longitude then
domoticz.log('You must change the longitude value !', domoticz.LOG_ERROR)
return
end
if not altitude then
domoticz.log('You must change the altitude value !', domoticz.LOG_ERROR)
return
end
local relativePressure = dsAPIData.currently.pressure -- if you have an another way to get the Pressure, (local barometer ...) then you may optimize the script
if idxPressure and domoticz.devices(idxPressure).barometer then
relativePressure = domoticz.devices(idxPressure).barometer
end
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 azimuth = 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 azimuth=360-azimuth 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 + ((614 * sinusSunAltitude)^2)) - 614 * sinusSunAltitude
local M = M0 * relativePressure/absolutePressure
domoticz.log('', domoticz.LOG_INFO)
domoticz.log('============== SUN LOG ==================', domoticz.LOG_INFO)
domoticz.log(city .. 'latitude: ' .. latitude .. ', longitude: ' .. longitude, domoticz.LOG_INFO)
domoticz.log('Home altitude = ' .. tostring(altitude) .. ' m', domoticz.LOG_DEBUG)
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 = ' .. azimuth .. '°', 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)
local okta = dsAPIData.currently.cloudCover
-- We store the last fetched value here to be used as a backup value
domoticz.log('Using the newly fetched cloud cover value: '..okta..' with UTC timestamp: '..dsAPIData.currently.time, domoticz.LOG_DEBUG)
domoticz.data.lastOkta = okta
local Kc = 1-0.75*(okta^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 * (0.6^M) * sinusSunAltitude
scatteredRadiation = RadiationAtm * (0.271 - 0.294 * (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
domoticz.log('Okta = '..okta, 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_DEBUG)
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
domoticz.devices(idxSolarAzimuth).updateCustomSensor(domoticz.utils.round(azimuth,0))
domoticz.devices(idxSolarAltitude).updateCustomSensor(domoticz.utils.round(sunAltitude,0))
local oktaPercent = domoticz.utils.round(okta*100)
local fetchIntervalMins = (domoticz.time.matchesRule('at daytime') and fetchIntervalDayMins or fetchIntervalNightMins)
if idxCloudCover and ((domoticz.devices(idxCloudCover).percentage ~= oktaPercent)
or (domoticz.devices(idxCloudCover).lastUpdate.minutesAgo >= (60 - fetchIntervalMins))) then
domoticz.devices(idxCloudCover).updatePercentage(oktaPercent)
end
if logToFile then
local logDebug = os.date('%Y-%m-%d %H:%M:%S',os.time())
logDebug=logDebug..' Azimuth:' .. azimuth .. ' Height:' .. sunAltitude
logDebug=logDebug..' cloud cover:' .. 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
}