Add Huawei LTE restart and clear traffic statistics buttons (#91967)

* Add Huawei LTE restart and clear traffic statistics buttons

Deprecate corresponding services in favour of these.

* Change to be removed service warnings to issues

* Add tests

* Update planned service remove versions
This commit is contained in:
Ville Skyttä 2023-11-23 20:35:35 +02:00 committed by GitHub
parent a1f7f899c9
commit 616f6aab76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 20 deletions

View file

@ -50,6 +50,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.typing import ConfigType
@ -57,6 +58,8 @@ from .const import (
ADMIN_SERVICES,
ALL_KEYS,
ATTR_CONFIG_ENTRY_ID,
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS,
BUTTON_KEY_RESTART,
CONF_MANUFACTURER,
CONF_UNAUTHENTICATED_MODE,
CONNECTION_TIMEOUT,
@ -127,6 +130,7 @@ SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_URL): cv.url})
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.DEVICE_TRACKER,
Platform.SENSOR,
Platform.SWITCH,
@ -524,12 +528,38 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return
if service.service == SERVICE_CLEAR_TRAFFIC_STATISTICS:
create_issue(
hass,
DOMAIN,
"service_clear_traffic_statistics_moved_to_button",
breaks_in_ha_version="2024.2.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="service_changed_to_button",
translation_placeholders={
"service": service.service,
"button": BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS,
},
)
if router.suspended:
_LOGGER.debug("%s: ignored, integration suspended", service.service)
return
result = router.client.monitoring.set_clear_traffic()
_LOGGER.debug("%s: %s", service.service, result)
elif service.service == SERVICE_REBOOT:
create_issue(
hass,
DOMAIN,
"service_reboot_moved_to_button",
breaks_in_ha_version="2024.2.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="service_changed_to_button",
translation_placeholders={
"service": service.service,
"button": BUTTON_KEY_RESTART,
},
)
if router.suspended:
_LOGGER.debug("%s: ignored, integration suspended", service.service)
return

View file

