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,