Fix unique_id not being unique in HomeWizard (#117940)

This commit is contained in:
Duco Sebel 2024-05-27 11:23:10 +02:00 committed by GitHub
parent 6b8223e339
commit 22cc7d34d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 3920 additions and 34 deletions

View file

@ -7,6 +7,6 @@
"iot_class": "local_polling",
"loggers": ["homewizard_energy"],
"quality_scale": "platinum",
"requirements": ["python-homewizard-energy==v5.0.0"],
"requirements": ["python-homewizard-energy==v6.0.0"],
"zeroconf": ["_hwenergy._tcp.local."]
}

View file

@ -625,6 +625,8 @@ async def async_setup_entry(
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
# Migrate original gas meter sensor to ExternalDevice
# This is sensor that was directly linked to the P1 Meter
# Migration can be removed after 2024.8.0
ent_reg = er.async_get(hass)
if (
@ -634,7 +636,7 @@ async def async_setup_entry(
) and coordinator.data.data.gas_unique_id is not None:
ent_reg.async_update_entity(
entity_id,
new_unique_id=f"{DOMAIN}_{coordinator.data.data.gas_unique_id}",
new_unique_id=f"{DOMAIN}_gas_meter_{coordinator.data.data.gas_unique_id}",
)
# Remove old gas_unique_id sensor
@ -654,6 +656,18 @@ async def async_setup_entry(
if coordinator.data.data.external_devices is not None:
for unique_id, device in coordinator.data.data.external_devices.items():
if description := EXTERNAL_SENSORS.get(device.meter_type):
# Migrate external devices to new unique_id
# This is to ensure that devices with same id but different type are unique
# Migration can be removed after 2024.11.0
if entity_id := ent_reg.async_get_entity_id(
Platform.SENSOR, DOMAIN, f"{DOMAIN}_{device.unique_id}"
):
ent_reg.async_update_entity(
entity_id,
new_unique_id=f"{DOMAIN}_{unique_id}",
)
# Add external device
entities.append(
HomeWizardExternalSensorEntity(coordinator, description, unique_id)
)

View file

@ -2251,7 +2251,7 @@ python-gitlab==1.6.0
python-homeassistant-analytics==0.6.0
# homeassistant.components.homewizard
python-homewizard-energy==v5.0.0
python-homewizard-energy==v6.0.0
# homeassistant.components.hp_ilo
python-hpilo==4.4.3

View file

@ -1754,7 +1754,7 @@ python-fullykiosk==0.0.12
python-homeassistant-analytics==0.6.0
# homeassistant.components.homewizard
python-homewizard-energy==v5.0.0
python-homewizard-energy==v6.0.0
# homeassistant.components.izone
python-izone==1.2.9

View file

@ -0,0 +1,82 @@
{
"wifi_ssid": "My Wi-Fi",
"wifi_strength": 100,
"smr_version": 50,
"meter_model": "ISKRA 2M550T-101",
"unique_id": "00112233445566778899AABBCCDDEEFF",
"active_tariff": 2,
"total_power_import_kwh": 13779.338,
"total_power_import_t1_kwh": 10830.511,
"total_power_import_t2_kwh": 2948.827,
"total_power_import_t3_kwh": 2948.827,
"total_power_import_t4_kwh": 2948.827,
"total_power_export_kwh": 13086.777,
"total_power_export_t1_kwh": 4321.333,
"total_power_export_t2_kwh": 8765.444,
"total_power_export_t3_kwh": 8765.444,
"total_power_export_t4_kwh": 8765.444,
"active_power_w": -123,
"active_power_l1_w": -123,
"active_power_l2_w": 456,
"active_power_l3_w": 123.456,
"active_voltage_l1_v": 230.111,
"active_voltage_l2_v": 230.222,
"active_voltage_l3_v": 230.333,
"active_current_l1_a": -4,
"active_current_l2_a": 2,
"active_current_l3_a": 0,
"active_frequency_hz": 50,
"voltage_sag_l1_count": 1,
"voltage_sag_l2_count": 2,
"voltage_sag_l3_count": 3,
"voltage_swell_l1_count": 4,
"voltage_swell_l2_count": 5,
"voltage_swell_l3_count": 6,
"any_power_fail_count": 4,
"long_power_fail_count": 5,
"total_gas_m3": 1122.333,
"gas_timestamp": 210314112233,
"gas_unique_id": "00000000000000000000000000000000",
"active_power_average_w": 123.0,
"montly_power_peak_w": 1111.0,
"montly_power_peak_timestamp": 230101080010,
"active_liter_lpm": 12.345,
"total_liter_m3": 1234.567,
"external": [
{
"unique_id": "00000000000000000000000000000000",
"type": "gas_meter",
"timestamp": 230125220957,
"value": 111.111,
"unit": "m3"
},
{
"unique_id": "00000000000000000000000000000000",
"type": "water_meter",
"timestamp": 230125220957,
"value": 222.222,
"unit": "m3"
},
{
"unique_id": "00000000000000000000000000000000",
"type": "warm_water_meter",
"timestamp": 230125220957,
"value": 333.333,
"unit": "m3"
},
{
"unique_id": "00000000000000000000000000000000",
"type": "heat_meter",
"timestamp": 230125220957,
"value": 444.444,
"unit": "GJ"
},
{
"unique_id": "00000000000000000000000000000000",
"type": "inlet_heat_meter",
"timestamp": 230125220957,
"value": 555.555,
"unit": "m3"
}
]
}

View file

@ -0,0 +1,7 @@
{
"product_type": "HWE-P1",
"product_name": "P1 meter",
"serial": "3c39e7aabbcc",
"firmware_version": "4.19",
"api_version": "v1"
}

View file

@ -0,0 +1,3 @@
{
"cloud_enabled": true
}

View file

@ -199,7 +199,7 @@
'active_voltage_v': None,
'any_power_fail_count': 4,
'external_devices': dict({
'G001': dict({
'gas_meter_G001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.GAS_METER: 3>',
@ -209,7 +209,7 @@
'unit': 'm3',
'value': 111.111,
}),
'H001': dict({
'heat_meter_H001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.HEAT_METER: 4>',
@ -219,7 +219,7 @@
'unit': 'GJ',
'value': 444.444,
}),
'IH001': dict({
'inlet_heat_meter_IH001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.INLET_HEAT_METER: 12>',
@ -229,17 +229,7 @@
'unit': 'm3',
'value': 555.555,
}),
'W001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.WATER_METER: 7>',
}),
'timestamp': '2023-01-25T22:09:57',
'unique_id': '**REDACTED**',
'unit': 'm3',
'value': 222.222,
}),
'WW001': dict({
'warm_water_meter_WW001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.WARM_WATER_METER: 6>',
@ -249,6 +239,16 @@
'unit': 'm3',
'value': 333.333,
}),
'water_meter_W001': dict({
'meter_type': dict({
'__type': "<enum 'DeviceType'>",
'repr': '<DeviceType.WATER_METER: 7>',
}),
'timestamp': '2023-01-25T22:09:57',
'unique_id': '**REDACTED**',
'unit': 'm3',
'value': 222.222,
}),
}),
'gas_timestamp': '2021-03-14T11:22:33',
'gas_unique_id': '**REDACTED**',

