OpenHAB CULfw for Somfy RTS Rollershutters

To Stop a device, you have to send “MY” equals “1” If you send another code the currently running movement will be stopped and the new one one will be executed.

I see. And if you press MY when the motor is not moving then it starts moving to the predefined MY postion?

Yes it should behave like this.

Hi Christoph,

thank you very much for this much needed tutorial. I got the CUL Switch for programming the motors on my sitemap and also a Switch Item for the rollershutters. I configured the real Item for the Shutters as “Rollershutter item” because I kind of missed the part where you configured the actual rollershutters as actual item. You justed wrote in the rules

when
	** Item AllShutter** received command OFF
    then

but in the tutorial you never defined the item for the rollershutters.

I think that the scripts are working (started from the command line, but I cannot get the rollingCode). However, the items on my sitemap seem to have no connection. I don’t know if it didn’t get the rule or something else…
Is there a way to find out the rollingCode?

Another question: Could you show me the definition of your rollershutter item? Maybe I have the wrong item type or something… I am not quite sure… I tried some out, but they did not work.
For debugging reasons I put the rollingCode on my sitemap, but as string and also as number he only shows an “-”.

Anyway, I will keep you updated! Thank you again for the scripts and configuration.

Hi Floh,

you have not missed anything. I made a mistake. I forgot to implement the second Switch, after programming the new Address. I corrected my tutorial. So there is no Rollershutter item. The reason is, that I would not use it.
There are two points:

  1. My Shutter behave very strange: If I send the command once. They only move a small step.
  2. My shutters are completely opened or close.

This is why I do not need a Rollershutter item. But you can easily adjust my rule. Just replace the Line

rule "Shutter UP"
    when
	Item MyShutterButton received command ON
    then

with

rule "Shutter UP"
    when
	Item <Your Shutter Item> received command UP
    then

and the same for DOWN.

Best regards,
Christoph

EDIT: changes to the description and the script to reflect all feedback until 21.04.2021

As announced this is a rule for sending commands to Somfy RTS devices such as rollershutters, awnings, etc.
create 4 items as described in the rule and follow the instructions.

.rules file

import org.openhab.core.model.script.ScriptServiceUtil
/*
=======================================================================================================================
DESCRIPTION:
  1. Create a group with a generic group name like `gRTS`. Note: this is not a group which represents a physical device/thing, it is a logical group.
     If you choose a different group name, make sure to change the rule accordingly.
  2. Add the following item for each device with the following syntax:
     <itemName>_control			(Type: STRING; item which sends commands to this rule. Valid commands: 0 or UP, STOP or MY, 100 or DOWN, PROG, NUMBER between 1 and 99)
     <itemName>_rollingCode		(Type: NUMBER; persisted item, which stores the last rollingCode)
     <itemName>_calibration		(Type: NUMBER; persisted item, which stores the duration of a half cycle from 0% to 100% in seconds. Required to control the motor with a command like 60[%])
     <itemName>_address			(Type: STRING, 6 digit; persisted item, which stores address of device)
  3. Add all items with suffix `_control` to your group. Do NOT add other items to that group.
  4. - specify the name and location of your python script
     - items for Rolling Code, Calibration and Address may not be NULL (use REST API to set their values) and have to be persisted.
     - If there are any problems with this script, set debug variable in this rule to true
=======================================================================================================================

=======================================================================================================================
CHANGE VALUES ACCORDINGLY: */

var Boolean debug = false
val String fileName = "RTS.rules"
val String scriptCommand = "python3"
val String scriptName = "/etc/openhab/scripts/RTS.py"
rule "RTS"
//======================================================================================================================

when
    Member of gRTS received command
then
    if (debug) logInfo(fileName,"Rule started")
    var String strThing = triggeringItem.name.toString().split("_").get(0)
    var String strItem = triggeringItem.name.toString()
    var String strCommand = receivedCommand.toString()
        if (debug) logInfo(fileName, "Thing: '{}', Item: '{}', Command: '{}'",strThing,strItem,strCommand)
    var String strRTSCode = ""
    var Integer intCommand = 0
    var Float fSeconds = 0.0

