Migration to OpenHab, multiple sensors and conditions best practice

Aplogize for longer explanation in advance.
I’m preparing migration from custom automated solution (C code + TXT config file) to openHAB. And do not know what will be the best practice to implement actual solution in openHAB.
Current solution:
There are cca 60 actuators (engines), each engine has 10 settings values (cfg.txt), then there are 1 to 5 sensors per engine group (in cfg.txt what sensors is particular engine using) totaling cca 20 sensors, sensors are communicating directly to the C code by proprietary protocol. Then there is (THE PROBLEM STARTS) rules file (more than 4000 rules, engine independent, based on settings value, sensor value and previous state) that defines for selected combinations of settings and sensor values required target.
On each change of settings or sensor value the C code loops thru all engines and for each engine search for matching rule (empty space ‘-’ in rule means any value fits the rule) and loads the ‘new’ target state from rule. If ‘new’ target state is different from actual state there is simple rule defining how to get to the target state and how to update the actual state so it will work on next run.
How to implement this in openHAB???
From the C code I can feed openHAB either via API or via file interface to push sensor values, the same can be done with sending orders to engines, but what to do with the rules?

  1. I considered creating ‘wrapper’ rule that will be triggered by any change and simply calls the C code, I believe this is not effective and definitely not the best practice
  2. I can create script to transform current rules to openHAB rules, but the ‘old’ ones are not related to state change, they are based on actual state so there will be cca 4000+ rules generated from this and I do not believe this is good way
  3. Creating ‘matrix’ in memory where the ‘old’ rule file will be loaded and then having one openHAB rule that triggers on any change in sensors (possible) or settings (how this can be done I do not want to write 600 settings values manually, can this be loaded from file and then changed in openHAB UI if needed?)

I really do not know what is the best practice and how to define this in openHAB - any ideas?
The biggest issue for me is that the ‘old’ rules are using fixed logic (when config+sensors+previous status is this target status should be that), but openHAB uses the change logic (when sensor has changed from A to B execute change), but in reality current C code uses change only as indicator if to recalculate all engines, but there is no rule depending on change directly.
The engine settings are often changing by external factors that I was thinking that can be possibly integrated into openHAB UI (e.g. switch all engines to manual mode etc…) still keeping also the possibility to batch update.
I have experience with Chef and was thinking that maybe it will be possible to write some cookbook that will generate all needed rules etc… files for openHAB, but I do not know how to approach this topic within openHAB

Thank you

Sample (in all samples ‘-’ means do not care matching anything for read, or do not touch/write anything keep last state for write) of engine definition (init states are used to reset engines to known state when starting program after boot):
Engine|InitMode|InitState|InitSend|OperationMode|TempControl|Daylight|Plant|DoorProtect|WaterProtect|CoolingEnabled|Presence|SensorLight|SensorWater|SensorTemp|SensorDoor
1|Auto|1|-|Auto|None|Day|NO|NO|YES|NO|Local|1|5|-|-
2|Auto|1|-|Auto|Cooling|Day|YES|Door|YES|NO|Remote|1|6|12|-
3|Auto|1|-|Auto|Heating|Day|YES|NO|YES|YES|Remote|-|5|12|-

Sample of rule file:
OperationMode|TempControl|Daylight|Plant|DoorProtect|WaterProtect|CoolingEnabled|Presence|SensorLight|SensorWater|SensorTemp|SensorDoor|Target
Auto|Cooling|-|YES|Door|-|-|Remote|Sun|Low|Cold|-|1
Auto|Cooling|-|YES|Door|-|-|Remote|Light|Low|Cold|-|2
Auto|Cooling|-|YES|Door|-|-|Remote|Dark|Low|Cold|-|1
Auto|Heating|-|YES|Door|-|-|Remote|Sun|Low|Cold|-|2
Auto|Heating|-|YES|Door|-|-|Remote|Light|Low|Cold|-|2
Auto|Heating|-|YES|Door|-|-|Remote|Dark|Low|Cold|-|1
Auto|None|Day|YES|NO|-|-|-|Sun|Low|Cold|-|2
Auto|None|Day|YES|NO|-|-|-|Light|Low|Cold|-|2
Auto|None|Day||NO|-|-|-|Dark|-|Cold|-|1
Auto|None|Day|NO|NO|-|-|Local|Sun|Low|Cold|-|2
Auto|None|Day|NO|NO|-|-|Local|Light|Low|Cold|-|2

