From 1640ae70875354056a0f53a612c1f99377266a86 Mon Sep 17 00:00:00 2001 From: connorroy Date: Mon, 27 Oct 2025 19:57:11 +0000 Subject: [PATCH] On going --- src/climate.json | 21 +++++++---- src/device.json | 12 ++++++ src/main.py | 27 ++++++++++--- src/mqtt_manager.py | 92 +++++++++++++++++++++++++++++++++------------ src/pytevolve.py | 55 +++++++++++++++------------ 5 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 src/device.json diff --git a/src/climate.json b/src/climate.json index 09c420c..13225a8 100644 --- a/src/climate.json +++ b/src/climate.json @@ -1,15 +1,22 @@ { + "dev": { + "ids": ["ea334450945afc"], + "name": "atc smart controller", + "mf": "Tevolve" + }, + + "uniq_id": "", "name": "{name}", - "device_class": "climate", "state_topic": "homeassistant/climate/{uid}/state", "modes": ["heat", "off"], - "mode_command_topic": "{name}/heater/setmode", - "mode_state_topic": "{name}/heater/currentmode", + "mode_command_topic": "{uid}/heater/setmode", + "mode_state_topic": "{uid}/heater/currentmode", "min_temp": 7, "max_temp": 30, "temp_step": 0.5, - "current_temperature_topic": "{name}/heater/currenttemp", - "temperature_command_topic": "{name}/heater/settemp", - "temperature_state_topic": "{name}/heater/currentsettemp", - "action_topic": "{name}/heater/idleaction" + "current_temperature_topic": "{uid}/heater/currenttemp", + "temperature_command_topic": "{uid}/heater/settemp", + "temperature_state_topic": "{uid}/heater/currentsettemp", + "action_topic": "{uid}/heater/idleaction", + "availability_topic": "{uid}/availablity/state" } \ No newline at end of file diff --git a/src/device.json b/src/device.json new file mode 100644 index 0000000..30efdc7 --- /dev/null +++ b/src/device.json @@ -0,0 +1,12 @@ +{ + "dev": { + "ids": ["ea334450945afc"], + "name": "atc smart controller", + "mf": "Tevolve" + }, + "o": { + "name":"tevolve2mqtt" + }, + + "qos": 2 +} \ No newline at end of file diff --git a/src/main.py b/src/main.py index 779cd33..19e7269 100644 --- a/src/main.py +++ b/src/main.py @@ -1,21 +1,38 @@ from pytevolve import Tevolve from mqtt_manager import MqttManager +import threading + +host = "192.168.0.100" + if __name__ == "__main__": + + tevolve = Tevolve() - devices = tevolve.get_devices() + tevolve.get_token() + tevolve.get_dev() + tevolve.get_devices() + + status = tevolve.get_status() mqtt_manager = MqttManager() - mqtt_manager.client.connect(heaters=devices["nodes"]) + # threading.Thread(target=mqtt_manager.start_mqtt, args=(host,)).start() - tevolve.get_token() - tevolve.get_dev() + while 1: + if mqtt_manager.is_connected == True: + + mqtt_manager.publish_discovery(tevolve.devices) + + mqtt_manager.publish_heaters(status) + break + + # Update Device States + - mqtt_manager.publish_discovery() diff --git a/src/mqtt_manager.py b/src/mqtt_manager.py index 441bb1b..0066b80 100644 --- a/src/mqtt_manager.py +++ b/src/mqtt_manager.py @@ -1,65 +1,111 @@ import json from pathlib import Path import paho.mqtt.client as mqtt - +import threading class MqttManager: def __init__(self): self.host = "192.168.0.100" self.username = "connorroy" self.password = "arkreactor7" - + self.is_connected = False self.object_ids = None self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) self.client.on_connect = self.on_connect + self.client.ondisconnect = self.on_disconnect self.client.on_message = self.on_message self.client.username_pw_set(username=self.username, password=self.password) - # self.client.connect(self.host, 1883, 60) + self.client.connect(self.host, 1883, 60) + threading.Thread(target=self.client.loop_forever).start() + # self.client.loop_forever() - self.client.loop_forever() - def mqtt_connect(self): - pass - def on_connect(self, client, userdata, flags, reason_code, properties, heaters=None): - - self.object_ids = heaters - print(f"Connected with result code {reason_code}") + def publish_heaters(self, rad_data): + # self.object_ids = heaters discovery_topic = "homeassistant/climate/" json_payload = self._load_json() - for index, i in enumerate(self.object_ids): - json_topic = "hmd/climate/" + i["uid"] + for device_index, device in enumerate(self.object_ids["nodes"]): - self.client.publish(topic=discovery_topic + i["uid"] + "/config", qos=0, retain=False, - payload=json.dumps(json_payload[index])) + self.client.subscribe(topic=json_payload[device_index]["state_topic"]) - for device_index, device in enumerate(self.object_ids): + if device["lost"] is True: + + + self.client.publish(topic=json_payload[device_index]["availability_topic"], qos=0, retain=False, + payload="offline") + else: + self.client.publish(topic=json_payload[device_index]["availability_topic"], qos=0, retain=False, + payload="online") + self.client.subscribe(topic=json_payload[device_index]["mode_state_topic"]) + + + if rad_data[device_index][device["name"]]["mode"] == "manual": + mode = "heat" + else: + mode = "off" + + + + + self.client.publish(topic=json_payload[device_index]["mode_state_topic"], qos=0, retain=False, + payload=mode) + self.client.publish(topic=json_payload[device_index]["action_topic"], qos=0, retain=False, + payload="heating") + + + # self.client.subscribe(topic=json_payload[device_index]["current_temperature_topic"]) + + self.client.publish(topic=json_payload[device_index]["current_temperature_topic"], qos=0, retain=False, + payload="17") + + + + def on_connect(self, client, userdata, flags, reason_code, properties): + + print("Connected") + self.is_connected = True - print(json_payload[device_index]["current_temperature_topic"]) - self.client.publish(topic=json_payload[device_index]["current_temperature_topic"], qos=0, retain=False, - payload=json.dumps(json_payload[index])) # # Subscribing in on_connect() means that if we lose the connection and # # reconnect then subscriptions will be renewed. # client.subscribe("$SYS/#") + def on_disconnect(self, client, userdata, flags, reason): + print("Disconnected") + self.is_connected = False + + def on_message(self, client, userdata, msg): print(msg.topic + " " + str(msg.payload)) - def publish_discovery(self): - discovery_topic = "homeassistant/climate/" - for i in self.object_ids: - self.client.publish(topic=discovery_topic + i["uid"] + "/config", qos=0, retain=False) + "Adds devices via mqtt discovery" + def publish_discovery(self, heaters): + + self.object_ids = heaters + + path = Path(__file__).parent + with open(str(path) + "/device.json") as j: + data = json.load(j) + + climate_json = self._load_json() + for index, i in enumerate(self.object_ids["nodes"]): + + climate_json[index]["uniq_id"] = i["uid"] + discovery_topic = "homeassistant/climate/" + i["uid"] + "/config" + + + self.client.publish(topic=discovery_topic, payload=json.dumps(climate_json[index]), qos=0, retain=False) def _load_json(self): path = Path(__file__).parent @@ -67,7 +113,7 @@ class MqttManager: with open(str(path) + "/climate.json") as j: data = json.load(j) - for dev in self.object_ids: + for dev in self.object_ids["nodes"]: new_data = data.copy() for i in new_data: if "{name}" in str(new_data.get(i)): diff --git a/src/pytevolve.py b/src/pytevolve.py index 7486924..03c49f1 100644 --- a/src/pytevolve.py +++ b/src/pytevolve.py @@ -91,8 +91,6 @@ class Tevolve: x = requests.post(url, headers=headers, data=data) - print(x) - if x.status_code == 200: Tevolve.token = x.json()["access_token"] else: @@ -113,16 +111,20 @@ class Tevolve: def get_devices(self): - endpoint = "/devs/"+self.dev_id+"/mgr/nodes" + try: + endpoint = "/devs/"+self.dev_id+"/mgr/nodes" - header = {'content-type': 'application/json', - "Authorization": "Bearer " + self.token_primary} + header = {'content-type': 'application/json', + "Authorization": "Bearer " + self.token_primary} - devices_request = requests.get(str(self.api_url + endpoint), headers=header).json() + url = str(self.api_url + endpoint) + devices_request = requests.get(str(self.api_url + endpoint), headers=header).json() - self.devices = devices_request + self.devices = devices_request + + except: + raise - return self.devices @@ -248,24 +250,29 @@ class Tevolve: return "heat" - def get_status(self, heater_id): + def get_status(self): + result = [] + try: + for index ,i in enumerate(self.devices["nodes"]): + headers = { + 'host': 'api-tevolve.termoweb.net', + 'origin': 'https://tevolve.termoweb.net', + 'content-type': 'application/json', + 'accept': 'application/json, text/plain, */*', + 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) ' + 'Version/15.4 Safari/605.1.15', + 'authorization': 'Bearer ' + self.token_primary, + 'referer': 'https://tevolve.termoweb.net/', - headers = { - 'host': 'api-tevolve.termoweb.net', - 'origin': 'https://tevolve.termoweb.net', - 'content-type': 'application/json', - 'accept': 'application/json, text/plain, */*', - 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) ' - 'Version/15.4 Safari/605.1.15', - 'authorization': 'Bearer ' + self.token_primary, - 'referer': 'https://tevolve.termoweb.net/', + } + url = "https://api-tevolve.termoweb.net/api/v2/devs/"+self.dev_id+"/htr/"+str(i["addr"])+"/status" - } - url = "https://api-tevolve.termoweb.net/api/v2/devs/"+self.dev_id+"/htr/"+str(heater_id)+"/status" - - response = requests.get(url, headers=headers) - if response.status_code == 201 or response.status_code == 200: - return response.json() + response = requests.get(url, headers=headers) + if response.status_code == 201 or response.status_code == 200: + result.append({i["name"]: response.json()}) + except Exception as e: + raise e + return result @staticmethod def set_temperature(heater_id, set_temperature):