Add Huawei LTE network mode select (#104614)
* Convert network mode from sensor to select for huawei_lte This also introduces the select platform to huawei_lte integration. * Move (networkmode, str) mapping to const Also, rebase on top of the current dev * Fix variable naming, initialize name * Fix wrong key for router access * Typing fixes * Adapt to current way of registering subscriptions * Simplify option management, make translatable * Make use of custom entity description * Add icon * Revert sensor formatting changes, move to another PR * Improve entity class naming * Add test * Make sure entity descriptions define a setter function --------- Co-authored-by: Teemu Rytilahti <tpr@iki.fi>
This commit is contained in:
parent
81d05acd07
commit
a29695e622
4 changed files with 190 additions and 0 deletions
|
@ -135,6 +135,7 @@ PLATFORMS = [
|
|||
Platform.DEVICE_TRACKER,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.SELECT,
|
||||
]
|
||||
|
||||
|
||||
|
|
132
homeassistant/components/huawei_lte/select.py
Normal file
132
homeassistant/components/huawei_lte/select.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
"""Support for Huawei LTE selects."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from functools import partial
|
||||
import logging
|
||||
|
||||
from huawei_lte_api.enums.net import LTEBandEnum, NetworkBandEnum, NetworkModeEnum
|
||||
|
||||
from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SelectEntity,
|
||||
SelectEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import UNDEFINED
|
||||
|
||||
from . import HuaweiLteBaseEntityWithDevice
|
||||
from .const import DOMAIN, KEY_NET_NET_MODE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class HuaweiSelectEntityMixin:
|
||||
"""Mixin for Huawei LTE select entities, to ensure required fields are set."""
|
||||
|
||||
setter_fn: Callable[[str], None]
|
||||
|
||||
|
||||
@dataclass
|
||||
class HuaweiSelectEntityDescription(SelectEntityDescription, HuaweiSelectEntityMixin):
|
||||
"""Class describing Huawei LTE select entities."""
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up from config entry."""
|
||||
router = hass.data[DOMAIN].routers[config_entry.entry_id]
|
||||
selects: list[Entity] = []
|
||||
|
||||
desc = HuaweiSelectEntityDescription(
|
||||
key=KEY_NET_NET_MODE,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:transmission-tower",
|
||||
name="Preferred network mode",
|
||||
translation_key="preferred_network_mode",
|
||||
options=[
|
||||
NetworkModeEnum.MODE_AUTO.value,
|
||||
NetworkModeEnum.MODE_4G_3G_AUTO.value,
|
||||
NetworkModeEnum.MODE_4G_2G_AUTO.value,
|
||||
NetworkModeEnum.MODE_4G_ONLY.value,
|
||||
NetworkModeEnum.MODE_3G_2G_AUTO.value,
|
||||
NetworkModeEnum.MODE_3G_ONLY.value,
|
||||
NetworkModeEnum.MODE_2G_ONLY.value,
|
||||
],
|
||||
setter_fn=partial(
|
||||
router.client.net.set_net_mode,
|
||||
LTEBandEnum.ALL,
|
||||
NetworkBandEnum.ALL,
|
||||
),
|
||||
)
|
||||
selects.append(
|
||||
HuaweiLteSelectEntity(
|
||||
router,
|
||||
entity_description=desc,
|
||||
key=desc.key,
|
||||
item="NetworkMode",
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(selects, True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class HuaweiLteSelectEntity(HuaweiLteBaseEntityWithDevice, SelectEntity):
|
||||
"""Huawei LTE select entity."""
|
||||
|
||||
entity_description: HuaweiSelectEntityDescription
|
||||
key: str
|
||||
item: str
|
||||
|
||||
_raw_state: str | None = field(default=None, init=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Initialize remaining attributes."""
|
||||
name = None
|
||||
if self.entity_description.name != UNDEFINED:
|
||||
name = self.entity_description.name
|
||||
self._attr_name = name or self.item
|
||||
|
||||
def select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
self.entity_description.setter_fn(option)
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return current option."""
|
||||
return self._raw_state
|
||||
|
||||
@property
|
||||
def _device_unique_id(self) -> str:
|
||||
return f"{self.key}.{self.item}"
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to needed data on add."""
|
||||
await super().async_added_to_hass()
|
||||
self.router.subscriptions[self.key].append(f"{SELECT_DOMAIN}/{self.item}")
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Unsubscribe from needed data on remove."""
|
||||
await super().async_will_remove_from_hass()
|
||||
self.router.subscriptions[self.key].remove(f"{SELECT_DOMAIN}/{self.item}")
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update state."""
|
||||
try:
|
||||
value = self.router.data[self.key][self.item]
|
||||
except KeyError:
|
||||
_LOGGER.debug("%s[%s] not in data", self.key, self.item)
|
||||
self._available = False
|
||||
return
|
||||
self._available = True
|
||||
self._raw_state = str(value)
|
|
@ -286,6 +286,20 @@
|
|||
"name": "SMS messages (SIM)"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"preferred_network_mode": {
|
||||
"name": "Preferred network mode",
|
||||
"state": {
|
||||
"00": "4G/3G/2G auto",
|
||||
"0302": "4G/3G auto",
|
||||
"0301": "4G/2G auto",
|
||||
"03": "4G only",
|
||||
"0201": "3G/2G auto",
|
||||
"02": "3G only",
|
||||
"01": "2G only"
|
||||
}
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"mobile_data": {
|
||||
"name": "Mobile data"
|
||||
|
|
43
tests/components/huawei_lte/test_select.py
Normal file
43
tests/components/huawei_lte/test_select.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
"""Tests for the Huawei LTE selects."""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from huawei_lte_api.enums.net import LTEBandEnum, NetworkBandEnum, NetworkModeEnum
|
||||
|
||||
from homeassistant.components.huawei_lte.const import DOMAIN
|
||||
from homeassistant.components.select import SERVICE_SELECT_OPTION
|
||||
from homeassistant.components.select.const import DOMAIN as SELECT_DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import magic_client
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
SELECT_NETWORK_MODE = "select.lte_preferred_network_mode"
|
||||
|
||||
|
||||
@patch("homeassistant.components.huawei_lte.Connection", MagicMock())
|
||||
@patch("homeassistant.components.huawei_lte.Client")
|
||||
async def test_set_net_mode(client, hass: HomeAssistant) -> None:
|
||||
"""Test setting network mode."""
|
||||
client.return_value = magic_client({})
|
||||
huawei_lte = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_URL: "http://huawei-lte.example.com"}
|
||||
)
|
||||
huawei_lte.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(huawei_lte.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: SELECT_NETWORK_MODE,
|
||||
ATTR_OPTION: NetworkModeEnum.MODE_4G_3G_AUTO.value,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
client.return_value.net.set_net_mode.assert_called_once()
|
||||
client.return_value.net.set_net_mode.assert_called_with(
|
||||
LTEBandEnum.ALL, NetworkBandEnum.ALL, NetworkModeEnum.MODE_4G_3G_AUTO.value
|
||||
)
|
Loading…
Add table
Reference in a new issue