Skip to content

Commit 0e69f3c

Browse files
authored
Merge pull request #30 from HotNoob/v1.1.1
V1.1.1
2 parents 524fc88 + dcd16bd commit 0e69f3c

16 files changed

+851
-206
lines changed

classes/Object.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#hack to allow generic objects
2+
class Object(object):
3+
pass

classes/protocol_settings.py

Lines changed: 355 additions & 24 deletions
Large diffs are not rendered by default.

classes/transports/modbus_base.py

Lines changed: 6 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def read_data(self) -> dict[str, str]:
107107
info = {}
108108
for registry_type in Registry_Type:
109109
registry = self.read_modbus_registers(ranges=self.protocolSettings.get_registry_ranges(registry_type=registry_type), registry_type=registry_type)
110-
new_info = self.process_registery(registry, self.protocolSettings.get_registry_map(registry_type))
110+
new_info = self.protocolSettings.process_registery(registry, self.protocolSettings.get_registry_map(registry_type))
111111

112112
if False:
113113
new_info = {self.__input_register_prefix + key: value for key, value in new_info.items()}
@@ -260,8 +260,8 @@ def evaluate_score(entry : registry_map_entry, val):
260260
holding_valid_count[name] = 0
261261

262262
#process registry based on protocol
263-
input_info = self.process_registery(input_registry, protocol.registry_map[Registry_Type.INPUT])
264-
holding_info = self.process_registery(input_registry, protocol.registry_map[Registry_Type.HOLDING])
263+
input_info = protocol.process_registery(input_registry, protocol.registry_map[Registry_Type.INPUT])
264+
holding_info = protocol.process_registery(input_registry, protocol.registry_map[Registry_Type.HOLDING])
265265

266266

