tydom2MQTT addon :D - Delta Dore Tydom to MQTT Broker

Addon fixed, and a quick and dirty script will ensure that script is restarted in case of mqtt kill or any unhandled error i could have missed.

I tried all day to make a clean build-on-update-on-hassio addon, with failed miserably with always a exec: fatal: unable to exec : No such file or directory with any shell (bashio, exelineb, sh, bash…).

So, it will work like that.

The addon use the exact same image as docker version, i recommand it to get easy logs on hassio interface.

0.88 avaiable in minutes, added sensors for all devices attributes (intrusion for covers, defects, outdoort temperature from alarm, zones, etc.), 1st update every sensor is going to be unavaiable, second update is good !

  • to try permission to map config drive, so maybe use !secret

0.89, last time i change sensor names, add to clear some of them !

Hello guys!! I’m trying @WiwiWillou addon right now and it works well for me. All covers popup immediatly and for the auto-update on boot i simply create an automation:

event: start
platform: homeassistant

data:
  payload: '{ "update" }'
  topic: hassio/tydom/update
service: mqtt.publish

And its perfect. The last thing i want to do (have) is my scenarios from tydom and the informations from my tywatt 2000 (external temperature and current consumption)

Thank you again, i waited this for a long time.

1 Like

Hey thanks, happy to have some feedback ! :smiley:

Scenarios will come soon, thanks to GiPe66 on github, + i/anyone brave enough have to create a light.py on the model of the others, implement the new device type on Messagehandler and boom :slight_smile:

But there is no script or scene on MQTT integration of Hassio, so i guess to expose them up as switches. What do you think ?

For “conso” endpoints (tywatt), i will check that too, but it will be sporadic (i have very little time at home).

I’m thinking about creating a generic tydom device, so every non supported device to date can popup even as a generic switch / sensor at start.

Will see. if anyone of you guys with some background on python can review my code to opimtise (i’m happy with performance but i guess you could still do better - and again i’m not a dev at all !), don’t hold up :stuck_out_tongue:

1 Like

Hi there,
when I try to start addon, I’ve “[FATAL tini (6)] exec python failed: Exec format error” with no other explanation. I’m on a home assistant docker installation so I follow the “Docker User” in the readme.
Did I need install something else ? Of course I put my credential in the addon option, I test my MQTT Brocker and it work fine. I’ve download all files and put them /share/tydom dir.
If u can help.
Thx

@WiwiWillou: I found a bug! 2 of my covers have the same id_endpoint but not the same id_device.

DEBUG:websockets.protocol:client < Frame(fin=False, opcode=0, data=b'to_shutter","last_usage":"shutter"},{"id_endpoint":1578853041,"first_usage":"shutter","id_device":1578853041,"name":"Haut","picto":"picto_shutter","last_usage":"shutter"},{"id_endpoint":1578853041,"first_usage":"shutter","id_device":1578853042,"name":"Salon","picto":"picto_shutter","last_usage":"shutter"}

can someone help me plzzz

Did you tried hassio addon ?

With docker you don’t need any file anywhere, just install.sh with your infos, sh install.sh and you should be good :slight_smile:

Just pushed hopefully some fixes.

Sensors seems broken for now…

I’m really sorry but I’m a real noob…

Yes I try Hassio addon, with my credential and start it : FATAL tini (6) or (7).

When I launch “sh install.sh” in putty answer is “docker invalid reference format” an it’s the same in the HomeAssistant Terminal
I’ve installed portainer and I see the container in it. When I try to start it… Fatal tini

Mmh… What’s your setup ? How is hassio installed ? And docker ?

I suspect you are on arm system and the image base I use is not, so I need to change the base, and I tried a lot to make a true addon with s6 and etc with no success.

If your are on a pi, change the base image to something arm to test, it’s the line with FROM.

I can’t do it now :frowning: does someone have experience with docker ? / Hassio addon)

Found a solution, we have to differentiate id_endpoint and id_device in PUT requests. This request worked for me.

