diff --git a/custom_components/yandex_smart_home/entity.py b/custom_components/yandex_smart_home/entity.py index 5aa195b2..d9ba6ce6 100644 --- a/custom_components/yandex_smart_home/entity.py +++ b/custom_components/yandex_smart_home/entity.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from typing import Any from homeassistant.const import ATTR_DEVICE_CLASS, CLOUD_NEVER_EXPOSED_ENTITIES, CONF_NAME, STATE_UNAVAILABLE @@ -28,6 +29,12 @@ from .prop_event import EventProperty +def _alias_priority(text: str) -> (int, str): + if re.search('[а-яё]', text, flags=re.IGNORECASE): + return 0, text + return 1, text + + class YandexEntity: """Adaptation of Entity expressed in Yandex's terms.""" @@ -131,13 +138,10 @@ async def devices_serialize(self, ent_reg: EntityRegistry, dev_reg: DeviceRegist if not self.capabilities() and not self.properties(): return None - entity_config = self.config.get_entity_config(self.entity_id) - name = (entity_config.get(CONF_NAME) or self.state.name).strip() or self.entity_id entity_entry, device_entry = await self._get_entity_and_device(ent_reg, dev_reg) - device = { 'id': self.entity_id, - 'name': name, + 'name': self._get_name(entity_entry).strip(), 'type': self.yandex_device_type, 'capabilities': [], 'properties': [], @@ -146,13 +150,8 @@ async def devices_serialize(self, ent_reg: EntityRegistry, dev_reg: DeviceRegist }, } - room = entity_config.get(CONF_ROOM) - if room: - device['room'] = room - else: - area = await self._get_area(entity_entry, device_entry, area_reg) - if area and area.name: - device['room'] = area.name + if room := self._get_room(entity_entry, device_entry, area_reg): + device['room'] = room.strip() if device_entry: if device_entry.manufacturer: @@ -239,9 +238,34 @@ async def _get_entity_and_device(self, ent_reg: EntityRegistry, dev_reg: DeviceR device_entry = dev_reg.devices.get(entity_entry.device_id) return entity_entry, device_entry + def _get_name(self, entity_entry: RegistryEntry | None) -> str: + entity_config = self.config.get_entity_config(self.entity_id) + + if CONF_NAME in entity_config: + return entity_config[CONF_NAME] + + if entity_entry and entity_entry.aliases: + return sorted(entity_entry.aliases, key=_alias_priority)[0] + + return self.state.name or self.entity_id + + def _get_room(self, entity_entry: RegistryEntry | None, device_entry: DeviceEntry | None, + area_reg: AreaRegistry) -> str | None: + entity_config = self.config.get_entity_config(self.entity_id) + + if CONF_ROOM in entity_config: + return entity_config[CONF_ROOM] + + area = self._get_area(entity_entry, device_entry, area_reg) + if area: + if area.aliases: + return sorted(area.aliases, key=_alias_priority)[0] + + return area.name + @staticmethod - async def _get_area(entity_entry: RegistryEntry | None, device_entry: DeviceEntry | None, - area_reg: AreaRegistry) -> AreaEntry | None: + def _get_area(entity_entry: RegistryEntry | None, device_entry: DeviceEntry | None, + area_reg: AreaRegistry) -> AreaEntry | None: """Calculate the area for an entity.""" if entity_entry and entity_entry.area_id: area_id = entity_entry.area_id diff --git a/docs/config/entity.md b/docs/config/entity.md index 3d4c00c1..b8534624 100644 --- a/docs/config/entity.md +++ b/docs/config/entity.md @@ -7,7 +7,7 @@ ## Имя и комната { id=name-room } > Параметры: `name` и `room` -Название и комната устройства, имеются [ограничения](../quirks.md#naming) по длине и возможным символам +Название и комната устройства, имеются [ограничения](../quirks.md#naming) по длине и возможным символам. Можно так же задавать [через интерфейс](../quirks.md#naming). !!! example "Пример" ```yaml diff --git a/docs/quirks.md b/docs/quirks.md index 2b29f053..de130895 100644 --- a/docs/quirks.md +++ b/docs/quirks.md @@ -17,7 +17,7 @@ Есть несколько способов сделать это: -1. На странице `Настройки` --> `Устройства и службы` --> [`Объекты`](https://my.home-assistant.io/redirect/entities/) через поле `Название` +1. На странице `Настройки` --> `Устройства и службы` --> [`Объекты`](https://my.home-assistant.io/redirect/entities/) через поле `Название` или `Альтернативные названия` (имеют больший приоритет) 2. Через атрибут `friendly_name` в [customization.yaml](https://www.home-assistant.io/docs/configuration/customizing-devices/) 3. Через параметр `name` в [параметрах устройств](config/entity.md) 4. Через параметр `alias` для скриптов @@ -29,10 +29,10 @@ Способы добавить устройство в комнату: -1. Создайте нужные комнаты на странице `Настройки` > `Пространства и зоны` > [`Пространства`](https://my.home-assistant.io/redirect/areas/) -2. Выберите комнату в свойствах объекта на cтранице `Настройки` --> `Устройства и службы` --> [`Объекты`](https://my.home-assistant.io/redirect/entities/), -или в свойствах [`Устройства`](https://my.home-assistant.io/redirect/devices/), в которое включен объект -3. Через параметр `room` в [параметрах устройств](config/entity.md) +1. Выберите пространство в свойствах объекта на cтранице `Настройки` --> `Устройства и службы` --> [`Объекты`](https://my.home-assistant.io/redirect/entities/), + или в свойствах [`Устройства`](https://my.home-assistant.io/redirect/devices/), в которое включен объект. + Если у пространства задано альтернативное название, то оно будет использовано вместо основного. +2. Через параметр `room` в [параметрах устройств](config/entity.md) !!! danger "Важно!" Комнаты в УДЯ нужно создать **вручную** через [квазар](quasar.md) или Дом с Алисой **перед** добавлением устройств: diff --git a/tests/test_entity.py b/tests/test_entity.py index 80099586..7fc7a27c 100644 --- a/tests/test_entity.py +++ b/tests/test_entity.py @@ -179,7 +179,7 @@ async def test_yandex_entity_properties(hass): ] -async def test_yandex_entity_devices_serialize_state(hass, registries): +async def test_yandex_entity_serialize_state_type(hass, registries): ent_reg, dev_reg, area_reg = registries.entity, registries.device, registries.area entity_unavailable = YandexEntity(hass, BASIC_CONFIG, State('switch.test', STATE_UNAVAILABLE)) @@ -198,23 +198,17 @@ async def test_yandex_entity_devices_serialize_state(hass, registries): config = MockConfig(entity_config={ 'switch.test_1': { - CONF_NAME: 'Тест', CONF_TYPE: const.TYPE_OPENABLE, - CONF_ROOM: 'Кухня' } }) entity = YandexEntity(hass, config, State('switch.test_1', STATE_ON)) s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) assert s['id'] == 'switch.test_1' - assert s['name'] == 'Тест' - assert s['room'] == 'Кухня' assert s['type'] == const.TYPE_OPENABLE -async def test_yandex_entity_devices_serialize_device(hass, registries): +async def test_yandex_entity_serialize_device_info(hass, registries): ent_reg, dev_reg, area_reg = registries.entity, registries.device, registries.area - area_kitchen = area_reg.async_get_or_create('Кухня') - area_closet = area_reg.async_get_or_create('Кладовка') state = State('switch.test_1', STATE_ON) device = dev_reg.async_get_or_create( @@ -222,12 +216,7 @@ async def test_yandex_entity_devices_serialize_device(hass, registries): identifiers={'test_1'}, config_entry_id='test_1', ) - ent_reg.async_get_or_create( - 'switch', - 'test', - '1', - device_id=device.id, - ) + ent_reg.async_get_or_create('switch', 'test', '1', device_id=device.id) entity = YandexEntity(hass, BASIC_CONFIG, state) s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) assert s['id'] == 'switch.test_1' @@ -241,7 +230,6 @@ async def test_yandex_entity_devices_serialize_device(hass, registries): identifiers={'test_2'}, config_entry_id='test_2', ) - dev_reg.async_update_device(device.id, area_id=area_closet.id) ent_reg.async_get_or_create( 'switch', 'test', @@ -251,42 +239,55 @@ async def test_yandex_entity_devices_serialize_device(hass, registries): entity = YandexEntity(hass, BASIC_CONFIG, state) s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) assert s['id'] == 'switch.test_2' - assert s['room'] == 'Кладовка' assert s['device_info'] == { 'manufacturer': 'Acme Inc.', 'model': 'Ultra Switch | switch.test_2', 'sw_version': '57' } + +async def test_yandex_entity_serialize_name_room(hass, registries): + ent_reg, dev_reg, area_reg = registries.entity, registries.device, registries.area + area_room = area_reg.async_create('Room') + area_kitchen = area_reg.async_create('Kitchen') + area_closet = area_reg.async_create('Closet', aliases=['Test', '1', 'Кладовка', 'ббб']) + + state = State('switch.test_1', STATE_ON) + device = dev_reg.async_get_or_create(identifiers={'test_1'}, config_entry_id='test_1') + entry = ent_reg.async_get_or_create('switch', 'test', '1', device_id=device.id) + entity = YandexEntity(hass, BASIC_CONFIG, state) + s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) + assert s['id'] == 'switch.test_1' + assert s['name'] == 'test 1' + assert 'room' not in s + + dev_reg.async_update_device(device.id, area_id=area_room.id) + s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) + assert s['room'] == 'Room' + + ent_reg.async_update_entity(entry.entity_id, area_id=area_kitchen.id) + s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) + assert s['name'] == 'test 1' + assert s['room'] == 'Kitchen' + + ent_reg.async_update_entity(entry.entity_id, + area_id=area_closet.id, + aliases=['2', 'foo', 'Устройство', 'апельсин']) + s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) + assert s['name'] == 'Устройство' + assert s['room'] == 'Кладовка' + config = MockConfig(entity_config={ - 'switch.test_2': { + 'switch.test_1': { + CONF_NAME: 'Имя', CONF_ROOM: 'Комната' } }) entity = YandexEntity(hass, config, state) s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) - assert s['id'] == 'switch.test_2' + assert s['name'] == 'Имя' assert s['room'] == 'Комната' - state = State('switch.test_3', STATE_ON) - device = dev_reg.async_get_or_create( - identifiers={'test_3'}, - config_entry_id='test_3', - ) - entry = ent_reg.async_get_or_create( - 'switch', - 'test', - '3', - device_id=device.id, - ) - ent_reg.async_update_entity(entry.entity_id, area_id=area_kitchen.id) - - entity = YandexEntity(hass, BASIC_CONFIG, state) - s = await entity.devices_serialize(ent_reg, dev_reg, area_reg) - assert s['id'] == 'switch.test_3' - assert s['room'] == 'Кухня' - assert s['device_info'] == {'model': 'switch.test_3'} - async def test_yandex_entity_should_expose(hass): entity = YandexEntity(hass, BASIC_CONFIG, State('group.all_locks', STATE_ON))