Skip to content

Commit

Permalink
Initial support for Algodue UEM meters on CP
Browse files Browse the repository at this point in the history
  • Loading branch information
dj3mu committed Oct 19, 2024
1 parent b9e5c44 commit 42d95a6
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
95 changes: 95 additions & 0 deletions packages/modules/common/algodue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
import logging
from typing import List, Tuple, Optional

from modules.common import modbus
from modules.common.abstract_counter import AbstractCounter
from modules.common.modbus import ModbusDataType

log = logging.getLogger(__name__)


class Algodue(AbstractCounter):
serial_cached: Optional[str] = None
model_cached: Optional[str] = None

def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None:
self.client = client
self.id = modbus_id

def get_imported(self) -> float:
return self.client.read_input_registers(0x1106, ModbusDataType.FLOAT_32, unit=self.id)

def get_exported(self) -> float:
return self.client.read_input_registers(0x110e, ModbusDataType.FLOAT_32, unit=self.id)

def get_frequency(self) -> float:
return self.client.read_input_registers(0x1038, ModbusDataType.FLOAT_32, unit=self.id)

def get_serial_number(self) -> Optional[str]:
# serial will never change - at least until power cycle
if self.serial_cached is not None:
return self.serial_cached

serial_chars = self.client.read_holding_registers(0x500, [ModbusDataType.UINT_8]*10, unit=self.id)
serial_string = ""
for x in serial_chars:
serial_string += chr(x)
log.debug("Algodue serial is " + serial_string)
self.serial_cached = serial_string
return self.serial_cached

def get_currents(self) -> List[float]:
return self.client.read_input_registers(0x100E, [ModbusDataType.FLOAT_32]*3, unit=self.id)

def get_power_factors(self) -> List[float]:
return self.client.read_input_registers(0x1018, [ModbusDataType.FLOAT_32]*3, unit=self.id)

def get_power(self) -> Tuple[List[float], float]:
powers = self.client.read_input_registers(0x1020, [ModbusDataType.FLOAT_32]*3, unit=self.id)
power = sum(powers)
return powers, power

def get_voltages(self) -> List[float]:
return self.client.read_input_registers(0x1000, [ModbusDataType.FLOAT_32]*3, unit=self.id)

def get_model(self) -> Optional[str]:
# model will never change - at least until power cycle
if self.model_cached is not None:
return self.model_cached

model_id = self.client.read_holding_registers(0x505, ModbusDataType.UINT_16, unit=self.id)
model_string = "unknown"
if model_id == 0x03:
model_string = "6 A, 3 phases, 4 wires"
elif model_id == 0x08:
model_string = "80 A, 3 phases, 4 wires"
elif model_id == 0x0c:
model_string = "80 A, 1 phase, 2 wires"
elif model_id == 0x10:
model_string = "40 A, 1 phase, 2 wires"
elif model_id == 0x12:
model_string = "63 A, 3 phases, 4 wires"

type_id = self.client.read_holding_registers(0x506, ModbusDataType.UINT_16, unit=self.id)
type_string = "unknown"
if type_id == 0x00:
type_string = "NO MID, RESET"
elif type_id == 0x01:
type_string = "MID"
elif type_id == 0x02:
type_string = "NO MID"
elif type_id == 0x03:
type_string = "NO MID, Wiring selection"
elif type_id == 0x05:
type_string = "MID no varh"
elif type_id == 0x09:
type_string = "MID Wiring selection"
elif type_id == 0x0a:
type_string = "MID no varh, Wiring selection"
elif type_id == 0x0b:
type_string = "NO MID, RESET, Wiring selection"
meterinfo = "Algodue UEM " + model_string + " (" + type_string + ")"
log.error("Algodue model returning: " + meterinfo)
self.model_cached = meterinfo
return self.model_cached
16 changes: 11 additions & 5 deletions packages/modules/internal_chargepoint_handler/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from modules.common.hardware_check import SeriesHardwareCheckMixin

from modules.common.modbus import ModbusSerialClient_, ModbusTcpClient_
from modules.common import mpm3pm, sdm
from modules.common import mpm3pm, sdm, algodue
from modules.common import evse
from modules.common import b23

Expand All @@ -15,13 +15,19 @@

BUS_SOURCES = ("/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyACM0", "/dev/serial0")

METERS = Union[mpm3pm.Mpm3pm, sdm.Sdm630_72, b23.B23]
METERS = Union[mpm3pm.Mpm3pm, sdm.Sdm630_72, b23.B23, algodue.Algodue]
meter_config = NamedTuple("meter_config", [('type', METERS), ('modbus_id', int)])

# Note: Algodue meters expect entry of modbus ID in hex. 9b = 155, 9c = 156.
# We code ID in hex here so it's exactly what must be entered in meter.
CP0_METERS = [meter_config(mpm3pm.Mpm3pm, modbus_id=5),
meter_config(sdm.Sdm630_72, modbus_id=105),
meter_config(b23.B23, modbus_id=201)]
meter_config(b23.B23, modbus_id=201),
meter_config(algodue.Algodue, modbus_id=0x9b)]

CP1_METERS = [meter_config(mpm3pm.Mpm3pm, modbus_id=6), meter_config(sdm.Sdm630_72, modbus_id=106)]
CP1_METERS = [meter_config(mpm3pm.Mpm3pm, modbus_id=6),
meter_config(sdm.Sdm630_72, modbus_id=106),
meter_config(algodue.Algodue, modbus_id=0x9c)]

EVSE_ID_CP0 = [1]
EVSE_ID_TWO_BUSSES_CP1 = [1, 2]
Expand Down Expand Up @@ -67,7 +73,7 @@ def find_meter_client(meters: List[meter_config], client: Union[ModbusSerialClie
try:
if meter_client.get_voltages()[0] > 200:
with ModifyLoglevelContext(log, logging.DEBUG):
log.debug("Verbauter Zähler: "+str(meter_type)+" mit Modbus-ID: "+str(modbus_id))
log.error("Verbauter Zähler: "+str(meter_type)+" mit Modbus-ID: "+str(modbus_id))
return meter_client
except Exception:
log.debug(client)
Expand Down

0 comments on commit 42d95a6

Please sign in to comment.