Add select platform to myuplink (#118661)

This commit is contained in:
Åke Strandberg 2024-07-19 11:29:58 +02:00 committed by GitHub
parent f5f9480b5a
commit 172778053c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 304 additions and 2 deletions

View File

@ -25,6 +25,7 @@ from .coordinator import MyUplinkDataCoordinator
PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,

View File

@ -21,6 +21,9 @@ def find_matching_platform(
return Platform.SWITCH
return Platform.BINARY_SENSOR
if len(device_point.enum_values) > 0 and device_point.writable:
return Platform.SELECT
if (
description
and description.native_unit_of_measurement == "DM"

View File

@ -0,0 +1,95 @@
"""Select entity for myUplink."""
from typing import cast
from aiohttp import ClientError
from myuplink import DevicePoint
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import MyUplinkConfigEntry, MyUplinkDataCoordinator
from .entity import MyUplinkEntity
from .helpers import find_matching_platform, skip_entity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: MyUplinkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up myUplink select."""
entities: list[SelectEntity] = []
coordinator: MyUplinkDataCoordinator = config_entry.runtime_data
# Setup device point select entities
for device_id, point_data in coordinator.data.points.items():
for point_id, device_point in point_data.items():
if skip_entity(device_point.category, device_point):
continue
description = None
if find_matching_platform(device_point, description) == Platform.SELECT:
entities.append(
MyUplinkSelect(
coordinator=coordinator,
device_id=device_id,
device_point=device_point,
entity_description=description,
unique_id_suffix=point_id,
)
)
async_add_entities(entities)
class MyUplinkSelect(MyUplinkEntity, SelectEntity):
"""Representation of a myUplink select entity."""
def __init__(
self,
coordinator: MyUplinkDataCoordinator,
device_id: str,
device_point: DevicePoint,
entity_description: SelectEntityDescription | None,
unique_id_suffix: str,
) -> None:
"""Initialize the select."""
super().__init__(
coordinator=coordinator,
device_id=device_id,
unique_id_suffix=unique_id_suffix,
)
# Internal properties
self.point_id = device_point.parameter_id
self._attr_name = device_point.parameter_name
self._attr_options = [x["text"].capitalize() for x in device_point.enum_values]
self.options_map = {
str(int(x["value"])): x["text"].capitalize()
for x in device_point.enum_values
}
self.options_rev = {value: key for key, value in self.options_map.items()}
@property
def current_option(self) -> str | None:
"""Retrieve currently selected option."""
device_point = self.coordinator.data.points[self.device_id][self.point_id]
value = int(cast(int, device_point.value_t))
return self.options_map.get(str(value))
async def async_select_option(self, option: str) -> None:
"""Set the current option."""
try:
await self.coordinator.api.async_set_device_points(
self.device_id, data={self.point_id: str(self.options_rev[option])}
)
except ClientError as err:
raise HomeAssistantError(
f"Failed to set new option {self.options_rev[option]} for {self.point_id}/{self.entity_id}"
) from err
await self.coordinator.async_request_refresh()

View File

@ -951,5 +951,43 @@
],
"scaleValue": "1",
"zoneId": null
},
{
"category": "NIBEF F730 CU 3x400V",
"parameterId": "47041",
"parameterName": "comfort mode",
"parameterUnit": "",
"writable": true,
"timestamp": "2024-05-22T15:02:03+00:00",
"value": 0,
"strVal": "economy",
"smartHomeCategories": [],
"minValue": null,
"maxValue": null,
"stepValue": 1,
"enumValues": [
{
"value": "4",
"text": "smart control",
"icon": ""
},
{
"value": "0",
"text": "economy",
"icon": ""
},
{
"value": "1",
"text": "normal",
"icon": ""
},
{
"value": "2",
"text": "luxury",
"icon": ""
}
],
"scaleValue": "1",
"zoneId": null
}
]

View File

@ -3996,9 +3996,9 @@
"parameterUnit": "",
"writable": true,
"timestamp": "2024-02-14T08:36:05+00:00",
"value": 0,
"value": 0.0,
"strVal": "economy",
"smartHomeCategories": [],
"smartHomeCategories": ["test"],
"minValue": null,
"maxValue": null,
"stepValue": 1,

View File

@ -1012,6 +1012,44 @@
],
"scaleValue": "1",
"zoneId": null
},
{
"category": "NIBEF F730 CU 3x400V",
"parameterId": "47041",
"parameterName": "comfort mode",
"parameterUnit": "",
"writable": true,
"timestamp": "2024-05-22T15:02:03+00:00",
"value": 0,
"strVal": "economy",
"smartHomeCategories": [],
"minValue": null,
"maxValue": null,
"stepValue": 1,
"enumValues": [
{
"value": "4",
"text": "smart control",
"icon": ""
},
{
"value": "0",
"text": "economy",
"icon": ""
},
{
"value": "1",
"text": "normal",
"icon": ""
},
{
"value": "2",
"text": "luxury",
"icon": ""
}
],
"scaleValue": "1",
"zoneId": null
}
]
@ -2017,6 +2055,44 @@
],
"scaleValue": "1",
"zoneId": null
},
{
"category": "NIBEF F730 CU 3x400V",
"parameterId": "47041",
"parameterName": "comfort mode",
"parameterUnit": "",
"writable": true,
"timestamp": "2024-05-22T15:02:03+00:00",
"value": 0,
"strVal": "economy",
"smartHomeCategories": [],
"minValue": null,
"maxValue": null,
"stepValue": 1,
"enumValues": [
{
"value": "4",
"text": "smart control",
"icon": ""
},
{
"value": "0",
"text": "economy",
"icon": ""
},
{
"value": "1",
"text": "normal",
"icon": ""
},
{
"value": "2",
"text": "luxury",
"icon": ""
}
],
"scaleValue": "1",
"zoneId": null
}
]

View File

@ -0,0 +1,89 @@
"""Tests for myuplink select module."""
from unittest.mock import MagicMock
from aiohttp import ClientError
import pytest
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_OPTION,
SERVICE_SELECT_OPTION,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
TEST_PLATFORM = Platform.SELECT
pytestmark = pytest.mark.parametrize("platforms", [(TEST_PLATFORM,)])
ENTITY_ID = "select.gotham_city_comfort_mode"
ENTITY_FRIENDLY_NAME = "Gotham City comfort mode"
ENTITY_UID = "robin-r-1234-20240201-123456-aa-bb-cc-dd-ee-ff-47041"
async def test_select_entity(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_myuplink_client: MagicMock,
setup_platform: None,
) -> None:
"""Test that the entities are registered in the entity registry."""
entry = entity_registry.async_get(ENTITY_ID)
assert entry.unique_id == ENTITY_UID
# Test the select attributes are correct.
state = hass.states.get(ENTITY_ID)
assert state.state == "Economy"
assert state.attributes == {
"options": ["Smart control", "Economy", "Normal", "Luxury"],
"friendly_name": ENTITY_FRIENDLY_NAME,
}
async def test_selecting(
hass: HomeAssistant,
mock_myuplink_client: MagicMock,
setup_platform: None,
) -> None:
"""Test select option service."""
await hass.services.async_call(
TEST_PLATFORM,
SERVICE_SELECT_OPTION,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_OPTION: "Economy"},
blocking=True,
)
await hass.async_block_till_done()
mock_myuplink_client.async_set_device_points.assert_called_once()
# Test handling of exception from API.
mock_myuplink_client.async_set_device_points.side_effect = ClientError
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
TEST_PLATFORM,
SERVICE_SELECT_OPTION,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_OPTION: "Economy"},
blocking=True,
)
assert mock_myuplink_client.async_set_device_points.call_count == 2
@pytest.mark.parametrize(
"load_device_points_file",
["device_points_nibe_smo20.json"],
)
async def test_entity_registry_smo20(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_myuplink_client: MagicMock,
setup_platform: None,
) -> None:
"""Test that the entities are registered in the entity registry."""
entry = entity_registry.async_get("select.gotham_city_all")
assert entry.unique_id == "robin-r-1234-20240201-123456-aa-bb-cc-dd-ee-ff-47660"