OpenHAB CULfw for Somfy RTS Rollershutters

Hi!

I have another working solution for OH3!
I’ve created a python script to control the nanoCUL(raw version, no error handling, no logging).
/etc/openhab/scripts/somfy.py

#!/usr/bin/python
import serial
import argparse

parser = argparse.ArgumentParser(description="Sends SOMFY RTS commands with the nanoCUL on the selected port")
parser.add_argument("shutter", help="The number of the shutter", type=str, choices=["Item1Name", "Item2Name", "Item3Name"])
parser.add_argument("command", help="The command to send", type=str.upper, choices=["UP", "DOWN", "STOP", "MY"])
args = parser.parse_args()

# Command (1 = My, 2 = Up, 4 = Down, 8 = Prog)
C="1"
if args.command == "UP":
    C="2"
elif args.command == "DOWN":
    C="4"

encryptionKey = 'A1'

if args.shutter == "Item1Name":
    rollingCode='AAA1'
    address='000001'
elif args.shutter == "Item2Name":
    rollingCode='BBB2'
    address='000002'
elif args.shutter == "Item3Name":
    rollingCode = 'CCC3'
    address = '000003'

command = 'Ys' + encryptionKey + C + '0' + rollingCode + address + "\n"
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.write(command)
ser.close()

Modify it according to your configuration:

  1. Edit the name of the Items (Item1Name, Item2Name, …)
  2. Change rolling codes, and addresses. The actual codes can be found in the SomfyCUL configuration files in /var/lib/openhab/somfycul/ (Thanks @Daniel_Weisser )
  3. Edit port (/dev/ttyUSB0)

For the script to work:

  1. pyserial should be installed: https://pythonhosted.org/pyserial/pyserial.html#installation
  2. Because Arduino resets on serial connect (check this), I’ve edited the /etc/rc.local file on my Raspberry pi to clear the hupcl setting on the port. I’ve added these lines:
# Clear the hupcl setting to prevent Arduino from reset on serial connect
stty -F /dev/ttyUSB0 -hupcl

Another solution could be to wait 2 seconds in the script after serial connect for the Arduino to restart.

The rule to call the python script:

rule "Somfy binding"
    when
	Item Item1Name received command or
	Item Item2Name received command or
	Item Item3Name received command
    then
    executeCommandLine(Duration.ofSeconds(3),"python", "/etc/openhab/scripts/somfy.py", triggeringItemName, receivedCommand.toString)
end

Hope this helps someone!

Hi Chacha,

I gave it a try on my OH3 system. Beside the fact that I am not able to trigger the python rule, it worked fine one time. For test reasons I just start the script via command line.

The reason is for this one time only is, that the rolling code is constant in your case.

How do you handle the

rollingCode

increment after each time you run the script?

Sorry I started OH one month ago. All my skills are C# and OH is trial and error.

Thanks in advance.

Hi Christoph,

I do not change the rolling code, but somehow it just works. :grinning:
I thought I should but it works without changing it.

Hi Chacha,

ok, I will give it a new try tomorrow. I had to update it each run.

One idea is, to have it as argument in the rule.

If you’ve never used the SomfyCUL to control your devices, you have to program the device with the help of your existing remote to accept the new “remote” (rollingCode+address).

Hi Chacha,

yes I did this. This is how I got it running on my Openhab 2.5 System:

As you state:

I copied the values from the files. This way I could send one single command. The solution to send more than one was to increment the rolling code.

encryptionKey = 'A1'
rollingCode='01FE'
address='000006'
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=1)
command = 'Ys' + encryptionKey + C + '0' + rollingCode + address + "\n"
ser.write(command)
time.sleep(.5)
rollingCode='01FF'
command = 'Ys' + encryptionKey + C + '0' + rollingCode + address + "\n"
ser.write(command)
ser.close()

I have to send the Command twice to make my shutters go completely to an end position.

I’m glad that you’ve made it work!
You can put a print ser.readline() after the line ser.write(command), to see if the nanoCUL receives the command.
I don’t know why I don’t have to increment the rolling code. I thought the nanoCUL takes care of it.

Thank you.

As I thought about the idea to put this parameter into a rule. I realized it will not work, that easy. E.g. a restart of the PI will reset the rule and the rollingCode. If I got it correctly it needs to be somehow an Item-State. Therefore may I ask you to the give some more details?

  • Item Setup which you use to trigger the rule?
  • Have you done the items via PaperUI? Is there a source code for your item?
  • Which Binding do you use, to connect the Items to a channel?