//find regular commands
    switch strCommand.toUpperCase {
        case "" : {
            logInfo(fileName,"Item: '{}' received empty command",strItem)
            return
        }
        case "MY", case "STOP" : strRTSCode = "1"
        case "UP" : strRTSCode = "2"
        case "DOWN" : strRTSCode = "4"
        case "PROG" : strRTSCode = "8"
//find number values in command
        default: {
	        if (debug) logInfo(fileName,"No standard commands submitted")
            var strCommand2 = transform("REGEX", "\\D*(\\d+)\\D*", strCommand)
                if (debug) logInfo(fileName,"strCommand2: '{}'",strCommand2)
            if (strCommand2 !== null) {
                intCommand = Integer.parseInt(strCommand2)
                    if (debug) logInfo(fileName,"intCommand: '{}'",intCommand.toString())
                switch intCommand {
                    case 0 : strRTSCode = "2"
                    case 100 : strRTSCode = "4"
                    case intCommand > 100 : logInfo(fileName,"Item: '{}' received command '{}': not in range [0..100]",strItem,intCommand.toString())
//calculate time when motor has to stop
                    default: {
                        strRTSCode = "4"
                        var Float fCalibration = (ScriptServiceUtil.getItemRegistry.getItem(strThing+"_calibration").state as DecimalType).floatValue
		            if (debug) logInfo(fileName, "fCalibration: '{}'",fCalibration.toString())
                        fSeconds = intCommand.floatValue * fCalibration / 100
                            if (debug) logInfo(fileName, "fSeconds: '{}'",fSeconds.toString())
                    }
                }
            } else {
                logInfo(fileName,"Item: '{}' received unknown command '{}'",strItem,strCommand)
                return
            }
        }
    }

//prepare serial data payload for python script
//Syntax: Ys + EncryptionKey=A1 + Command + 0 + RollingCode + Address
    var Integer intRollingCode = (ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").state as DecimalType).intValue
        if (debug) logInfo(fileName, "intRollingCode: '{}'",intRollingCode.toString())
    var String strRollingCodeHex = String::format("%04X", intRollingCode)
    var String strAddress = ScriptServiceUtil.getItemRegistry.getItem(strThing + "_address").state.toString()
        if (debug) logInfo(fileName, "PAYLOAD: strRTSCode: '{}', strRollingCodeHex: '{}', strAddress: '{}'",strRTSCode,strRollingCodeHex,strAddress)
    var String strPayLoad = "YsA1" + strRTSCode + "0" + strRollingCodeHex + strAddress + "\n"
        if (debug) logInfo(fileName, "PAYLOAD STRING: '{}'",strPayLoad)
    var result = executeCommandLine(Duration.ofSeconds(2),scriptCommand, scriptName, strPayLoad)
        logInfo(fileName, "Python script result: {}",result)
    ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").postUpdate(intRollingCode + 1)

//send STOP after fSeconds if command is number from 1 to 99
    if (fSeconds > 0.0) {
        strRTSCode = "1"
        strRollingCodeHex = String::format("%04X", intRollingCode +1)
        strPayLoad = "YsA1" + strRTSCode + "0" + strRollingCodeHex + strAddress + "\n"
            if (debug) logInfo(fileName, "PAYLOAD STRING timer triggered: '{}'",strPayLoad)
        createTimer(now.plusSeconds(fSeconds.longValue), [ |
            var result = executeCommandLine(Duration.ofSeconds(2),scriptCommand, scriptName, strPayLoad)
                logInfo(fileName, "Python script result timer triggered: {}",result)
            ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").postUpdate(intRollingCode + 2)
        ])
    }
end

Python script (copy to /etc/openhab/scripts/)

#!/usr/bin/python3
# cudos go to chacha
# Install pyserial from https://pythonhosted.org/pyserial/pyserial.html#installation
# add 'stty -F /dev/ttyUSB0 -hupcl' to file /etc/rc.local
import serial
import sys

payLoad = sys.argv[1]
print("Python script received payload " + payLoad)
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=.1)
	#or: ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.write(payLoad.encode)
print ser.readline()
	#todo: throw error if not ok
ser.close()

Reason for editing: adding automatic stop to rule

RTS allows to have a gap of 100 between the counter of transmitter and receiver.
You probably will have that problem in the near future

do you know if both packages are required?

I’d guess you need transport-serial for openhab bindings

Hey Oliver,

many thanks for the script. I tried everything out and got the following error:
If the state of the rollingCode is e.g. ‘0x0001’:

2021-03-31 10:53:26.612 [INFO ] [.model.script.Shutter_BueroFlo.rules] - Rule started
2021-03-31 10:53:26.618 [INFO ] [.model.script.Shutter_BueroFlo.rules] - Thing: 'ShutterBueroFlo', Item: 'ShutterBueroFlo', Command: 'DOWN'
2021-03-31 10:53:26.622 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Shutter_BueroFlo-1' failed: Could not cast 0x0001 to org.openhab.core.library.types.DecimalType; line 84, column 35, length 89 in Shutter_BueroFlo

