tydom2MQTT addon :D - Delta Dore Tydom to MQTT Broker

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

Hey thanks, could you do a pull request pls ?

Not easily :frowning:. Can’t you apply my patch directly?

^^ I don’t know how ! Just drag and drop your modded files on my github page directly, it will ask for PR :slight_smile:

in the new release :

MQTT - Deprecated features from MQTT platforms are now removed. - (@emontnemery - #32909) (mqtt docs 1)

For MQTT lights setup through MQTT discovery, it’s no longer possible to specify schema with platform configuration variable. Instead, schema must be specified by the schema configuration variable.
For MQTT alarm_control_panel, MQTT binary_sensor and MQTT sensor set up through MQTT discovery: The undocumented implicit setting of state_topic is no longer supported. Instead, state_topic must be explicitly set.
Support for json_attributes is removed from MQTT sensor, json_attributes_topic should be used instead.

Maybe something to change ? :slight_smile:

Nope, already anticiped that one ! :slight_smile:

1 Like

Hello after the update I’ve this log message : " exec: fatal: unable to exec bashio
: No such file or directory".
The addon is still working.
I’ve a question : is that normal that i need to clic 2 times for down or up my covers ? 1 time nothing happend, and 2nd time its ok.
Thx again for ur work.

Mmh… Ils there any other normal log ?

That issue is known, for now I can’t use the service part of s6overlay that is default in hassio add-ons.

Rebuild it, it should be fixed

after rebuild addon wont start anymore :
s6-supervise tydom2mqtt: warning: unable to spawn ./run - waiting 10 seconds
s6-supervise (child): fatal: unable to exec run: No such file or directory
s6-maximumtime: warning: unable to wait for child process: Operation timed out

Ok retry !

Yes it work.
Tks.

Pls send me your files !

I will rework a lot of the base, and I could use some help to do a proper integration, with a common base with MQTT :

  • Isolate Tydom client to just instanciate and forget.
  • take the flow logic out in a separate py (connection, listen for incoming, heartbeat…)
  • I need the home assistant part (integration), I don’t understand yet how it is handled with a push logic from Tydom to HASS (hass create devices from a dict returned from library (instanciated at init ?) then use the provided function to update ?)

Here my Integration of French data source :slight_smile:
https://github.com/max5962/prixCarburant-home-assistant
Very simple exemple and works well.

1 Like

Thanks !!

But it’s a poll logic no ? Not push (i.e. Hass is the one to fetch the data, data does not come at it alone)

yes you are right
regularly update => PULL