Skip to content

Commit

Permalink
Merge branch 'dev' into protobuf_528
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Sep 25, 2024
2 parents acde17c + f4c339d commit 4cc9362
Show file tree
Hide file tree
Showing 162 changed files with 896 additions and 425 deletions.
8 changes: 4 additions & 4 deletions homeassistant/components/air_quality/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

_LOGGER: Final = logging.getLogger(__name__)

DOMAIN_DATA: HassKey[EntityComponent[AirQualityEntity]] = HassKey(DOMAIN)
DATA_COMPONENT: HassKey[EntityComponent[AirQualityEntity]] = HassKey(DOMAIN)
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
Expand Down Expand Up @@ -56,7 +56,7 @@

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the air quality component."""
component = hass.data[DOMAIN_DATA] = EntityComponent[AirQualityEntity](
component = hass.data[DATA_COMPONENT] = EntityComponent[AirQualityEntity](
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
)
await component.async_setup(config)
Expand All @@ -65,12 +65,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
return await hass.data[DOMAIN_DATA].async_setup_entry(entry)
return await hass.data[DATA_COMPONENT].async_setup_entry(entry)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.data[DOMAIN_DATA].async_unload_entry(entry)
return await hass.data[DATA_COMPONENT].async_unload_entry(entry)


class AirQualityEntity(Entity):
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

_LOGGER: Final = logging.getLogger(__name__)

DOMAIN_DATA: HassKey[EntityComponent[AlarmControlPanelEntity]] = HassKey(DOMAIN)
DATA_COMPONENT: HassKey[EntityComponent[AlarmControlPanelEntity]] = HassKey(DOMAIN)
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
PLATFORM_SCHEMA: Final = cv.PLATFORM_SCHEMA
PLATFORM_SCHEMA_BASE: Final = cv.PLATFORM_SCHEMA_BASE
Expand All @@ -71,7 +71,7 @@

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Track states and offer events for sensors."""
component = hass.data[DOMAIN_DATA] = EntityComponent[AlarmControlPanelEntity](
component = hass.data[DATA_COMPONENT] = EntityComponent[AlarmControlPanelEntity](
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
)

Expand Down Expand Up @@ -124,12 +124,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
return await hass.data[DOMAIN_DATA].async_setup_entry(entry)
return await hass.data[DATA_COMPONENT].async_setup_entry(entry)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.data[DOMAIN_DATA].async_unload_entry(entry)
return await hass.data[DATA_COMPONENT].async_unload_entry(entry)


class AlarmControlPanelEntityDescription(EntityDescription, frozen_or_thawed=True):
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/assist_satellite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from .connection_test import ConnectionTestView
from .const import (
CONNECTION_TEST_DATA,
DATA_COMPONENT,
DOMAIN,
DOMAIN_DATA,
AssistSatelliteEntityFeature,
)
from .entity import (
Expand Down Expand Up @@ -44,7 +44,7 @@


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
component = hass.data[DOMAIN_DATA] = EntityComponent[AssistSatelliteEntity](
component = hass.data[DATA_COMPONENT] = EntityComponent[AssistSatelliteEntity](
_LOGGER, DOMAIN, hass
)
await component.async_setup(config)
Expand Down Expand Up @@ -72,9 +72,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
return await hass.data[DOMAIN_DATA].async_setup_entry(entry)
return await hass.data[DATA_COMPONENT].async_setup_entry(entry)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.data[DOMAIN_DATA].async_unload_entry(entry)
return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
2 changes: 1 addition & 1 deletion homeassistant/components/assist_satellite/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

DOMAIN = "assist_satellite"

DOMAIN_DATA: HassKey[EntityComponent[AssistSatelliteEntity]] = HassKey(DOMAIN)
DATA_COMPONENT: HassKey[EntityComponent[AssistSatelliteEntity]] = HassKey(DOMAIN)
CONNECTION_TEST_DATA: HassKey[dict[str, asyncio.Event]] = HassKey(
f"{DOMAIN}_connection_tests"
)
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/assist_satellite/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from .connection_test import CONNECTION_TEST_URL_BASE
from .const import (
CONNECTION_TEST_DATA,
DATA_COMPONENT,
DOMAIN,
DOMAIN_DATA,
AssistSatelliteEntityFeature,
)
from .entity import AssistSatelliteEntity
Expand Down Expand Up @@ -48,7 +48,7 @@ async def websocket_intercept_wake_word(
msg: dict[str, Any],
) -> None:
"""Intercept the next wake word from a satellite."""
satellite = hass.data[DOMAIN_DATA].get_entity(msg["entity_id"])
satellite = hass.data[DATA_COMPONENT].get_entity(msg["entity_id"])
if satellite is None:
connection.send_error(
msg["id"], websocket_api.ERR_NOT_FOUND, "Entity not found"
Expand Down Expand Up @@ -86,7 +86,7 @@ def websocket_get_configuration(
msg: dict[str, Any],
) -> None:
"""Get the current satellite configuration."""
satellite = hass.data[DOMAIN_DATA].get_entity(msg["entity_id"])
satellite = hass.data[DATA_COMPONENT].get_entity(msg["entity_id"])
if satellite is None:
connection.send_error(
msg["id"], websocket_api.ERR_NOT_FOUND, "Entity not found"
Expand Down Expand Up @@ -115,7 +115,7 @@ async def websocket_set_wake_words(
msg: dict[str, Any],
) -> None:
"""Set the active wake words for the satellite."""
satellite = hass.data[DOMAIN_DATA].get_entity(msg["entity_id"])
satellite = hass.data[DATA_COMPONENT].get_entity(msg["entity_id"])
if satellite is None:
connection.send_error(
msg["id"], websocket_api.ERR_NOT_FOUND, "Entity not found"
Expand Down
20 changes: 10 additions & 10 deletions homeassistant/components/automation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
from .helpers import async_get_blueprints
from .trace import trace_automation

DOMAIN_DATA: HassKey[EntityComponent[BaseAutomationEntity]] = HassKey(DOMAIN)
DATA_COMPONENT: HassKey[EntityComponent[BaseAutomationEntity]] = HassKey(DOMAIN)
ENTITY_ID_FORMAT = DOMAIN + ".{}"


Expand Down Expand Up @@ -163,12 +163,12 @@ def _automations_with_x(
hass: HomeAssistant, referenced_id: str, property_name: str
) -> list[str]:
"""Return all automations that reference the x."""
if DOMAIN_DATA not in hass.data:
if DATA_COMPONENT not in hass.data:
return []

return [
automation_entity.entity_id
for automation_entity in hass.data[DOMAIN_DATA].entities
for automation_entity in hass.data[DATA_COMPONENT].entities
if referenced_id in getattr(automation_entity, property_name)
]

Expand All @@ -177,10 +177,10 @@ def _x_in_automation(
hass: HomeAssistant, entity_id: str, property_name: str
) -> list[str]:
"""Return all x in an automation."""
if DOMAIN_DATA not in hass.data:
if DATA_COMPONENT not in hass.data:
return []

if (automation_entity := hass.data[DOMAIN_DATA].get_entity(entity_id)) is None:
if (automation_entity := hass.data[DATA_COMPONENT].get_entity(entity_id)) is None:
return []

return list(getattr(automation_entity, property_name))
Expand Down Expand Up @@ -254,26 +254,26 @@ def automations_with_blueprint(hass: HomeAssistant, blueprint_path: str) -> list

return [
automation_entity.entity_id
for automation_entity in hass.data[DOMAIN_DATA].entities
for automation_entity in hass.data[DATA_COMPONENT].entities
if automation_entity.referenced_blueprint == blueprint_path
]


@callback
def blueprint_in_automation(hass: HomeAssistant, entity_id: str) -> str | None:
"""Return the blueprint the automation is based on or None."""
if DOMAIN_DATA not in hass.data:
if DATA_COMPONENT not in hass.data:
return None

if (automation_entity := hass.data[DOMAIN_DATA].get_entity(entity_id)) is None:
if (automation_entity := hass.data[DATA_COMPONENT].get_entity(entity_id)) is None:
return None

return automation_entity.referenced_blueprint


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up all automations."""
hass.data[DOMAIN_DATA] = component = EntityComponent[BaseAutomationEntity](
hass.data[DATA_COMPONENT] = component = EntityComponent[BaseAutomationEntity](
LOGGER, DOMAIN, hass
)

Expand Down Expand Up @@ -1204,7 +1204,7 @@ def websocket_config(
msg: dict[str, Any],
) -> None:
"""Get automation config."""
automation = hass.data[DOMAIN_DATA].get_entity(msg["entity_id"])
automation = hass.data[DATA_COMPONENT].get_entity(msg["entity_id"])

if automation is None:
connection.send_error(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/automation/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"step": {
"confirm": {
"title": "[%key:component::automation::issues::service_not_found::title%]",
"description": "The automation \"{name}\" (`{entity_id}`) has an unknown action: `{service}`.\n\nThis error prevents the automation from running correctly. Maybe this action is no longer available, or perhaps a typo caused it.\n\nTo fix this error, [edit the automation]({edit}) and remove this action.\n\nClick on SUBMIT below to confirm you have fixed this automation."
"description": "The automation \"{name}\" (`{entity_id}`) has an unknown action: `{service}`.\n\nThis error prevents the automation from running correctly. Maybe this action is no longer available, or perhaps a typo caused it.\n\nTo fix this error, [edit the automation]({edit}) and remove this action.\n\nSelect **Submit** below to confirm you have fixed this automation."
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/awair/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}
},
"local": {
"description": "Follow [these instructions]({url}) on how to enable the Awair Local API.\n\nClick submit when done."
"description": "Follow [these instructions]({url}) on how to enable the Awair Local API.\n\nSelect **Submit** when done."
},
"local_pick": {
"data": {
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/bang_olufsen/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class BangOlufsenModel(StrEnum):
class WebsocketNotification(StrEnum):
"""Enum for WebSocket notification types."""

ACTIVE_LISTENING_MODE = "active_listening_mode"
PLAYBACK_ERROR = "playback_error"
PLAYBACK_METADATA = "playback_metadata"
PLAYBACK_PROGRESS = "playback_progress"
Expand Down
45 changes: 45 additions & 0 deletions homeassistant/components/bang_olufsen/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
Action,
Art,
BeolinkLeader,
ListeningModeProps,
ListeningModeRef,
OverlayPlayRequest,
OverlayPlayRequestTextToSpeechTextToSpeech,
PlaybackContentMetadata,
Expand Down Expand Up @@ -88,6 +90,7 @@
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
)


Expand Down Expand Up @@ -137,6 +140,7 @@ def __init__(self, entry: ConfigEntry, client: MozartClient) -> None:
self._sources: dict[str, str] = {}
self._state: str = MediaPlayerState.IDLE
self._video_sources: dict[str, str] = {}
self._sound_modes: dict[str, int] = {}

# Beolink compatible sources
self._beolink_sources: dict[str, bool] = {}
Expand All @@ -148,6 +152,7 @@ async def async_added_to_hass(self) -> None:

signal_handlers: dict[str, Callable] = {
CONNECTION_STATUS: self._async_update_connection_state,
WebsocketNotification.ACTIVE_LISTENING_MODE: self._async_update_sound_modes,
WebsocketNotification.BEOLINK: self._async_update_beolink,
WebsocketNotification.PLAYBACK_ERROR: self._async_update_playback_error,
WebsocketNotification.PLAYBACK_METADATA: self._async_update_playback_metadata_and_beolink,
Expand Down Expand Up @@ -211,6 +216,8 @@ async def _initialize(self) -> None:
# If the device has been updated with new sources, then the API will fail here.
await self._async_update_sources()

await self._async_update_sound_modes()

async def _async_update_sources(self) -> None:
"""Get sources for the specific product."""

Expand Down Expand Up @@ -432,6 +439,29 @@ def _get_beolink_jid(self, entity_id: str) -> str:
# Return JID
return cast(str, config_entry.data[CONF_BEOLINK_JID])

async def _async_update_sound_modes(
self, active_sound_mode: ListeningModeProps | ListeningModeRef | None = None
) -> None:
"""Update the available sound modes."""
sound_modes = await self._client.get_listening_mode_set()

if active_sound_mode is None:
active_sound_mode = await self._client.get_active_listening_mode()

# Add the key to make the labels unique (As labels are not required to be unique on B&O devices)
for sound_mode in sound_modes:
label = f"{sound_mode.name} ({sound_mode.id})"

self._sound_modes[label] = sound_mode.id

if sound_mode.id == active_sound_mode.id:
self._attr_sound_mode = label

# Set available options
self._attr_sound_mode_list = list(self._sound_modes.keys())

self.async_write_ha_state()

@property
def state(self) -> MediaPlayerState:
"""Return the current state of the media player."""
Expand Down Expand Up @@ -620,6 +650,21 @@ async def async_select_source(self, source: str) -> None:
# Video
await self._client.post_remote_trigger(id=key)

async def async_select_sound_mode(self, sound_mode: str) -> None:
"""Select a sound mode."""
# Ensure only known sound modes known by the integration can be activated.
if sound_mode not in self._sound_modes:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_sound_mode",
translation_placeholders={
"invalid_sound_mode": sound_mode,
"valid_sound_modes": ", ".join(list(self._sound_modes.keys())),
},
)

await self._client.activate_listening_mode(id=self._sound_modes[sound_mode])

async def async_play_media(
self,
media_type: MediaType | str,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/bang_olufsen/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
},
"invalid_grouping_entity": {
"message": "Entity with id: {entity_id} can't be added to the Beolink session. Is the entity a Bang & Olufsen media_player?"
},
"invalid_sound_mode": {
"message": "{invalid_sound_mode} is an invalid sound mode. Valid values are: {valid_sound_modes}."
}
}
}
12 changes: 12 additions & 0 deletions homeassistant/components/bang_olufsen/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging

from mozart_api.models import (
ListeningModeProps,
PlaybackContentMetadata,
PlaybackError,
PlaybackProgress,
Expand Down Expand Up @@ -50,6 +51,9 @@ def __init__(
self._client.get_notification_notifications(self.on_notification_notification)
self._client.get_on_connection_lost(self.on_connection_lost)
self._client.get_on_connection(self.on_connection)
self._client.get_active_listening_mode_notifications(
self.on_active_listening_mode
)
self._client.get_playback_error_notifications(
self.on_playback_error_notification
)
Expand Down Expand Up @@ -89,6 +93,14 @@ def on_connection_lost(self) -> None:
_LOGGER.error("Lost connection to the %s", self.entry.title)
self._update_connection_status()

def on_active_listening_mode(self, notification: ListeningModeProps) -> None:
"""Send active_listening_mode dispatch."""
async_dispatcher_send(
self.hass,
f"{self._unique_id}_{WebsocketNotification.ACTIVE_LISTENING_MODE}",
notification,
)

def on_notification_notification(
self, notification: WebsocketNotificationTag
) -> None:
Expand Down
Loading

0 comments on commit 4cc9362

Please sign in to comment.