Remove redundant parameter from config_entry data of LCN integration (#135912)

This commit is contained in:
Andre Lengwenus 2025-04-22 18:00:30 +02:00 committed by GitHub
parent e9789e0b3e
commit e56f6fafdc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 375 additions and 89 deletions

View File

@ -24,12 +24,17 @@ from homeassistant.const import (
CONF_IP_ADDRESS,
CONF_PASSWORD,
CONF_PORT,
CONF_RESOURCE,
CONF_USERNAME,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.typing import ConfigType
from .const import (
@ -38,6 +43,7 @@ from .const import (
CONF_DIM_MODE,
CONF_DOMAIN_DATA,
CONF_SK_NUM_TRIES,
CONF_TARGET_VALUE_LOCKED,
CONF_TRANSITION,
CONNECTION,
DEVICE_CONNECTIONS,
@ -155,6 +161,7 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
if config_entry.minor_version < 2:
new_data[CONF_ACKNOWLEDGE] = False
if config_entry.version < 2:
# update to 2.1 (fix transitions for lights and switches)
new_entities_data = [*new_data[CONF_ENTITIES]]
for entity in new_entities_data:
@ -164,8 +171,19 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
entity[CONF_DOMAIN_DATA][CONF_TRANSITION] /= 1000.0
new_data[CONF_ENTITIES] = new_entities_data
if config_entry.version < 3:
# update to 3.1 (remove resource parameter, add climate target lock value parameter)
for entity in new_data[CONF_ENTITIES]:
entity.pop(CONF_RESOURCE, None)
if entity[CONF_DOMAIN] == Platform.CLIMATE:
entity[CONF_DOMAIN_DATA].setdefault(CONF_TARGET_VALUE_LOCKED, -1)
# migrate climate and scene unique ids
await async_migrate_entities(hass, config_entry)
hass.config_entries.async_update_entry(
config_entry, data=new_data, minor_version=1, version=2
config_entry, data=new_data, minor_version=1, version=3
)
_LOGGER.debug(
@ -176,6 +194,29 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return True
async def async_migrate_entities(
hass: HomeAssistant, config_entry: ConfigEntry
) -> None:
"""Migrate entity registry."""
@callback
def update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
"""Update unique ID of entity entry."""
# fix unique entity ids for climate and scene
if "." in entity_entry.unique_id:
if entity_entry.domain == Platform.CLIMATE:
setpoint = entity_entry.unique_id.split(".")[-1]
return {
"new_unique_id": entity_entry.unique_id.rsplit("-", 1)[0]
+ f"-{setpoint}"
}
if entity_entry.domain == Platform.SCENE:
return {"new_unique_id": entity_entry.unique_id.replace(".", "")}
return None
await er.async_migrate_entries(hass, config_entry.entry_id, update_unique_id)
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Close connection to PCHK host represented by config_entry."""
# forward unloading to platforms

View File

@ -110,7 +110,7 @@ async def validate_connection(data: ConfigType) -> str | None:
class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a LCN config flow."""
VERSION = 2
VERSION = 3
MINOR_VERSION = 1
async def async_step_user(

View File

@ -3,18 +3,19 @@
from collections.abc import Callable
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_RESOURCE
from homeassistant.const import CONF_ADDRESS, CONF_DOMAIN, CONF_NAME
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .const import CONF_DOMAIN_DATA, DOMAIN
from .helpers import (
AddressType,
DeviceConnectionType,
InputType,
generate_unique_id,
get_device_connection,
get_resource,
)
@ -48,7 +49,11 @@ class LcnEntity(Entity):
def unique_id(self) -> str:
"""Return a unique ID."""
return generate_unique_id(
self.config_entry.entry_id, self.address, self.config[CONF_RESOURCE]
self.config_entry.entry_id,
self.address,
get_resource(
self.config[CONF_DOMAIN], self.config[CONF_DOMAIN_DATA]
).lower(),
)
async def async_added_to_hass(self) -> None:

View File

@ -19,7 +19,6 @@ from homeassistant.const import (
CONF_ENTITIES,
CONF_LIGHTS,
CONF_NAME,
CONF_RESOURCE,
CONF_SENSORS,
CONF_SWITCHES,
)
@ -29,6 +28,7 @@ from homeassistant.helpers.typing import ConfigType
from .const import (
CONF_CLIMATES,
CONF_DOMAIN_DATA,
CONF_HARDWARE_SERIAL,
CONF_HARDWARE_TYPE,
CONF_SCENES,
@ -79,9 +79,9 @@ def get_resource(domain_name: str, domain_data: ConfigType) -> str:
if domain_name == "cover":
return cast(str, domain_data["motor"])
if domain_name == "climate":
return f"{domain_data['source']}.{domain_data['setpoint']}"
return cast(str, domain_data["setpoint"])
if domain_name == "scene":
return f"{domain_data['register']}.{domain_data['scene']}"
return f"{domain_data['register']}{domain_data['scene']}"
raise ValueError("Unknown domain")
@ -115,7 +115,9 @@ def purge_entity_registry(
references_entry_data = set()
for entity_data in imported_entry_data[CONF_ENTITIES]:
entity_unique_id = generate_unique_id(
entry_id, entity_data[CONF_ADDRESS], entity_data[CONF_RESOURCE]
entry_id,
entity_data[CONF_ADDRESS],
get_resource(entity_data[CONF_DOMAIN], entity_data[CONF_DOMAIN_DATA]),
)
entity_id = entity_registry.async_get_entity_id(
entity_data[CONF_DOMAIN], DOMAIN, entity_unique_id

View File

@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/lcn",
"iot_class": "local_push",
"loggers": ["pypck"],
"requirements": ["pypck==0.8.5", "lcn-frontend==0.2.3"]
"requirements": ["pypck==0.8.5", "lcn-frontend==0.2.4"]
}

View File

@ -19,7 +19,6 @@ from homeassistant.const import (
CONF_DOMAIN,
CONF_ENTITIES,
CONF_NAME,
CONF_RESOURCE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import (
@ -343,7 +342,6 @@ async def websocket_add_entity(
entity_config = {
CONF_ADDRESS: msg[CONF_ADDRESS],
CONF_NAME: msg[CONF_NAME],
CONF_RESOURCE: resource,
CONF_DOMAIN: domain_name,
CONF_DOMAIN_DATA: domain_data,
}
@ -371,7 +369,15 @@ async def websocket_add_entity(
vol.Required("entry_id"): cv.string,
vol.Required(CONF_ADDRESS): ADDRESS_SCHEMA,
vol.Required(CONF_DOMAIN): cv.string,
vol.Required(CONF_RESOURCE): cv.string,
vol.Required(CONF_DOMAIN_DATA): vol.Any(
DOMAIN_DATA_BINARY_SENSOR,
DOMAIN_DATA_SENSOR,
DOMAIN_DATA_SWITCH,
DOMAIN_DATA_LIGHT,
DOMAIN_DATA_CLIMATE,
DOMAIN_DATA_COVER,
DOMAIN_DATA_SCENE,
),
}
)
@websocket_api.async_response
@ -390,7 +396,10 @@ async def websocket_delete_entity(
if (
tuple(entity_config[CONF_ADDRESS]) == msg[CONF_ADDRESS]
and entity_config[CONF_DOMAIN] == msg[CONF_DOMAIN]
and entity_config[CONF_RESOURCE] == msg[CONF_RESOURCE]
and get_resource(
entity_config[CONF_DOMAIN], entity_config[CONF_DOMAIN_DATA]
)
== get_resource(msg[CONF_DOMAIN], msg[CONF_DOMAIN_DATA])
)
),
None,

2
requirements_all.txt generated
View File

@ -1308,7 +1308,7 @@ lakeside==0.13
laundrify-aio==1.2.2
# homeassistant.components.lcn
lcn-frontend==0.2.3
lcn-frontend==0.2.4
# homeassistant.components.ld2410_ble
ld2410-ble==0.1.1

View File

@ -1108,7 +1108,7 @@ lacrosse-view==1.1.1
laundrify-aio==1.2.2
# homeassistant.components.lcn
lcn-frontend==0.2.3
lcn-frontend==0.2.4
# homeassistant.components.ld2410_ble
ld2410-ble==0.1.1

View File

@ -88,7 +88,7 @@ def create_config_entry(
title = entry_data[CONF_HOST]
return MockConfigEntry(
entry_id=fixture_filename,
entry_id=fixture_filename.replace(".", "_"),
domain=DOMAIN,
title=title,
data=entry_data,

View File

@ -27,7 +27,6 @@
{
"address": [0, 7, false],
"name": "Light_Output1",
"resource": "output1",
"domain": "light",
"domain_data": {
"output": "OUTPUT1",
@ -38,7 +37,6 @@
{
"address": [0, 7, false],
"name": "Light_Output2",
"resource": "output2",
"domain": "light",
"domain_data": {
"output": "OUTPUT2",
@ -49,7 +47,6 @@
{
"address": [0, 7, false],
"name": "Light_Relay1",
"resource": "relay1",
"domain": "light",
"domain_data": {
"output": "RELAY1",
@ -60,7 +57,6 @@
{
"address": [0, 7, false],
"name": "Switch_Output1",
"resource": "output1",
"domain": "switch",
"domain_data": {
"output": "OUTPUT1"
@ -69,7 +65,6 @@
{
"address": [0, 7, false],
"name": "Switch_Output2",
"resource": "output2",
"domain": "switch",
"domain_data": {
"output": "OUTPUT2"
@ -78,7 +73,6 @@
{
"address": [0, 7, false],
"name": "Switch_Relay1",
"resource": "relay1",
"domain": "switch",
"domain_data": {
"output": "RELAY1"
@ -87,7 +81,6 @@
{
"address": [0, 7, false],
"name": "Switch_Relay2",
"resource": "relay2",
"domain": "switch",
"domain_data": {
"output": "RELAY2"
@ -96,7 +89,6 @@
{
"address": [0, 7, false],
"name": "Switch_Regulator1",
"resource": "r1varsetpoint",
"domain": "switch",
"domain_data": {
"output": "R1VARSETPOINT"
@ -105,7 +97,6 @@
{
"address": [0, 7, false],
"name": "Switch_KeyLock1",
"resource": "a1",
"domain": "switch",
"domain_data": {
"output": "A1"
@ -114,7 +105,6 @@
{
"address": [0, 5, true],
"name": "Switch_Group5",
"resource": "relay1",
"domain": "switch",
"domain_data": {
"output": "RELAY1"
@ -123,7 +113,6 @@
{
"address": [0, 7, false],
"name": "Cover_Outputs",
"resource": "outputs",
"domain": "cover",
"domain_data": {
"motor": "OUTPUTS",
@ -133,7 +122,6 @@
{
"address": [0, 7, false],
"name": "Cover_Relays",
"resource": "motor1",
"domain": "cover",
"domain_data": {
"motor": "MOTOR1",
@ -143,12 +131,12 @@
{
"address": [0, 7, false],
"name": "Climate1",
"resource": "var1.r1varsetpoint",
"domain": "climate",
"domain_data": {
"source": "VAR1",
"setpoint": "R1VARSETPOINT",
"lockable": true,
"target_value_locked": -1,
"min_temp": 0.0,
"max_temp": 40.0,
"unit_of_measurement": "°C"
@ -157,7 +145,6 @@
{
"address": [0, 7, false],
"name": "Romantic",
"resource": "0.0",
"domain": "scene",
"domain_data": {
"register": 0,
@ -169,7 +156,6 @@
{
"address": [0, 7, false],
"name": "Romantic Transition",
"resource": "0.1",
"domain": "scene",
"domain_data": {
"register": 0,
@ -181,7 +167,6 @@
{
"address": [0, 7, false],
"name": "Sensor_LockRegulator1",
"resource": "r1varsetpoint",
"domain": "binary_sensor",
"domain_data": {
"source": "R1VARSETPOINT"
@ -190,7 +175,6 @@
{
"address": [0, 7, false],
"name": "Binary_Sensor1",
"resource": "binsensor1",
"domain": "binary_sensor",
"domain_data": {
"source": "BINSENSOR1"
@ -199,7 +183,6 @@
{
"address": [0, 7, false],
"name": "Sensor_KeyLock",
"resource": "a5",
"domain": "binary_sensor",
"domain_data": {
"source": "A5"
@ -208,7 +191,6 @@
{
"address": [0, 7, false],
"name": "Sensor_Var1",
"resource": "var1",
"domain": "sensor",
"domain_data": {
"source": "VAR1",
@ -218,7 +200,6 @@
{
"address": [0, 7, false],
"name": "Sensor_Setpoint1",
"resource": "r1varsetpoint",
"domain": "sensor",
"domain_data": {
"source": "R1VARSETPOINT",
@ -228,7 +209,6 @@
{
"address": [0, 7, false],
"name": "Sensor_Led6",
"resource": "led6",
"domain": "sensor",
"domain_data": {
"source": "LED6",
@ -238,7 +218,6 @@
{
"address": [0, 7, false],
"name": "Sensor_LogicOp1",
"resource": "logicop1",
"domain": "sensor",
"domain_data": {
"source": "LOGICOP1",

View File

@ -0,0 +1,96 @@
{
"host": "pchk",
"ip_address": "192.168.2.41",
"port": 4114,
"username": "lcn",
"password": "lcn",
"sk_num_tries": 0,
"dim_mode": "STEPS200",
"acknowledge": false,
"devices": [
{
"address": [0, 7, false],
"name": "TestModule",
"hardware_serial": -1,
"software_serial": -1,
"hardware_type": -1
}
],
"entities": [
{
"address": [0, 7, false],
"name": "Light_Output1",
"resource": "output1",
"domain": "light",
"domain_data": {
"output": "OUTPUT1",
"dimmable": true,
"transition": 5.0
}
},
{
"address": [0, 7, false],
"name": "Switch_Relay1",
"resource": "relay1",
"domain": "switch",
"domain_data": {
"output": "RELAY1"
}
},
{
"address": [0, 7, false],
"name": "Cover_Relays",
"resource": "motor1",
"domain": "cover",
"domain_data": {
"motor": "MOTOR1",
"reverse_time": "RT1200"
}
},
{
"address": [0, 7, false],
"name": "Climate1",
"resource": "var1.r1varsetpoint",
"domain": "climate",
"domain_data": {
"source": "VAR1",
"setpoint": "R1VARSETPOINT",
"lockable": true,
"min_temp": 0.0,
"max_temp": 40.0,
"unit_of_measurement": "°C"
}
},
{
"address": [0, 7, false],
"name": "Romantic",
"resource": "0.0",
"domain": "scene",
"domain_data": {
"register": 0,
"scene": 0,
"outputs": ["OUTPUT1", "OUTPUT2", "RELAY1"],
"transition": 0.0
}
},
{
"address": [0, 7, false],
"name": "Binary_Sensor1",
"resource": "binsensor1",
"domain": "binary_sensor",
"domain_data": {
"source": "BINSENSOR1"
}
},
{
"address": [0, 7, false],
"name": "Sensor_Var1",
"resource": "var1",
"domain": "sensor",
"domain_data": {
"source": "VAR1",
"unit_of_measurement": "°C"
}
}
]
}

View File

@ -29,7 +29,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-binsensor1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-binsensor1',
'unit_of_measurement': None,
})
# ---
@ -76,7 +76,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-a5',
'unique_id': 'lcn/config_entry_pchk_json-m000007-a5',
'unit_of_measurement': None,
})
# ---
@ -123,7 +123,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-r1varsetpoint',
'unique_id': 'lcn/config_entry_pchk_json-m000007-r1varsetpoint',
'unit_of_measurement': None,
})
# ---

View File

@ -36,7 +36,7 @@
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 385>,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-var1.r1varsetpoint',
'unique_id': 'lcn/config_entry_pchk_json-m000007-r1varsetpoint',
'unit_of_measurement': None,
})
# ---

View File

@ -29,7 +29,7 @@
'previous_unique_id': None,
'supported_features': <CoverEntityFeature: 11>,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-outputs',
'unique_id': 'lcn/config_entry_pchk_json-m000007-outputs',
'unit_of_measurement': None,
})
# ---
@ -78,7 +78,7 @@
'previous_unique_id': None,
'supported_features': <CoverEntityFeature: 11>,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-motor1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-motor1',
'unit_of_measurement': None,
})
# ---

View File

@ -30,7 +30,6 @@
'transition': 5.0,
}),
'name': 'Light_Output1',
'resource': 'output1',
}),
]),
'host': 'pchk',
@ -72,7 +71,6 @@
'transition': 5.0,
}),
'name': 'Light_Output1',
'resource': 'output1',
}),
dict({
'address': tuple(
@ -87,7 +85,6 @@
'transition': 0.0,
}),
'name': 'Light_Output2',
'resource': 'output2',
}),
dict({
'address': tuple(
@ -107,7 +104,6 @@
'transition': 0.0,
}),
'name': 'Romantic',
'resource': '0.0',
}),
dict({
'address': tuple(
@ -127,7 +123,134 @@
'transition': 10.0,
}),
'name': 'Romantic Transition',
'resource': '0.1',
}),
]),
'host': 'pchk',
'ip_address': '192.168.2.41',
'password': 'lcn',
'port': 4114,
'sk_num_tries': 0,
'username': 'lcn',
})
# ---
# name: test_migrate_2_1
dict({
'acknowledge': False,
'devices': list([
dict({
'address': tuple(
0,
7,
False,
),
'hardware_serial': -1,
'hardware_type': -1,
'name': 'TestModule',
'software_serial': -1,
}),
]),
'dim_mode': 'STEPS200',
'entities': list([
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'light',
'domain_data': dict({
'dimmable': True,
'output': 'OUTPUT1',
'transition': 5.0,
}),
'name': 'Light_Output1',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'switch',
'domain_data': dict({
'output': 'RELAY1',
}),
'name': 'Switch_Relay1',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'cover',
'domain_data': dict({
'motor': 'MOTOR1',
'reverse_time': 'RT1200',
}),
'name': 'Cover_Relays',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'climate',
'domain_data': dict({
'lockable': True,
'max_temp': 40.0,
'min_temp': 0.0,
'setpoint': 'R1VARSETPOINT',
'source': 'VAR1',
'target_value_locked': -1,
'unit_of_measurement': '°C',
}),
'name': 'Climate1',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'scene',
'domain_data': dict({
'outputs': list([
'OUTPUT1',
'OUTPUT2',
'RELAY1',
]),
'register': 0,
'scene': 0,
'transition': 0.0,
}),
'name': 'Romantic',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'binary_sensor',
'domain_data': dict({
'source': 'BINSENSOR1',
}),
'name': 'Binary_Sensor1',
}),
dict({
'address': tuple(
0,
7,
False,
),
'domain': 'sensor',
'domain_data': dict({
'source': 'VAR1',
'unit_of_measurement': '°C',
}),
'name': 'Sensor_Var1',
}),
]),
'host': 'pchk',

View File

@ -33,7 +33,7 @@
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-output1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-output1',
'unit_of_measurement': None,
})
# ---
@ -90,7 +90,7 @@
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-output2',
'unique_id': 'lcn/config_entry_pchk_json-m000007-output2',
'unit_of_measurement': None,
})
# ---
@ -146,7 +146,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-relay1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-relay1',
'unit_of_measurement': None,
})
# ---

View File

@ -29,7 +29,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-0.0',
'unique_id': 'lcn/config_entry_pchk_json-m000007-00',
'unit_of_measurement': None,
})
# ---
@ -76,7 +76,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-0.1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-01',
'unit_of_measurement': None,
})
# ---

View File

@ -29,7 +29,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-led6',
'unique_id': 'lcn/config_entry_pchk_json-m000007-led6',
'unit_of_measurement': None,
})
# ---
@ -76,7 +76,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-logicop1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-logicop1',
'unit_of_measurement': None,
})
# ---
@ -123,7 +123,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-r1varsetpoint',
'unique_id': 'lcn/config_entry_pchk_json-m000007-r1varsetpoint',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
@ -172,7 +172,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-var1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-var1',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---

View File

@ -29,7 +29,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-g000005-relay1',
'unique_id': 'lcn/config_entry_pchk_json-g000005-relay1',
'unit_of_measurement': None,
})
# ---
@ -76,7 +76,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-a1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-a1',
'unit_of_measurement': None,
})
# ---
@ -123,7 +123,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-output1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-output1',
'unit_of_measurement': None,
})
# ---
@ -170,7 +170,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-output2',
'unique_id': 'lcn/config_entry_pchk_json-m000007-output2',
'unit_of_measurement': None,
})
# ---
@ -217,7 +217,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-r1varsetpoint',
'unique_id': 'lcn/config_entry_pchk_json-m000007-r1varsetpoint',
'unit_of_measurement': None,
})
# ---
@ -264,7 +264,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-relay1',
'unique_id': 'lcn/config_entry_pchk_json-m000007-relay1',
'unit_of_measurement': None,
})
# ---
@ -311,7 +311,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'lcn/config_entry_pchk.json-m000007-relay2',
'unique_id': 'lcn/config_entry_pchk_json-m000007-relay2',
'unit_of_measurement': None,
})
# ---

