From fd30dd0aeed4cc36b4ea764d9d70579a0fa18c11 Mon Sep 17 00:00:00 2001 From: Pete Sage <76050312+PeteRager@users.noreply.github.com> Date: Sun, 8 Jun 2025 05:45:20 -0400 Subject: [PATCH 1/2] Add tests for sonos switch alarms on and off (#146314) * fix: add tests for switch on/off * fix: simplify * fix: simplify * fix: comment * fix: comment --- tests/components/sonos/conftest.py | 70 +++++++++++++++------------ tests/components/sonos/test_switch.py | 41 +++++++++++++++- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/tests/components/sonos/conftest.py b/tests/components/sonos/conftest.py index 2fbec2b0903d88..4994d36f1bf59b 100644 --- a/tests/components/sonos/conftest.py +++ b/tests/components/sonos/conftest.py @@ -85,6 +85,16 @@ def __init__(self, service_type, ip_address="192.168.42.2") -> None: self.subscribe = AsyncMock(return_value=SonosMockSubscribe(ip_address)) +class SonosMockAlarmClock(SonosMockService): + """Mock a Sonos AlarmClock Service used in callbacks.""" + + def __init__(self, return_value: dict[str, str], ip_address="192.168.42.2") -> None: + """Initialize the instance.""" + super().__init__("AlarmClock", ip_address) + self.ListAlarms = Mock(return_value=return_value) + self.UpdateAlarm = Mock() + + class SonosMockEvent: """Mock a sonos Event used in callbacks.""" @@ -593,43 +603,39 @@ def music_library_fixture( @pytest.fixture(name="alarm_clock") -def alarm_clock_fixture(): +def alarm_clock_fixture() -> SonosMockAlarmClock: """Create alarmClock fixture.""" - alarm_clock = SonosMockService("AlarmClock") - # pylint: disable-next=attribute-defined-outside-init - alarm_clock.ListAlarms = Mock() - alarm_clock.ListAlarms.return_value = { - "CurrentAlarmListVersion": "RINCON_test:14", - "CurrentAlarmList": "" - '' - "", - } - return alarm_clock + return SonosMockAlarmClock( + { + "CurrentAlarmListVersion": "RINCON_test:14", + "CurrentAlarmList": "" + '' + "", + } + ) @pytest.fixture(name="alarm_clock_extended") -def alarm_clock_fixture_extended(): +def alarm_clock_fixture_extended() -> SonosMockAlarmClock: """Create alarmClock fixture.""" - alarm_clock = SonosMockService("AlarmClock") - # pylint: disable-next=attribute-defined-outside-init - alarm_clock.ListAlarms = Mock() - alarm_clock.ListAlarms.return_value = { - "CurrentAlarmListVersion": "RINCON_test:15", - "CurrentAlarmList": "" - '' - '' - "", - } - return alarm_clock + return SonosMockAlarmClock( + { + "CurrentAlarmListVersion": "RINCON_test:15", + "CurrentAlarmList": "" + '' + '' + "", + } + ) @pytest.fixture(name="speaker_model") diff --git a/tests/components/sonos/test_switch.py b/tests/components/sonos/test_switch.py index 11ce1aa5ddbef7..04457ee95c7735 100644 --- a/tests/components/sonos/test_switch.py +++ b/tests/components/sonos/test_switch.py @@ -4,6 +4,8 @@ from datetime import timedelta from unittest.mock import patch +import pytest + from homeassistant.components.sonos.const import DATA_SONOS_DISCOVERY_MANAGER from homeassistant.components.sonos.switch import ( ATTR_DURATION, @@ -13,13 +15,21 @@ ATTR_RECURRENCE, ATTR_VOLUME, ) +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY -from homeassistant.const import ATTR_TIME, STATE_OFF, STATE_ON +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_TIME, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.util import dt as dt_util -from .conftest import SonosMockEvent +from .conftest import MockSoCo, SonosMockEvent from tests.common import async_fire_time_changed @@ -132,6 +142,33 @@ async def test_switch_attributes( assert touch_controls_state.state == STATE_ON +@pytest.mark.parametrize( + ("service", "expected_result"), + [ + (SERVICE_TURN_OFF, "0"), + (SERVICE_TURN_ON, "1"), + ], +) +async def test_switch_alarm_turn_on( + hass: HomeAssistant, + async_setup_sonos, + soco: MockSoCo, + service: str, + expected_result: str, +) -> None: + """Test enabling and disabling of alarm.""" + await async_setup_sonos() + + await hass.services.async_call( + SWITCH_DOMAIN, service, {ATTR_ENTITY_ID: "switch.sonos_alarm_14"}, blocking=True + ) + + assert soco.alarmClock.UpdateAlarm.call_count == 1 + call_args = soco.alarmClock.UpdateAlarm.call_args[0] + assert call_args[0][0] == ("ID", "14") + assert call_args[0][4] == ("Enabled", expected_result) + + async def test_alarm_create_delete( hass: HomeAssistant, async_setup_sonos, From 9a6ebb0848bc1be1a05eddc0230447beb743892b Mon Sep 17 00:00:00 2001 From: Sanjay Govind Date: Mon, 9 Jun 2025 00:35:54 +1200 Subject: [PATCH 2/2] Fix bosch alarm areas not correctly subscribing to alarms (#146322) * Fix bosch alarm areas not correctly subscribing to alarms * add test --- .../components/bosch_alarm/alarm_control_panel.py | 2 +- .../components/bosch_alarm/test_alarm_control_panel.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/bosch_alarm/alarm_control_panel.py b/homeassistant/components/bosch_alarm/alarm_control_panel.py index 60365070587d6b..b502ee32fcacae 100644 --- a/homeassistant/components/bosch_alarm/alarm_control_panel.py +++ b/homeassistant/components/bosch_alarm/alarm_control_panel.py @@ -50,7 +50,7 @@ class AreaAlarmControlPanel(BoschAlarmAreaEntity, AlarmControlPanelEntity): def __init__(self, panel: Panel, area_id: int, unique_id: str) -> None: """Initialise a Bosch Alarm control panel entity.""" - super().__init__(panel, area_id, unique_id, False, False, True) + super().__init__(panel, area_id, unique_id, True, False, True) self._attr_unique_id = self._area_unique_id @property diff --git a/tests/components/bosch_alarm/test_alarm_control_panel.py b/tests/components/bosch_alarm/test_alarm_control_panel.py index 31d2f928ec5c0d..517673968805ad 100644 --- a/tests/components/bosch_alarm/test_alarm_control_panel.py +++ b/tests/components/bosch_alarm/test_alarm_control_panel.py @@ -66,6 +66,16 @@ async def test_update_alarm_device( assert hass.states.get(entity_id).state == AlarmControlPanelState.ARMED_AWAY + area.is_triggered.return_value = True + + await call_observable(hass, area.alarm_observer) + + assert hass.states.get(entity_id).state == AlarmControlPanelState.TRIGGERED + + area.is_triggered.return_value = False + + await call_observable(hass, area.alarm_observer) + await hass.services.async_call( ALARM_CONTROL_PANEL_DOMAIN, SERVICE_ALARM_DISARM,