From 7c81f5bd55793798f492abfe1ea1f9ef14fcf208 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 20 Jan 2025 18:31:10 +0100 Subject: [PATCH 1/9] Adapt to newer version of pymodbus - framer and Big/BIG, Little/LITTLE --- semp-rtu.py | 19 ++++++++++--------- semp-tcp.py | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/semp-rtu.py b/semp-rtu.py index 04cd82d..d4055db 100755 --- a/semp-rtu.py +++ b/semp-rtu.py @@ -7,19 +7,19 @@ import sys import threading import time +import traceback from pymodbus.server import StartSerialServer from pymodbus.constants import Endian from pymodbus.device import ModbusDeviceIdentification -from pymodbus.transaction import ModbusRtuFramer from pymodbus.datastore import ModbusSlaveContext from pymodbus.datastore import ModbusServerContext from pymodbus.payload import BinaryPayloadBuilder - +# Protocol for WattNode register list: https://ctlsys.com/wp-content/uploads/2016/10/WNC-Modbus-Register-List-V18.xls def t_update(ctx, stop, module, device, refresh): - this_t = threading.currentThread() + this_t = threading.current_thread() logger = logging.getLogger() while not stop.is_set(): @@ -30,7 +30,7 @@ def t_update(ctx, stop, module, device, refresh): logger.debug(f"{this_t.name}: no new values") continue - block_1001 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1001 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1001.add_32bit_float(values.get("energy_active", 0)) # total active energy block_1001.add_32bit_float(values.get("import_energy_active", 0)) # imported active energy block_1001.add_32bit_float(values.get("energy_active", 0)) # total active energy non-reset @@ -50,7 +50,7 @@ def t_update(ctx, stop, module, device, refresh): block_1001.add_32bit_float(values.get("frequency", 0)) # line frequency ctx.setValues(3, 1000, block_1001.to_registers()) - block_1101 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1101 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1101.add_32bit_float(values.get("l1_energy_active", 0)) # total active energy l1 block_1101.add_32bit_float(values.get("l2_energy_active", 0)) # total active energy l2 block_1101.add_32bit_float(values.get("l3_energy_active", 0)) # total active energy l3 @@ -95,6 +95,7 @@ def t_update(ctx, stop, module, device, refresh): ctx.setValues(3, 1100, block_1101.to_registers()) except Exception as e: logger.critical(f"{this_t.name}: {e}") + print(traceback.format_exc()) finally: time.sleep(refresh) @@ -157,7 +158,7 @@ def t_update(ctx, stop, module, device, refresh): slave_ctx = ModbusSlaveContext() - block_1601 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1601 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1601.add_32bit_int(0) # config passcode block_1601.add_16bit_int(confparser[meter].getint("ct_current", fallback=default_config["meters"]["ct_current"])) # ct rated current block_1601.add_16bit_int(confparser[meter].getint("ct_current", fallback=default_config["meters"]["ct_current"])) # ct rated current l1 @@ -182,7 +183,7 @@ def t_update(ctx, stop, module, device, refresh): block_1601.add_16bit_int(0) # io pin mode slave_ctx.setValues(3, 1600, block_1601.to_registers()) - block_1651 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1651 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1651.add_16bit_int(0) # apply config block_1651.add_16bit_int(address) # modbus address block_1651.add_16bit_int(4) # baud rate @@ -191,7 +192,7 @@ def t_update(ctx, stop, module, device, refresh): block_1651.add_16bit_int(5) # message delay slave_ctx.setValues(3, 1650, block_1651.to_registers()) - block_1701 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1701 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1701.add_32bit_int(confparser[meter].getint("serial_number", fallback=default_config["meters"]["serial_number"])) # serial number block_1701.add_32bit_int(0) # uptime (s) block_1701.add_32bit_int(0) # total uptime (s) @@ -247,7 +248,7 @@ def t_update(ctx, stop, module, device, refresh): StartSerialServer( context=server_ctx, - framer=ModbusRtuFramer, + framer="rtu", identity=identity, port=confparser["server"].get("device", fallback=default_config["server"]["device"]), baudrate=confparser["server"].get("baud", fallback=default_config["server"]["baud"]), diff --git a/semp-tcp.py b/semp-tcp.py index a4b815f..d0ef3b9 100755 --- a/semp-tcp.py +++ b/semp-tcp.py @@ -7,20 +7,19 @@ import sys import threading import time +import traceback from pymodbus.server import StartTcpServer from pymodbus.constants import Endian from pymodbus.device import ModbusDeviceIdentification -from pymodbus.transaction import ModbusSocketFramer -from pymodbus.transaction import ModbusRtuFramer from pymodbus.datastore import ModbusSlaveContext from pymodbus.datastore import ModbusServerContext from pymodbus.payload import BinaryPayloadBuilder - +# Protocol for WattNode register list: https://ctlsys.com/wp-content/uploads/2016/10/WNC-Modbus-Register-List-V18.xls def t_update(ctx, stop, module, device, refresh): - this_t = threading.currentThread() + this_t = threading.current_thread() logger = logging.getLogger() while not stop.is_set(): @@ -30,8 +29,8 @@ def t_update(ctx, stop, module, device, refresh): if not values: logger.debug(f"{this_t.name}: no new values") continue - - block_1001 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + + block_1001 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1001.add_32bit_float(values.get("energy_active", 0)) # total active energy block_1001.add_32bit_float(values.get("import_energy_active", 0)) # imported active energy block_1001.add_32bit_float(values.get("energy_active", 0)) # total active energy non-reset @@ -51,7 +50,7 @@ def t_update(ctx, stop, module, device, refresh): block_1001.add_32bit_float(values.get("frequency", 0)) # line frequency ctx.setValues(3, 1000, block_1001.to_registers()) - block_1101 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1101 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1101.add_32bit_float(values.get("l1_energy_active", 0)) # total active energy l1 block_1101.add_32bit_float(values.get("l2_energy_active", 0)) # total active energy l2 block_1101.add_32bit_float(values.get("l3_energy_active", 0)) # total active energy l3 @@ -96,6 +95,7 @@ def t_update(ctx, stop, module, device, refresh): ctx.setValues(3, 1100, block_1101.to_registers()) except Exception as e: logger.critical(f"{this_t.name}: {e}") + print(traceback.format_exc()) finally: time.sleep(refresh) @@ -157,7 +157,7 @@ def t_update(ctx, stop, module, device, refresh): slave_ctx = ModbusSlaveContext() - block_1601 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1601 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1601.add_32bit_int(1234) # config passcode block_1601.add_16bit_int(confparser[meter].getint("ct_current", fallback=default_config["meters"]["ct_current"])) # ct rated current block_1601.add_16bit_int(confparser[meter].getint("ct_current", fallback=default_config["meters"]["ct_current"])) # ct rated current l1 @@ -182,7 +182,7 @@ def t_update(ctx, stop, module, device, refresh): block_1601.add_16bit_int(0) # io pin mode slave_ctx.setValues(3, 1600, block_1601.to_registers()) - block_1651 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1651 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1651.add_16bit_int(0) # apply config block_1651.add_16bit_int(address) # modbus address block_1651.add_16bit_int(4) # baud rate @@ -191,7 +191,7 @@ def t_update(ctx, stop, module, device, refresh): block_1651.add_16bit_int(5) # message delay slave_ctx.setValues(3, 1650, block_1651.to_registers()) - block_1701 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) + block_1701 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) block_1701.add_32bit_int(confparser[meter].getint("serial_number", fallback=default_config["meters"]["serial_number"])) # serial number block_1701.add_32bit_int(0) # uptime (s) block_1701.add_32bit_int(0) # total uptime (s) @@ -240,9 +240,9 @@ def t_update(ctx, stop, module, device, refresh): framer = False if config_framer == "socket": - framer = ModbusSocketFramer + framer = "socket" elif config_framer == "rtu": - framer = ModbusRtuFramer + framer = "rtu" identity = ModbusDeviceIdentification() server_ctx = ModbusServerContext(slaves=slaves, single=False) From 55602938534117dffbf29c2402d1c35408a59443 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 20 Jan 2025 19:00:36 +0100 Subject: [PATCH 2/9] Add support for Carlo Gavazzi EM24 --- devices/em24.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 devices/em24.py diff --git a/devices/em24.py b/devices/em24.py new file mode 100644 index 0000000..85de384 --- /dev/null +++ b/devices/em24.py @@ -0,0 +1,128 @@ +import logging +import sys +import sdm_modbus + + +def device(config): + + # Configuration parameters: + # + # timeout seconds to wait for a response, default: 1 + # retries number of retries, default: 3 + # unit modbus address, default: 1 + # + # For Modbus TCP: + # host ip or hostname + # port modbus tcp port + # + # For Modbus RTU: + # device serial device, e.g. /dev/ttyUSB0 + # stopbits number of stop bits + # parity parity setting, N, E or O + # baud baud rate + + timeout = config.getint("timeout", fallback=1) + retries = config.getint("retries", fallback=3) + unit = config.getint("src_address", fallback=1) + + host = config.get("host", fallback=False) + port = config.getint("port", fallback=False) + device = config.get("device", fallback=False) + + if device: + stopbits = config.getint("stopbits", fallback=1) + parity = config.get("parity", fallback="N") + baud = config.getint("baud", fallback=2400) + + if (parity + and parity.upper() in ["N", "E", "O"]): + parity = parity.upper() + else: + parity = False + + return sdm_modbus.EM24( + device=device, + stopbits=stopbits, + parity=parity, + baud=baud, + timeout=timeout, + retries=retries, + unit=unit + ) + else: + return sdm_modbus.EM24( + host=host, + port=port, + timeout=timeout, + retries=retries, + unit=unit + ) + + +def values(device): + if not device: + return {} + + logger = logging.getLogger() + logger.debug(f"device: {device}") + + values = device.read_all(scaling = True) + + logger.debug(f"values: {values}") + + return { + "import_energy_active": values.get("import_energy_active", 0), + "power_active": values.get("power_active", 0), + "l1_power_active": values.get("l1_power_active", 0), + "l2_power_active": values.get("l2_power_active", 0), + "l3_power_active": values.get("l3_power_active", 0), + "voltage_ln": values.get("voltage_ln", 0), + "l1n_voltage": values.get("l1_voltage", 0), + "l1n_voltage": values.get("l2_voltage", 0), + "l1n_voltage": values.get("l3_voltage", 0), + "voltage_ll": values.get("voltage_ll", 0), + "l12_voltage": values.get("l12_voltage", 0), + "l23_voltage": values.get("l23_voltage", 0), + "l31_voltage": values.get("l31_voltage", 0), + "frequency": values.get("frequency", 0), + # "l1_energy_active" + # "l2_energy_active" + # "l3_energy_active" + "import_energy_active": values.get("import_energy_active", 0), + "l1_import_energy_active": values.get("l1_import_energy_active", 0), + "l2_import_energy_active": values.get("l2_import_energy_active", 0), + "l3_import_energy_active": values.get("l3_import_energy_active", 0), + "export_energy_active": values.get("export_energy_active", 0), + #"l1_export_energy_active" + #"l2_export_energy_active" + #"l3_export_energy_active" + #"l1_energy_reactive" + #"l2_energy_reactive" + #"l3_energy_reactive" + #"energy_apparent" + #"l1_energy_apparent" + #"l2_energy_apparent" + #"l3_energy_apparent" + "power_factor": values.get("total_pf", 0), + "l1_power_factor": values.get("l1_power_factor", 0), + "l2_power_factor": values.get("l2_power_factor", 0), + "l3_power_factor": values.get("l3_power_factor", 0), + "power_reactive": values.get("power_reactive", 0), + "l1_power_reactive": values.get("l1_power_reactive", 0), + "l2_power_reactive": values.get("l2_power_reactive", 0), + "l3_power_reactive": values.get("l3_power_reactive", 0), + "power_apparent": values.get("power_apparent", 0), + "l1_power_apparent": values.get("l1_power_apparent", 0), + "l2_power_apparent": values.get("l2_power_apparent", 0), + "l3_power_apparent": values.get("l3_power_apparent", 0), + "l1_current": values.get("l1_current", 0), + "l2_current": values.get("l2_current", 0), + "l3_current": values.get("l3_current", 0), + "demand_power_active": values.get("demand_power_active", 0), + # "minimum_demand_power_active" + "maximum_demand_power_active": values.get("maximum_demand_power_active", 0), + # "demand_power_apparent" + #"l1_demand_power_active" + #"l2_demand_power_active" + #"l3_demand_power_active" + } From 3e1024438a701e0c9eadcfbd46ab05cfed5b3a4a Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Tue, 21 Jan 2025 14:00:07 +0100 Subject: [PATCH 3/9] Allow refresh time in fractions of seconds --- semp-rtu.py | 2 +- semp-tcp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/semp-rtu.py b/semp-rtu.py index d4055db..beb339a 100755 --- a/semp-rtu.py +++ b/semp-rtu.py @@ -224,7 +224,7 @@ def t_update(ctx, stop, module, device, refresh): update_t_stop, meter_module, meter_device, - confparser[meter].getint("refresh_rate", fallback=default_config["meters"]["refresh_rate"]) + confparser[meter].getfloat("refresh_rate", fallback=default_config["meters"]["refresh_rate"]) ) ) diff --git a/semp-tcp.py b/semp-tcp.py index d0ef3b9..330be81 100755 --- a/semp-tcp.py +++ b/semp-tcp.py @@ -223,7 +223,7 @@ def t_update(ctx, stop, module, device, refresh): update_t_stop, meter_module, meter_device, - confparser[meter].getint("refresh_rate", fallback=default_config["meters"]["refresh_rate"]) + confparser[meter].getfloat("refresh_rate", fallback=default_config["meters"]["refresh_rate"]) ) ) From 5567e3e80bb99db29cd48d4bacbc296003c97b65 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 26 Jan 2025 10:36:14 +0100 Subject: [PATCH 4/9] Calculate sum energy active --- devices/em24.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/em24.py b/devices/em24.py index 85de384..d8ef28d 100644 --- a/devices/em24.py +++ b/devices/em24.py @@ -71,7 +71,7 @@ def values(device): logger.debug(f"values: {values}") return { - "import_energy_active": values.get("import_energy_active", 0), + "energy_active": values.get("import_energy_active", 0) - values.get("export_energy_active", 0)), "power_active": values.get("power_active", 0), "l1_power_active": values.get("l1_power_active", 0), "l2_power_active": values.get("l2_power_active", 0), From 3e70841331b8a45826986b47be19089bdf6e987a Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 26 Jan 2025 10:57:29 +0100 Subject: [PATCH 5/9] fix parentheses --- devices/em24.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/em24.py b/devices/em24.py index d8ef28d..23cde43 100644 --- a/devices/em24.py +++ b/devices/em24.py @@ -71,7 +71,7 @@ def values(device): logger.debug(f"values: {values}") return { - "energy_active": values.get("import_energy_active", 0) - values.get("export_energy_active", 0)), + "energy_active": values.get("import_energy_active", 0) - values.get("export_energy_active", 0), "power_active": values.get("power_active", 0), "l1_power_active": values.get("l1_power_active", 0), "l2_power_active": values.get("l2_power_active", 0), From 0de652d1f8494586d1f883a98a4c62b77b6ba526 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 26 Jan 2025 11:15:44 +0100 Subject: [PATCH 6/9] more calculated variables --- devices/em24.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/devices/em24.py b/devices/em24.py index 23cde43..46e07bd 100644 --- a/devices/em24.py +++ b/devices/em24.py @@ -85,17 +85,18 @@ def values(device): "l23_voltage": values.get("l23_voltage", 0), "l31_voltage": values.get("l31_voltage", 0), "frequency": values.get("frequency", 0), - # "l1_energy_active" - # "l2_energy_active" - # "l3_energy_active" + "l1_energy_active": values.get("l1_import_energy_active", 0) - values.get("export_energy_active", 0)/3, + "l2_energy_active": values.get("l2_import_energy_active", 0) - values.get("export_energy_active", 0)/3, + "l3_energy_active": values.get("l3_import_energy_active", 0) - values.get("export_energy_active", 0)/3, "import_energy_active": values.get("import_energy_active", 0), "l1_import_energy_active": values.get("l1_import_energy_active", 0), "l2_import_energy_active": values.get("l2_import_energy_active", 0), "l3_import_energy_active": values.get("l3_import_energy_active", 0), "export_energy_active": values.get("export_energy_active", 0), - #"l1_export_energy_active" - #"l2_export_energy_active" - #"l3_export_energy_active" + "l1_export_energy_active": values.get("export_energy_active", 0)/3, + "l2_export_energy_active": values.get("export_energy_active", 0)/3, + "l3_export_energy_active": values.get("export_energy_active", 0)/3, + "energy_reactive": values.get("import_energy_reactive", 0) - values.get("export_energy_reactive", 0), #"l1_energy_reactive" #"l2_energy_reactive" #"l3_energy_reactive" From 8b8b416a85f65be2231fab4a1c62ea08050a7799 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 26 Jan 2025 15:02:23 +0100 Subject: [PATCH 7/9] typos --- devices/em24.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/em24.py b/devices/em24.py index 46e07bd..de20694 100644 --- a/devices/em24.py +++ b/devices/em24.py @@ -78,8 +78,8 @@ def values(device): "l3_power_active": values.get("l3_power_active", 0), "voltage_ln": values.get("voltage_ln", 0), "l1n_voltage": values.get("l1_voltage", 0), - "l1n_voltage": values.get("l2_voltage", 0), - "l1n_voltage": values.get("l3_voltage", 0), + "l2n_voltage": values.get("l2_voltage", 0), + "l3n_voltage": values.get("l3_voltage", 0), "voltage_ll": values.get("voltage_ll", 0), "l12_voltage": values.get("l12_voltage", 0), "l23_voltage": values.get("l23_voltage", 0), From 5a69855afc8772c6543a0077ddffe17ebbec4f36 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 2 Feb 2025 10:08:57 +0100 Subject: [PATCH 8/9] support request for register 2127 --- semp-tcp.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/semp-tcp.py b/semp-tcp.py index 330be81..e7a0376 100755 --- a/semp-tcp.py +++ b/semp-tcp.py @@ -213,6 +213,12 @@ def t_update(ctx, stop, module, device, refresh): block_1701.add_16bit_int(0) # error status 7 block_1701.add_16bit_int(0) # error status 8 slave_ctx.setValues(3, 1700, block_1701.to_registers()) + + block_2128 = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE) + block_2128.add_16bit_int(0) # SolarEdge requests the value for the register 2127 + # If you don't supply it, it will keep asking + # if you supply it, it will only ask it once + slave_ctx.setValues(3, 2127, block_2128.to_registers()) update_t_stop = threading.Event() update_t = threading.Thread( From 11cb19c437db57988c24a52bc11336756a5934cc Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 23 Feb 2025 09:01:14 +0100 Subject: [PATCH 9/9] Fixed sign in virtual meteres --- devices/em24.py | 10 +++++----- semp-tcp.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/devices/em24.py b/devices/em24.py index de20694..cc312f5 100644 --- a/devices/em24.py +++ b/devices/em24.py @@ -71,7 +71,7 @@ def values(device): logger.debug(f"values: {values}") return { - "energy_active": values.get("import_energy_active", 0) - values.get("export_energy_active", 0), + "energy_active": values.get("import_energy_active", 0) + values.get("export_energy_active", 0), "power_active": values.get("power_active", 0), "l1_power_active": values.get("l1_power_active", 0), "l2_power_active": values.get("l2_power_active", 0), @@ -85,9 +85,9 @@ def values(device): "l23_voltage": values.get("l23_voltage", 0), "l31_voltage": values.get("l31_voltage", 0), "frequency": values.get("frequency", 0), - "l1_energy_active": values.get("l1_import_energy_active", 0) - values.get("export_energy_active", 0)/3, - "l2_energy_active": values.get("l2_import_energy_active", 0) - values.get("export_energy_active", 0)/3, - "l3_energy_active": values.get("l3_import_energy_active", 0) - values.get("export_energy_active", 0)/3, + "l1_energy_active": values.get("l1_import_energy_active", 0) + values.get("export_energy_active", 0)/3, + "l2_energy_active": values.get("l2_import_energy_active", 0) + values.get("export_energy_active", 0)/3, + "l3_energy_active": values.get("l3_import_energy_active", 0) + values.get("export_energy_active", 0)/3, "import_energy_active": values.get("import_energy_active", 0), "l1_import_energy_active": values.get("l1_import_energy_active", 0), "l2_import_energy_active": values.get("l2_import_energy_active", 0), @@ -96,7 +96,7 @@ def values(device): "l1_export_energy_active": values.get("export_energy_active", 0)/3, "l2_export_energy_active": values.get("export_energy_active", 0)/3, "l3_export_energy_active": values.get("export_energy_active", 0)/3, - "energy_reactive": values.get("import_energy_reactive", 0) - values.get("export_energy_reactive", 0), + "energy_reactive": values.get("import_energy_reactive", 0) + values.get("export_energy_reactive", 0), #"l1_energy_reactive" #"l2_energy_reactive" #"l3_energy_reactive" diff --git a/semp-tcp.py b/semp-tcp.py index e7a0376..c24f733 100755 --- a/semp-tcp.py +++ b/semp-tcp.py @@ -16,7 +16,9 @@ from pymodbus.datastore import ModbusServerContext from pymodbus.payload import BinaryPayloadBuilder -# Protocol for WattNode register list: https://ctlsys.com/wp-content/uploads/2016/10/WNC-Modbus-Register-List-V18.xls +# Protocol for WattNode register list: +# https://ctlsys.com/wp-content/uploads/2016/10/WNC-Modbus-Register-List-V18.xls +# https://ctlsys.com/wp-content/uploads/2016/10/WNC-Modbus-Manual-V18c.pdf def t_update(ctx, stop, module, device, refresh): this_t = threading.current_thread()