Sorry, for asking so much. You gave sniped of working code. But I am unable to see the hole context.

Thanks in advance,
Christoph

The Items just simple rollershutter items. You can define it anywhere. The rule is triggered, when one of the items’ switch is pressed (UP, DOWN or STOP), or the item received a command. There’s no binding associated with them. Earlier it was the SomfyCUL binding.

Rollershutter Item1Name "Displayname" <rollershutter>

It took some days, but I found no way to not increment the rollingCode, each time I send a command. And this makes sense. Because the RTS Protocol is one-way only. So the motor will only do the commands ones if they have a unique identifier. To not do a command twice, it will only to the command with the correct rollingCode once. This is at least my theory. But if it works like this there is a weak point. What if for some reason one command got lost. Like for example if the receiving motor lost power. Since RTS is one-way only, the remote will not recognize.

Anyhow I found a solution. I added a counter for each address in the *.item file
Number rollingNumber_AllShutter "Befehlcounter alle Shutter" (RollerCommandCounter)

In the rule I send the counter to the python script and increment it, too. Again my increment is by 2 because I have to send the command "UP" twice to make my shutters move the complete way. If I do it only once the shutter only moves up some centimetre.

rule "AllShutter UP"
    when
    Item AllShutter received command OFF
    then
    executeCommandLine(Duration.ofSeconds(2),"python", "/etc/openhab/scripts/OpenAllShutter.py", rollingNumber_AllShutter.state.toString)
    rollingNumber_AllShutter.postUpdate(2 + (rollingNumber_AllShutter.state as DecimalType)) 
end

But this approach has a big weakness. What if the python script for some reasons does not run as expected? So the

command = 'Ys' + encryptionKey + C + '0' + Str_rollingCode + address + "\n"
ser.write(command)

is only called once?

I have no idea how to handle this. How to increase a Number item out of the Python script? This would provide an option to count in a not fully failure proof way, but much better than incrementing the Number item later.

I am open for ideas.

My python script looks like this now:

#!/usr/bin/python
import serial
import argparse
import time

parser = argparse.ArgumentParser(description="Sends SOMFY RTS commands with the nanoCUL on the selected port")
parser.add_argument("rollingCode", help="The rollingCode", type=str)

args = parser.parse_args()

# Fixed Address for all Shutter together 
address='00000A'

#Encryptionkey Somfy
encryptionKey = 'A1'

#Interface for CUL
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=.1)

#Parse RollingCode to integer
Int_rollingcode=int(args.rollingCode)

# Command (1 = My, 2 = Up, 4 = Down, 8 = Prog)
C="2"

#Parse integer RollingCode to HEX String with lengh 4 and without leading 0x an uppercase letters
Str_rollingCode=hex(Int_rollingcode)[2:].zfill(4).upper()
#Build command
command = 'Ys' + encryptionKey + C + '0' + Str_rollingCode + address + "\n"
#Write/Send Command
ser.write(command)

time.sleep(.5) #wait half a second

# increment rollingCode for the next command to send
Int_rollingcode=Int_rollingcode+1 
#Parse integer RollingCode to HEX String with lengh 4 and without leading 0x an uppercase letters
Str_rollingCode=hex(Int_rollingcode)[2:].zfill(4).upper()
#Build command
command = 'Ys' + encryptionKey + C + '0' + Str_rollingCode + address + "\n"
#Write/Send Command
ser.write(command)
#Close Interface to CUL
ser.close()

Hi Christoph!
I don’t think that you have to be sure, that a command is sent before incrementing rolling code. In a one-way communication you never can be sure that the receiver will get the message, but this shouldn’t have to brake communication. Imagine the situation when a remote sends a command, but the receiver is out of range.
On wikipedia you can find this about rolling code:
A typical implementation compares within the next 256 codes in case receiver missed some transmitted keypresses.

You may want to have a look at this implementation:

Works perfectly for me…

You were faster than me…

First of all, @chacha: thank you for this wonderful code. I got it working on the command line!!! After 2 months of research… :slight_smile: Finally!

My setup is also the same: OH3 with openhabian 3, rasperry pi 4, and the same CUL-Stick as you have.

@Christoph_Bergmann my code is a little bit less advanced than yours. But I have the same problem, that you have. I can only control the shutters when I increase the rolling code by 1. I don’t think that you need to see my type of code, because you seem to be far more experienced in Python than I am… but for documentation’s sake and debugging reasons (and if someone is as desperate as I was), here it is. One explanation before:

