Getting a part of a string in a rule

Hi all,

I’m trying to build an own alarm-system with openhab. My experience with openhab is not very extensive, so I’ve a simple question to rules. I’ve build a rule with the UI but my problem now is that my Nuki lock sends a state like this: 4,0,95763,0,1. For my rule I only need the first number. I’ve found some posts here ( [SOLVED] How to split a string in a rule ), but I’m not able to implement it to my own code. Can somebody help me please?

configuration: {}
triggers:
  - id: "1"
    configuration:
      event: ""
      thingUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer
      channelUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer:sensor#contactOpened
    type: core.ChannelEventTrigger
  - id: "3"
    configuration:
      itemName: Bass_Output_Switch
      command: ON
    type: core.ItemCommandTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: Haustuer_Schloss_actionevent
      operator: =
      state: "4"
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "4"
    configuration:
      itemName: Stehlampe_Output_Switch
      command: OFF
    type: core.ItemCommandAction

In this example the state of id:2 Haustuer_Schloss_actionevent is what I need. I need a state 4, but I get 4,0,95763,0,1. I think I can get the first part with the following command

Haustuer_Schloss_actionevent.split(’ , ').get(0)”

but can I create a variable and an if-function at the conditions part?

Thanks for your help in advance

Michael

Hello @gandalf23

This would require creating a script in the rule that can be created when you click add action

You will need to pick the language you want to use (all except DSL which is more legacy) are good choices with the first Blockly being a good choice for beginners and for those not familiar with coding or the other languages because it provides a drag and drop approach. Rules Blockly | openHAB

blockly is also a good choice because it will allow you to see the code that it has generated

To use Blockly you will also need to have the Javascript addon installed in openHAB JavaScript Scripting - Automation | openHAB

1 Like

Hi,

You can already do the splitting with a transformation in the profile (between item and channel). If you have to change “SCRIPT Rule DSL (v1)” to “Thing to Item Transformation”
| input.split(',').get(0);

2 Likes

Thank you for your answer @BrettLHolmes,

I’ve tried it with javascript, without Blockly, but I don’t know how my core.ItemStateCondition does now the variable from the script part. The variable is not defined global. How do I define it global, if I only can use scripting in a single function?

Perhaps I have an error in my (thinking) logic, but from programming languages I now a little bit, a variable defined in an function is only local to this function.

Sorry for my stupid questions, but I try understand how it works with rules.

Here is my new code:

configuration: {}
triggers:
  - id: "1"
    configuration:
      event: ""
      thingUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer
      channelUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer:sensor#contactOpened
    type: core.ChannelEventTrigger
  - id: "3"
    configuration:
      itemName: Bass_Output_Switch
      command: ON
    type: core.ItemCommandTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: Haustuer_Schloss_actionevent
      operator: =
      state: lock_staus
    type: core.ItemStateCondition
  - inputs: {}
    id: "4"
    configuration:
      type: application/javascript
      script: |-
        var lock_status = 9;
        if (Haustuer_Schloss_actionevent.split(',')[0]=4){
        lock_status=4;
        }
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "5"
    configuration:
      itemName: Stehlampe_Output_Switch
      command: OFF
    type: core.ItemCommandAction



Thanks in advance

Thanks for your answer too, I think I’ve to look for transformations at the future, but at the moment I have no idea about the things you are writing.

Sorry

There are a couple of important concepts here:

  1. When defining a rule using the UI you can only add scripts or script-like functions to specific script elements (such as the inline script action). All other values are static.

  2. All of the different components of the rule that do include lines of script (either script actions or script conditions) run in their own context. It is possible to use the shared cache to set values that can be accessed by multiple scripts, but that, again, is only available between scripts, it is not something you can access in the other UI configured rule parts like a Item action.

  3. Conditions are evaluated only to determine if the rules actions should be run. Not to pass values to other parts of the rule. If a condition is a script, then the rules engine will run that script and wait for it to return a value that is either true or false. If the value is true the then the rule execution continues (either to the next condition or to the first action), if it is false then the rule execution halts at that point and no further actions are taken.

With those things in mind you have two options here:

  1. You can process the incoming value of your lock before the rule ever begins to run and then have a simple rule built completely in the UI with a condition that checks the single already processed value of the lock. There are numerous ways to transform the value depending on the binding/channel your item is linked to, but this what a transform profile could do for you.
  1. If you are going to keep the full 4,0,95763,0,1 state of the lock then you will have to do most of the work of the rule in the script action and not bother with a separate condition. In this case you would still have the same rule triggers you have configured, but then you will just have a single inline script action. Then within that script you will:
  • Extract the value you need from the lock’s state
  • Test that value
  • Send a command or not to the target item based on the result of the test.

Any of the available scripting languages can handle this basic script.

1 Like

javascript:

let rfinput = "4,0,95763,0,1"
rfinput = rfinput.slice(0,1)
  console.info(rfinput)

Don’t know if that is what you want but it works.

1 Like

Did you work it out?
If so how?

Hi @JustinG,

sorry for my late answer and thanks for your detailed explanation, that helps a lot. I will try with scripting and come back here.

sorry, not yet.

Hi all,
I tried out a little bit more, but It didn’t work yet. If i run the rule manually it works, but I think my condition never sends back true.

This is my actual code:

configuration: {}
triggers:
  - id: "1"
    configuration:
      event: ""
      thingUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer
      channelUID: tapocontrol:T110:Tapo-Bridge:Alarm_Hub_Parterre:Open_Haustuer:sensor#contactOpened
    type: core.ChannelEventTrigger
  - id: "2"
    configuration:
      itemName: Eingang
      command: ON
    type: core.ItemCommandTrigger
conditions:
  - inputs: {}
    id: "6"
    label: Lock_State
    description: Wenn das Schloss den Status Lock'n'go hat
    configuration:
      blockSource: <xml
        xmlns="https://developers.google.com/blockly/xml"><variables><variable
        id="R+6*JRD01/PC1Z-^yjL~">Lock_Info</variable><variable
        id=".W,|~!7N5`tl95WSl;7j">Lock_state</variable></variables><block
        type="logic_compare" id="m%K{L0cec(6PHnYW7L|f" x="24" y="-183"><field
        name="OP">EQ</field><value name="A"><block type="variables_get"
        id="vVmFNQ`o*9}_[ytpl!H2"><field name="VAR"
        id=".W,|~!7N5`tl95WSl;7j">Lock_state</field></block></value><value
        name="B"><block type="math_number" id="M:*`n/4@,f!mx{zo(8+w"><field
        name="NUM">1</field></block></value></block><block type="variables_set"
        id="AKXwK4o:oiPF!=__UKIR" x="19" y="-54"><field name="VAR"
        id="R+6*JRD01/PC1Z-^yjL~">Lock_Info</field><value name="VALUE"><block
        type="oh_getitem_state" id="LW0LOhzokC/,V-*6(DRM"><value
        name="itemName"><shadow type="oh_item"
        id="g~~.$Qlwi`/p-fF]t3|}"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="HKZe[OBz3e;,W}~*X@Q;"><mutation
        itemName="Haustuer_Schloss_actionevent"
        itemLabel="actionevent"></mutation><field
        name="itemName">Haustuer_Schloss_actionevent</field></block></value></block></value></block><block
        type="variables_set" id=".p.8[+TDe^75M}}{pa:Y" x="22" y="69"><field
        name="VAR" id=".W,|~!7N5`tl95WSl;7j">Lock_state</field><value
        name="VALUE"><block type="text_charAt"
        id="[KuW~TFL{Y%8+e_m2CPL"><mutation at="true"></mutation><field
        name="WHERE">FROM_START</field><value name="VALUE"><block
        type="variables_get" id="KJWAKYN*K(ZZAOO9pBQ:"><field name="VAR"
        id="R+6*JRD01/PC1Z-^yjL~">Lock_Info</field></block></value><value
        name="AT"><block type="math_number" id="K3nwE%$RmkU1uEJA[,6}"><field
        name="NUM">1</field></block></value></block></value></block><block
        type="oh_print" id="MQ2J~J7e%c8FGWn8q4t." disabled="true" x="25"
        y="218"><value name="message"><shadow type="text"
        id="XGH*B-0|TYy$6{ZGO5g5"><field name="TEXT">abc</field></shadow><block
        type="variables_get" id="weRC,La,05IjN,.:gr%("><field name="VAR"
        id=".W,|~!7N5`tl95WSl;7j">Lock_state</field></block></value></block></xml>
      type: application/javascript
      script: |
        var Lock_Info, Lock_state;


        Lock_state == 1;

        Lock_Info = items.getItem('Haustuer_Schloss_actionevent').state;

        Lock_state = Lock_Info.charAt(0);
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      itemName: Rentier_Steckdose
      command: ON
    type: core.ItemCommandAction



Sorry for using other power switches now to test, my tapo p100 didn’t work constant.

I don’t use blockly enough to know whether it is even possible to return a value from a blockly script, but yours certainly doesn’t appear to. There are some other issues with the script as well from looking at the few lines of javascript is has produced. For example, there’s no actual test of the value of your lock state after you set the set from the character value in the state string, but that can be cleaned up later.

More relevant is the fact that putting a script in a condition is a little hit or miss right now because of recent changes to how js script conditions (which includes blockly) are processed. So exactly what form the script should take depends a little on what version of OH you are using and whether or not you have changed a particular setting.

Short version: Old conditions needed to end with a line that evaluated to true/false and the rule engine would use that final evaluation as the result of the condition. New conditions are wrapped in an invisible function (unless you have turned this option off) which means that a return {some value here} statement is required or the result of the condition function is null which is a false value (what I suspect is going on here).

This is partly why, in my answer above I said that if you are going to go with a script to process your Item state, don’t even bother with a condition. Just check the state as part of the rule action script. It should be easier for you to work out the blockly blocks to check the state (you have most of that already) and then just send a command to your other item within the action script. Then you don’t need to worry about conditions at all.

The only reason you may want to keep the condition and the action separate is if you every intend to call this rule from some other rule. In that case, it is possible to specify whether the condition should be taken into account, so you could have cases where the action is taken regardless of the test condition. If you don’t need that complexity, then just make the action a script and gorget trying to get the separate condition to work.

Hi @JustinG,

that’s what I’ve read and why I’ve used the following line at the end

Lock_state = Lock_Info.charAt(0);

I didn’t know/find that, much thanks for clarification.

You’re right, I should write one script for the rule.

Also I debt you an explanation what I want to realise in detail

  • My Nuki Lock should activate an alarm system by leaving my home (Lock’n’go state)
  • If a door or window is open, openhab should send a message by telegram and let speak an alexa which window is open
  • The sensor of the open window will be excluded from from the rule till the alarm system will be activated the next time
  • If another door/window sensor will send an open signal after the alarm system is activated, all Alexas should play a sound, all lights should be turned on, all cameras should record permanently and I will get an alarm on my mobile phone
  • The alarm can only be disabled with a mobile phone of my family, if somebody tries to stop Alexa, it will always reactivate automatically
  • Without an alarm the system should be deactivated by unlocking the door (unlock state)
  • If the door will be locked (lock state), a restricted alarm system will be activated, not every door/window sensor will be used

I think I will need more than one rule, but I’m not sure yet.

I’ll chime in here since I’m a Blockly user myself.

For the “But only if” condition to work, you need a Logic block. It seems there are still issues with using “return”.

I’d like to advocate for the transformation here again. If you create an additional Item of type NUMBER in the channel using “Add Link to Item…” and “Create a new Item”, you only need to select the Parent Groups and Script. One advantage of Number is persistence.

Theoretically, you should be able to do everything with just one rule, but I advise against it. With that volume of tasks, it quickly gets cluttered in Blockly. Break it down into individual tasks and use functions, scripts, or rules for each. For example, one rule triggered by the Nuki that arms/disarms the alarm system, saves the contacts when leaving, and outputs a message for any that are already open. Another rule triggered by the Contacts group reacts when the alarm system is armed, and so on…

If you create multiple Blockly scripts for a single rule, you can give them a title and description within the script. To do this, click on ⌃ in the bottom right corner.

Shameless plug for https://community.openhab.org/t/semantic-features-filter-and-air-humidity/164297 because the filters could be helpful here.

2 Likes

I agree with @Tschetan this looks like several rules.

You have at least two rules but probably better as four because you have several different triggering conditions:

You could combine 2 or all 3 of the door based rules, but you gain very little from that and only increase the complexity of the rule code, troubleshooting, and maintenance.

You do have a situation where you want to share information between rules

For this you will want to use the public cache which will hold values in an object that can be accessed by all rules. Blockly rules have access to the public cache using blocks in the openHABValue storage group.

1 Like

Hi, I’ve tried this way, but have still aproblem.
Can you please explain how to do?

Here’s the way I’ve tried to do.
I’ve created an Item of type number by using link to Item (Actionevent) and named it Haustuer_Schloss_lock_state, selected the parent group and chose script ECMA…

At the thing to item transformation I’ve put in your code of your first post. I think I did something wrong, because the result is NULL.

Here some screenshots:

The solution above was for DSL because I didn’t know which language they prefer. In JS should
| input.split(‘,’)[0];
go.

Thanks for your fast answer, but the result is unfortunately the same.
I’m not sure if I have chosen the right transformation.
Sorry I really have not so much experience with openhab.

Hi,

I’ve solved it with a rule, which takes the first character from my Haustuer_Schloss_actionevent and writes it to a new item.

(function () {

    const itemName = "Haustuer_Schloss_actionevent";
    const targetItem = "Haustuer_Lock_State";

    const item = items.getItem(itemName);
    const state = item.state;

    // Safety check
    if (!state) {
        console.log("Rule triggered, but item state is undefined – ignoring");
        return;
    }

    console.log("Rule triggered, raw value: " + state);

    const payload = state.toString();   // e.g. "3,0,147646,0,1"

    if (payload.length === 0) {
        console.log("Empty payload received – ignoring");
        return;
    }

    // Extract the first value before the first comma
    const firstValue = payload.split(",")[0];

    // Update the target item
    items.getItem(targetItem).postUpdate(firstValue);

})();

Thanks for helping to everyone here.

This can never happen. An Item always has a state. That state may be "NULL" or "UNDEF" but it will never be null nor false nor 0. !"NULL" or !"UNDEF" will always return false so this check effectively does nothing.

It’s better to replace this with:

    if(item.isUninitialized) 
        console.log("Rule triggered, but item state is undefined – ignoring");
        return;
    }
1 Like