[SOLVED] Unknown variable or command '-' In Rule

Really struggled with this rule and I know it should’t be this hard. I have overcome several errors to get to this point but this one I cannot figure out. At the end of the day, not sure I have the rule setup right but here goes.

It’s a countdown rule to subtract a number each time an item updates…


Number racksensor1_DLI "Grow Rack Current DLI [%.2f]"
Number racksensor1_DLI_remaining "Grow Rack Remaining DLI [%.2f]"
Number DLI_Daily "Grow Rack DLI Daily Number [%.2f]"


rule "Countdown of the DLI"
	Item racksensor1_DLI received update
    val DLI_Daily = DLI_Daily - racksensor1_DLI
 logInfo("DLI Daily Remaining", DLI_Daily.toString)	


Rule 'Countdown of the DLI': Unknown variable or command '-'; line 67, column 21, length 27

Looks to me like it doesn’t like the ‘-’ sign. I use ‘+’ in another rule just fine and found the link to the eclipse expressions and seems to be correct. Surmising that I have something else wrong but not sure what that is…



  1. You are creating a value named DLI_Daily. You then try to assign that new value the result of a calculation that uses DLI_Daily. DLI_Daily doesn’t have a value yet so DLI_Daily - racksensor1_DLI is a meaningless statement. When you create a val or var that has the same name as an Item, you completely mask the existence of the Item.

  2. It is clear that what you really want to do is set the Item DLI_Daily to it’s current state minus racksensr1_DLI’s state. To do that, you need to get the Item’s state using .state and in this case, you need to tell the Rules that the state is of type Number. You should also check that neither DLI_Daily nor racksensor1_DLI are NULL or UNDEF. But we still are not done because to set the state of an Item, you need to either sendCommand or postUpdate, you can’t just assign it to an Item using =.

Honestly, it’s not completely clear here what you are trying to accomplish. I think this might be what you are after.

    val delta = (DLI_Daily.state as Number) - (racksensor1_DLI.state as Number)
    logInfo("DLI Daily Remaining", "DLI remainging = " + delta)
1 Like

Hey Rick! I’ll explain a little further. I have been working through this piece by piece. Pretty happy I got to where I am! lol…

In any case, DLI_Daily will be a number that gets updated daily. It starts the day by getting set to, in this case, 12. I do that through a cron rule. The over the course of the day, as the racksensor1_DLI number updates, it will subtract from the 12. DLI is actually the amount of light that the plants need for the day. I use other rules to convert lux to PPFD and then to the DLI for that moment in time. All fun lighting stuff. The idea is to count down during the day, subtracting from the 12 until it gets to 0. I thought about adding up to 12 and may end up doing that but wanted to get something going. Once it gets to zero then the lights would turn off. The plants would then have enough light. Pretty cool actually. So this step was just to have the DLI_Daily be 12 and then subtract over the course of the day. I started out trying to just do that with it as variable but never got that working right. So then I went to the item.

So thats “the rest of the story”…

From your code, I need to subtract but then keep subtracting. So thats where I was trying to set that delta as the DLI_Daily and then just subtract again from it.

I have run logs and have numbers for everything.


It’s just about taking care with the objects you’re handling in rules.
Best to not to regard DLI_Daily as a number - it is an Item object, with type, label, icon, etc. properties.
In rules, you are most likely interested in its state property - the numeric part.

But the .state itself is a state object, not a simple number - after all an Item can be a String or a Switch which don’t have numeric states, it’s amorphous.

Rules interpreter tries to sort things out, and often you can treat someItem.state as though it were a simple number or string etc.
But often you need to give clear direction, (someItem.state as Number)

Review Rich’s suggestion in that light, it should make more sense.

The original error would be that rules didn’t know what to with the ‘-’ minus operator with two complex Item objects

1 Like

Thanks guys! You lead me down the right path and I was able to figure it out! I have it setup now so it counts up. I also had to divide it by 12 because my measurement comes over every 5 minutes.

I also changed the DLI_Daily to a variable. That helped me get through it. Thanks so much! This is really cool now and will help me take the plant management to a new level!

Here’s the final code…

/DLI Countdown for the Day
rule "Countdown of the DLI 2"

	Item racksensor1_DLI received update
     DLI_Daily = DLI_Daily + (racksensor1_DLI.state as Number / 12)
 logInfo("DLI counter", "DLI remaining = " + DLI_Daily.toString)	