267267
for entry in protocol.registry_map[Registry_Type.INPUT]:
@@ -302,7 +302,7 @@ def write_variable(self, entry : registry_map_entry, value : str, registry_type
302302

303303
#read current value
304304
current_registers = self.read_modbus_registers(start=entry.register, end=entry.register, registry_type=registry_type)
305-
results = self.process_registery(current_registers, self.protocolSettings.get_registry_map(registry_type))
305+
results = self.protocolSettings.process_registery(current_registers, self.protocolSettings.get_registry_map(registry_type))
306306
current_value = current_registers[entry.register]
307307

308308

@@ -381,7 +381,7 @@ def read_variable(self, variable_name : str, registry_type : Registry_Type, entr
381381
end = max(entry.concatenate_registers)
382382

383383
registers = self.read_modbus_registers(start=start, end=end, registry_type=registry_type)
384-
results = self.process_registery(registers, registry_map)
384+
results = self.protocolSettings.process_registery(registers, registry_map)
385385
return results[entry.variable_name]
386386

387387
def read_modbus_registers(self, ranges : list[tuple] = None, start : int = 0, end : int = None, batch_size : int = 45, registry_type : Registry_Type = Registry_Type.INPUT ) -> dict:
@@ -452,134 +452,12 @@ def read_modbus_registers(self, ranges : list[tuple] = None, start : int = 0, en
452452
registry[i+range[0]] = register.registers[i]
453453

454454
return registry
455-
456-
def process_registery(self, registry : dict, map : list[registry_map_entry]) -> dict[str,str]:
457-
'''process registry into appropriate datatypes and names'''
458-
459-
concatenate_registry : dict = {}
460-
info = {}
461-
for item in map:
462-
463-
if item.register not in registry:
464-
continue
465-
value = ''
466-
467-
if item.data_type == Data_Type.UINT: #read uint
468-
if item.register + 1 not in registry:
469-
continue
470-
value = float((registry[item.register] << 16) + registry[item.register + 1])
471-
elif item.data_type == Data_Type.SHORT: #read signed short
472-
val = registry[item.register]
473-
474-
# Convert the combined unsigned value to a signed integer if necessary
475-
if val & (1 << 15): # Check if the sign bit (bit 31) is set
476-
# Perform two's complement conversion to get the signed integer
477-
value = val - (1 << 16)
478-
else:
479-
value = val
480-
value = -value
481-
elif item.data_type == Data_Type.INT: #read int
482-
if item.register + 1 not in registry:
483-
continue
484-
485-
combined_value_unsigned = (registry[item.register] << 16) + registry[item.register + 1]
486-
487-
# Convert the combined unsigned value to a signed integer if necessary
488-
if combined_value_unsigned & (1 << 31): # Check if the sign bit (bit 31) is set
489-
# Perform two's complement conversion to get the signed integer
490-
value = combined_value_unsigned - (1 << 32)
491-
else:
492-
value = combined_value_unsigned
493-
value = -value
494-
#value = struct.unpack('<h', bytes([min(max(registry[item.register], 0), 255), min(max(registry[item.register+1], 0), 255)]))[0]
495-
#value = int.from_bytes(bytes([registry[item.register], registry[item.register + 1]]), byteorder='little', signed=True)
496-
elif item.data_type == Data_Type._16BIT_FLAGS or item.data_type == Data_Type._8BIT_FLAGS:
497-
val = registry[item.register]
498-
#16 bit flags
499-
start_bit : int = 0
500-
if item.data_type == Data_Type._8BIT_FLAGS:
501-
start_bit = 8
502-
503-
if item.documented_name+'_codes' in self.protocolSettings.codes:
504-
flags : list[str] = []
505-
for i in range(start_bit, 16): # Iterate over each bit position (0 to 15)
506-
# Check if the i-th bit is set
507-
if (val >> i) & 1:
508-
flag_index = "b"+str(i)
509-
if flag_index in self.protocolSettings.codes[item.documented_name+'_codes']:
510-
flags.append(self.protocolSettings.codes[item.documented_name+'_codes'][flag_index])
511-
512-
value = ",".join(flags)
513-
else:
514-
flags : list[str] = []
515-
for i in range(start_bit, 16): # Iterate over each bit position (0 to 15)
516-
# Check if the i-th bit is set
517-
if (val >> i) & 1:
518-
flags.append("1")
519-
else:
520-
flags.append("0")
521-
value = ''.join(flags)
522-
elif item.data_type.value > 200 or item.data_type == Data_Type.BYTE: #bit types
523-
bit_size = Data_Type.getSize(item.data_type)
524-
bit_mask = (1 << bit_size) - 1 # Create a mask for extracting X bits
525-
bit_index = item.register_bit
526-
value = (registry[item.register] >> bit_index) & bit_mask
527-
elif item.data_type == Data_Type.ASCII:
528-
value = registry[item.register].to_bytes((16 + 7) // 8, byteorder='big') #convert to ushort to bytes
529-
try:
530-
value = value.decode("utf-8") #convert bytes to ascii
531-
except UnicodeDecodeError as e:
532-
print("UnicodeDecodeError:", e)
533-
534-
else: #default, Data_Type.USHORT
535-
value = float(registry[item.register])
536-
537-
if item.unit_mod != float(1):
538-
value = value * item.unit_mod
539-
540-
#move this to transport level
541-
#if isinstance(value, float) and self.max_precision > -1:
542-
# value = round(value, self.max_precision)
543-
544-
if (item.data_type != Data_Type._16BIT_FLAGS and
545-
item.documented_name+'_codes' in self.protocolSettings.codes):
546-
try:
547-
cleanval = str(int(value))
548-
549-
if cleanval in self.protocolSettings.codes[item.documented_name+'_codes']:
550-
value = self.protocolSettings.codes[item.documented_name+'_codes'][cleanval]
551-
except:
552-
#do nothing; try is for intval
553-
value = value
554-
555-
#if item.unit:
556-
# value = str(value) + item.unit
557-
if item.concatenate:
558-
concatenate_registry[item.register] = value
559-
560-
all_exist = True
561-
for key in item.concatenate_registers:
562-
if key not in concatenate_registry:
563-
all_exist = False
564-
break
565-
if all_exist:
566-
#if all(key in concatenate_registry for key in item.concatenate_registers):
567-
concatenated_value = ""
568-
for key in item.concatenate_registers:
569-
concatenated_value = concatenated_value + str(concatenate_registry[key])
570-
del concatenate_registry[key]
571-
572-
info[item.variable_name] = concatenated_value
573-
else:
574-
info[item.variable_name] = value
575-
576-
return info
577455

578456
def read_registry(self, registry_type : Registry_Type = Registry_Type.INPUT) -> dict[str,str]:
579457
map = self.protocolSettings.get_registry_map(registry_type)
580458
if not map:
581459
return {}
582460

583461
registry = self.read_modbus_registers(self.protocolSettings.get_registry_ranges(registry_type), registry_type=registry_type)
584-
info = self.process_registery(registry, map)
462+
info = self.protocolSettings.process_registery(registry, map)
585463
return info

classes/transports/modbus_rtu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings
2020
if not self.port:
2121
raise ValueError("Port is not set")
2222

23-
self.baudrate = settings.getint("buadrate", 9600)
23+
self.baudrate = settings.getint("baudrate", 9600)
2424

2525
address : int = settings.getint("address", 0)
2626
self.addresses = [address]

classes/transports/mqtt.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import paho.mqtt.properties
1010
import paho.mqtt.packettypes
1111

12-
from paho.mqtt.client import Client as MQTTClient
12+
from paho.mqtt.client import Client as MQTTClient, MQTT_ERR_NO_CONN
1313

1414
from defs.common import strtobool
1515
from .transport_base import transport_base
@@ -42,16 +42,16 @@ class mqtt(transport_base):
4242

4343
__first_connection : bool = True
4444
__reconnecting : bool = False
45-
__connected : bool = False
45+
connected : bool = False
4646

4747
def __init__(self, settings : SectionProxy):
4848
self.host = settings.get('host', fallback="")
4949
if not self.host:
5050
raise ValueError("Host is not set")
5151

5252
self.port = settings.getint('port', fallback=self.port)
53-
self.base_topic = settings.get('base_topic', fallback=self.base_topic)
54-
self.error_topic = settings.get('error_topic', fallback=self.error_topic)
53+
self.base_topic = settings.get('base_topic', fallback=self.base_topic).rstrip('/')
54+
self.error_topic = settings.get('error_topic', fallback=self.error_topic).rstrip('/')
5555
self.discovery_topic = settings.get('discovery_topic', fallback=self.discovery_topic)
5656
self.discovery_enabled = strtobool(settings.get('discovery_enabled', self.discovery_enabled))
5757
self.json = strtobool(settings.get('json', self.json))
@@ -131,7 +131,7 @@ def mqtt_reconnect(self):
131131

132132
#sleep to give a chance to reconnect.
133133
time.sleep(self.reconnect_delay)
134-
if self.__connected:
134+
if self.connected:
135135
self.__reconnecting = 0
136136
return
137137
except:
@@ -144,23 +144,28 @@ def mqtt_reconnect(self):
144144
quit() #exit, service should restart entire script
145145

146146
def on_disconnect(self, client, userdata, rc):
147-
self.mqtt_reconnect()
147+
self.connected = False
148148

149149
def on_connect(self, client, userdata, flags, rc):
150150
""" The callback for when the client receives a CONNACK response from the server. """
151151
self._log.info("Connected with result code %s\n",str(rc))
152-
self.__connected = True
152+
self.connected = True
153153

154154
__write_topics : dict[str, registry_map_entry] = {}
155155

156156
def write_data(self, data : dict[str, str]):
157157
if not self.write_enabled:
158158
return
159159

160+
if self.connected:
161+
self.connected = self.client.is_connected()
162+
160163
self._log.info("write data to mqtt transport")
161164
self._log.info(data)
162165
#have to send this every loop, because mqtt doesnt disconnect when HA restarts. HA bug.
163-
self.client.publish(self.base_topic + "/availability","online", qos=0,retain=True)
166+
info = self.client.publish(self.base_topic + "/availability","online", qos=0,retain=True)
167+
if info.rc == MQTT_ERR_NO_CONN:
168+
self.connected = False
164169

165170
if(self.json):
166171
# Serializing json
@@ -204,7 +209,7 @@ def mqtt_discovery(self, from_transport : transport_base):
204209
device = {}
205210
device['manufacturer'] = from_transport.device_manufacturer
206211
device['model'] = from_transport.device_model
207-
device['identifiers'] = "hotnoob_" + from_transport.device_serial_number
212+
device['identifiers'] = "hotnoob_" + from_transport.device_model + "_" + from_transport.device_serial_number
208213
device['name'] = from_transport.device_name
209214

210215
registry_map : list[registry_map_entry] = []

classes/transports/pace.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ def __init__(self, settings : dict[str,str]):
306306
if "port" in settings:
307307
self.port = settings["port"]
308308

309-
if "buadrate" in settings:
310-
self.baudrate = settings["buadrate"]
309+
if "baudrate" in settings:
310+
self.baudrate = settings["baudrate"]
311311

312312
self.client = CustomModbusSerialClient(method='binary', port=self.port,
313313
baudrate=int(self.baudrate),

0 commit comments

Comments
 (0)