victron_gx: Bump victron-mqtt to 2026.9.2 (#181452)

This commit is contained in:
Tomer
2026-09-06 20:11:49 +02:00
committed by GitHub
parent 5e93134436
commit 8827dbb286
10 changed files with 145 additions and 15 deletions
@@ -51,6 +51,8 @@ async def async_setup_entry(
class VictronButton(VictronBaseEntity, ButtonEntity):
"""Implementation of a Victron GX button entity."""
_follow_metric_availability = False
@callback
@override
def _on_update_cb(self, _value: Any) -> None:
@@ -1,12 +1,13 @@
"""Support for Victron GX device tracker."""
from typing import Any, override
from typing import TYPE_CHECKING, override
from victron_mqtt import (
Device as VictronVenusDevice,
GpsLocation,
Metric as VictronVenusMetric,
MetricKind,
MetricValue,
)
from homeassistant.components.device_tracker import TrackerEntity
@@ -63,11 +64,16 @@ class VictronDeviceTracker(VictronBaseEntity, TrackerEntity):
) -> None:
"""Initialize the device tracker."""
super().__init__(device, metric, device_info, installation_id)
self._update_from_location(metric.value)
value = metric.value
if TYPE_CHECKING:
assert value is None or isinstance(value, GpsLocation)
self._update_from_location(value)
@callback
@override
def _on_update_cb(self, value: Any) -> None:
def _on_update_cb(self, value: MetricValue) -> None:
if TYPE_CHECKING:
assert value is None or isinstance(value, GpsLocation)
self._update_from_location(value)
self.async_write_ha_state()
+13 -3
View File
@@ -1,12 +1,13 @@
"""Base entity for entities in victron_gx integration."""
from abc import abstractmethod
from typing import Any, override
from typing import override
from victron_mqtt import (
Device as VictronVenusDevice,
Metric as VictronVenusMetric,
MetricType,
MetricValue,
)
from homeassistant.const import EntityCategory
@@ -51,6 +52,7 @@ class VictronBaseEntity(Entity):
_attr_should_poll = False
_attr_has_entity_name = True
_follow_metric_availability = True
def __init__(
self,
@@ -62,6 +64,8 @@ class VictronBaseEntity(Entity):
"""Initialize the entity."""
self._device = device
self._metric = metric
if self._follow_metric_availability:
self._attr_available = metric.available
self._attr_device_info = device_info
self._attr_unique_id = f"{installation_id}_{metric.unique_id}"
self._attr_suggested_display_precision = metric.precision
@@ -111,11 +115,17 @@ class VictronBaseEntity(Entity):
@callback
@abstractmethod
def _on_update_cb(self, value: Any) -> None:
def _on_update_cb(self, value: MetricValue) -> None:
"""Handle the metric update. Must be implemented by subclasses."""
@callback
def _on_update(self, _: VictronVenusMetric, value: Any) -> None:
def _on_update(self, metric: VictronVenusMetric, value: MetricValue) -> None:
if self._follow_metric_availability and not metric.available:
if self._attr_available:
self._attr_available = False
self.async_write_ha_state()
return
self._attr_available = True
self._on_update_cb(value)
@override
@@ -7,7 +7,7 @@
"integration_type": "hub",
"iot_class": "local_push",
"quality_scale": "platinum",
"requirements": ["victron-mqtt==2026.8.4"],
"requirements": ["victron-mqtt==2026.9.2"],
"ssdp": [
{
"X_MqttOnLan": "1",
@@ -1,12 +1,13 @@
"""Support for Victron GX number entities."""
from typing import TYPE_CHECKING, Any, override
from typing import TYPE_CHECKING, override
from victron_mqtt import (
Device as VictronVenusDevice,
Metric as VictronVenusMetric,
MetricKind,
MetricType,
MetricValue,
WritableMetric as VictronVenusWritableMetric,
)
@@ -77,7 +78,10 @@ class VictronNumber(VictronBaseEntity, NumberEntity):
"""Initialize the number entity."""
super().__init__(device, metric, device_info, installation_id)
self._attr_device_class = METRIC_TYPE_TO_DEVICE_CLASS.get(metric.metric_type)
self._attr_native_value = metric.value
value = metric.value
if TYPE_CHECKING:
assert value is None or isinstance(value, int | float)
self._attr_native_value = value
if metric.min_value is not None:
self._attr_native_min_value = metric.min_value
if metric.max_value is not None:
@@ -93,7 +97,9 @@ class VictronNumber(VictronBaseEntity, NumberEntity):
@callback
@override
def _on_update_cb(self, value: Any) -> None:
def _on_update_cb(self, value: MetricValue) -> None:
if TYPE_CHECKING:
assert value is None or isinstance(value, int | float)
self._attr_native_value = value
self.async_write_ha_state()
@@ -338,6 +338,12 @@
"hub4_ac_grid_setpoint": {
"name": "AC grid setpoint"
},
"hub4_max_charge_power": {
"name": "Maximum charge power"
},
"hub4_max_discharge_power": {
"name": "Maximum discharge power"
},
"multi_ess_min_soc_limit": {
"name": "ESS minimum SoC limit"
},
@@ -347,6 +353,9 @@
"multiplus_assist_current_boost_factor": {
"name": "Assist current boost factor"
},
"pvinverter_power_limit": {
"name": "Power limit"
},
"solarcharger_charge_current_limit": {
"name": "[%key:component::victron_gx::common::charge_current_limit%]"
},
@@ -987,6 +996,18 @@
"dcload_voltage": {
"name": "[%key:component::victron_gx::common::voltage%]"
},
"dcsource_current": {
"name": "[%key:component::victron_gx::common::current%]"
},
"dcsource_power": {
"name": "[%key:component::victron_gx::common::power%]"
},
"dcsource_temperature": {
"name": "[%key:component::victron_gx::common::temperature%]"
},
"dcsource_voltage": {
"name": "[%key:component::victron_gx::common::voltage%]"
},
"dcsystem_aux_voltage": {
"name": "Auxiliary voltage"
},
@@ -1525,6 +1546,21 @@
"platform_venus_firmware_installed_version": {
"name": "Installed version"
},
"platform_venus_firmware_progress": {
"name": "Firmware update progress"
},
"platform_venus_firmware_state": {
"name": "Firmware update state",
"state": {
"checking": "Checking",
"downloading_and_installing": "Downloading and installing",
"error_during_check": "Error during check",
"error_during_update": "Error during update",
"idle": "[%key:common::state::idle%]",
"rebooting": "Rebooting",
"update_file_not_found": "Update file not found"
}
},
"pvinverter_current_phase": {
"name": "Current {phase}"
},
+10 -4
View File
@@ -2,12 +2,13 @@
from datetime import time
import logging
from typing import TYPE_CHECKING, Any, override
from typing import TYPE_CHECKING, override
from victron_mqtt import (
Device as VictronVenusDevice,
Metric as VictronVenusMetric,
MetricKind,
MetricValue,
WritableMetric as VictronVenusWritableMetric,
)
@@ -58,11 +59,16 @@ class VictronTime(VictronBaseEntity, TimeEntity):
) -> None:
"""Initialize the time entity."""
super().__init__(device, metric, device_info, installation_id)
self._attr_native_value = VictronTime.victron_time_to_time(metric.value)
value = metric.value
if TYPE_CHECKING:
assert value is None or isinstance(value, int | float)
self._attr_native_value = VictronTime.victron_time_to_time(value)
@callback
@override
def _on_update_cb(self, value: Any) -> None:
def _on_update_cb(self, value: MetricValue) -> None:
if TYPE_CHECKING:
assert value is None or isinstance(value, int | float)
self._attr_native_value = VictronTime.victron_time_to_time(value)
self.async_write_ha_state()
@@ -81,7 +87,7 @@ class VictronTime(VictronBaseEntity, TimeEntity):
self._metric.set(total_minutes)
@staticmethod
def victron_time_to_time(value: int | None) -> time | None:
def victron_time_to_time(value: float | None) -> time | None:
"""Convert minutes since midnight to time object."""
if value is None:
return None
+1 -1
View File
@@ -3382,7 +3382,7 @@ viaggiatreno_ha==0.2.4
victron-ble-ha-parser==0.7.0
# homeassistant.components.victron_gx
victron-mqtt==2026.8.4
victron-mqtt==2026.9.2
# homeassistant.components.victron_remote_monitoring
victron-vrm==0.1.12
@@ -1,10 +1,13 @@
"""Tests for Victron GX MQTT device trackers."""
from unittest.mock import MagicMock
from victron_mqtt import Hub as VictronVenusHub
from victron_mqtt.testing import finalize_injection, inject_message
from homeassistant.components.device_tracker import SourceType, TrackingType
from homeassistant.components.victron_gx.const import DOMAIN
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -122,6 +125,7 @@ async def test_victron_device_tracker(
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == STATE_UNKNOWN
assert state.attributes == {
"source_type": SourceType.GPS,
"altitude": None,
@@ -131,3 +135,12 @@ async def test_victron_device_tracker(
"in_zones": [],
"tracking_type": TrackingType.POSITION,
}
metric = victron_hub.devices["gps_0"].get_metric("gps_location")
assert metric is not None
metric._keepalive(force_invalidate=True, log_debug=MagicMock())
await hass.async_block_till_done()
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == STATE_UNAVAILABLE
@@ -1,10 +1,13 @@
"""Tests for Victron GX MQTT number entities."""
from unittest.mock import MagicMock
from victron_mqtt import Hub as VictronVenusHub
from victron_mqtt.testing import finalize_injection, inject_message
from homeassistant.components.number import NumberDeviceClass
from homeassistant.components.victron_gx.const import DOMAIN
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -98,6 +101,54 @@ async def test_victron_number_update(
assert state.state == "58.0"
async def test_nullable_number_availability(
hass: HomeAssistant,
init_integration: tuple[VictronVenusHub, MockConfigEntry],
entity_registry: er.EntityRegistry,
) -> None:
"""Test nullable values remain distinct from source unavailability."""
victron_hub, mock_config_entry = init_integration
topic = f"N/{MOCK_INSTALLATION_ID}/hub4/0/Overrides/MaxChargePower"
await inject_message(victron_hub, topic, '{"value": 1200.0}')
await finalize_injection(victron_hub, disconnect=False)
await hass.async_block_till_done()
entity = next(
entity
for entity in er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
if entity.translation_key == "hub4_max_charge_power"
)
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == "1200.0"
await inject_message(victron_hub, topic, '{"value": null}')
await hass.async_block_till_done()
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == STATE_UNKNOWN
metric = victron_hub.devices["hub4_0"].get_metric("hub4_max_charge_power")
assert metric is not None
metric._keepalive(force_invalidate=True, log_debug=MagicMock())
await hass.async_block_till_done()
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == STATE_UNAVAILABLE
await inject_message(victron_hub, topic, '{"value": 1300.0}')
await hass.async_block_till_done()
state = hass.states.get(entity.entity_id)
assert state is not None
assert state.state == "1300.0"
async def test_victron_number_actions(
hass: HomeAssistant,
init_integration: tuple[VictronVenusHub, MockConfigEntry],