Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
This commit is contained in:
Daniel Hjelseth Høyer
2026-01-14 14:36:43 +01:00
parent a25fbf57ef
commit b8dfc523da
8 changed files with 22 additions and 62 deletions

View File

@@ -8,5 +8,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["homevolt==0.2.3"]
"requirements": ["homevolt==0.2.4"]
}

View File

@@ -36,7 +36,7 @@ rules:
docs-installation-parameters: todo
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
log-when-unavailable: todo
parallel-updates: done
reauthentication-flow: todo
test-coverage: todo

View File

@@ -2,10 +2,8 @@
from __future__ import annotations
import logging
from homevolt.models import SensorType
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
@@ -31,8 +29,6 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER, HomevoltConfigEntry
from .coordinator import HomevoltDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0 # Coordinator-based updates
SENSORS: tuple[SensorEntityDescription, ...] = (
@@ -115,14 +111,12 @@ async def async_setup_entry(
sensors_by_key = {sensor.key: sensor for sensor in SENSORS}
for sensor_key, sensor in coordinator.data.sensors.items():
if (description := sensors_by_key.get(sensor.type)) is None:
_LOGGER.error("Sensor %s not found", sensor.type.name)
continue
entities.append(
HomevoltSensor(
description,
coordinator,
sensor_key,
)
)
async_add_entities(entities)
@@ -145,22 +139,25 @@ class HomevoltSensor(CoordinatorEntity[HomevoltDataUpdateCoordinator], SensorEnt
device_id = coordinator.data.device_id
self._attr_unique_id = f"{device_id}_{sensor_key}"
sensor_data = coordinator.data.sensors[sensor_key]
self._attr_translation_key = sensor_data.slug
self._sensor_key = sensor_key
device_metadata = coordinator.data.device_metadata.get(
sensor_data.device_identifier
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{device_id}_{sensor_data.device_identifier}")},
configuration_url=coordinator.client.hostname,
configuration_url=coordinator.client.base_url,
manufacturer=MANUFACTURER,
model=device_metadata.model if device_metadata else None,
name=device_metadata.name if device_metadata else None,
)
@property
def available(self) -> bool:
"""Return if entity is available."""
return super().available and self._sensor_key in self.coordinator.data.sensors
@property
def native_value(self) -> StateType:
"""Return the native value of the sensor."""
sensor_data = self.coordinator.data.sensors[self._sensor_key]
if sensor_data is None:
return None
return sensor_data.value
return self.coordinator.data.sensors[self._sensor_key].value

View File

@@ -151,6 +151,9 @@
"l3_voltage_load": {
"name": "Load L3 voltage"
},
"power": {
"name": "Power"
},
"power_grid": {
"name": "Grid power"
},
@@ -178,6 +181,9 @@
"schedule_type": {
"name": "Schedule type"
},
"state_of_charge": {
"name": "State of charge"
},
"system_temperature": {
"name": "System temperature"
},

2
requirements_all.txt generated
View File

@@ -1225,7 +1225,7 @@ homelink-integration-api==0.0.1
homematicip==2.4.0
# homeassistant.components.homevolt
homevolt==0.2.3
homevolt==0.2.4
# homeassistant.components.horizon
horimote==0.4.1

View File

@@ -1083,7 +1083,7 @@ homelink-integration-api==0.0.1
homematicip==2.4.0
# homeassistant.components.homevolt
homevolt==0.2.3
homevolt==0.2.4
# homeassistant.components.remember_the_milk
httplib2==0.20.4

View File

@@ -51,7 +51,7 @@ def mock_homevolt_client() -> Generator[MagicMock]:
),
):
client = homevolt_mock.return_value
client.hostname = "http://127.0.0.1"
client.base_url = "http://127.0.0.1"
client.update_info = AsyncMock()
client.close_connection = AsyncMock()
@@ -63,16 +63,19 @@ def mock_homevolt_client() -> Generator[MagicMock]:
value=234.5,
type=SensorType.VOLTAGE,
device_identifier="ems_40580137858664",
slug="l1_voltage",
),
"Battery State of Charge": Sensor(
value=80.6,
type=SensorType.PERCENTAGE,
device_identifier="ems_40580137858664",
slug="battery_state_of_charge",
),
"Power": Sensor(
value=-12,
type=SensorType.POWER,
device_identifier="ems_40580137858664",
slug="power",
),
}
device.device_metadata = {

View File

@@ -100,7 +100,6 @@ async def test_step_user_errors(
assert result["step_id"] == "user"
assert result["errors"] == {"base": expected_error}
# Clear the error and complete the flow successfully
with (
patch(
"homeassistant.components.homevolt.config_flow.Homevolt.update_info",
@@ -169,48 +168,3 @@ async def test_duplicate_entry(
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_duplicate_entry_no_device_id(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
) -> None:
"""Test that a duplicate host aborts the flow when device_id is None."""
existing_entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: "192.168.1.100", CONF_PASSWORD: "test-password"},
)
existing_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
user_input = {
CONF_HOST: "192.168.1.100",
CONF_PASSWORD: "test-password",
}
with (
patch(
"homeassistant.components.homevolt.config_flow.Homevolt.update_info",
new_callable=AsyncMock,
),
patch(
"homeassistant.components.homevolt.config_flow.Homevolt.get_device",
) as mock_get_device,
):
mock_device = MagicMock()
mock_device.device_id = None
mock_get_device.return_value = mock_device
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"