Virtual solar sensor

The script for domoticz use the Wunderground API to get cloud cover.

I think you can build this yourself with weather binding and a few rules. But with this script it would be much easier.

This looks really interesting. I wish I had time to look into it more deeply. It does look like something that could be implemented in a Rule getting the needed cloud cover data from the Weather binding and Astro binding. It looks like it only uses the relative pressure from wunderground and gets cloud information from http://www.ogimet.com. Site is in Spanish I think, does anyone know if it works outside Europe?

I do know you can execute scripts using the Exec binding and/or executeCommandLine actions. However, I’m no Lua expert, but it looks like this particular script would have to be modified so it only logs out the value or the API calls at the end changed to openHAB API calls.

I really wish I had the time to do more than lay out this rough outline of the approach. Good luck!

1 Like

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
6 Likes

I will have to try out the OH rule adaptation of this script. It looks awesome as you would not need LUA and other setup.

One suggestion would be to use variables instead of static device names, that way we would not be forced to have the same names as you. Of course the rule can be edited to have different device names, but it would be easier with variables in the beginning of the rule file.

There is a version of the lua script available on a Norwegian home automation forum, it works with OpenHAB1, OpenHAB2 , Homeseer and Domoticz. I have been using that for the last 5 months.

Forum post
Download link

I have been able to make this much simpler by using the new Synop Analyzer binding and the Radiation channel in the Weatherunderground 2.0 binding. This is the net result of it:

/*
* Rules to calculate outside light lux.
* Basic idea 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
* 23-Jun-2017	Modified to use Okta from Synop Analyzer binding and Radiation from Astro binding
*				These bindings already do most of the queries and calculations
*/

/*
* Required openHab items as input:
* 	Number Okta					current okta value from Synop Analyzer binding
*	Number TotalRadiation		current total solar radiation from Astro binding
* 
* Required openHab items storing results:
* 	Number Lux					radiation in Lux
* 	Number WeightedLux			radiation in Lux corrected for cloud layer
*/
 
rule "Calculate outside light"
when
	Item Okta changed or
	Item TotalRadiation changed
then
	// Okta
	var int vOkta = (Okta.state as Number).intValue
	var double vTotalRadiation = (TotalRadiation.state as Number).doubleValue

	// 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 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
3 Likes

Please can you tell me how to set up the synop binding?

I installed it, but what do i have to do next? Where do i get the synop station number?

And which wunderground binding? Is this the WeatherUnderground Binding from the market?

I have another idea to get a virtual solar sensor, can someone help me with this?

I have photovoltaic panels on my roof. So now i have the radiation value from astro-binding and the powervalue from the photovoltaic-power-meter.

Can i calculate something with this two values? Maybe if radiation should be high, but there is only very small power output, then the chance is high, that it is a cloudy day?

But i don´t have an idea, how i can calculate this???

The WeatherUnderground Binding is indeed the one from the Eclipse marketplace. I expect it will be merged in openHAB in the future. It works perfectly as is though.
You can look for a WOID here or here. Look for a location that is close and upstream from the main wind direction. Not all locations update every hour, so pick one with regular updates.

I do use photovoltaic power as a trigger to switch off some lights myself. It is just a question of finding the right power level to switch off. I don’t even think you need the radiation as an input for that. You could try graphing radiation, octa, photovoltaic power and cloudiness for a while and look for a correlation. The advantage of this is that it definitely correlates to the light level at your specific location.

Ok, i made some new items and a chart to get the info in the next few days.

I also wanted to make an average calculation to prevent my blinds to open on every little cloud on the sky.

I now have this items:
solar-power
radiation
calculation-item --> (solar-power / radiation)
10min average of calculation-item

The calculation-item is to get better values on morning and evening.

Which average would be a good value for automatic stop of the shading? I think 10 minutes is to low?

I think your rule with the data from a weatherstation is not accurate enough. Next weather station is 25km away from my home. And i only get data in a 1 hour interval. Not good for partly cloudy days.

When i have all this information, the bigger part will begin… Include inside temperatur, azimuth/elevation, window directions, time (clock), time (season), opened windows and current sun-radiation/brightness into a rule.

Hi,

