def async_extract_referenced_entity_ids( hass: HomeAssistant, service_call: ServiceCall, expand_group: bool = True) -> SelectedEntities: """Extract referenced entity IDs from a service call.""" selector = ServiceTargetSelector(service_call) selected = SelectedEntities() if not selector.has_any_selector: return selected entity_ids = selector.entity_ids if expand_group: entity_ids = hass.components.group.expand_entity_ids(entity_ids) selected.referenced.update(entity_ids) if not selector.device_ids and not selector.area_ids: return selected ent_reg = entity_registry.async_get(hass) dev_reg = device_registry.async_get(hass) area_reg = area_registry.async_get(hass) for device_id in selector.device_ids: if device_id not in dev_reg.devices: selected.missing_devices.add(device_id) for area_id in selector.area_ids: if area_id not in area_reg.areas: selected.missing_areas.add(area_id) # Find devices for this area selected.referenced_devices.update(selector.device_ids) for device_entry in dev_reg.devices.values(): if device_entry.area_id in selector.area_ids: selected.referenced_devices.add(device_entry.id) if not selector.area_ids and not selected.referenced_devices: return selected for ent_entry in ent_reg.entities.values(): # Do not add config or diagnostic entities referenced by areas or devices if ent_entry.entity_category in ( ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_DIAGNOSTIC, ): continue if ( # when area matches the target area ent_entry.area_id in selector.area_ids # when device matches a referenced devices with no explicitly set area or (not ent_entry.area_id and ent_entry.device_id in selected.referenced_devices) # when device matches target device or ent_entry.device_id in selector.device_ids): selected.indirectly_referenced.add(ent_entry.entity_id) return selected
def websocket_list_areas(hass, connection, msg): """Handle list areas command.""" registry = async_get(hass) connection.send_result( msg["id"], [_entry_dict(entry) for entry in registry.async_list_areas()], )
async def test_ws_api(hass, hass_ws_client): """Test WS API.""" assert await async_setup_component(hass, "search", {}) area_reg = ar.async_get(hass) device_reg = dr.async_get(hass) kitchen_area = area_reg.async_create("Kitchen") hue_config_entry = MockConfigEntry(domain="hue") hue_config_entry.add_to_hass(hass) hue_device = device_reg.async_get_or_create( config_entry_id=hue_config_entry.entry_id, name="Light Strip", identifiers=({"hue", "hue-1"}), ) device_reg.async_update_device(hue_device.id, area_id=kitchen_area.id) client = await hass_ws_client(hass) await client.send_json({ "id": 1, "type": "search/related", "item_type": "device", "item_id": hue_device.id, }) response = await client.receive_json() assert response["success"] assert response["result"] == { "config_entry": [hue_config_entry.entry_id], "area": [kitchen_area.id], }
def resolve_area_entities( hass: HomeAssistantType, area_id_or_name: str) -> dict[str, entity_registry.RegistryEntry]: """Get a listing of al entities in a given area""" area_reg = area_registry.async_get(hass) area = area_reg.async_get_area(area_id_or_name) if area is None: area = area_reg.async_get_area_by_name(str(area_id_or_name)) if area is None: raise SensorConfigurationError( f"No area with id or name '{area_id_or_name}' found in your HA instance" ) area_id = area.id entity_reg = entity_registry.async_get(hass) entities = entity_registry.async_entries_for_area(entity_reg, area_id) device_reg = device_registry.async_get(hass) # We also need to add entities tied to a device in the area that don't themselves # have an area specified since they inherit the area from the device. entities.extend([ entity for device in device_registry.async_entries_for_area( device_reg, area_id) for entity in entity_registry.async_entries_for_device( entity_reg, device.id) if entity.area_id is None ]) return { entity.entity_id: entity for entity in entities if entity.domain == LIGHT_DOMAIN }
async def test_onboarding_user(hass, hass_storage, hass_client_no_auth): """Test creating a new user.""" assert await async_setup_component(hass, "person", {}) assert await async_setup_component(hass, "onboarding", {}) await hass.async_block_till_done() cur_users = len(await hass.auth.async_get_users()) client = await hass_client_no_auth() resp = await client.post( "/api/onboarding/users", json={ "client_id": CLIENT_ID, "name": "Test Name", "username": "******", "password": "******", "language": "en", }, ) assert resp.status == 200 assert const.STEP_USER in hass_storage[const.DOMAIN]["data"]["done"] data = await resp.json() assert "auth_code" in data users = await hass.auth.async_get_users() assert len(await hass.auth.async_get_users()) == cur_users + 1 user = next((user for user in users if user.name == "Test Name"), None) assert user is not None assert len(user.credentials) == 1 assert user.credentials[0].data["username"] == "test-user" assert len(hass.data["person"][1].async_items()) == 1 # Validate refresh token 1 resp = await client.post( "/auth/token", data={ "client_id": CLIENT_ID, "grant_type": "authorization_code", "code": data["auth_code"], }, ) assert resp.status == 200 tokens = await resp.json() assert (await hass.auth.async_validate_access_token(tokens["access_token"]) is not None) # Validate created areas area_registry = ar.async_get(hass) assert len(area_registry.areas) == 3 assert sorted(area.name for area in area_registry.async_list_areas()) == [ "Bedroom", "Kitchen", "Living Room", ]
def _get_registry_entries( hass: HomeAssistant, entity_id: str ) -> tuple[device_registry.DeviceEntry, area_registry.AreaEntry]: """Get registry entries.""" ent_reg = entity_registry.async_get(hass) dev_reg = device_registry.async_get(hass) area_reg = area_registry.async_get(hass) if (entity_entry := ent_reg.async_get(entity_id)) and entity_entry.device_id: device_entry = dev_reg.devices.get(entity_entry.device_id)
def websocket_delete_area(hass, connection, msg): """Delete area command.""" registry = async_get(hass) try: registry.async_delete(msg["area_id"]) except KeyError: connection.send_error(msg["id"], "invalid_info", "Area ID doesn't exist") else: connection.send_message( websocket_api.result_message(msg["id"], "success"))
def websocket_update_area(hass, connection, msg): """Handle update area websocket command.""" registry = async_get(hass) data = dict(msg) data.pop("type") data.pop("id") try: entry = registry.async_update(**data) except ValueError as err: connection.send_error(msg["id"], "invalid_info", str(err)) else: connection.send_result(msg["id"], _entry_dict(entry))
async def post(self, request, data): """Handle user creation, area creation.""" hass = request.app["hass"] async with self._lock: if self._async_is_done(): return self.json_message("User step already done", HTTPStatus.FORBIDDEN) provider = _async_get_hass_provider(hass) await provider.async_initialize() user = await hass.auth.async_create_user( data["name"], group_ids=[GROUP_ID_ADMIN]) await hass.async_add_executor_job(provider.data.add_auth, data["username"], data["password"]) credentials = await provider.async_get_or_create_credentials( {"username": data["username"]}) await provider.data.async_save() await hass.auth.async_link_user(user, credentials) if "person" in hass.config.components: await person.async_create_person(hass, data["name"], user_id=user.id) # Create default areas using the users supplied language. translations = await async_get_translations( hass, data["language"], "area", {DOMAIN}) area_registry = ar.async_get(hass) for area in DEFAULT_AREAS: name = translations[f"component.onboarding.area.{area}"] # Guard because area might have been created by an automatically # set up integration. if not area_registry.async_get_area_by_name(name): area_registry.async_create(name) await self._async_mark_done(hass) # Return authorization code for fetching tokens and connect # during onboarding. # pylint: disable=import-outside-toplevel from homeassistant.components.auth import create_auth_code auth_code = create_auth_code(hass, data["client_id"], credentials) return self.json({"auth_code": auth_code})
async def async_extract_referenced_entity_ids( hass: HomeAssistantType, service_call: ha.ServiceCall, expand_group: bool = True) -> SelectedEntities: """Extract referenced entity IDs from a service call.""" selector = ServiceTargetSelector(service_call) selected = SelectedEntities() if not selector.has_any_selector: return selected entity_ids = selector.entity_ids if expand_group: entity_ids = hass.components.group.expand_entity_ids(entity_ids) selected.referenced.update(entity_ids) if not selector.device_ids and not selector.area_ids: return selected ent_reg = entity_registry.async_get(hass) dev_reg = device_registry.async_get(hass) area_reg = area_registry.async_get(hass) for device_id in selector.device_ids: if device_id not in dev_reg.devices: selected.missing_devices.add(device_id) for area_id in selector.area_ids: if area_id not in area_reg.areas: selected.missing_areas.add(area_id) # Find devices for this area selected.referenced_devices.update(selector.device_ids) for device_entry in dev_reg.devices.values(): if device_entry.area_id in selector.area_ids: selected.referenced_devices.add(device_entry.id) if not selector.area_ids and not selected.referenced_devices: return selected for ent_entry in ent_reg.entities.values(): if ent_entry.area_id in selector.area_ids or ( not ent_entry.area_id and ent_entry.device_id in selected.referenced_devices): selected.indirectly_referenced.add(ent_entry.entity_id) return selected
async def test_loading_area_from_storage(hass, hass_storage): """Test loading stored areas on start.""" hass_storage[area_registry.STORAGE_KEY] = { "version": area_registry.STORAGE_VERSION, "data": { "areas": [{ "id": "12345A", "name": "mock" }] }, } await area_registry.async_load(hass) registry = area_registry.async_get(hass) assert len(registry.areas) == 1
async def sync_areas_with_loxone(data={}): create_areas = data.get(ATTR_AREA_CREATE, DEFAULT) if create_areas not in [True, False]: create_areas = False lox_items = [] er_registry = er.async_get(hass) ar_registry = ar.async_get(hass) for id, entry in er_registry.entities.items(): if entry.platform == DOMAIN: state = hass.states.get(entry.entity_id) if hasattr(state, "attributes") and "room" in state.attributes: area = ar_registry.async_get_area_by_name( state.attributes["room"]) if area is None and create_areas: area = ar_registry.async_get_or_create( state.attributes["room"]) if area and entry.area_id is None: lox_items.append((entry.entity_id, area.id)) for _ in lox_items: er_registry.async_update_entity(_[0], area_id=_[1])
async def test_existing_node_not_replaced_when_not_ready( hass, zp3111, zp3111_not_ready_state, zp3111_state, client, integration): """Test when a node added event with a non-ready node is received. The existing node should not be replaced, and no customization should be lost. """ dev_reg = dr.async_get(hass) er_reg = er.async_get(hass) kitchen_area = ar.async_get(hass).async_create("Kitchen") device_id = f"{client.driver.controller.home_id}-{zp3111.node_id}" device_id_ext = (f"{device_id}-{zp3111.manufacturer_id}:" f"{zp3111.product_type}:{zp3111.product_id}") device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)}) assert device assert device.name == "4-in-1 Sensor" assert not device.name_by_user assert device.manufacturer == "Vision Security" assert device.model == "ZP3111-5" assert device.sw_version == "5.1" assert not device.area_id assert device == dev_reg.async_get_device(identifiers={(DOMAIN, device_id_ext)}) motion_entity = "binary_sensor.4_in_1_sensor_home_security_motion_detection" state = hass.states.get(motion_entity) assert state assert state.name == "4-in-1 Sensor: Home Security - Motion detection" dev_reg.async_update_device(device.id, name_by_user="******", area_id=kitchen_area.id) custom_device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)}) assert custom_device assert custom_device.name == "4-in-1 Sensor" assert custom_device.name_by_user == "Custom Device Name" assert custom_device.manufacturer == "Vision Security" assert custom_device.model == "ZP3111-5" assert device.sw_version == "5.1" assert custom_device.area_id == kitchen_area.id assert custom_device == dev_reg.async_get_device( identifiers={(DOMAIN, device_id_ext)}) custom_entity = "binary_sensor.custom_motion_sensor" er_reg.async_update_entity(motion_entity, new_entity_id=custom_entity, name="Custom Entity Name") await hass.async_block_till_done() state = hass.states.get(custom_entity) assert state assert state.name == "Custom Entity Name" assert not hass.states.get(motion_entity) node_state = deepcopy(zp3111_not_ready_state) node_state["isSecure"] = False event = Event( type="node added", data={ "source": "controller", "event": "node added", "node": node_state, "result": {}, }, ) client.driver.receive_event(event) await hass.async_block_till_done() device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)}) assert device assert device == dev_reg.async_get_device(identifiers={(DOMAIN, device_id_ext)}) assert device.id == custom_device.id assert device.identifiers == custom_device.identifiers assert device.name == f"Node {zp3111.node_id}" assert device.name_by_user == "Custom Device Name" assert not device.manufacturer assert not device.model assert not device.sw_version assert device.area_id == kitchen_area.id state = hass.states.get(custom_entity) assert state assert state.name == "Custom Entity Name" event = Event( type="ready", data={ "source": "node", "event": "ready", "nodeId": zp3111_state["nodeId"], "nodeState": deepcopy(zp3111_state), }, ) client.driver.receive_event(event) await hass.async_block_till_done() device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)}) assert device assert device == dev_reg.async_get_device(identifiers={(DOMAIN, device_id_ext)}) assert device.id == custom_device.id assert device.identifiers == custom_device.identifiers assert device.name == "4-in-1 Sensor" assert device.name_by_user == "Custom Device Name" assert device.manufacturer == "Vision Security" assert device.model == "ZP3111-5" assert device.area_id == kitchen_area.id assert device.sw_version == "5.1" state = hass.states.get(custom_entity) assert state assert state.state != STATE_UNAVAILABLE assert state.name == "Custom Entity Name"
async def test_search(hass): """Test that search works.""" area_reg = ar.async_get(hass) device_reg = dr.async_get(hass) entity_reg = er.async_get(hass) living_room_area = area_reg.async_create("Living Room") # Light strip with 2 lights. wled_config_entry = MockConfigEntry(domain="wled") wled_config_entry.add_to_hass(hass) wled_device = device_reg.async_get_or_create( config_entry_id=wled_config_entry.entry_id, name="Light Strip", identifiers=({"wled", "wled-1"}), ) device_reg.async_update_device(wled_device.id, area_id=living_room_area.id) wled_segment_1_entity = entity_reg.async_get_or_create( "light", "wled", "wled-1-seg-1", suggested_object_id="wled segment 1", config_entry=wled_config_entry, device_id=wled_device.id, ) wled_segment_2_entity = entity_reg.async_get_or_create( "light", "wled", "wled-1-seg-2", suggested_object_id="wled segment 2", config_entry=wled_config_entry, device_id=wled_device.id, ) # Non related info. kitchen_area = area_reg.async_create("Kitchen") hue_config_entry = MockConfigEntry(domain="hue") hue_config_entry.add_to_hass(hass) hue_device = device_reg.async_get_or_create( config_entry_id=hue_config_entry.entry_id, name="Light Strip", identifiers=({"hue", "hue-1"}), ) device_reg.async_update_device(hue_device.id, area_id=kitchen_area.id) hue_segment_1_entity = entity_reg.async_get_or_create( "light", "hue", "hue-1-seg-1", suggested_object_id="hue segment 1", config_entry=hue_config_entry, device_id=hue_device.id, ) hue_segment_2_entity = entity_reg.async_get_or_create( "light", "hue", "hue-1-seg-2", suggested_object_id="hue segment 2", config_entry=hue_config_entry, device_id=hue_device.id, ) await async_setup_component( hass, "group", { "group": { "wled": { "name": "wled", "entities": [ wled_segment_1_entity.entity_id, wled_segment_2_entity.entity_id, ], }, "hue": { "name": "hue", "entities": [ hue_segment_1_entity.entity_id, hue_segment_2_entity.entity_id, ], }, "wled_hue": { "name": "wled and hue", "entities": [ wled_segment_1_entity.entity_id, wled_segment_2_entity.entity_id, hue_segment_1_entity.entity_id, hue_segment_2_entity.entity_id, ], }, } }, ) await async_setup_component( hass, "scene", { "scene": [ { "name": "scene_wled_seg_1", "entities": { wled_segment_1_entity.entity_id: "on" }, }, { "name": "scene_hue_seg_1", "entities": { hue_segment_1_entity.entity_id: "on" }, }, { "name": "scene_wled_hue", "entities": { wled_segment_1_entity.entity_id: "on", wled_segment_2_entity.entity_id: "on", hue_segment_1_entity.entity_id: "on", hue_segment_2_entity.entity_id: "on", }, }, ] }, ) await async_setup_component( hass, "script", { "script": { "wled": { "sequence": [ { "service": "test.script", "data": { "entity_id": wled_segment_1_entity.entity_id }, }, ] }, "hue": { "sequence": [ { "service": "test.script", "data": { "entity_id": hue_segment_1_entity.entity_id }, }, ] }, } }, ) assert await async_setup_component( hass, "automation", { "automation": [ { "alias": "wled_entity", "trigger": { "platform": "template", "value_template": "true" }, "action": [ { "service": "test.script", "data": { "entity_id": wled_segment_1_entity.entity_id }, }, ], }, { "alias": "wled_device", "trigger": { "platform": "template", "value_template": "true" }, "action": [ { "domain": "light", "device_id": wled_device.id, "entity_id": wled_segment_1_entity.entity_id, "type": "turn_on", }, ], }, ] }, ) # Explore the graph from every node and make sure we find the same results expected = { "config_entry": {wled_config_entry.entry_id}, "area": {living_room_area.id}, "device": {wled_device.id}, "entity": {wled_segment_1_entity.entity_id, wled_segment_2_entity.entity_id}, "scene": {"scene.scene_wled_seg_1", "scene.scene_wled_hue"}, "group": {"group.wled", "group.wled_hue"}, "script": {"script.wled"}, "automation": {"automation.wled_entity", "automation.wled_device"}, } for search_type, search_id in ( ("config_entry", wled_config_entry.entry_id), ("area", living_room_area.id), ("device", wled_device.id), ("entity", wled_segment_1_entity.entity_id), ("entity", wled_segment_2_entity.entity_id), ("scene", "scene.scene_wled_seg_1"), ("group", "group.wled"), ("script", "script.wled"), ("automation", "automation.wled_entity"), ("automation", "automation.wled_device"), ): searcher = search.Searcher(hass, device_reg, entity_reg) results = searcher.async_search(search_type, search_id) # Add the item we searched for, it's omitted from results results.setdefault(search_type, set()).add(search_id) assert (results == expected ), f"Results for {search_type}/{search_id} do not match up" # For combined things, needs to return everything. expected_combined = { "config_entry": {wled_config_entry.entry_id, hue_config_entry.entry_id}, "area": {living_room_area.id, kitchen_area.id}, "device": {wled_device.id, hue_device.id}, "entity": { wled_segment_1_entity.entity_id, wled_segment_2_entity.entity_id, hue_segment_1_entity.entity_id, hue_segment_2_entity.entity_id, }, "scene": { "scene.scene_wled_seg_1", "scene.scene_hue_seg_1", "scene.scene_wled_hue", }, "group": {"group.wled", "group.hue", "group.wled_hue"}, "script": {"script.wled", "script.hue"}, "automation": {"automation.wled_entity", "automation.wled_device"}, } for search_type, search_id in ( ("scene", "scene.scene_wled_hue"), ("group", "group.wled_hue"), ): searcher = search.Searcher(hass, device_reg, entity_reg) results = searcher.async_search(search_type, search_id) # Add the item we searched for, it's omitted from results results.setdefault(search_type, set()).add(search_id) assert (results == expected_combined ), f"Results for {search_type}/{search_id} do not match up" for search_type, search_id in ( ("entity", "automation.non_existing"), ("entity", "scene.non_existing"), ("entity", "group.non_existing"), ("entity", "script.non_existing"), ("entity", "light.non_existing"), ("area", "non_existing"), ("config_entry", "non_existing"), ("device", "non_existing"), ("group", "group.non_existing"), ("scene", "scene.non_existing"), ("script", "script.non_existing"), ("automation", "automation.non_existing"), ): searcher = search.Searcher(hass, device_reg, entity_reg) assert searcher.async_search(search_type, search_id) == {}
async def test_area_lookup(hass): """Test area based lookup.""" area_reg = ar.async_get(hass) device_reg = dr.async_get(hass) entity_reg = er.async_get(hass) living_room_area = area_reg.async_create("Living Room") await async_setup_component( hass, "script", { "script": { "wled": { "sequence": [ { "service": "light.turn_on", "target": { "area_id": living_room_area.id }, }, ] }, } }, ) assert await async_setup_component( hass, "automation", { "automation": [ { "alias": "area_turn_on", "trigger": { "platform": "template", "value_template": "true" }, "action": [ { "service": "light.turn_on", "data": { "area_id": living_room_area.id, }, }, ], }, ] }, ) searcher = search.Searcher(hass, device_reg, entity_reg) assert searcher.async_search("area", living_room_area.id) == { "script": {"script.wled"}, "automation": {"automation.area_turn_on"}, } searcher = search.Searcher(hass, device_reg, entity_reg) assert searcher.async_search("automation", "automation.area_turn_on") == { "area": {living_room_area.id}, }