For anyone interested, I had a go at this and converted the LUA script to OH rules. I combined this with the Astro binding and the Weather Underground binding. My rules file contains comments on the required items from these bindings to be able to do the calculations. Here is what I came up with:
/*
* Rules to calculate outside light lux.
* Rules are taken from Domoticz LUA script at https://www.domoticz.com/wiki/Real-time_solar_data_without_any_hardware_sensor_:_azimuth,_Altitude,_Lux_sensor...
* Authors: Sébastien Joly, Neutrino, Jmleglise
*
* Adapted to openHab rule by mherwege
*/
/* Required openHab items as input:
* Number Pressure from a weather binding or external pressure measurement
* Number Elevation current sun elevation from Astro binding
*
* Required openHab items storing results:
* Number AbsolutePressure calculated pressure corrected for altitude
* Number Okta current okta value from ogimet
* Number RadiationAtm solar radiation at entrance of atmosphere (W/m^2)
* Number TotalRadiation total solar radiation (W/m^2)
* Number Lux radiation in Lux
* Number WeightedLux radiation in Lux corrected for cloud layer
*/
val altitude = zz // your home altitude
// ogimet query parameters:
// Look for appropriate WOID here: http://www.ogimet.com/indicativos.phtml.en
// Test the query in a browser and check potential extra characters in WOID (query can return a group).
// Not all locations return required okta values.
// Also check update frequency. Some locations do not update every hour.
// Preference for a location in the predominant wind direction.
val WOID = "wwwww" // WOID for ogimet cloudiness query
rule "update okta"
when
// Retrieve data 10 minutes after the hour to increase chance data has been updated on website (usually at the hour)
Time cron "0 10/15 * * * ?"
then
// Get okta value from ogimet
var queryTime = new DateTime(DateTimeZone.UTC)
var query = "http://www.ogimet.com/cgi-bin/getsynop?block=" + WOID + "&begin=" + queryTime.toString("YYYYMMdd") + "0000"
var String result = sendHttpGetRequest(query)
logInfo("Light", "ogimet query: {}", query)
var String[] rslt = result.split("\\R")
result = rslt.get(rslt.length-1)
logInfo("Light", "ogimet response: {}", result)
rslt = result.split(",")
var String codeStation = rslt.get(0)
var vOktaLastUpdate = new DateTime(rslt.get(1) + "-" + rslt.get(2) + "-" + rslt.get(3) + "T" + rslt.get(4) + ":" + rslt.get(5) + ":00", DateTimeZone.UTC)
logInfo("Light", "ogimet last update: {}", vOktaLastUpdate)
rslt = result.split(" " + codeStation + " ")
rslt = rslt.get(1).split(" ")
var oktaStr = rslt.get(1).substring(0,1)
if (oktaStr.matches("[0-9]")) {
var vOkta = Integer.parseInt(oktaStr)
if (vOkta > 8) {vOkta = 8}
Okta.postUpdate(vOkta)
OktaLastUpdate.postUpdate(vOktaLastUpdate.toString)
}
logInfo("Light", "Okta {}", oktaStr)
end
rule "calculate absolute pressure"
when
Item Pressure changed
then
// Current pressure from binding, can be any source of pressure
var int relativePressure = (Pressure.state as Number).intValue
var int vAbsolutePressure = relativePressure - (altitude * 10 / 83) // hPa
AbsolutePressure.postUpdate(vAbsolutePressure)
logInfo("Light", "Relative Pressure {}", relativePressure)
logInfo("Light", "Absolute Pressure {}", vAbsolutePressure)
end
rule "calculate sun radiation"
when
Time cron "0 0 1 * * ?"
then
val int constantSolarRadiation = 1361 // Solar Constant W/m^2
// Sun radiation (in W/m^2) at the entrance of atmosphere
var int numOfDay = now.getDayOfYear()
var int nbDaysInYear = (new LocalDate(now.getYear(),12,31)).getDayOfYear()
var double vTemp = 2 * Math.PI * (numOfDay * 1.0) / (nbDaysInYear * 1.0)
var double vRadiationAtm = constantSolarRadiation * (1 + 0.034 * Math.cos(vTemp))
RadiationAtm.postUpdate(vRadiationAtm)
logInfo("Light", "RadiationAtm {}", vRadiationAtm)
end
rule "Calculate outside light"
when
Item Okta changed or
Item Elevation changed
then
val arbitraryTwilightLux = 6.32 // W/m^2 equal 800 Lux (the theoretical value is 4.74 but more accurate result with 6.32...)
// Sun altitude from astro binding
var double sunAltitude = (Elevation.state as Number).doubleValue
// Okta
var int vOkta = (Okta.state as Number).intValue
// Sun radiation at entry to atmosphere
var double vRadiationAtm = (RadiationAtm.state as Number).doubleValue
// Relative and absolute pressure, corrected for altitude
var int relativePressure = (Pressure.state as Number).intValue
var int vAbsolutePressure = (AbsolutePressure.state as Number).intValue
// Coefficient of mitigation M
var double sunAltRad = Math.toRadians(sunAltitude)
var double sinusSunAltitude = Math.sin(sunAltRad)
var double M1 = Math.pow(614 * sinusSunAltitude,2)
var double M0 = Math.sqrt(1229 + M1) - 614 * sinusSunAltitude
var double M = M0 * relativePressure / vAbsolutePressure
logInfo("Light", "M {}", M)
// Factor of mitigation for the cloud layer
var double Kc = 1 - 0.75 * Math.pow(vOkta/8.0, 3.4)
logInfo("Light", "Kc {}", Kc)
var double vTotalRadiation = 0
// Below 1° of Altitude , the formulae reach their limit of precision, then apply theoretical lux of twilight
if (sunAltitude > 1) {
var double directRadiation = vRadiationAtm * Math.pow(0.6,M) * sinusSunAltitude
var double scatteredRadiation = vRadiationAtm * (0.271 - 0.294 * Math.pow(0.6,M)) * sinusSunAltitude
vTotalRadiation = scatteredRadiation + directRadiation
} else if ((sunAltitude <= 1) && (sunAltitude >= -7)) {
vTotalRadiation = arbitraryTwilightLux - (1.0 - sunAltitude)/8.0 * arbitraryTwilightLux
}
TotalRadiation.postUpdate(vTotalRadiation)
logInfo("Light", "totalRadiation {}", vTotalRadiation)
var double vLux = vTotalRadiation / 0.0079 // Radiation in Lux. 1 Lux = 0.0079 W/m²
var double vWeightedLux = vLux * Kc // radiation of the Sun with the cloud layer
Lux.postUpdate(vLux)
WeightedLux.postUpdate(vWeightedLux)
logInfo("Light", "Lux {}", vLux)
logInfo("Light", "WeightedLux {}", vWeightedLux)
end