@ -0,0 +1,97 @@
"""Huawei LTE buttons."""
from __future__ import annotations
import logging
from huawei_lte_api.enums.device import ControlModeEnum
from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform
from . import HuaweiLteBaseEntityWithDevice
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up Huawei LTE buttons."""
router = hass.data[DOMAIN].routers[config_entry.entry_id]
buttons = [
ClearTrafficStatisticsButton(router),
RestartButton(router),
]
async_add_entities(buttons)
class BaseButton(HuaweiLteBaseEntityWithDevice, ButtonEntity):
"""Huawei LTE button base class."""
@property
def _device_unique_id(self) -> str:
"""Return unique ID for entity within a router."""
return f"button-{self.entity_description.key}"
async def async_update(self) -> None:
"""Update is not necessary for button entities."""
def press(self) -> None:
"""Press button."""
if self.router.suspended:
_LOGGER.debug(
"%s: ignored, integration suspended", self.entity_description.key
)
return
result = self._press()
_LOGGER.debug("%s: %s", self.entity_description.key, result)
def _press(self) -> str:
"""Invoke low level action of button press."""
raise NotImplementedError
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS = "clear_traffic_statistics"
class ClearTrafficStatisticsButton(BaseButton):
"""Huawei LTE clear traffic statistics button."""
entity_description = ButtonEntityDescription(
key=BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS,
name="Clear traffic statistics",
entity_category=EntityCategory.CONFIG,
)
def _press(self) -> str:
"""Call clear traffic statistics endpoint."""
return self.router.client.monitoring.set_clear_traffic()
BUTTON_KEY_RESTART = "restart"
class RestartButton(BaseButton):
"""Huawei LTE restart button."""
entity_description = ButtonEntityDescription(
key=BUTTON_KEY_RESTART,
name="Restart",
device_class=ButtonDeviceClass.RESTART,
entity_category=EntityCategory.CONFIG,
)
def _press(self) -> str:
"""Call restart endpoint."""
return self.router.client.device.set_control(ControlModeEnum.REBOOT)

View file

@ -79,3 +79,6 @@ ALL_KEYS = (
| SWITCH_KEYS
| {KEY_DEVICE_BASIC_INFORMATION}
)
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS = "clear_traffic_statistics"
BUTTON_KEY_RESTART = "restart"

View file

@ -279,6 +279,12 @@
}
}
},
"issues": {
"service_changed_to_button": {
"title": "Service changed to a button",
"description": "The {service} service is deprecated, use the corresponding {button} button instead."
}
},
"services": {
"clear_traffic_statistics": {
"name": "Clear traffic statistics",

View file

@ -1 +1,23 @@
"""Tests for the huawei_lte component."""
from unittest.mock import MagicMock
from huawei_lte_api.enums.cradle import ConnectionStatusEnum
def magic_client(multi_basic_settings_value: dict) -> MagicMock:
"""Mock huawei_lte.Client."""
information = MagicMock(return_value={"SerialNumber": "test-serial-number"})
check_notifications = MagicMock(return_value={"SmsStorageFull": 0})
status = MagicMock(
return_value={"ConnectionStatus": ConnectionStatusEnum.CONNECTED.value}
)
multi_basic_settings = MagicMock(return_value=multi_basic_settings_value)
wifi_feature_switch = MagicMock(return_value={"wifi24g_switch_enable": 1})
device = MagicMock(information=information)
monitoring = MagicMock(check_notifications=check_notifications, status=status)
wlan = MagicMock(
multi_basic_settings=multi_basic_settings,
wifi_feature_switch=wifi_feature_switch,
)
return MagicMock(device=device, monitoring=monitoring, wlan=wlan)

View file

@ -0,0 +1,76 @@
"""Tests for the Huawei LTE switches."""
from unittest.mock import MagicMock, patch
from huawei_lte_api.enums.device import ControlModeEnum
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.huawei_lte.const import (
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS,
BUTTON_KEY_RESTART,
DOMAIN,
SERVICE_SUSPEND_INTEGRATION,
)
from homeassistant.const import ATTR_ENTITY_ID, CONF_URL
from homeassistant.core import HomeAssistant
from . import magic_client
from tests.common import MockConfigEntry
MOCK_CONF_URL = "http://huawei-lte.example.com"
@patch("homeassistant.components.huawei_lte.Connection", MagicMock())
@patch("homeassistant.components.huawei_lte.Client", return_value=magic_client({}))
async def test_clear_traffic_statistics(client, hass: HomeAssistant) -> None:
"""Test clear traffic statistics button."""
huawei_lte = MockConfigEntry(domain=DOMAIN, data={CONF_URL: MOCK_CONF_URL})
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(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: f"button.lte_{BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS}"},
blocking=True,
)
await hass.async_block_till_done()
client.return_value.monitoring.set_clear_traffic.assert_called_once()
client.return_value.monitoring.set_clear_traffic.reset_mock()
await hass.services.async_call(
DOMAIN,
SERVICE_SUSPEND_INTEGRATION,
{CONF_URL: MOCK_CONF_URL},
blocking=True,
)
await hass.async_block_till_done()
client.return_value.monitoring.set_clear_traffic.assert_not_called()
@patch("homeassistant.components.huawei_lte.Connection", MagicMock())
@patch("homeassistant.components.huawei_lte.Client", return_value=magic_client({}))
async def test_restart(client, hass: HomeAssistant) -> None:
"""Test restart button."""
huawei_lte = MockConfigEntry(domain=DOMAIN, data={CONF_URL: MOCK_CONF_URL})
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(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: f"button.lte_{BUTTON_KEY_RESTART}"},
blocking=True,
)
await hass.async_block_till_done()
client.return_value.device.set_control.assert_called_with(ControlModeEnum.REBOOT)
client.return_value.device.set_control.reset_mock()
await hass.services.async_call(
DOMAIN,
SERVICE_SUSPEND_INTEGRATION,
{CONF_URL: MOCK_CONF_URL},
blocking=True,
)
await hass.async_block_till_done()
client.return_value.device.set_control.assert_not_called()

View file

@ -1,8 +1,6 @@
"""Tests for the Huawei LTE switches."""
from unittest.mock import MagicMock, patch
from huawei_lte_api.enums.cradle import ConnectionStatusEnum
from homeassistant.components.huawei_lte.const import DOMAIN
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
@ -13,29 +11,13 @@ from homeassistant.const import ATTR_ENTITY_ID, CONF_URL, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import magic_client
from tests.common import MockConfigEntry
SWITCH_WIFI_GUEST_NETWORK = "switch.lte_wi_fi_guest_network"
def magic_client(multi_basic_settings_value: dict) -> MagicMock:
"""Mock huawei_lte.Client."""
information = MagicMock(return_value={"SerialNumber": "test-serial-number"})
check_notifications = MagicMock(return_value={"SmsStorageFull": 0})
status = MagicMock(
return_value={"ConnectionStatus": ConnectionStatusEnum.CONNECTED.value}
)
multi_basic_settings = MagicMock(return_value=multi_basic_settings_value)
wifi_feature_switch = MagicMock(return_value={"wifi24g_switch_enable": 1})
device = MagicMock(information=information)
monitoring = MagicMock(check_notifications=check_notifications, status=status)
wlan = MagicMock(
multi_basic_settings=multi_basic_settings,
wifi_feature_switch=wifi_feature_switch,
)
return MagicMock(device=device, monitoring=monitoring, wlan=wlan)
@patch("homeassistant.components.huawei_lte.Connection", MagicMock())
@patch("homeassistant.components.huawei_lte.Client", return_value=magic_client({}))
async def test_huawei_lte_wifi_guest_network_config_entry_when_network_is_not_present(