diff --git a/dbus-modbus-client.py b/dbus-modbus-client.py index 6b5a43d..7a1f610 100755 --- a/dbus-modbus-client.py +++ b/dbus-modbus-client.py @@ -31,6 +31,7 @@ import cre import dse import ev_charger +import inepro import smappee import victron_em diff --git a/inepro.py b/inepro.py new file mode 100644 index 0000000..5683597 --- /dev/null +++ b/inepro.py @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2023 Holger Steinhaus +# +# SPDX-License-Identifier: MIT + +import device +import logging +import probe +from register import * + + +log = logging.getLogger() + + +class PRO380_Meter(device.EnergyMeter): + """ + Generic driver for the PRO380 meter series. There are a lot of brandings and variants available, + which are probably working on a similar register map. + + All register information based on https://ineprometering.com/wp-content/uploads/2019/04/PRO380-user-manual-V2.18v6.pdf + + Attention: The Inepro meter has a two-pole RS485 terminal (D+, D-) without any ground pin. + It therefore does not work with the genuine Victron RS485 cable and requires an INSULATED RS485 adapter! + Using an non-insulated cable will result in strange communication errors like missing bytes and injected zero bytes. + """ + + productid = 0xb077 + productname = 'PRO380-Mod' + + def __init__(self, *args): + super(PRO380_Meter, self).__init__(*args) + + self.info_regs = [ + Reg_u32b(0x4000, '/Serial'), + Reg_u32b(0x4007, '/FirmwareVersion'), + Reg_u32b(0x4009, '/HardwareVersion'), + ] + + def phase_regs(self, n): + s = 2 * (n - 1) + return [ + Reg_f32b(0x5002 + s, '/Ac/L%d/Voltage' % n, 1, '%.1f V'), + Reg_f32b(0x500c + s, '/Ac/L%d/Current' % n, 1, '%.1f A'), + Reg_f32b(0x5014 + s, '/Ac/L%d/Power' % n, scale=0.001, text='%.1f W'), + Reg_f32b(0x6012 + s, '/Ac/L%d/Energy/Forward' % n, 1.,'%.1f kWh'), + Reg_f32b(0x601c + s, '/Ac/L%d/Energy/Reverse' % n, 1.,'%.1f kWh'), + ] + + def device_init(self): + log.debug("init") + # make sure application is set to H + appreg = Reg_u16(0xa000) + self.read_info() + + phases = 3 + + regs = [ + Reg_f32b(0x5008, '/Ac/Frequency', 1., '%.1f Hz'), + Reg_f32b(0x5012, '/Ac/Power', 0.001, '%.1f W'), + Reg_f32b(0x502a, '/Ac/PowerFactor', 1, '%.1f'), + Reg_f32b(0x600c, '/Ac/Energy/Forward', 1., '%.1f kWh'), + Reg_f32b(0x6018, '/Ac/Energy/Reverse', 1., '%.1f kWh'), + ] + + for n in range(1, phases + 1): + regs += self.phase_regs(n) + + self.data_regs = regs + + def dbus_write_register(self, reg, path, val): + super(PRO380_Meter, self).dbus_write_register(reg, path, val) + self.sched_reinit() + + def get_ident(self): + return 'cg_%s' % self.info['/Serial'] + + +models = { + 0x2007: { + 'model': 'SolarLog PRO380-Mod', + 'handler': PRO380_Meter, + }, +} + + + +probe.add_handler(probe.ModelRegister(Reg_u16(0x1010), models, + methods=['rtu'], + rates=[9600], + units=[1])) diff --git a/register.py b/register.py index bc11a8e..fcbd8bc 100644 --- a/register.py +++ b/register.py @@ -130,6 +130,11 @@ class Reg_f32l(Reg_num): count = 2 rtype = float +class Reg_f32b(Reg_num): + coding = ('>f', '>2H') + count = 2 + rtype = float + class Reg_e16(Reg, int): def __init__(self, base, name, enum, **kwargs): super().__init__(base, 1, name, **kwargs)