DEBUG:websockets.protocol:client > Frame(fin=True, opcode=2, data=b'PUT /devices/1578853042/endpoints/1578853041/data HTTP/1.1\r\nContent-Length: 36\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n[{"name":"position","value":"10.0"}]\r\n\r\n', rsv1=False, rsv2=False, rsv3=False)

We need to put the id_device in all put methods from the class TydomWebSocketClient()
and complete devices class attribute with the id_device and the id_endpoint.

I’m docker installation home assistant (home assistant as docker container).
I try to put a line for armv7 in the build.json… no success same error. here is my config :

image

Big change for addon, old working version is now tagged x86, edge stay for now for test, but i can’t make it work.

The edge version should be multi arch, and based on hassio addon image for docker, so it WILL be the future system when it works, for now i can build it locally with docker built, but i can’t on hassio, error :slight_smile:

20-04-05 19:20:10 ERROR (SyncWorker_8) [supervisor.docker.addon] Can't build 0d3f33ad/amd64-addon-tydom2mqtt_hassio:test_hassio_base_0.90a: The command '/bin/bash -o pipefail -c apk add --no-cache python3' returned a non-zero code: 127

Hassio addon should work, with a annoying error but inoffensive (exec: fatal: unable to exec bashio
: No such file or directory)

@cadaver could you try it please ? it’s the tydom2MQTT beta version, should be multi arch now.

Correction, it runs locally but is broken afterwards…

WORKING !!!

It’s dirty how it works for now, the forever script is working, but if it crash it will stop logging.

I can’t make that run service.d to work, I don’t understand why…

Yes It working !! Tks so much.

Hello guys! @WiwiWillou I made some modification to take into account device_id and endpoint_id in covers. Here is the patch i made:

From 476db1afd56cb07d794fb8d0d296f1ec8bda7485 Mon Sep 17 00:00:00 2001
From: Max013
Date: Wed, 8 Apr 2020 13:57:49 +0200
Subject: [PATCH] Add endpoint_id and device_id in cover instead of id

---
 cover.py               | 51 ++++++++++++++++++++++--------------------
 forever.py             |  4 ++--
 mqtt_client.py         | 10 +++++++--
 tydomMessagehandler.py | 26 ++++++++++++---------
 tydom_websocket.py     |  8 +++----
 5 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/cover.py b/cover.py
index 97c875c..3f96d9a 100644
--- a/cover.py
+++ b/cover.py
@@ -3,11 +3,11 @@ import time
 from datetime import datetime
 from sensors import sensor
 
-cover_command_topic = "cover/tydom/{id}/set_positionCmd"
-cover_config_topic = "homeassistant/cover/tydom/{id}/config"
-cover_position_topic = "cover/tydom/{id}/current_position"
-cover_set_postion_topic = "cover/tydom/{id}/set_position"
-cover_attributes_topic = "cover/tydom/{id}/attributes"
+cover_command_topic = "cover/tydom/{device_id}_{endpoint_id}/set_positionCmd"
+cover_config_topic = "homeassistant/cover/tydom/{device_id}_{endpoint_id}/config"
+cover_position_topic = "cover/tydom/{device_id}_{endpoint_id}/current_position"
+cover_set_postion_topic = "cover/tydom/{device_id}_{endpoint_id}/set_position"
+cover_attributes_topic = "cover/tydom/{device_id}_{endpoint_id}/attributes"
 
 
 class Cover:
@@ -15,7 +15,8 @@ class Cover:
         
         self.attributes = tydom_attributes
 
-        self.id = self.attributes['id']
+        self.device_id = self.attributes['device_id']
+        self.endpoint_id = self.attributes['endpoint_id']
         self.name = self.attributes['cover_name']
         self.current_position = self.attributes['position']
         self.set_position = set_position
@@ -41,17 +42,19 @@ class Cover:
         self.device['manufacturer'] = 'Delta Dore'
         self.device['model'] = 'Volet'
         self.device['name'] = self.name
