Fix device identifier in ViCare integration (#124483)

* use correct serial

* add migration handler

* adjust init call

* add missing types

* adjust init call

* adjust init call

* adjust init call

* adjust init call

* Update types.py

* fix loop

* fix loop

* fix parameter order

* align parameter naming

* remove comment

* correct init

* update

* Update types.py

* correct merge

* revert type change

* add test case

* add helper

* add test case

* update snapshot

* add snapshot

* add device.serial data point

* fix device unique id

* update snapshot

* add comments

* update nmigration

* fix missing parameter

* move static parameters

* fix circuit access

* update device.serial

* update snapshots

* remove test case

* Update binary_sensor.py

* convert climate entity

* Update entity.py

* update snapshot

* use snake case

* add migration test

* enhance test case

* add test case

* Apply suggestions from code review

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
pull/125246/head
Christopher Fenner 2024-09-04 18:41:20 +02:00 committed by GitHub
parent 0fb1fbf0d1
commit 349ea35dc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 252 additions and 79 deletions

View File

@ -15,10 +15,12 @@ from PyViCare.PyViCareUtils import (
PyViCareInvalidCredentialsError,
)
from homeassistant.components.climate import DOMAIN as DOMAIN_CLIMATE
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.storage import STORAGE_DIR
from .const import (
@ -47,6 +49,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError) as err:
raise ConfigEntryAuthFailed("Authentication failed") from err
for device in hass.data[DOMAIN][entry.entry_id][DEVICE_LIST]:
# Migration can be removed in 2025.4.0
await async_migrate_devices(hass, entry, device)
# Migration can be removed in 2025.4.0
await async_migrate_entities(hass, entry, device)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@ -109,6 +117,72 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
async def async_migrate_devices(
hass: HomeAssistant, entry: ConfigEntry, device: ViCareDevice
) -> None:
"""Migrate old entry."""
registry = dr.async_get(hass)
gateway_serial: str = device.config.getConfig().serial
device_serial: str = device.api.getSerial()
old_identifier = gateway_serial
new_identifier = f"{gateway_serial}_{device_serial}"
# Migrate devices
for device_entry in dr.async_entries_for_config_entry(registry, entry.entry_id):
if device_entry.identifiers == {(DOMAIN, old_identifier)}:
_LOGGER.debug("Migrating device %s", device_entry.name)
registry.async_update_device(
device_entry.id,
serial_number=device_serial,
new_identifiers={(DOMAIN, new_identifier)},
)
async def async_migrate_entities(
hass: HomeAssistant, entry: ConfigEntry, device: ViCareDevice
) -> None:
"""Migrate old entry."""
gateway_serial: str = device.config.getConfig().serial
device_serial: str = device.api.getSerial()
new_identifier = f"{gateway_serial}_{device_serial}"
@callback
def _update_unique_id(
entity_entry: er.RegistryEntry,
) -> dict[str, str] | None:
"""Update unique ID of entity entry."""
if not entity_entry.unique_id.startswith(gateway_serial):
# belongs to other device/gateway
return None
if entity_entry.unique_id.startswith(f"{gateway_serial}_"):
# Already correct, nothing to do
return None
unique_id_parts = entity_entry.unique_id.split("-")
unique_id_parts[0] = new_identifier
# convert climate entity unique id from `<device_identifier>-<circuit_no>` to `<device_identifier>-heating-<circuit_no>`
if entity_entry.domain == DOMAIN_CLIMATE:
unique_id_parts[len(unique_id_parts) - 1] = (
f"{entity_entry.translation_key}-{unique_id_parts[len(unique_id_parts)-1]}"
)
entity_new_unique_id = "-".join(unique_id_parts)
_LOGGER.debug(
"Migrating entity %s from %s to new id %s",
entity_entry.entity_id,
entity_entry.unique_id,
entity_new_unique_id,
)
return {"new_unique_id": entity_new_unique_id}
# Migrate entities
await er.async_migrate_entries(hass, entry.entry_id, _update_unique_id)
def get_supported_devices(
devices: list[PyViCareDeviceConfig],
) -> list[PyViCareDeviceConfig]:

View File

@ -148,10 +148,10 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
circuit: PyViCareHeatingCircuit,
) -> None:
"""Initialize the climate device."""
super().__init__(circuit.id, device_config, device)
self._circuit = circuit
super().__init__(self._attr_translation_key, device_config, device, circuit)
self._device = device
self._attributes: dict[str, Any] = {}
self._attributes["vicare_programs"] = self._circuit.getPrograms()
self._attributes["vicare_programs"] = self._api.getPrograms()
self._attr_preset_modes = [
preset
for heating_program in self._attributes["vicare_programs"]
@ -163,11 +163,11 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
try:
_room_temperature = None
with suppress(PyViCareNotSupportedFeatureError):
_room_temperature = self._circuit.getRoomTemperature()
_room_temperature = self._api.getRoomTemperature()
_supply_temperature = None
with suppress(PyViCareNotSupportedFeatureError):
_supply_temperature = self._circuit.getSupplyTemperature()
_supply_temperature = self._api.getSupplyTemperature()
if _room_temperature is not None:
self._attr_current_temperature = _room_temperature
@ -177,15 +177,13 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
self._attr_current_temperature = None
with suppress(PyViCareNotSupportedFeatureError):
self._current_program = self._circuit.getActiveProgram()
self._current_program = self._api.getActiveProgram()
with suppress(PyViCareNotSupportedFeatureError):
self._attr_target_temperature = (
self._circuit.getCurrentDesiredTemperature()
)
self._attr_target_temperature = self._api.getCurrentDesiredTemperature()
with suppress(PyViCareNotSupportedFeatureError):
self._current_mode = self._circuit.getActiveMode()
self._current_mode = self._api.getActiveMode()
# Update the generic device attributes
self._attributes = {
@ -196,25 +194,25 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
with suppress(PyViCareNotSupportedFeatureError):
self._attributes["heating_curve_slope"] = (
self._circuit.getHeatingCurveSlope()
self._api.getHeatingCurveSlope()
)
with suppress(PyViCareNotSupportedFeatureError):
self._attributes["heating_curve_shift"] = (
self._circuit.getHeatingCurveShift()
self._api.getHeatingCurveShift()
)
with suppress(PyViCareNotSupportedFeatureError):
self._attributes["vicare_modes"] = self._circuit.getModes()
self._attributes["vicare_modes"] = self._api.getModes()
self._current_action = False
# Update the specific device attributes
with suppress(PyViCareNotSupportedFeatureError):
for burner in get_burners(self._api):
for burner in get_burners(self._device):
self._current_action = self._current_action or burner.getActive()
with suppress(PyViCareNotSupportedFeatureError):
for compressor in get_compressors(self._api):
for compressor in get_compressors(self._device):
self._current_action = (
self._current_action or compressor.getActive()
)
@ -245,9 +243,9 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
raise ValueError(f"Cannot set invalid hvac mode: {hvac_mode}")
_LOGGER.debug("Setting hvac mode to %s / %s", hvac_mode, vicare_mode)
self._circuit.setMode(vicare_mode)
self._api.setMode(vicare_mode)
def vicare_mode_from_hvac_mode(self, hvac_mode):
def vicare_mode_from_hvac_mode(self, hvac_mode) -> str | None:
"""Return the corresponding vicare mode for an hvac_mode."""
if "vicare_modes" not in self._attributes:
return None
@ -283,7 +281,7 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
def set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperatures."""
if (temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
self._circuit.setProgramTemperature(self._current_program, temp)
self._api.setProgramTemperature(self._current_program, temp)
self._attr_target_temperature = temp
@property
@ -312,7 +310,7 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
):
_LOGGER.debug("deactivating %s", self._current_program)
try:
self._circuit.deactivateProgram(self._current_program)
self._api.deactivateProgram(self._current_program)
except PyViCareCommandError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
@ -326,7 +324,7 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
if target_program in CHANGABLE_HEATING_PROGRAMS:
_LOGGER.debug("activating %s", target_program)
try:
self._circuit.activateProgram(target_program)
self._api.activateProgram(target_program)
except PyViCareCommandError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
@ -341,9 +339,9 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
"""Show Device Attributes."""
return self._attributes
def set_vicare_mode(self, vicare_mode):
def set_vicare_mode(self, vicare_mode) -> None:
"""Service function to set vicare modes directly."""
if vicare_mode not in self._attributes["vicare_modes"]:
raise ValueError(f"Cannot set invalid vicare mode: {vicare_mode}.")
self._circuit.setMode(vicare_mode)
self._api.setMode(vicare_mode)

View File

@ -25,18 +25,20 @@ class ViCareEntity(Entity):
component: PyViCareHeatingDeviceComponent | None = None,
) -> None:
"""Initialize the entity."""
gateway_serial = device_config.getConfig().serial
device_serial = device.getSerial()
identifier = f"{gateway_serial}_{device_serial}"
self._api: PyViCareDevice | PyViCareHeatingDeviceComponent = (
component if component else device
)
self._attr_unique_id = f"{device_config.getConfig().serial}-{unique_id_suffix}"
# valid for compressors, circuits, burners (HeatingDeviceWithComponent)
self._attr_unique_id = f"{identifier}-{unique_id_suffix}"
if component:
self._attr_unique_id += f"-{component.id}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device_config.getConfig().serial)},
serial_number=device.getSerial(),
identifiers={(DOMAIN, identifier)},
serial_number=device_serial,
name=device_config.getModel(),
manufacturer="Viessmann",
model=device_config.getModel(),

View File

@ -50,7 +50,7 @@
"properties": {
"value": {
"type": "string",
"value": "################"
"value": "deviceSerialViAir300F"
}
},
"timestamp": "2024-03-20T01:29:35.549Z",

View File

@ -11,10 +11,10 @@
"properties": {
"value": {
"type": "string",
"value": "################"
"value": "deviceSerialVitodens300W"
}
},
"timestamp": "2024-03-20T01:29:35.549Z",
"timestamp": "2024-07-30T20:03:40.073Z",
"uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.serial"
},
{

View File

@ -28,7 +28,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'burner',
'unique_id': 'gateway0-burner_active-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-burner_active-0',
'unit_of_measurement': None,
})
# ---
@ -75,7 +75,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'circulation_pump',
'unique_id': 'gateway0-circulationpump_active-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-circulationpump_active-0',
'unit_of_measurement': None,
})
# ---
@ -122,7 +122,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'circulation_pump',
'unique_id': 'gateway0-circulationpump_active-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-circulationpump_active-1',
'unit_of_measurement': None,
})
# ---
@ -169,7 +169,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'domestic_hot_water_charging',
'unique_id': 'gateway0-charging_active',
'unique_id': 'gateway0_deviceSerialVitodens300W-charging_active',
'unit_of_measurement': None,
})
# ---
@ -216,7 +216,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'domestic_hot_water_circulation_pump',
'unique_id': 'gateway0-dhw_circulationpump_active',
'unique_id': 'gateway0_deviceSerialVitodens300W-dhw_circulationpump_active',
'unit_of_measurement': None,
})
# ---
@ -263,7 +263,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'domestic_hot_water_pump',
'unique_id': 'gateway0-dhw_pump_active',
'unique_id': 'gateway0_deviceSerialVitodens300W-dhw_pump_active',
'unit_of_measurement': None,
})
# ---
@ -310,7 +310,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'frost_protection',
'unique_id': 'gateway0-frost_protection_active-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-frost_protection_active-0',
'unit_of_measurement': None,
})
# ---
@ -356,7 +356,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'frost_protection',
'unique_id': 'gateway0-frost_protection_active-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-frost_protection_active-1',
'unit_of_measurement': None,
})
# ---

View File

@ -28,7 +28,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'activate_onetimecharge',
'unique_id': 'gateway0-activate_onetimecharge',
'unique_id': 'gateway0_deviceSerialVitodens300W-activate_onetimecharge',
'unit_of_measurement': None,
})
# ---

View File

@ -40,7 +40,7 @@
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 401>,
'translation_key': 'heating',
'unique_id': 'gateway0-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating-0',
'unit_of_measurement': None,
})
# ---
@ -123,7 +123,7 @@
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 401>,
'translation_key': 'heating',
'unique_id': 'gateway0-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating-1',
'unit_of_measurement': None,
})
# ---

View File

@ -16,10 +16,10 @@
'properties': dict({
'value': dict({
'type': 'string',
'value': '################',
'value': 'deviceSerialVitodens300W',
}),
}),
'timestamp': '2024-03-20T01:29:35.549Z',
'timestamp': '2024-07-30T20:03:40.073Z',
'uri': 'https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.serial',
}),
dict({

View File

@ -35,7 +35,7 @@
'previous_unique_id': None,
'supported_features': <FanEntityFeature: 9>,
'translation_key': 'ventilation',
'unique_id': 'gateway0-ventilation',
'unique_id': 'gateway0_deviceSerialViAir300F-ventilation',
'unit_of_measurement': None,
})
# ---

View File

@ -33,7 +33,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'comfort_temperature',
'unique_id': 'gateway0-comfort_temperature-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-comfort_temperature-0',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -90,7 +90,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'comfort_temperature',
'unique_id': 'gateway0-comfort_temperature-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-comfort_temperature-1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -147,7 +147,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'heating_curve_shift',
'unique_id': 'gateway0-heating curve shift-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating curve shift-0',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -204,7 +204,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'heating_curve_shift',
'unique_id': 'gateway0-heating curve shift-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating curve shift-1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -261,7 +261,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'heating_curve_slope',
'unique_id': 'gateway0-heating curve slope-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating curve slope-0',
'unit_of_measurement': None,
})
# ---
@ -316,7 +316,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'heating_curve_slope',
'unique_id': 'gateway0-heating curve slope-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-heating curve slope-1',
'unit_of_measurement': None,
})
# ---
@ -371,7 +371,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'normal_temperature',
'unique_id': 'gateway0-normal_temperature-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-normal_temperature-0',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -428,7 +428,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'normal_temperature',
'unique_id': 'gateway0-normal_temperature-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-normal_temperature-1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -485,7 +485,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reduced_temperature',
'unique_id': 'gateway0-reduced_temperature-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-reduced_temperature-0',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -542,7 +542,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reduced_temperature',
'unique_id': 'gateway0-reduced_temperature-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-reduced_temperature-1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -599,7 +599,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'dhw_temperature',
'unique_id': 'gateway0-dhw_temperature',
'unique_id': 'gateway0_deviceSerialVitodens300W-dhw_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---

View File

@ -30,7 +30,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'boiler_temperature',
'unique_id': 'gateway0-boiler_temperature',
'unique_id': 'gateway0_deviceSerialVitodens300W-boiler_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -81,7 +81,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'burner_hours',
'unique_id': 'gateway0-burner_hours-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-burner_hours-0',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
})
# ---
@ -131,7 +131,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'burner_modulation',
'unique_id': 'gateway0-burner_modulation-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-burner_modulation-0',
'unit_of_measurement': '%',
})
# ---
@ -181,7 +181,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'burner_starts',
'unique_id': 'gateway0-burner_starts-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-burner_starts-0',
'unit_of_measurement': None,
})
# ---
@ -230,7 +230,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_gas_consumption_heating_this_month',
'unique_id': 'gateway0-hotwater_gas_consumption_heating_this_month',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_gas_consumption_heating_this_month',
'unit_of_measurement': None,
})
# ---
@ -279,7 +279,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_gas_consumption_heating_this_week',
'unique_id': 'gateway0-hotwater_gas_consumption_heating_this_week',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_gas_consumption_heating_this_week',
'unit_of_measurement': None,
})
# ---
@ -328,7 +328,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_gas_consumption_heating_this_year',
'unique_id': 'gateway0-hotwater_gas_consumption_heating_this_year',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_gas_consumption_heating_this_year',
'unit_of_measurement': None,
})
# ---
@ -377,7 +377,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_gas_consumption_today',
'unique_id': 'gateway0-hotwater_gas_consumption_today',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_gas_consumption_today',
'unit_of_measurement': None,
})
# ---
@ -426,7 +426,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_max_temperature',
'unique_id': 'gateway0-hotwater_max_temperature',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_max_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -477,7 +477,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'hotwater_min_temperature',
'unique_id': 'gateway0-hotwater_min_temperature',
'unique_id': 'gateway0_deviceSerialVitodens300W-hotwater_min_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -528,7 +528,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power consumption this month',
'unique_id': 'gateway0-power consumption this month',
'unique_id': 'gateway0_deviceSerialVitodens300W-power consumption this month',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
@ -579,7 +579,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power_consumption_this_year',
'unique_id': 'gateway0-power consumption this year',
'unique_id': 'gateway0_deviceSerialVitodens300W-power consumption this year',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
@ -630,7 +630,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power_consumption_today',
'unique_id': 'gateway0-power consumption today',
'unique_id': 'gateway0_deviceSerialVitodens300W-power consumption today',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
@ -681,7 +681,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'gas_consumption_heating_this_month',
'unique_id': 'gateway0-gas_consumption_heating_this_month',
'unique_id': 'gateway0_deviceSerialVitodens300W-gas_consumption_heating_this_month',
'unit_of_measurement': None,
})
# ---
@ -730,7 +730,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'gas_consumption_heating_this_week',
'unique_id': 'gateway0-gas_consumption_heating_this_week',
'unique_id': 'gateway0_deviceSerialVitodens300W-gas_consumption_heating_this_week',
'unit_of_measurement': None,
})
# ---
@ -779,7 +779,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'gas_consumption_heating_this_year',
'unique_id': 'gateway0-gas_consumption_heating_this_year',
'unique_id': 'gateway0_deviceSerialVitodens300W-gas_consumption_heating_this_year',
'unit_of_measurement': None,
})
# ---
@ -828,7 +828,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'gas_consumption_heating_today',
'unique_id': 'gateway0-gas_consumption_heating_today',
'unique_id': 'gateway0_deviceSerialVitodens300W-gas_consumption_heating_today',
'unit_of_measurement': None,
})
# ---
@ -877,7 +877,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'outside_temperature',
'unique_id': 'gateway0-outside_temperature',
'unique_id': 'gateway0_deviceSerialVitodens300W-outside_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -928,7 +928,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power_consumption_this_week',
'unique_id': 'gateway0-power consumption this week',
'unique_id': 'gateway0_deviceSerialVitodens300W-power consumption this week',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
@ -979,7 +979,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'supply_temperature',
'unique_id': 'gateway0-supply_temperature-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-supply_temperature-0',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -1030,7 +1030,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'supply_temperature',
'unique_id': 'gateway0-supply_temperature-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-supply_temperature-1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---

View File

@ -31,7 +31,7 @@
'previous_unique_id': None,
'supported_features': <WaterHeaterEntityFeature: 1>,
'translation_key': 'domestic_hot_water',
'unique_id': 'gateway0-0',
'unique_id': 'gateway0_deviceSerialVitodens300W-0',
'unit_of_measurement': None,
})
# ---
@ -87,7 +87,7 @@
'previous_unique_id': None,
'supported_features': <WaterHeaterEntityFeature: 1>,
'translation_key': 'domestic_hot_water',
'unique_id': 'gateway0-1',
'unique_id': 'gateway0_deviceSerialVitodens300W-1',
'unit_of_measurement': None,
})
# ---

View File

@ -0,0 +1,99 @@
"""Test ViCare migration."""
from unittest.mock import patch
from homeassistant.components.vicare.const import DOMAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import MODULE
from .conftest import Fixture, MockPyViCare
from tests.common import MockConfigEntry
# Device migration test can be removed in 2025.4.0
async def test_device_migration(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that the device registry is updated correctly."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]),
):
mock_config_entry.add_to_hass(hass)
device_registry.async_get_or_create(
config_entry_id=mock_config_entry.entry_id,
identifiers={
(DOMAIN, "gateway0"),
},
)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert device_registry.async_get_device(identifiers={(DOMAIN, "gateway0")}) is None
assert (
device_registry.async_get_device(
identifiers={(DOMAIN, "gateway0_deviceSerialVitodens300W")}
)
is not None
)
# Entity migration test can be removed in 2025.4.0
async def test_climate_entity_migration(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that the climate entity unique_id gets migrated correctly."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]),
):
mock_config_entry.add_to_hass(hass)
entry1 = entity_registry.async_get_or_create(
domain=Platform.CLIMATE,
platform=DOMAIN,
config_entry=mock_config_entry,
unique_id="gateway0-0",
translation_key="heating",
)
entry2 = entity_registry.async_get_or_create(
domain=Platform.CLIMATE,
platform=DOMAIN,
config_entry=mock_config_entry,
unique_id="gateway0_deviceSerialVitodens300W-heating-1",
translation_key="heating",
)
entry3 = entity_registry.async_get_or_create(
domain=Platform.CLIMATE,
platform=DOMAIN,
config_entry=mock_config_entry,
unique_id="gateway1-0",
translation_key="heating",
)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert (
entity_registry.async_get(entry1.entity_id).unique_id
== "gateway0_deviceSerialVitodens300W-heating-0"
)
assert (
entity_registry.async_get(entry2.entity_id).unique_id
== "gateway0_deviceSerialVitodens300W-heating-1"
)
assert entity_registry.async_get(entry3.entity_id).unique_id == "gateway1-0"