Thanks @Mherwege for the rules. As I have OpenHab 1.8, it didn’t run properly so I had to adapt it a bit. It works well, meaning there is data recorded, but I am not sure if the result is the one expected:
chart

The green curve shown in the graph above is the results from your rules during a sunny day with clouds appearing occasionally.

From the rules, there is a formula to determine the total radiation depending on sun altitude. I understand that this causes a disruption in the value of the total radiation at sunrise and sunset (just before 22:00 and just after 6:00 in the graph above).

My question is: is such a disruption from 40 klux to less than 1000 lux at sunrise and sunset expected? Or did I mess up when I adapted the rules to my version of OpenHab?

Cheers,

Ludovic

@lolu I don’t have the original version of the rule running anymore. I use the simpler version using data from the Astro and Weather Undeground bindings, see above. This does not correct for pressure anymore, but that effect was minimal anyway. Even with these formulas, I see a very similar difference, but the break is not as abrupt. Note that in my case the radiation is taken from the Astro binding and the break for sun below 1 degrees is therefore in the binding code.
I copied the formulas from the Domoticz ones and left the comment about accuracy around the break point in. You might want to see what happens if you remove the break and just put it at 0 when sun is below -7 degrees. Your curve might be smoother. Also, try plotting the sun elevation. My rules assume this to be updated by the Astro binding. If this is not frquent enough, it may also rxplain the abrupt break.

Hi @Mherwege,
Sorry for the late reply (I just saw your post) and thanks for your answer.
I will try to remove the break for the sun between -1 ° and -7 °.
Also, I will plot the sun elevation. In the mean time, I reduced the refresh period of the sun elevation from 600 s to 35 s.

Cheers,
Ludovic

Sorry to take up this old tread - but this is just a fun exercise, and I might be able to use it to adapt my own scene lightning.
However, that Kc formula seems weird. I used an hour or two trying to google the effect of cloud cover on sun radiation, and came up with very little. I did find a use or two of the above formula but with no references to its source. This is probably because clouds comes in all kinds of forms with each their on influence on the radiation, so no simple formula is really useful.

But still, you do not have to google much before finding tables like:
Cloudless: 100.000 lux
Overcast: 1.500 lux
That goes quite well with my own subjective experience - it really gets a lot darker when overcast :wink: and the formula above does not at all reflect that.

I will try some experiments with an adapted formula: 1 - 0.95 * cloudPct^1.6

Also, I am getting a cloudPct from OpenWeathermap.

I will write again if I think my formula seems useful after some weeks, but anyway I suggest you think twice before using the original formula :wink:

Hi,

Thanks for the information. I would be happy to have an update.
The rule I use is now broken. I don’t I have time to try to find out why unfortunately.

So now I am just looking at the time of the day to switch on the lights.

Do you plan to compare the results of your adapted equation to the one given in this thread? That would be very interesting.

Cheers,

Ludovic

Will do. It will probably take some weeks to see anything interesting, as we are having some sunny days here right now :wink:
I can however already see that the radiation based lux by it self is quite good to get an rough idea of when it is dark’ish or not, so I’ll see if it can be better with adjustments for clouds.
I don’t like to much automation for my lights, but I am going to try it out for adjusting some ambient lights in my light scene setup.

I’ll be back :slight_smile:

Hi,

I never thought I would say something like this: too bad you have sunny days…:slight_smile:

As opposed to you, I like the light automation, especially in the living room. When my rule was not broken, the light used to come up gradually starting latest at the sunset time for a sunny day and earliest 2 hours before for a day with heavy clouds. As you would guess, it was mainly occurring in between.
I found it to be the most useful automation as we always had enough luminosity in the living room at the end of the afternoon without doing anything.

On the other hand, I think that it would be quite challenging to regulate the ambient light brightness according to the outside luminosity when it is based on a virtual sensor. But it is fun to embrace such a challenge and I would be very interested to know how you come up with it.

Cheers,

Ludovic

The approach seems to work well!

The original formula is not as bad as I thought, and the formula I tried seems to underestimate the amount of light.

I will try with 1 - 0.85 * cloudPct^2

This is my new rule:

