Zemismart Roller Shade Integration

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 this 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.

  1. Download or clone the repository:
  2. In the directory run pip install -r requirements.txt
  3. Configure details in rollshade.py and then run with python rollshade.py

Just run:

python rollshade.py

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

cover:
  - platform: mqtt
    name: "Blinds Bedroom"
    state_topic: "blinds/00:00:00:00:00:00/status"
    command_topic: "blinds/00:00:00:00:00:00"
    qos: 0
    state_open: "on"
    state_closed: "off"
    payload_open: "open"
    payload_close: "close"
    payload_stop: "stop"
    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 Someone already did a great job doing this!! https://github.com/buxtronix/am43

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.

Run on boot

  • In case someone is interested, just used did the following steps to run the script as service so it starts when reboot:
sudo nano /lib/systemd/system/rollershade.service

Add the following lines into that file:

[Unit]
Description=Rollshade
After=multi-user.target
 
[Service]
Type=simple
ExecStart=/usr/bin/python /home/pi/zemirollshade/rollshade.py

Restart=on-abort
 
[Install]
WantedBy=multi-user.target

Now we need to active the service:

sudo chmod 644 /lib/systemd/system/rollshade.service
chmod +x /home/pi/zemirollshade/rollshade.py
sudo systemctl daemon-reload
sudo systemctl enable rollshade.service
sudo systemctl start rollshade.service

Other
Thanks @ErikNL, @stcbus and @ngylling for the support! :slight_smile: I just adapt my original post to use the version from @stcbus!

5 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):
pi@raspberrypi:~ $ 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

Great!! I make the changes to my repository with your code to work with Python 3! And good idea about using covers! Looks like there is an MQTT Cover, I will see how to use that one!

Just received one of the Zemismart blind motors, and planning to try it using this method.
A couple of newbie questions, though, never having used a python script in HA :wink:

  • where should I put the rollshade.py file within the file structure?
  • any need to start rollshade.py, or does it happen automatically?

Thanks in advance.
Andrew

Sorry for the late reply!! Right now I’m running the scrip in a different machine, in a cheap Raspberry Pi Zero W because my blinds are away from my HA install and Bluetooth range is limited! If you want to start from scratch my suggestion is to take a look at this guide: Set Up Raspberry Pi From Scratch posted by @andrewjfreyer in his “Monitor” guide. I’m not sure how to install this one int he Hassio because of the docker structure, but I will try to do some research for the future! :slight_smile:

Thanks, I’ll give that a try.

Nice work!
I just received the Zemismart roller shade this week and I thought I would add it to my Home Assistant setup. I’m hosting Home Assistant on my Raspberry Pi 3 B+. This has built in Bluetooth, so I want it to control my shades directly. But it may not be possible due to the limited Bluetooth range.
I have made a python package to communicate with the shades (I have not integrated it with Home Assistant yet). This can like your script close and open the shade, and it can set it to a specific position. It can also get the current position and battery status (but this is not working perfectly).
Next step is to integrate it with Home Assistant :slight_smile:
You may be able to use it with your integration.

1 Like

Wow! That is great! Has the commands that I was missing to add, I will try to add them to my script! :slight_smile:

Good work, @ngylling, I’ll give this a try.
BTW, which version of Python is required? (Sorry if I’m missing this somewhere.)
Very interested in the integration with Home Assistant!

@AndrewJ I don’t think you are missing something. I have been using python 3. I don’t know the exact version, probably the latest one.

I have a working integration with Home Assistant as a Cover device. I will post it when I have fixed small issues.

Many thanks. I have both python 2.7 and python 3 on my system, so I’ll do like you and use python 3. But I could use some advice if you don’t mind - I’m new to python - how should I install and run your package, please?

I’m new to python packages too. But as I understand you should be in the directory with the requirements.txt file and setup.py file. Here you can write a python script, where you import the package as described in the README.md file. If you want to install the package I think you can write the following while being in the same directory as mentioned before

pip install .

I don’t know if it should be pip or pip3

That helps me, thank you. I guess it will be pip3 for the install, I’ll try that first when I get some free time (probably tomorrow). Thanks for your help.

I have it working thanks for the hard work.
What would be realy nice if this could be done on an esp32 with esp home they are a bit smaller then the pi

Great that worked!! Agreed on the ESP32, I tried to do once but couldn’t make it work, it’s in my todo list! Maybe I should try to contact someone from ESP Home team and see how to add as one of their sensors!

Hey all, if you’re interested, since the integration originally posted here by @ccmtozzi didn’t work for me (maybe wrong pin code?), I modified it to use the excellent python package by @ngylling and kept the script around for the mqtt bits. Obviously an integration in HASS would be ideal, but it’s outside my skill level for now :slight_smile:

I forked both of them to both make the changes and also added a clean disconnect for the one by @ngylling. If they accept my pull request, I can change it to grab the package from their original repository. I also in my config set it up as a “cover” to get native functionality. I’m running the script now in supervisord.

That is great!! :slight_smile: I will certainly give it a try!!

Many thanks for your work on this @stcbus, @ccmtozzi and @ngylling .
I installed the stcbus/zemirollshade and a cover in Home Assistant and it’s working well.
I modified rollshade.py slightly to get the stop function working (for some reason initially HA was sending STOP in uppercase and rollershade.py was looking for lowercase, so I changed the if… line to accept both.)
I’m setting it up as a service in systemd so it will (hopefully!) restart automatically after a reboot.

Does anyone have any ideas how to read the battery state into HA? :smiley: