Confusing about 1 and 1.0 in rules

Well, both are working workarounds, but for „normal users“ it shouldn’t make a difference I think.

If new rule engine starts such behavior need to be explained.

So: will it be like this alltTime? Then I would use a workaround for me while migrating to python. Or is there a change will be changed in core for better usability?

Because you never know exactly if it’s 17.0 or 17 for example with temperatures you always have to build rules for both variations.

If calculating with values, it depends on details of code which results you get. Shouldn’t be tricky like that :wink:

It’s not to do with rules, it is a quirk of the java BigDecimal type which is essentially what Number type Items use.
BigDecimal has internally separate “scale” and “value” parts, which is why 2 and 2.0 don’t appear exactly equal. 2.00 would be different again.

The oddity with persistence is about whichever database you chose using its own number system - OH state has to be converted to whatever for storage and back again to BigDecimal. Cleary something gets lost in the process with some of the persistence services.

In a great many rules it doesn’t really matter, e.g. 3 > 2 and 3 > 2.0 both work.

In “real” java, there is a compareTo method that does regard 2 and 2.0 as “same value”. .I’ve no idea if that is available in jython or whatever, but I doubt DSL rules == will be changing now.

If it helps for development: I’m using jdbc:mysql as persistence service with a classic “old school” mysql Database. There the Value is shown as 3 (for example) - If it helps.

What would you suggest? Using everywhere Values like 2.0 or use the workaround?

Edit: Thanks for your time and an qualified answer! :slight_smile:

Could you provide some examples of where this is an issue?

First the already mentioned status item, should normally have int values like 1,2,3,4 as written first got wrong reload after restart from persistence (checked)

Other examples I have to take a deep look. In dsl rules i know it took a long time to get calculations for temperatures ( changes every item.state to number, then float, then calculate, then back to number to store again), but I really have to look. If i remember right: an item with should be temperature, an item with decrease temperature difference if away. First 20.5 degrees second 2.0 degrees for example. If away it should set device temperature to 20.5-2.0 = 18.5 degrees. Things like that (spontan out of my brain) only worked like
((item.state as number).floatValue - (item2.state as number).floatValue) as number)

It’s just an example, because I really have to look and no chance to paste the rules now from phone :wink:

I opened this now, because got same during migration to py and don’t like to spend again so much time to test changing variable type instead of just calculate with plus or minus :wink:

Just think it’s one reason for so much support for rule syntax and if don’t have a deep knowledge of variable types and objects it’s not really comfortable for users.

Edit: in DSL I used for status everywhere 1.0,2.0,3.0,4.0 because of persistence as work around. (First reason I opened here)

If I try an If in py: if item.state == 4 not working, == 4.0 not working and DecimalType(4) not working until I go back to values like 1,2,3,4 but then got persistence problem again.
Item.state here please as an example, had to look correct syntax first, but took right in py.

Yes, I would agree. The problem arises when we use a Number type as “multistate switch” - say Fan Speed 0 1 2 3

Typical usage in a rule is then something like
if( state == 2) do whatever or switch-case equivalents
and that fails if state is 2.0
Usually it doesn’t happen, because we set 0 1 2 3 in rules or mappings or blah.

But at least some of the persistence services will store “3” but restore “3.0” due to internal processes.

When you get a chance to, post some actual examples of your DSL rules that you’d like to migrate to Jython, or some Jython rules that you are having difficulty with. It will be much easier to understand what you are trying to say when looking at code. But it sounds as though you are making things more difficult than they have to be.

From your first post:

