2026-06-25 18:51:33 +02:00

105 lines
3.2 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import json
import struct
import sys
from datetime import datetime, timezone
from pymodbus.client import ModbusSerialClient
def decode_float_abcd(registers):
if len(registers) != 2:
raise ValueError(f"Expected 2 registers, got {len(registers)}")
raw = registers[0].to_bytes(2, "big") + registers[1].to_bytes(2, "big")
return struct.unpack(">f", raw)[0]
def read_float(client, address, device_id):
rr = client.read_input_registers(
address=address,
count=2,
device_id=device_id,
)
if rr.isError():
raise RuntimeError(f"Modbus error at address {address}: {rr}")
return decode_float_abcd(rr.registers)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--port", default="/dev/ttyRS485")
parser.add_argument("--slave", type=int, default=1)
parser.add_argument("--baud", type=int, default=9600)
parser.add_argument("--parity", default="N")
parser.add_argument("--stopbits", type=int, default=1)
parser.add_argument("--bytesize", type=int, default=8)
parser.add_argument("--timeout", type=float, default=1.5)
args = parser.parse_args()
client = ModbusSerialClient(
port=args.port,
baudrate=args.baud,
parity=args.parity,
stopbits=args.stopbits,
bytesize=args.bytesize,
timeout=args.timeout,
)
if not client.connect():
print("[ERROR] Cannot open Modbus serial port", file=sys.stderr)
return 2
try:
voltage_v = read_float(client, 0, args.slave)
current_a = read_float(client, 6, args.slave)
active_power_w = read_float(client, 12, args.slave)
frequency_hz = read_float(client, 70, args.slave)
import_kwh = read_float(client, 72, args.slave)
export_kwh = read_float(client, 74, args.slave)
payload = {
"ts": datetime.now(timezone.utc).isoformat(),
"source": "revpi_sdm120m",
"meter_model": "Eastron SDM120-M",
"meter_id": f"modbus_{args.slave}",
"port": args.port,
"slave": args.slave,
"baud": args.baud,
"voltage_l1_v": round(voltage_v, 3),
"current_l1_a": round(current_a, 3),
"power_total_kw": round(active_power_w / 1000.0, 6),
"frequency_hz": round(frequency_hz, 3),
"import_kwh": round(import_kwh, 3),
"export_kwh": round(export_kwh, 3),
"comm_ok": True,
}
print(json.dumps(payload, indent=2, ensure_ascii=False))
return 0
except Exception as exc:
error_payload = {
"ts": datetime.now(timezone.utc).isoformat(),
"source": "revpi_sdm120m",
"meter_model": "Eastron SDM120-M",
"meter_id": f"modbus_{args.slave}",
"port": args.port,
"slave": args.slave,
"baud": args.baud,
"comm_ok": False,
"error_text": f"{type(exc).__name__}: {exc}",
}
print(json.dumps(error_payload, indent=2, ensure_ascii=False))
return 1
finally:
client.close()
if __name__ == "__main__":
sys.exit(main())