Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for Algodue UEM meters on CP0 and CP1 #1960

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions packages/modules/common/algodue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/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)
# due to caching this appears rarely - but it's nice to have always have it in main log
log.error("Algodue meter serial " + 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

# due to caching this appears rarely - but it's nice to have always have it in main log
log.error("Algodue model: " + 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