diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index be9d02e45fdc4..1c9023f169bb5 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -21,6 +21,7 @@ ResponseErrorNotSupportedException, ) from requests.exceptions import Timeout +from url_normalize import url_normalize import voluptuous as vol from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN @@ -40,7 +41,11 @@ Platform, ) from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryNotReady, + ServiceValidationError, +) from homeassistant.helpers import ( config_validation as cv, device_registry as dr, @@ -121,7 +126,7 @@ extra=vol.ALLOW_EXTRA, ) -SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_URL): cv.url}) +SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_URL): cv.string}) PLATFORMS = [ Platform.BINARY_SENSOR, @@ -507,26 +512,22 @@ def service_handler(service: ServiceCall) -> None: because the latter is not available anywhere in the UI. """ routers = hass.data[DOMAIN].routers - if url := service.data.get(CONF_URL): + if url := url_normalize(service.data.get(CONF_URL), default_scheme="http"): router = next( (router for router in routers.values() if router.url == url), None ) elif not routers: - _LOGGER.error("%s: no routers configured", service.service) - return + raise ServiceValidationError("No routers configured") elif len(routers) == 1: router = next(iter(routers.values())) else: - _LOGGER.error( - "%s: more than one router configured, must specify one of URLs %s", - service.service, - sorted(router.url for router in routers.values()), + raise ServiceValidationError( + f"More than one router configured, must specify one of URLs {sorted(router.url for router in routers.values())}" ) - return if not router: - _LOGGER.error("%s: router %s unavailable", service.service, url) - return + raise ServiceValidationError(f"Router {url} not available") + was_suspended = router.suspended if service.service == SERVICE_RESUME_INTEGRATION: # Login will be handled automatically on demand router.suspended = False @@ -536,7 +537,10 @@ def service_handler(service: ServiceCall) -> None: router.suspended = True _LOGGER.debug("%s: %s", service.service, "done") else: - _LOGGER.error("%s: unsupported service", service.service) + raise ServiceValidationError(f"Unsupported service {service.service}") + if was_suspended != router.suspended: + # Make interactive entities' availability update + dispatcher_send(hass, UPDATE_SIGNAL, router.config_entry.unique_id) for service in ADMIN_SERVICES: async_register_admin_service( diff --git a/homeassistant/components/huawei_lte/button.py b/homeassistant/components/huawei_lte/button.py index 44b35d51dd42d..e981ee2fc9cfb 100644 --- a/homeassistant/components/huawei_lte/button.py +++ b/homeassistant/components/huawei_lte/button.py @@ -14,10 +14,11 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import entity_platform from .const import DOMAIN -from .entity import HuaweiLteBaseEntityWithDevice +from .entity import HuaweiLteBaseInteractiveEntity _LOGGER = logging.getLogger(__name__) @@ -36,7 +37,7 @@ async def async_setup_entry( async_add_entities(buttons) -class BaseButton(HuaweiLteBaseEntityWithDevice, ButtonEntity): +class BaseButton(HuaweiLteBaseInteractiveEntity, ButtonEntity): """Huawei LTE button base class.""" @property @@ -50,10 +51,7 @@ async def async_update(self) -> None: def press(self) -> None: """Press button.""" if self.router.suspended: - _LOGGER.debug( - "%s: ignored, integration suspended", self.entity_description.key - ) - return + raise ServiceValidationError("Integration is suspended") result = self._press() _LOGGER.debug("%s: %s", self.entity_description.key, result) diff --git a/homeassistant/components/huawei_lte/entity.py b/homeassistant/components/huawei_lte/entity.py index b69d2e79fb693..06fa820592c21 100644 --- a/homeassistant/components/huawei_lte/entity.py +++ b/homeassistant/components/huawei_lte/entity.py @@ -66,3 +66,12 @@ def device_info(self) -> DeviceInfo: connections=self.router.device_connections, identifiers=self.router.device_identifiers, ) + + +class HuaweiLteBaseInteractiveEntity(HuaweiLteBaseEntityWithDevice): + """Base interactive entity.""" + + @property + def available(self) -> bool: + """Return if entity is available.""" + return super().available and not self.router.suspended diff --git a/homeassistant/components/huawei_lte/select.py b/homeassistant/components/huawei_lte/select.py index 3df6fa53320ea..11e82a8e3d3a8 100644 --- a/homeassistant/components/huawei_lte/select.py +++ b/homeassistant/components/huawei_lte/select.py @@ -17,13 +17,14 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.typing import UNDEFINED from . import Router from .const import DOMAIN, KEY_NET_NET_MODE -from .entity import HuaweiLteBaseEntityWithDevice +from .entity import HuaweiLteBaseInteractiveEntity _LOGGER = logging.getLogger(__name__) @@ -76,7 +77,7 @@ async def async_setup_entry( async_add_entities(selects, True) -class HuaweiLteSelectEntity(HuaweiLteBaseEntityWithDevice, SelectEntity): +class HuaweiLteSelectEntity(HuaweiLteBaseInteractiveEntity, SelectEntity): """Huawei LTE select entity.""" entity_description: HuaweiSelectEntityDescription @@ -102,6 +103,8 @@ def __init__( def select_option(self, option: str) -> None: """Change the selected option.""" + if self.router.suspended: + raise ServiceValidationError("Integration is suspended") self.entity_description.setter_fn(option) @property diff --git a/homeassistant/components/huawei_lte/switch.py b/homeassistant/components/huawei_lte/switch.py index ac8bca4234c19..80c2ab804392e 100644 --- a/homeassistant/components/huawei_lte/switch.py +++ b/homeassistant/components/huawei_lte/switch.py @@ -12,6 +12,7 @@ ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -20,7 +21,7 @@ KEY_DIALUP_MOBILE_DATASWITCH, KEY_WLAN_WIFI_GUEST_NETWORK_SWITCH, ) -from .entity import HuaweiLteBaseEntityWithDevice +from .entity import HuaweiLteBaseInteractiveEntity _LOGGER = logging.getLogger(__name__) @@ -43,7 +44,7 @@ async def async_setup_entry( async_add_entities(switches, True) -class HuaweiLteBaseSwitch(HuaweiLteBaseEntityWithDevice, SwitchEntity): +class HuaweiLteBaseSwitch(HuaweiLteBaseInteractiveEntity, SwitchEntity): """Huawei LTE switch device base class.""" key: str @@ -57,10 +58,14 @@ def _turn(self, state: bool) -> None: def turn_on(self, **kwargs: Any) -> None: """Turn switch on.""" + if self.router.suspended: + raise ServiceValidationError("Integration is suspended") self._turn(state=True) def turn_off(self, **kwargs: Any) -> None: """Turn switch off.""" + if self.router.suspended: + raise ServiceValidationError("Integration is suspended") self._turn(state=False) async def async_added_to_hass(self) -> None: diff --git a/tests/components/huawei_lte/test_button.py b/tests/components/huawei_lte/test_button.py index c99c08c436c87..46af9d7f69cf8 100644 --- a/tests/components/huawei_lte/test_button.py +++ b/tests/components/huawei_lte/test_button.py @@ -18,7 +18,7 @@ from tests.common import MockConfigEntry -MOCK_CONF_URL = "http://huawei-lte.example.com" +MOCK_CONF_URL = "http://192.168.1.1/" @patch("homeassistant.components.huawei_lte.Connection", MagicMock())