// adapted from https://community.openhab.org/t/virtual-solar-sensor/24705/10
rule "Calculate outside light"
when
	Item Weather2_Now_Cloudiness changed or // from OpenWeatherMap binding
	Item Astro_Sun_TotalRadiation changed   // from Astro binding
then

	// Factor of mitigation for the cloud layer
	// a "common" formula is 1 - 0.75 * cloudPct^3.4, which is used in the referenced openhab community article
	// however, it is also common to see a table with 100.000 lux on cloudless day and 1.500 lux on overcast days - that goes with my own subjective experience
	// I use an adapted formula dreamt up by my self: 1 - 0.95 * cloudPct^1.6
	var double Kc = 1 - 0.85 * Math.pow(((Weather2_Now_Cloudiness.state as DecimalType).intValue)/100.0, 2)
	var double Kc2 = 1 - 0.75 * Math.pow(((Weather2_Now_Cloudiness.state as DecimalType).intValue)/100.0, 3.4)
	
	//logInfo("Light", "Kc {}", Kc)

	// Radiation in Lux. 1 Lux = 0.0079 W/m²
	var double vLux = (Astro_Sun_TotalRadiation.state as DecimalType).doubleValue / 0.0079 
	
	// radiation of the Sun with the cloud layer
	var double vWeightedLux = vLux * Kc			
	var double vWeightedLux2 = vLux * Kc2			

	Astro_Sun_Lux.postUpdate(vLux)
	Weather2_Sun_Lux.postUpdate(vWeightedLux)
	Weather2_Sun_Lux2.postUpdate(vWeightedLux2)
	
	//logInfo("Light", "Lux {}", vLux)
	//logInfo("Light", "WeightedLux {}", vWeightedLux)
end

I have chosen to only turn on my main lights manually. But I turn on some ambient additionally lights based on the sun weighted lux. Depending on the placement in the house I turn them on at lux values ranging from 18K (the darkest parts of the house) to 5K. It works quite well :slight_smile:

This is amazing formula could be very useful I’m a bit of a newbie I don’t understand the basic wording of this language yet mostly go by trying and see what happen. I Had this particular code working on my side but had to bring the following change to it, no sure why! Converting to DecimalType was’t working I had to use QuantityType unsure why. And the postUpdate wasn’t doing anything so I changed it to sendCommand and it start showing in BasicUI here’s you code wuth the change to make it work on my side.

Blockquote
// adapted from Virtual solar sensor - #10 by Mherwege

rule “Calculate outside light”

when

    Item localCurrentCloudiness changed or // from OpenWeatherMap binding

    Item Total_Radiation changed   // from Astro binding

then

    // Factor of mitigation for the cloud layer

    // a "common" formula is 1 - 0.75 * cloudPct^3.4, which is used in the referenced openhab community article

    // however, it is also common to see a table with 100.000 lux on cloudless day and 1.500 lux on overcast days - that goes with my own subjective experience

    // I use an adapted formula dreamt up by my self: 1 - 0.95 * cloudPct^1.6

    var double Kc = 1 - 0.85 * Math.pow(((localCurrentCloudiness.state as QuantityType<Number>).intValue)/100.0, 2)

    var double Kc2 = 1 - 0.75 * Math.pow(((localCurrentCloudiness.state as QuantityType<Number>).intValue)/100.0, 3.4)

    

    logInfo("Light", "Kc {}", Kc)

    // Radiation in Lux. 1 Lux = 0.0079 W/m²

    var double vLux = (Total_Radiation.state as QuantityType<Number>).doubleValue / 0.0079 

    

    // radiation of the Sun with the cloud layer

    var double vWeightedLux = vLux * Kc                     

    var double vWeightedLux2 = vLux * Kc2                   

    Astro_Sun_Lux.sendCommand(vLux)

    Weather2_Sun_Lux.sendCommand(vWeightedLux)

    Weather2_Sun_Lux2.sendCommand(vWeightedLux2)

    

    logInfo("Light", "Lux {}", vLux)

    logInfo("Light", "WeightedLux {}", vWeightedLux)

end

I tried this rule but it is soooooo slow. I think its because of the Math.pow() argument.

Can someone help me?

I use OH 3

and I get this error:

2021-01-29 19:10:26.736 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID ‘shutters-1’ failed: An error occurred during the script execution: null in shutters