-        self.device['identifiers'] = self.id
+        self.device['identifiers'] = self.device_id
 
-        self.config_topic = cover_config_topic.format(id=self.id)
+        self.config_topic = cover_config_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id)
         self.config = {}
         self.config['name'] = self.name
-        self.config['unique_id'] = self.id
+        self.config['unique_id'] = self.device_id
+        self.config['device_id'] = self.device_id
+        self.config['endpoint_id'] = self.endpoint_id
         # self.config['attributes'] = self.attributes
-        self.config['command_topic'] = cover_command_topic.format(id=self.id)
-        self.config['set_position_topic'] = cover_set_postion_topic.format(id=self.id)
-        self.config['position_topic'] = cover_position_topic.format(id=self.id)
-        self.config['json_attributes_topic'] = cover_attributes_topic.format(id=self.id)
+        self.config['command_topic'] = cover_command_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id)
+        self.config['set_position_topic'] = cover_set_postion_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id)
+        self.config['position_topic'] = cover_position_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id)
+        self.config['json_attributes_topic'] = cover_attributes_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id)
 
         self.config['payload_open'] = "UP"
         self.config['payload_close'] = "DOWN"
@@ -75,13 +78,13 @@ class Cover:
             print(e)
 
 
-        self.position_topic = cover_position_topic.format(id=self.id, current_position=self.current_position)
+        self.position_topic = cover_position_topic.format(device_id=self.device_id, endpoint_id=self.endpoint_id, current_position=self.current_position)
         
         if (self.mqtt != None):
             self.mqtt.mqtt_client.publish(self.position_topic, self.current_position, qos=0, retain=True)
             # self.mqtt.mqtt_client.publish('homeassistant/sensor/tydom/last_update', str(datetime.fromtimestamp(time.time())), qos=1, retain=True)
             self.mqtt.mqtt_client.publish(self.config['json_attributes_topic'], self.attributes, qos=0)
-        print("Cover created / updated : ", self.name, self.id, self.current_position)
+        print("Cover created / updated : ", self.name, self.device_id, self.current_position)
 
         # update_pub = '(self.position_topic, self.current_position, qos=0, retain=True)'
         # return(update_pub)
@@ -92,7 +95,7 @@ class Cover:
         for i, j in self.attributes.items():
             # sensor_name = "tydom_alarm_sensor_"+i
             # print("name "+sensor_name, "elem_name "+i, "attributes_topic_from_device ",self.config['json_attributes_topic'], "mqtt",self.mqtt)
-            if not i == 'device_type' or not i == 'id':
+            if not i == 'device_type' or not i == 'device_id':
                 new_sensor = None
                 new_sensor = sensor(elem_name=i, tydom_attributes_payload=self.attributes, attributes_topic_from_device=self.config['json_attributes_topic'], mqtt=self.mqtt)
                 await new_sensor.update()
@@ -103,24 +106,24 @@ class Cover:
 
 
 
-    async def put_position(tydom_client, cover_id, position):
-        print(cover_id, 'position', position)
+    async def put_position(tydom_client, device_id, endpoint_id, position):
+        print(device_id, endpoint_id, 'position', position)
         if not tydom_client.connection.open:
             print('MQTT req : Websocket not opened, reconnect...')
             await tydom_client.connect()
-            await tydom_client.put_devices_data(cover_id, 'position', position)
+            await tydom_client.put_devices_data(device_id, endpoint_id, 'position', position)
 
         else:
             if not (position == ''):
-                await tydom_client.put_devices_data(cover_id, 'position', position)
+                await tydom_client.put_devices_data(device_id, endpoint_id, 'position', position)
 
-    async def put_positionCmd(tydom_client, cover_id, positionCmd):
-        print(cover_id, 'positionCmd', positionCmd)
+    async def put_positionCmd(tydom_client, device_id, endpoint_id, positionCmd):
+        print(device_id, endpoint_id, 'positionCmd', positionCmd)
         if not tydom_client.connection.open:
             print('MQTT req : Websocket not opened, reconnect...')
             await tydom_client.connect()
