Skip to content

Commit

Permalink
2025.1.1 (#134940)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck authored Jan 7, 2025
2 parents a412ace + 298f059 commit d59a91a
Show file tree
Hide file tree
Showing 81 changed files with 922 additions and 238 deletions.
8 changes: 4 additions & 4 deletions homeassistant/components/androidtv_remote/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@
}
},
"apps": {
"title": "Configure Android Apps",
"description": "Configure application id {app_id}",
"title": "Configure Android apps",
"description": "Configure application ID {app_id}",
"data": {
"app_name": "Application Name",
"app_name": "Application name",
"app_id": "Application ID",
"app_icon": "Application Icon",
"app_icon": "Application icon",
"app_delete": "Check to delete this application"
}
}
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/asuswrt/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"invalid_unique_id": "Impossible to determine a valid unique id for the device",
"no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible"
"invalid_unique_id": "Impossible to determine a valid unique ID for the device",
"no_unique_id": "A device without a valid unique ID is already configured. Configuration of multiple instances is not possible"
}
},
"options": {
Expand All @@ -42,7 +42,7 @@
"consider_home": "Seconds to wait before considering a device away",
"track_unknown": "Track unknown / unnamed devices",
"interface": "The interface that you want statistics from (e.g. eth0, eth1 etc)",
"dnsmasq": "The location in the router of the dnsmasq.leases files",
"dnsmasq": "The location of the dnsmasq.leases file in the router",
"require_ip": "Devices must have IP (for access point mode)"
}
}
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/backup/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ async def _async_upload_backup(
# no point in continuing
raise BackupManagerError(str(result)) from result
if isinstance(result, BackupAgentError):
LOGGER.error("Error uploading to %s: %s", agent_ids[idx], result)
agent_errors[agent_ids[idx]] = result
continue
if isinstance(result, Exception):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/bluetooth/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
"bluetooth-auto-recovery==1.4.2",
"bluetooth-data-tools==1.20.0",
"dbus-fast==2.24.3",
"habluetooth==3.6.0"
"habluetooth==3.7.0"
]
}
1 change: 1 addition & 0 deletions homeassistant/components/bring/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/bring",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["bring_api"],
"requirements": ["bring-api==0.9.1"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/cambridge_audio/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}
},
"discovery_confirm": {
"description": "Do you want to setup {name}?"
"description": "Do you want to set up {name}?"
},
"reconfigure": {
"description": "Reconfigure your Cambridge Audio Streamer.",
Expand All @@ -28,7 +28,7 @@
"cannot_connect": "Failed to connect to Cambridge Audio device. Please make sure the device is powered up and connected to the network. Try power-cycling the device if it does not connect."
},
"abort": {
"wrong_device": "This Cambridge Audio device does not match the existing device id. Please make sure you entered the correct IP address.",
"wrong_device": "This Cambridge Audio device does not match the existing device ID. Please make sure you entered the correct IP address.",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
Expand Down
26 changes: 21 additions & 5 deletions homeassistant/components/camera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,19 @@ def supported_features(self) -> CameraEntityFeature:
"""Flag supported features."""
return self._attr_supported_features

@property
def supported_features_compat(self) -> CameraEntityFeature:
"""Return the supported features as CameraEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = CameraEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features

@cached_property
def is_recording(self) -> bool:
"""Return true if the device is recording."""
Expand Down Expand Up @@ -569,7 +582,7 @@ def frontend_stream_type(self) -> StreamType | None:

self._deprecate_attr_frontend_stream_type_logged = True
return self._attr_frontend_stream_type
if CameraEntityFeature.STREAM not in self.supported_features:
if CameraEntityFeature.STREAM not in self.supported_features_compat:
return None
if (
self._webrtc_provider
Expand Down Expand Up @@ -798,7 +811,9 @@ def async_update_token(self) -> None:
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()
self.__supports_stream = self.supported_features & CameraEntityFeature.STREAM
self.__supports_stream = (
self.supported_features_compat & CameraEntityFeature.STREAM
)
await self.async_refresh_providers(write_state=False)

async def async_refresh_providers(self, *, write_state: bool = True) -> None:
Expand Down Expand Up @@ -838,7 +853,7 @@ async def _async_get_supported_webrtc_provider[_T](
self, fn: Callable[[HomeAssistant, Camera], Coroutine[None, None, _T | None]]
) -> _T | None:
"""Get first provider that supports this camera."""
if CameraEntityFeature.STREAM not in self.supported_features:
if CameraEntityFeature.STREAM not in self.supported_features_compat:
return None

return await fn(self.hass, self)
Expand Down Expand Up @@ -896,7 +911,7 @@ def _invalidate_camera_capabilities_cache(self) -> None:
def camera_capabilities(self) -> CameraCapabilities:
"""Return the camera capabilities."""
frontend_stream_types = set()
if CameraEntityFeature.STREAM in self.supported_features:
if CameraEntityFeature.STREAM in self.supported_features_compat:
if self._supports_native_sync_webrtc or self._supports_native_async_webrtc:
# The camera has a native WebRTC implementation
frontend_stream_types.add(StreamType.WEB_RTC)
Expand All @@ -916,7 +931,8 @@ def async_write_ha_state(self) -> None:
"""
super().async_write_ha_state()
if self.__supports_stream != (
supports_stream := self.supported_features & CameraEntityFeature.STREAM
supports_stream := self.supported_features_compat
& CameraEntityFeature.STREAM
):
self.__supports_stream = supports_stream
self._invalidate_camera_capabilities_cache()
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/cloud/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ async def async_upload_backup(
headers=details["headers"] | {"content-length": str(backup.size)},
timeout=ClientTimeout(connect=10.0, total=43200.0), # 43200s == 12h
)
_LOGGER.log(
logging.DEBUG if upload_status.status < 400 else logging.WARNING,
"Backup upload status: %s",
upload_status.status,
)
upload_status.raise_for_status()
except (TimeoutError, ClientError) as err:
raise BackupAgentError("Failed to upload backup") from err
Expand Down
12 changes: 7 additions & 5 deletions homeassistant/components/cookidoo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from cookidoo_api import Cookidoo, CookidooConfig, CookidooLocalizationConfig
from cookidoo_api import Cookidoo, CookidooConfig, get_localization_options

from homeassistant.const import (
CONF_COUNTRY,
Expand All @@ -22,15 +22,17 @@
async def async_setup_entry(hass: HomeAssistant, entry: CookidooConfigEntry) -> bool:
"""Set up Cookidoo from a config entry."""

localizations = await get_localization_options(
country=entry.data[CONF_COUNTRY].lower(),
language=entry.data[CONF_LANGUAGE],
)

cookidoo = Cookidoo(
async_get_clientsession(hass),
CookidooConfig(
email=entry.data[CONF_EMAIL],
password=entry.data[CONF_PASSWORD],
localization=CookidooLocalizationConfig(
country_code=entry.data[CONF_COUNTRY].lower(),
language=entry.data[CONF_LANGUAGE],
),
localization=localizations[0],
),
)

Expand Down
16 changes: 8 additions & 8 deletions homeassistant/components/cookidoo/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
Cookidoo,
CookidooAuthException,
CookidooConfig,
CookidooLocalizationConfig,
CookidooRequestException,
get_country_options,
get_localization_options,
Expand Down Expand Up @@ -219,18 +218,19 @@ async def validate_input(
else:
data_input[CONF_LANGUAGE] = (
await get_localization_options(country=data_input[CONF_COUNTRY].lower())
)[0] # Pick any language to test login
)[0].language # Pick any language to test login

localizations = await get_localization_options(
country=data_input[CONF_COUNTRY].lower(),
language=data_input[CONF_LANGUAGE],
)

session = async_get_clientsession(self.hass)
cookidoo = Cookidoo(
session,
async_get_clientsession(self.hass),
CookidooConfig(
email=data_input[CONF_EMAIL],
password=data_input[CONF_PASSWORD],
localization=CookidooLocalizationConfig(
country_code=data_input[CONF_COUNTRY].lower(),
language=data_input[CONF_LANGUAGE],
),
localization=localizations[0],
),
)
try:
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/cookidoo/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/cookidoo",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["cookidoo_api"],
"quality_scale": "silver",
"requirements": ["cookidoo-api==0.10.0"]
"requirements": ["cookidoo-api==0.11.2"]
}
4 changes: 4 additions & 0 deletions homeassistant/components/cover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ def state_attributes(self) -> dict[str, Any]:
def supported_features(self) -> CoverEntityFeature:
"""Flag supported features."""
if (features := self._attr_supported_features) is not None:
if type(features) is int: # noqa: E721
new_features = CoverEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features

supported_features = (
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/enigma2/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["openwebif"],
"requirements": ["openwebifpy==4.3.0"]
"requirements": ["openwebifpy==4.3.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/eq3btsmart/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==1.1.0"]
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.0.0"]
}
4 changes: 1 addition & 3 deletions homeassistant/components/esphome/bluetooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from aioesphomeapi import APIClient, DeviceInfo
from bleak_esphome import connect_scanner
from bleak_esphome.backend.cache import ESPHomeBluetoothCache

from homeassistant.components.bluetooth import async_register_scanner
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
Expand All @@ -28,10 +27,9 @@ def async_connect_scanner(
entry_data: RuntimeEntryData,
cli: APIClient,
device_info: DeviceInfo,
cache: ESPHomeBluetoothCache,
) -> CALLBACK_TYPE:
"""Connect scanner."""
client_data = connect_scanner(cli, device_info, cache, entry_data.available)
client_data = connect_scanner(cli, device_info, entry_data.available)
entry_data.bluetooth_device = client_data.bluetooth_device
client_data.disconnect_callbacks = entry_data.disconnect_callbacks
scanner = client_data.scanner
Expand Down
5 changes: 0 additions & 5 deletions homeassistant/components/esphome/domain_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from functools import cache
from typing import Self

from bleak_esphome.backend.cache import ESPHomeBluetoothCache

from homeassistant.core import HomeAssistant
from homeassistant.helpers.json import JSONEncoder

Expand All @@ -22,9 +20,6 @@ class DomainData:
"""Define a class that stores global esphome data in hass.data[DOMAIN]."""

_stores: dict[str, ESPHomeStorage] = field(default_factory=dict)
bluetooth_cache: ESPHomeBluetoothCache = field(
default_factory=ESPHomeBluetoothCache
)

def get_entry_data(self, entry: ESPHomeConfigEntry) -> RuntimeEntryData:
"""Return the runtime entry data associated with this config entry.
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/esphome/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,7 @@ async def _on_connnect(self) -> None:

if device_info.bluetooth_proxy_feature_flags_compat(api_version):
entry_data.disconnect_callbacks.add(
async_connect_scanner(
hass, entry_data, cli, device_info, self.domain_data.bluetooth_cache
)
async_connect_scanner(hass, entry_data, cli, device_info)
)

if device_info.voice_assistant_feature_flags_compat(api_version) and (
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/esphome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"requirements": [
"aioesphomeapi==28.0.0",
"esphome-dashboard-api==1.2.3",
"bleak-esphome==1.1.0"
"bleak-esphome==2.0.0"
],
"zeroconf": ["_esphomelib._tcp.local."]
}
26 changes: 14 additions & 12 deletions homeassistant/components/flick_electric/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from datetime import datetime as dt
import logging
from typing import Any

import jwt
from pyflick import FlickAPI
from pyflick.authentication import AbstractFlickAuth
from pyflick.authentication import SimpleFlickAuth
from pyflick.const import DEFAULT_CLIENT_ID, DEFAULT_CLIENT_SECRET

from homeassistant.config_entries import ConfigEntry
Expand Down Expand Up @@ -93,16 +94,22 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return True


class HassFlickAuth(AbstractFlickAuth):
class HassFlickAuth(SimpleFlickAuth):
"""Implementation of AbstractFlickAuth based on a Home Assistant entity config."""

def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
def __init__(self, hass: HomeAssistant, entry: FlickConfigEntry) -> None:
"""Flick authentication based on a Home Assistant entity config."""
super().__init__(aiohttp_client.async_get_clientsession(hass))
super().__init__(
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
client_id=entry.data.get(CONF_CLIENT_ID, DEFAULT_CLIENT_ID),
client_secret=entry.data.get(CONF_CLIENT_SECRET, DEFAULT_CLIENT_SECRET),
websession=aiohttp_client.async_get_clientsession(hass),
)
self._entry = entry
self._hass = hass

async def _get_entry_token(self):
async def _get_entry_token(self) -> dict[str, Any]:
# No token saved, generate one
if (
CONF_TOKEN_EXPIRY not in self._entry.data
Expand All @@ -119,13 +126,8 @@ async def _get_entry_token(self):
async def _update_token(self):
_LOGGER.debug("Fetching new access token")

token = await self.get_new_token(
username=self._entry.data[CONF_USERNAME],
password=self._entry.data[CONF_PASSWORD],
client_id=self._entry.data.get(CONF_CLIENT_ID, DEFAULT_CLIENT_ID),
client_secret=self._entry.data.get(
CONF_CLIENT_SECRET, DEFAULT_CLIENT_SECRET
),
token = await super().get_new_token(
self._username, self._password, self._client_id, self._client_secret
)

_LOGGER.debug("New token: %s", token)
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/fritz/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,18 @@ async def async_setup(
self._options = options
await self.hass.async_add_executor_job(self.setup)

device_registry = dr.async_get(self.hass)
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
configuration_url=f"http://{self.host}",
connections={(dr.CONNECTION_NETWORK_MAC, self.mac)},
identifiers={(DOMAIN, self.unique_id)},
manufacturer="AVM",
model=self.model,
name=self.config_entry.title,
sw_version=self.current_firmware,
)

def setup(self) -> None:
"""Set up FritzboxTools class."""

Expand Down
Loading

0 comments on commit d59a91a

Please sign in to comment.