Make use of entry id rather than unique id when storing deconz entry in hass.data (#52584)

* Make use of entry id rather than unique id when storing entry in hass data

* Update homeassistant/components/deconz/services.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Robert Svensson 2021-07-06 17:18:54 +02:00 committed by GitHub
parent 7ba1fdea70
commit 605f65b75d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 37 deletions

View File

@ -20,8 +20,7 @@ async def async_setup_entry(hass, config_entry):
Load config, group, light and sensor data for server information. Load config, group, light and sensor data for server information.
Start websocket for push notification of state changes from deCONZ. Start websocket for push notification of state changes from deCONZ.
""" """
if DOMAIN not in hass.data: hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN] = {}
await async_update_group_unique_id(hass, config_entry) await async_update_group_unique_id(hass, config_entry)
@ -33,7 +32,7 @@ async def async_setup_entry(hass, config_entry):
if not await gateway.async_setup(): if not await gateway.async_setup():
return False return False
hass.data[DOMAIN][config_entry.unique_id] = gateway hass.data[DOMAIN][config_entry.entry_id] = gateway
await gateway.async_update_device_registry() await gateway.async_update_device_registry()
@ -48,7 +47,7 @@ async def async_setup_entry(hass, config_entry):
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload deCONZ config entry.""" """Unload deCONZ config entry."""
gateway = hass.data[DOMAIN].pop(config_entry.unique_id) gateway = hass.data[DOMAIN].pop(config_entry.entry_id)
if not hass.data[DOMAIN]: if not hass.data[DOMAIN]:
await async_unload_services(hass) await async_unload_services(hass)

View File

@ -33,8 +33,8 @@ from .errors import AuthenticationRequired, CannotConnect
@callback @callback
def get_gateway_from_config_entry(hass, config_entry): def get_gateway_from_config_entry(hass, config_entry):
"""Return gateway with a matching bridge id.""" """Return gateway with a matching config entry ID."""
return hass.data[DECONZ_DOMAIN][config_entry.unique_id] return hass.data[DECONZ_DOMAIN][config_entry.entry_id]
class DeconzGateway: class DeconzGateway:

View File

@ -59,14 +59,29 @@ async def async_setup_services(hass):
service = service_call.service service = service_call.service
service_data = service_call.data service_data = service_call.data
gateway = get_master_gateway(hass)
if CONF_BRIDGE_ID in service_data:
found_gateway = False
bridge_id = normalize_bridge_id(service_data[CONF_BRIDGE_ID])
for possible_gateway in hass.data[DOMAIN].values():
if possible_gateway.bridgeid == bridge_id:
gateway = possible_gateway
found_gateway = True
break
if not found_gateway:
LOGGER.error("Could not find the gateway %s", bridge_id)
return
if service == SERVICE_CONFIGURE_DEVICE: if service == SERVICE_CONFIGURE_DEVICE:
await async_configure_service(hass, service_data) await async_configure_service(gateway, service_data)
elif service == SERVICE_DEVICE_REFRESH: elif service == SERVICE_DEVICE_REFRESH:
await async_refresh_devices_service(hass, service_data) await async_refresh_devices_service(gateway)
elif service == SERVICE_REMOVE_ORPHANED_ENTRIES: elif service == SERVICE_REMOVE_ORPHANED_ENTRIES:
await async_remove_orphaned_entries_service(hass, service_data) await async_remove_orphaned_entries_service(gateway)
hass.services.async_register( hass.services.async_register(
DOMAIN, DOMAIN,
@ -102,7 +117,7 @@ async def async_unload_services(hass):
hass.services.async_remove(DOMAIN, SERVICE_REMOVE_ORPHANED_ENTRIES) hass.services.async_remove(DOMAIN, SERVICE_REMOVE_ORPHANED_ENTRIES)
async def async_configure_service(hass, data): async def async_configure_service(gateway, data):
"""Set attribute of device in deCONZ. """Set attribute of device in deCONZ.
Entity is used to resolve to a device path (e.g. '/lights/1'). Entity is used to resolve to a device path (e.g. '/lights/1').
@ -118,10 +133,6 @@ async def async_configure_service(hass, data):
See Dresden Elektroniks REST API documentation for details: See Dresden Elektroniks REST API documentation for details:
http://dresden-elektronik.github.io/deconz-rest-doc/rest/ http://dresden-elektronik.github.io/deconz-rest-doc/rest/
""" """
gateway = get_master_gateway(hass)
if CONF_BRIDGE_ID in data:
gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]
field = data.get(SERVICE_FIELD, "") field = data.get(SERVICE_FIELD, "")
entity_id = data.get(SERVICE_ENTITY) entity_id = data.get(SERVICE_ENTITY)
data = data[SERVICE_DATA] data = data[SERVICE_DATA]
@ -136,31 +147,21 @@ async def async_configure_service(hass, data):
await gateway.api.request("put", field, json=data) await gateway.api.request("put", field, json=data)
async def async_refresh_devices_service(hass, data): async def async_refresh_devices_service(gateway):
"""Refresh available devices from deCONZ.""" """Refresh available devices from deCONZ."""
gateway = get_master_gateway(hass)
if CONF_BRIDGE_ID in data:
gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]
gateway.ignore_state_updates = True gateway.ignore_state_updates = True
await gateway.api.refresh_state() await gateway.api.refresh_state()
gateway.ignore_state_updates = False gateway.ignore_state_updates = False
gateway.async_add_device_callback(NEW_GROUP, force=True) for new_device_type in [NEW_GROUP, NEW_LIGHT, NEW_SCENE, NEW_SENSOR]:
gateway.async_add_device_callback(NEW_LIGHT, force=True) gateway.async_add_device_callback(new_device_type, force=True)
gateway.async_add_device_callback(NEW_SCENE, force=True)
gateway.async_add_device_callback(NEW_SENSOR, force=True)
async def async_remove_orphaned_entries_service(hass, data): async def async_remove_orphaned_entries_service(gateway):
"""Remove orphaned deCONZ entries from device and entity registries.""" """Remove orphaned deCONZ entries from device and entity registries."""
gateway = get_master_gateway(hass)
if CONF_BRIDGE_ID in data:
gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]
device_registry, entity_registry = await asyncio.gather( device_registry, entity_registry = await asyncio.gather(
hass.helpers.device_registry.async_get_registry(), gateway.hass.helpers.device_registry.async_get_registry(),
hass.helpers.entity_registry.async_get_registry(), gateway.hass.helpers.entity_registry.async_get_registry(),
) )
entity_entries = async_entries_for_config_entry( entity_entries = async_entries_for_config_entry(

View File

@ -61,8 +61,8 @@ async def test_setup_entry_successful(hass, aioclient_mock):
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert hass.data[DECONZ_DOMAIN] assert hass.data[DECONZ_DOMAIN]
assert config_entry.unique_id in hass.data[DECONZ_DOMAIN] assert config_entry.entry_id in hass.data[DECONZ_DOMAIN]
assert hass.data[DECONZ_DOMAIN][config_entry.unique_id].master assert hass.data[DECONZ_DOMAIN][config_entry.entry_id].master
async def test_setup_entry_multiple_gateways(hass, aioclient_mock): async def test_setup_entry_multiple_gateways(hass, aioclient_mock):
@ -80,8 +80,8 @@ async def test_setup_entry_multiple_gateways(hass, aioclient_mock):
) )
assert len(hass.data[DECONZ_DOMAIN]) == 2 assert len(hass.data[DECONZ_DOMAIN]) == 2
assert hass.data[DECONZ_DOMAIN][config_entry.unique_id].master assert hass.data[DECONZ_DOMAIN][config_entry.entry_id].master
assert not hass.data[DECONZ_DOMAIN][config_entry2.unique_id].master assert not hass.data[DECONZ_DOMAIN][config_entry2.entry_id].master
async def test_unload_entry(hass, aioclient_mock): async def test_unload_entry(hass, aioclient_mock):
@ -112,7 +112,7 @@ async def test_unload_entry_multiple_gateways(hass, aioclient_mock):
assert await async_unload_entry(hass, config_entry) assert await async_unload_entry(hass, config_entry)
assert len(hass.data[DECONZ_DOMAIN]) == 1 assert len(hass.data[DECONZ_DOMAIN]) == 1
assert hass.data[DECONZ_DOMAIN][config_entry2.unique_id].master assert hass.data[DECONZ_DOMAIN][config_entry2.entry_id].master
async def test_update_group_unique_id(hass): async def test_update_group_unique_id(hass):

View File

@ -152,8 +152,27 @@ async def test_configure_service_with_entity_and_field(hass, aioclient_mock):
assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20} assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20}
async def test_configure_service_with_faulty_bridgeid(hass, aioclient_mock):
"""Test that service fails on a bad bridge id."""
await setup_deconz_integration(hass, aioclient_mock)
aioclient_mock.clear_requests()
data = {
CONF_BRIDGE_ID: "Bad bridge id",
SERVICE_FIELD: "/lights/1",
SERVICE_DATA: {"on": True},
}
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data
)
await hass.async_block_till_done()
assert len(aioclient_mock.mock_calls) == 0
async def test_configure_service_with_faulty_field(hass, aioclient_mock): async def test_configure_service_with_faulty_field(hass, aioclient_mock):
"""Test that service invokes pydeconz with the correct path and data.""" """Test that service fails on a bad field."""
await setup_deconz_integration(hass, aioclient_mock) await setup_deconz_integration(hass, aioclient_mock)
data = {SERVICE_FIELD: "light/2", SERVICE_DATA: {}} data = {SERVICE_FIELD: "light/2", SERVICE_DATA: {}}
@ -166,7 +185,7 @@ async def test_configure_service_with_faulty_field(hass, aioclient_mock):
async def test_configure_service_with_faulty_entity(hass, aioclient_mock): async def test_configure_service_with_faulty_entity(hass, aioclient_mock):
"""Test that service invokes pydeconz with the correct path and data.""" """Test that service on a non existing entity."""
await setup_deconz_integration(hass, aioclient_mock) await setup_deconz_integration(hass, aioclient_mock)
aioclient_mock.clear_requests() aioclient_mock.clear_requests()