You are probably using rrd4j or some other persistence service that initializes to float after restart. Use mapdb persistence for these types of things if you don’t need history (see the 3rd post that @rossko57 included.

Here an example - sniplet how I need to do calculations in DSL so it works:

// LC-13 EG-Flur
// Nur, wenn Nachts Raum kälter als Tagessoll-Temperatur
if (EG_Flur_Sensor_Temperatur.state <= Pref_EG_Flur_Soll_Temp.state) {
if (currentHour != 6) { UpMin=1 }
else { UpMin=(Pref_EG_Flur_Soll_UpMin.state as Number).intValue }

logInfo("11_Status_Heat-Control-Home.rules", "EG-Flur auf " + Pref_EG_Flur_Soll_Temp.state + " in " + UpMin.toString + "Min.")
createTimer(now.plusMinutes(UpMin )) [|

// Falls sich bis zur Uptime der Modus geändert hat
    if (Status_Home.state == 1.0) {
	logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Flur jetzt erhöht")
	EG_Flur_Thermo_Soll_Temp.sendCommand(Pref_EG_Flur_Soll_Temp.state as Number) 
    } else if (Status_Home.state == 3.0) {
	logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Flur jetzt erhöht (Abwesend)")
	help=((Pref_EG_Flur_Soll_Temp.state as Number) - (Pref_Away_Soll_Decrease.state as Number)).doubleValue
	EG_Flur_Thermo_Soll_Temp.sendCommand(help) 
    }
]
}

As you the: Minutes calculation needed some transformations so it worked, same ab calculating with temperatures.

I tried to avoid this help variable too, using calculation directly in sendCommand, but when I tried: It didnt work. But that had been during daily SNAPSHOTS while the community created 2.5.0 :slight_smile:

Thanks for your reply. That hat been the original problem why I opened this thread. Is there a Chance, the persistence services will get changed in this point?

No. They’re still essentially OH1 and not likely to change for OH2. I’d have doubts its regarded as an urgent matter for yet-to-come OH3 either, and more doubts that there is any easy fix (because different databases use different storage methods).

It’s only a case of writing rules that don’t care, and only then for equalities.

It’s tedious but not difficult

if (Status_Home.state == 1.0 || Status_Home.state == 1) {

Don’t forget if you have a chain of if()s or a switch-case you can use < operator

if (x == 0 || x == 0.0) {   // case 0
     // code
else if (x < 2 ) { //  case 1 or 1.0 or 0.99999
    ...

:wink: I just got ready with it when reading your reply.

For me it’s fine, I found a lot ways for workaraounds. Just thinking about should be easier.

Same as in if will be needed in all Triggers for rules. So I have to add this in triggers too.

Does DSL support combined when again? Had been an issue with it some earlier, hadnt it?

But any value restored-on-startup won’t be triggering any rules.

The only case I can think where it matters is an “old” value

when
   Item fred changed from 1 to 2 or
   Item fred changed from 1.0 to 2

But how would I do in Jython?

if items.Status_Home == DecimalType(4):

only true vor value 4 and not for 4.0, right?

For triggers can I use just two when in rules?

@when ("Item Status_Home == 4.0")
@when ("Item Status_Home == 4")
def .....

It does look like this could be simplified, but I can’t be much help without seeing the full rule and Item definitions. Do you have a Jython version, which will be much easier?

You don’t need to worry about the decimal in Jython. DecimalType(5) == DecimalType(5.0).

@when("Items Status_Home changed to 4.0")
@when("Items Status_Home changed to 4")

Or my preference…

@when("Items Status_Home changed")
def some_function)name(event):
    if event.ItemState == DecimnalType(4):
        # do stuff

You might be losing sight of what you’re trying to fix here.

If you use mapdb for restore-on-startup, exactly the purpose for which is is made, you will not get the problem.
mapdb uses minimal resources and can easily sit alongside any other persistence services.

Also, I don’t know what your Status_Home Item represents, but you should review whether restoring it all is a sensible thing to do. Remember that you’re guessing what some status is now based only on what it was previously at some undetermined time in the past. Sometimes it’s better to code for “I don’t know yet”.
Not my business, just a suggestion for consideration.

It does seem as though a lot of the trouble coiul dbe avoided if Status_Home was a StringItem. It seems like a presence status. I use Home, Away, Sleep, Security, Guest as possible states for a Presence Item.

Status_Home ist just for at Home, sleeping, away and on Holiday. So what you say is: Everyboda should use IntValues for defined “Help-Items” and should install mapdb.

Yes, its a solution. But how to roll out to all Users, wants to get a running system fast without much programming? I know al lot of people from other solutions tried openhab and throw it away, cause to difficult.

How can be solve this for all? Install mapdb automatically with openhab2 as standard persistence? With a hint in persistence documentation? Would help I think. I just tried and read a lot. I can do workarounds, but this system is to great to be only for people with a lot of know how and interest on programming :wink:

Edit: Different translation to avoid mistakes.

Nope, that was your choice.

It’s the obvious, most effective way to get restore-on-startup.
The mapdb docs tell you what it does.
openHAB is flexible, does offer alternatives, and doesn’t force you to use mapdb as the One True Way.

You can do that yourself.
(It is true that mapdb doesn’t sell itself until you look at its own page)

I think you’re rather over reacting here.

I should I think! Cause Status_Home could be 4 or 4.0 :wink: Better change

if (DecimalType(items.Status_Home) == 4):

That should be the solution in python.

Still cutting parts out, because it’s room for room the same:

rule "Status_Heat-Control: Home"
when
   Item Status_Home changed to 1.0 or
   Item Status_Home changed to 1
then
var Number help
var int UpMin
var int UpMinD
var int currentHour = now.getHourOfDay



// Modus Thermostate Fussbodenheizung (OG) auf Eco zurücksetzen
logInfo("11_Status_Heat-Control-Home.rules","OG-Heating: Neuer Modus nun ECO")
gThermoModus.members.forEach[ i | 
	i.sendCommand(11)
	logInfo("11_Status_Heat-Control-Home.rules", "Auf Eco: " + i.name)
]

logInfo("11_Status_Heat-Control-Home.rules", "Einstellungen Zuhause: Setze Temperaturen hoch (" + currentHour.toString + " Uhr")

// LC-13 EG-Flur
// Nur, wenn Nachts Raum kälter als Tagessoll-Temperatur
if (EG_Flur_Sensor_Temperatur.state <= Pref_EG_Flur_Soll_Temp.state) {
	if (currentHour != 6) { UpMin=1 }
	else { UpMin=(Pref_EG_Flur_Soll_UpMin.state as Number).intValue }

	logInfo("11_Status_Heat-Control-Home.rules", "EG-Flur auf " + Pref_EG_Flur_Soll_Temp.state + " in " + UpMin.toString + "Min.")
	createTimer(now.plusMinutes(UpMin )) [|
// Falls sich bis zur Uptime der Modus geändert hat
	    if (Status_Home.state == 1.0) {
		logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Flur jetzt erhöht")
		EG_Flur_Thermo_Soll_Temp.sendCommand(Pref_EG_Flur_Soll_Temp.state as Number) 
	    } else if (Status_Home.state == 3.0) {
		logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Flur jetzt erhöht (Abwesend)")
		help=((Pref_EG_Flur_Soll_Temp.state as Number) - (Pref_Away_Soll_Decrease.state as Number)).doubleValue
		EG_Flur_Thermo_Soll_Temp.sendCommand(help) 
	    }
	]
}

// LC-13 EG-Bad
if (currentHour != 6) { UpMin=1 }
else { UpMin=(Pref_EG_Bad_Soll_UpMin.state as Number).intValue }
logInfo("11_Status_Heat-Control-Home.rules", "EG-Badezimmer auf " + Pref_EG_Bad_Soll_Temp.state + " in " + UpMin.toString + "Min.")
//    var Timer UptimerB=createTimer(now.plusMinutes(UpMin ))) [|
createTimer(now.plusMinutes(UpMin )) [|
	if ((Status_Home.state == 1.0) || (Status_Home.state == 1)) {
	    logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Bad jetzt erhöht")	
	    if (EG_Bad_Window_Contact.state == CLOSED) {
		EG_Bad_Thermo_Soll_Temp.sendCommand(Pref_EG_Bad_Soll_Temp.state as Number)
	    }
	    else {
		TMP_EG_Bad_Soll_Temp.sendCommand(Pref_EG_Bad_Soll_Temp.state as Number)
		TMP_EG_Bad_Soll_Temp.postUpdate(Pref_EG_Bad_Soll_Temp.state as Number)
	    }
	} else if (Status_Home.state == 3.0) {
		logInfo("11_Status_Heat-Control-Home.rules", "Timer: EG-Bad jetzt erhöht (Abwesend)")
		help=((Pref_EG_Bad_Soll_Temp.state as Number) - (Pref_Away_Soll_Decrease.state as Number)).doubleValue
		if (EG_Bad_Window_Contact.state == CLOSED) {
		    EG_Bad_Thermo_Soll_Temp.sendCommand(help)
		} else {
		    TMP_EG_Bad_Soll_Temp.sendCommand(help)
		}
	}
//		UptimerB=null
]


// TRM2fx OG-Flur
if (currentHour != 6) { 
	UpMin=1 
	UpMinD=1 
} else { 
	UpMin=(Pref_OG_Flur_Soll_UpMin.state as Number).intValue 
	UpMinD=(Pref_OG_Flur_Display_UpMin.state as Number).intValue 
}
if (OG_Flur_Sensor_Temperatur.state <= Pref_OG_Flur_Soll_Temp.state) {
	logInfo("11_Status_Heat-Control-Home.rules", "OG-Flur auf " + Pref_OG_Flur_Soll_Temp.state + " in " + UpMin.toString + "Min.")
	createTimer(now.plusMinutes(UpMin )) [|
	    if ((Status_Home.state == 1.0) || (Status_Home.state == 1)) {
		logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Flur jetzt erhöht")	
		OG_Flur_Thermo_Soll_Temp.sendCommand(Pref_OG_Flur_Soll_Temp.state as Number) 
		OG_Flur_Thermo_Soll_Temp.postUpdate(Pref_OG_Flur_Soll_Temp.state as Number) 
	    } else if (Status_Home.state == 3.0) {
		logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Flur jetzt erhöht (Abwesend)")
		help=((Pref_OG_Flur_Soll_Temp.state as Number) - (Pref_Away_Soll_Decrease.state as Number)).doubleValue
		OG_Flur_Thermo_Soll_Temp.sendCommand(help)
		OG_Flur_Thermo_Soll_Temp.postUpdate(help)
	    }
	]
}

logInfo("11_Status_Heat-Control-Home.rules", "OG-Flur Display: " + Pref_OG_Flur_Disp_Day.state + " Buttons: "+Pref_OG_Flur_Butt_Day.state + "in " + UpMinD.toString + "Min.")
createTimer(now.plusMinutes(UpMinD )) [|
	if ((Status_Home.state == 1.0) || (Status_Home.state == 1)) {
	    logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Flur Display jetzt erhellt")	
	    OG_Flur_Thermo_Display_Helligkeit.sendCommand((Pref_OG_Flur_Disp_Day.state as Number).intValue) 
	    OG_Flur_Thermo_Button_Helligkeit.sendCommand((Pref_OG_Flur_Butt_Day.state as Number).intValue) 
	}
]

// TRM2fx OG-Bad
if (currentHour != 6) { 
   UpMin=1 
   UpMinD=1 
} else { 
	UpMin=(Pref_OG_Bad_Soll_UpMin.state as Number).intValue 
	UpMinD=(Pref_OG_Bad_Display_UpMin.state as Number).intValue 
}
logInfo("11_Status_Heat-Control-Home.rules", "OG-Bad auf " + Pref_OG_Bad_Soll_Temp.state + " in " + UpMin.toString + "Min.")
createTimer(now.plusMinutes(UpMin )) [|
	if ((Status_Home.state == 1.0) || (Status_Home.state == 1)) {
	    logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Bad jetzt erhöht")	
	    OG_Bad_Thermo_Soll_Temp.sendCommand(Pref_OG_Bad_Soll_Temp.state as Number) 
	    OG_Bad_Thermo_Soll_Temp.postUpdate(Pref_OG_Bad_Soll_Temp.state as Number) 
	} else if (Status_Home.state == 3.0) {
	    logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Bad jetzt erhöht (Abwesend)")
	    help=((Pref_OG_Bad_Soll_Temp.state as Number) - (Pref_Away_Soll_Decrease.state as Number)).doubleValue
	    OG_Bad_Thermo_Soll_Temp.sendCommand(help)
	    OG_Bad_Thermo_Soll_Temp.postUpdate(help)
	}
]

logInfo("11_Status_Heat-Control-Home.rules", "OG-Bad Display: " + Pref_OG_Bad_Disp_Day.state + " Buttons: "+Pref_OG_Bad_Butt_Day.state + "in " + UpMinD.toString + "Min.")
createTimer(now.plusMinutes(UpMinD )) [|
	if ((Status_Home.state == 1.0) || (Status_Home.state == 1)) {
	    logInfo("11_Status_Heat-Control-Home.rules", "Timer: OG-Bad Display jetzt erhellt")	
	    OG_Bad_Thermo_Display_Helligkeit.sendCommand((Pref_OG_Bad_Disp_Day.state as Number).intValue) 
	    OG_Bad_Thermo_Button_Helligkeit.sendCommand((Pref_OG_Bad_Butt_Day.state as Number).intValue) 
	}
]


//		Downtimer = null
//		Uptimer = null
help = null
end