Skip to content

Commit

Permalink
Add support for entity aliases (close dext0r#433)
Browse files Browse the repository at this point in the history
  • Loading branch information
dext0r committed Feb 8, 2023
1 parent 2d2648f commit 893970c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 56 deletions.
50 changes: 37 additions & 13 deletions custom_components/yandex_smart_home/entity.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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."""

Expand Down Expand Up @@ -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': [],
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/config/entity.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
## Имя и комната { id=name-room }
> Параметры: `name` и `room`
Название и комната устройства, имеются [ограничения](../quirks.md#naming) по длине и возможным символам
Название и комната устройства, имеются [ограничения](../quirks.md#naming) по длине и возможным символам. Можно так же задавать [через интерфейс](../quirks.md#naming).

!!! example "Пример"
```yaml
Expand Down
10 changes: 5 additions & 5 deletions docs/quirks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` для скриптов
Expand All @@ -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) или Дом с Алисой **перед** добавлением устройств:
Expand Down
75 changes: 38 additions & 37 deletions tests/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -198,36 +198,25 @@ 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(
manufacturer='Acme Inc.',
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'
Expand All @@ -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',
Expand All @@ -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))
Expand Down

0 comments on commit 893970c

Please sign in to comment.