These are RS485, multidrop, 4800 baud.
I already have them via another automation package, and I'm trying to migrate to Domoticz.
I've managed to setup a plugin that will poll (request) the information from one of the thermostats, and plan to expand from this, but I'm stuck on parsing the data.
I can see that normally a line feed is required to signify the end of data.
There is no line feed from this device.
Its data should always start with 0x81, with the next two bytes signifying the data length (minus crc) and the last two bytes the CRC calc.
I'd planned on looking for the 81, reading the length, then filling a string with the correct number of bytes, then error checking, then waiting for the next 81.
Note, the data length is not always the same (depending what command is used)
In my last software, I dealt with this by establishing a connection with just a timeout of 1000ms.
On timeout, the buffer was read.
On new data, the buffer was cleared, and filled with new data.
Thats been working for years, but clearly I could have checked datalength and CRC etc.
This is my code so far (note, lots of commented out sections for my migration from old software, written in pseudo lua, and plans to code in this):
Code: Select all
"""
<plugin key="Heatmiser" name="Heatmiser Driver" author="JF" version="1.0.0" >
<description>
<h2>Heatmiser Driver</h2><br/>
</description>
<params>
<param field="SerialPort" label="SerialPort" width="200px" required="true" default=""/>
<param field="Mode4" label="Baud" width="150px" required="true" default="4800"/>
<param field="Mode5" label="Number of Devices" width="150px" required="true" default="1"/>
<param field="Mode6" label="Debug" width="150px">
<options>
<option label="None" value="0" default="true" />
<option label="Python Only" value="2"/>
<option label="Basic Debugging" value="62"/>
<option label="Basic+Messages" value="126"/>
<option label="Connections Only" value="16"/>
<option label="Connections+Python" value="18"/>
<option label="Connections+Queue" value="144"/>
<option label="All" value="-1"/>
</options>
</param>
</params>
</plugin>
"""
import Domoticz
from ctypes import c_ushort
import binascii
#heatmiser prt range of thermostats, rs485, 4800baud
#need data in hex string, comes in 8bit words
#format is (byte number):
#datalength = ([3]+[2])*2
#if total string length matches datalength then valid data
#if total byte lengh is >46 then data is from a poll request
#Self.TheDay={
#[1] = "Weekday",
#[2] = "Weekend",
#[3] = "Monday",
#[4] = "Tuesday",
#[5] = "Wednesday",
#[6] = "Thursday",
#[7] = "Friday",
#[8] = "Saturday",
#[9] = "Sunday"
#Self.TimeGroup={
#[1] = "1",
#[2] = "2",
#[3] = "3",
#[4] = "4"
#Self.TheData={
#[1] = "Hour",
#[2] = "Minute",
#[3] = "Temp"
#Self.TheTimerData={
#[1] = "On",
#[2] = "Off"
#anticipated string length returns:
#Self.StatType={
#Stattype:[47] = "DT",
#Stattype:[75] = "PRT52",
#Stattype:[159] = "PRT7",
#Stattype:[107] = "PRTHW52",
#Stattype:[304] = "PRTHW7"
#write data:
#poll id is 2 byte, leading 0 if requried, in hex.
#if poll id = FF then ??
#masterID = "81"
#mywrite = "01"
#myread = "00
#readlength = "0A"
#if mycommand == "frosttemp" then address 17 = 11 in HEX for 1 row
#elseif mycommand == "settemp" then address 18 = 12 in HEX for 1 row
#elseif mycommand == "onoff" then address 21 = 15 in HEX for 1 row
#elseif mycommand == "keylock" then address 22 = 16 in HEX for 1 row
#elseif mycommand == "frostmode" then address 23 = 17 in HEX for 1 row
#elseif mycommand == "holiday" then address 24 = 18 in HEX for 2 rows
#elseif mycommand == "alldata" then address 00 for FF rows (all)
#elseif mycommand == "programdata" then address 00 for FF rows (all) to update netremote with programming data
#elseif mycommand == "programweekday" then address 47 = 2F00 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programweekend" then address 59 = 3B00 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programmonday" then address 103 = 6700 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programtuesday" then address 115 = 7300 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programwednesday" then address 127 = 7F00 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programthursday" then address 139 = 8B00 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programfriday" then address 151 = 9700 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programsaturday" then address 163 = A300 in HEX for 12 rows = 0C00in HEX
#elseif mycommand == "programsunday" then address 175 = AF00 in HEX for 12 rows = 0C00 in HEX
#elseif mycommand == "programTimerWeekday" then address 71 = 4700 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerWeekend" then address 87 = 5700 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerMonday" then address 187 = BB00 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerTuesday" then address 203 = CB00 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerWednesday" then address 219 = DB00 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerThursday" then address 235 = EB00 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerFriday" then address 251 = FB00 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerSaturday" then address 267= 0B01 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "programTimerSunday" then address 283 = 1B01 in HEX for 16 rows = 1000 in HEX (LSB first)
#elseif mycommand == "settime" then address 175 = AF in HEX for 12 rows = 0C in HEX
#else Self.datalocation = "0000".."0000"
#framelength = (string.len(Self.mydata) / 2) + 10
#framehexlength = math.decimaltohex(Self.framelength), add leading zero
#if mycommand == "alldata" then Self.data1 = Self.myID .. Self.readlength .. Self.masterID .. Self.myread .. Self.datalocation
#else Self.data1 = Self.myID .. Self.mylength .. Self.masterID .. Self.mywrite .. Self.datalocation .. Self.mydata
#data1b = (math.hextobyte(Self.data1))
#mycrc = math.crcCCITT.process (Self.data1b) --defaults to crc.ccitt FFFF, input is ascii, output is decimal
#mycrc2 = math.decimaltohex(Self.mycrc)
#mycrc3 = string.upper(Self.mycrc2)
#if string.len(Self.mycrc3)<4 then Self.mycrc4="0"..Self.mycrc3
#else Self.mycrc4 = Self.mycrc3
#lowcrc = string.sub(Self.mycrc4,1,2)--captures first two characters
#highcrc = string.sub(Self.mycrc4,3,4)--captures last two characters
#finalcrc = Self.highcrc .. Self.lowcrc--swaps bytes around
#mydata = math.hextobyte(Self.data1 .. Self.finalcrc)
#tx:Send (Self.mydata)--sends data to the transport device
#read data:
#Note received thermostat DCB index = stated thermostat DCB index in the protocol documentation + 10 (note dcb in the doc starts at 0, so there are actually 11 additional bytes, 9 before and 2 after the data)
#statID = 4,4
#roomtemp = 42,43))/10
#setfrosttemp = 27,27))/1
#settemp = 28,28))/1
#statusheat = 45,45))/1
#statuspower = 31,31))/1
#statusfrost = 33,33))/1
#holidayhours = 34,35))/1
#programmode = 26,26))/1 programmode >0 = 7 Day, else 5/2 day
#data for 5/2 days mode of the PRT stat
#if Self.TH.StatType == "PRT52" or Self.TH.StatType == "PRT7" then
#StatDay = 46,46))/1
#StatHour = 47,47))/1
#StatMinute = 48,48))/1
#StatSecond = 49,49))/1
#if Self.TH[Self.statID].statuspower == 0 then Self.TH[Self.statID].statstatus = "Power Off"
#elseif Self.TH[Self.statID].statusheat == 1 then Self.TH[Self.statID].statstatus = "Heat Request"
#elseif Self.TH[Self.statID].statusfrost == 1 and Self.TH[Self.statID].holidayhours == 0 then Self.TH[Self.statID].statstatus = "Frost Mode"
#elseif Self.TH[Self.statID].holidayhours > 0 then Self.TH[Self.statID].statstatus = "Holiday " .. Self.TH[Self.statID].holidayhours .." hours"
#else Self.TH[Self.statID].statstatus = "Power on"
#DSTIndex = 50
#for s_TheDay = 1,2,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheData = 1,3,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
#data for 7 days mode of the PRT stat
#if Self.TH.StatType == "PRT7" then
#DSTIndex = 74
#for s_TheDay = 3,9,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheData = 1,3,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
#CH data for 5/2 days mode of the PRTHW stat
#if Self.TH.StatType == "PRTHW52" or Self.TH.StatType == "PRTHW7" then
#HWStatus = 46,46))/1
#StatDay = 47,47))/1
#StatHour = 48,48))/1
#StatMinute = 49,49))/1
#StatSecond = 50,50))/1
#if Self.TH[Self.statID].statuspower == 0 then Self.TH[Self.statID].statstatus = "Power Off"
#elseif Self.TH[Self.statID].statusheat == 1 and Self.TH[Self.statID].HWStatus == 0 then Self.TH[Self.statID].statstatus = "Heat Request"
#elseif Self.TH[Self.statID].statusheat == 0 and Self.TH[Self.statID].HWStatus == 1 then Self.TH[Self.statID].statstatus = "HW Request"
#elseif Self.TH[Self.statID].statusheat == 1 and Self.TH[Self.statID].HWStatus == 1 then Self.TH[Self.statID].statstatus = "Heat & HW Request"
#elseif Self.TH[Self.statID].statusfrost == 1 and Self.TH[Self.statID].holidayhours == 0 then Self.TH[Self.statID].statstatus = "Frost Mode"
#elseif Self.TH[Self.statID].holidayhours > 0 then Self.TH[Self.statID].statstatus = "Holiday " .. Self.TH[Self.statID].holidayhours .." hours"
#else Self.TH[Self.statID].statstatus = "Power on"
#DSTIndex = 51
#for s_TheDay = 1,2,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheData = 1,3,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
#HW data for 5/2 days mode of the PRTHW stat (note, set netremote stat ID to ID + 1 for HW status and ignore the temps)
#DSTIndex = 75
#for s_TheDay = 1,2,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheTimerData = 1,2,1 do
# for s_TheData = 1,2,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
#CH data for 7 days mode of the PRT stat
#if Self.TH.StatType == "PRTHW7" then
#DSTIndex = 107
#for s_TheDay = 3,9,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheData = 1,3,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
#HW data for 7 days mode of the PRTHW stat
#DSTIndex = 191
#for s_TheDay = 3,9,1 do
# for s_TimeGroup = 1,4,1 do
# for s_TheTimerData = 1,2,1 do
# for s_TheData = 1,2,1 do
# value = hextodecimal Self.DSTIndex/1
# Self.DSTIndex = Self.DSTIndex + 1
class BasePlugin:
enabled = False
lastPolled = 0
lastResponse = 0
Heatmiser = None
crc_ccitt_tab = []
crc_ccitt_constant = 0x1021
masterID = "81"
mywrite = "01"
myread = "00"
readlength = "0A"
Data_String = ""
length_low = ""
length_high = ""
Message_data = ""
crc = ""
def __init__(self):
return
#CRC calculations
def start_crc(self, version='FFFF'):
dict_versions = {'XModem': 0x0000, 'FFFF': 0xffff, '1D0F': 0x1d0f}
self.starting_value = dict_versions[version]
# initialize the precalculated tables
if not len(self.crc_ccitt_tab):
self.init_crc_ccitt()
def calculate(self, input_data):
is_string = isinstance(input_data, str)
is_bytes = isinstance(input_data, bytes)
crcValue = self.starting_value
for c in input_data:
d = ord(c) if is_string else c
tmp = (c_ushort(crcValue >> 8).value) ^ d
crcValue = (c_ushort(crcValue << 8).value) ^ int(
self.crc_ccitt_tab[tmp], 0)
return crcValue
def init_crc_ccitt(self):
#'''The algorithm uses tables with precalculated values'''
for i in range(0, 256):
crc = 0
c = i << 8
for j in range(0, 8):
if ((crc ^ c) & 0x8000):
crc = c_ushort(crc << 1).value ^ self.crc_ccitt_constant
else:
crc = c_ushort(crc << 1).value
c = c_ushort(c << 1).value # equivalent of c = c << 1
self.crc_ccitt_tab.append(hex(crc))
def onStart(self):
if Parameters["Mode6"] == "Debug":
Domoticz.Debugging(1)
Device_Count=int(Parameters["Mode5"])
if(len(Devices)==0):
for n in range(1,Device_Count+1):
Domoticz.Log("Creating device: "+str(n))
#type 242 is a thermostat
#subtype 1 is setpoint
Domoticz.Device(Name="TH_"+str(n), Unit=n, Type=242, Subtype=1).Create()
#DumpConfigToLog()
self.handleConnect()
self.start_crc()
return
def onStop(self):
Domoticz.Log("onStop called")
def onConnect(self, Connection, Status, Description):
if (Connection == self.Heatmiser):
if (Status == 0):
Domoticz.Log("Connected successfully to: "+Parameters["SerialPort"]+", Baud:"+Parameters["Mode4"])
#self.Heatmiser.Send("New Connection\r")
else:
#nothing
Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["SerialPort"]+", Baud:"+Parameters["Mode4"]+" with error: "+Description)
self.Heatmiser = None
def onMessage(self, Connection, Data):
#hex_bytes = binascii.hexlify(Data)
Data_hex = Data.hex()
Data_Str = str(Data_hex)
Domoticz.Log(Data_Str)
#if (hex_str == "81"):
# Domoticz.Log("Last Data: "+self.Message_data)
# self.Message_data = ""
# self.lastResponse = 1
#if (self.lastResponse == 1):
# self.length_low = hex_str
# self.lastResponse = 2
#if (self.lastResponse == 2):
# self.length_high = hex_str
# self.lastResponse = 3
#if (self.lastResponse == 3):
# self.Message_data = self.Message_data+hex_str
# self.lastResponse = 3
def onCommand(self, Unit, Command, Level, Hue):
Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
#input = bytes.fromhex("000102030405060708090A0B0C0D0E0F")
#Domoticz.Log("CRC:")
#Domoticz.Log(str(hex(self.calculate(input))))
def onDisconnect(self, Connection):
Domoticz.Log("Hardware has disconnected.")
self.Heatmiser = None
for Device in Devices:
UpdateDevice(Device, Devices[Device].nValue, Devices[Device].sValue, 1)
return
def onHeartbeat(self):
global Heatmiser
myID="01"
datalocation="0000"+"FFFF"
data_string = myID+self.readlength+self.masterID+self.myread+datalocation
#Domoticz.Log(data_string)
data_bytes = bytes.fromhex(data_string)
data_crc = str(hex(self.calculate(data_bytes)))
data_crc = data_crc[2:6]
data_crc = data_crc.zfill(4)
#Domoticz.Log("data_crc: "+data_crc)
data_crc_LSBs = data_crc[0:2]
data_crc_USBs = data_crc[2:4]
data_crc = data_crc_USBs+data_crc_LSBs
#Domoticz.Log("data_crc: "+data_crc)
data_tx_string = data_string+data_crc
Domoticz.Log("data_tx: "+data_tx_string)
data_tx = bytes.fromhex(data_tx_string)
self.Heatmiser.Send(data_tx)
#Domoticz.Log("data_tx_Check: "+str(data_tx))
def handleConnect(self):
self.Heatmiser = None
self.Heatmiser=Domoticz.Connection(Name="Heatmiser Connection", Transport="Serial", Protocol="None", Address=Parameters["SerialPort"], Baud=int(Parameters["Mode4"]))
self.Heatmiser.Connect()
global _plugin
_plugin = BasePlugin()
def onStart():
global _plugin
_plugin.onStart()
def onStop():
global _plugin
_plugin.onStop()
def onConnect(Connection, Status, Description):
global _plugin
_plugin.onConnect(Connection, Status, Description)
def onMessage(Connection, Data):
global _plugin
_plugin.onMessage(Connection, Data)
def onCommand(Unit, Command, Level, Hue):
global _plugin
_plugin.onCommand(Unit, Command, Level, Hue)
def onDisconnect(Connection):
global _plugin
_plugin.onDisconnect(Connection)
def onHeartbeat():
global _plugin
_plugin.onHeartbeat()
# Generic helper functions
def DumpConfigToLog():
for x in Parameters:
if Parameters[x] != "":
Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
Domoticz.Debug("Device count: " + str(len(Devices)))
for x in Devices:
Domoticz.Debug("Device: " + str(x) + " - " + str(Devices[x]))
Domoticz.Debug("Device ID: '" + str(Devices[x].ID) + "'")
Domoticz.Debug("Device Name: '" + Devices[x].Name + "'")
Domoticz.Debug("Device nValue: " + str(Devices[x].nValue))
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
return
def UpdateDevice(Unit, nValue, sValue, TimedOut):
# Make sure that the Domoticz device still exists (they can be deleted) before updating it
if (Unit in Devices):
if (Devices[Unit].nValue != nValue) or (Devices[Unit].sValue != sValue) or (Devices[Unit].TimedOut != TimedOut):
Devices[Unit].Update(nValue=nValue, sValue=str(sValue), TimedOut=TimedOut)
Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
return
This is the expected response (confirmed my direct coms from a serial terminal software):010A81000000FFFF2c09
This is what I get in the logger:81300101000000250101250016040001000000000100000014010C121C0101000000000000FFFFFF
FF00C800000002162718080015091E10101E161700110900151600101800101800100600061E1000
101E1400141E180018000600061E1000101E1400141E180018000700140800141100141500120700
14080014110014150012070014080014110014150012070014080014110014150012070014080014
1100141500120700140800141100141500120700140800141100141500120600061E1000101E1400
141E180018000600061E1000101E1400141E180018000600061E1000101E1400141E180018000600
061E1000101E1400141E180018000600061E1000101E1400141E180018000600061E1000101E1400
141E180018000600061E1000101E1400141E18001800F289
which is a start.2019-02-05 19:17:12.839 (TH) 18
2019-02-05 19:17:12.839 (TH) 00
2019-02-05 19:17:12.839 (TH) 06
2019-02-05 19:17:12.839 (TH) 00
2019-02-05 19:17:12.840 (TH) 06
2019-02-05 19:17:12.890 (TH) 1e
2019-02-05 19:17:12.890 (TH) 10
2019-02-05 19:17:12.890 (TH) 00
2019-02-05 19:17:12.890 (TH) 10
2019-02-05 19:17:12.891 (TH) 1e
2019-02-05 19:17:12.891 (TH) 14
2019-02-05 19:17:12.891 (TH) 00
2019-02-05 19:17:12.891 (TH) 14
2019-02-05 19:17:12.891 (TH) 1e
2019-02-05 19:17:12.891 (TH) 18
2019-02-05 19:17:12.891 (TH) 00
2019-02-05 19:17:12.891 (TH) 18
2019-02-05 19:17:12.891 (TH) 00
2019-02-05 19:17:12.891 (TH) 06
2019-02-05 19:17:12.891 (TH) 00
2019-02-05 19:17:12.891 (TH) 06
2019-02-05 19:17:12.891 (TH) 1e
2019-02-05 19:17:12.892 (TH) 10
2019-02-05 19:17:12.892 (TH) 00
2019-02-05 19:17:12.892 (TH) 10
2019-02-05 19:17:12.892 (TH) 1e
2019-02-05 19:17:12.892 (TH) 14
2019-02-05 19:17:12.892 (TH) 00
2019-02-05 19:17:12.892 (TH) 14
2019-02-05 19:17:12.892 (TH) 1e
2019-02-05 19:17:12.943 (TH) 18
2019-02-05 19:17:12.943 (TH) 00
2019-02-05 19:17:12.943 (TH) 18
2019-02-05 19:17:12.943 (TH) 00
2019-02-05 19:17:12.943 (TH) 06
2019-02-05 19:17:12.943 (TH) 00
2019-02-05 19:17:12.943 (TH) 06
2019-02-05 19:17:12.943 (TH) 1e
2019-02-05 19:17:12.943 (TH) 10
2019-02-05 19:17:12.943 (TH) 00
2019-02-05 19:17:12.943 (TH) 10
2019-02-05 19:17:12.944 (TH) 1e
2019-02-05 19:17:12.944 (TH) 14
2019-02-05 19:17:12.944 (TH) 00
2019-02-05 19:17:12.944 (TH) 14
2019-02-05 19:17:12.944 (TH) 1e
2019-02-05 19:17:12.944 (TH) 18
2019-02-05 19:17:12.944 (TH) 00
2019-02-05 19:17:12.944 (TH) 18
2019-02-05 19:17:12.944 (TH) 00
2019-02-05 19:17:12.944 (TH) 06
2019-02-05 19:17:12.944 (TH) 00
2019-02-05 19:17:12.944 (TH) 06
2019-02-05 19:17:12.945 (TH) 1e
2019-02-05 19:17:12.945 (TH) 10
2019-02-05 19:17:12.995 (TH) 00
2019-02-05 19:17:12.995 (TH) 10
2019-02-05 19:17:12.995 (TH) 1e
2019-02-05 19:17:12.995 (TH) 14
2019-02-05 19:17:12.995 (TH) 00
2019-02-05 19:17:12.996 (TH) 14
2019-02-05 19:17:12.996 (TH) 1e
2019-02-05 19:17:12.996 (TH) 18
2019-02-05 19:17:12.996 (TH) 00
2019-02-05 19:17:12.996 (TH) 18
2019-02-05 19:17:12.996 (TH) 00
2019-02-05 19:17:12.996 (TH) 06
2019-02-05 19:17:12.996 (TH) 00
2019-02-05 19:17:12.996 (TH) 06
2019-02-05 19:17:12.996 (TH) 1e
2019-02-05 19:17:12.996 (TH) 10
2019-02-05 19:17:12.996 (TH) 00
2019-02-05 19:17:12.996 (TH) 10
2019-02-05 19:17:12.997 (TH) 1e
2019-02-05 19:17:12.997 (TH) 14
2019-02-05 19:17:12.997 (TH) 00
2019-02-05 19:17:12.997 (TH) 14
2019-02-05 19:17:12.997 (TH) 1e
2019-02-05 19:17:12.997 (TH) 18
2019-02-05 19:17:12.997 (TH) 00
2019-02-05 19:17:12.997 (TH) 18
2019-02-05 19:17:13.048 (TH) 00
2019-02-05 19:17:13.048 (TH) 06
2019-02-05 19:17:13.048 (TH) 00
2019-02-05 19:17:13.048 (TH) 06
2019-02-05 19:17:13.048 (TH) 1e
2019-02-05 19:17:13.048 (TH) 10
2019-02-05 19:17:13.048 (TH) 00
2019-02-05 19:17:13.048 (TH) 10
2019-02-05 19:17:13.048 (TH) 1e
2019-02-05 19:17:13.048 (TH) 14
2019-02-05 19:17:13.049 (TH) 00
2019-02-05 19:17:13.049 (TH) 14
2019-02-05 19:17:13.049 (TH) 1e
2019-02-05 19:17:13.049 (TH) 18
2019-02-05 19:17:13.049 (TH) 00
2019-02-05 19:17:13.049 (TH) 18
2019-02-05 19:17:13.049 (TH) 00
2019-02-05 19:17:13.049 (TH) 59
2019-02-05 19:17:13.049 (TH) 8d
So to the question..........
How should I deal with an incomming message and get it into one long hex string (I can deal with it from then) without a fixed length or end delimiter.
I'd expected a timeout parameter for the connection, but other than the plug in guide, I can't find any more information.
is this dedicated domoticz? Or generic python?
Thanks for reading.
Jon