mirror of
https://github.com/home-assistant/core.git
synced 2026-05-25 10:15:22 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6dea67e8c9 |
@@ -609,7 +609,6 @@ homeassistant.components.valve.*
|
||||
homeassistant.components.velbus.*
|
||||
homeassistant.components.velux.*
|
||||
homeassistant.components.victron_gx.*
|
||||
homeassistant.components.vistapool.*
|
||||
homeassistant.components.vivotek.*
|
||||
homeassistant.components.vlc_telnet.*
|
||||
homeassistant.components.vodafone_station.*
|
||||
|
||||
Generated
-2
@@ -1930,8 +1930,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/victron_remote_monitoring/ @AndyTempel
|
||||
/homeassistant/components/vilfo/ @ManneW
|
||||
/tests/components/vilfo/ @ManneW
|
||||
/homeassistant/components/vistapool/ @fdebrus
|
||||
/tests/components/vistapool/ @fdebrus
|
||||
/homeassistant/components/vivotek/ @HarlemSquirrel
|
||||
/tests/components/vivotek/ @HarlemSquirrel
|
||||
/homeassistant/components/vizio/ @raman325
|
||||
|
||||
@@ -7,11 +7,10 @@ from altruistclient import AltruistClient, AltruistDeviceModel, AltruistError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import CONF_HOST, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
"""Constants for the Altruist integration."""
|
||||
|
||||
DOMAIN = "altruist"
|
||||
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_HOST = "host"
|
||||
|
||||
@@ -10,12 +10,13 @@ import logging
|
||||
from altruistclient import AltruistClient, AltruistError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import CONF_HOST
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(seconds=15)
|
||||
|
||||
@@ -345,10 +345,7 @@ class AppleTvMediaPlayer(
|
||||
play_item = await media_source.async_resolve_media(
|
||||
self.hass, media_id, self.entity_id
|
||||
)
|
||||
if play_item.path and self._is_feature_available(FeatureName.StreamFile):
|
||||
media_id = str(play_item.path)
|
||||
else:
|
||||
media_id = async_process_play_media_url(self.hass, play_item.url)
|
||||
media_id = async_process_play_media_url(self.hass, play_item.url)
|
||||
media_type = MediaType.MUSIC
|
||||
|
||||
if self._is_feature_available(FeatureName.StreamFile) and (
|
||||
|
||||
@@ -17,11 +17,10 @@ from homeassistant.components.backup import (
|
||||
OnProgressCallback,
|
||||
suggested_filename,
|
||||
)
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from . import S3ConfigEntry
|
||||
from .const import CONF_BUCKET, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
|
||||
from .const import CONF_BUCKET, CONF_PREFIX, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
|
||||
from .helpers import async_list_backups_from_s3
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -8,7 +8,6 @@ from botocore.exceptions import ClientError, ConnectionError, ParamValidationErr
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.selector import (
|
||||
TextSelector,
|
||||
@@ -21,6 +20,7 @@ from .const import (
|
||||
CONF_ACCESS_KEY_ID,
|
||||
CONF_BUCKET,
|
||||
CONF_ENDPOINT_URL,
|
||||
CONF_PREFIX,
|
||||
CONF_SECRET_ACCESS_KEY,
|
||||
DEFAULT_ENDPOINT_URL,
|
||||
DESCRIPTION_AWS_S3_DOCS_URL,
|
||||
|
||||
@@ -11,6 +11,8 @@ CONF_ACCESS_KEY_ID = "access_key_id"
|
||||
CONF_SECRET_ACCESS_KEY = "secret_access_key"
|
||||
CONF_ENDPOINT_URL = "endpoint_url"
|
||||
CONF_BUCKET = "bucket"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_PREFIX = "prefix"
|
||||
|
||||
AWS_DOMAIN = "amazonaws.com"
|
||||
DEFAULT_ENDPOINT_URL = f"https://s3.eu-central-1.{AWS_DOMAIN}/"
|
||||
|
||||
@@ -8,11 +8,10 @@ from aiobotocore.client import AioBaseClient as S3Client
|
||||
from botocore.exceptions import BotoCoreError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import CONF_BUCKET, DOMAIN
|
||||
from .const import CONF_BUCKET, CONF_PREFIX, DOMAIN
|
||||
from .helpers import async_list_backups_from_s3
|
||||
|
||||
SCAN_INTERVAL = timedelta(hours=6)
|
||||
|
||||
@@ -5,10 +5,15 @@ from typing import Any
|
||||
|
||||
from homeassistant.components.backup import DATA_MANAGER as BACKUP_DATA_MANAGER
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_ACCESS_KEY_ID, CONF_BUCKET, CONF_SECRET_ACCESS_KEY, DOMAIN
|
||||
from .const import (
|
||||
CONF_ACCESS_KEY_ID,
|
||||
CONF_BUCKET,
|
||||
CONF_PREFIX,
|
||||
CONF_SECRET_ACCESS_KEY,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import S3ConfigEntry
|
||||
from .helpers import async_list_backups_from_s3
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from urllib.parse import urlsplit
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_IGNORE,
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_RECONFIGURE,
|
||||
ConfigEntry,
|
||||
@@ -138,15 +139,25 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
async def _create_entry(self, serial: str) -> ConfigFlowResult:
|
||||
"""Create entry for device.
|
||||
|
||||
Use the discovered device name when available.
|
||||
Generate a name to be used as a prefix for device entities.
|
||||
"""
|
||||
if (title_placeholders := self.context.get("title_placeholders")) is not None:
|
||||
name = title_placeholders[CONF_NAME]
|
||||
else:
|
||||
name = f"{self.config[CONF_MODEL]} - {serial}"
|
||||
model = self.config[CONF_MODEL]
|
||||
same_model = [
|
||||
entry.data[CONF_NAME]
|
||||
for entry in self.hass.config_entries.async_entries(DOMAIN)
|
||||
if entry.source != SOURCE_IGNORE and entry.data[CONF_MODEL] == model
|
||||
]
|
||||
|
||||
name = model
|
||||
for idx in range(len(same_model) + 1):
|
||||
name = f"{model} {idx}"
|
||||
if name not in same_model:
|
||||
break
|
||||
|
||||
self.config[CONF_NAME] = name
|
||||
|
||||
return self.async_create_entry(title=name, data=self.config)
|
||||
title = f"{model} - {serial}"
|
||||
return self.async_create_entry(title=title, data=self.config)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
],
|
||||
"quality_scale": "internal",
|
||||
"requirements": [
|
||||
"bleak==3.0.2",
|
||||
"bleak-retry-connector==4.6.1",
|
||||
"bleak==2.1.1",
|
||||
"bleak-retry-connector==4.6.0",
|
||||
"bluetooth-adapters==2.1.0",
|
||||
"bluetooth-auto-recovery==1.5.3",
|
||||
"bluetooth-data-tools==1.29.18",
|
||||
"dbus-fast==5.0.9",
|
||||
"habluetooth==6.7.3"
|
||||
"dbus-fast==5.0.3",
|
||||
"habluetooth==6.7.2"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import DAVError
|
||||
@@ -204,17 +204,20 @@ class WebDavCalendarEntity(CoordinatorEntity[CalDavUpdateCoordinator], CalendarE
|
||||
self._attr_unique_id = unique_id
|
||||
self._supports_offset = supports_offset
|
||||
|
||||
@override
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the next upcoming event."""
|
||||
return self._event
|
||||
|
||||
@override
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Get all events in a specific time frame."""
|
||||
return await self.coordinator.async_get_events(hass, start_date, end_date)
|
||||
|
||||
@override
|
||||
async def async_create_event(self, **kwargs: Any) -> None:
|
||||
"""Create a new event in the calendar."""
|
||||
_LOGGER.debug("Event: %s", kwargs)
|
||||
@@ -240,6 +243,7 @@ class WebDavCalendarEntity(CoordinatorEntity[CalDavUpdateCoordinator], CalendarE
|
||||
except (requests.ConnectionError, requests.Timeout, DAVError) as err:
|
||||
raise HomeAssistantError(f"CalDAV save error: {err}") from err
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Update event data."""
|
||||
@@ -255,6 +259,7 @@ class WebDavCalendarEntity(CoordinatorEntity[CalDavUpdateCoordinator], CalendarE
|
||||
}
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass update state from existing coordinator data."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import AuthorizationError, DAVError
|
||||
@@ -33,6 +33,7 @@ class CalDavConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -4,7 +4,7 @@ from datetime import date, datetime, time, timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
import caldav
|
||||
|
||||
@@ -90,6 +90,7 @@ class CalDavUpdateCoordinator(DataUpdateCoordinator[CalendarEvent | None]):
|
||||
|
||||
return event_list
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> CalendarEvent | None:
|
||||
"""Get the latest data."""
|
||||
start_of_today = dt_util.start_of_local_day()
|
||||
|
||||
@@ -4,7 +4,7 @@ import asyncio
|
||||
from datetime import date, datetime, timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any, cast, override
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import DAVError, NotFoundError
|
||||
@@ -121,6 +121,7 @@ class WebDavTodoListEntity(TodoListEntity):
|
||||
if (todo_item := _todo_item(resource)) is not None
|
||||
]
|
||||
|
||||
@override
|
||||
async def async_create_todo_item(self, item: TodoItem) -> None:
|
||||
"""Add an item to the To-do list."""
|
||||
item_data: dict[str, Any] = {}
|
||||
@@ -141,6 +142,7 @@ class WebDavTodoListEntity(TodoListEntity):
|
||||
except (requests.ConnectionError, requests.Timeout, DAVError) as err:
|
||||
raise HomeAssistantError(f"CalDAV save error: {err}") from err
|
||||
|
||||
@override
|
||||
async def async_update_todo_item(self, item: TodoItem) -> None:
|
||||
"""Update a To-do item."""
|
||||
uid: str = cast(str, item.uid)
|
||||
@@ -177,6 +179,7 @@ class WebDavTodoListEntity(TodoListEntity):
|
||||
except (requests.ConnectionError, requests.Timeout, DAVError) as err:
|
||||
raise HomeAssistantError(f"CalDAV save error: {err}") from err
|
||||
|
||||
@override
|
||||
async def async_delete_todo_items(self, uids: list[str]) -> None:
|
||||
"""Delete To-do items."""
|
||||
tasks = (
|
||||
|
||||
@@ -7,7 +7,7 @@ from http import HTTPStatus
|
||||
from itertools import groupby
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, Final, cast, final
|
||||
from typing import Any, Final, cast, final, override
|
||||
|
||||
from aiohttp import web
|
||||
from dateutil.rrule import rrulestr
|
||||
@@ -546,6 +546,7 @@ class CalendarEntity(Entity):
|
||||
return self.entity_description.initial_color
|
||||
return None
|
||||
|
||||
@override
|
||||
def get_initial_entity_options(self) -> er.EntityOptionsType | None:
|
||||
"""Return initial entity options."""
|
||||
if self.initial_color is None:
|
||||
@@ -564,6 +565,7 @@ class CalendarEntity(Entity):
|
||||
"""Return the next upcoming event."""
|
||||
raise NotImplementedError
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, Any] | None:
|
||||
@@ -580,6 +582,7 @@ class CalendarEntity(Entity):
|
||||
"description": event.description or "",
|
||||
}
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state(self) -> str:
|
||||
@@ -594,6 +597,7 @@ class CalendarEntity(Entity):
|
||||
|
||||
return STATE_OFF
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine.
|
||||
@@ -653,6 +657,7 @@ class CalendarEntity(Entity):
|
||||
self._event_listener_debouncer.async_cancel()
|
||||
self._event_listener_debouncer = None
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Run when entity will be removed from hass.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from typing import TYPE_CHECKING, Any, cast, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -113,6 +113,7 @@ class Timespan:
|
||||
"""
|
||||
return Timespan(self.end, max(self.end, now) + interval)
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
"""Return a string representing the half open interval time span."""
|
||||
return f"[{self.start}, {self.end})"
|
||||
@@ -325,6 +326,7 @@ class TargetCalendarEventListener(TargetEntityChangeTracker):
|
||||
self._pending_listener_task: asyncio.Task[None] | None = None
|
||||
self._calendar_event_listener: CalendarEventListener | None = None
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _handle_entities_update(self, tracked_entities: set[str]) -> None:
|
||||
"""Restart listeners when tracked target entities update."""
|
||||
@@ -351,6 +353,7 @@ class TargetCalendarEventListener(TargetEntityChangeTracker):
|
||||
)
|
||||
await self._calendar_event_listener.async_attach()
|
||||
|
||||
@override
|
||||
def _unsubscribe(self) -> None:
|
||||
"""Unsubscribe from all events."""
|
||||
super()._unsubscribe()
|
||||
@@ -367,6 +370,7 @@ class SingleEntityEventTrigger(Trigger):
|
||||
|
||||
_options: dict[str, Any]
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
async def async_validate_complete_config(
|
||||
cls, hass: HomeAssistant, complete_config: ConfigType
|
||||
@@ -377,6 +381,7 @@ class SingleEntityEventTrigger(Trigger):
|
||||
)
|
||||
return await super().async_validate_complete_config(hass, complete_config)
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
async def async_validate_config(
|
||||
cls, hass: HomeAssistant, config: ConfigType
|
||||
@@ -392,6 +397,7 @@ class SingleEntityEventTrigger(Trigger):
|
||||
assert config.options is not None
|
||||
self._options = config.options
|
||||
|
||||
@override
|
||||
async def async_attach_runner(
|
||||
self, run_action: TriggerActionRunner
|
||||
) -> CALLBACK_TYPE:
|
||||
@@ -426,6 +432,7 @@ class EventTrigger(Trigger):
|
||||
_options: dict[str, Any]
|
||||
_event_type: str
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
async def async_validate_config(
|
||||
cls, hass: HomeAssistant, config: ConfigType
|
||||
@@ -443,6 +450,7 @@ class EventTrigger(Trigger):
|
||||
self._target = config.target
|
||||
self._options = config.options
|
||||
|
||||
@override
|
||||
async def async_attach_runner(
|
||||
self, run_action: TriggerActionRunner
|
||||
) -> CALLBACK_TYPE:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for Cambridge Audio."""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
import voluptuous as vol
|
||||
@@ -29,6 +29,7 @@ class CambridgeAudioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Initialize the config flow."""
|
||||
self.data: dict[str, Any] = {}
|
||||
|
||||
@override
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: ZeroconfServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
@@ -81,6 +82,7 @@ class CambridgeAudioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable, Coroutine
|
||||
from functools import wraps
|
||||
from typing import Any, Concatenate
|
||||
from typing import Any, Concatenate, override
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
from aiostreammagic.models import CallbackType
|
||||
@@ -60,15 +60,18 @@ class CambridgeAudioEntity(Entity):
|
||||
"""Call when the device is notified of changes."""
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return self.client.is_connected()
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register callback handlers."""
|
||||
await self.client.register_state_update_callbacks(self._state_update_callback)
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Remove callbacks."""
|
||||
self.client.unregister_state_update_callbacks(self._state_update_callback)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for Cambridge Audio AV Receiver."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from aiostreammagic import (
|
||||
RepeatMode as CambridgeRepeatMode,
|
||||
@@ -83,6 +83,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
super().__init__(client)
|
||||
self._attr_unique_id = client.info.unit_id
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_features(self) -> MediaPlayerEntityFeature:
|
||||
"""Supported features for the media player."""
|
||||
@@ -100,6 +101,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
features |= feature
|
||||
return features
|
||||
|
||||
@override
|
||||
@property
|
||||
def state(self) -> MediaPlayerState:
|
||||
"""Return the state of the device."""
|
||||
@@ -118,11 +120,13 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
return MediaPlayerState.ON
|
||||
return MediaPlayerState.OFF
|
||||
|
||||
@override
|
||||
@property
|
||||
def source_list(self) -> list[str]:
|
||||
"""Return a list of available input sources."""
|
||||
return [item.name for item in self.client.sources]
|
||||
|
||||
@override
|
||||
@property
|
||||
def source(self) -> str | None:
|
||||
"""Return the current input source."""
|
||||
@@ -135,11 +139,13 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
None,
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_title(self) -> str | None:
|
||||
"""Title of current playing media."""
|
||||
return self.client.play_state.metadata.title
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_artist(self) -> str | None:
|
||||
"""Artist of current playing media, music track only."""
|
||||
@@ -151,52 +157,62 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
return self.client.play_state.metadata.station
|
||||
return self.client.play_state.metadata.artist
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_album_name(self) -> str | None:
|
||||
"""Album name of current playing media, music track only."""
|
||||
return self.client.play_state.metadata.album
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_image_url(self) -> str | None:
|
||||
"""Image url of current playing media."""
|
||||
return self.client.play_state.metadata.art_url
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_duration(self) -> int | None:
|
||||
"""Duration of the current media."""
|
||||
return self.client.play_state.metadata.duration
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_position(self) -> int | None:
|
||||
"""Position of the current media."""
|
||||
return self.client.play_state.position
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_position_updated_at(self) -> datetime:
|
||||
"""Last time the media position was updated."""
|
||||
return self.client.position_last_updated
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_channel(self) -> str | None:
|
||||
"""Channel currently playing."""
|
||||
return self.client.play_state.metadata.station
|
||||
|
||||
@override
|
||||
@property
|
||||
def is_volume_muted(self) -> bool | None:
|
||||
"""Volume mute status."""
|
||||
return self.client.state.mute
|
||||
|
||||
@override
|
||||
@property
|
||||
def volume_level(self) -> float | None:
|
||||
"""Current pre-amp volume level."""
|
||||
volume = self.client.state.volume_percent or 0
|
||||
return volume / 100
|
||||
|
||||
@override
|
||||
@property
|
||||
def shuffle(self) -> bool:
|
||||
"""Current shuffle configuration."""
|
||||
return self.client.play_state.mode_shuffle != ShuffleMode.OFF
|
||||
|
||||
@override
|
||||
@property
|
||||
def repeat(self) -> RepeatMode | None:
|
||||
"""Current repeat configuration."""
|
||||
@@ -205,11 +221,13 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
mode_repeat = RepeatMode.ALL
|
||||
return mode_repeat
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_play_pause(self) -> None:
|
||||
"""Toggle play/pause the current media."""
|
||||
await self.client.play_pause()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_pause(self) -> None:
|
||||
"""Pause the current media."""
|
||||
@@ -222,11 +240,13 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
else:
|
||||
await self.client.pause()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_stop(self) -> None:
|
||||
"""Stop the current media."""
|
||||
await self.client.stop()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_play(self) -> None:
|
||||
"""Play the current media."""
|
||||
@@ -239,16 +259,19 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
else:
|
||||
await self.client.play()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_next_track(self) -> None:
|
||||
"""Skip to the next track."""
|
||||
await self.client.next_track()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_previous_track(self) -> None:
|
||||
"""Skip to the previous track."""
|
||||
await self.client.previous_track()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_select_source(self, source: str) -> None:
|
||||
"""Select the source."""
|
||||
@@ -257,41 +280,49 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
await self.client.set_source_by_id(src.id)
|
||||
break
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Power on the device."""
|
||||
await self.client.power_on()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Power off the device."""
|
||||
await self.client.power_off()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_volume_up(self) -> None:
|
||||
"""Step the volume up."""
|
||||
await self.client.volume_up()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_volume_down(self) -> None:
|
||||
"""Step the volume down."""
|
||||
await self.client.volume_down()
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_set_volume_level(self, volume: float) -> None:
|
||||
"""Set the volume level."""
|
||||
await self.client.set_volume(int(volume * 100))
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_mute_volume(self, mute: bool) -> None:
|
||||
"""Set the mute state."""
|
||||
await self.client.set_mute(mute)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_media_seek(self, position: float) -> None:
|
||||
"""Seek to a position in the current media."""
|
||||
await self.client.media_seek(int(position))
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_set_shuffle(self, shuffle: bool) -> None:
|
||||
"""Set the shuffle mode for the current queue."""
|
||||
@@ -300,6 +331,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
shuffle_mode = ShuffleMode.ALL
|
||||
await self.client.set_shuffle(shuffle_mode)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_set_repeat(self, repeat: RepeatMode) -> None:
|
||||
"""Set the repeat mode for the current queue."""
|
||||
@@ -308,6 +340,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
repeat_mode = CambridgeRepeatMode.ALL
|
||||
await self.client.set_repeat(repeat_mode)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_play_media(
|
||||
self, media_type: MediaType | str, media_id: str, **kwargs: Any
|
||||
@@ -353,6 +386,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
if media_type == CAMBRIDGE_MEDIA_TYPE_INTERNET_RADIO:
|
||||
await self.client.play_radio_url("Radio", media_id)
|
||||
|
||||
@override
|
||||
async def async_browse_media(
|
||||
self,
|
||||
media_content_type: MediaType | str | None = None,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
|
||||
@@ -90,16 +90,19 @@ class CambridgeAudioNumber(CambridgeAudioEntity, NumberEntity):
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{client.info.unit_id}-{description.key}"
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> int | None:
|
||||
"""Return the state of the number."""
|
||||
return self.entity_description.value_fn(self.client)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set the selected value."""
|
||||
await self.entity_description.set_value_fn(self.client, int(value))
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import override
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
from aiostreammagic.models import ControlBusMode, DisplayBrightness
|
||||
@@ -127,11 +128,13 @@ class CambridgeAudioSelect(CambridgeAudioEntity, SelectEntity):
|
||||
if options_fn:
|
||||
self._attr_options = options_fn
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the state of the select."""
|
||||
return self.entity_description.value_fn(self.client)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
|
||||
@@ -103,16 +103,19 @@ class CambridgeAudioSwitch(CambridgeAudioEntity, SwitchEntity):
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{client.info.unit_id}-{description.key}"
|
||||
|
||||
@override
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the switch."""
|
||||
return self.entity_description.value_fn(self.client)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
await self.entity_description.set_value_fn(self.client, True)
|
||||
|
||||
@override
|
||||
@command
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
|
||||
@@ -12,7 +12,7 @@ import logging
|
||||
import os
|
||||
from random import SystemRandom
|
||||
import time
|
||||
from typing import Any, Final, final
|
||||
from typing import Any, Final, final, override
|
||||
|
||||
from aiohttp import hdrs, web
|
||||
import attr
|
||||
@@ -455,6 +455,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
!= Camera.async_handle_async_webrtc_offer
|
||||
)
|
||||
|
||||
@override
|
||||
@cached_property
|
||||
def entity_picture(self) -> str:
|
||||
"""Return a link to the camera feed as entity picture."""
|
||||
@@ -467,6 +468,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Whether or not to use stream to generate stills."""
|
||||
return False
|
||||
|
||||
@override
|
||||
@cached_property
|
||||
def supported_features(self) -> CameraEntityFeature:
|
||||
"""Flag supported features."""
|
||||
@@ -502,6 +504,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Return the interval between frames of the mjpeg stream."""
|
||||
return self._attr_frame_interval
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
@@ -595,6 +598,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""
|
||||
return await self.handle_async_still_stream(request, self.frame_interval)
|
||||
|
||||
@override
|
||||
@property
|
||||
@final
|
||||
def state(self) -> str:
|
||||
@@ -642,6 +646,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Call the job and disable motion detection."""
|
||||
await self.hass.async_add_executor_job(self.disable_motion_detection)
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, str | None]:
|
||||
@@ -665,6 +670,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
self.access_tokens.append(hex(_RND.getrandbits(256))[2:])
|
||||
self.__dict__.pop("entity_picture", None)
|
||||
|
||||
@override
|
||||
async def async_internal_added_to_hass(self) -> None:
|
||||
"""Run when entity about to be added to hass."""
|
||||
await super().async_internal_added_to_hass()
|
||||
@@ -758,6 +764,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
|
||||
return CameraCapabilities(frontend_stream_types)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine.
|
||||
@@ -817,6 +824,7 @@ class CameraImageView(CameraView):
|
||||
url = "/api/camera_proxy/{entity_id}"
|
||||
name = "api:camera:image"
|
||||
|
||||
@override
|
||||
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
|
||||
"""Serve camera image."""
|
||||
width = request.query.get("width")
|
||||
@@ -840,6 +848,7 @@ class CameraMjpegStream(CameraView):
|
||||
url = "/api/camera_proxy_stream/{entity_id}"
|
||||
name = "api:camera:stream"
|
||||
|
||||
@override
|
||||
async def handle(self, request: web.Request, camera: Camera) -> web.StreamResponse:
|
||||
"""Serve camera stream, possibly with interval."""
|
||||
if (interval_str := request.query.get("interval")) is None:
|
||||
@@ -985,6 +994,7 @@ class _TemplateCameraEntity:
|
||||
self._report_issue()
|
||||
return getattr(self._camera, name)
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
"""Forward to the camera entity."""
|
||||
self._report_issue()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Expose cameras as media sources."""
|
||||
|
||||
import asyncio
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.media_player import BrowseError, MediaClass
|
||||
from homeassistant.components.media_source import (
|
||||
@@ -54,6 +55,7 @@ class CameraMediaSource(MediaSource):
|
||||
super().__init__(DOMAIN)
|
||||
self.hass = hass
|
||||
|
||||
@override
|
||||
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
|
||||
"""Resolve media to a url."""
|
||||
component = self.hass.data[DATA_COMPONENT]
|
||||
@@ -82,6 +84,7 @@ class CameraMediaSource(MediaSource):
|
||||
|
||||
return PlayMedia(url, FORMAT_CONTENT_TYPE[HLS_PROVIDER])
|
||||
|
||||
@override
|
||||
async def async_browse_media(
|
||||
self,
|
||||
item: MediaSourceItem,
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections.abc import Awaitable, Callable
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from functools import cache, partial, wraps
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from mashumaro import MissingField
|
||||
import voluptuous as vol
|
||||
@@ -73,6 +73,7 @@ class WebRTCCandidate(WebRTCMessage):
|
||||
|
||||
candidate: RTCIceCandidate | RTCIceCandidateInit
|
||||
|
||||
@override
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
"""Return a dict representation of the message."""
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Support for Canary alarm."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from canary.const import LOCATION_MODE_AWAY, LOCATION_MODE_HOME, LOCATION_MODE_NIGHT
|
||||
from canary.model import Location
|
||||
@@ -58,6 +58,7 @@ class CanaryAlarm(
|
||||
"""Return information about the location."""
|
||||
return self.coordinator.data["locations"][self._location_id]
|
||||
|
||||
@override
|
||||
@property
|
||||
def alarm_state(self) -> AlarmControlPanelState | None:
|
||||
"""Return the state of the device."""
|
||||
@@ -74,25 +75,30 @@ class CanaryAlarm(
|
||||
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
return {"private": self.location.is_private}
|
||||
|
||||
@override
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
self.coordinator.canary.set_location_mode(
|
||||
self._location_id, self.location.mode.name, True
|
||||
)
|
||||
|
||||
@override
|
||||
def alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
self.coordinator.canary.set_location_mode(self._location_id, LOCATION_MODE_HOME)
|
||||
|
||||
@override
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
self.coordinator.canary.set_location_mode(self._location_id, LOCATION_MODE_AWAY)
|
||||
|
||||
@override
|
||||
def alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
self.coordinator.canary.set_location_mode(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Final
|
||||
from typing import Final, override
|
||||
|
||||
from aiohttp.web import Request, StreamResponse
|
||||
from canary.live_stream_api import LiveStreamSession
|
||||
@@ -108,16 +108,19 @@ class CanaryCamera(CoordinatorEntity[CanaryDataUpdateCoordinator], Camera):
|
||||
"""Return information about the location."""
|
||||
return self.coordinator.data["locations"][self._location_id]
|
||||
|
||||
@override
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
"""Return true if the device is recording."""
|
||||
return self.location.is_recording # type: ignore[no-any-return]
|
||||
|
||||
@override
|
||||
@property
|
||||
def motion_detection_enabled(self) -> bool:
|
||||
"""Return the camera motion detection status."""
|
||||
return not self.location.is_recording
|
||||
|
||||
@override
|
||||
async def async_camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
@@ -150,6 +153,7 @@ class CanaryCamera(CoordinatorEntity[CanaryDataUpdateCoordinator], Camera):
|
||||
|
||||
return self._image
|
||||
|
||||
@override
|
||||
async def handle_async_mjpeg_stream(
|
||||
self, request: Request
|
||||
) -> StreamResponse | None:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for Canary."""
|
||||
|
||||
import logging
|
||||
from typing import Any, Final
|
||||
from typing import Any, Final, override
|
||||
|
||||
from canary.api import Api
|
||||
from requests.exceptions import ConnectTimeout, HTTPError
|
||||
@@ -46,12 +46,14 @@ class CanaryConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
|
||||
"""Get the options flow for this handler."""
|
||||
return CanaryOptionsFlowHandler()
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -4,6 +4,7 @@ import asyncio
|
||||
from collections.abc import ValuesView
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from canary.api import Api
|
||||
from canary.model import Location, Reading
|
||||
@@ -60,6 +61,7 @@ class CanaryDataUpdateCoordinator(DataUpdateCoordinator[CanaryData]):
|
||||
"readings": readings_by_device_id,
|
||||
}
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> CanaryData:
|
||||
"""Fetch data from Canary."""
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Support for Canary sensors."""
|
||||
|
||||
from typing import Final
|
||||
from typing import Final, override
|
||||
|
||||
from canary.model import Device, Location, SensorType
|
||||
|
||||
@@ -143,11 +143,13 @@ class CanarySensor(CoordinatorEntity[CanaryDataUpdateCoordinator], SensorEntity)
|
||||
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self.reading
|
||||
|
||||
@override
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the state attributes."""
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Casper Glow integration binary sensor platform."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
@@ -43,6 +45,7 @@ class CasperGlowPausedBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
||||
if coordinator.device.state.is_paused is not None:
|
||||
self._attr_is_on = coordinator.device.state.is_paused
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register state update callback when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
@@ -71,6 +74,7 @@ class CasperGlowChargingBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
||||
if coordinator.device.state.is_charging is not None:
|
||||
self._attr_is_on = coordinator.device.state.is_charging
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register state update callback when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import CasperGlow
|
||||
|
||||
@@ -66,6 +67,7 @@ class CasperGlowButton(CasperGlowEntity, ButtonEntity):
|
||||
f"{format_mac(coordinator.device.address)}_{description.key}"
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_press(self) -> None:
|
||||
"""Press the button."""
|
||||
await self._async_command(self.entity_description.press_fn(self._device))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for Casper Glow integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from bluetooth_data_tools import human_readable_name
|
||||
from pycasperglow import CasperGlow, CasperGlowError
|
||||
@@ -31,6 +31,7 @@ class CasperGlowConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_devices: dict[str, BluetoothServiceInfoBleak] = {}
|
||||
|
||||
@override
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> ConfigFlowResult:
|
||||
@@ -73,6 +74,7 @@ class CasperGlowConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
description_placeholders=self.context["title_placeholders"],
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Coordinator for the Casper Glow integration."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from bleak import BleakError
|
||||
from bluetooth_data_tools import monotonic_time_coarse
|
||||
@@ -74,6 +75,7 @@ class CasperGlowCoordinator(ActiveBluetoothDataUpdateCoordinator[None]):
|
||||
"""Poll device state."""
|
||||
await self.device.query_state()
|
||||
|
||||
@override
|
||||
async def _async_poll(self) -> None:
|
||||
"""Poll the device and log availability changes."""
|
||||
assert self._last_service_info
|
||||
@@ -99,6 +101,7 @@ class CasperGlowCoordinator(ActiveBluetoothDataUpdateCoordinator[None]):
|
||||
|
||||
self._async_handle_bluetooth_poll()
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _async_handle_bluetooth_event(
|
||||
self,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Casper Glow integration light platform."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
@@ -54,6 +54,7 @@ class CasperGlowLight(CasperGlowEntity, LightEntity):
|
||||
self._attr_unique_id = format_mac(coordinator.device.address)
|
||||
self._update_from_state(coordinator.device.state)
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register state update callback when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
@@ -77,6 +78,7 @@ class CasperGlowLight(CasperGlowEntity, LightEntity):
|
||||
self._update_from_state(state)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
brightness_pct: int | None = None
|
||||
@@ -98,6 +100,7 @@ class CasperGlowLight(CasperGlowEntity, LightEntity):
|
||||
self._attr_brightness = _device_pct_to_ha_brightness(brightness_pct)
|
||||
self.coordinator.last_brightness_pct = brightness_pct
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
await self._async_command(self._device.turn_off())
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Casper Glow integration select platform for dimming time."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
@@ -38,6 +40,7 @@ class CasperGlowDimmingTimeSelect(CasperGlowEntity, SelectEntity, RestoreEntity)
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = f"{format_mac(coordinator.device.address)}_dimming_time"
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the currently selected dimming time from the coordinator."""
|
||||
@@ -45,6 +48,7 @@ class CasperGlowDimmingTimeSelect(CasperGlowEntity, SelectEntity, RestoreEntity)
|
||||
return None
|
||||
return str(self.coordinator.last_dimming_time_minutes)
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Restore last known dimming time and register state update callback."""
|
||||
await super().async_added_to_hass()
|
||||
@@ -75,6 +79,7 @@ class CasperGlowDimmingTimeSelect(CasperGlowEntity, SelectEntity, RestoreEntity)
|
||||
# to update the current state.
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Set the dimming time."""
|
||||
await self._async_command(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Casper Glow integration sensor platform."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
@@ -51,6 +52,7 @@ class CasperGlowBatterySensor(CasperGlowEntity, SensorEntity):
|
||||
if coordinator.device.state.battery_level is not None:
|
||||
self._attr_native_value = coordinator.device.state.battery_level.percentage
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register state update callback when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
@@ -93,6 +95,7 @@ class CasperGlowDimmingEndTimeSensor(CasperGlowEntity, SensorEntity):
|
||||
"""Calculate projected dimming end time from remaining milliseconds."""
|
||||
return utcnow() + timedelta(milliseconds=remaining_ms)
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register state update callback when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Config flow for Cast."""
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -47,6 +47,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
@@ -55,12 +56,14 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Get the options flow for this handler."""
|
||||
return CastOptionsFlowHandler()
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
return await self.async_step_config()
|
||||
|
||||
@override
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: ZeroconfServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
import threading
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
import pychromecast.discovery
|
||||
import pychromecast.models
|
||||
@@ -67,14 +67,17 @@ def setup_internal_discovery(
|
||||
class CastListener(pychromecast.discovery.AbstractCastListener):
|
||||
"""Listener for discovering chromecasts."""
|
||||
|
||||
@override
|
||||
def add_cast(self, uuid, _):
|
||||
"""Handle zeroconf discovery of a new chromecast."""
|
||||
discover_chromecast(hass, browser.devices[uuid], config_entry)
|
||||
|
||||
@override
|
||||
def update_cast(self, uuid, _):
|
||||
"""Handle zeroconf discovery of an updated chromecast."""
|
||||
discover_chromecast(hass, browser.devices[uuid], config_entry)
|
||||
|
||||
@override
|
||||
def remove_cast(self, uuid, service, cast_info):
|
||||
"""Handle zeroconf discovery of a removed chromecast."""
|
||||
_remove_chromecast(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import configparser
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, ClassVar
|
||||
from typing import TYPE_CHECKING, ClassVar, override
|
||||
from urllib.parse import urlparse
|
||||
from uuid import UUID
|
||||
|
||||
@@ -180,37 +180,45 @@ class CastStatusListener(
|
||||
if not cast_device._cast_info.is_audio_group: # noqa: SLF001
|
||||
self._mz_mgr.register_listener(chromecast.uuid, self)
|
||||
|
||||
@override
|
||||
def new_cast_status(self, status):
|
||||
"""Handle reception of a new CastStatus."""
|
||||
if self._valid:
|
||||
self._cast_device.new_cast_status(status)
|
||||
|
||||
@override
|
||||
def new_media_status(self, status):
|
||||
"""Handle reception of a new MediaStatus."""
|
||||
if self._valid:
|
||||
self._cast_device.new_media_status(status)
|
||||
|
||||
@override
|
||||
def load_media_failed(self, queue_item_id, error_code):
|
||||
"""Handle reception of a new MediaStatus."""
|
||||
if self._valid:
|
||||
self._cast_device.load_media_failed(queue_item_id, error_code)
|
||||
|
||||
@override
|
||||
def new_connection_status(self, status):
|
||||
"""Handle reception of a new ConnectionStatus."""
|
||||
if self._valid:
|
||||
self._cast_device.new_connection_status(status)
|
||||
|
||||
@override
|
||||
def added_to_multizone(self, group_uuid):
|
||||
"""Handle the cast added to a group."""
|
||||
|
||||
@override
|
||||
def removed_from_multizone(self, group_uuid):
|
||||
"""Handle the cast removed from a group."""
|
||||
if self._valid:
|
||||
self._cast_device.multizone_new_media_status(group_uuid, None)
|
||||
|
||||
@override
|
||||
def multizone_new_cast_status(self, group_uuid, cast_status):
|
||||
"""Handle reception of a new CastStatus for a group."""
|
||||
|
||||
@override
|
||||
def multizone_new_media_status(self, group_uuid, media_status):
|
||||
"""Handle reception of a new MediaStatus for a group."""
|
||||
if self._valid:
|
||||
|
||||
@@ -6,7 +6,7 @@ from datetime import datetime
|
||||
from functools import wraps
|
||||
import json
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Concatenate
|
||||
from typing import TYPE_CHECKING, Any, Concatenate, override
|
||||
|
||||
import pychromecast.config
|
||||
import pychromecast.const
|
||||
@@ -338,6 +338,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
]:
|
||||
self._attr_device_class = MediaPlayerDeviceClass.SPEAKER
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Create chromecast object when added to hass."""
|
||||
self._async_setup(self.entity_id)
|
||||
@@ -346,6 +347,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self.hass, SIGNAL_HASS_CAST_SHOW_VIEW, self._handle_signal_show_view
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Disconnect Chromecast object when removed."""
|
||||
await self._async_tear_down()
|
||||
@@ -354,6 +356,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self._cast_view_remove_handler()
|
||||
self._cast_view_remove_handler = None
|
||||
|
||||
@override
|
||||
async def _async_connect_to_chromecast(self):
|
||||
"""Set up the chromecast object."""
|
||||
await super()._async_connect_to_chromecast()
|
||||
@@ -363,6 +366,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self.media_status = self._chromecast.media_controller.status
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
async def _async_disconnect(self):
|
||||
"""Disconnect Chromecast object if it is set."""
|
||||
await super()._async_disconnect()
|
||||
@@ -370,6 +374,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self._attr_available = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
def _invalidate(self):
|
||||
"""Invalidate some attributes."""
|
||||
super()._invalidate()
|
||||
@@ -528,6 +533,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
"""Start an app."""
|
||||
self._get_chromecast().start_app(app_id)
|
||||
|
||||
@override
|
||||
def turn_on(self) -> None:
|
||||
"""Turn on the cast device."""
|
||||
|
||||
@@ -547,51 +553,60 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
else:
|
||||
self._start_app(pychromecast.config.APP_MEDIA_RECEIVER)
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def turn_off(self) -> None:
|
||||
"""Turn off the cast device."""
|
||||
self._get_chromecast().quit_app()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def mute_volume(self, mute: bool) -> None:
|
||||
"""Mute the volume."""
|
||||
self._get_chromecast().set_volume_muted(mute)
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def set_volume_level(self, volume: float) -> None:
|
||||
"""Set volume level, range 0..1."""
|
||||
self._get_chromecast().set_volume(volume)
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_play(self) -> None:
|
||||
"""Send play command."""
|
||||
media_controller = self._media_controller()
|
||||
media_controller.play()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_pause(self) -> None:
|
||||
"""Send pause command."""
|
||||
media_controller = self._media_controller()
|
||||
media_controller.pause()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_stop(self) -> None:
|
||||
"""Send stop command."""
|
||||
media_controller = self._media_controller()
|
||||
media_controller.stop()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_previous_track(self) -> None:
|
||||
"""Send previous track command."""
|
||||
media_controller = self._media_controller()
|
||||
media_controller.queue_prev()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_next_track(self) -> None:
|
||||
"""Send next track command."""
|
||||
media_controller = self._media_controller()
|
||||
media_controller.queue_next()
|
||||
|
||||
@override
|
||||
@api_error
|
||||
def media_seek(self, position: float) -> None:
|
||||
"""Seek the media to a specific location."""
|
||||
@@ -636,6 +651,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
children=sorted(children, key=lambda c: c.title),
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_browse_media(
|
||||
self,
|
||||
media_content_type: MediaType | str | None = None,
|
||||
@@ -675,6 +691,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self.hass, media_content_id, content_filter=content_filter
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_play_media(
|
||||
self, media_type: MediaType | str, media_id: str, **kwargs: Any
|
||||
) -> None:
|
||||
@@ -816,6 +833,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return (media_status, media_status_received)
|
||||
|
||||
@override
|
||||
@property
|
||||
def state(self) -> MediaPlayerState | None:
|
||||
"""Return the state of the player."""
|
||||
@@ -858,6 +876,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return MediaPlayerState.IDLE
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_content_id(self) -> str | None:
|
||||
"""Content ID of current playing media."""
|
||||
@@ -867,6 +886,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.content_id if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_content_type(self) -> MediaType | None:
|
||||
"""Content type of current playing media."""
|
||||
@@ -891,6 +911,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return MediaType.VIDEO
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_duration(self):
|
||||
"""Duration of current playing media in seconds."""
|
||||
@@ -900,6 +921,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.duration if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_image_url(self):
|
||||
"""Image url of current playing media."""
|
||||
@@ -910,64 +932,75 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return images[0].url if images and images[0].url else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Title of current playing media."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.title if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_artist(self):
|
||||
"""Artist of current playing media (Music track only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.artist if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_album_name(self):
|
||||
"""Album of current playing media (Music track only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.album_name if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_album_artist(self):
|
||||
"""Album artist of current playing media (Music track only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.album_artist if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_track(self):
|
||||
"""Track number of current playing media (Music track only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.track if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_series_title(self):
|
||||
"""Return the title of the series of current playing media."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.series_title if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_season(self):
|
||||
"""Season of current playing media (TV Show only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.season if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_episode(self):
|
||||
"""Episode of current playing media (TV Show only)."""
|
||||
media_status = self._media_status()[0]
|
||||
return media_status.episode if media_status else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def app_id(self):
|
||||
"""Return the ID of the current running app."""
|
||||
return self._chromecast.app_id if self._chromecast else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def app_name(self):
|
||||
"""Name of the current running app."""
|
||||
return self._chromecast.app_display_name if self._chromecast else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_features(self) -> MediaPlayerEntityFeature:
|
||||
"""Flag media player features that are supported."""
|
||||
@@ -1006,6 +1039,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return support
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_position(self):
|
||||
"""Position of current playing media in seconds."""
|
||||
@@ -1021,6 +1055,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
return None
|
||||
return media_status.current_time
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_position_updated_at(self):
|
||||
"""When was the position of the current playing media valid.
|
||||
@@ -1075,6 +1110,7 @@ class DynamicCastGroup(CastDevice):
|
||||
"""Create chromecast object."""
|
||||
self._async_setup("Dynamic group")
|
||||
|
||||
@override
|
||||
async def _async_cast_removed(self, discover: ChromecastInfo):
|
||||
"""Handle removal of Chromecast."""
|
||||
if self._cast_info.uuid != discover.uuid:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Climate device for CCM15 coordinator."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from ccm15 import CCM15DeviceState, CCM15SlaveDevice
|
||||
|
||||
@@ -93,6 +93,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
"""Return device data."""
|
||||
return self.coordinator.get_ac_data(self._ac_index)
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_temperature(self) -> int | None:
|
||||
"""Return current temperature."""
|
||||
@@ -100,6 +101,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return data.temperature
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature(self) -> int | None:
|
||||
"""Return target temperature."""
|
||||
@@ -107,6 +109,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return data.temperature_setpoint
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return hvac mode."""
|
||||
@@ -115,6 +118,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return CONST_CMD_STATE_MAP[mode]
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
"""Return fan mode."""
|
||||
@@ -123,6 +127,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return CONST_CMD_FAN_MAP[mode]
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def swing_mode(self) -> str | None:
|
||||
"""Return swing mode."""
|
||||
@@ -130,11 +135,13 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return SWING_ON if data.is_swing_on else SWING_OFF
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return the availability of the entity."""
|
||||
return self.data is not None
|
||||
|
||||
@override
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the optional state attributes."""
|
||||
@@ -142,6 +149,7 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return {"error_code": data.error_code}
|
||||
return {}
|
||||
|
||||
@override
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the target temperature."""
|
||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is not None:
|
||||
@@ -149,18 +157,22 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
self._ac_index, self.data, temperature, kwargs.get(ATTR_HVAC_MODE)
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the hvac mode."""
|
||||
await self.coordinator.async_set_hvac_mode(self._ac_index, self.data, hvac_mode)
|
||||
|
||||
@override
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set the fan mode."""
|
||||
await self.coordinator.async_set_fan_mode(self._ac_index, self.data, fan_mode)
|
||||
|
||||
@override
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off."""
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
|
||||
@override
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on."""
|
||||
await self.async_set_hvac_mode(HVACMode.AUTO)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for Midea ccm15 AC Controller integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from ccm15 import CCM15Device
|
||||
import voluptuous as vol
|
||||
@@ -27,6 +27,7 @@ class CCM15ConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice
|
||||
import httpx
|
||||
@@ -44,6 +45,7 @@ class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
|
||||
"""Get the host."""
|
||||
return self._host
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> CCM15DeviceState:
|
||||
"""Fetch data from Rain Bird device."""
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for the CentriConnect/MyPropane API integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from aiocentriconnect import CentriConnect
|
||||
from aiocentriconnect.exceptions import (
|
||||
@@ -58,6 +58,7 @@ class CentriConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -6,6 +6,7 @@ Responsible for polling the device API endpoint and normalizing data for entitie
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from aiocentriconnect import CentriConnect, Tank
|
||||
from aiocentriconnect.exceptions import CentriConnectConnectionError, CentriConnectError
|
||||
@@ -65,6 +66,7 @@ class CentriConnectCoordinator(DataUpdateCoordinator[Tank]):
|
||||
session=async_get_clientsession(hass),
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_setup(self) -> None:
|
||||
try:
|
||||
tank_data = await self.api_client.async_get_tank_data()
|
||||
@@ -79,6 +81,7 @@ class CentriConnectCoordinator(DataUpdateCoordinator[Tank]):
|
||||
tank_size_unit=tank_data.tank_size_unit,
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> Tank:
|
||||
"""Fetch device state."""
|
||||
try:
|
||||
|
||||
@@ -4,6 +4,7 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import StrEnum
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
EntityCategory,
|
||||
@@ -236,6 +237,7 @@ class CentriConnectSensor(CentriConnectBaseEntity, SensorEntity):
|
||||
|
||||
entity_description: CentriConnectSensorEntityDescription
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> StateType | datetime | None:
|
||||
"""Return the state of the sensor."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for the Cert Expiry platform."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -53,6 +53,7 @@ class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
return True
|
||||
return False
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self,
|
||||
user_input: Mapping[str, Any] | None = None,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -46,6 +47,7 @@ class CertExpiryDataUpdateCoordinator(DataUpdateCoordinator[datetime | None]):
|
||||
always_update=False,
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> datetime | None:
|
||||
"""Fetch certificate."""
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Counter for the days until an HTTPS (TLS) certificate will expire."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -12,6 +12,7 @@ class CertExpiryEntity(CoordinatorEntity[CertExpiryDataUpdateCoordinator]):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
@override
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return additional sensor state attributes."""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Counter for the days until an HTTPS (TLS) certificate will expire."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -44,6 +45,7 @@ class SSLCertificateTimestamp(CertExpiryEntity, SensorEntity):
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> datetime | None:
|
||||
"""Return the state of the sensor."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for chacon_dio integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from dio_chacon_wifi_api import DIOChaconAPIClient
|
||||
from dio_chacon_wifi_api.exceptions import DIOChaconAPIError, DIOChaconInvalidAuthError
|
||||
@@ -25,6 +25,7 @@ DATA_SCHEMA = vol.Schema(
|
||||
class ChaconDioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for chacon_dio."""
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Cover Platform for Chacon Dio REV-SHUTTER devices."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from dio_chacon_wifi_api.const import DeviceTypeEnum, ShutterMoveEnum
|
||||
|
||||
@@ -49,6 +49,7 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
| CoverEntityFeature.SET_POSITION
|
||||
)
|
||||
|
||||
@override
|
||||
def _update_attr(self, data: dict[str, Any]) -> None:
|
||||
"""Recompute the attribute values on init or state change."""
|
||||
self._attr_available = data["connected"]
|
||||
@@ -57,6 +58,7 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
self._attr_is_opening = data["movement"] == ShutterMoveEnum.UP.value
|
||||
self._attr_is_closed = self._attr_current_cover_position == 0
|
||||
|
||||
@override
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close the cover.
|
||||
|
||||
@@ -80,6 +82,7 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
self.target_id, ShutterMoveEnum.DOWN
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover.
|
||||
|
||||
@@ -101,6 +104,7 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
|
||||
await self.client.move_shutter_direction(self.target_id, ShutterMoveEnum.UP)
|
||||
|
||||
@override
|
||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||
"""Stop the cover."""
|
||||
|
||||
@@ -112,6 +116,7 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
|
||||
await self.client.move_shutter_direction(self.target_id, ShutterMoveEnum.STOP)
|
||||
|
||||
@override
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Set the cover open position in percentage.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Base entity for the Chacon Dio entity."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from dio_chacon_wifi_api import DIOChaconAPIClient
|
||||
|
||||
@@ -38,6 +38,7 @@ class ChaconDioEntity(Entity):
|
||||
def _update_attr(self, data: dict[str, Any]) -> None:
|
||||
"""Recomputes the attributes values."""
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register the callback for server side events."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Switch Platform for Chacon Dio REV-LIGHT and switch plug devices."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from dio_chacon_wifi_api.const import DeviceTypeEnum
|
||||
|
||||
@@ -38,11 +38,13 @@ class ChaconDioSwitch(ChaconDioEntity, SwitchEntity):
|
||||
_attr_device_class = SwitchDeviceClass.SWITCH
|
||||
_attr_name = None
|
||||
|
||||
@override
|
||||
def _update_attr(self, data: dict[str, Any]) -> None:
|
||||
"""Recompute the attribute values on init or state change."""
|
||||
self._attr_available = data["connected"]
|
||||
self._attr_is_on = data["is_on"]
|
||||
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the switch.
|
||||
|
||||
@@ -59,6 +61,7 @@ class ChaconDioSwitch(ChaconDioEntity, SwitchEntity):
|
||||
|
||||
await self.client.switch_switch(self.target_id, True)
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the switch.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Support for interfacing with an instance of getchannels.com."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from pychannels import Channels
|
||||
import voluptuous as vol
|
||||
@@ -138,11 +138,13 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
self.now_playing_summary = None
|
||||
self.now_playing_image_url = None
|
||||
|
||||
@override
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the player."""
|
||||
return self._name
|
||||
|
||||
@override
|
||||
@property
|
||||
def state(self) -> MediaPlayerState | None:
|
||||
"""Return the state of the player."""
|
||||
@@ -162,21 +164,25 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
self.update_favorite_channels()
|
||||
self.update_state(self.client.status())
|
||||
|
||||
@override
|
||||
@property
|
||||
def source_list(self):
|
||||
"""List of favorite channels."""
|
||||
return [channel["name"] for channel in self.favorite_channels]
|
||||
|
||||
@override
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
"""Boolean if volume is currently muted."""
|
||||
return self.muted
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_content_id(self):
|
||||
"""Content ID of current playing channel."""
|
||||
return self.channel_number
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_image_url(self):
|
||||
"""Image url of current playing media."""
|
||||
@@ -187,6 +193,7 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
|
||||
return "https://getchannels.com/assets/img/icon-1024.png"
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Title of current playing media."""
|
||||
@@ -195,38 +202,45 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
|
||||
return None
|
||||
|
||||
@override
|
||||
def mute_volume(self, mute: bool) -> None:
|
||||
"""Mute (true) or unmute (false) player."""
|
||||
if mute != self.muted:
|
||||
response = self.client.toggle_muted()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def media_stop(self) -> None:
|
||||
"""Send media_stop command to player."""
|
||||
self.status = "stopped"
|
||||
response = self.client.stop()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def media_play(self) -> None:
|
||||
"""Send media_play command to player."""
|
||||
response = self.client.resume()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def media_pause(self) -> None:
|
||||
"""Send media_pause command to player."""
|
||||
response = self.client.pause()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def media_next_track(self) -> None:
|
||||
"""Seek ahead."""
|
||||
response = self.client.skip_forward()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def media_previous_track(self) -> None:
|
||||
"""Seek back."""
|
||||
response = self.client.skip_backward()
|
||||
self.update_state(response)
|
||||
|
||||
@override
|
||||
def select_source(self, source: str) -> None:
|
||||
"""Select a channel to tune to."""
|
||||
for channel in self.favorite_channels:
|
||||
@@ -235,6 +249,7 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
self.update_state(response)
|
||||
break
|
||||
|
||||
@override
|
||||
def play_media(
|
||||
self, media_type: MediaType | str, media_id: str, **kwargs: Any
|
||||
) -> None:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Config flow for the Chess.com integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from chess_com_api import ChessComClient, NotFoundError
|
||||
import voluptuous as vol
|
||||
@@ -18,6 +18,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class ChessConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Chess.com."""
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from chess_com_api import ChessComAPIError, ChessComClient, Player, PlayerStats
|
||||
|
||||
@@ -45,6 +46,7 @@ class ChessCoordinator(DataUpdateCoordinator[ChessData]):
|
||||
)
|
||||
self.client = ChessComClient(session=async_get_clientsession(hass))
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> ChessData:
|
||||
"""Update data from Chess.com."""
|
||||
try:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from chess_com_api import PlayerStats
|
||||
|
||||
@@ -121,6 +121,7 @@ class ChessPlayerSensor(ChessEntity, SensorEntity):
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.config_entry.unique_id}.{description.key}"
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
"""Return the state of the sensor."""
|
||||
@@ -148,6 +149,7 @@ class ChessGameModeSensor(ChessEntity, SensorEntity):
|
||||
)
|
||||
self._attr_translation_key = f"{game_mode}_{description.translation_key}"
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
"""Return the state of the sensor."""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any, Concatenate
|
||||
from typing import Any, Concatenate, override
|
||||
|
||||
from cieloconnectapi.exceptions import AuthenticationError
|
||||
|
||||
@@ -103,6 +103,7 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
super().__init__(coordinator, device_id)
|
||||
self._attr_unique_id = device_id
|
||||
|
||||
@override
|
||||
@property
|
||||
def temperature_unit(self) -> str:
|
||||
"""Return the unit of temperature in Home Assistant format.
|
||||
@@ -124,6 +125,7 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
|
||||
return UnitOfTemperature.CELSIUS
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_features(self) -> ClimateEntityFeature:
|
||||
"""Return dynamic feature flags based on the current mode."""
|
||||
@@ -147,6 +149,7 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
|
||||
return flags
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_humidity(self) -> int | None:
|
||||
"""Return the current humidity, if available."""
|
||||
@@ -154,58 +157,69 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
return self.device_data.humidity
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature_low(self) -> float | None:
|
||||
"""Return the low target temperature for HEAT_COOL mode."""
|
||||
return self.client.target_temperature_low(self.temperature_unit)
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature_high(self) -> float | None:
|
||||
"""Return the high target temperature for HEAT_COOL mode."""
|
||||
return self.client.target_temperature_high(self.temperature_unit)
|
||||
|
||||
@override
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return the current HVAC mode."""
|
||||
mode = self.client.hvac_mode()
|
||||
return CIELO_TO_HA_HVAC.get(mode, mode)
|
||||
|
||||
@override
|
||||
@property
|
||||
def hvac_modes(self) -> list[HVACMode]:
|
||||
"""Return the list of available HVAC modes."""
|
||||
modes = self.client.hvac_modes() or []
|
||||
return [CIELO_TO_HA_HVAC.get(m, m) for m in modes]
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current indoor temperature."""
|
||||
return self.client.current_temperature()
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the target temperature."""
|
||||
return self.client.target_temperature()
|
||||
|
||||
@override
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Return the minimum possible target temperature."""
|
||||
return self.client.min_temp()
|
||||
|
||||
@override
|
||||
@property
|
||||
def max_temp(self) -> float:
|
||||
"""Return the maximum possible target temperature."""
|
||||
return self.client.max_temp()
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature_step(self) -> float | None:
|
||||
"""Return the precision of the thermostat."""
|
||||
return self.client.target_temperature_step(self.temperature_unit)
|
||||
|
||||
@override
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
"""Return the current fan mode."""
|
||||
return self.client.fan_mode()
|
||||
|
||||
@override
|
||||
@property
|
||||
def fan_modes(self) -> list[str] | None:
|
||||
"""Return the list of available fan modes.
|
||||
@@ -217,6 +231,7 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
"""
|
||||
return self.client.fan_modes()
|
||||
|
||||
@override
|
||||
@property
|
||||
def swing_modes(self) -> list[str] | None:
|
||||
"""Return the list of available swing modes.
|
||||
@@ -228,11 +243,13 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
"""
|
||||
return self.client.swing_modes()
|
||||
|
||||
@override
|
||||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode."""
|
||||
return self.client.preset_mode()
|
||||
|
||||
@override
|
||||
@property
|
||||
def preset_modes(self) -> list[str] | None:
|
||||
"""Return the list of available preset modes.
|
||||
@@ -244,16 +261,19 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
"""
|
||||
return self.client.preset_modes()
|
||||
|
||||
@override
|
||||
@property
|
||||
def swing_mode(self) -> str | None:
|
||||
"""Return the current swing mode."""
|
||||
return self.device_data.swing_mode if self.device_data else None
|
||||
|
||||
@override
|
||||
@property
|
||||
def precision(self) -> float:
|
||||
"""Return the precision of the thermostat."""
|
||||
return self.client.precision(self.temperature_unit)
|
||||
|
||||
@override
|
||||
@async_handle_api_call
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
@@ -270,27 +290,32 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
**{ATTR_TEMPERATURE: kwargs.get(ATTR_TEMPERATURE)},
|
||||
)
|
||||
|
||||
@override
|
||||
@async_handle_api_call
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set new fan mode."""
|
||||
return await self.client.async_set_fan_mode(fan_mode)
|
||||
|
||||
@override
|
||||
@async_handle_api_call
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
return await self.client.async_set_preset_mode(preset_mode)
|
||||
|
||||
@override
|
||||
@async_handle_api_call
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set new HVAC mode."""
|
||||
cielo_mode = HA_TO_CIELO_HVAC.get(hvac_mode)
|
||||
return await self.client.async_set_hvac_mode(cielo_mode)
|
||||
|
||||
@override
|
||||
@async_handle_api_call
|
||||
async def async_set_swing_mode(self, swing_mode: str) -> None:
|
||||
"""Set new swing mode."""
|
||||
return await self.client.async_set_swing_mode(swing_mode)
|
||||
|
||||
@override
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the climate device on."""
|
||||
modes = self.hvac_modes or []
|
||||
@@ -303,6 +328,7 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
|
||||
raise HomeAssistantError("No non-off HVAC modes available to turn on device")
|
||||
|
||||
@override
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn the climate device off."""
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Config Flow for Cielo integration."""
|
||||
|
||||
from typing import Any, Final
|
||||
from typing import Any, Final, override
|
||||
|
||||
from aiohttp import ClientError
|
||||
from cieloconnectapi import CieloClient
|
||||
@@ -61,6 +61,7 @@ class CieloConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return client.user_id, {CONF_TOKEN: token}
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from copy import copy
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import Any, Final
|
||||
from typing import Any, Final, override
|
||||
|
||||
from aiohttp import ClientError
|
||||
from cieloconnectapi import CieloClient
|
||||
@@ -59,6 +59,7 @@ class CieloDataUpdateCoordinator(DataUpdateCoordinator[CieloData]):
|
||||
),
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> CieloData:
|
||||
"""Fetch data from the API."""
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Base entity for Cielo integration."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from cieloconnectapi.device import CieloDeviceAPI
|
||||
from cieloconnectapi.model import CieloDevice
|
||||
|
||||
@@ -31,6 +33,7 @@ class CieloBaseEntity(CoordinatorEntity[CieloDataUpdateCoordinator]):
|
||||
coordinator.client, coordinator.data.parsed[device_id]
|
||||
)
|
||||
|
||||
@override
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
if (dev := self.device_data) is not None:
|
||||
@@ -42,6 +45,7 @@ class CieloBaseEntity(CoordinatorEntity[CieloDataUpdateCoordinator]):
|
||||
"""Return the device data from the coordinator."""
|
||||
return self.coordinator.data.parsed.get(self._device_id)
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if the device is available and online."""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Support for Cisco IOS Routers."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from pexpect import pxssh
|
||||
import voluptuous as vol
|
||||
@@ -50,10 +51,12 @@ class CiscoDeviceScanner(DeviceScanner):
|
||||
|
||||
self.success_init = self._update_info()
|
||||
|
||||
@override
|
||||
async def async_get_device_name(self, device: str) -> str | None:
|
||||
"""Get the firmware doesn't save the name of the wireless device."""
|
||||
return None
|
||||
|
||||
@override
|
||||
def scan_devices(self):
|
||||
"""Scan for new devices and return a list with found device IDs."""
|
||||
self._update_info()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Support for Cisco Mobility Express."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from ciscomobilityexpress.ciscome import CiscoMobilityExpress
|
||||
import voluptuous as vol
|
||||
@@ -62,12 +63,14 @@ class CiscoMEDeviceScanner(DeviceScanner):
|
||||
self.controller = controller
|
||||
self.last_results = {}
|
||||
|
||||
@override
|
||||
def scan_devices(self):
|
||||
"""Scan for new devices and return a list with found device IDs."""
|
||||
self._update_info()
|
||||
|
||||
return [device.macaddr for device in self.last_results]
|
||||
|
||||
@override
|
||||
def get_device_name(self, device):
|
||||
"""Return the name of the given device or None if we don't know."""
|
||||
return next(
|
||||
@@ -75,6 +78,7 @@ class CiscoMEDeviceScanner(DeviceScanner):
|
||||
None,
|
||||
)
|
||||
|
||||
@override
|
||||
def get_extra_attributes(self, device):
|
||||
"""Get extra attributes of a device.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Cisco Webex notify component."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import voluptuous as vol
|
||||
from webexpythonsdk import ApiError, WebexAPI, exceptions
|
||||
@@ -50,6 +50,7 @@ class CiscoWebexNotificationService(BaseNotificationService):
|
||||
self.room = room
|
||||
self.client = client
|
||||
|
||||
@override
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Send a message to a user."""
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import time
|
||||
from typing import override
|
||||
|
||||
from clementineremote import ClementineRemote
|
||||
import voluptuous as vol
|
||||
@@ -108,6 +109,7 @@ class ClementineDevice(MediaPlayerEntity):
|
||||
self._attr_state = MediaPlayerState.OFF
|
||||
raise
|
||||
|
||||
@override
|
||||
def select_source(self, source: str) -> None:
|
||||
"""Select input source."""
|
||||
client = self._client
|
||||
@@ -115,6 +117,7 @@ class ClementineDevice(MediaPlayerEntity):
|
||||
if len(sources) == 1:
|
||||
client.change_song(sources[0]["id"], 0)
|
||||
|
||||
@override
|
||||
async def async_get_media_image(self) -> tuple[bytes | None, str | None]:
|
||||
"""Fetch media image of current playing image."""
|
||||
if self._client.current_track:
|
||||
@@ -123,10 +126,12 @@ class ClementineDevice(MediaPlayerEntity):
|
||||
|
||||
return None, None
|
||||
|
||||
@override
|
||||
def mute_volume(self, mute: bool) -> None:
|
||||
"""Send mute command."""
|
||||
self._client.set_volume(0)
|
||||
|
||||
@override
|
||||
def set_volume_level(self, volume: float) -> None:
|
||||
"""Set volume level."""
|
||||
self._client.set_volume(int(100 * volume))
|
||||
@@ -138,20 +143,24 @@ class ClementineDevice(MediaPlayerEntity):
|
||||
else:
|
||||
self.media_play()
|
||||
|
||||
@override
|
||||
def media_play(self) -> None:
|
||||
"""Send play command."""
|
||||
self._attr_state = MediaPlayerState.PLAYING
|
||||
self._client.play()
|
||||
|
||||
@override
|
||||
def media_pause(self) -> None:
|
||||
"""Send media pause command to media player."""
|
||||
self._attr_state = MediaPlayerState.PAUSED
|
||||
self._client.pause()
|
||||
|
||||
@override
|
||||
def media_next_track(self) -> None:
|
||||
"""Send next track command."""
|
||||
self._client.next()
|
||||
|
||||
@override
|
||||
def media_previous_track(self) -> None:
|
||||
"""Send the previous track command."""
|
||||
self._client.previous()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -44,6 +44,7 @@ class ClickatellNotificationService(BaseNotificationService):
|
||||
self.api_key: str = config[CONF_API_KEY]
|
||||
self.recipient: str = config[CONF_RECIPIENT]
|
||||
|
||||
@override
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Send a message to a user."""
|
||||
data = {"apiKey": self.api_key, "to": self.recipient, "content": message}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -64,6 +64,7 @@ class ClicksendNotificationService(BaseNotificationService):
|
||||
self.recipients: list[str] = config[CONF_RECIPIENT]
|
||||
self.sender: str = config[CONF_SENDER]
|
||||
|
||||
@override
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Send a message to a user."""
|
||||
data: dict[str, Any] = {"messages": []}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -14,7 +14,6 @@ from homeassistant.components.notify import (
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LANGUAGE,
|
||||
CONF_NAME,
|
||||
CONF_RECIPIENT,
|
||||
CONF_USERNAME,
|
||||
@@ -30,6 +29,8 @@ BASE_API_URL = "https://rest.clicksend.com/v3"
|
||||
|
||||
HEADERS = {"Content-Type": CONTENT_TYPE_JSON}
|
||||
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_LANGUAGE = "language"
|
||||
CONF_VOICE = "voice"
|
||||
|
||||
MALE_VOICE = "male"
|
||||
@@ -80,6 +81,7 @@ class ClicksendNotificationService(BaseNotificationService):
|
||||
self.language = config[CONF_LANGUAGE]
|
||||
self.voice = config[CONF_VOICE]
|
||||
|
||||
@override
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Send a voice call to a user."""
|
||||
data = {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from datetime import timedelta
|
||||
import functools as ft
|
||||
import logging
|
||||
from typing import Any, Literal, final
|
||||
from typing import Any, Literal, final, override
|
||||
|
||||
from propcache.api import cached_property
|
||||
import voluptuous as vol
|
||||
@@ -283,6 +283,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
_attr_target_temperature: float | None = None
|
||||
_attr_temperature_unit: str
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
@@ -304,6 +305,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return PRECISION_TENTHS
|
||||
return PRECISION_WHOLE
|
||||
|
||||
@override
|
||||
@property
|
||||
def capability_attributes(self) -> dict[str, Any] | None:
|
||||
"""Return the capability attributes."""
|
||||
@@ -342,6 +344,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
|
||||
return data
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, Any]:
|
||||
@@ -700,6 +703,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
else:
|
||||
await self.async_turn_off()
|
||||
|
||||
@override
|
||||
@cached_property
|
||||
def supported_features(self) -> ClimateEntityFeature:
|
||||
"""Return the list of supported features."""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Provides conditions for climates."""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -47,6 +47,7 @@ class ClimateHVACModeCondition(EntityConditionBase):
|
||||
assert config.options is not None
|
||||
self._hvac_modes: set[str] = set(config.options[CONF_HVAC_MODE])
|
||||
|
||||
@override
|
||||
def is_valid_state(self, entity_state: State) -> bool:
|
||||
"""Check if the state matches any of the expected HVAC modes."""
|
||||
return entity_state.state in self._hvac_modes
|
||||
@@ -59,6 +60,7 @@ class ClimateTargetTemperatureCondition(EntityNumericalConditionWithUnitBase):
|
||||
_domain_specs = {DOMAIN: DomainSpec(value_source=ATTR_TEMPERATURE)}
|
||||
_unit_converter = TemperatureConverter
|
||||
|
||||
@override
|
||||
def _should_include(self, state: State) -> bool:
|
||||
"""Skip climate entities that do not expose a target temperature."""
|
||||
return (
|
||||
@@ -66,6 +68,7 @@ class ClimateTargetTemperatureCondition(EntityNumericalConditionWithUnitBase):
|
||||
and state.attributes.get(ATTR_TEMPERATURE) is not None
|
||||
)
|
||||
|
||||
@override
|
||||
def _get_entity_unit(self, entity_state: State) -> str | None:
|
||||
"""Get the temperature unit of a climate entity from its state."""
|
||||
# Climate entities convert temperatures to the system unit via show_temp
|
||||
@@ -78,6 +81,7 @@ class ClimateTargetHumidityCondition(EntityNumericalConditionBase):
|
||||
_domain_specs = {DOMAIN: DomainSpec(value_source=ATTR_HUMIDITY)}
|
||||
_valid_unit = "%"
|
||||
|
||||
@override
|
||||
def _should_include(self, state: State) -> bool:
|
||||
"""Skip climate entities that do not expose a target humidity."""
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Intents for the climate integration."""
|
||||
|
||||
from typing import override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
@@ -35,6 +37,7 @@ class SetTemperatureIntent(intent.IntentHandler):
|
||||
}
|
||||
platforms = {DOMAIN}
|
||||
|
||||
@override
|
||||
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||
"""Handle the intent."""
|
||||
hass = intent_obj.hass
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Provides triggers for climates."""
|
||||
|
||||
from typing import override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_TEMPERATURE, CONF_OPTIONS, UnitOfTemperature
|
||||
@@ -56,6 +58,7 @@ class _ClimateTargetTemperatureTriggerMixin(EntityNumericalStateTriggerWithUnitB
|
||||
_domain_specs = {DOMAIN: DomainSpec(value_source=ATTR_TEMPERATURE)}
|
||||
_unit_converter = TemperatureConverter
|
||||
|
||||
@override
|
||||
def _should_include(self, state: State) -> bool:
|
||||
"""Skip climate entities that do not expose a target temperature."""
|
||||
return (
|
||||
@@ -63,6 +66,7 @@ class _ClimateTargetTemperatureTriggerMixin(EntityNumericalStateTriggerWithUnitB
|
||||
and state.attributes.get(ATTR_TEMPERATURE) is not None
|
||||
)
|
||||
|
||||
@override
|
||||
def _get_entity_unit(self, state: State) -> str | None:
|
||||
"""Get the temperature unit of a climate entity from its state."""
|
||||
# Climate entities convert temperatures to the system unit via show_temp
|
||||
@@ -89,6 +93,7 @@ class _ClimateTargetHumidityTriggerMixin(EntityNumericalStateTriggerBase):
|
||||
_domain_specs = {DOMAIN: DomainSpec(value_source=ATTR_HUMIDITY)}
|
||||
_valid_unit = "%"
|
||||
|
||||
@override
|
||||
def _should_include(self, state: State) -> bool:
|
||||
"""Skip climate entities that do not expose a target humidity."""
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import aiohttp
|
||||
from awesomeversion import AwesomeVersion
|
||||
@@ -95,16 +95,19 @@ class CloudOAuth2Implementation(config_entry_oauth2_flow.AbstractOAuth2Implement
|
||||
self.hass = hass
|
||||
self.service = service
|
||||
|
||||
@override
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Name of the implementation."""
|
||||
return "Home Assistant Cloud"
|
||||
|
||||
@override
|
||||
@property
|
||||
def domain(self) -> str:
|
||||
"""Domain that is providing the implementation."""
|
||||
return DOMAIN
|
||||
|
||||
@override
|
||||
async def async_generate_authorize_url(self, flow_id: str) -> str:
|
||||
"""Generate a url for the user to authorize."""
|
||||
helper = account_link.AuthorizeAccountHelper(
|
||||
@@ -138,12 +141,14 @@ class CloudOAuth2Implementation(config_entry_oauth2_flow.AbstractOAuth2Implement
|
||||
|
||||
return authorize_url
|
||||
|
||||
@override
|
||||
async def async_resolve_external_data(self, external_data: Any) -> dict:
|
||||
"""Resolve external data to tokens."""
|
||||
# We already passed in tokens
|
||||
dict_data: dict = external_data
|
||||
return dict_data
|
||||
|
||||
@override
|
||||
async def _async_refresh_token(self, token: dict) -> dict:
|
||||
"""Refresh a token."""
|
||||
new_token = await account_link.async_fetch_access_token(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import io
|
||||
from json import JSONDecodeError
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from hass_nabucasa.llm import (
|
||||
LLMAuthenticationError,
|
||||
@@ -108,11 +109,13 @@ class CloudAITaskEntity(BaseCloudLLMEntity, ai_task.AITaskEntity):
|
||||
_attr_translation_key = "cloud_ai"
|
||||
_attr_unique_id = AI_TASK_ENTITY_UNIQUE_ID
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if the entity is available."""
|
||||
return self._cloud.is_logged_in and self._cloud.valid_subscription
|
||||
|
||||
@override
|
||||
async def _async_generate_data(
|
||||
self,
|
||||
task: ai_task.GenDataTask,
|
||||
@@ -150,6 +153,7 @@ class CloudAITaskEntity(BaseCloudLLMEntity, ai_task.AITaskEntity):
|
||||
data=data,
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_generate_image(
|
||||
self,
|
||||
task: ai_task.GenImageTask,
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable
|
||||
from contextlib import suppress
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
import aiohttp
|
||||
from hass_nabucasa import AlexaApiError, Cloud
|
||||
@@ -162,11 +162,13 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
and self._prefs.alexa_enabled
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def supports_auth(self) -> bool:
|
||||
"""Return if config supports auth."""
|
||||
return True
|
||||
|
||||
@override
|
||||
@property
|
||||
def should_report_state(self) -> bool:
|
||||
"""Return if states should be proactively reported."""
|
||||
@@ -176,6 +178,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
and self.authorized
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def endpoint(self) -> str | URL | None:
|
||||
"""Endpoint for report state."""
|
||||
@@ -184,17 +187,20 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
|
||||
return self._endpoint
|
||||
|
||||
@override
|
||||
@property
|
||||
def locale(self) -> str:
|
||||
"""Return config locale."""
|
||||
# Not clear how to determine locale atm.
|
||||
return "en-US"
|
||||
|
||||
@override
|
||||
@property
|
||||
def entity_config(self) -> dict[str, Any]:
|
||||
"""Return entity config."""
|
||||
return self._config.get(CONF_ENTITY_CONFIG) or {}
|
||||
|
||||
@override
|
||||
@callback
|
||||
def user_identifier(self) -> str:
|
||||
"""Return an identifier for the user that represents this config."""
|
||||
@@ -217,6 +223,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
self._should_expose_legacy(entity_id),
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_initialize(self) -> None:
|
||||
"""Initialize the Alexa config."""
|
||||
await super().async_initialize()
|
||||
@@ -299,6 +306,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
and entity_supported(self.hass, entity_id)
|
||||
)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def should_expose(self, entity_id: str) -> bool:
|
||||
"""If an entity should be exposed."""
|
||||
@@ -308,11 +316,13 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
|
||||
return async_should_expose(self.hass, CLOUD_ALEXA, entity_id)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def async_invalidate_access_token(self) -> None:
|
||||
"""Invalidate access token."""
|
||||
self._token_valid = None
|
||||
|
||||
@override
|
||||
async def async_get_access_token(self) -> str | None:
|
||||
"""Get an access token."""
|
||||
details: AlexaAccessTokenDetails | None
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import AsyncIterator, Callable, Coroutine, Mapping
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
import random
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from hass_nabucasa import Cloud, CloudApiError, CloudApiNonRetryableError, CloudError
|
||||
@@ -79,6 +79,7 @@ class CloudBackupAgent(BackupAgent):
|
||||
self._cloud = cloud
|
||||
self._hass = hass
|
||||
|
||||
@override
|
||||
async def async_download_backup(
|
||||
self,
|
||||
backup_id: str,
|
||||
@@ -100,6 +101,7 @@ class CloudBackupAgent(BackupAgent):
|
||||
|
||||
return ChunkAsyncStreamIterator(content)
|
||||
|
||||
@override
|
||||
async def async_upload_backup(
|
||||
self,
|
||||
*,
|
||||
@@ -170,6 +172,7 @@ class CloudBackupAgent(BackupAgent):
|
||||
)
|
||||
await asyncio.sleep(retry_timer)
|
||||
|
||||
@override
|
||||
async def async_delete_backup(
|
||||
self,
|
||||
backup_id: str,
|
||||
@@ -188,6 +191,7 @@ class CloudBackupAgent(BackupAgent):
|
||||
except (ClientError, CloudError) as err:
|
||||
raise BackupAgentError("Failed to delete backup") from err
|
||||
|
||||
@override
|
||||
async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]:
|
||||
"""List backups."""
|
||||
backups = await self._async_list_backups()
|
||||
@@ -203,6 +207,7 @@ class CloudBackupAgent(BackupAgent):
|
||||
_LOGGER.debug("Cloud backups: %s", backups)
|
||||
return backups
|
||||
|
||||
@override
|
||||
async def async_get_backup(
|
||||
self,
|
||||
backup_id: str,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for Home Assistant Cloud binary sensors."""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
|
||||
@@ -44,16 +44,19 @@ class CloudRemoteBinary(BinarySensorEntity):
|
||||
"""Initialize the binary sensor."""
|
||||
self.cloud = cloud
|
||||
|
||||
@override
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if the binary sensor is on."""
|
||||
return self.cloud.remote.is_connected
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return self.cloud.remote.certificate is not None
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register update dispatcher."""
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from datetime import datetime
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
from typing import Any, Literal, override
|
||||
|
||||
import aiohttp
|
||||
from hass_nabucasa.client import CloudClient as Interface, RemoteActivationNotAllowed
|
||||
@@ -71,6 +71,7 @@ class CloudClient(Interface):
|
||||
self._cloud_ice_servers_listener: Callable[[], None] | None = None
|
||||
self._ice_servers: list[RTCIceServer] = []
|
||||
|
||||
@override
|
||||
@property
|
||||
def base_path(self) -> Path:
|
||||
"""Return path to base dir."""
|
||||
@@ -81,31 +82,37 @@ class CloudClient(Interface):
|
||||
"""Return Cloud preferences."""
|
||||
return self._prefs
|
||||
|
||||
@override
|
||||
@property
|
||||
def loop(self) -> asyncio.AbstractEventLoop:
|
||||
"""Return client loop."""
|
||||
return self._hass.loop
|
||||
|
||||
@override
|
||||
@property
|
||||
def websession(self) -> aiohttp.ClientSession:
|
||||
"""Return client session for aiohttp."""
|
||||
return self._websession
|
||||
|
||||
@override
|
||||
@property
|
||||
def aiohttp_runner(self) -> aiohttp.web.AppRunner | None:
|
||||
"""Return client webinterface aiohttp application."""
|
||||
return self._hass.http.runner
|
||||
|
||||
@override
|
||||
@property
|
||||
def cloudhooks(self) -> dict[str, dict[str, str | bool]]:
|
||||
"""Return list of cloudhooks."""
|
||||
return self._prefs.cloudhooks
|
||||
|
||||
@override
|
||||
@property
|
||||
def remote_autostart(self) -> bool:
|
||||
"""Return true if we want start a remote connection."""
|
||||
return self._prefs.remote_enabled
|
||||
|
||||
@override
|
||||
@property
|
||||
def client_name(self) -> str:
|
||||
"""Return the client name that will be used for API calls."""
|
||||
@@ -165,6 +172,7 @@ class CloudClient(Interface):
|
||||
|
||||
return self._google_config
|
||||
|
||||
@override
|
||||
async def cloud_connected(self) -> None:
|
||||
"""When cloud is connected."""
|
||||
_LOGGER.debug("cloud_connected")
|
||||
@@ -258,18 +266,22 @@ class CloudClient(Interface):
|
||||
if tasks:
|
||||
await asyncio.gather(*(task(None) for task in tasks))
|
||||
|
||||
@override
|
||||
async def cloud_disconnected(self) -> None:
|
||||
"""When cloud disconnected."""
|
||||
_LOGGER.debug("cloud_disconnected")
|
||||
if self._google_config:
|
||||
self._google_config.async_disable_local_sdk()
|
||||
|
||||
@override
|
||||
async def cloud_started(self) -> None:
|
||||
"""When cloud is started."""
|
||||
|
||||
@override
|
||||
async def cloud_stopped(self) -> None:
|
||||
"""When the cloud is stopped."""
|
||||
|
||||
@override
|
||||
async def logout_cleanups(self) -> None:
|
||||
"""Cleanup some stuff after logout."""
|
||||
self._ice_servers = []
|
||||
@@ -287,23 +299,27 @@ class CloudClient(Interface):
|
||||
self._cloud_ice_servers_listener()
|
||||
self._cloud_ice_servers_listener = None
|
||||
|
||||
@override
|
||||
@callback
|
||||
def user_message(self, identifier: str, title: str, message: str) -> None:
|
||||
"""Create a message for user to UI."""
|
||||
persistent_notification.async_create(self._hass, message, title, identifier)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def dispatcher_message(self, identifier: str, data: Any = None) -> None:
|
||||
"""Match cloud notification to dispatcher."""
|
||||
if identifier.startswith("remote_"):
|
||||
async_dispatcher_send(self._hass, DISPATCHER_REMOTE_UPDATE, data)
|
||||
|
||||
@override
|
||||
async def async_cloud_connect_update(self, connect: bool) -> None:
|
||||
"""Process cloud remote message to client."""
|
||||
if not self._prefs.remote_allow_remote_enable:
|
||||
raise RemoteActivationNotAllowed
|
||||
await self._prefs.async_update(remote_enabled=connect)
|
||||
|
||||
@override
|
||||
async def async_cloud_connection_info(
|
||||
self, payload: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
@@ -321,6 +337,7 @@ class CloudClient(Interface):
|
||||
"name": self._hass.config.location_name,
|
||||
}
|
||||
|
||||
@override
|
||||
async def async_alexa_message(self, payload: dict[Any, Any]) -> dict[Any, Any]:
|
||||
"""Process cloud alexa message to client."""
|
||||
cloud_user = await self._prefs.get_cloud_user()
|
||||
@@ -333,6 +350,7 @@ class CloudClient(Interface):
|
||||
enabled=self._prefs.alexa_enabled,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_google_message(self, payload: dict[Any, Any]) -> dict[Any, Any]:
|
||||
"""Process cloud google message to client."""
|
||||
gconf = await self.get_google_config()
|
||||
@@ -356,6 +374,7 @@ class CloudClient(Interface):
|
||||
google_assistant.SOURCE_CLOUD,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_webhook_message(self, payload: dict[Any, Any]) -> dict[Any, Any]:
|
||||
"""Process cloud webhook message to client."""
|
||||
cloudhook_id = payload["cloudhook_id"]
|
||||
@@ -395,17 +414,20 @@ class CloudClient(Interface):
|
||||
"headers": {"Content-Type": response.content_type},
|
||||
}
|
||||
|
||||
@override
|
||||
async def async_system_message(self, payload: dict[Any, Any] | None) -> None:
|
||||
"""Handle system messages."""
|
||||
if payload and (region := payload.get("region")):
|
||||
self._relayer_region = region
|
||||
|
||||
@override
|
||||
async def async_cloudhooks_update(
|
||||
self, data: dict[str, dict[str, str | bool]]
|
||||
) -> None:
|
||||
"""Update local list of cloudhooks."""
|
||||
await self._prefs.async_update(cloudhooks=data)
|
||||
|
||||
@override
|
||||
async def async_create_repair_issue(
|
||||
self,
|
||||
identifier: str,
|
||||
@@ -432,6 +454,7 @@ class CloudClient(Interface):
|
||||
is_fixable=False,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_delete_repair_issue(self, identifier: str) -> None:
|
||||
"""Delete a repair issue."""
|
||||
async_delete_issue(hass=self._hass, domain=DOMAIN, issue_id=identifier)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Conversation support for Home Assistant Cloud."""
|
||||
|
||||
from typing import Literal
|
||||
from typing import Literal, override
|
||||
|
||||
from homeassistant.components import conversation
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -35,16 +35,19 @@ class CloudConversationEntity(
|
||||
_attr_unique_id = CONVERSATION_ENTITY_UNIQUE_ID
|
||||
_attr_supported_features = conversation.ConversationEntityFeature.CONTROL
|
||||
|
||||
@override
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if the entity is available."""
|
||||
return self._cloud.is_logged_in and self._cloud.valid_subscription
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_languages(self) -> list[str] | Literal["*"]:
|
||||
"""Return a list of supported languages."""
|
||||
return MATCH_ALL
|
||||
|
||||
@override
|
||||
async def _async_handle_message(
|
||||
self,
|
||||
user_input: conversation.ConversationInput,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
from hass_nabucasa.google_report_state import ErrorResponse
|
||||
@@ -145,6 +145,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
self._cloud = cloud
|
||||
self._sync_entities_lock = asyncio.Lock()
|
||||
|
||||
@override
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
"""Return if Google is enabled."""
|
||||
@@ -154,25 +155,30 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
and self._prefs.google_enabled
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def entity_config(self) -> dict[str, Any]:
|
||||
"""Return entity config."""
|
||||
return self._config.get(CONF_ENTITY_CONFIG) or {}
|
||||
|
||||
@override
|
||||
@property
|
||||
def secure_devices_pin(self) -> str | None:
|
||||
"""Return entity config."""
|
||||
return self._prefs.google_secure_devices_pin
|
||||
|
||||
@override
|
||||
@property
|
||||
def should_report_state(self) -> bool:
|
||||
"""Return if states should be proactively reported."""
|
||||
return self.enabled and self._prefs.google_report_state
|
||||
|
||||
@override
|
||||
def get_local_webhook_id(self, agent_user_id: Any) -> str:
|
||||
"""Return the webhook ID for actions for an agent user id via the local SDK."""
|
||||
return self._prefs.google_local_webhook_id
|
||||
|
||||
@override
|
||||
def get_local_user_id(self, webhook_id: Any) -> str:
|
||||
"""Map webhook ID to a Home Assistant user ID.
|
||||
|
||||
@@ -211,6 +217,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
_2fa_disabled,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_initialize(self) -> None:
|
||||
"""Perform async initialization of config."""
|
||||
_LOGGER.debug("async_initialize")
|
||||
@@ -275,6 +282,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
)
|
||||
)
|
||||
|
||||
@override
|
||||
def should_expose(self, state: State) -> bool:
|
||||
"""If a state object should be exposed."""
|
||||
return self._should_expose_entity_id(state.entity_id)
|
||||
@@ -326,10 +334,12 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
"""Return if we have a Agent User Id registered."""
|
||||
return len(self.async_get_agent_users()) > 0
|
||||
|
||||
@override
|
||||
def get_agent_user_id_from_context(self, context: Any) -> str:
|
||||
"""Get agent user ID making request."""
|
||||
return self.agent_user_id
|
||||
|
||||
@override
|
||||
def get_agent_user_id_from_webhook(self, webhook_id: str) -> str | None:
|
||||
"""Map webhook ID to a Google agent user ID.
|
||||
|
||||
@@ -346,6 +356,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
entity_config = entity_configs.get(entity_id, {})
|
||||
return entity_config.get(PREF_DISABLE_2FA)
|
||||
|
||||
@override
|
||||
def should_2fa(self, state: State) -> bool:
|
||||
"""If an entity should be checked for 2FA."""
|
||||
try:
|
||||
@@ -357,6 +368,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
assistant_options = settings.get(CLOUD_GOOGLE, {})
|
||||
return not assistant_options.get(PREF_DISABLE_2FA, DEFAULT_DISABLE_2FA)
|
||||
|
||||
@override
|
||||
async def async_report_state(
|
||||
self, message: Any, agent_user_id: str, event_id: str | None = None
|
||||
) -> None:
|
||||
@@ -366,6 +378,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
except ErrorResponse as err:
|
||||
_LOGGER.warning("Error reporting state - %s: %s", err.code, err.message)
|
||||
|
||||
@override
|
||||
async def _async_request_sync_devices(self, agent_user_id: str) -> HTTPStatus | int:
|
||||
"""Trigger a sync with Google."""
|
||||
if self._sync_entities_lock.locked():
|
||||
@@ -375,6 +388,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
resp = await self._cloud.google_report_state.request_sync()
|
||||
return resp.status
|
||||
|
||||
@override
|
||||
async def async_connect_agent_user(self, agent_user_id: str) -> None:
|
||||
"""Add a synced and known agent_user_id.
|
||||
|
||||
@@ -382,6 +396,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
"""
|
||||
await self._prefs.async_update(google_connected=True)
|
||||
|
||||
@override
|
||||
async def async_disconnect_agent_user(self, agent_user_id: str) -> None:
|
||||
"""Turn off report state and disable further state reporting.
|
||||
|
||||
@@ -392,6 +407,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
"""
|
||||
await self._prefs.async_update(google_connected=False)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def async_get_agent_users(self) -> tuple:
|
||||
"""Return known agent users."""
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections import deque
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -16,6 +17,7 @@ class FixedSizeQueueLogHandler(logging.Handler):
|
||||
super().__init__()
|
||||
self._records: deque[logging.LogRecord] = deque(maxlen=self.MAX_RECORDS)
|
||||
|
||||
@override
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
"""Store log message."""
|
||||
self._records.append(record)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from functools import wraps
|
||||
from typing import TYPE_CHECKING, Any, Concatenate
|
||||
from typing import TYPE_CHECKING, Any, Concatenate, override
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.web_exceptions import HTTPUnauthorized
|
||||
@@ -64,6 +64,7 @@ class CloudForgotPasswordView(
|
||||
url = "/api/onboarding/cloud/forgot_password"
|
||||
name = "api:onboarding:cloud:forgot_password"
|
||||
|
||||
@override
|
||||
@ensure_not_done
|
||||
async def post(self, request: web.Request) -> web.Response:
|
||||
"""Handle forgot password request."""
|
||||
@@ -76,6 +77,7 @@ class CloudLoginView(NoAuthBaseOnboardingView, cloud_http.CloudLoginView):
|
||||
url = "/api/onboarding/cloud/login"
|
||||
name = "api:onboarding:cloud:login"
|
||||
|
||||
@override
|
||||
@ensure_not_done
|
||||
async def post(self, request: web.Request) -> web.Response:
|
||||
"""Handle login request."""
|
||||
@@ -88,6 +90,7 @@ class CloudLogoutView(NoAuthBaseOnboardingView, cloud_http.CloudLogoutView):
|
||||
url = "/api/onboarding/cloud/logout"
|
||||
name = "api:onboarding:cloud:logout"
|
||||
|
||||
@override
|
||||
@ensure_not_done
|
||||
async def post(self, request: web.Request) -> web.Response:
|
||||
"""Handle logout request."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Preference management for cloud."""
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
import uuid
|
||||
|
||||
from hass_nabucasa.voice import MAP_VOICE, Gender
|
||||
@@ -58,6 +58,7 @@ GOOGLE_SETTINGS_VERSION = 3
|
||||
class CloudPreferencesStore(Store):
|
||||
"""Store cloud preferences."""
|
||||
|
||||
@override
|
||||
async def _async_migrate_func(
|
||||
self, old_major_version: int, old_minor_version: int, old_data: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import AsyncIterable
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
from hass_nabucasa.voice import STT_LANGUAGES, VoiceError
|
||||
@@ -52,36 +53,43 @@ class CloudProviderEntity(SpeechToTextEntity):
|
||||
"""Initialize cloud Speech to text entity."""
|
||||
self.cloud = cloud
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
return STT_LANGUAGES
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_formats(self) -> list[AudioFormats]:
|
||||
"""Return a list of supported formats."""
|
||||
return [AudioFormats.WAV, AudioFormats.OGG]
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_codecs(self) -> list[AudioCodecs]:
|
||||
"""Return a list of supported codecs."""
|
||||
return [AudioCodecs.PCM, AudioCodecs.OPUS]
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_bit_rates(self) -> list[AudioBitRates]:
|
||||
"""Return a list of supported bitrates."""
|
||||
return [AudioBitRates.BITRATE_16]
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_sample_rates(self) -> list[AudioSampleRates]:
|
||||
"""Return a list of supported samplerates."""
|
||||
return [AudioSampleRates.SAMPLERATE_16000]
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_channels(self) -> list[AudioChannels]:
|
||||
"""Return a list of supported channels."""
|
||||
return [AudioChannels.CHANNEL_MONO]
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Run when entity is about to be added to hass."""
|
||||
|
||||
@@ -97,6 +105,7 @@ class CloudProviderEntity(SpeechToTextEntity):
|
||||
|
||||
async_when_setup(self.hass, "assist_pipeline", pipeline_setup)
|
||||
|
||||
@override
|
||||
async def async_process_audio_stream(
|
||||
self, metadata: SpeechMetadata, stream: AsyncIterable[bytes]
|
||||
) -> SpeechResult:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for the cloud for text-to-speech service."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
from hass_nabucasa.voice import MAP_VOICE, AudioOutput, Gender, VoiceError
|
||||
@@ -323,11 +323,13 @@ class CloudTTSEntity(TextToSpeechEntity):
|
||||
"""Sync preferences."""
|
||||
self._language, self._voice = prefs.tts_default_voice
|
||||
|
||||
@override
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
"""Return the default language."""
|
||||
return self._language
|
||||
|
||||
@override
|
||||
@property
|
||||
def default_options(self) -> dict[str, str]:
|
||||
"""Return a dict include default options."""
|
||||
@@ -335,17 +337,20 @@ class CloudTTSEntity(TextToSpeechEntity):
|
||||
ATTR_AUDIO_OUTPUT: AudioOutput.MP3.value,
|
||||
}
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
"""Return list of supported options like voice, emotion."""
|
||||
# The gender option is deprecated and will be removed in 2024.10.0.
|
||||
return [ATTR_GENDER, ATTR_VOICE, ATTR_AUDIO_OUTPUT]
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle entity which will be added."""
|
||||
await super().async_added_to_hass()
|
||||
@@ -366,6 +371,7 @@ class CloudTTSEntity(TextToSpeechEntity):
|
||||
self.cloud.client.prefs.async_listen_updates(self._sync_prefs)
|
||||
)
|
||||
|
||||
@override
|
||||
@callback
|
||||
def async_get_supported_voices(self, language: str) -> list[Voice] | None:
|
||||
"""Return a list of supported voices for a language."""
|
||||
@@ -404,6 +410,7 @@ class CloudTTSEntity(TextToSpeechEntity):
|
||||
|
||||
return result
|
||||
|
||||
@override
|
||||
async def async_get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
@@ -433,6 +440,7 @@ class CloudTTSEntity(TextToSpeechEntity):
|
||||
|
||||
return (options[ATTR_AUDIO_OUTPUT], data)
|
||||
|
||||
@override
|
||||
async def async_stream_tts_audio(
|
||||
self, request: TTSAudioRequest
|
||||
) -> TTSAudioResponse:
|
||||
@@ -473,22 +481,26 @@ class CloudProvider(Provider):
|
||||
"""Sync preferences."""
|
||||
self._language, self._voice = prefs.tts_default_voice
|
||||
|
||||
@override
|
||||
@property
|
||||
def default_language(self) -> str | None:
|
||||
"""Return the default language."""
|
||||
return self._language
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
"""Return list of supported options like voice, emotion."""
|
||||
# The gender option is deprecated and will be removed in 2024.10.0.
|
||||
return [ATTR_GENDER, ATTR_VOICE, ATTR_AUDIO_OUTPUT]
|
||||
|
||||
@override
|
||||
@callback
|
||||
def async_get_supported_voices(self, language: str) -> list[Voice] | None:
|
||||
"""Return a list of supported voices for a language."""
|
||||
@@ -527,6 +539,7 @@ class CloudProvider(Provider):
|
||||
|
||||
return result
|
||||
|
||||
@override
|
||||
@property
|
||||
def default_options(self) -> dict[str, str]:
|
||||
"""Return a dict include default options."""
|
||||
@@ -534,6 +547,7 @@ class CloudProvider(Provider):
|
||||
ATTR_AUDIO_OUTPUT: AudioOutput.MP3,
|
||||
}
|
||||
|
||||
@override
|
||||
async def async_get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
import pycfdns
|
||||
import voluptuous as vol
|
||||
@@ -111,6 +111,7 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -4,6 +4,7 @@ import asyncio
|
||||
from datetime import timedelta
|
||||
from logging import getLogger
|
||||
import socket
|
||||
from typing import override
|
||||
|
||||
import pycfdns
|
||||
|
||||
@@ -42,6 +43,7 @@ class CloudflareCoordinator(DataUpdateCoordinator[None]):
|
||||
update_interval=timedelta(minutes=DEFAULT_UPDATE_INTERVAL),
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_setup(self) -> None:
|
||||
"""Set up the coordinator."""
|
||||
self.client = pycfdns.Client(
|
||||
@@ -60,6 +62,7 @@ class CloudflareCoordinator(DataUpdateCoordinator[None]):
|
||||
except pycfdns.ComunicationException as e:
|
||||
raise UpdateFailed("Error communicating with API") from e
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> None:
|
||||
"""Update records."""
|
||||
_LOGGER.debug("Starting update for zone %s", self.zone["name"])
|
||||
|
||||
@@ -5,7 +5,7 @@ import functools
|
||||
import json
|
||||
import logging
|
||||
from time import time
|
||||
from typing import Any, cast
|
||||
from typing import Any, cast, override
|
||||
|
||||
from botocore.exceptions import BotoCoreError
|
||||
|
||||
@@ -17,11 +17,10 @@ from homeassistant.components.backup import (
|
||||
OnProgressCallback,
|
||||
suggested_filename,
|
||||
)
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from . import R2ConfigEntry
|
||||
from .const import CONF_BUCKET, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
|
||||
from .const import CONF_BUCKET, CONF_PREFIX, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CACHE_TTL = 300
|
||||
@@ -107,6 +106,7 @@ class R2BackupAgent(BackupAgent):
|
||||
return key
|
||||
return f"{self._prefix}/{key}"
|
||||
|
||||
@override
|
||||
@handle_boto_errors
|
||||
async def async_download_backup(
|
||||
self,
|
||||
@@ -126,6 +126,7 @@ class R2BackupAgent(BackupAgent):
|
||||
)
|
||||
return response["Body"].iter_chunks()
|
||||
|
||||
@override
|
||||
async def async_upload_backup(
|
||||
self,
|
||||
*,
|
||||
@@ -288,6 +289,7 @@ class R2BackupAgent(BackupAgent):
|
||||
_LOGGER.exception("Failed to abort multipart upload")
|
||||
raise
|
||||
|
||||
@override
|
||||
@handle_boto_errors
|
||||
async def async_delete_backup(
|
||||
self,
|
||||
@@ -312,12 +314,14 @@ class R2BackupAgent(BackupAgent):
|
||||
# Reset cache after successful deletion
|
||||
self._cache_expiration = time()
|
||||
|
||||
@override
|
||||
@handle_boto_errors
|
||||
async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]:
|
||||
"""List backups."""
|
||||
backups = await self._list_backups()
|
||||
return list(backups.values())
|
||||
|
||||
@override
|
||||
@handle_boto_errors
|
||||
async def async_get_backup(
|
||||
self,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Config flow for the Cloudflare R2 integration."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from aiobotocore.session import AioSession
|
||||
@@ -13,7 +13,6 @@ from botocore.exceptions import (
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_PREFIX
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.selector import (
|
||||
TextSelector,
|
||||
@@ -26,6 +25,7 @@ from .const import (
|
||||
CONF_ACCESS_KEY_ID,
|
||||
CONF_BUCKET,
|
||||
CONF_ENDPOINT_URL,
|
||||
CONF_PREFIX,
|
||||
CONF_SECRET_ACCESS_KEY,
|
||||
DEFAULT_ENDPOINT_URL,
|
||||
DESCRIPTION_R2_AUTH_DOCS_URL,
|
||||
@@ -50,6 +50,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
class R2ConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Cloudflare R2."""
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -11,6 +11,8 @@ CONF_ACCESS_KEY_ID = "access_key_id"
|
||||
CONF_SECRET_ACCESS_KEY = "secret_access_key"
|
||||
CONF_ENDPOINT_URL = "endpoint_url"
|
||||
CONF_BUCKET = "bucket"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_PREFIX = "prefix"
|
||||
|
||||
# R2 is S3-compatible. Endpoint should be like:
|
||||
# https://<accountid>.r2.cloudflarestorage.com
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for interacting with and controlling the cmus music player."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from pycmus import exceptions, remote
|
||||
import voluptuous as vol
|
||||
@@ -140,14 +140,17 @@ class CmusDevice(MediaPlayerEntity):
|
||||
|
||||
_LOGGER.warning("Received no status from cmus")
|
||||
|
||||
@override
|
||||
def turn_off(self) -> None:
|
||||
"""Service to send the CMUS the command to stop playing."""
|
||||
self._remote.cmus.player_stop()
|
||||
|
||||
@override
|
||||
def turn_on(self) -> None:
|
||||
"""Service to send the CMUS the command to start playing."""
|
||||
self._remote.cmus.player_play()
|
||||
|
||||
@override
|
||||
def set_volume_level(self, volume: float) -> None:
|
||||
"""Set volume level, range 0..1."""
|
||||
self._remote.cmus.set_volume(int(volume * 100))
|
||||
@@ -176,6 +179,7 @@ class CmusDevice(MediaPlayerEntity):
|
||||
if current_volume <= 100:
|
||||
self._remote.cmus.set_volume(int(current_volume) - 5)
|
||||
|
||||
@override
|
||||
def play_media(
|
||||
self, media_type: MediaType | str, media_id: str, **kwargs: Any
|
||||
) -> None:
|
||||
@@ -190,26 +194,32 @@ class CmusDevice(MediaPlayerEntity):
|
||||
MediaType.PLAYLIST,
|
||||
)
|
||||
|
||||
@override
|
||||
def media_pause(self) -> None:
|
||||
"""Send the pause command."""
|
||||
self._remote.cmus.player_pause()
|
||||
|
||||
@override
|
||||
def media_next_track(self) -> None:
|
||||
"""Send next track command."""
|
||||
self._remote.cmus.player_next()
|
||||
|
||||
@override
|
||||
def media_previous_track(self) -> None:
|
||||
"""Send next track command."""
|
||||
self._remote.cmus.player_prev()
|
||||
|
||||
@override
|
||||
def media_seek(self, position: float) -> None:
|
||||
"""Send seek command."""
|
||||
self._remote.cmus.seek(position)
|
||||
|
||||
@override
|
||||
def media_play(self) -> None:
|
||||
"""Send the play command."""
|
||||
self._remote.cmus.player_play()
|
||||
|
||||
@override
|
||||
def media_stop(self) -> None:
|
||||
"""Send the stop command."""
|
||||
self._remote.cmus.stop()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from aioelectricitymaps import (
|
||||
ElectricityMaps,
|
||||
@@ -47,6 +47,7 @@ class ElectricityMapsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
VERSION = 1
|
||||
_data: dict | None
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from aioelectricitymaps import (
|
||||
ElectricityMaps,
|
||||
@@ -49,6 +50,7 @@ class CO2SignalCoordinator(DataUpdateCoordinator[HomeAssistantCarbonIntensityRes
|
||||
"""Return entry ID."""
|
||||
return self.config_entry.entry_id
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> HomeAssistantCarbonIntensityResponse:
|
||||
"""Fetch the latest data from the source."""
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
from aioelectricitymaps import HomeAssistantCarbonIntensityResponse
|
||||
|
||||
@@ -90,11 +91,13 @@ class CO2Sensor(CoordinatorEntity[CO2SignalCoordinator], SensorEntity):
|
||||
f"{coordinator.entry_id}_{description.unique_id or description.key}"
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return sensor state."""
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
|
||||
@override
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit of measurement."""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, override
|
||||
|
||||
from coinbase.rest import RESTClient
|
||||
from coinbase.rest.rest_base import HTTPError
|
||||
@@ -114,6 +114,7 @@ class CoinbaseConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
reauth_entry: CoinbaseConfigEntry
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
@@ -199,6 +200,7 @@ class CoinbaseConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
@override
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user