Skip to content

Commit

Permalink
Merge pull request #51 from stefanor/smart-shunt
Browse files Browse the repository at this point in the history
Smart  shunt tweaks
  • Loading branch information
keshavdv committed May 31, 2024
2 parents e28c5f8 + e181a99 commit cccd995
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 40 deletions.
25 changes: 21 additions & 4 deletions tests/test_dc_energy_meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_end_to_end_parse(self) -> None:
)

assert isinstance(actual, DcEnergyMeterData)
assert actual.get_meter_type() == MeterType.FUEL_CELL
assert actual.get_meter_type() == MeterType.DC_DC_CHARGER
assert actual.get_aux_mode() == AuxMode.STARTER_VOLTAGE
assert actual.get_current() == 0.0
assert actual.get_voltage() == 12.52
Expand All @@ -31,7 +31,7 @@ def parse_decrypted(self, decrypted: str) -> DcEnergyMeterData:

def test_parse(self) -> None:
actual = self.parse_decrypted("fdffe4040000ffff00000059a65a1c8c")
assert actual.get_meter_type() == MeterType.FUEL_CELL
assert actual.get_meter_type() == MeterType.DC_DC_CHARGER
assert actual.get_aux_mode() == AuxMode.STARTER_VOLTAGE
assert actual.get_current() == 0.0
assert actual.get_voltage() == 12.52
Expand All @@ -41,8 +41,25 @@ def test_parse(self) -> None:

def test_aux_starter(self) -> None:
actual = self.parse_decrypted("fdffe4040000ffff00000059a65a1c8c")
assert actual.get_aux_mode() == AuxMode.STARTER_VOLTAGE
assert actual.get_starter_voltage() == -0.01

def test_aux_temperature(self) -> None:
def test_aux_temperature_none(self) -> None:
actual = self.parse_decrypted("fdffe4040000ffff020000ae28af8a5c")
assert actual.get_temperature() == 382.2
assert actual.get_aux_mode() == AuxMode.TEMPERATURE
assert actual.get_temperature() is None

def test_aux_temperature(self) -> None:
actual = self.parse_decrypted("fdffe40400008888020000ae28af8a5c")
assert actual.get_aux_mode() == AuxMode.TEMPERATURE
assert actual.get_temperature() == 76.37

def test_generic_source(self) -> None:
actual = self.parse_decrypted("ffff6f0a0000000003000043698118eb")
assert actual.get_meter_type() == MeterType.GENERIC_SOURCE
assert actual.get_aux_mode() == AuxMode.DISABLED
assert actual.get_current() == 0.0
assert actual.get_voltage() == 26.71
assert actual.get_starter_voltage() is None
assert actual.get_alarm() is None
assert actual.get_temperature() is None
1 change: 1 addition & 0 deletions victron_ble/devices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ class ACInState(Enum):
0xA3C7: "Orion Smart 48V|48V-6A Isolated DC-DC Charger",
0xA3CF: "Orion Smart 48V|48V-8.5A Isolated DC-DC Charger",
0x2780: "Victron Multiplus II 12/3000/120-50 2x120V",
0xC030: "SmartShunt IP65 500A/50mV",
}


Expand Down
78 changes: 42 additions & 36 deletions victron_ble/devices/dc_energy_meter.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
from enum import Enum
from typing import Optional

from construct import Int16sl, Int16ul, Int24sl, Struct
from construct import BitStruct, BitsInteger, ByteSwapped, Int16sl, Padding

from victron_ble.devices.base import AlarmReason, Device, DeviceData, kelvin_to_celsius
from victron_ble.devices.battery_monitor import AuxMode


class MeterType(Enum):
SOLAR_CHARGER = -7
WIND_CHARGER = -6
SHAFT_GENERATOR = -5
ALTERNATOR = -4
FUEL_CELL = -3
WATER_GENERATOR = -2
DC_DC_CHARGER = -1
AC_CHARGER = 1
GENERIC_SOURCE = 2
GENERIC_LOAD = 3
ELECTRIC_DRIVE = 4
FRIDGE = 5
WATER_PUMP = 6
BILGE_PUMP = 7
DC_SYSTEM = 8
INVERTER = 9
WATER_HEATER = 10
SOLAR_CHARGER = -9
WIND_CHARGER = -8
SHAFT_GENERATOR = -7
ALTERNATOR = -6
FUEL_CELL = -5
WATER_GENERATOR = -4
DC_DC_CHARGER = -3
AC_CHARGER = -2
GENERIC_SOURCE = -1
GENERIC_LOAD = 1
ELECTRIC_DRIVE = 2
FRIDGE = 3
WATER_PUMP = 4
BILGE_PUMP = 5
DC_SYSTEM = 6
INVERTER = 7
WATER_HEATER = 8


class DcEnergyMeterData(DeviceData):
Expand Down Expand Up @@ -77,27 +77,29 @@ def get_starter_voltage(self) -> Optional[float]:
class DcEnergyMeter(Device):
data_type = DcEnergyMeterData

PACKET = Struct(
"meter_type" / Int16sl,
# Voltage reading in 10mV increments
"voltage" / Int16ul,
# Alarm reason
"alarm" / Int16ul,
# Value of the auxillary input
"aux" / Int16ul,
# The upper 22 bits indicate the current in milliamps
# The lower 2 bits identify the aux input mode:
# 0 = Starter battery voltage
# 1 = Midpoint voltage
# 2 = Temperature
# 3 = Disabled
"current" / Int24sl,
PACKET = ByteSwapped(
BitStruct(
Padding(40),
"current" / BitsInteger(22, signed=True),
# Aux input mode:
# 0 = Starter battery voltage
# 1 = Midpoint voltage
# 2 = Temperature
# 3 = Disabled
"aux_mode" / BitsInteger(2),
# Value of the auxillary input
"aux" / BitsInteger(16),
# Alarm reason
"alarm" / BitsInteger(16),
# Voltage reading in 10mV increments
"voltage" / BitsInteger(16, signed=True),
"meter_type" / BitsInteger(16, signed=True),
)
)

def parse_decrypted(self, decrypted: bytes) -> dict:
pkt = self.PACKET.parse(decrypted)

aux_mode = AuxMode(pkt.current & 0b11)
aux_mode = AuxMode(pkt.aux_mode)

parsed = {
"meter_type": MeterType(pkt.meter_type),
Expand All @@ -113,6 +115,10 @@ def parse_decrypted(self, decrypted: bytes) -> dict:
Int16sl.parse((pkt.aux).to_bytes(2, "little")) / 100
)
elif aux_mode == AuxMode.TEMPERATURE:
parsed["temperature_kelvin"] = pkt.aux / 100
if pkt.aux == 0xFFFF:
temp_k = None
else:
temp_k = pkt.aux / 100
parsed["temperature_kelvin"] = temp_k

return parsed

0 comments on commit cccd995

Please sign in to comment.