Add Imou collection point select entity (#181251)

Co-authored-by: Erwin Douna <e.douna@gmail.com>
This commit is contained in:
Imou-OpenPlatform
2026-09-04 09:58:03 +02:00
committed by GitHub
co-authored by Erwin Douna
parent f6fe0d0aaf
commit da29e2de4a
6 changed files with 113 additions and 7 deletions
+3
View File
@@ -15,6 +15,9 @@
}
},
"select": {
"collection_point": {
"default": "mdi:crosshairs-gps"
},
"device_volume": {
"default": "mdi:volume-high",
"state": {
+5
View File
@@ -3,6 +3,7 @@
from typing import override
from pyimouapi.const import (
PARAM_COLLECTION_POINT,
PARAM_CURRENT_OPTION,
PARAM_DEVICE_VOLUME,
PARAM_NIGHT_VISION_MODE,
@@ -33,6 +34,10 @@ SELECT_TYPES: tuple[SelectEntityDescription, ...] = (
entity_category=EntityCategory.CONFIG,
translation_key=PARAM_NIGHT_VISION_MODE,
),
SelectEntityDescription(
key=PARAM_COLLECTION_POINT,
translation_key=PARAM_COLLECTION_POINT,
),
)
@@ -63,6 +63,12 @@
}
},
"select": {
"collection_point": {
"name": "Go to collection point",
"state": {
"select_collection_point": "Select a collection point…"
}
},
"device_volume": {
"name": "Volume",
"state": {
+10
View File
@@ -2,6 +2,8 @@
from pyimouapi.const import (
PARAM_BATTERY,
PARAM_COLLECTION_POINT,
PARAM_COLLECTION_POINT_PROMPT,
PARAM_CURRENT_OPTION,
PARAM_DEVICE_VOLUME,
PARAM_MOTION_DETECT,
@@ -63,6 +65,14 @@ DEFAULT_SELECTS = {
PARAM_CURRENT_OPTION: "medium",
PARAM_OPTIONS: ["mute", "low", "medium", "high"],
},
PARAM_COLLECTION_POINT: {
PARAM_CURRENT_OPTION: PARAM_COLLECTION_POINT_PROMPT,
PARAM_OPTIONS: [
PARAM_COLLECTION_POINT_PROMPT,
"Front door",
"Back yard",
],
},
}
DEFAULT_SWITCHES = {
@@ -1,4 +1,65 @@
# serializer version: 1
# name: test_select_entities_snapshot[select_mock_devices-platforms0][select.device_1_go_to_collection_point-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'select_collection_point',
'Front door',
'Back yard',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.device_1_go_to_collection_point',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Go to collection point',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Go to collection point',
'platform': 'imou',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'collection_point',
'unique_id': 'd1$collection_point',
'unit_of_measurement': None,
})
# ---
# name: test_select_entities_snapshot[select_mock_devices-platforms0][select.device_1_go_to_collection_point-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Device 1 Go to collection point',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'select_collection_point',
'Front door',
'Back yard',
]),
}),
'context': <ANY>,
'entity_id': 'select.device_1_go_to_collection_point',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'select_collection_point',
})
# ---
# name: test_select_entities_snapshot[select_mock_devices-platforms0][select.device_1_night_vision_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
+28 -7
View File
@@ -4,6 +4,8 @@ from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
from pyimouapi.const import (
PARAM_COLLECTION_POINT,
PARAM_COLLECTION_POINT_PROMPT,
PARAM_CURRENT_OPTION,
PARAM_DEVICE_VOLUME,
PARAM_NIGHT_VISION_MODE,
@@ -88,40 +90,59 @@ async def test_setup_ignores_unknown_select_types(
@pytest.mark.parametrize("platforms", [[Platform.SELECT]], indirect=True)
@pytest.mark.parametrize("imou_mock_devices", [select_mock_devices], indirect=True)
@pytest.mark.parametrize(
("unique_id", "option", "select_type", "expected_state"),
[
pytest.param(
"d1$device_volume", "high", PARAM_DEVICE_VOLUME, "high", id="volume"
),
pytest.param(
f"d1${PARAM_COLLECTION_POINT}",
"Front door",
PARAM_COLLECTION_POINT,
PARAM_COLLECTION_POINT_PROMPT,
id="collection_point",
),
],
)
@pytest.mark.usefixtures("init_integration")
async def test_select_option_via_domain_service(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_config_entry: MockConfigEntry,
mock_imou_ha_device_manager: MagicMock,
unique_id: str,
option: str,
select_type: str,
expected_state: str,
) -> None:
"""Selecting an option calls the vendor library through the coordinator."""
async def _side_effect(device: ImouHaDevice, select_type: str, option: str) -> None:
device.selects[select_type][PARAM_CURRENT_OPTION] = option
device.selects[select_type][PARAM_CURRENT_OPTION] = expected_state
mock_imou_ha_device_manager.async_select_option.side_effect = _side_effect
volume_entry = next(
select_entry = next(
entry
for entry in er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
if entry.unique_id == "d1$device_volume"
if entry.unique_id == unique_id
)
await hass.services.async_call(
SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
{ATTR_ENTITY_ID: volume_entry.entity_id, ATTR_OPTION: "high"},
{ATTR_ENTITY_ID: select_entry.entity_id, ATTR_OPTION: option},
blocking=True,
)
mock_imou_ha_device_manager.async_select_option.assert_awaited_once()
call = mock_imou_ha_device_manager.async_select_option.await_args
assert call is not None
assert call.args[1] == PARAM_DEVICE_VOLUME
assert call.args[2] == "high"
assert hass.states.get(volume_entry.entity_id).state == "high"
assert call.args[1] == select_type
assert call.args[2] == option
assert hass.states.get(select_entry.entity_id).state == expected_state
@pytest.mark.parametrize("platforms", [[Platform.SELECT]], indirect=True)