Don't allow switch toggle when device in locked in AVM FRITZ!SmartHome (#120132)

* fix: set state of the FritzBox-Switch to disabled if the option for manuel switching in the userinterface is disabled

* feat: raise an error instead of disabling switch

* feat: rename method signature

* fix: tests

* fix: wrong import

* feat: Update homeassistant/components/fritzbox/strings.json

Update error message

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>

* Update tests/components/fritzbox/test_switch.py

feat: update test

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>

* make ruff happy

* fix expected error message check

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
This commit is contained in:
Florian 2024-06-26 21:45:17 +02:00 committed by GitHub
parent dd6cc82f70
commit 9b2915efed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 1 deletions

View file

@ -81,6 +81,9 @@
} }
}, },
"exceptions": { "exceptions": {
"manual_switching_disabled": {
"message": "Can't toggle switch while manual switching is disabled for the device."
},
"change_preset_while_active_mode": { "change_preset_while_active_mode": {
"message": "Can't change preset while holiday or summer mode is active on the device." "message": "Can't change preset while holiday or summer mode is active on the device."
}, },

View file

@ -6,9 +6,11 @@ from typing import Any
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import FritzBoxDeviceEntity from . import FritzBoxDeviceEntity
from .const import DOMAIN
from .coordinator import FritzboxConfigEntry from .coordinator import FritzboxConfigEntry
@ -48,10 +50,20 @@ class FritzboxSwitch(FritzBoxDeviceEntity, SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on.""" """Turn the switch on."""
self.check_lock_state()
await self.hass.async_add_executor_job(self.data.set_switch_state_on) await self.hass.async_add_executor_job(self.data.set_switch_state_on)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off.""" """Turn the switch off."""
self.check_lock_state()
await self.hass.async_add_executor_job(self.data.set_switch_state_off) await self.hass.async_add_executor_job(self.data.set_switch_state_off)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
def check_lock_state(self) -> None:
"""Raise an Error if manual switching via FRITZ!Box user interface is disabled."""
if self.data.lock:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="manual_switching_disabled",
)

View file

@ -151,7 +151,7 @@ class FritzDeviceSwitchMock(FritzEntityBaseMock):
has_thermostat = False has_thermostat = False
has_blind = False has_blind = False
switch_state = "fake_state" switch_state = "fake_state"
lock = "fake_locked" lock = False
power = 5678 power = 5678
present = True present = True
temperature = 1.23 temperature = 1.23

View file

@ -3,6 +3,7 @@
from datetime import timedelta from datetime import timedelta
from unittest.mock import Mock from unittest.mock import Mock
import pytest
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
from homeassistant.components.fritzbox.const import DOMAIN as FB_DOMAIN from homeassistant.components.fritzbox.const import DOMAIN as FB_DOMAIN
@ -29,6 +30,7 @@ from homeassistant.const import (
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -130,6 +132,7 @@ async def test_turn_on(hass: HomeAssistant, fritz: Mock) -> None:
async def test_turn_off(hass: HomeAssistant, fritz: Mock) -> None: async def test_turn_off(hass: HomeAssistant, fritz: Mock) -> None:
"""Test turn device off.""" """Test turn device off."""
device = FritzDeviceSwitchMock() device = FritzDeviceSwitchMock()
assert await setup_config_entry( assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
) )
@ -137,9 +140,36 @@ async def test_turn_off(hass: HomeAssistant, fritz: Mock) -> None:
await hass.services.async_call( await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
) )
assert device.set_switch_state_off.call_count == 1 assert device.set_switch_state_off.call_count == 1
async def test_toggle_while_locked(hass: HomeAssistant, fritz: Mock) -> None:
"""Test toggling while device is locked."""
device = FritzDeviceSwitchMock()
device.lock = True
assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
)
with pytest.raises(
HomeAssistantError,
match="Can't toggle switch while manual switching is disabled for the device",
):
await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
with pytest.raises(
HomeAssistantError,
match="Can't toggle switch while manual switching is disabled for the device",
):
await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
async def test_update(hass: HomeAssistant, fritz: Mock) -> None: async def test_update(hass: HomeAssistant, fritz: Mock) -> None:
"""Test update without error.""" """Test update without error."""
device = FritzDeviceSwitchMock() device = FritzDeviceSwitchMock()