async def test_registry_cleanup(hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock): """Test for 1-Wire device. As they would be on a clean setup: all binary-sensors and switches disabled. """ entity_registry = mock_registry(hass) device_registry = mock_device_registry(hass) # Initialise with two components setup_owproxy_mock_devices(owproxy, SENSOR_DOMAIN, ["10.111111111111", "28.111111111111"]) await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 2 assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 2 # Second item has disappeared from bus, and was removed manually from the front-end setup_owproxy_mock_devices(owproxy, SENSOR_DOMAIN, ["10.111111111111"]) entity_registry.async_remove("sensor.28_111111111111_temperature") await hass.async_block_till_done() assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 1 assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 2 # Second item has disappeared from bus, and was removed manually from the front-end await hass.config_entries.async_reload("2") await hass.async_block_till_done() assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 1 assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 1
async def test_registry_cleanup(owproxy, hass): """Test for 1-Wire device. As they would be on a clean setup: all binary-sensors and switches disabled. """ await async_setup_component(hass, "persistent_notification", {}) entity_registry = mock_registry(hass) device_registry = mock_device_registry(hass) # Initialise with two components setup_owproxy_mock_devices(owproxy, SENSOR_DOMAIN, ["10.111111111111", "28.111111111111"]) with patch("homeassistant.components.onewire.PLATFORMS", [SENSOR_DOMAIN]): await setup_onewire_patched_owserver_integration(hass) await hass.async_block_till_done() assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 2 assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 2 # Second item has disappeared from bus, and was removed manually from the front-end setup_owproxy_mock_devices(owproxy, SENSOR_DOMAIN, ["10.111111111111"]) entity_registry.async_remove("sensor.28_111111111111_temperature") await hass.async_block_till_done() assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 1 assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 2 # Second item has disappeared from bus, and was removed manually from the front-end with patch("homeassistant.components.onewire.PLATFORMS", [SENSOR_DOMAIN]): await hass.config_entries.async_reload("2") await hass.async_block_till_done() assert len(er.async_entries_for_config_entry(entity_registry, "2")) == 1 assert len(dr.async_entries_for_config_entry(device_registry, "2")) == 1
async def test_device_registry_cleanup( hass: HomeAssistant, mock_config_entry: MockConfigEntry, aioclient_mock: AiohttpClientMocker, caplog: LogCaptureFixture, ) -> None: """Test that we remove untracked repositories from the decvice registry.""" mock_config_entry.options = {CONF_REPOSITORIES: ["home-assistant/core"]} await setup_github_integration(hass, mock_config_entry, aioclient_mock) device_registry = dr.async_get(hass) devices = dr.async_entries_for_config_entry( registry=device_registry, config_entry_id=mock_config_entry.entry_id, ) assert len(devices) == 1 mock_config_entry.options = {CONF_REPOSITORIES: []} assert await hass.config_entries.async_reload(mock_config_entry.entry_id) await hass.async_block_till_done() assert ( f"Unlinking device {devices[0].id} for untracked repository home-assistant/core from config entry {mock_config_entry.entry_id}" in caplog.text) devices = dr.async_entries_for_config_entry( registry=device_registry, config_entry_id=mock_config_entry.entry_id, ) assert len(devices) == 0
async def test_removed_device(hass, client, climate_radio_thermostat_ct100_plus, lock_schlage_be469, integration): """Test that the device registry gets updated when a device gets removed.""" driver = client.driver assert driver # Verify how many nodes are available assert len(driver.controller.nodes) == 2 # Make sure there are the same number of devices dev_reg = dr.async_get(hass) device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) assert len(device_entries) == 2 # Check how many entities there are ent_reg = er.async_get(hass) entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 29 # Remove a node and reload the entry old_node = driver.controller.nodes.pop(13) await hass.config_entries.async_reload(integration.entry_id) await hass.async_block_till_done() # Assert that the node and all of it's entities were removed from the device and # entity registry device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) assert len(device_entries) == 1 entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 17 assert dev_reg.async_get_device({get_device_id(driver, old_node)}) is None
async def test_removed_device(hass, client, multiple_devices, integration): """Test that the device registry gets updated when a device gets removed.""" nodes = multiple_devices # Verify how many nodes are available assert len(client.driver.controller.nodes) == 2 # Make sure there are the same number of devices dev_reg = dr.async_get(hass) device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) assert len(device_entries) == 2 # Check how many entities there are ent_reg = er.async_get(hass) entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 24 # Remove a node and reload the entry old_node = nodes.pop(13) await hass.config_entries.async_reload(integration.entry_id) await hass.async_block_till_done() # Assert that the node and all of it's entities were removed from the device and # entity registry device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) assert len(device_entries) == 1 entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 15 assert dev_reg.async_get_device({get_device_id(client, old_node)}) is None
async def test_device_management(hass: HomeAssistant): """Test that we are adding and removing devices for monitors returned from the API.""" mock_entry = await setup_uptimerobot_integration(hass) dev_reg = dr.async_get(hass) devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 1 assert devices[0].identifiers == {(DOMAIN, "1234")} assert devices[0].name == "Test monitor" assert hass.states.get( UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON assert hass.states.get( f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2") is None with patch( "pyuptimerobot.UptimeRobot.async_get_monitors", return_value=mock_uptimerobot_api_response(data=[ MOCK_UPTIMEROBOT_MONITOR, { **MOCK_UPTIMEROBOT_MONITOR, "id": 12345 } ]), ): async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL) await hass.async_block_till_done() devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 2 assert devices[0].identifiers == {(DOMAIN, "1234")} assert devices[1].identifiers == {(DOMAIN, "12345")} assert hass.states.get( UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON assert (hass.states.get(f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2").state == STATE_ON) with patch( "pyuptimerobot.UptimeRobot.async_get_monitors", return_value=mock_uptimerobot_api_response(), ): async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL) await hass.async_block_till_done() devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 1 assert devices[0].identifiers == {(DOMAIN, "1234")} assert hass.states.get( UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON assert hass.states.get( f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2") is None
async def _async_update_data(self) -> list[UptimeRobotMonitor] | None: """Update data.""" try: response = await self.api.async_get_monitors() except UptimeRobotAuthenticationException as exception: raise ConfigEntryAuthFailed(exception) from exception except UptimeRobotException as exception: raise UpdateFailed(exception) from exception else: if response.status != API_ATTR_OK: raise UpdateFailed(response.error.message) monitors: list[UptimeRobotMonitor] = response.data current_monitors = { list(device.identifiers)[0][1] for device in dr.async_entries_for_config_entry( self._device_registry, self._config_entry_id) } new_monitors = {str(monitor.id) for monitor in monitors} if stale_monitors := current_monitors - new_monitors: for monitor_id in stale_monitors: if device := self._device_registry.async_get_device({ (DOMAIN, monitor_id) }): self._device_registry.async_remove_device(device.id)
async def test_get_trigger_capabilities_central_scene_value_notification( hass, client, wallmote_central_scene, integration ): """Test we get the expected capabilities from a value_notification.central_scene trigger.""" dev_reg = async_get_dev_reg(hass) device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] capabilities = await device_trigger.async_get_trigger_capabilities( hass, { "platform": "device", "domain": DOMAIN, "type": "event.value_notification.central_scene", "device_id": device.id, "command_class": CommandClass.CENTRAL_SCENE.value, "property": "scene", "property_key": "001", "endpoint": 0, "subtype": "Endpoint 0 Scene 001", }, ) assert capabilities and "extra_fields" in capabilities assert voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer ) == [ { "name": "value", "optional": True, "type": "select", "options": [(0, "KeyPressed"), (1, "KeyReleased"), (2, "KeyHeldDown")], }, ]
async def test_get_trigger_capabilities_scene_activation_value_notification( hass, client, hank_binary_switch, integration ): """Test we get the expected capabilities from a value_notification.scene_activation trigger.""" dev_reg = async_get_dev_reg(hass) device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] capabilities = await device_trigger.async_get_trigger_capabilities( hass, { "platform": "device", "domain": DOMAIN, "type": "event.value_notification.scene_activation", "device_id": device.id, "command_class": CommandClass.SCENE_ACTIVATION.value, "property": "sceneId", "property_key": None, "endpoint": 0, "subtype": "Endpoint 0", }, ) assert capabilities and "extra_fields" in capabilities assert voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer ) == [ { "name": "value", "optional": True, "type": "integer", "valueMin": 1, "valueMax": 255, } ]
def async_update_sensors(hass: HomeAssistant, config_entry: ConfigEntry) -> None: dev_reg = device_registry.async_get(hass) existing_devices = { device.name: device.id for device in device_registry.async_entries_for_config_entry( dev_reg, config_entry.entry_id ) } sensors = [] for tracked_asset_pair in config_entry.options[CONF_TRACKED_ASSET_PAIRS]: # Only create new devices if ( device_name := create_device_name(tracked_asset_pair) ) in existing_devices: existing_devices.pop(device_name) else: for sensor_type in SENSOR_TYPES: sensors.append( KrakenSensor( hass.data[DOMAIN], tracked_asset_pair, sensor_type, ) )
async def test_get_trigger_capabilities_entry_control_notification( hass, client, lock_schlage_be469, integration ): """Test we get the expected capabilities from a notification.entry_control trigger.""" dev_reg = async_get_dev_reg(hass) device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] capabilities = await device_trigger.async_get_trigger_capabilities( hass, { "platform": "device", "domain": DOMAIN, "device_id": device.id, "type": "event.notification.entry_control", "command_class": CommandClass.ENTRY_CONTROL.value, }, ) assert capabilities and "extra_fields" in capabilities assert_lists_same( voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer ), [ {"name": "event_type", "optional": True, "type": "string"}, {"name": "data_type", "optional": True, "type": "string"}, ], )
async def test_text_overlay_good_left(hass: HomeAssistant) -> None: """Test a working text overlay with device_id.""" client = create_mock_motioneye_client() await setup_mock_motioneye_config_entry(hass, client=client) device = dr.async_entries_for_config_entry( await dr.async_get_registry(hass), TEST_CONFIG_ENTRY_ID)[0] custom_right_text = "one\ntwo\nthree" data = { ATTR_DEVICE_ID: device.id, KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP, KEY_TEXT_OVERLAY_RIGHT: KEY_TEXT_OVERLAY_CUSTOM_TEXT, KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT: custom_right_text, } client.async_get_camera = AsyncMock( return_value=copy.deepcopy(TEST_CAMERA)) await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data) await hass.async_block_till_done() assert client.async_get_camera.called expected_camera = copy.deepcopy(TEST_CAMERA) expected_camera[KEY_TEXT_OVERLAY_LEFT] = KEY_TEXT_OVERLAY_TIMESTAMP expected_camera[KEY_TEXT_OVERLAY_RIGHT] = KEY_TEXT_OVERLAY_CUSTOM_TEXT expected_camera[KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT] = "one\\ntwo\\nthree" assert client.async_set_camera.call_args == call(TEST_CAMERA_ID, expected_camera)
async def async_unload_entry(hass, entry): """Unload a config entry.""" # cleanup platforms unload_ok = all(await asyncio.gather(*[ hass.config_entries.async_forward_entry_unload(entry, platform) for platform in PLATFORMS ])) if not unload_ok: return False # disable discovery await discovery.async_stop(hass) # cleanup subscriptions for unsub in hass.data[DATA_UNSUB]: unsub() hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))() for platform in PLATFORMS: hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(platform))() # deattach device triggers device_registry = await hass.helpers.device_registry.async_get_registry() devices = async_entries_for_config_entry(device_registry, entry.entry_id) for device in devices: await device_automation.async_remove_automations(hass, device.id) return True
async def async_step_init(self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage Hue options.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) # create a list of Hue device ID's that the user can select # to ignore availability status dev_reg = device_registry.async_get(self.hass) entries = device_registry.async_entries_for_config_entry( dev_reg, self.config_entry.entry_id) dev_ids = { identifier[1]: entry.name for entry in entries for identifier in entry.identifiers if identifier[0] == DOMAIN } # filter any non existing device id's from the list cur_ids = [ item for item in self.config_entry.options.get( CONF_IGNORE_AVAILABILITY, []) if item in dev_ids ] return self.async_show_form( step_id="init", data_schema=vol.Schema({ vol.Optional( CONF_IGNORE_AVAILABILITY, default=cur_ids, ): cv.multi_select(dev_ids), }), )
async def async_setup(self) -> bool: """Set up a Netgear router.""" async with self._api_lock: if not await self.hass.async_add_executor_job(self._setup): return False # set already known devices to away instead of unavailable device_registry = dr.async_get(self.hass) devices = dr.async_entries_for_config_entry(device_registry, self.entry_id) for device_entry in devices: if device_entry.via_device_id is None: continue # do not add the router itself device_mac = dict(device_entry.connections)[dr.CONNECTION_NETWORK_MAC] self.devices[device_mac] = { "mac": device_mac, "name": device_entry.name, "active": False, "last_seen": dt_util.utcnow() - timedelta(days=365), "device_model": None, "device_type": None, "type": None, "link_rate": None, "signal": None, "ip": None, "ssid": None, "conn_ap_mac": None, "allow_or_block": None, } return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms( entry, PLATFORMS) router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) if not hass.data[DOMAIN]: hass.data.pop(DOMAIN) if router.mode != MODE_ROUTER: router_id = None # Remove devices that are no longer tracked device_registry = dr.async_get(hass) devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id) for device_entry in devices: if device_entry.via_device_id is None: router_id = device_entry.id continue # do not remove the router itself device_registry.async_update_device( device_entry.id, remove_config_entry_id=entry.entry_id) # Remove entities that are no longer tracked entity_registry = er.async_get(hass) entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id) for entity_entry in entries: if entity_entry.device_id is not router_id: entity_registry.async_remove(entity_entry.entity_id) return unload_ok
async def try_connect(tries: int = 0, is_disconnect: bool = True) -> None: """Try connecting to the API client. Will retry if not successful.""" if entry.entry_id not in hass.data[DOMAIN]: # When removing/disconnecting manually return device_registry = await hass.helpers.device_registry.async_get_registry() devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id) for device in devices: # There is only one device in ESPHome if device.disabled: # Don't attempt to connect if it's disabled return data: RuntimeEntryData = hass.data[DOMAIN][entry.entry_id] for disconnect_cb in data.disconnect_callbacks: disconnect_cb() data.disconnect_callbacks = [] data.available = False data.async_update_device_state(hass) if is_disconnect: # This can happen often depending on WiFi signal strength. # So therefore all these connection warnings are logged # as infos. The "unavailable" logic will still trigger so the # user knows if the device is not connected. _LOGGER.info("Disconnected from ESPHome API for %s", host) if tries != 0: # If not first re-try, wait and print message # Cap wait time at 1 minute. This is because while working on the # device (e.g. soldering stuff), users don't want to have to wait # a long time for their device to show up in HA again (this was # mentioned a lot in early feedback) # # In the future another API will be set up so that the ESP can # notify HA of connectivity directly, but for new we'll use a # really short reconnect interval. tries = min(tries, 10) # prevent OverflowError wait_time = int(round(min(1.8 ** tries, 60.0))) _LOGGER.info("Trying to reconnect to %s in %s seconds", host, wait_time) await asyncio.sleep(wait_time) try: await cli.connect(on_stop=try_connect, login=True) except APIConnectionError as error: _LOGGER.info( "Can't connect to ESPHome API for %s (%s): %s", entry.unique_id, host, error, ) # Schedule re-connect in event loop in order not to delay HA # startup. First connect is scheduled in tracked tasks. data.reconnect_task = hass.loop.create_task( try_connect(tries + 1, is_disconnect=False) ) else: _LOGGER.info("Successfully connected to %s", host) hass.async_create_task(on_login())
def async_migrate_entities_devices( hass: HomeAssistant, legacy_entry_id: str, new_entry: ConfigEntry ) -> None: """Move entities and devices to the new config entry.""" migrated_devices = [] device_registry = dr.async_get(hass) for dev_entry in dr.async_entries_for_config_entry( device_registry, legacy_entry_id ): for domain, value in dev_entry.identifiers: if domain == DOMAIN and value == new_entry.unique_id: _LOGGER.debug( "Migrating device with %s to %s", dev_entry.identifiers, new_entry.unique_id, ) migrated_devices.append(dev_entry.id) device_registry.async_update_device( dev_entry.id, add_config_entry_id=new_entry.entry_id, remove_config_entry_id=legacy_entry_id, ) entity_registry = er.async_get(hass) for reg_entity in er.async_entries_for_config_entry( entity_registry, legacy_entry_id ): if reg_entity.device_id in migrated_devices: entity_registry.async_update_entity( reg_entity.entity_id, config_entry_id=new_entry.entry_id )
def async_migrate_legacy_entries( hass: HomeAssistant, hosts_by_mac: dict[str, str], config_entries_by_mac: dict[str, ConfigEntry], legacy_entry: ConfigEntry, ) -> None: """Migrate the legacy config entries to have an entry per device.""" device_registry = dr.async_get(hass) for dev_entry in dr.async_entries_for_config_entry(device_registry, legacy_entry.entry_id): for connection_type, mac in dev_entry.connections: if (connection_type != dr.CONNECTION_NETWORK_MAC or mac in config_entries_by_mac): continue hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": "migration"}, data={ CONF_HOST: hosts_by_mac.get(mac), CONF_MAC: mac, CONF_NAME: dev_entry.name or f"TP-Link device {mac}", }, )) async def _async_cleanup_legacy_entry(_now: datetime) -> None: await async_cleanup_legacy_entry(hass, legacy_entry.entry_id) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_cleanup_legacy_entry)
async def test_get_condition_capabilities_node_status(hass, client, lock_schlage_be469, integration): """Test we don't get capabilities from a node_status condition.""" dev_reg = device_registry.async_get(hass) device = device_registry.async_entries_for_config_entry( dev_reg, integration.entry_id)[0] capabilities = await device_condition.async_get_condition_capabilities( hass, { "platform": "device", "domain": DOMAIN, "device_id": device.id, "type": "node_status", }, ) assert capabilities and "extra_fields" in capabilities assert voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer) == [{ "name": "status", "required": True, "type": "select", "options": [ ("asleep", "asleep"), ("awake", "awake"), ("dead", "dead"), ("alive", "alive"), ], }]
async def test_deconz_events_bad_unique_id(hass, aioclient_mock, mock_deconz_websocket): """Verify no devices are created if unique id is bad or missing.""" data = { "sensors": { "1": { "name": "Switch 1 no unique id", "type": "ZHASwitch", "state": { "buttonevent": 1000 }, "config": {}, }, "2": { "name": "Switch 2 bad unique id", "type": "ZHASwitch", "state": { "buttonevent": 1000 }, "config": { "battery": 100 }, "uniqueid": "00:00-00", }, } } with patch.dict(DECONZ_WEB_REQUEST, data): config_entry = await setup_deconz_integration(hass, aioclient_mock) device_registry = dr.async_get(hass) assert len(hass.states.async_all()) == 1 assert (len( dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 2)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" # cleanup platforms unload_ok = await hass.config_entries.async_unload_platforms( entry, PLATFORMS) if not unload_ok: return False # disable discovery await discovery.async_stop(hass) # cleanup subscriptions for unsub in hass.data[DATA_UNSUB]: unsub() hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))() for platform in PLATFORMS: hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(platform))() # deattach device triggers device_registry = dr.async_get(hass) devices = async_entries_for_config_entry(device_registry, entry.entry_id) for device in devices: await device_automation.async_remove_automations(hass, device.id) return True
async def test_text_overlay_bad_device_identifier(hass: HomeAssistant) -> None: """Test text overlay with bad device identifier.""" client = create_mock_motioneye_client() await setup_mock_motioneye_config_entry(hass, client=client) device = dr.async_entries_for_config_entry( await dr.async_get_registry(hass), TEST_CONFIG_ENTRY_ID)[0] device_registry = await dr.async_get_registry(hass) data = { ATTR_DEVICE_ID: device.id, KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP, } # Set the device identifier to have a non-int camera_id. device_registry.async_update_device(device_id=device.id, new_identifiers={(DOMAIN, "host:port_str")}) await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data) await hass.async_block_till_done() assert not client.async_set_camera.called # Set the device_unique_id to have the wrong number of values. device_registry.async_update_device(device_id=device.id, new_identifiers={(DOMAIN, "host:port", "another")}) await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data) await hass.async_block_till_done() assert not client.async_set_camera.called
async def _reconnect_once(self) -> None: # Wait and clear reconnection event await self._reconnect_event.wait() self._reconnect_event.clear() # If in connected state, do not try to connect again. async with self._connected_lock: if self._connected: return # Check if the entry got removed or disabled, in which case we shouldn't reconnect if not DomainData.get(self._hass).is_entry_loaded(self._entry): # When removing/disconnecting manually return device_registry = self._hass.helpers.device_registry.async_get(self._hass) devices = dr.async_entries_for_config_entry( device_registry, self._entry.entry_id ) for device in devices: # There is only one device in ESPHome if device.disabled: # Don't attempt to connect if it's disabled return await self._try_connect()
async def async_setup_entry(hass, config_entry: ConfigEntry, async_add_entities): """Set up the component sensors from a config entry.""" # Register service to change the update interval on specific bridges platform = entity_platform.current_platform.get() platform.async_register_entity_service( SERVICE_SET_UPDATE_INTERVAL, SET_UPDATE_INTERVAL_SERVICE_SCHEMA, "async_set_update_interval", ) # Add one sensor entity for each hue bridge and link it to the hub device base_name = config_entry.data.get(CONF_NAME, DEFAULT_SENSOR_NAME) initial_scan_interval = max(1, config_entry.data.get(CONF_SCAN_INTERVAL)) device_registry: dr.DeviceRegistry = await dr.async_get_registry(hass) new_entities = [] for i, (b_entry_id, bridge) in enumerate(hass.data[HUE_DOMAIN].items()): # Extract hue hub device to link the sensor with it device = next( filter( lambda dev: dev.via_device_id is None, dr.async_entries_for_config_entry(device_registry, b_entry_id), )) new_entities.append( HuePollingInterval( f"{base_name}_{i + 1}" if i else base_name, device, bridge.sensor_manager, initial_scan_interval, )) async_add_entities(new_entities, False)
async def handle_event(ev): # Find ESPHome Device ID esphome_id = ev.data["device_id"] device_id = device_ids.get(esphome_id) if device_id is not None: await scan_tag(ev.data["tag_id"], device_id) return esphome_entry = None # Find ESPHome entry based on esphome_id for entry in hass.config_entries.async_entries("esphome"): if entry.unique_id: esphome_entry = entry break if esphome_entry is None: await scan_tag(ev.data["tag_id"], device_id) return # Find ESPHome device in registry dev_reg = await device_registry.async_get_registry(hass) devices = device_registry.async_entries_for_config_entry( dev_reg, esphome_entry.entry_id) if devices: device_id = devices[0].id device_ids[esphome_id] = device_id await scan_tag(ev.data["tag_id"], device_id)
async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry, ) -> dict[str, Any]: """Return diagnostics for a config entry.""" coordinator = hass.data[DOMAIN][config_entry.entry_id] device_registry = dr.async_get(hass) entity_registry = er.async_get(hass) devices = [] registry_devices = dr.async_entries_for_config_entry( device_registry, config_entry.entry_id) for device in registry_devices: entities = [] registry_entities = er.async_entries_for_device( entity_registry, device_id=device.id, include_disabled_entities=True, ) for entity in registry_entities: state_dict = None if state := hass.states.get(entity.entity_id): state_dict = dict(state.as_dict()) state_dict.pop("context", None) entities.append({"entry": asdict(entity), "state": state_dict}) devices.append({"device": asdict(device), "entities": entities})
def _async_get_diagnostics( hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry | None = None, ) -> dict[str, Any]: """Return diagnostics for a config entry.""" mqtt_instance: MQTT = hass.data[DATA_MQTT] redacted_config = async_redact_data(mqtt_instance.conf, REDACT_CONFIG) data = { "connected": is_connected(hass), "mqtt_config": redacted_config, } if device: data["device"] = _async_device_as_dict(hass, device) data["mqtt_debug_info"] = debug_info.info_for_device(hass, device.id) else: device_registry = dr.async_get(hass) data.update( devices=[ _async_device_as_dict(hass, device) for device in dr.async_entries_for_config_entry( device_registry, entry.entry_id) ], mqtt_debug_info=debug_info.info_for_config_entry(hass), ) return data
async def async_setup(self) -> None: """Set up a Netgear router.""" await self.hass.async_add_executor_job(self._setup) # set already known devices to away instead of unavailable device_registry = dr.async_get(self.hass) devices = dr.async_entries_for_config_entry(device_registry, self.entry_id) for device_entry in devices: if device_entry.via_device_id is None: continue # do not add the router itself device_mac = dict(device_entry.connections).get(dr.CONNECTION_NETWORK_MAC) self.devices[device_mac] = { "mac": device_mac, "name": device_entry.name, "active": False, "last_seen": dt_util.utcnow() - timedelta(days=365), "device_model": None, "device_type": None, "type": None, "link_rate": None, "signal": None, "ip": None, } await self.async_update_device_trackers() self.entry.async_on_unload( async_track_time_interval( self.hass, self.async_update_device_trackers, SCAN_INTERVAL ) ) async_dispatcher_send(self.hass, self.signal_device_new)
def add_public_entities(): """Retrieve Netatmo public weather entities.""" entities = [] for area in entry.options.get(CONF_WEATHER_AREAS, {}).values(): data = NetatmoPublicData( auth, lat_ne=area[CONF_LAT_NE], lon_ne=area[CONF_LON_NE], lat_sw=area[CONF_LAT_SW], lon_sw=area[CONF_LON_SW], ) for sensor_type in SUPPORTED_PUBLIC_SENSOR_TYPES: entities.append(NetatmoPublicSensor( area, data, sensor_type, )) for device in async_entries_for_config_entry(device_registry, entry.entry_id): if device.model == "Public Weather stations": device_registry.async_remove_device(device.id) if entities: async_add_entities(entities)