This commit is contained in:
2025-10-27 19:57:11 +00:00
parent a46405f4fd
commit 1640ae7087
5 changed files with 148 additions and 59 deletions

View File

@@ -1,15 +1,22 @@
{ {
"dev": {
"ids": ["ea334450945afc"],
"name": "atc smart controller",
"mf": "Tevolve"
},
"uniq_id": "",
"name": "{name}", "name": "{name}",
"device_class": "climate",
"state_topic": "homeassistant/climate/{uid}/state", "state_topic": "homeassistant/climate/{uid}/state",
"modes": ["heat", "off"], "modes": ["heat", "off"],
"mode_command_topic": "{name}/heater/setmode", "mode_command_topic": "{uid}/heater/setmode",
"mode_state_topic": "{name}/heater/currentmode", "mode_state_topic": "{uid}/heater/currentmode",
"min_temp": 7, "min_temp": 7,
"max_temp": 30, "max_temp": 30,
"temp_step": 0.5, "temp_step": 0.5,
"current_temperature_topic": "{name}/heater/currenttemp", "current_temperature_topic": "{uid}/heater/currenttemp",
"temperature_command_topic": "{name}/heater/settemp", "temperature_command_topic": "{uid}/heater/settemp",
"temperature_state_topic": "{name}/heater/currentsettemp", "temperature_state_topic": "{uid}/heater/currentsettemp",
"action_topic": "{name}/heater/idleaction" "action_topic": "{uid}/heater/idleaction",
"availability_topic": "{uid}/availablity/state"
} }

12
src/device.json Normal file
View File

@@ -0,0 +1,12 @@
{
"dev": {
"ids": ["ea334450945afc"],
"name": "atc smart controller",
"mf": "Tevolve"
},
"o": {
"name":"tevolve2mqtt"
},
"qos": 2
}

View File

@@ -1,21 +1,38 @@
from pytevolve import Tevolve from pytevolve import Tevolve
from mqtt_manager import MqttManager from mqtt_manager import MqttManager
import threading
host = "192.168.0.100"
if __name__ == "__main__": if __name__ == "__main__":
tevolve = Tevolve() tevolve = Tevolve()
devices = tevolve.get_devices() tevolve.get_token()
tevolve.get_dev()
tevolve.get_devices()
status = tevolve.get_status()
mqtt_manager = MqttManager() mqtt_manager = MqttManager()
mqtt_manager.client.connect(heaters=devices["nodes"]) # threading.Thread(target=mqtt_manager.start_mqtt, args=(host,)).start()
tevolve.get_token() while 1:
tevolve.get_dev() 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()

View File

@@ -1,65 +1,111 @@
import json import json
from pathlib import Path from pathlib import Path
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
import threading
class MqttManager: class MqttManager:
def __init__(self): def __init__(self):
self.host = "192.168.0.100" self.host = "192.168.0.100"
self.username = "connorroy" self.username = "connorroy"
self.password = "arkreactor7" self.password = "arkreactor7"
self.is_connected = False
self.object_ids = None self.object_ids = None
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
self.client.on_connect = self.on_connect self.client.on_connect = self.on_connect
self.client.ondisconnect = self.on_disconnect
self.client.on_message = self.on_message self.client.on_message = self.on_message
self.client.username_pw_set(username=self.username, password=self.password) 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): def publish_heaters(self, rad_data):
# self.object_ids = heaters
self.object_ids = heaters
print(f"Connected with result code {reason_code}")
discovery_topic = "homeassistant/climate/" discovery_topic = "homeassistant/climate/"
json_payload = self._load_json() 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, self.client.subscribe(topic=json_payload[device_index]["state_topic"])
payload=json.dumps(json_payload[index]))
for device_index, device in enumerate(self.object_ids): if device["lost"] is True:
print(json_payload[device_index]["current_temperature_topic"])
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, self.client.publish(topic=json_payload[device_index]["current_temperature_topic"], qos=0, retain=False,
payload=json.dumps(json_payload[index])) payload="17")
def on_connect(self, client, userdata, flags, reason_code, properties):
print("Connected")
self.is_connected = True
# # Subscribing in on_connect() means that if we lose the connection and # # Subscribing in on_connect() means that if we lose the connection and
# # reconnect then subscriptions will be renewed. # # reconnect then subscriptions will be renewed.
# client.subscribe("$SYS/#") # client.subscribe("$SYS/#")
def on_disconnect(self, client, userdata, flags, reason):
print("Disconnected")
self.is_connected = False
def on_message(self, client, userdata, msg): def on_message(self, client, userdata, msg):
print(msg.topic + " " + str(msg.payload)) 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): def _load_json(self):
path = Path(__file__).parent path = Path(__file__).parent
@@ -67,7 +113,7 @@ class MqttManager:
with open(str(path) + "/climate.json") as j: with open(str(path) + "/climate.json") as j:
data = json.load(j) data = json.load(j)
for dev in self.object_ids: for dev in self.object_ids["nodes"]:
new_data = data.copy() new_data = data.copy()
for i in new_data: for i in new_data:
if "{name}" in str(new_data.get(i)): if "{name}" in str(new_data.get(i)):

View File

@@ -91,8 +91,6 @@ class Tevolve:
x = requests.post(url, headers=headers, data=data) x = requests.post(url, headers=headers, data=data)
print(x)
if x.status_code == 200: if x.status_code == 200:
Tevolve.token = x.json()["access_token"] Tevolve.token = x.json()["access_token"]
else: else:
@@ -113,16 +111,20 @@ class Tevolve:
def get_devices(self): def get_devices(self):
try:
endpoint = "/devs/"+self.dev_id+"/mgr/nodes" endpoint = "/devs/"+self.dev_id+"/mgr/nodes"
header = {'content-type': 'application/json', header = {'content-type': 'application/json',
"Authorization": "Bearer " + self.token_primary} "Authorization": "Bearer " + self.token_primary}
url = str(self.api_url + endpoint)
devices_request = requests.get(str(self.api_url + endpoint), headers=header).json() devices_request = requests.get(str(self.api_url + endpoint), headers=header).json()
self.devices = devices_request self.devices = devices_request
return self.devices except:
raise
@@ -248,8 +250,10 @@ class Tevolve:
return "heat" return "heat"
def get_status(self, heater_id): def get_status(self):
result = []
try:
for index ,i in enumerate(self.devices["nodes"]):
headers = { headers = {
'host': 'api-tevolve.termoweb.net', 'host': 'api-tevolve.termoweb.net',
'origin': 'https://tevolve.termoweb.net', 'origin': 'https://tevolve.termoweb.net',
@@ -261,11 +265,14 @@ class Tevolve:
'referer': 'https://tevolve.termoweb.net/', 'referer': 'https://tevolve.termoweb.net/',
} }
url = "https://api-tevolve.termoweb.net/api/v2/devs/"+self.dev_id+"/htr/"+str(heater_id)+"/status" url = "https://api-tevolve.termoweb.net/api/v2/devs/"+self.dev_id+"/htr/"+str(i["addr"])+"/status"
response = requests.get(url, headers=headers) response = requests.get(url, headers=headers)
if response.status_code == 201 or response.status_code == 200: if response.status_code == 201 or response.status_code == 200:
return response.json() result.append({i["name"]: response.json()})
except Exception as e:
raise e
return result
@staticmethod @staticmethod
def set_temperature(heater_id, set_temperature): def set_temperature(heater_id, set_temperature):