View File

@ -138,15 +138,12 @@ async def test_async_entry_reload_on_host_event_received(
async def test_migrate_1_1(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test migration config entry."""
entry_v1_1 = create_config_entry("pchk_v1_1", version=(1, 1))
entry_v1_1.add_to_hass(hass)
await hass.config_entries.async_setup(entry_v1_1.entry_id)
await hass.async_block_till_done()
await init_integration(hass, entry_v1_1)
entry_migrated = hass.config_entries.async_get_entry(entry_v1_1.entry_id)
assert entry_migrated.state is ConfigEntryState.LOADED
assert entry_migrated.version == 2
assert entry_migrated.version == 3
assert entry_migrated.minor_version == 1
assert entry_migrated.data == snapshot
@ -155,14 +152,51 @@ async def test_migrate_1_1(hass: HomeAssistant, snapshot: SnapshotAssertion) ->
async def test_migrate_1_2(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test migration config entry."""
entry_v1_2 = create_config_entry("pchk_v1_2", version=(1, 2))
entry_v1_2.add_to_hass(hass)
await hass.config_entries.async_setup(entry_v1_2.entry_id)
await hass.async_block_till_done()
await init_integration(hass, entry_v1_2)
entry_migrated = hass.config_entries.async_get_entry(entry_v1_2.entry_id)
assert entry_migrated.state is ConfigEntryState.LOADED
assert entry_migrated.version == 2
assert entry_migrated.version == 3
assert entry_migrated.minor_version == 1
assert entry_migrated.data == snapshot
@patch("homeassistant.components.lcn.PchkConnectionManager", MockPchkConnectionManager)
async def test_migrate_2_1(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test migration config entry."""
entry_v2_1 = create_config_entry("pchk_v2_1", version=(2, 1))
await init_integration(hass, entry_v2_1)
entry_migrated = hass.config_entries.async_get_entry(entry_v2_1.entry_id)
assert entry_migrated.state is ConfigEntryState.LOADED
assert entry_migrated.version == 3
assert entry_migrated.minor_version == 1
assert entry_migrated.data == snapshot
@pytest.mark.parametrize(
("entity_id", "replace"),
[
("climate.climate1", ("-r1varsetpoint", "-var1.r1varsetpoint")),
("scene.romantic", ("-00", "-0.0")),
],
)
@patch("homeassistant.components.lcn.PchkConnectionManager", MockPchkConnectionManager)
async def test_entity_migration_on_2_1(
hass: HomeAssistant, entity_registry: er.EntityRegistry, entity_id, replace
) -> None:
"""Test entity.unique_id migration on config_entry migration from 2.1."""
entry_v2_1 = create_config_entry("pchk_v2_1", version=(2, 1))
await init_integration(hass, entry_v2_1)
migrated_unique_id = entity_registry.async_get(entity_id).unique_id
old_unique_id = migrated_unique_id.replace(*replace)
entity_registry.async_update_entity(entity_id, new_unique_id=old_unique_id)
assert entity_registry.async_get(entity_id).unique_id == old_unique_id
await hass.config_entries.async_unload(entry_v2_1.entry_id)
entry_v2_1 = create_config_entry("pchk_v2_1", version=(2, 1))
await init_integration(hass, entry_v2_1)
assert entity_registry.async_get(entity_id).unique_id == migrated_unique_id

View File

@ -7,14 +7,13 @@ import pytest
from homeassistant.components.lcn import AddressType
from homeassistant.components.lcn.const import CONF_DOMAIN_DATA
from homeassistant.components.lcn.helpers import get_device_config, get_resource
from homeassistant.components.lcn.helpers import get_device_config
from homeassistant.const import (
CONF_ADDRESS,
CONF_DEVICES,
CONF_DOMAIN,
CONF_ENTITIES,
CONF_NAME,
CONF_RESOURCE,
CONF_TYPE,
)
from homeassistant.core import HomeAssistant
@ -52,7 +51,7 @@ ENTITIES_DELETE_PAYLOAD = {
"entry_id": "",
CONF_ADDRESS: (0, 7, False),
CONF_DOMAIN: "switch",
CONF_RESOURCE: "relay1",
CONF_DOMAIN_DATA: {"output": "RELAY1"},
}
@ -184,18 +183,14 @@ async def test_lcn_entities_add_command(
for key in (CONF_ADDRESS, CONF_NAME, CONF_DOMAIN, CONF_DOMAIN_DATA)
}
resource = get_resource(
ENTITIES_ADD_PAYLOAD[CONF_DOMAIN], ENTITIES_ADD_PAYLOAD[CONF_DOMAIN_DATA]
).lower()
assert {**entity_config, CONF_RESOURCE: resource} not in entry.data[CONF_ENTITIES]
assert entity_config not in entry.data[CONF_ENTITIES]
await client.send_json_auto_id({**ENTITIES_ADD_PAYLOAD, "entry_id": entry.entry_id})
res = await client.receive_json()
assert res["success"], res
assert {**entity_config, CONF_RESOURCE: resource} in entry.data[CONF_ENTITIES]
assert entity_config in entry.data[CONF_ENTITIES]
async def test_lcn_entities_delete_command(
@ -213,7 +208,8 @@ async def test_lcn_entities_delete_command(
for entity in entry.data[CONF_ENTITIES]
if entity[CONF_ADDRESS] == ENTITIES_DELETE_PAYLOAD[CONF_ADDRESS]
and entity[CONF_DOMAIN] == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN]
and entity[CONF_RESOURCE] == ENTITIES_DELETE_PAYLOAD[CONF_RESOURCE]
and entity[CONF_DOMAIN_DATA]
== ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN_DATA]
]
)
== 1
@ -233,7 +229,8 @@ async def test_lcn_entities_delete_command(
for entity in entry.data[CONF_ENTITIES]
if entity[CONF_ADDRESS] == ENTITIES_DELETE_PAYLOAD[CONF_ADDRESS]
and entity[CONF_DOMAIN] == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN]
and entity[CONF_RESOURCE] == ENTITIES_DELETE_PAYLOAD[CONF_RESOURCE]
and entity[CONF_DOMAIN_DATA]
== ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN_DATA]
]
)
== 0