Zemismart Roller Shade Integration

Tags: #<Tag:0x00007f7c5dcd79a0>

What?
This is a pythons script to connect an Raspberry PI to an Zemismart Roller Shade, it’s listen to an MQTT topic and execute a close or open command based on that topic.

Why?
The Zemismart Roller shade are great and have a good price (Thanks @TheHookUp video I discovered them!), but they only have bluetooth connection! :frowning: this script help integrate them to HA using a Raspberry PI (I’m currently using a cheap RPi W). This is my first “public” script and so far has been a long journey to make it work!! My programming skills are kind of limit, but with a lot of copy/past and google I made it happen!! :slight_smile:

Where do I get it??
Here is the link to my github project link! First time using github, so hope I don’t mess up too much… like I did uploading my password in there last week!

How do I install?
So, you will need an Raspberry PI or any machine running linux, so far I follow this guide (Set Up Raspberry Pi From Scratch) posted by @andrewjfreyer.

After setting up the Rapsberry, just add some other stuff and you should be good to go!

  • paho-mqtt (pip install paho-mqtt)
  • bluepy (sudo pip install bluepy)
  • libglib2.0-dev (sudo apt-get install libglib2.0-dev)

The file and the config it’s one script: rollshade.py

Just run:

python rollshade.py

In HA you can add some switch with each one of the shades, just add this:

- platform: mqtt
  name: "Curtain Bedroom"
  state_topic: "curtains/00:00:00:00:00:00/status"
  command_topic: "curtains/00:00:00:00:00:00"
  qos: 0
  state_on: "on"
  state_off: "off"
  payload_on: "open"
  payload_off: "close"
  retain: false
  optimistic: false

What next?

  • Get information from the Shade (battery, state, etc…), currently it’s just sending command, for open and close.
  • ESP32 version - Would prefer to run this in a ESP32 instead of a Raspberry PI

Know issues

  • Since I can’t get information on current state and home assistant start with Off, if the shade is Open (On) you need to rapidly click the button twice to make it close and sync the state.
2 Likes

Nice work! I did some toying around with it, I got the script running without errors, but it seems like nothing is happening when I send the mqtt topic/payload. Could it be that I would have to change this line in the code:

if ch.uuid == “0000fe51-0000-1000-8000-00805f9b34fb”:

it seems like this would be the address of the device.

Edit:

After some more toying around, I figured out that that uuid is not the device address, you already implemented it in such a way that the device address is in the mqtt topic, nice!

I do however struggle to get my roller shades to do anything, so to figure out what was wrong, I boiled your python script down into this simple test script:

from bluepy import *
import time
import re

dev = btle.Peripheral('02:7F:27:8E:78:FE')
chs = dev.getCharacteristics()
for ch in chs:
  if ch.uuid == "0000fe51-0000-1000-8000-00805f9b34fb":
    close = "\x00\xff\x00\x00\x9a\x0d\x01\x64\xf2"
    ch.write(close)

dev.disconnect()

Connecting seems to work, but I get the error:

TypeError: a bytes-like object is required, not ‘str’

I tried changing the code to:

close = "\x00\xff\x00\x00\x9a\x0d\x01\x64\xf2".encode()

That made the error disappear, but the roller isn’t doing anything.
I was wondering where you got the uuid and the commands from. Are they the same for every unit?

Below are some suggestions:

  1. Always test using open or close, sometimes the close actually opens them and vice-versa
  2. When I first extract the command sniffing the BLE the items were: “00ff00009a0d010096” and “00ff00009a0d0164f2”, after various testing in the python I notice I needed the \x, since they are bytes.
  3. I’m using the standard password, 8888, I think this is part of this “command” since I don’t use anywhere else!
  4. Try using gatttool, below is the code (replace with your mac):
[email protected]:~ $ gatttool -I -b 02:62:56:81:4F:35
[02:62:56:81:4F:35][LE]> connect
Attempting to connect to 02:62:56:81:4F:35
Connection successful
[02:62:56:81:4F:35][LE]> char-write-cmd 0x000e 00ff00009a0d0164f2
Notification handle = 0x000e value: 9a 0d 01 5a 31

The Handle 0x000e it’s the one related to the UUID 0000fe51-0000-1000-8000-00805f9b34fb, you can list the characteristics to double check!

[02:62:56:81:4F:35][LE]> characteristics
handle: 0x0002, char properties: 0x20, char value handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x4e, char value handle: 0x0007, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0008, char properties: 0x4e, char value handle: 0x0009, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x000a, char properties: 0x02, char value handle: 0x000b, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x16, char value handle: 0x000e, uuid: 0000fe51-0000-1000-8000-00805f9b34fb
handle: 0x0010, char properties: 0x08, char value handle: 0x0011, uuid: 0000fe52-0000-1000-8000-00805f9b34fb

Turned out I was using python 3, while your script was probably written for python 2.
I forked your repository and made some changes to make it work with python 3: https://github.com/eriknl1982/zemirollshade

It now works like a charm :smiley: , thanks for the great work!

After making a switch like you described, you can also easily use that switch to create a cover entity:

cover:
  - platform: template
    covers:
      woonkamer_links_achter:
        friendly_name: "Rolgordijn links achter"
        open_cover:
          service: homeassistant.turn_off
          entity_id: switch.gordijn_woonkamer_links_achter
        close_cover:
          service: homeassistant.turn_on
          entity_id: switch.gordijn_woonkamer_links_achter

That way, you can also see it as a cover in the front-end:

image