Skip to content

Commit 2c0af8a

Browse files
Update victron_mqtt to 2026.6.3 (#433)
## Changes - Rename multi AC Input sensors to distinguish from AC Output (#423) - Set precision=2 on solarcharger total yield and clarify MQTT Explorer docs (#431) ## Contributors @github-actions[bot], @tomer-w Co-authored-by: tomer-w <57483589+tomer-w@users.noreply.github.com>
1 parent 4a744ba commit 2c0af8a

9 files changed

Lines changed: 445 additions & 19 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Vendored third-party packages."""
22

3-
VICTRON_MQTT_VERSION = "2026.6.1"
3+
VICTRON_MQTT_VERSION = "2026.6.3"

custom_components/victron_mqtt/_vendor/victron_mqtt/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
DigitalInputInputState,
2020
DigitalInputState,
2121
DigitalInputType,
22+
DVCCMode,
2223
ErrorCode,
2324
ESSMode,
2425
ESSModeHub4,
@@ -74,6 +75,7 @@
7475
"DESSReactiveStrategy",
7576
"DESSRestrictions",
7677
"DESSStrategy",
78+
"DVCCMode",
7779
"Device",
7880
"DeviceType",
7981
"DigitalInputInputState",

custom_components/victron_mqtt/_vendor/victron_mqtt/_victron_enums.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class DeviceType(VictronDeviceEnum):
3333
SWITCH = ("switch", "switch", "Switch")
3434
GPS = ("gps", "gps", "GPS")
3535
SYSTEM_SETUP = ("SystemSetup", "system_setup", "System setup", "system") # Should be mapped to SYSTEM
36+
SERVICES = ("Services", "services", "<Not used>", "system") # Should be mapped to SYSTEM
3637
TRANSFER_SWITCH = ("TransferSwitch", "transfer_switch", "Transfer switch")
3738
DIGITAL_INPUT = ("digitalinput", "digital_input", "Digital input")
3839
DC_SYSTEM = ("dcsystem", "dc_system", "DC system")
@@ -53,6 +54,20 @@ class DeviceType(VictronDeviceEnum):
5354
DCDC = ("dcdc", "dcdc", "DC/DC charger") # Orion XS 1400 in battery to battery charging mode.
5455

5556

57+
class DVCCMode(VictronEnum):
58+
"""DVCC (Distributed Voltage and Current Control) mode.
59+
60+
Bit 0: DVCC enabled (0=off, 1=on)
61+
Bit 1: Forced by system/BMS (0=user-controllable, 1=forced)
62+
See https://github.com/victronenergy/dbus-systemcalc-py delegates/dvcc.py
63+
"""
64+
65+
OFF = (0, "off", "Off")
66+
ON = (1, "on", "On")
67+
FORCED_OFF = (2, "forced_off", "Forced off")
68+
FORCED_ON = (3, "forced_on", "Forced on")
69+
70+
5671
class GenericOnOff(VictronEnum):
5772
"""On/Off Enum"""
5873

custom_components/victron_mqtt/_vendor/victron_mqtt/_victron_formulas.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@
44

55
from typing import TYPE_CHECKING
66

7-
from ._victron_enums import ChargeSchedule, ESSModeHub4, ESSState, ESSUserMode, GenericOnOff, PreferRenewableEnergyEnum
7+
from ._victron_enums import (
8+
ChargeSchedule,
9+
DVCCMode,
10+
ESSModeHub4,
11+
ESSState,
12+
ESSUserMode,
13+
GenericOnOff,
14+
PreferRenewableEnergyEnum,
15+
)
816
from .constants import MetricType
917
from .data_classes import GpsLocation
1018
from .formula_common import left_riemann_sum_internal
@@ -93,6 +101,50 @@ def schedule_charge_enabled_set(
93101
return enabled, None
94102

95103

104+
def dvcc_enabled(
105+
depends_on: dict[str, Metric], _transient_state: FormulaTransientState | None
106+
) -> tuple[GenericOnOff | None, None]:
107+
"""Derive DVCC on/off from Settings/Services/Bol (bit 0 = enabled)."""
108+
metric = next(iter(depends_on.values()))
109+
if metric.value is None:
110+
return None, None
111+
code = metric.value.code if isinstance(metric.value, DVCCMode) else int(metric.value)
112+
return (GenericOnOff.ON if code & 1 else GenericOnOff.OFF), None
113+
114+
115+
def dvcc_enabled_set(
116+
value: str, depends_on: dict[str, Metric], _transient_state: FormulaTransientState | None
117+
) -> tuple[GenericOnOff, None]:
118+
"""Toggle DVCC by writing 0 or 1 to the hidden Bol SELECT.
119+
120+
When the system/BMS has forced DVCC (bit 1 set), the write is ignored
121+
and the current forced state is returned unchanged.
122+
"""
123+
enabled = value if isinstance(value, GenericOnOff) else GenericOnOff.from_id_or_string(value)
124+
assert enabled is not None, "Failed to determine DVCC enabled state"
125+
metric = next(iter(depends_on.values()))
126+
assert isinstance(metric, WritableMetric), "Expected WritableMetric for dvcc_enabled_set"
127+
# Do not override BMS/system-forced state (FORCED_OFF=2, FORCED_ON=3)
128+
current_code = metric.value.code if isinstance(metric.value, DVCCMode) else int(metric.value or 0)
129+
if current_code & 2:
130+
# Forced by system — return current state without writing
131+
return (GenericOnOff.ON if current_code & 1 else GenericOnOff.OFF), None
132+
metric.set(DVCCMode.ON if enabled == GenericOnOff.ON else DVCCMode.OFF)
133+
return enabled, None
134+
135+
136+
def dvcc_state(
137+
depends_on: dict[str, Metric], _transient_state: FormulaTransientState | None
138+
) -> tuple[DVCCMode | None, None]:
139+
"""Pass through the full DVCC state (Off, On, Forced off, Forced on)."""
140+
metric = next(iter(depends_on.values()))
141+
if metric.value is None:
142+
return None, None
143+
if isinstance(metric.value, DVCCMode):
144+
return metric.value, None
145+
return DVCCMode.from_code(int(metric.value)), None
146+
147+
96148
def ess_batterylife_state(
97149
depends_on: dict[str, Metric], _transient_state: FormulaTransientState | None
98150
) -> tuple[ESSState | None, None]:

custom_components/victron_mqtt/_vendor/victron_mqtt/_victron_topics.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DigitalInputInputState,
1919
DigitalInputState,
2020
DigitalInputType,
21+
DVCCMode,
2122
ErrorCode,
2223
ESSMode,
2324
ESSModeHub4,
@@ -276,6 +277,15 @@
276277
enum=GenericAlarmEnum,
277278
metric_type=MetricType.PROBLEM,
278279
),
280+
TopicDescriptor(
281+
topic="N/{installation_id}/battery/{device_id}/Alarms/HighCellVoltage",
282+
message_type=MetricKind.SENSOR,
283+
short_id="battery_high_cell_voltage",
284+
name="High cell voltage",
285+
value_type=ValueType.ENUM,
286+
enum=GenericAlarmEnum,
287+
metric_type=MetricType.PROBLEM,
288+
),
279289
TopicDescriptor(
280290
topic="N/{installation_id}/battery/{device_id}/Alarms/HighChargeCurrent",
281291
message_type=MetricKind.SENSOR,
@@ -302,6 +312,15 @@
302312
value_type=ValueType.ENUM,
303313
enum=GenericAlarmEnum,
304314
),
315+
TopicDescriptor(
316+
topic="N/{installation_id}/battery/{device_id}/Alarms/HighVoltage",
317+
message_type=MetricKind.SENSOR,
318+
short_id="battery_high_voltage",
319+
name="High voltage",
320+
value_type=ValueType.ENUM,
321+
enum=GenericAlarmEnum,
322+
metric_type=MetricType.PROBLEM,
323+
),
305324
TopicDescriptor(
306325
topic="N/{installation_id}/battery/{device_id}/Alarms/InternalFailure",
307326
message_type=MetricKind.SENSOR,
@@ -329,6 +348,24 @@
329348
enum=GenericAlarmEnum,
330349
metric_type=MetricType.PROBLEM,
331350
),
351+
TopicDescriptor(
352+
topic="N/{installation_id}/battery/{device_id}/Alarms/LowSoc",
353+
message_type=MetricKind.SENSOR,
354+
short_id="battery_low_soc",
355+
name="Low state of charge",
356+
value_type=ValueType.ENUM,
357+
enum=GenericAlarmEnum,
358+
metric_type=MetricType.PROBLEM,
359+
),
360+
TopicDescriptor(
361+
topic="N/{installation_id}/battery/{device_id}/Alarms/LowVoltage",
362+
message_type=MetricKind.SENSOR,
363+
short_id="battery_low_voltage",
364+
name="Low voltage",
365+
value_type=ValueType.ENUM,
366+
enum=GenericAlarmEnum,
367+
metric_type=MetricType.PROBLEM,
368+
),
332369
TopicDescriptor(
333370
topic="N/{installation_id}/battery/{device_id}/Capacity",
334371
message_type=MetricKind.SENSOR,
@@ -1564,21 +1601,21 @@
15641601
topic="N/{installation_id}/multi/{device_id}/Ac/In/1/{phase}/I",
15651602
message_type=MetricKind.SENSOR,
15661603
short_id="multi_acin_current_{phase}",
1567-
name="Current {phase}",
1604+
name="Input current on {phase}",
15681605
metric_type=MetricType.CURRENT,
15691606
),
15701607
TopicDescriptor(
15711608
topic="N/{installation_id}/multi/{device_id}/Ac/In/1/{phase}/P",
15721609
message_type=MetricKind.SENSOR,
15731610
short_id="multi_acin_power_{phase}",
1574-
name="Power on {phase}",
1611+
name="Input power on {phase}",
15751612
metric_type=MetricType.POWER,
15761613
),
15771614
TopicDescriptor(
15781615
topic="N/{installation_id}/multi/{device_id}/Ac/In/1/{phase}/V",
15791616
message_type=MetricKind.SENSOR,
15801617
short_id="multi_acin_voltage_{phase}",
1581-
name="Voltage on {phase}",
1618+
name="Input voltage on {phase}",
15821619
metric_type=MetricType.VOLTAGE,
15831620
),
15841621
TopicDescriptor(
@@ -2344,6 +2381,36 @@
23442381
name="Relay {relay} custom name",
23452382
value_type=ValueType.STRING,
23462383
),
2384+
# DVCC (Distributed Voltage and Current Control)
2385+
# Hidden SELECT receives the raw Settings/Services/Bol value (0-3).
2386+
# Two formula metrics expose it as a writable SWITCH (on/off) and a read-only SENSOR (all 4 states).
2387+
TopicDescriptor(
2388+
topic="N/{installation_id}/settings/{device_id}/Settings/Services/Bol",
2389+
message_type=MetricKind.SELECT,
2390+
short_id="system_dvcc_raw",
2391+
name="DVCC (raw)",
2392+
value_type=ValueType.ENUM,
2393+
enum=DVCCMode,
2394+
hidden=True,
2395+
),
2396+
TopicDescriptor(
2397+
topic="$$func/system/dvcc_enabled:dvcc_enabled_set",
2398+
depends_on=["system_dvcc_raw"],
2399+
message_type=MetricKind.SWITCH,
2400+
short_id="system_dvcc",
2401+
name="DVCC",
2402+
value_type=ValueType.ENUM,
2403+
enum=GenericOnOff,
2404+
),
2405+
TopicDescriptor(
2406+
topic="$$func/system/dvcc_state",
2407+
depends_on=["system_dvcc_raw"],
2408+
message_type=MetricKind.SENSOR,
2409+
short_id="system_dvcc_state",
2410+
name="DVCC state",
2411+
value_type=ValueType.ENUM,
2412+
enum=DVCCMode,
2413+
),
23472414
# System Setup topics
23482415
TopicDescriptor(
23492416
topic="N/{installation_id}/settings/{device_id}/Settings/SystemSetup/AcInput1",
@@ -2638,6 +2705,7 @@
26382705
short_id="solarcharger_yield_total",
26392706
name="Total yield",
26402707
metric_type=MetricType.ENERGY,
2708+
precision=2,
26412709
),
26422710
# Switch topics based on SwitchableOutput
26432711
TopicDescriptor(

custom_components/victron_mqtt/_vendor/victron_mqtt/metric.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,12 @@ def _handle_message(
271271
self._value = value
272272

273273
# In case of non-zero update frequency, respect the update frequency limit only for numerical values
274-
if not force and self._hub._update_frequency_seconds is not None and isinstance(value, float | int):
274+
if (
275+
not force
276+
and self._hub._update_frequency_seconds is not None
277+
and isinstance(value, float | int)
278+
and not isinstance(value, bool) # bool is a subclass of int, but we only want real numeric sensor values.
279+
):
275280
elapsed = now - self._last_notified
276281
if elapsed < self._hub._update_frequency_seconds:
277282
_LOGGER.debug(

custom_components/victron_mqtt/_vendor/victron_mqtt/writable_metric.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,18 @@ def _is_dynamic_dropdown(self) -> bool:
158158

159159
@property
160160
def metric_kind(self) -> MetricKind:
161-
"""Returns the metric kind, resolved dynamically when DYNAMIC."""
161+
"""Returns the metric kind, resolved dynamically when DYNAMIC.
162+
163+
DYNAMIC is used for SwitchableOutput State, which is always on/off (0/1)
164+
regardless of output type. For dropdown outputs (Type=6 with labels),
165+
it resolves to SELECT; otherwise it stays SWITCH.
166+
167+
Note: dimmable outputs (Type=2) have a separate Dimming topic that is
168+
hardcoded as MetricKind.NUMBER — State itself is still a switch.
169+
"""
162170
if self._descriptor.message_type == MetricKind.DYNAMIC:
163171
if self._is_dynamic_dropdown:
164172
return MetricKind.SELECT
165-
if self._output_type == SwitchableOutputType.DIMMABLE:
166-
return MetricKind.NUMBER
167173
return MetricKind.SWITCH
168174
return super().metric_kind
169175

0 commit comments

Comments
 (0)