If the state of the rollingCode is e.g. ‘0001’ (i thought maybe he doesn’t like the leading ‘0x’):

2021-03-31 10:56:09.423 [INFO ] [.model.script.Shutter_BueroFlo.rules] - Rule started
2021-03-31 10:56:09.430 [INFO ] [.model.script.Shutter_BueroFlo.rules] - Thing: 'ShutterBueroFlo', Item: 'ShutterBueroFlo', Command: 'DOWN'
2021-03-31 10:56:09.440 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Shutter_BueroFlo-1' failed: Could not cast 0001 to org.openhab.core.library.types.DecimalType; line 84, column 35, length 89 in Shutter_BueroFlo

The referenced line in your rule is:

var String strPayLoad = "YsA1" + strRTSCode + "0" + strRollingCodeHex + strAddress + "\n"

My question is: How do I need to save the rollingCode state? I tried it in decimal values (1 and 101) as well and got the same error.

As short background:
I added a fifth item: The shutter itself, so that I could interact with it on my sitemap.

Rollershutter ShutterBueroFlo "Rolladen Büro Flo" <rollershutter> (gShutter_BueroFlo)

Of course I made sure, that all items are in the same group (gShutter_BueroFlo). And obviously the rule is triggered, as I can see in the logs.

Maybe it has something to do with the type of variable you store the rolling code in and after that parse it to the strPayLoad variable.
I found this link and that link that might help with the format of the rollingCode. Unfortanetly I am not expierienced enough in coding.

I would appreciate your help!

Hi Flo,
your Item hast to be of type number dimensionless.
what OH version are on?

maybe a misunderstandig. Please put all _control items into one group. not alle items belonging to buroflo.

All Shutter related items are in the group “gShutter_BueroFlo”. And the group seems to work.

I am on OH3 with openHabian 3.0.1 on a Rasperry Pi 4.
Which item do you mean? The rollershutter item (the actual rollershutter), or one of the others (calibration or rolling code)?

No, if you have 3 shutters then you need to assign 3 items to your group - only the ones that receive commands, in my example all Items with suffix control
Otherwise the script will be executed every time you change calibration or rolling code

Hi Flo,
what do you mean by this?

Hey Oliver,
Sorry for the late response. I meant, that I created a fith item (a rollershutter item) where I can send the commands UP and DOWN via the PaperUI.

Anyway, I deleted them after your response and set up everything like you said. all _control-items in one group, item type number:dimensionless…

He still seems to have a problem with the script:

2021-04-06 13:57:49.372 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Shutter_BueroFlo-1' failed: Could not cast 0x0200 to org.openhab.core.library.types.DecimalType; line 84, column 35, length 89 in Shutter_BueroFlo

in the same referenced line I mentioned earlier… I tested the rollingCode out as string, number, number:dimensionless and everything failed.
What would help me is, if you could let me see the actual content of your shutter related items, so e.g. I have my four items _control, _rollingcode, _calibration and _address, and they look like this:

_control:

Number:Dimensionless ShutterBueroFlo_control "Command [%d]" (gShutter_BueroFlo)

(gShutter_BueroFlo) is the groupname where the _control-item is a member of (and noone else).

My _rollingCode file looks like this:

String ShutterBueroFlo_rollingCode "RollingCode [%s]"

My _calibration file looks like this:

Number ShutterBueroFlo_calibration "Calibration: [%d]%%"

And my _address file looks like this:

String ShutterBueroFlo_address "Address: [%s]"

Now somehow the script seems to have a problem with the rollingCode. As I said earlier: I tried several itemtypes (number, number:dimensionless, string) and the two options “0x0200” and “0200”. But nothing worked. The script always says, that it could not cast “0x0200” or “0200” to the decimaltype-library from openHAB.
I really appreciate the help! thanks again!

excuse my. My docs are wrong. rollingCode has to be NUMBER.
don’t forget to set the state via REST API to a proper value

the overall idea behind this script is, that you can add as many rollershutters as you want without the need to have multiple copies of this script.
In case you have two more rollershutters, just add each _control item to your existing group gShutter_BueroFlo. Of course then it would make sense to rename the group to a more general name.

Hey Oliver,

thank you very much!! I got it working!! I can post my config next week, when I have more time. I needed to make little adjustments, because pyserial had a problem with the command. But this was a minor change. If anyone is interested in how to integrate it to Alexa I can post this as well.

thank you again! I am more than happy!

Hi Flo,
could you post the changes to the py script?

One more question to the nanoCul. Is this device working out of the box? Or do you have to change the Firmware or add specific drivers?