mirror of
https://github.com/home-assistant/core.git
synced 2026-05-25 10:15:22 +02:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf48806ee9 | |||
| 2c25c5ad26 | |||
| ff1177dde4 | |||
| 1cc91cd3b6 | |||
| ecac38a359 | |||
| 8301addc94 | |||
| 77bc932cf0 | |||
| 11903ac62e | |||
| 878761cb41 | |||
| d7bf7df59f | |||
| e5890172a0 | |||
| e9a58cdd20 | |||
| cab7c41a7f | |||
| 277a2d847a | |||
| 7835a4992a | |||
| 9dc37a2f46 | |||
| 7534c438c1 | |||
| 69efa8ee1a | |||
| 305b5d6e00 | |||
| d94226260b | |||
| ecc8e52f3e | |||
| 5771b0c86c | |||
| 3e289da366 | |||
| 944fb1ef67 | |||
| 1b6e9f5094 | |||
| b2257caeb7 | |||
| 0ec0ea30ac | |||
| 584b32c8b3 | |||
| 4033a8b83a | |||
| add8a5f799 | |||
| 7c137b5c73 | |||
| 4a6c5b5a22 | |||
| 1009ce4180 | |||
| 22fb68b7a1 | |||
| 81e06539e6 | |||
| 7c18b67b2e | |||
| a8bc244a7a | |||
| 5975f4b179 | |||
| 9ed16b63a3 | |||
| 8dadaa2f9e | |||
| 4f98c71586 |
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
|
||||
"integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -917,12 +917,49 @@ jobs:
|
||||
key: >-
|
||||
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
|
||||
needs.info.outputs.python_cache_key }}
|
||||
- name: Restore pytest test counts cache
|
||||
id: cache-pytest-counts
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: pytest_test_counts.json
|
||||
# Primary key is a sentinel; restore-keys pick the most recent
|
||||
# prefix match since the real (content-addressed) key isn't
|
||||
# known until split_tests.py runs below.
|
||||
key: >-
|
||||
pytest-counts-${{ runner.os }}-${{ runner.arch }}-${{
|
||||
steps.python.outputs.python-version }}-${{
|
||||
needs.info.outputs.python_cache_key }}-restore-sentinel
|
||||
restore-keys: |
|
||||
pytest-counts-${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{ needs.info.outputs.python_cache_key }}-
|
||||
- name: Run split_tests.py
|
||||
env:
|
||||
TEST_GROUP_COUNT: ${{ needs.info.outputs.test_group_count }}
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
python -m script.split_tests ${TEST_GROUP_COUNT} tests
|
||||
python -m script.split_tests \
|
||||
--cache pytest_test_counts.json \
|
||||
${TEST_GROUP_COUNT} tests
|
||||
- name: Hash pytest test counts cache
|
||||
id: cache-pytest-counts-hash
|
||||
run: |
|
||||
echo "hash=$(sha256sum pytest_test_counts.json | cut -d' ' -f1)" \
|
||||
>> "$GITHUB_OUTPUT"
|
||||
- name: Save pytest test counts cache
|
||||
# Content-addressed key: identical content reuses the same entry.
|
||||
# Skip the save when the restore already matched that hash.
|
||||
if: >-
|
||||
!endsWith(
|
||||
steps.cache-pytest-counts.outputs.cache-matched-key,
|
||||
steps.cache-pytest-counts-hash.outputs.hash
|
||||
)
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: pytest_test_counts.json
|
||||
key: >-
|
||||
pytest-counts-${{ runner.os }}-${{ runner.arch }}-${{
|
||||
steps.python.outputs.python-version }}-${{
|
||||
needs.info.outputs.python_cache_key }}-${{
|
||||
steps.cache-pytest-counts-hash.outputs.hash }}
|
||||
- name: Upload pytest_buckets
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
||||
Generated
+4
-2
@@ -236,8 +236,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/blebox/ @bbx-a @swistakm @bkobus-bbx
|
||||
/homeassistant/components/blink/ @fronzbot
|
||||
/tests/components/blink/ @fronzbot
|
||||
/homeassistant/components/blue_current/ @gleeuwen @jtodorova23
|
||||
/tests/components/blue_current/ @gleeuwen @jtodorova23
|
||||
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
|
||||
/tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
|
||||
/homeassistant/components/bluemaestro/ @bdraco
|
||||
/tests/components/bluemaestro/ @bdraco
|
||||
/homeassistant/components/blueprint/ @home-assistant/core
|
||||
@@ -945,6 +945,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/knx/ @Julius2342 @farmio @marvin-w
|
||||
/homeassistant/components/kodi/ @OnFreund
|
||||
/tests/components/kodi/ @OnFreund
|
||||
/homeassistant/components/konnected/ @heythisisnate
|
||||
/tests/components/konnected/ @heythisisnate
|
||||
/homeassistant/components/kostal_plenticore/ @stegm
|
||||
/tests/components/kostal_plenticore/ @stegm
|
||||
/homeassistant/components/kraken/ @eifinger
|
||||
|
||||
@@ -39,6 +39,7 @@ from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
CLOUD_NEVER_EXPOSED_ENTITIES,
|
||||
CONF_DESCRIPTION,
|
||||
CONF_NAME,
|
||||
UnitOfTemperature,
|
||||
@@ -372,6 +373,9 @@ def async_get_entities(
|
||||
"""Return all entities that are supported by Alexa."""
|
||||
entities: list[AlexaEntity] = []
|
||||
for state in hass.states.async_all():
|
||||
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
continue
|
||||
|
||||
if state.domain not in ENTITY_ADAPTERS:
|
||||
continue
|
||||
|
||||
|
||||
@@ -16,13 +16,6 @@ from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_UDN, SsdpServiceIn
|
||||
|
||||
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN
|
||||
|
||||
STEP_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle config flow."""
|
||||
@@ -38,22 +31,13 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
await self.async_set_unique_id(uuid)
|
||||
self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port})
|
||||
|
||||
async def _async_try_connect(self, host: str, port: int) -> dict[str, str]:
|
||||
"""Verify the device is reachable; return errors keyed by reason."""
|
||||
async def _async_try_connect(self, host: str, port: int) -> None:
|
||||
"""Verify the device is reachable."""
|
||||
client = Client(host, port)
|
||||
try:
|
||||
await client.start()
|
||||
except socket.gaierror:
|
||||
return {"base": "invalid_host"}
|
||||
except TimeoutError:
|
||||
return {"base": "timeout_connect"}
|
||||
except ConnectionRefusedError:
|
||||
return {"base": "connection_refused"}
|
||||
except ConnectionFailed, OSError:
|
||||
return {"base": "cannot_connect"}
|
||||
finally:
|
||||
await client.stop()
|
||||
return {}
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
@@ -69,10 +53,19 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
user_input[CONF_HOST], user_input[CONF_PORT], uuid
|
||||
)
|
||||
|
||||
errors = await self._async_try_connect(
|
||||
user_input[CONF_HOST], user_input[CONF_PORT]
|
||||
)
|
||||
if not errors:
|
||||
try:
|
||||
await self._async_try_connect(
|
||||
user_input[CONF_HOST], user_input[CONF_PORT]
|
||||
)
|
||||
except socket.gaierror:
|
||||
errors["base"] = "invalid_host"
|
||||
except TimeoutError:
|
||||
errors["base"] = "timeout_connect"
|
||||
except ConnectionRefusedError:
|
||||
errors["base"] = "connection_refused"
|
||||
except ConnectionFailed, OSError:
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=f"{DEFAULT_NAME} ({user_input[CONF_HOST]})",
|
||||
data={
|
||||
@@ -81,46 +74,16 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
},
|
||||
)
|
||||
|
||||
schema = STEP_DATA_SCHEMA
|
||||
fields = {
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
||||
}
|
||||
schema = vol.Schema(fields)
|
||||
if user_input is not None:
|
||||
schema = self.add_suggested_values_to_schema(schema, user_input)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle reconfiguration of an existing entry."""
|
||||
errors: dict[str, str] = {}
|
||||
reconfigure_entry = self._get_reconfigure_entry()
|
||||
if user_input is not None:
|
||||
uuid = await get_uniqueid_from_host(
|
||||
async_get_clientsession(self.hass), user_input[CONF_HOST]
|
||||
)
|
||||
if uuid:
|
||||
await self.async_set_unique_id(uuid)
|
||||
self._abort_if_unique_id_mismatch()
|
||||
|
||||
errors = await self._async_try_connect(
|
||||
user_input[CONF_HOST], user_input[CONF_PORT]
|
||||
)
|
||||
if not errors:
|
||||
return self.async_update_reload_and_abort(
|
||||
reconfigure_entry,
|
||||
data_updates={
|
||||
CONF_HOST: user_input[CONF_HOST],
|
||||
CONF_PORT: user_input[CONF_PORT],
|
||||
},
|
||||
)
|
||||
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
STEP_DATA_SCHEMA, user_input or reconfigure_entry.data
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reconfigure", data_schema=schema, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
@@ -150,7 +113,9 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
await self._async_set_unique_id_and_update(host, port, uuid)
|
||||
|
||||
if await self._async_try_connect(host, port):
|
||||
try:
|
||||
await self._async_try_connect(host, port)
|
||||
except ConnectionFailed, OSError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
self.host = host
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
@@ -18,13 +16,6 @@
|
||||
"confirm": {
|
||||
"description": "Do you want to add Arcam FMJ on `{host}` to Home Assistant?"
|
||||
},
|
||||
"reconfigure": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"description": "[%key:component::arcam_fmj::config::step::user::description%]"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
|
||||
@@ -9,11 +9,12 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import usb
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import ATTR_MODEL, ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
|
||||
from homeassistant.const import ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import (
|
||||
ATTR_FIRMWARE,
|
||||
ATTR_MODEL,
|
||||
DEFAULT_ADDRESS,
|
||||
DEFAULT_INTEGRATION_TITLE,
|
||||
DOMAIN,
|
||||
|
||||
@@ -19,4 +19,8 @@ DEVICES = "devices"
|
||||
MANUFACTURER = "ABB"
|
||||
|
||||
ATTR_DEVICE_NAME = "device_name"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
ATTR_DEVICE_ID = "device_id"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
ATTR_MODEL = "model"
|
||||
ATTR_FIRMWARE = "firmware"
|
||||
|
||||
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_MODEL,
|
||||
ATTR_SERIAL_NUMBER,
|
||||
EntityCategory,
|
||||
UnitOfElectricCurrent,
|
||||
@@ -32,6 +31,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from .const import (
|
||||
ATTR_DEVICE_NAME,
|
||||
ATTR_FIRMWARE,
|
||||
ATTR_MODEL,
|
||||
DEFAULT_DEVICE_NAME,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import axis
|
||||
from axis.errors import Unauthorized
|
||||
from axis.models.mqtt import ClientState, mqtt_json_to_event
|
||||
from axis.interfaces.mqtt import mqtt_json_to_event
|
||||
from axis.models.mqtt import ClientState
|
||||
from axis.stream_manager import Signal, State
|
||||
|
||||
from homeassistant.components import mqtt
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["axis"],
|
||||
"requirements": ["axis==72"],
|
||||
"requirements": ["axis==71"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "AXIS"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "blue_current",
|
||||
"name": "Blue Current",
|
||||
"codeowners": ["@gleeuwen", "@jtodorova23"],
|
||||
"codeowners": ["@gleeuwen", "@NickKoepr", "@jtodorova23"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/blue_current",
|
||||
"integration_type": "hub",
|
||||
|
||||
@@ -11,7 +11,6 @@ from bluetooth_adapters import (
|
||||
ADAPTER_CONNECTION_SLOTS,
|
||||
ADAPTER_HW_VERSION,
|
||||
ADAPTER_MANUFACTURER,
|
||||
ADAPTER_PASSIVE_SCAN,
|
||||
ADAPTER_SW_VERSION,
|
||||
DEFAULT_ADDRESS,
|
||||
DEFAULT_CONNECTION_SLOTS,
|
||||
@@ -70,7 +69,6 @@ from .api import (
|
||||
async_register_callback,
|
||||
async_register_scanner,
|
||||
async_remove_scanner,
|
||||
async_request_active_scan,
|
||||
async_scanner_by_source,
|
||||
async_scanner_count,
|
||||
async_scanner_devices_by_address,
|
||||
@@ -81,6 +79,7 @@ from .const import (
|
||||
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
||||
CONF_ADAPTER,
|
||||
CONF_DETAILS,
|
||||
CONF_PASSIVE,
|
||||
CONF_SOURCE_CONFIG_ENTRY_ID,
|
||||
CONF_SOURCE_DEVICE_ID,
|
||||
CONF_SOURCE_DOMAIN,
|
||||
@@ -94,7 +93,7 @@ from .manager import HomeAssistantBluetoothManager
|
||||
from .match import BluetoothCallbackMatcher, IntegrationMatcher
|
||||
from .models import BluetoothCallback, BluetoothChange
|
||||
from .storage import BluetoothStorage
|
||||
from .util import adapter_title, resolve_scanning_mode
|
||||
from .util import adapter_title
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -129,7 +128,6 @@ __all__ = [
|
||||
"async_register_callback",
|
||||
"async_register_scanner",
|
||||
"async_remove_scanner",
|
||||
"async_request_active_scan",
|
||||
"async_scanner_by_source",
|
||||
"async_scanner_count",
|
||||
"async_scanner_devices_by_address",
|
||||
@@ -389,15 +387,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryNotReady(
|
||||
f"Bluetooth adapter {adapter} with address {address} not found"
|
||||
)
|
||||
passive = entry.options.get(CONF_PASSIVE)
|
||||
adapters = await manager.async_get_bluetooth_adapters()
|
||||
details = adapters[adapter]
|
||||
mode = resolve_scanning_mode(entry.options)
|
||||
# AUTO needs passive scanning support to flip on demand; without it
|
||||
# the scanner would start passive on hardware that can't do passive.
|
||||
if mode is BluetoothScanningMode.AUTO and not details.get(ADAPTER_PASSIVE_SCAN):
|
||||
mode = BluetoothScanningMode.ACTIVE
|
||||
mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE
|
||||
scanner = HaScanner(mode, adapter, address)
|
||||
scanner.async_setup()
|
||||
details = adapters[adapter]
|
||||
if entry.title == address:
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, title=adapter_title(adapter, details)
|
||||
|
||||
@@ -68,20 +68,9 @@ class ActiveBluetoothProcessorCoordinator[_DataT](
|
||||
| None = None,
|
||||
poll_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None,
|
||||
connectable: bool = True,
|
||||
scan_interval: float | None = None,
|
||||
scan_duration: float | None = None,
|
||||
) -> None:
|
||||
"""Initialize the processor."""
|
||||
super().__init__(
|
||||
hass,
|
||||
logger,
|
||||
address,
|
||||
mode,
|
||||
update_method,
|
||||
connectable,
|
||||
scan_interval,
|
||||
scan_duration,
|
||||
)
|
||||
super().__init__(hass, logger, address, mode, update_method, connectable)
|
||||
|
||||
self._needs_poll_method = needs_poll_method
|
||||
self._poll_method = poll_method
|
||||
|
||||
@@ -130,26 +130,17 @@ def async_register_callback(
|
||||
callback: BluetoothCallback,
|
||||
match_dict: BluetoothCallbackMatcher | None,
|
||||
mode: BluetoothScanningMode,
|
||||
*,
|
||||
scan_interval: float | None = None,
|
||||
scan_duration: float | None = None,
|
||||
) -> Callable[[], None]:
|
||||
"""Register to receive a callback on bluetooth change.
|
||||
|
||||
When ``mode`` is not PASSIVE and ``match_dict["address"]`` is set,
|
||||
the address is registered with habluetooth's active-scan scheduler
|
||||
so AUTO-mode scanners flip ACTIVE on demand for that device.
|
||||
``scan_interval`` / ``scan_duration`` default to habluetooth's
|
||||
DEFAULT_ACTIVE_SCAN_* (5 minutes / 10 seconds) when not provided;
|
||||
integrations that need a different cadence can pass explicit
|
||||
values. Without an address in the matcher the active-scan request
|
||||
is skipped; the callback itself still fires normally.
|
||||
mode is currently not used as we only support active scanning.
|
||||
Passive scanning will be available in the future. The flag
|
||||
is required to be present to avoid a future breaking change
|
||||
when we support passive scanning.
|
||||
|
||||
Returns a callback that can be used to cancel the registration.
|
||||
"""
|
||||
return _get_manager(hass).async_register_callback(
|
||||
callback, match_dict, mode, scan_interval, scan_duration
|
||||
)
|
||||
return _get_manager(hass).async_register_callback(callback, match_dict)
|
||||
|
||||
|
||||
async def async_process_advertisements(
|
||||
@@ -170,7 +161,7 @@ async def async_process_advertisements(
|
||||
done.set_result(service_info)
|
||||
|
||||
unload = _get_manager(hass).async_register_callback(
|
||||
_async_discovered_device, match_dict, mode, scan_duration=timeout
|
||||
_async_discovered_device, match_dict
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -284,19 +275,3 @@ def async_set_fallback_availability_interval(
|
||||
) -> None:
|
||||
"""Override the fallback availability timeout for a MAC address."""
|
||||
_get_manager(hass).async_set_fallback_availability_interval(address, interval)
|
||||
|
||||
|
||||
async def async_request_active_scan(
|
||||
hass: HomeAssistant, duration: float | None = None
|
||||
) -> None:
|
||||
"""Run an on-demand active sweep across every AUTO scanner.
|
||||
|
||||
Intended for config-flow discovery and other one-shot probes that
|
||||
need fresh advertisements without waiting for the periodic
|
||||
rediscovery cadence. Awaits ``duration`` seconds so the caller can
|
||||
then read newly discovered advertisements. Defaults to habluetooth's
|
||||
on-demand sweep duration when ``duration`` is not provided; the
|
||||
scheduler clamps the value to its allowed range. Concurrent callers
|
||||
dedupe to a single bus-wide window.
|
||||
"""
|
||||
await _get_manager(hass).async_request_active_scan(duration)
|
||||
|
||||
@@ -12,7 +12,7 @@ from bluetooth_adapters import (
|
||||
adapter_model,
|
||||
get_adapters,
|
||||
)
|
||||
from habluetooth import BluetoothScanningMode, get_manager
|
||||
from habluetooth import get_manager
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import onboarding
|
||||
@@ -24,21 +24,14 @@ from homeassistant.config_entries import (
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.schema_config_entry_flow import (
|
||||
SchemaCommonFlowHandler,
|
||||
SchemaFlowFormStep,
|
||||
SchemaOptionsFlowHandler,
|
||||
)
|
||||
from homeassistant.helpers.selector import (
|
||||
SelectSelector,
|
||||
SelectSelectorConfig,
|
||||
SelectSelectorMode,
|
||||
)
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
|
||||
from .const import (
|
||||
CONF_ADAPTER,
|
||||
CONF_DETAILS,
|
||||
CONF_MODE,
|
||||
CONF_PASSIVE,
|
||||
CONF_SOURCE,
|
||||
CONF_SOURCE_CONFIG_ENTRY_ID,
|
||||
@@ -47,39 +40,15 @@ from .const import (
|
||||
CONF_SOURCE_MODEL,
|
||||
DOMAIN,
|
||||
)
|
||||
from .util import adapter_title, resolve_scanning_mode
|
||||
from .util import adapter_title
|
||||
|
||||
_MODE_SELECTOR = SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=[
|
||||
BluetoothScanningMode.AUTO.value,
|
||||
BluetoothScanningMode.ACTIVE.value,
|
||||
BluetoothScanningMode.PASSIVE.value,
|
||||
],
|
||||
translation_key="mode",
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
)
|
||||
OPTIONS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSIVE, default=False): bool,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def _options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||
"""Build the options schema with the saved mode as the default."""
|
||||
current = resolve_scanning_mode(handler.options).value
|
||||
return vol.Schema({vol.Required(CONF_MODE, default=current): _MODE_SELECTOR})
|
||||
|
||||
|
||||
async def _validate_options(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Mirror CONF_MODE into the legacy CONF_PASSIVE for downgrade safety."""
|
||||
user_input[CONF_PASSIVE] = (
|
||||
user_input[CONF_MODE] == BluetoothScanningMode.PASSIVE.value
|
||||
)
|
||||
return user_input
|
||||
|
||||
|
||||
OPTIONS_FLOW = {
|
||||
"init": SchemaFlowFormStep(_options_schema, validate_user_input=_validate_options),
|
||||
"init": SchemaFlowFormStep(OPTIONS_SCHEMA),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,21 +7,14 @@ from habluetooth import ( # noqa: F401
|
||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
BluetoothScanningMode,
|
||||
)
|
||||
|
||||
from homeassistant.const import CONF_MODE # noqa: F401
|
||||
|
||||
DOMAIN = "bluetooth"
|
||||
|
||||
CONF_ADAPTER = "adapter"
|
||||
CONF_DETAILS = "details"
|
||||
# CONF_PASSIVE is the legacy boolean option; we keep writing it alongside
|
||||
# CONF_MODE so a downgrade to a pre-AUTO release reads a sensible value.
|
||||
CONF_PASSIVE = "passive"
|
||||
|
||||
DEFAULT_MODE = BluetoothScanningMode.AUTO.value
|
||||
|
||||
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_SOURCE: Final = "source"
|
||||
|
||||
@@ -202,9 +202,6 @@ class HomeAssistantBluetoothManager(BluetoothManager):
|
||||
self,
|
||||
callback: BluetoothCallback,
|
||||
matcher: BluetoothCallbackMatcher | None,
|
||||
mode: BluetoothScanningMode = BluetoothScanningMode.ACTIVE,
|
||||
scan_interval: float | None = None,
|
||||
scan_duration: float | None = None,
|
||||
) -> Callable[[], None]:
|
||||
"""Register a callback."""
|
||||
callback_matcher = BluetoothCallbackMatcherWithCallback(callback=callback)
|
||||
@@ -219,31 +216,15 @@ class HomeAssistantBluetoothManager(BluetoothManager):
|
||||
connectable = callback_matcher[CONNECTABLE]
|
||||
self._callback_index.add_callback_matcher(callback_matcher)
|
||||
|
||||
# If the matcher targets a specific address and the caller
|
||||
# didn't explicitly ask for PASSIVE, wire it into habluetooth's
|
||||
# active-scan scheduler so AUTO-mode scanners flip ACTIVE on
|
||||
# demand for this device. ``scan_interval``/``scan_duration``
|
||||
# default to habluetooth's DEFAULT_ACTIVE_SCAN_* when None.
|
||||
cancel_active_scan: Callable[[], None] | None = None
|
||||
if (
|
||||
mode is not BluetoothScanningMode.PASSIVE
|
||||
and (address := callback_matcher.get(ADDRESS)) is not None
|
||||
):
|
||||
cancel_active_scan = self.async_register_active_scan(
|
||||
address, scan_interval, scan_duration
|
||||
)
|
||||
|
||||
def _async_remove_callback() -> None:
|
||||
self._callback_index.remove_callback_matcher(callback_matcher)
|
||||
if cancel_active_scan is not None:
|
||||
cancel_active_scan()
|
||||
|
||||
# If we have history for the subscriber, we can trigger the callback
|
||||
# immediately with the last packet so the subscriber can see the
|
||||
# device.
|
||||
history = self._connectable_history if connectable else self._all_history
|
||||
service_infos: Iterable[BluetoothServiceInfoBleak] = []
|
||||
if (address := callback_matcher.get(ADDRESS)) is not None:
|
||||
if address := callback_matcher.get(ADDRESS):
|
||||
if service_info := history.get(address):
|
||||
service_infos = [service_info]
|
||||
else:
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"bleak-retry-connector==4.6.0",
|
||||
"bluetooth-adapters==2.1.0",
|
||||
"bluetooth-auto-recovery==1.5.3",
|
||||
"bluetooth-data-tools==1.29.18",
|
||||
"bluetooth-data-tools==1.29.11",
|
||||
"dbus-fast==5.0.3",
|
||||
"habluetooth==6.7.2"
|
||||
"habluetooth==6.2.0"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -298,13 +298,9 @@ class PassiveBluetoothProcessorCoordinator[_DataT](BasePassiveBluetoothCoordinat
|
||||
mode: BluetoothScanningMode,
|
||||
update_method: Callable[[BluetoothServiceInfoBleak], _DataT],
|
||||
connectable: bool = False,
|
||||
scan_interval: float | None = None,
|
||||
scan_duration: float | None = None,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
hass, logger, address, mode, connectable, scan_interval, scan_duration
|
||||
)
|
||||
super().__init__(hass, logger, address, mode, connectable)
|
||||
self._processors: list[PassiveBluetoothDataProcessor[Any, _DataT]] = []
|
||||
self._update_method = update_method
|
||||
self.last_update_success = True
|
||||
|
||||
@@ -48,21 +48,9 @@
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"mode": "Scanning mode"
|
||||
},
|
||||
"data_description": {
|
||||
"mode": "Auto is recommended for most setups. It saves battery on your Bluetooth devices while still catching new devices and updates quickly."
|
||||
"passive": "Passive scanning"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"mode": {
|
||||
"options": {
|
||||
"active": "Active (uses more device battery, fastest updates)",
|
||||
"auto": "Auto (recommended, saves device battery)",
|
||||
"passive": "Passive (lowest device battery use, some details may be missing)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ class BasePassiveBluetoothCoordinator(ABC):
|
||||
address: str,
|
||||
mode: BluetoothScanningMode,
|
||||
connectable: bool,
|
||||
scan_interval: float | None = None,
|
||||
scan_duration: float | None = None,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
self.hass = hass
|
||||
@@ -40,8 +38,6 @@ class BasePassiveBluetoothCoordinator(ABC):
|
||||
self.connectable = connectable
|
||||
self._on_stop: list[CALLBACK_TYPE] = []
|
||||
self.mode = mode
|
||||
self._scan_interval = scan_interval
|
||||
self._scan_duration = scan_duration
|
||||
self._last_unavailable_time = 0.0
|
||||
self._last_name = address
|
||||
# Subclasses are responsible for setting _available to True
|
||||
@@ -96,8 +92,6 @@ class BasePassiveBluetoothCoordinator(ABC):
|
||||
address=self.address, connectable=self.connectable
|
||||
),
|
||||
self.mode,
|
||||
scan_interval=self._scan_interval,
|
||||
scan_duration=self._scan_duration,
|
||||
)
|
||||
)
|
||||
self._on_stop.append(
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
"""The bluetooth integration utilities."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from bluetooth_adapters import (
|
||||
ADAPTER_ADDRESS,
|
||||
ADAPTER_MANUFACTURER,
|
||||
@@ -13,32 +9,14 @@ from bluetooth_adapters import (
|
||||
adapter_unique_name,
|
||||
)
|
||||
from bluetooth_data_tools import monotonic_time_coarse
|
||||
from habluetooth import BluetoothScanningMode, get_manager
|
||||
from habluetooth import get_manager
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import CONF_MODE, CONF_PASSIVE, DEFAULT_MODE
|
||||
from .models import BluetoothServiceInfoBleak
|
||||
from .storage import BluetoothStorage
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def resolve_scanning_mode(options: Mapping[str, Any]) -> BluetoothScanningMode:
|
||||
"""Resolve CONF_MODE, falling back to legacy CONF_PASSIVE or DEFAULT_MODE."""
|
||||
if (mode_value := options.get(CONF_MODE)) is not None:
|
||||
try:
|
||||
return BluetoothScanningMode(mode_value)
|
||||
except TypeError, ValueError:
|
||||
_LOGGER.warning("Unknown bluetooth scanning mode %r", mode_value)
|
||||
return BluetoothScanningMode(DEFAULT_MODE)
|
||||
if (legacy_passive := options.get(CONF_PASSIVE)) is True:
|
||||
return BluetoothScanningMode.PASSIVE
|
||||
if legacy_passive is False:
|
||||
return BluetoothScanningMode.ACTIVE
|
||||
return BluetoothScanningMode(DEFAULT_MODE)
|
||||
|
||||
|
||||
class InvalidConfigEntryID(HomeAssistantError):
|
||||
"""Invalid config entry id."""
|
||||
|
||||
@@ -9,14 +9,7 @@ from pybravia import BraviaAuthError, BraviaClient, BraviaError, BraviaNotSuppor
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import (
|
||||
ATTR_MODEL,
|
||||
CONF_CLIENT_ID,
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_NAME,
|
||||
CONF_PIN,
|
||||
)
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PIN
|
||||
from homeassistant.helpers import instance_id
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant.helpers.service_info.ssdp import (
|
||||
@@ -30,6 +23,7 @@ from homeassistant.util.network import is_host_valid
|
||||
from .const import (
|
||||
ATTR_CID,
|
||||
ATTR_MAC,
|
||||
ATTR_MODEL,
|
||||
CONF_NICKNAME,
|
||||
CONF_USE_PSK,
|
||||
CONF_USE_SSL,
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Final
|
||||
ATTR_CID: Final = "cid"
|
||||
ATTR_MAC: Final = "macAddr"
|
||||
ATTR_MANUFACTURER: Final = "Sony"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
ATTR_MODEL: Final = "model"
|
||||
|
||||
CONF_NICKNAME: Final = "nickname"
|
||||
CONF_USE_PSK: Final = "use_psk"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, override
|
||||
from typing import Any
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import DAVError
|
||||
@@ -204,20 +204,17 @@ 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)
|
||||
@@ -243,7 +240,6 @@ 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."""
|
||||
@@ -259,7 +255,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import AuthorizationError, DAVError
|
||||
@@ -33,7 +33,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import caldav
|
||||
|
||||
@@ -90,7 +90,6 @@ 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, override
|
||||
from typing import Any, cast
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import DAVError, NotFoundError
|
||||
@@ -121,7 +121,6 @@ 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] = {}
|
||||
@@ -142,7 +141,6 @@ 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)
|
||||
@@ -179,7 +177,6 @@ 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, override
|
||||
from typing import Any, Final, cast, final
|
||||
|
||||
from aiohttp import web
|
||||
from dateutil.rrule import rrulestr
|
||||
@@ -546,7 +546,6 @@ 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:
|
||||
@@ -565,7 +564,6 @@ class CalendarEntity(Entity):
|
||||
"""Return the next upcoming event."""
|
||||
raise NotImplementedError
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, Any] | None:
|
||||
@@ -582,7 +580,6 @@ class CalendarEntity(Entity):
|
||||
"description": event.description or "",
|
||||
}
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state(self) -> str:
|
||||
@@ -597,7 +594,6 @@ class CalendarEntity(Entity):
|
||||
|
||||
return STATE_OFF
|
||||
|
||||
@override
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine.
|
||||
@@ -657,7 +653,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -113,7 +113,6 @@ 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})"
|
||||
@@ -326,7 +325,6 @@ 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."""
|
||||
@@ -353,7 +351,6 @@ class TargetCalendarEventListener(TargetEntityChangeTracker):
|
||||
)
|
||||
await self._calendar_event_listener.async_attach()
|
||||
|
||||
@override
|
||||
def _unsubscribe(self) -> None:
|
||||
"""Unsubscribe from all events."""
|
||||
super()._unsubscribe()
|
||||
@@ -370,7 +367,6 @@ class SingleEntityEventTrigger(Trigger):
|
||||
|
||||
_options: dict[str, Any]
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
async def async_validate_complete_config(
|
||||
cls, hass: HomeAssistant, complete_config: ConfigType
|
||||
@@ -381,7 +377,6 @@ 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
|
||||
@@ -397,7 +392,6 @@ 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:
|
||||
@@ -432,7 +426,6 @@ class EventTrigger(Trigger):
|
||||
_options: dict[str, Any]
|
||||
_event_type: str
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
async def async_validate_config(
|
||||
cls, hass: HomeAssistant, config: ConfigType
|
||||
@@ -450,7 +443,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
import voluptuous as vol
|
||||
@@ -29,7 +29,6 @@ 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:
|
||||
@@ -82,7 +81,6 @@ 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, override
|
||||
from typing import Any, Concatenate
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
from aiostreammagic.models import CallbackType
|
||||
@@ -60,18 +60,15 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from aiostreammagic import (
|
||||
RepeatMode as CambridgeRepeatMode,
|
||||
@@ -83,7 +83,6 @@ 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."""
|
||||
@@ -101,7 +100,6 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
||||
features |= feature
|
||||
return features
|
||||
|
||||
@override
|
||||
@property
|
||||
def state(self) -> MediaPlayerState:
|
||||
"""Return the state of the device."""
|
||||
@@ -120,13 +118,11 @@ 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."""
|
||||
@@ -139,13 +135,11 @@ 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."""
|
||||
@@ -157,62 +151,52 @@ 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."""
|
||||
@@ -221,13 +205,11 @@ 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."""
|
||||
@@ -240,13 +222,11 @@ 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."""
|
||||
@@ -259,19 +239,16 @@ 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."""
|
||||
@@ -280,49 +257,41 @@ 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."""
|
||||
@@ -331,7 +300,6 @@ 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."""
|
||||
@@ -340,7 +308,6 @@ 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
|
||||
@@ -386,7 +353,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
|
||||
@@ -90,19 +90,16 @@ 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,7 +2,6 @@
|
||||
|
||||
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
|
||||
@@ -128,13 +127,11 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
|
||||
@@ -103,19 +103,16 @@ 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, override
|
||||
from typing import Any, Final, final
|
||||
|
||||
from aiohttp import hdrs, web
|
||||
import attr
|
||||
@@ -455,7 +455,6 @@ 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."""
|
||||
@@ -468,7 +467,6 @@ 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."""
|
||||
@@ -504,7 +502,6 @@ 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."""
|
||||
@@ -598,7 +595,6 @@ 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:
|
||||
@@ -646,7 +642,6 @@ 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]:
|
||||
@@ -670,7 +665,6 @@ 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()
|
||||
@@ -764,7 +758,6 @@ 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.
|
||||
@@ -824,7 +817,6 @@ 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")
|
||||
@@ -848,7 +840,6 @@ 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:
|
||||
@@ -994,7 +985,6 @@ class _TemplateCameraEntity:
|
||||
self._report_issue()
|
||||
return getattr(self._camera, name)
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
"""Forward to the camera entity."""
|
||||
self._report_issue()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""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 (
|
||||
@@ -55,7 +54,6 @@ 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]
|
||||
@@ -84,7 +82,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from mashumaro import MissingField
|
||||
import voluptuous as vol
|
||||
@@ -73,7 +73,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from canary.const import LOCATION_MODE_AWAY, LOCATION_MODE_HOME, LOCATION_MODE_NIGHT
|
||||
from canary.model import Location
|
||||
@@ -58,7 +58,6 @@ 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."""
|
||||
@@ -75,30 +74,25 @@ 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, override
|
||||
from typing import Final
|
||||
|
||||
from aiohttp.web import Request, StreamResponse
|
||||
from canary.live_stream_api import LiveStreamSession
|
||||
@@ -108,19 +108,16 @@ 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:
|
||||
@@ -153,7 +150,6 @@ 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, override
|
||||
from typing import Any, Final
|
||||
|
||||
from canary.api import Api
|
||||
from requests.exceptions import ConnectTimeout, HTTPError
|
||||
@@ -46,14 +46,12 @@ 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,7 +4,6 @@ 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
|
||||
@@ -61,7 +60,6 @@ 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, override
|
||||
from typing import Final
|
||||
|
||||
from canary.model import Device, Location, SensorType
|
||||
|
||||
@@ -143,13 +143,11 @@ 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,7 +1,5 @@
|
||||
"""Casper Glow integration binary sensor platform."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
@@ -45,7 +43,6 @@ 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()
|
||||
@@ -74,7 +71,6 @@ 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,7 +2,6 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import CasperGlow
|
||||
|
||||
@@ -67,7 +66,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from bluetooth_data_tools import human_readable_name
|
||||
from pycasperglow import CasperGlow, CasperGlowError
|
||||
@@ -31,7 +31,6 @@ 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:
|
||||
@@ -74,7 +73,6 @@ 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,7 +1,6 @@
|
||||
"""Coordinator for the Casper Glow integration."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from bleak import BleakError
|
||||
from bluetooth_data_tools import monotonic_time_coarse
|
||||
@@ -75,7 +74,6 @@ 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
|
||||
@@ -101,7 +99,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
@@ -54,7 +54,6 @@ 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()
|
||||
@@ -78,7 +77,6 @@ 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
|
||||
@@ -100,7 +98,6 @@ 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,7 +1,5 @@
|
||||
"""Casper Glow integration select platform for dimming time."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
@@ -40,7 +38,6 @@ 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."""
|
||||
@@ -48,7 +45,6 @@ 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()
|
||||
@@ -79,7 +75,6 @@ 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,7 +1,6 @@
|
||||
"""Casper Glow integration sensor platform."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import override
|
||||
|
||||
from pycasperglow import GlowState
|
||||
|
||||
@@ -52,7 +51,6 @@ 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()
|
||||
@@ -95,7 +93,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -47,7 +47,6 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
@@ -56,14 +55,12 @@ 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, override
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pychromecast.discovery
|
||||
import pychromecast.models
|
||||
@@ -67,17 +67,14 @@ 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, override
|
||||
from typing import TYPE_CHECKING, ClassVar
|
||||
from urllib.parse import urlparse
|
||||
from uuid import UUID
|
||||
|
||||
@@ -180,45 +180,37 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any, Concatenate
|
||||
|
||||
import pychromecast.config
|
||||
import pychromecast.const
|
||||
@@ -338,7 +338,6 @@ 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)
|
||||
@@ -347,7 +346,6 @@ 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()
|
||||
@@ -356,7 +354,6 @@ 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()
|
||||
@@ -366,7 +363,6 @@ 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()
|
||||
@@ -374,7 +370,6 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
self._attr_available = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
def _invalidate(self):
|
||||
"""Invalidate some attributes."""
|
||||
super()._invalidate()
|
||||
@@ -533,7 +528,6 @@ 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."""
|
||||
|
||||
@@ -553,60 +547,51 @@ 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."""
|
||||
@@ -651,7 +636,6 @@ 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,
|
||||
@@ -691,7 +675,6 @@ 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:
|
||||
@@ -833,7 +816,6 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return (media_status, media_status_received)
|
||||
|
||||
@override
|
||||
@property
|
||||
def state(self) -> MediaPlayerState | None:
|
||||
"""Return the state of the player."""
|
||||
@@ -876,7 +858,6 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return MediaPlayerState.IDLE
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_content_id(self) -> str | None:
|
||||
"""Content ID of current playing media."""
|
||||
@@ -886,7 +867,6 @@ 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."""
|
||||
@@ -911,7 +891,6 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return MediaType.VIDEO
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_duration(self):
|
||||
"""Duration of current playing media in seconds."""
|
||||
@@ -921,7 +900,6 @@ 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."""
|
||||
@@ -932,75 +910,64 @@ 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."""
|
||||
@@ -1039,7 +1006,6 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
|
||||
return support
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_position(self):
|
||||
"""Position of current playing media in seconds."""
|
||||
@@ -1055,7 +1021,6 @@ 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.
|
||||
@@ -1110,7 +1075,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from ccm15 import CCM15DeviceState, CCM15SlaveDevice
|
||||
|
||||
@@ -93,7 +93,6 @@ 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."""
|
||||
@@ -101,7 +100,6 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return data.temperature
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def target_temperature(self) -> int | None:
|
||||
"""Return target temperature."""
|
||||
@@ -109,7 +107,6 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return data.temperature_setpoint
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return hvac mode."""
|
||||
@@ -118,7 +115,6 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return CONST_CMD_STATE_MAP[mode]
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
"""Return fan mode."""
|
||||
@@ -127,7 +123,6 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
|
||||
return CONST_CMD_FAN_MAP[mode]
|
||||
return None
|
||||
|
||||
@override
|
||||
@property
|
||||
def swing_mode(self) -> str | None:
|
||||
"""Return swing mode."""
|
||||
@@ -135,13 +130,11 @@ 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."""
|
||||
@@ -149,7 +142,6 @@ 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:
|
||||
@@ -157,22 +149,18 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from ccm15 import CCM15Device
|
||||
import voluptuous as vol
|
||||
@@ -27,7 +27,6 @@ class CCM15ConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice
|
||||
import httpx
|
||||
@@ -45,7 +44,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from aiocentriconnect import CentriConnect
|
||||
from aiocentriconnect.exceptions import (
|
||||
@@ -58,7 +58,6 @@ class CentriConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -6,7 +6,6 @@ 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
|
||||
@@ -66,7 +65,6 @@ 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()
|
||||
@@ -81,7 +79,6 @@ 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,7 +4,6 @@ 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,
|
||||
@@ -237,7 +236,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -53,7 +53,6 @@ class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
return True
|
||||
return False
|
||||
|
||||
@override
|
||||
async def async_step_user(
|
||||
self,
|
||||
user_input: Mapping[str, Any] | None = None,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -47,7 +46,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -12,7 +12,6 @@ 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,7 +1,6 @@
|
||||
"""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
|
||||
@@ -45,7 +44,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from dio_chacon_wifi_api import DIOChaconAPIClient
|
||||
from dio_chacon_wifi_api.exceptions import DIOChaconAPIError, DIOChaconInvalidAuthError
|
||||
@@ -25,7 +25,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from dio_chacon_wifi_api.const import DeviceTypeEnum, ShutterMoveEnum
|
||||
|
||||
@@ -49,7 +49,6 @@ 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"]
|
||||
@@ -58,7 +57,6 @@ 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.
|
||||
|
||||
@@ -82,7 +80,6 @@ class ChaconDioCover(ChaconDioEntity, CoverEntity):
|
||||
self.target_id, ShutterMoveEnum.DOWN
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover.
|
||||
|
||||
@@ -104,7 +101,6 @@ 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."""
|
||||
|
||||
@@ -116,7 +112,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from dio_chacon_wifi_api import DIOChaconAPIClient
|
||||
|
||||
@@ -38,7 +38,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from dio_chacon_wifi_api.const import DeviceTypeEnum
|
||||
|
||||
@@ -38,13 +38,11 @@ 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.
|
||||
|
||||
@@ -61,7 +59,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from pychannels import Channels
|
||||
import voluptuous as vol
|
||||
@@ -138,13 +138,11 @@ 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."""
|
||||
@@ -164,25 +162,21 @@ 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."""
|
||||
@@ -193,7 +187,6 @@ class ChannelsPlayer(MediaPlayerEntity):
|
||||
|
||||
return "https://getchannels.com/assets/img/icon-1024.png"
|
||||
|
||||
@override
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Title of current playing media."""
|
||||
@@ -202,45 +195,38 @@ 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:
|
||||
@@ -249,7 +235,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from chess_com_api import ChessComClient, NotFoundError
|
||||
import voluptuous as vol
|
||||
@@ -18,7 +18,6 @@ _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,7 +3,6 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from chess_com_api import ChessComAPIError, ChessComClient, Player, PlayerStats
|
||||
|
||||
@@ -46,7 +45,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from chess_com_api import PlayerStats
|
||||
|
||||
@@ -121,7 +121,6 @@ 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."""
|
||||
@@ -149,7 +148,6 @@ 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, override
|
||||
from typing import Any, Concatenate
|
||||
|
||||
from cieloconnectapi.exceptions import AuthenticationError
|
||||
|
||||
@@ -103,7 +103,6 @@ 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.
|
||||
@@ -125,7 +124,6 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
|
||||
return UnitOfTemperature.CELSIUS
|
||||
|
||||
@override
|
||||
@property
|
||||
def supported_features(self) -> ClimateEntityFeature:
|
||||
"""Return dynamic feature flags based on the current mode."""
|
||||
@@ -149,7 +147,6 @@ class CieloClimate(CieloDeviceEntity, ClimateEntity):
|
||||
|
||||
return flags
|
||||
|
||||
@override
|
||||
@property
|
||||
def current_humidity(self) -> int | None:
|
||||
"""Return the current humidity, if available."""
|
||||
@@ -157,69 +154,58 @@ 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.
|
||||
@@ -231,7 +217,6 @@ 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.
|
||||
@@ -243,13 +228,11 @@ 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.
|
||||
@@ -261,19 +244,16 @@ 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."""
|
||||
@@ -290,32 +270,27 @@ 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 []
|
||||
@@ -328,7 +303,6 @@ 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, override
|
||||
from typing import Any, Final
|
||||
|
||||
from aiohttp import ClientError
|
||||
from cieloconnectapi import CieloClient
|
||||
@@ -61,7 +61,6 @@ 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, override
|
||||
from typing import Any, Final
|
||||
|
||||
from aiohttp import ClientError
|
||||
from cieloconnectapi import CieloClient
|
||||
@@ -59,7 +59,6 @@ class CieloDataUpdateCoordinator(DataUpdateCoordinator[CieloData]):
|
||||
),
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> CieloData:
|
||||
"""Fetch data from the API."""
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Base entity for Cielo integration."""
|
||||
|
||||
from typing import override
|
||||
|
||||
from cieloconnectapi.device import CieloDeviceAPI
|
||||
from cieloconnectapi.model import CieloDevice
|
||||
|
||||
@@ -33,7 +31,6 @@ 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:
|
||||
@@ -45,7 +42,6 @@ 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,7 +1,6 @@
|
||||
"""Support for Cisco IOS Routers."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from pexpect import pxssh
|
||||
import voluptuous as vol
|
||||
@@ -51,12 +50,10 @@ 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,7 +1,6 @@
|
||||
"""Support for Cisco Mobility Express."""
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from ciscomobilityexpress.ciscome import CiscoMobilityExpress
|
||||
import voluptuous as vol
|
||||
@@ -63,14 +62,12 @@ 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(
|
||||
@@ -78,7 +75,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
from webexpythonsdk import ApiError, WebexAPI, exceptions
|
||||
@@ -50,7 +50,6 @@ 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,7 +2,6 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import time
|
||||
from typing import override
|
||||
|
||||
from clementineremote import ClementineRemote
|
||||
import voluptuous as vol
|
||||
@@ -109,7 +108,6 @@ class ClementineDevice(MediaPlayerEntity):
|
||||
self._attr_state = MediaPlayerState.OFF
|
||||
raise
|
||||
|
||||
@override
|
||||
def select_source(self, source: str) -> None:
|
||||
"""Select input source."""
|
||||
client = self._client
|
||||
@@ -117,7 +115,6 @@ 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:
|
||||
@@ -126,12 +123,10 @@ 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))
|
||||
@@ -143,24 +138,20 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -44,7 +44,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -64,7 +64,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
@@ -81,7 +81,6 @@ 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, override
|
||||
from typing import Any, Literal, final
|
||||
|
||||
from propcache.api import cached_property
|
||||
import voluptuous as vol
|
||||
@@ -283,7 +283,6 @@ 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:
|
||||
@@ -305,7 +304,6 @@ 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."""
|
||||
@@ -344,7 +342,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
|
||||
return data
|
||||
|
||||
@override
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, Any]:
|
||||
@@ -703,7 +700,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -47,7 +47,6 @@ 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
|
||||
@@ -60,7 +59,6 @@ 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 (
|
||||
@@ -68,7 +66,6 @@ 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
|
||||
@@ -81,7 +78,6 @@ 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,7 +1,5 @@
|
||||
"""Intents for the climate integration."""
|
||||
|
||||
from typing import override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
@@ -37,7 +35,6 @@ 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,7 +1,5 @@
|
||||
"""Provides triggers for climates."""
|
||||
|
||||
from typing import override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_TEMPERATURE, CONF_OPTIONS, UnitOfTemperature
|
||||
@@ -58,7 +56,6 @@ 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 (
|
||||
@@ -66,7 +63,6 @@ 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
|
||||
@@ -93,7 +89,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from awesomeversion import AwesomeVersion
|
||||
@@ -95,19 +95,16 @@ 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(
|
||||
@@ -141,14 +138,12 @@ 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,7 +3,6 @@
|
||||
import io
|
||||
from json import JSONDecodeError
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from hass_nabucasa.llm import (
|
||||
LLMAuthenticationError,
|
||||
@@ -109,13 +108,11 @@ 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,
|
||||
@@ -153,7 +150,6 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import aiohttp
|
||||
from hass_nabucasa import AlexaApiError, Cloud
|
||||
@@ -32,6 +32,7 @@ from homeassistant.components.homeassistant.exposed_entities import (
|
||||
async_should_expose,
|
||||
)
|
||||
from homeassistant.components.sensor import SensorDeviceClass
|
||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
|
||||
from homeassistant.core import Event, HomeAssistant, callback, split_entity_id
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er, start
|
||||
@@ -162,13 +163,11 @@ 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."""
|
||||
@@ -178,7 +177,6 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
and self.authorized
|
||||
)
|
||||
|
||||
@override
|
||||
@property
|
||||
def endpoint(self) -> str | URL | None:
|
||||
"""Endpoint for report state."""
|
||||
@@ -187,20 +185,17 @@ 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."""
|
||||
@@ -223,7 +218,6 @@ 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()
|
||||
@@ -281,6 +275,9 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
|
||||
|
||||
def _should_expose_legacy(self, entity_id: str) -> bool:
|
||||
"""If an entity should be exposed."""
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
return False
|
||||
|
||||
entity_configs = self._prefs.alexa_entity_configs
|
||||
entity_config = entity_configs.get(entity_id, {})
|
||||
entity_expose: bool | None = entity_config.get(PREF_SHOULD_EXPOSE)
|
||||
@@ -306,23 +303,22 @@ 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."""
|
||||
entity_filter: EntityFilter = self._config[CONF_FILTER]
|
||||
if not entity_filter.empty_filter:
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
return False
|
||||
return entity_filter(entity_id)
|
||||
|
||||
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, override
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from hass_nabucasa import Cloud, CloudApiError, CloudApiNonRetryableError, CloudError
|
||||
@@ -79,7 +79,6 @@ class CloudBackupAgent(BackupAgent):
|
||||
self._cloud = cloud
|
||||
self._hass = hass
|
||||
|
||||
@override
|
||||
async def async_download_backup(
|
||||
self,
|
||||
backup_id: str,
|
||||
@@ -101,7 +100,6 @@ class CloudBackupAgent(BackupAgent):
|
||||
|
||||
return ChunkAsyncStreamIterator(content)
|
||||
|
||||
@override
|
||||
async def async_upload_backup(
|
||||
self,
|
||||
*,
|
||||
@@ -172,7 +170,6 @@ class CloudBackupAgent(BackupAgent):
|
||||
)
|
||||
await asyncio.sleep(retry_timer)
|
||||
|
||||
@override
|
||||
async def async_delete_backup(
|
||||
self,
|
||||
backup_id: str,
|
||||
@@ -191,7 +188,6 @@ 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()
|
||||
@@ -207,7 +203,6 @@ 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, override
|
||||
from typing import Any
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
|
||||
@@ -44,19 +44,16 @@ 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, override
|
||||
from typing import Any, Literal
|
||||
|
||||
import aiohttp
|
||||
from hass_nabucasa.client import CloudClient as Interface, RemoteActivationNotAllowed
|
||||
@@ -71,7 +71,6 @@ 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."""
|
||||
@@ -82,37 +81,31 @@ 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."""
|
||||
@@ -172,7 +165,6 @@ class CloudClient(Interface):
|
||||
|
||||
return self._google_config
|
||||
|
||||
@override
|
||||
async def cloud_connected(self) -> None:
|
||||
"""When cloud is connected."""
|
||||
_LOGGER.debug("cloud_connected")
|
||||
@@ -266,22 +258,18 @@ 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 = []
|
||||
@@ -299,27 +287,23 @@ 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]:
|
||||
@@ -337,7 +321,6 @@ 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()
|
||||
@@ -350,7 +333,6 @@ 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()
|
||||
@@ -374,7 +356,6 @@ 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"]
|
||||
@@ -414,20 +395,17 @@ 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,
|
||||
@@ -454,7 +432,6 @@ 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, override
|
||||
from typing import Literal
|
||||
|
||||
from homeassistant.components import conversation
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -35,19 +35,16 @@ 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, override
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
from hass_nabucasa.google_report_state import ErrorResponse
|
||||
@@ -22,6 +22,7 @@ from homeassistant.components.homeassistant.exposed_entities import (
|
||||
async_should_expose,
|
||||
)
|
||||
from homeassistant.components.sensor import SensorDeviceClass
|
||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
|
||||
from homeassistant.core import (
|
||||
CoreState,
|
||||
Event,
|
||||
@@ -145,7 +146,6 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
self._cloud = cloud
|
||||
self._sync_entities_lock = asyncio.Lock()
|
||||
|
||||
@override
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
"""Return if Google is enabled."""
|
||||
@@ -155,30 +155,25 @@ 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.
|
||||
|
||||
@@ -217,7 +212,6 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
_2fa_disabled,
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_initialize(self) -> None:
|
||||
"""Perform async initialization of config."""
|
||||
_LOGGER.debug("async_initialize")
|
||||
@@ -282,13 +276,15 @@ 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)
|
||||
|
||||
def _should_expose_legacy(self, entity_id: str) -> bool:
|
||||
"""If an entity ID should be exposed."""
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
return False
|
||||
|
||||
entity_configs = self._prefs.google_entity_configs
|
||||
entity_config = entity_configs.get(entity_id, {})
|
||||
entity_expose: bool | None = entity_config.get(PREF_SHOULD_EXPOSE)
|
||||
@@ -320,6 +316,8 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
"""If an entity should be exposed."""
|
||||
entity_filter: EntityFilter = self._config[CONF_FILTER]
|
||||
if not entity_filter.empty_filter:
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
return False
|
||||
return entity_filter(entity_id)
|
||||
|
||||
return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
|
||||
@@ -334,12 +332,10 @@ 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.
|
||||
|
||||
@@ -356,7 +352,6 @@ 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:
|
||||
@@ -368,7 +363,6 @@ 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:
|
||||
@@ -378,7 +372,6 @@ 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():
|
||||
@@ -388,7 +381,6 @@ 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.
|
||||
|
||||
@@ -396,7 +388,6 @@ 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.
|
||||
|
||||
@@ -407,7 +398,6 @@ 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,7 +2,6 @@
|
||||
|
||||
from collections import deque
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -17,7 +16,6 @@ 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)
|
||||
|
||||
@@ -29,6 +29,7 @@ from homeassistant.components.homeassistant import exposed_entities
|
||||
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
|
||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||
from homeassistant.components.system_health import get_info as get_system_health_info
|
||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -972,7 +973,7 @@ async def google_assistant_get(
|
||||
return
|
||||
|
||||
entity = google_helpers.GoogleEntity(hass, gconf, state)
|
||||
if not entity.is_supported():
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES or not entity.is_supported():
|
||||
connection.send_error(
|
||||
msg["id"],
|
||||
websocket_api.ERR_NOT_SUPPORTED,
|
||||
@@ -1074,7 +1075,9 @@ async def alexa_get(
|
||||
"""Get data for a single alexa entity."""
|
||||
entity_id: str = msg["entity_id"]
|
||||
|
||||
if not entity_supported_by_alexa(hass, entity_id):
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES or not entity_supported_by_alexa(
|
||||
hass, entity_id
|
||||
):
|
||||
connection.send_error(
|
||||
msg["id"],
|
||||
websocket_api.ERR_NOT_SUPPORTED,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from functools import wraps
|
||||
from typing import TYPE_CHECKING, Any, Concatenate, override
|
||||
from typing import TYPE_CHECKING, Any, Concatenate
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.web_exceptions import HTTPUnauthorized
|
||||
@@ -64,7 +64,6 @@ 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."""
|
||||
@@ -77,7 +76,6 @@ 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."""
|
||||
@@ -90,7 +88,6 @@ 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."""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user