1 """Entity representing a Sonos Alarm."""
3 from __future__
import annotations
7 from typing
import Any, cast
9 from soco.alarms
import Alarm
10 from soco.exceptions
import SoCoSlaveException, SoCoUPnPException
23 DOMAIN
as SONOS_DOMAIN,
26 SONOS_CREATE_SWITCHES,
28 from .entity
import SonosEntity, SonosPollingEntity
29 from .helpers
import soco_error
30 from .speaker
import SonosSpeaker
32 _LOGGER = logging.getLogger(__name__)
34 ATTR_DURATION =
"duration"
36 ATTR_PLAY_MODE =
"play_mode"
37 ATTR_RECURRENCE =
"recurrence"
38 ATTR_SCHEDULED_TODAY =
"scheduled_today"
39 ATTR_VOLUME =
"volume"
40 ATTR_INCLUDE_LINKED_ZONES =
"include_linked_zones"
42 ATTR_CROSSFADE =
"cross_fade"
43 ATTR_LOUDNESS =
"loudness"
44 ATTR_MUSIC_PLAYBACK_FULL_VOLUME =
"surround_mode"
45 ATTR_NIGHT_SOUND =
"night_mode"
46 ATTR_SPEECH_ENHANCEMENT =
"dialog_level"
47 ATTR_STATUS_LIGHT =
"status_light"
48 ATTR_SUB_ENABLED =
"sub_enabled"
49 ATTR_SURROUND_ENABLED =
"surround_enabled"
50 ATTR_TOUCH_CONTROLS =
"buttons_enabled"
56 ATTR_MUSIC_PLAYBACK_FULL_VOLUME,
58 ATTR_SPEECH_ENHANCEMENT,
60 ATTR_SURROUND_ENABLED,
64 COORDINATOR_FEATURES = ATTR_CROSSFADE
76 config_entry: ConfigEntry,
77 async_add_entities: AddEntitiesCallback,
79 """Set up Sonos from a config entry."""
81 async
def _async_create_alarms(speaker: SonosSpeaker, alarm_ids: list[str]) ->
None:
84 hass.data[DATA_SONOS].alarms[speaker.household_id].created_alarm_ids
86 for alarm_id
in alarm_ids:
87 if alarm_id
in created_alarms:
89 _LOGGER.debug(
"Creating alarm %s on %s", alarm_id, speaker.zone_name)
90 created_alarms.add(alarm_id)
94 def available_soco_attributes(speaker: SonosSpeaker) -> list[str]:
96 for feature_type
in ALL_FEATURES:
98 if (state := getattr(speaker.soco, feature_type,
None))
is not None:
99 setattr(speaker, feature_type, state)
100 features.append(feature_type)
101 except SoCoSlaveException:
102 features.append(feature_type)
105 async
def _async_create_switches(speaker: SonosSpeaker) ->
None:
107 available_features = await hass.async_add_executor_job(
108 available_soco_attributes, speaker
110 for feature_type
in available_features:
112 "Creating %s switch on %s",
119 config_entry.async_on_unload(
122 config_entry.async_on_unload(
128 """Representation of a Sonos feature switch."""
130 def __init__(self, feature_type: str, speaker: SonosSpeaker) ->
None:
131 """Initialize the switch."""
139 if feature_type
in POLL_REQUIRED:
144 """Handle polling for subscription-based switches when subscription fails."""
150 """Poll the current state of the switch."""
156 """Return True if entity is on."""
162 """Turn the entity on."""
166 """Turn the entity off."""
171 """Enable or disable the feature on the device."""
173 soco = self.
socosoco.group.coordinator
178 except SoCoUPnPException
as exc:
179 _LOGGER.warning(
"Could not toggle %s: %s", self.
entity_identity_id, exc)
183 """Representation of a Sonos Alarm entity."""
185 _attr_entity_category = EntityCategory.CONFIG
186 _attr_icon =
"mdi:alarm"
188 def __init__(self, alarm_id: str, speaker: SonosSpeaker) ->
None:
189 """Initialize the switch."""
197 """Handle switch setup when added to hass."""
202 f
"{SONOS_ALARMS_UPDATED}-{self.household_id}",
207 async
def async_write_state_daily(now: datetime.datetime) ->
None:
208 """Update alarm state attributes each calendar day."""
209 _LOGGER.debug(
"Updating state attributes for %s", self.
namenamename)
214 self.
hasshass, async_write_state_daily, hour=0, minute=0, second=0
220 """Return the alarm instance."""
225 """Return the name of the sensor."""
227 f
"{self.alarm.recurrence.capitalize()} alarm"
228 f
" {str(self.alarm.start_time)[:5]}"
232 """Call the central alarm polling method."""
233 await self.
hasshass.data[DATA_SONOS].alarms[self.
household_idhousehold_id].async_poll()
237 """Check if alarm exists and remove alarm entity if not available."""
243 entity_registry = er.async_get(self.
hasshass)
250 """Poll the device for the current state."""
256 self.
alarmalarm.zone.uid
260 "No configured Sonos speaker has been found to match the alarm."
269 """Update the device, since this alarm moved to a different player."""
270 device_registry = dr.async_get(self.
hasshass)
271 entity_registry = er.async_get(self.
hasshass)
275 raise RuntimeError(
"Alarm has been deleted by accident.")
277 new_device = device_registry.async_get_or_create(
278 config_entry_id=cast(str, entity.config_entry_id),
279 identifiers={(SONOS_DOMAIN, self.
socosoco.uid)},
280 connections={(dr.CONNECTION_NETWORK_MAC, self.
speakerspeakerspeaker.mac_address)},
284 )
and device.device_id != new_device.id:
285 _LOGGER.debug(
"%s is moving to %s", self.
entity_identity_identity_id, new_device.name)
286 entity_registry.async_update_entity(self.
entity_identity_identity_id, device_id=new_device.id)
290 """Return whether this alarm is scheduled for today."""
291 recurrence = self.
alarmalarm.recurrence
292 daynum =
int(datetime.datetime.today().strftime(
"%w"))
294 recurrence
in (
"DAILY",
"ONCE")
295 or (recurrence ==
"WEEKENDS" and daynum
in WEEKEND_DAYS)
296 or (recurrence ==
"WEEKDAYS" and daynum
not in WEEKEND_DAYS)
297 or (recurrence.startswith(
"ON_")
and str(daynum)
in recurrence)
302 """Return whether this alarm is available."""
307 """Return state of Sonos alarm switch."""
308 return self.
alarmalarm.enabled
312 """Return attributes of Sonos alarm switch."""
315 ATTR_TIME:
str(self.
alarmalarm.start_time),
316 ATTR_DURATION:
str(self.
alarmalarm.duration),
317 ATTR_RECURRENCE:
str(self.
alarmalarm.recurrence),
318 ATTR_VOLUME: self.
alarmalarm.volume / 100,
319 ATTR_PLAY_MODE:
str(self.
alarmalarm.play_mode),
320 ATTR_SCHEDULED_TODAY: self.
_is_today_is_today,
321 ATTR_INCLUDE_LINKED_ZONES: self.
alarmalarm.include_linked_zones,
325 """Turn alarm switch on."""
329 """Turn alarm switch off."""
334 """Handle turn on/off of alarm switch."""
335 self.
alarmalarm.enabled = turn_on
336 self.
alarmalarm.save()
None _handle_switch_on_off(self, bool turn_on)
None async_update_state(self)
None turn_on(self, **Any kwargs)
None turn_off(self, **Any kwargs)
None _async_update_device(self)
dict[str, Any] extra_state_attributes(self)
None async_added_to_hass(self)
bool async_check_if_available(self)
None _async_fallback_poll(self)
None __init__(self, str alarm_id, SonosSpeaker speaker)
None _async_fallback_poll(self)
_attr_entity_registry_enabled_default
None turn_off(self, **Any kwargs)
None send_command(self, bool enable)
None __init__(self, str feature_type, SonosSpeaker speaker)
None turn_on(self, **Any kwargs)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
str|UndefinedType|None name(self)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
CALLBACK_TYPE async_track_time_change(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, Any|None hour=None, Any|None minute=None, Any|None second=None)