diff --git a/itho-wpu.py b/itho-wpu.py index cc1b90a..a6619ad 100755 --- a/itho-wpu.py +++ b/itho-wpu.py @@ -10,6 +10,7 @@ import time import os import datetime import json +import db from collections import namedtuple consolelogformatter = logging.Formatter("%(asctime)-15s %(levelname)s: %(message)s") @@ -175,6 +176,7 @@ class IthoWPU(): self.cache = IthoWPUCache() self.nodeid = self.get('getnodeid') self.datatype = self.get('getdatatype') + self.heatpump_db = db.sqlite('heatpump.sqlite') def get(self, action): if not self.no_cache: @@ -205,6 +207,45 @@ class IthoWPU(): return response + def get_listversion_from_nodeid(self): + if self.nodeid is None: + return + return int(self.nodeid[10], 0) + + def get_datalog_structure(self): + listversion = self.get_listversion_from_nodeid() + datalabel_version = self.heatpump_db.execute( + f"SELECT datalabel FROM versiebeheer WHERE version = {listversion}")[0]['datalabel'] + if datalabel_version is None or not type(datalabel_version) == int: + logger.error(f"Datalabel not found in database for version {listversion}") + return None + datalabel = self.heatpump_db.execute( + f"SELECT name, title, tooltip, unit FROM datalabel_v{datalabel_version} order by id") + + if len(self.datatype[5:-1]) != len(datalabel): + logger.warning(f"Number of datatype items ({len(self.datatype[5:-1])}) is not equal to the number of datalabels ({len(datalabel)}) in the database.") + + Field = namedtuple('Field', 'index type label description') + + datalog = [] + index = 0 + for dl, dt in zip(datalabel, self.datatype[5:-1]): + description = dl['title'].title() + if dl['unit'] is not None: + description = f"{description} ({dl['unit']})" + datalog.append(Field(index, int(dt, 0), dl['name'].lower(), description)) + + if dt in ['0x0', '0xc']: + index = index + 1 + elif dt in ['0x10', '0x12', '0x92']: + index = index + 2 + elif dt in ['0x20']: + index = index + 4 + else: + logger.error(f"Unknown data type for label {dl['name']}: {dt}") + return datalog + return datalog + class IthoWPUCache: def __init__(self): @@ -259,7 +300,7 @@ def is_messageclass_valid(action, response): return True -def process_response(action, response, args): +def process_response(action, response, args, wpu): if int(response[3], 0) != 0x01: logger.error(f"Response MessageType != 0x01 (response), but {response[3]}") return @@ -267,7 +308,7 @@ def process_response(action, response, args): return if action == "getdatalog": - measurements = process_datalog(response) + measurements = process_datalog(response, wpu) if args.export_to_influxdb: export_to_influxdb(action, measurements) elif action == "getnodeid": @@ -302,42 +343,8 @@ def process_serial(response): logger.info(f"Serial: {serial}") -def process_datalog(response): - # 0 = Byte - # 1 = UnsignedInt - # 2 = SignedIntDec2 - Field = namedtuple('Field', 'index type label description') - datalog = [ - Field(0, 0x92, "t_out", "Buitentemperatuur"), - Field(2, 0x92, "t_boiltop", "Boiler laag"), - Field(4, 0x92, "t_boildwn", "Boiler hoog"), - Field(6, 0x92, "t_evap", "Verdamper temperatuur"), - Field(8, 0x92, "t_suct", "Zuiggas temperatuur"), - Field(10, 0x92, "t_disc", "Persgas temperatuur"), - Field(12, 0x92, "t_cond", "Vloeistof temperatuur"), - Field(14, 0x92, "t_source_r", "Naar bron"), - Field(16, 0x92, "t_source_s", "Van bron"), - Field(18, 0x92, "t_ch_supp", "CV aanvoer"), - Field(20, 0x92, "t_ch_ret", "CV retour"), - Field(22, 0x92, "p_sens", "Druksensor (Bar)"), - Field(24, 0x92, "i_tr1", "Stroom trafo 1 (A)"), - Field(26, 0x92, "i_tr2", "Stroom trafo 2 (A)"), - Field(34, 0x10, "in_flow", "Flow sensor bron (l/h)"), - Field(37, 0x0, "out_ch", "Snelheid cv pomp (%)"), - Field(38, 0x0, "out_src", "Snelheid bron pomp (%)"), - Field(39, 0x0, "out_dhw", "Snelheid boiler pomp (%)"), - Field(44, 0xc, "out_c1", "Compressor aan/uit"), - Field(45, 0xc, "out_ele", "Elektrisch element aan/uit"), - Field(46, 0xc, "out_trickle", "Trickle heating aan/uit"), - Field(47, 0xc, "out_fault", "Fout aanwezig (0=J, 1=N)"), - Field(48, 0xc, "out_fc", "Vrijkoelen actief (0=uit, 1=aan)"), - Field(51, 0x92, "ot_room", "Kamertemperatuur"), - Field(55, 0x0, "ot_mod", "Warmtevraag (%)"), - Field(56, 0x0, "state", "State (0=init,1=uit,2=CV,3=boiler,4=vrijkoel,5=ontluchten)"), - Field(57, 0x0, "sub_state", "Substatus (255=geen)"), - Field(67, 0x0, "fault_reported", "Fout gevonden (foutcode)"), - Field(92, 0x10, "tr_fc", "Vrijkoelen interval (sec)"), - ] +def process_datalog(response, wpu): + datalog = wpu.get_datalog_structure() message = response[5:] measurements = {} for d in datalog: @@ -356,6 +363,11 @@ def process_datalog(response): if num >= 32768: num -= 65536 num = round(num / 100, 2) + elif d.type == 0x20: + m = message[d.index:d.index+4] + num = ((int(m[0], 0) << 24) + (int(m[1], 0) << 16) + (int(m[2], 0) << 8) + int(m[3], 0)) + else: + logger.error(f"Unknown message type for datalog {d.name}: {d.type}") logger.info(f"{d.description}: {num}") measurements[d.label] = num return measurements @@ -370,4 +382,4 @@ if __name__ == "__main__": wpu = IthoWPU(args.master_only, args.slave_only, args.slave_timeout, args.no_cache) response = wpu.get(args.action) if response is not None: - process_response(args.action, response, args) + process_response(args.action, response, args, wpu)