I made 2 files, where each file holds the rolling code for each of my shutters (two files = two shutters).
rollingCode_Schlafzimmer:

0x1000

rollingCode_BueroFlo:

0x0800

The actual python script:

#!/usr/bin/python
import serial
import argparse

parser = argparse.ArgumentParser(description="Sends SOMFY RTS commands with the nanoCUL on the selected port")
parser.add_argument("shutter", help="The number of the shutter", type=str, choices=["Somfy-Rollade_Schlafzimmer", "Somfy-Rollade_BueroFlo"])
parser.add_argument("command", help="The command to send", type=str.upper, choices=["UP", "DOWN", "STOP", "MY", "PROG"])
args = parser.parse_args()

#Open file with rolling code, read the content and put it in the variable
with open('rollingCode_Schlafzimmer', 'r') as file:
    rollingCode1 = file.readline()

with open('rollingCode_BueroFlo', 'r') as file:
    rollingCode2 = file.readline()

# Command (1 = My, 2 = Up, 4 = Down, 8 = Prog)
C="1"
if args.command == "UP":
    C="2"
elif args.command == "DOWN":
    C="4"
elif args.command == "PROG":
    C="8"

encryptionKey = 'A1'

#Strip rolling code of leading "0x"
if args.shutter == "Somfy-Rollade_Schlafzimmer":
    rollingCode = rollingCode1[2:].rstrip("\n")
    address='000029'
elif args.shutter == "Somfy-Rollade_BueroFlo":
    rollingCode = rollingCode2[2:].rstrip("\n")
    address='000029'

command = 'Ys' + encryptionKey + C + '0' + str(rollingCode) + address + "\n"
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=1)
ser.write(command)
ser.close()

#AFTER the command execution, increase the variable by 1 hex value)
if args.shutter == "Somfy-Rollade_Schlafzimmer":
    with open('rollingCode_Schlafzimmer', 'r+') as file:
        rollingCode1 = int(file.read(),16)
        file.seek(0)
        file.write(hex(rollingCode1 + 1))
elif args.shutter == "Somfy-Rollade_BueroFlo":
    with open('rollingCode_BueroFlo', 'r+') as file:
        rollingCode2 = int(file.read(),16)
        file.seek(0)
        file.write(hex(rollingCode2 + 1))

@Christoph_Bergmann
I will test your version of the code next week.

@DrRSatzteil
I will also test this implementation. Hopefully it helps…

One last question @chacha and @Christoph_Bergmann:
How do I implement the shutters via PaperUI? I am not quite sure… I implemented the CUL-Stick as Serial Bridge, and after that I got lost, honestly… Sorry, if this question sounds dumb.

You would have to port it to python though. I thought it might help to understand how the rolling code actually works

As far as I understood it (just the basics) that it increases by 1 (in hex value). But I did not deep dive into it, like what happens when you arrive at 0xFFFF, and that sort of things. I found one link, where one guy reverse engineered the signal back, but that was far over my experience. I’ll post that here next week, if it is of interest. Maybe someone can draw more insight from this than me. :wink:

Hi,

I have not done the shutters via paperUI. Sorry, I just did it with a file in the item folder

/etc/openhab/items/
in my case

you can give the file any name you want, but the file ending must be .items

Good luck

I just read a bit about the protocol and as far as I understand it you can choose any value as a start value for your remote and teach this code to your motor. After that you always increase the code by 1 with every button press. The motor will accept the command of the code is not about 100 higher than the last known code. I got this info from Somfy Smoove Origin RTS Protocol | PushStack where they say that also some obfuscation of the payload is applied. I don’t really know what happens after you pushed a button on your remote more than 65000 times, probably it just breaks on the way to get there…

Oh and by the way you could just keep on incrementing even after you hit your 16 bit limit. The results would not make sense mathematically but that does not really matter as long as both ends come to the same conclusions.

I am watching closely this thread because I want to integrate a couple of somfy motors into openhab (OH3). Until I saw a few of the latest threads in here (python scripts) I was about to buy an rfxcom transceiver and integrate it via rfxcom binding. Now I decided to go for nanocul in combination with these scripts. I hope that I get it working😂

@chacha and @Christoph_Bergmann: Is it possible for you to keep your script updated here in this thread? that would be very cool. many thanks!