-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -135,6 +135,7 @@ | |
Platform.DEVICE_TRACKER, | ||
Platform.SENSOR, | ||
Platform.SWITCH, | ||
Platform.SELECT, | ||
] | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |