Virtual solar sensor

sensor
scripts
Tags: #<Tag:0x00007f0144cddf88> #<Tag:0x00007f0144cdde20>

(Benoit Krings) #1

Hello,

I would like to have a virtual solar sensor like :
https://www.domoticz.com/wiki/Real-time_solar_data_without_any_hardware_sensor_:_azimuth,_Altitude,_Lux_sensor…

This is a Lua script. I don’t know if we can run a lua script with openhab or if it’s easy to translate this script.

Any help is welcome.

Regards.
Benoit


(Hallo Ween) #2

Why do you want to have this? What do you want to do with this?

Is this not already possible with only a lux sensor, astro binding and a few rules?

Is it for a better opening/closing rule for the rollershutters/blinds?


(Benoit Krings) #3

Yes it’s to automate the roller shutters.

I don’t have a physical lux sensor. This script calculate the solar exposition using sun position and cloud cover without any physical sensor
Astro binding is almost good but doesn’t use the cloud cover.

regards.
Benoit


(Hallo Ween) #4

And where do you get the cloud cover from?

Weather binding?

My shutters open/close with astro binding, but that is not the best solution for this. A lux sensor would be good to have for this, too.


(Benoit Krings) #5

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


(Hallo Ween) #6

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


(Rich Koshak) #7

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!


(Mark Herwege) #8

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

(Asbjørn Skrettingland) #9

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


(Mark Herwege) #10

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

Solar Radiation
(Hallo Ween) #11

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?


(Hallo Ween) #12

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???


(Mark Herwege) #13

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.


(Hallo Ween) #14

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.


(Ludovic) #15

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


(Mark Herwege) #16

@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.


(Ludovic) #17

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