-            await tydom_client.put_devices_data(cover_id, 'positionCmd', positionCmd)
+            await tydom_client.put_devices_data(device_id, endpoint_id, 'positionCmd', positionCmd)
 
         else:
             if not (positionCmd == ''):
-                await tydom_client.put_devices_data(cover_id, 'positionCmd', positionCmd)
+                await tydom_client.put_devices_data(device_id, endpoint_id, 'positionCmd', positionCmd)
diff --git a/forever.py b/forever.py
index abc45dd..5fba71e 100644
--- a/forever.py
+++ b/forever.py
@@ -6,8 +6,8 @@ import sys
 filename = sys.argv[1]
 while True:
     print("\nStarting " + filename)
-    p = Popen("python3 " + filename, shell=True)
+    p = Popen("python3 -u " + filename, shell=True)
     p.wait()
 
 
-    #thanks https://ep.gnt.md/index.php/how-to-restart-python-script-after-exception-and-run-it-forever/
\ No newline at end of file
+    #thanks https://ep.gnt.md/index.php/how-to-restart-python-script-after-exception-and-run-it-forever/
diff --git a/mqtt_client.py b/mqtt_client.py
index 27be75c..df0bde7 100644
--- a/mqtt_client.py
+++ b/mqtt_client.py
@@ -111,7 +111,10 @@ class MQTT_Hassio():
             value = str(payload).strip('b').strip("'")
             get_id = (topic.split("/"))[2] #extract id from mqtt
             print(str(get_id), 'positionCmd', value)
-            await Cover.put_positionCmd(tydom_client=self.tydom, cover_id=get_id, positionCmd=str(value))
+            get_id = get_id.split("_")
+            device_id = get_id[0]
+            endpoint_id = get_id[1]
+            await Cover.put_positionCmd(tydom_client=self.tydom, device_id=device_id, endpoint_id=endpoint_id, positionCmd=str(value))
 
         elif ('set_position' in str(topic)) and not ('set_positionCmd'in str(topic)):
             
@@ -119,7 +122,10 @@ class MQTT_Hassio():
             value = json.loads(payload)
             print(value)
             get_id = (topic.split("/"))[2] #extract id from mqtt
-            await Cover.put_position(tydom_client=self.tydom, cover_id=get_id, position=str(value))
+            get_id = get_id.split("_")
+            device_id = get_id[0]
+            endpoint_id = get_id[1]
+            await Cover.put_position(tydom_client=self.tydom, device_id=device_id, endpoint_id=endpoint_id, position=str(value))
 
 
 