Transition from target to real engine order (if any):
TargetMode|TargetState|LastMode|LastState|NewMode|NewState|SendToEngine
Manual|0|-|-|Manual|-|0
Manual|1|-|-|Manual|1|1
Manual|2|-|-|Manual|2|2
Auto|0|Manual|-|Auto|-|0
Auto|1|Manual|1|Auto|-|-
Auto|2|Manual|2|Auto|-|-
Auto|0|Auto|-|Auto|-|0
Auto|1|Auto|2|Auto|1|1
Auto|2|Auto|1|Auto|2|2

So you have implemented a state machine with transistions for all your sensors and actors.

I would probably first find out if all sensors and actors can be interacted with from openHAB. Either via add-ons or a self written add-on.

Second step would be to write a converter tool that takes the txt input files and generates json rule files for the new rule engine. Because that engine can be ‘abused’ as state machine (if follows the if-when-then pattern).

Cheers David

Hi David
Thank for response, for the proprietary communication I was intending to still use the old C code and connect it via REST or FILE interface (the file interface I have already experimented with and works.

I do not understand your last part. It is no problem to change form of the rules file, but it is problem for me to understand how can it be used in case there are NO changes mentioned in 'old’rule file (the same rule triggers regardless of how many values have changed or how much they have changed, it even triggers on the same values and then the transition table will filter it out).
In opposite openHAB relies on changes in state. Can you please share a link to some place where I can read more about your proposed usage?

Ultimately you are moving from a state based Rules engine to an event based Rules engine and the two work just differently enough that I don’t think you will be able to just write a script to convert from one to the other. But OH is an EVENT based engine, not a CHANGE based engine.

OH supports:

  • updates (an Item received a new value but the value is not necessarily different from the current value)
  • commands (an Item is being told to go do something)
  • changes (an Item changed state)
  • channel trigger (some 2.x version bindings support Channels that directly trigger a Rule, e.g. Astro binding triggers a Rule at sunrise through a Channel trigger)
  • time based trigger (a certain time of day, in Rules DSL (the default Rules Engine) this is a cron expression)
  • system trigger (OH startup, shutdown)

In the new Rules Engine with the new MQTT binding David is working on, we can also trigger Rules based on MQTT messages directly.

The new Rules Engine is Experimental and has basically no documentation yet. I’ve started some User’s Guides here. I believe what David is proposing is that you can mimic a state machine by creating rules with a When... but only if... action that correspond to your existing state machine.

What would probably be easiest would be to click together a Rule by hand then look at the resultant JSON file. then you can write a script to convert your state machine Rules to JSON rules.

But this is an “abuse” because OH Rules are not designed like a state machine. They are designed as “When this event happens (again, event, not necessarily change) run this program.” As such your 4000+ state machine Rules would probably be <400 OH rules, if you rewrote them in the way OH is designed to work.

Thanks for the doc link, will try to read more about this.
I have spend last 2 days trying to create some form of logic base don changes, events, however it is really complex to take into account all the 4000 ‘old’ rules and I’m slowly starting to accept the idea that I will fail and use the OH as wrapper :frowning: I tried via all possible cases, tried also search trees and decision trees, but no luck until now…
The current system is rally very easy, if anything changes rerun all engines against the txt file with new and old values, for engines there target is different than before use transition table (this I can easily translate rules in OH) to get there.

Well, I believe I have made some ‘draft’ how the rules can look like… Now I have huge tree that I will need to process via script for each engine on any of sensors change (we will get to this later).
I was now trying to setup manual sample to try how it works. I have created really primitive structure:
THINGS:

Thing exec:command:echo_only [command="echo abc"]

RULES (the group is one of groups I intend to have, but it is not relevant for now):

rule "test run"
  when
    Member of gZO received command
  then
    ECHO_SEND_Args.sendCommand("10 4 0")
    logInfo("EchoSimple", "Member " + triggeringItem.name + " received " + receivedCommand + " finished with " + ECHO_SEND_Out.state)
end

ITEMS:

Switch ECHO_SEND { channel="exec:command:echo_only:run" }
String ECHO_SEND_Args { channel="exec:command:echo_only:input" }
String ECHO_SEND_Out { channel="exec:command:echo_only:output" }

Now if I click on any item from that group, I see in log this message:

2018-11-19 23:45:23.715 [INFO ] [se.smarthome.model.script.EchoSimple] - Member Z1_S_PETER_BIGx10x1 received STOP finished with NULL
2018-11-19 23:45:23.861 [INFO ] [se.smarthome.model.script.EchoSimple] - Member Z1_S_PETER_BIGx10x1 received STOP finished with NULL

Normally there was script called, but it did not work so I used this echo command for test.
I tried all the recomended sudo tests to see if openhab has access and did not find any problems.
What I’m doing wrong?

not sure if this is the fault, but since you are passing additional arguments to the Thing, try:

Thing exec:command:echo_only [command="echo abc %2$s"]

otherwise, your configs look good

And according to the docs, you need to have the RegEx Transformation installed also.

Thanks for help, added the %2$s but no change.
RegEx transformation service was the key! Thanks, such a primitive error.

Now back to engine question, for now I’m thinking about having script that will be run from basic rules, but what I do not know is where to store properties of the engines, can I define ‘struct/hash’ that can be used also by rules?

1 Like

You can but it will probably be easier to maintain if you use a .map file and the transform action.

my.map

value1=mappedvalue1
value2=mappedvalue2
...

in Rules:

    val mappedValue = transform("MAP", "my.map", MyItem.state.toString)

Be sure to install the Map transform.

1 Like

Now I got little bit further, have rule (not yet coded, on paper for now) that can react on sensors, however each “item” consists of several items (storing run-time variables for the rule to work, firing of the rule etc…). For example Item1 is in items file defined as:

  • Item1x10x1 (the 2 values diveded by x are communication addresses, used by direct change via UI)
  • Item1xTRIGGER (this is fictive switch that triggers re-calculation of the item1 in automation)
  • Item1xLastRunTime
  • Item1xLastCommand

Then there are sensors where for each sensor there are multiple items in items file (sample below) logic is that for each sensor there is custom rule that crates CLEAN value from RAW_IN value

  • Sensor1xRAW_IN (receives data via api)
  • Sensor1xCLEAN (sensor value for further processing this is in group g_TRIGGER_ALL)
  • Sensor1xTRIGGER (group of engine triggers that should be activated if ‘cleaned’ value of sensor1)

Now in code I have this one:

rule "Sensor triggers" 
  when
    Member of g_TRIGGER_ALL changed
  then
    val String split_part0 = triggeringItem.name.toString.split("x").get(0)
    val local_tiggered_group_name = split_part0 + "xTRIGGER"
    val local_tiggered_group_item = ScriptServiceUtil.getItemRegistry.getItem(local_tiggered_group_name) as GenericItem
    local_tiggered_group_item.allMembers.forEach [i | i.postUpdate (ON) ]
end

In simple words if any sensor value from g_TRIGGER_ALL is changed then all engines in corresponding sensor group are triggered for re-calculation.

I’m getting this warning:

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

What is the problem? I have read on these forums that you need to add true before the ], but it did not work for me. Also I’m not sure if reading group in this way is really correct…

Because of all the redirection the Rules Engine doesn’t have enough information at load time to determine the type for i in the forEach lambda.

You can probably fix it using

val GroupItem local_triggered_group_item = ScriptServiceUtil...

and definitely fix the warning with

...allMembers.forEach[ SwitchItem i | i.postUpdate(ON) ]

Note that it is just a warning and the Rule probably works as written because by the time the code actually executes there is enough information to surmise the type of i.