File diff suppressed because it is too large Load diff

View file

@ -241,3 +241,62 @@ async def test_sensor_migration_does_not_trigger(
assert entity
assert entity.unique_id == new_unique_id
assert entity.previous_unique_id is None
@pytest.mark.parametrize(
("device_fixture", "old_unique_id", "new_unique_id"),
[
(
"HWE-P1",
"homewizard_G001",
"homewizard_gas_meter_G001",
),
(
"HWE-P1",
"homewizard_W001",
"homewizard_water_meter_W001",
),
(
"HWE-P1",
"homewizard_WW001",
"homewizard_warm_water_meter_WW001",
),
(
"HWE-P1",
"homewizard_H001",
"homewizard_heat_meter_H001",
),
(
"HWE-P1",
"homewizard_IH001",
"homewizard_inlet_heat_meter_IH001",
),
],
)
@pytest.mark.usefixtures("mock_homewizardenergy")
async def test_external_sensor_migration(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_config_entry: MockConfigEntry,
old_unique_id: str,
new_unique_id: str,
) -> None:
"""Test unique ID or External sensors are migrated."""
mock_config_entry.add_to_hass(hass)
entity: er.RegistryEntry = entity_registry.async_get_or_create(
domain=Platform.SENSOR,
platform=DOMAIN,
unique_id=old_unique_id,
config_entry=mock_config_entry,
)
assert entity.unique_id == old_unique_id
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
entity_migrated = entity_registry.async_get(entity.entity_id)
assert entity_migrated
assert entity_migrated.unique_id == new_unique_id
assert entity_migrated.previous_unique_id == old_unique_id

View file

@ -244,6 +244,55 @@ pytestmark = [
"sensor.device_wi_fi_strength",
],
),
(
"HWE-P1-invalid-EAN",
[
"sensor.device_average_demand",
"sensor.device_current_phase_1",
"sensor.device_current_phase_2",
"sensor.device_current_phase_3",
"sensor.device_dsmr_version",
"sensor.device_energy_export_tariff_1",
"sensor.device_energy_export_tariff_2",
"sensor.device_energy_export_tariff_3",
"sensor.device_energy_export_tariff_4",
"sensor.device_energy_export",
"sensor.device_energy_import_tariff_1",
"sensor.device_energy_import_tariff_2",
"sensor.device_energy_import_tariff_3",
"sensor.device_energy_import_tariff_4",
"sensor.device_energy_import",
"sensor.device_frequency",
"sensor.device_long_power_failures_detected",
"sensor.device_peak_demand_current_month",
"sensor.device_power_failures_detected",
"sensor.device_power_phase_1",
"sensor.device_power_phase_2",
"sensor.device_power_phase_3",
"sensor.device_power",
"sensor.device_smart_meter_identifier",
"sensor.device_smart_meter_model",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
"sensor.device_voltage_sags_detected_phase_1",
"sensor.device_voltage_sags_detected_phase_2",
"sensor.device_voltage_sags_detected_phase_3",
"sensor.device_voltage_swells_detected_phase_1",
"sensor.device_voltage_swells_detected_phase_2",
"sensor.device_voltage_swells_detected_phase_3",
"sensor.device_water_usage",
"sensor.device_wi_fi_ssid",
"sensor.device_wi_fi_strength",
"sensor.gas_meter_gas",
"sensor.heat_meter_energy",
"sensor.inlet_heat_meter_none",
"sensor.warm_water_meter_water",
"sensor.water_meter_water",
],
),
],
)
async def test_sensors(