Hi,
Here is a stripped down LUA port of the SunCalc JavaScript library by Vladimir Agafonkin (@mourner)
https://github.com/mourner/suncalc
I've done some minor modifications to the code, mainly due to the fact that the original functions returned the result in radians. Also, the result was negative or positive relative to the South direction, so I converted it to heading in degrees: 0 - North, 90 - East, 180 - South, 270 - West. I've ported just the azimuth and elevation computation (for now).
The scene updates 2 simple global variables (must be created first, names can be changed), and based on these variables, scenes can be triggered, or a VD can display the data. Tested by entering Vladimir's data (see test.js) and by knowing that today at 13:26 was solar noon at my location (180° azimuth at that time - see attached picture).
I use this scene to drive my sunshades through the day when is hot outside
--[[
%% autostart
%% properties
%% events
%% globals
--]]
--[[
Scene for computing solar azimuth and elevation, based on SunCalc project
https://github.com/mourner/suncalc
Copyright (c) 2014, Vladimir Agafonkin
All rights reserved.
--]]
local refreshRate = 60 -- update rate in seconds
local azimuthVar = 'solarAzimuth'
local elevationVar = 'solarElevation'
local debug = true
local locationInfo = api.get('/settings/location')
print('Location: ' .. locationInfo.city)
print('Latitude: ' .. locationInfo.latitude)
print('Longitude: ' .. locationInfo.longitude)
print()
local PI = math.pi
local sin = math.sin
local cos = math.cos
local tan = math.tan
local asin = math.asin
local atan = math.atan2
local acos = math.acos
local rad = PI / 180
local dayMs = 1000 * 60 * 60 * 24
local J1970 = 2440588
local J2000 = 2451545
local e = rad * 23.4397 -- obliquity of the Earth
function toJulian(timestamp) return timestamp / dayMs - 0.5 + J1970 end
function fromJulian(j) return (j + 0.5 - J1970) * dayMs end
function toDays(timestamp) return toJulian(timestamp) - J2000 end
function rightAscension(l, b) return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)) end
function declination(l, b) return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)) end
function azimuth(H, phi, dec) return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)) end
function elevation(H, phi, dec) return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)) end
function siderealTime(d, lw) return rad * (280.16 + 360.9856235 * d) - lw end
function solarMeanAnomaly(d) return rad * (357.5291 + 0.98560028 * d) end
function eclipticLongitude(M)
local C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)) -- equation of center
local P = rad * 102.9372 -- perihelion of the Earth
return M + C + P + PI
end
function sunCoords(d)
local M = solarMeanAnomaly(d)
local L = eclipticLongitude(M)
return {
dec = declination(L, 0),
ra = rightAscension(L, 0)
}
end
local SunCalc = {}
SunCalc.getPosition = function (timestamp, lat, lng)
local lw = rad * -lng
local phi = rad * lat
local d = toDays(timestamp)
local c = sunCoords(d)
local H = siderealTime(d, lw) - c.ra
return {
azimuth = azimuth(H, phi, c.dec) * 180 / PI,
elevation = elevation(H, phi, c.dec) * 180 / PI
}
end
local pos = {}
while true do
pos = SunCalc.getPosition(os.time() * 1000, locationInfo.latitude, locationInfo.longitude)
fibaro:setGlobal(azimuthVar, string.format('%.2f', pos.azimuth + 180))
fibaro:setGlobal(elevationVar, string.format('%.2f', pos.elevation))
if (debug) then
print('Azimuth: ' .. string.format('%.2f', pos.azimuth + 180) .. '°')
print('Elevation: ' .. string.format('%.2f', pos.elevation) .. '°')
end
fibaro:sleep(refreshRate * 1000)
end