I recommend that you move it back to an Item and then use restoreOnStartup for that Item. That way you don’t lose the current count when ever you restart OH or reload this .rules file.

Good idea Rick. I will try that out tonight.

Went back and tried to change this to an item but no joy. I tried to follow what you did @rlkoshak but didn’t get it to work.

I added:

Number racksensor1_DLI_total "Grow Rack Total DLI [%.2f]"

Redid the rule to this:

rule "Countdown of the DLI 2"

	Item racksensor1_DLI received update
     racksensor1_DLI_total = (DLI_Daily.state as Number) + (racksensor1_DLI.state as Number / 12)
 logInfo("DLI counter", "DLI total = " + racksensor1_DLI_total.toString)	

Persisted everything and ran a startup script to set everything to 0.

Then I get this…

Rule 'Countdown of the DLI 2': An error occurred during the script execution: Cannot assign a value in null context.

Couldn’t really find anything on the boards that helped.

When you define a variable you must put a “val” (constant) or “var” in front to indicate to the engine what your are doing.

val racksensor1_DLI_total = (DLI_Daily.state ...

Okay, that’s an Item.

No. You should postUpdate values to Items.

No. If you want to send the value of Item racksensor1_DLI_total, it is the .state that you want to send.

But there is a bigger problem ; you have only just postUpdated a new value to it, and postUpdate is asynchronous. That is, the rule does not stop and wait for it to happen, and it takes a few milliseconds.
If you try to use it again in the same rule, you might get old or new value, who knows.

But that’s okay, your rule knows what value to send because you’ve just worked it out. Let’s rearrange a bit so that you can use it in two places (I have to say I’m not really sure why you are sending the same value to two Items)

 var xx = (DLI_Daily.state as Number) + (racksensor1_DLI.state as Number / 12)
 logInfo("DLI counter", "DLI total = " + xx.toString)	

I note that for some reason you have changed the last line from a postUpdate to a sendCommand, I don’t know why, but you do.

rossko57 you are awesome and the best troubleshooter this community has. Hats off to you


Well, I keep trying but still not there. Based on what you have sent I tried to do some cleanup so it would be less confusing. I get a number for DLI total but it is just the same as racksensor1_DLI_total.

In any case, this is where I am now. Each of these are now setup as items. No variables. I got rid of racksensor1_DLI_remaining that was confusing now since I am now counting up not down. For the XX I created DLI_Daily_Counter which made more sense for what I was trying to do.

     val DLI_Daily_Counter = (DLI_Daily.state as Number) + (racksensor1_DLI.state as Number / 12)
	 logInfo("DLI counter", "DLI total = " + DLI_Daily_Counter.toString)	

This is what happens when the rule fires:

2019-11-20 18:35:21.532 [INFO ] [e.smarthome.model.script.DLI counter] - DLI total = 0.04883721

2019-11-20 18:35:21.538 [vent.ItemStateChangedEvent] - racksensor1_DLI_total changed from 0.04823577 to 0.04883721

It doesn’t add to the DLI_Daily_Counter.

Hope this is closer!


No, it wouldn’t.

This creates a new variable called DLI_Daily_Counter.
It’s a dumb computer, and it does exactly what you tell it to.
It does not care if you already have an Item called DLI_Daily_Counter, it goes right ahead and makes a new variable, and puts the results into it.
For the remainder of this rule, reference to DLI_Daily_Counter will point to this variable.
At the end of the rule, your new variable evaporates.

Important : variables are not Items. Items are not variables.

Okay, so I guess you don’t want to create a new variable, you want to use the pre-existing Item? Then don’t do the val - which declares a new variable.

DLI_Daily_Counter = ...
then? No, that is no way to treat an Item as we already discussed.

DLI_Daily_Counter.postUpdate( your maths )
would do the trick.

Let’s see if that works.

     DLI_Daily_Counter.postUpdate( (DLI_Daily.state as Number) +(racksensor1_DLI.state as Number / 12) )
     logInfo("DLI counter", "DLI total = " + DLI_Daily_Counter.toString)		

Nope, that’s all going to go horribly wrong.
First line is fine.

Second line tries to postUpdate the whole DLI_Daily_Counter Item object - with label, icon, type, group memberships, etc etc, to racksensor1_DLI_total state. It’s not going to fit.

As already discussed, it’s generally the Item state of an Item that you are interested in - not its label, icon etc.
Sooo …
would be better.

Now we run into a non-obvious problem. We just only postupdated a new value to the DLI_Daily_Counter Item in the previous line.

I will guarantee that in that second line, your postUpdate in the first line will not yet have taken effect. You’ll get the old value of the state, not what you want?
What can we do?

Well you know exactly what you just postUpdated to DLI_Daily_Counter, you don’t need to read it back from the Item at all.
Only problem is, you didn’t make a note of what that was. You could do the math and work it out again, but that’s dumb.
Let’s just make a note by storing the value in a number variable.

        // this is a temporary store for the maths result
     val Number xxx = (DLI_Daily.state as Number) +(racksensor1_DLI.state as Number / 12)
        // now we can use the result
        // so can the third line
         // so can the logging
     logInfo("DLI counter", "DLI total = " + xxx.toString)	
         // now the xxx variable will be automatically discarded, we don't need it anymore	

I still don’t understand why you post the same value to two Items.

1 Like

Great stuff @rossko57! I didn’t have to stay after school very much but this was one that got me detention.

DLI_Daily_Counter = ...
then? No, that is no way to treat an Item as we already discussed.

So I got it working! Yeah! But…

Found and issue…

My DLI_Total went up like it should…

2019-11-20 21:41:37.212 [INFO ] [e.smarthome.model.script.DLI counter] - DLI total = 0.03498476

2019-11-20 21:46:41.296 [INFO ] [e.smarthome.model.script.DLI counter] - DLI total = 0.04835605

2019-11-20 21:51:49.484 [INFO ] [e.smarthome.model.script.DLI counter] - DLI total = 0.04654691

2019-11-20 21:56:52.276 [INFO ] [e.smarthome.model.script.DLI counter] - DLI total = 0.04818765

Until it got to 2019-11-20 21:51:49.484. Then it went down. What happened?
this happened:

racksensor1_DLI changed from 0.580272654384 to 0.558562951080

My DLI went down so it decided to make it a negative in the formula. At least that’s what I am assuming. It went back up in the next one so DLI_Total went back up.

racksensor1_DLI changed from 0.558562951080 to 0.578251804320
DLI total = 0.04818765

How to force it to always add?

And thanks so much for all the detail. Always interesting to understand how the code is actually working, yeah I said that. Also, I took out the annoying second item!


I don’t really understand your maths or the double Item update, just been fixing rules. What are you trying to achieve?

I am a farmer and am trying to track light levels to determine supplemental light needed. End game will be to understand the amount of light that the plants receive and then adjust lighting accordingly. Eventually automatically. The amount of light they need is called DLI or Daily Light Integer. Different plants have different DLI requirements. What I grow needs a 12 DLI a day.

I collect a light level for the plants (LUX). I convert Lux to PPFD - Photosynthetic Photon Flux Density with one rule. I then run a rule that converts that PPFD to a DLI equivalent. That reading comes in every 5 minutes but the DLI equivalent is based on an hourly number so I then use the rule we have been working on to divide it by 12, collect each reading over the course of the day and add it up to 12 or whatever it ends up being for right now. I reset the rule we worked on to 0 at night and start over. Eventually it would count to 12 and turn off/on lights as needed to make sure the plants get enough light.

So here is whole enchilada. I am sure there was a way to consolidate but it was just one step at a time getting here.

//Light Conversions

rule "Convert Lux to PPFD - Photosynthetic Photon Flux Density"
	Item racksensor1_LightIntensity received update
	var Number current =  racksensor1_LightIntensity.state
	var Number currentInPPFD1 = (current as DecimalType / 62.35)
	logInfo("PPFD", "racksensor1_LightIntensity received update: " + current + " Lux = " + currentInPPFD1 + " PPFD.")

//Current DLI for the Day

rule "Current DLI for the Day - Daily Light Integral"
	Item racksensor1_PPFD received update
	var Number current =  racksensor1_PPFD.state
	var Number currentInDLI1 = (current as DecimalType * 0.0036)
	logInfo("Daily DLI1", "racksensor1_PPFD received update: " + current + " PPFD = " + currentInDLI1 + " DLI.")

//DLI Countdown for the Day

rule "Countdown of the DLI 2"

	Item racksensor1_DLI received update
	    // this is a temporary store for the maths result
     val Number xxx = (DLI_Daily.state as Number) + (racksensor1_DLI.state as Number / 12)
	    // now we can use the result
	    // so can the logging
	 logInfo("DLI counter", "DLI total = " + xxx.toString)	

rule "Check the DLI"


	Item DLI_Daily_Counter_remaining received update
	if (DLI_Daily_Counter_remaining.state >= 12) 
	sendNotification("email@gmail.com", "DLI Limit is reached.")

rule "DLI Reset"
   Time cron "	0 0 1 1/1 * ? *"
//  Time cron " 0 40 9 1/1 * ? *"
    DLI_Daily.postUpdate(0) //This is a item
	DLI_Daily_Counter.postUpdate(0) //This is a item

Everything works great up to the counting part. So what is happening, that light level (LUX) varies of course so it might go up and down. But in every case you still want the number to be a positive and count up.

The calculation does always add. The problem is you received a negative reading for some reason. You need to figure out when/where/why you got a negative value because that is almost certainly not what you want. It doesn’t make sense to have a value less than 0 IIUC.

I don’t want to know what the crop is.

That’s the way to go. You have a complex sausage machine to create here, and will need to craft individual parts for it. But we should be prepared to change the design in the light of experience, that’s just development :smiley:

Okay, job is to calculate daily irradiation from a lux sensor.

It’s not clear if you need a rolling hourly average or daily somethings or what, say for graphing?
If you do, it is probably worth invoking use of persistence (“database logging”) in your overall design sooner rather than later.
You’d only need that for graphing - but it can also be exploited for the daily stuff.

If you only want today’s daily total figure, we can just do it with rules. But I suspect with an eye to the future this sounds like a job for databases and graphing, though?

One possibly important thing - when do you need to know the day’s result? I’m guessing you plan to extend daylight hours by turning on artificial lights? (so having today’s result at midnight is useless).
Now might be the time to plan how that would happen in practice.
Maybe if lux falls below a certain value AND today’s total not yet over some threshold, turn lights on (we don’t care about the time of day or anything.)
When today’s total crosses the threshold, lights off.
Right approach??
If you want pre-dawn boosting based on a prediction, that is more complex. (but today’s prediction can be yesterday’s reality)

Okay, that’s all general stuff. To the specifics of existing rules …

How reliable / accurate is this -

I wouldn’t trust it, myself. I’d be inclined to just let the sensor report when it likes, do the scaling on it, and keep a continuous approximately current “light level” Item.

The 5-minute thing (or as we choose, any other convenient period) can be carried out by a cron triggered rule (clock based)

I think I understand what your xx_Counter thing is supposed to do, but can’t see where it does any counting. Perhaps by accident it gets the running total?

I actually think you don’t need any counter here.

A possible general scheme:

** Have an Item for a running total irradiation. Have that restore-on-startup so a reboot doesn’t ruin your day.
** Keep the rule that resets total at midnight
** perhaps have that rule report or record today’s score.
** Keep the rule that scales lux to DLI equivalent, run every 5 mins.
** Also in that rule, add DLI/12 to running total Item

That’s it, no counting. Lighting actions can be added on to this later.

1 Like

Hey @rlkoshak, I think that is correct.I think it’s in the code before. The reading goes from a higher number to lower so assume its subtracting instead of adding even though that is a valid number. So yes, there is something in my math me thinks.

2019-11-26 07:28:22.254 [vent.ItemStateChangedEvent] - racksensor1_DLI changed from 0.557408179620 to 0.320044907772

2019-11-26 07:28:22.284 [vent.ItemStateChangedEvent] - DLI_Daily_Counter changed from 0.04645068 to 0.02667041

Lol, yeah, sadly just just leafy greens and herb seedlings and microgreens for now :slight_smile:

Yes, I that setup. Did that right off the bat.

Correct. That is what I am working towards.

It’s a miflora (for now) that collects every five minutes. It’s nothing that I set, it just sends the mqtt data.

It does count but as @rlkoshak noted it turns up a negative value during some of the readings. I think there is a math issue somewhere in one of the rules.