mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
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:
parent
a1f7f899c9
commit
616f6aab76
@ -50,6 +50,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
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.service import async_register_admin_service
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
@ -57,6 +58,8 @@ from .const import (
|
|||||||
ADMIN_SERVICES,
|
ADMIN_SERVICES,
|
||||||
ALL_KEYS,
|
ALL_KEYS,
|
||||||
ATTR_CONFIG_ENTRY_ID,
|
ATTR_CONFIG_ENTRY_ID,
|
||||||
|
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS,
|
||||||
|
BUTTON_KEY_RESTART,
|
||||||
CONF_MANUFACTURER,
|
CONF_MANUFACTURER,
|
||||||
CONF_UNAUTHENTICATED_MODE,
|
CONF_UNAUTHENTICATED_MODE,
|
||||||
CONNECTION_TIMEOUT,
|
CONNECTION_TIMEOUT,
|
||||||
@ -127,6 +130,7 @@ SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_URL): cv.url})
|
|||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
|
Platform.BUTTON,
|
||||||
Platform.DEVICE_TRACKER,
|
Platform.DEVICE_TRACKER,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
@ -524,12 +528,38 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if service.service == SERVICE_CLEAR_TRAFFIC_STATISTICS:
|
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:
|
if router.suspended:
|
||||||
_LOGGER.debug("%s: ignored, integration suspended", service.service)
|
_LOGGER.debug("%s: ignored, integration suspended", service.service)
|
||||||
return
|
return
|
||||||
result = router.client.monitoring.set_clear_traffic()
|
result = router.client.monitoring.set_clear_traffic()
|
||||||
_LOGGER.debug("%s: %s", service.service, result)
|
_LOGGER.debug("%s: %s", service.service, result)
|
||||||
elif service.service == SERVICE_REBOOT:
|
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:
|
if router.suspended:
|
||||||
_LOGGER.debug("%s: ignored, integration suspended", service.service)
|
_LOGGER.debug("%s: ignored, integration suspended", service.service)
|
||||||
return
|
return
|
||||||
|
97
homeassistant/components/huawei_lte/button.py
Normal file
97
homeassistant/components/huawei_lte/button.py
Normal 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)
|
@ -79,3 +79,6 @@ ALL_KEYS = (
|
|||||||
| SWITCH_KEYS
|
| SWITCH_KEYS
|
||||||
| {KEY_DEVICE_BASIC_INFORMATION}
|
| {KEY_DEVICE_BASIC_INFORMATION}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
BUTTON_KEY_CLEAR_TRAFFIC_STATISTICS = "clear_traffic_statistics"
|
||||||
|
BUTTON_KEY_RESTART = "restart"
|
||||||
|
@ -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": {
|
"services": {
|
||||||
"clear_traffic_statistics": {
|
"clear_traffic_statistics": {
|
||||||
"name": "Clear traffic statistics",
|
"name": "Clear traffic statistics",
|
||||||
|
@ -1 +1,23 @@
|
|||||||
"""Tests for the huawei_lte component."""
|
"""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)
|
||||||
|
76
tests/components/huawei_lte/test_button.py
Normal file
76
tests/components/huawei_lte/test_button.py
Normal 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()
|
@ -1,8 +1,6 @@
|
|||||||
"""Tests for the Huawei LTE switches."""
|
"""Tests for the Huawei LTE switches."""
|
||||||
from unittest.mock import MagicMock, patch
|
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.huawei_lte.const import DOMAIN
|
||||||
from homeassistant.components.switch import (
|
from homeassistant.components.switch import (
|
||||||
DOMAIN as SWITCH_DOMAIN,
|
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.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from . import magic_client
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
SWITCH_WIFI_GUEST_NETWORK = "switch.lte_wi_fi_guest_network"
|
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.Connection", MagicMock())
|
||||||
@patch("homeassistant.components.huawei_lte.Client", return_value=magic_client({}))
|
@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(
|
async def test_huawei_lte_wifi_guest_network_config_entry_when_network_is_not_present(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user