Virtual solar sensor

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

As I’m preparing to move on to OH3 I tried porting the rule to jython, it seems to be working though it’s not thoroughly tested and probably not scientifically correct but here is my attempt (use at your own risk :slight_smile: )

see updated version below.

Are you aware that direct, scattered and total radiation are already provided by the Astro binding ?

To be honest, no, this is just a very quick attempt to port the scripts I use over to jython. It would definitely cleanup the script quite a lot! thanks for the hint :slight_smile:

Updated to use radiation information provided by Astro. It looks like Astro doesn’t calculate radiation if sun altitude is below 3 degrees. I re-added ArbitraryTwilight to compensate for this. Probably not scientifically correct but it works to control my garden lights :slight_smile:

Edit: Fixed negative lux values.
oh_vlux3.py.txt (4.6 KB)

1 Like

Hey hey,

allthough all my indoor motion sensors have a brightness sensor, I want to have an absolute value for determining if it’s dark or not indipendent if my indoor lights are on or not.

My ambient light turns on either if it’s night (Astro sunset/sunrise) or if it’s dark based on this rule.

I adapted it to the new JS rule engine.

configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: System_Sonnendaten_Nacht_Gesamtstrahlung
    type: core.ItemStateChangeTrigger
  - id: "3"
    configuration:
      itemName: System_Wetter_Bewoelkung
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        // https://community.openhab.org/t/virtual-solar-sensor/24705/22


        console.log("RULE System_Wetter_Aussenhelligkeit")


        var Bewoelkung = items.getItem("System_Wetter_Bewoelkung").rawState

        var Strahlung = items.getItem("System_Sonnendaten_Nacht_Gesamtstrahlung").rawState


        //Limit

        if (Bewoelkung < 0.0) Bewoelkung = 0.0

        if (Bewoelkung > 100.0) Bewoelkung = 100.0


        if (Strahlung < 0.0) Strahlung = 0.0

        if (Strahlung > 1500.0) Strahlung = 1500.0


        // 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.85 * cloudPct^2

        // wolframalpha.com/input?i=plot+1-+0.85*x%5E2+from+0+to+1


        var Kc = 1 - 0.85 * Math.pow(Bewoelkung/100.0, 2)

        var Kc2 = 1 - 0.75 * Math.pow(Bewoelkung/100.0, 3.4)

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


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

        var Lux = Strahlung / 0.0079 


        // radiation of the Sun with the cloud layer

        var WeightedLux = Lux * Kc					

        var WeightedLux2 = Lux * Kc2


        items.getItem("System_Wetter_Aussenhelligkeit_unveraendert").postUpdate(Lux)

        items.getItem("System_Wetter_Aussenhelligkeit").postUpdate(WeightedLux)

        items.getItem("System_Wetter_Aussenhelligkeit_Alternativ").postUpdate(WeightedLux2)


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

        //logInfo("Light", "WeightedLux {}", vWeightedLux)
    type: script.ScriptAction

Have fun.

1 Like

I didn’t see this post before posting mine, but it looks like we both had the same idea. I create a rule template using javascript for the original formula. You can find it here. That should make it straightforward to configure this.

1 Like