diff --git a/tydomMessagehandler.py b/tydomMessagehandler.py
index 3434fbf..7149080 100644
--- a/tydomMessagehandler.py
+++ b/tydomMessagehandler.py
@@ -17,7 +17,8 @@ deviceCoverKeywords = ['position','onFavPos','thermicDefect','obstacleDefect','i
 deviceCoverDetailsKeywords = ['onFavPos','thermicDefect','obstacleDefect','intrusion','battDefect']
 
 # Device dict for parsing
-device_dict = dict()
+device_name = dict()
+device_endpoint = dict()
 
 climateKeywords = ['temperature', 'authorization', 'hvacMode', 'setpoint']
 
@@ -201,12 +202,13 @@ class TydomMessageHandler():
             # Get list of shutter
             if i["last_usage"] == 'shutter':
                 # print('{} {}'.format(i["id_endpoint"],i["name"]))
-                # device_dict[i["id_endpoint"]] = i["name"]
-                device_dict[i["id_device"]] = i["name"]
+                # device_name[i["id_endpoint"]] = i["name"]
+                device_name[i["id_device"]] = i["name"]
+                device_endpoint[i["id_device"]] = i["id_endpoint"]
                 # TODO get other device type
             if i["last_usage"] == 'alarm':
                 # print('{} {}'.format(i["id_endpoint"], i["name"]))
-                device_dict[i["id_endpoint"]] = "Tyxal Alarm"
+                device_name[i["id_endpoint"]] = "Tyxal Alarm"
         print('Configuration updated')
         
     async def parse_devices_data(self, parsed):
@@ -221,13 +223,14 @@ class TydomMessageHandler():
                     for elem in i["endpoints"][0]["data"]:
                         
                         endpoint_id = None
+                        device_id = None
                         elementName = None
                         elementValue = None
                         elementValidity = None
 
                         # Get full name of this id
                         # endpoint_id = i["endpoints"][0]["id"]
-                        endpoint_id = i["id"] # thanks @azrod
+                        device_id = i["id"] # thanks @azrod
                         # Element name
                         elementName = elem["name"]
                         # Element value
@@ -249,13 +252,16 @@ class TydomMessageHandler():
                             # */
                             #TODO : Sensors with everything
                             print_id = None
-                            name_of_id = self.get_name_from_id(endpoint_id)
+                            name_of_id = self.get_name_from_id(device_id)
                             if len(name_of_id) != 0:
                                 print_id = name_of_id
+                                endpoint_id = device_endpoint[device_id]
                             else:
-                                print_id = endpoint_id
+                                print_id = device_id
+                                endpoint_id = device_id
 
-                            attr_cover['id'] = i["id"]
+                            attr_cover['device_id'] = device_id
+                            attr_cover['endpoint_id'] = endpoint_id
                             attr_cover['cover_name'] = print_id
                             attr_cover['name'] = print_id
                             attr_cover['device_type'] = 'cover'
@@ -390,8 +396,8 @@ class TydomMessageHandler():
     # Get pretty name for a device id
     def get_name_from_id(self, id):
         name = ""
-        if len(device_dict) != 0:
-            name = device_dict[id]
+        if len(device_name) != 0:
+            name = device_name[id]
         return(name)
 
 
diff --git a/tydom_websocket.py b/tydom_websocket.py
index d610472..006ef2f 100644
--- a/tydom_websocket.py
+++ b/tydom_websocket.py
@@ -216,7 +216,7 @@ class TydomWebSocketClient():
         return 0
 
     # Give order (name + value) to endpoint
-    async def put_devices_data(self, endpoint_id, name, value):
+    async def put_devices_data(self, device_id, endpoint_id, name, value):
         if not self.connection.open:
 
             print('Connection closed on PUT trial, exiting to ensure restart....')
@@ -227,7 +227,7 @@ class TydomWebSocketClient():
         # For shutter, value is the percentage of closing
         body="[{\"name\":\"" + name + "\",\"value\":\""+ value + "\"}]"
         # endpoint_id is the endpoint = the device (shutter in this case) to open.
-        str_request = self.cmd_prefix + "PUT /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: ".format(str(endpoint_id),str(endpoint_id))+str(len(body))+"\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"+body+"\r\n\r\n"
+        str_request = self.cmd_prefix + "PUT /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: ".format(str(device_id),str(endpoint_id))+str(len(body))+"\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"+body+"\r\n\r\n"
         a_bytes = bytes(str_request, "ascii")
         print(a_bytes)
         await self.connection.send(a_bytes)
@@ -384,9 +384,9 @@ class TydomWebSocketClient():
         await self.get_devices_data()
 
     # Give order to endpoint
-    async def get_device_data(self, id):
+    async def get_device_data(self, device_id, endpoint_id):
         # 10 here is the endpoint = the device (shutter in this case) to open.
-        str_request = self.cmd_prefix + "GET /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n".format(str(id),str(id))
+        str_request = self.cmd_prefix + "GET /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n".format(str(device_id),str(endpoint_id))
         a_bytes = bytes(str_request, "ascii")
         await self.connection.send(a_bytes)
         # name = await websocket.recv()
-- 
2.25.1.windows.1