New binding: Selve rollershutter binding

Tags: #<Tag:0x00007f616d5841e0> #<Tag:0x00007f616d597038>

Hello board,
I’m an openHab user for some time now. Now I got something new … an USB dongle, that contains a Gateway solution for Selve rollershutter RF motors.
The USB key is basically a virtual Serial port towards the Operating System. I tested it with a current Debian release and it automatically creates a virtual Serial port (/dev/tty ….).

The commands for the rollershutters is passed in XML (using screen or minicom or putty or or or…) to the RF Gateway.
The Vendor selve offers a Pretty good documentation regarding the API
(https://www.selve.de/de/service/software-updates/service-entwicklungstool-commeo-usb-rf-gateway/) - sorry it’s in German.

So the Question is … should I use an existing “Serial” binding for it? From my Point of view it is more user friedly to develop a Special binding for this, that a user must not the bothered with the API directly. What do you think?

I’m a Network Consultant and used to program in Python, C#, PowerShell and of curse the litte bash helpers. However, I’m completely new to Java but I’m keen to give it a try.

So here are some Questions:

  • Do you think this binding is worth it? Meaning is there a user base, that is interested in it?
  • It it better to not do it and use an existing General Serial binding (and probably write a how-to for the Integration of the “selve USB key” as a use-case?
  • A good hint how to start. As i wrote before, I’m started here: https://www.openhab.org/docs/developer/contributing/contributing.html#the-repositories
  • Which Namespace to Chose?

Thanks in Advance
netgab / joe

P.S.: Don’t know why my browser auto-corrects the upper- and lowercase in this text in a weird way … just ignore the incorrect case-sensitivity :slight_smile:

1 Like

That’s a good way to prototype and work through the details of the API. Once you know the ins and outs of the API it might inform design decisions when you start to build the binding itself.

There is at least one user who is interested in it, you. And binds are like Field of Dreams, if you build it they will come.

Like I said, that isn’t a bad approach. It lets you focus on one thing (the API) without needing to tackle the added complexity of learning Java and working with the OH libraries and such. Several bindings developed this way.

Read all of the articles under Developer Guide.

Follow the convention the other bindings use. I think it would be org.openhab.binding.selve but verify.

Good luck!

Hi Johannes @netgab,
did you start (or have even finished) building the selve roller shutter binding?
Thanks for your reply!
Christoph

Hi Christoph @Syn,
sorry - not even started with the binding (too lazy learning Java) :slight_smile:
However I managed to get it working using the # Serial Binding.

Are you interested in the details?

Yes, I will get some selve blinds shortly. So I would be very happy, if you could share your way of making them work with openhab!

Well … that’s good … finally there’s a reason to document the whole stuff because someone is interested.

**Optional: Device symlink **
First of all my openhab runs on a Debian Linux system. You have to make sure, that the USB key is recognized by the system (e.g. dmesg) or lsusb.
Because the device name of the USB key must be hardcoded in the openhab application I make sure, that a generic symlink is created for the /dev/ttyUSB device, which is used by openhab. Example:

$ ls /dev/ttyUSB -la
crw-rw-rw- 1 root dialout 188, 0 Jan 10 06:26 /dev/ttyUSB0
lrwxrwxrwx 1 root root         7 Dec  8 15:55 /dev/ttyUSB_selve -> ttyUSB0

So in my case I tell openhab to use the device /dev/ttyUSB_selve. For most Debian systems this can be acomplished using an udev rule. I created a new file:
/etc/udev/rules.d/10-USBSelve_TTY.rules
SUBSYSTEM==“tty”, ATTRS{idVendor}==“0403”, ATTRS{idProduct}==“6015”, ATTRS{serial}==“DM00B3ZB”, ATTRS{product}==“FT230X Basic UART”, SYMLINK+=“ttyUSB_selve”, GROUP=“dialout”, MODE=“0666”

You have to edit the idVendor, idProduct, product and serial attribute. The values can be obtained by the dmesg output when plugging in the key. An alternative is:
udevadm info --name=/dev/ttyUSB --attribute-walk

**Basic config (Linux): **
In the file “/etc/default/openhab2” I had to add the serial port to the JAVA_OPTS variable by adding the line (section JAVA OPTIONS for good documentation)
EXTRA_JAVA_OPTS="-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB_selve"
==> Openhab needs to be restarted now

Items file (/etc/openhab2/items/selve.items)
Example for one rollershutter:

Rollershutter  rollershutter_selve_living_state     { serial="/dev/ttyUSB_selve@115200,UP(<methodCall><methodName>selve.GW.iveo.commandManual</methodName><array><base64>AQAAAAAAAAA=</base64><int>1</int></array></methodCall>),DOWN(<methodCall><methodName>selve.GW.iveo.commandManual</methodName><array><base64>AQAAAAAAAAA=</base64><int>2</int></array></methodCall>),STOP(<methodCall><methodName>selve.GW.iveo.commandManual</methodName><array><base64>AQAAAAAAAAA=</base64><int>0</int></array></methodCall>)"}

Basically the base64 string (example above AQAAAAAAAAA=) controls which rollershutter to use. This is documented in the Selve XML API documentation.
In very short:
Per rollershutter you assign a channel in the USB RF gateway. More or less this is the same as when you program a selve remote control. I won’t go into details here, because this is not an openhab specific issue. This is all selve black magic. Read the selve documentation for this :slight_smile: One little hint: What helps is the XML API Windows Tool from selve to debug the whole stuff. (https://www.selve.de/en/service/software-updates/service-development-tool-commeo-usb-rf-gateway/)

Back to the Base64 string:
The channel ID mask is 8 Byte long. Each bit of represents one channel ID. If the bit in the mask is set to “1” to corresponding channel is used for the operation.
Now the brainf*** begins. First of all you need to determine the right bit for the corresponding channel. As documented in the XML API of selve here are some examples for the mask:
Byte 0.0 IveoID 0
Byte 0.7 IveoID 7
Byte 1.0 IveoID 8
Byte 7.7 IveoID 63

Now… how to get the base64 string for one (or a set of IDs)? Examples:
Channel 1 (Mask address Byte block index 0 / first bit [least significant])
Hex: 01 00 00 00 00 00 00 00
Bin: 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Base64: AQAAAAAAAAA=

Channel 2 (Mask address Byte block index 0 / second bit [least significant])
Hex: 02 00 00 00 00 00 00 00
Bin: 00000010 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Base64: AgAAAAAAAAA=

Channel 3 (Mask address Byte block index 0 / third bit [least significant])
Hex: 04 00 00 00 00 00 00 00
Bin: 00000100 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Base64: BAAAAAAAAAA=

[…]

Channel 11 (Mask address Byte block index 1 / third bit [least significant])
Hex: 00 04 00 00 00 00 00 00
Bin: 00000000 00000100 00000000 00000000 00000000 00000000 00000000 00000000
Base64: AAQAAAAAAAA=

Of course you can control multiple channels at the same time.
Example Channel 1 and 2
Hex: 03 00 00 00 00 00 00 00
Bin: 00000011 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Base64: AwAAAAAAAAA=

Hex to Base64 conversion: https://cryptii.com/pipes/base64-to-hex

Again - please read and understand the XML API and the USB key itself! :slight_smile:
https://www.selve.de/en/service/software-updates/service-development-tool-commeo-usb-rf-gateway/

2 Likes

Thanks a lot! When the blinds are installed, I will have a closer look. And maybe I’m going to bother you with some questions :wink:

1 Like

Thanks again for your guide, it has helped me a lot. Let me add some notes and things I stumbled upon:

First of all: don’t forget to install the serial binding :wink:

Second, regarding the udev rule: be aware of double quotes, when you paste and copy (" vs. and ) :upside_down_face:

Third the selve stuff itself:
I have commeo roller shutters, and I don’t need the whole base64 stuff (you could use it, but you haven’t) and there are of course other API calls.
So maybe it is helpful for other users to post the way I put it together (maybe there’s a better way to get the actual state of the roller shutters?!):

selve.items

String          rollershutter_selve_gw_responses     { serial="/dev/ttyUSB_selve@115200"}
String          rollershutter_selve_gw_command     { serial="/dev/ttyUSB_selve@115200"}

Rollershutter  rollershutter_selve_WZi_li     { serial="/dev/ttyUSB_selve@115200,UP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>0</int><int>1</int><int>1</int><int>0</int></array></methodCall>),DOWN(<methodCall><methodName>selve.GW.command.device</methodName><array><int>0</int><int>2</int><int>1</int><int>0</int></array></methodCall>),STOP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>0</int><int>0</int><int>1</int><int>0</int></array></methodCall>)"}
Switch         rollershutter_selve_WZi_li_pos1   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>0</int><int>3</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Switch         rollershutter_selve_WZi_li_pos2   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>0</int><int>5</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Number         rollershutter_selve_akt_WZi_li   "Aktuelle Position WZi li [SCALE(selve.scale):%s]" <rollershutter>

Rollershutter  rollershutter_selve_WZi_re     { serial="/dev/ttyUSB_selve@115200,UP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>1</int><int>1</int><int>1</int><int>0</int></array></methodCall>),DOWN(<methodCall><methodName>selve.GW.command.device</methodName><array><int>1</int><int>2</int><int>1</int><int>0</int></array></methodCall>),STOP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>1</int><int>0</int><int>1</int><int>0</int></array></methodCall>)"}
Switch         rollershutter_selve_WZi_re_pos1   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>1</int><int>3</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Switch         rollershutter_selve_WZi_re_pos2   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>1</int><int>5</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Number         rollershutter_selve_akt_WZi_re   "Aktuelle Position WZi re [SCALE(selve.scale):%s]" <rollershutter>

Rollershutter  rollershutter_selve_Kue_li     { serial="/dev/ttyUSB_selve@115200,UP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>2</int><int>1</int><int>1</int><int>0</int></array></methodCall>),DOWN(<methodCall><methodName>selve.GW.command.device</methodName><array><int>2</int><int>2</int><int>1</int><int>0</int></array></methodCall>),STOP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>2</int><int>0</int><int>1</int><int>0</int></array></methodCall>)"}
Switch         rollershutter_selve_Kue_li_pos1   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>2</int><int>3</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Switch         rollershutter_selve_Kue_li_pos2   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>2</int><int>5</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Number         rollershutter_selve_akt_Kue_li   "Aktuelle Position Kue li [SCALE(selve.scale):%s]" <rollershutter>

Rollershutter  rollershutter_selve_Kue_re     { serial="/dev/ttyUSB_selve@115200,UP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>3</int><int>1</int><int>1</int><int>0</int></array></methodCall>),DOWN(<methodCall><methodName>selve.GW.command.device</methodName><array><int>3</int><int>2</int><int>1</int><int>0</int></array></methodCall>),STOP(<methodCall><methodName>selve.GW.command.device</methodName><array><int>3</int><int>0</int><int>1</int><int>0</int></array></methodCall>)"}
Switch         rollershutter_selve_Kue_re_pos1   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>3</int><int>3</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Switch         rollershutter_selve_Kue_re_pos2   { serial="/dev/ttyUSB_selve@115200,ON(<methodCall><methodName>selve.GW.command.device</methodName><array><int>3</int><int>5</int><int>1</int><int>0</int></array></methodCall>)", expire="15s,OFF"}
Number         rollershutter_selve_akt_Kue_re   "Aktuelle Position Kue re [SCALE(selve.scale):%s]" <rollershutter>

selve.rules

rule "roller shutter"
when
    Item rollershutter_selve_gw_responses received update
then
val String xml = rollershutter_selve_gw_responses.state.toString
    if(xml.contains("selve.GW.device.getValues")){
        val String rollade = xml.split("<").get(18).replace("string>", "")
        val rollade_status = xml.toString.split("<").get(10).replace("int>", "")
        if(rollade == "WZi li"){
            rollershutter_selve_akt_WZi_li.postUpdate(rollade_status)
        }
        else if(rollade == "WZi re"){
            rollershutter_selve_akt_WZi_re.postUpdate(rollade_status)
        }
        else if(rollade == "Kue li"){
            rollershutter_selve_akt_Kue_li.postUpdate(rollade_status)
        }
        else if(rollade == "Kue re"){
            rollershutter_selve_akt_Kue_re.postUpdate(rollade_status)
        }
    } else if(xml.contains("selve.GW.command.device")){
//        do nothing
    } else if(xml.contains("selve.GW.event.device")){
        val String rollade = xml.split("<").get(18).replace("string>", "")
        val rollade_status = xml.split("<").get(10).replace("int>", "")
        if(rollade == "WZi li"){
            rollershutter_selve_akt_WZi_li.postUpdate(rollade_status)
        }
        else if(rollade == "WZi re"){
            rollershutter_selve_akt_WZi_re.postUpdate(rollade_status)
        }
        else if(rollade == "Kue li"){
            rollershutter_selve_akt_Kue_li.postUpdate(rollade_status)
        }
        else if(rollade == "Kue re"){
            rollershutter_selve_akt_Kue_re.postUpdate(rollade_status)
        }
    }
end

rule "roller shutter cron"
when
    Time cron "0 0/15 * 1/1 * ? *"
then
    rollershutter_selve_gw_command.sendCommand("<methodCall><methodName>selve.GW.device.getValues</methodName><array><int>0</int></array></methodCall>")
    Thread::sleep(220)
    rollershutter_selve_gw_command.sendCommand("<methodCall><methodName>selve.GW.device.getValues</methodName><array><int>1</int></array></methodCall>")
    Thread::sleep(220)
    rollershutter_selve_gw_command.sendCommand("<methodCall><methodName>selve.GW.device.getValues</methodName><array><int>2</int></array></methodCall>")
    Thread::sleep(220)
    rollershutter_selve_gw_command.sendCommand("<methodCall><methodName>selve.GW.device.getValues</methodName><array><int>3</int></array></methodCall>")
end

selve.scale

[..51]=0%
]51..6553]=10%
]6553..13106]=20%
]13106..19659]=30%
]19659..26212]=40%
]26212..32765]=50%
]32765..39318]=60%
]39318..45871]=70%
]45871..52424]=80%
]52424..58977]=90%
[58977..65535]=100%

Hi everybody,

first of all thanks for this selve example. I used this example on my raspberry pi 3, but I had some problems with the strings from the serial binding. I was not able to proceed the data until I removed all of the tabs, spaces from the selve gateway.
The solution was the addition of the following line in the rule: xml.replaceAll(’(\r\n|\n\r|\r|\n|\t)’, ‘’)

    ...    
    var String xml = rollershutter_selve_gw_responses.state.toString
    xml = xml.replaceAll('(\r\n|\n\r|\r|\n|\t)', '')
    if(xml.contains("selve.GW.device.getValues")){
    ...

Now the selve gateway works without any problems. thx