sdm630_mqtt/sdm630_mqtt.py

137 lines
4.7 KiB
Python

import logging
from sys import argv, exit
from time import sleep
from tomllib import load
import paho.mqtt.client as mqtt
from pyModbusTCP.client import ModbusClient
from pyModbusTCP.utils import decode_ieee, word_list_to_long
logging.basicConfig(level=logging.DEBUG)
LOG = logging.getLogger(__name__)
class SDM630_MQTT:
def __init__(
self,
config,
):
self.modbus = ModbusClient(
host=config["modbus"]["host"],
port=config["modbus"]["port"],
unit_id=config["modbus"]["unit_id"],
)
self.modbus.timeout = 1
self.mqtt = mqtt.Client()
self.mqtt_host = config["mqtt"]["host"]
self.mqtt_port = config["mqtt"]["port"]
self.mqtt_prefix = config["mqtt"]["prefix"]
self.mqtt.on_connect = self._on_mqtt_connect
self.mqtt.on_disconnect = self._on_mqtt_disconnect
def _publish(self, topic, msg):
LOG.debug(f"_publish({topic!r}, {msg!r})")
self.mqtt.publish(f"{self.mqtt_prefix.rstrip('/')}/{topic}", msg)
def _on_mqtt_connect(self, client, userdata, flags, rc):
LOG.info(f"Connected to mqtt server {self.mqtt_host}:{self.mqtt_port}")
def _on_mqtt_disconnect(self, client, userdata, rc):
LOG.info(f"Disconnected from mqtt server {self.mqtt_host}:{self.mqtt_port}")
def start(self):
LOG.debug("_start()")
self.mqtt.connect(self.mqtt_host, self.mqtt_port, 10)
self.mqtt.loop_start()
def stop(self):
self.mqtt.loop_stop()
self.mqtt.disconnect()
def request_and_publish(self):
self.modbus.open()
for idx, (readable, address) in enumerate(
{
"active-power/L1": 0x000C,
"active-power/L2": 0x000E,
"active-power/L3": 0x0010,
"active-power/total": 0x0034,
"apparent-power/L1": 0x0012,
"apparent-power/L2": 0x0014,
"apparent-power/L3": 0x0016,
"apparent-power/total": 0x0038,
"current-demand/L1": 0x0102,
"current-demand/L2": 0x0104,
"current-demand/L3": 0x0106,
"current-demand/N": 0x0068,
"current-demand/max_L1": 0x0108,
"current-demand/max_L2": 0x010A,
"current-demand/max_L3": 0x010C,
"current-demand/max_N": 0x006A,
"current/L1": 0x0006,
"current/L2": 0x0008,
"current/L3": 0x000A,
"current/average": 0x002E,
"current/neutral": 0x00E0,
"current/total": 0x0030,
"export_kVArh": 0x004C,
"export_kWh": 0x004A,
"frequency": 0x0046,
"import_kVArh": 0x004E,
"import_kWh": 0x0048,
"phase-angle/L1": 0x0024,
"phase-angle/L2": 0x0026,
"phase-angle/L3": 0x0028,
"phase-angle/total": 0x003E,
"power-demand/max": 0x0056,
"power-demand/total": 0x0054,
"power-factor/L1": 0x001E,
"power-factor/L2": 0x0020,
"power-factor/L3": 0x0022,
"power-factor/total": 0x003E,
"reactive-power/L1": 0x0018,
"reactive-power/L2": 0x001A,
"reactive-power/L3": 0x001E,
"reactive-power/total": 0x003C,
"voltage/L1_L2": 0x00C8,
"voltage/L1_N": 0x0000,
"voltage/L2_L3": 0x00CA,
"voltage/L2_N": 0x0002,
"voltage/L3_L1": 0x00CC,
"voltage/L3_N": 0x0004,
"voltage/average": 0x002A,
}.items()
):
try:
read = self.modbus.read_input_registers(address, 2)
longs = word_list_to_long(read)
if len(longs) == 1:
self._publish(readable, decode_ieee(longs[0]))
else:
self._publish(readable, sum([decode_ieee(x) for x in longs]))
except Exception:
LOG.exception(f'error while reading {readable} from meter')
if idx % 20:
self.modbus.close()
self.modbus.open()
self.modbus.close()
if __name__ == "__main__":
try:
with open(argv[1], "rb") as f:
config = load(f)
TITLE = config["printout"]["title"]
except Exception as e:
print(f"Usage: {argv[0]} config.toml")
exit(1)
client = SDM630_MQTT(config)
client.start()
try:
while True:
client.request_and_publish()
except Exception as e:
LOG.exception("oops")
client.stop()