From 45142b0cc0ef2f47ee7989599951f727c9c30192 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ludovic=20BOU=C3=89?= <lboue@users.noreply.github.com>
Date: Mon, 6 Jan 2025 15:35:42 +0100
Subject: [PATCH 01/47] Matter Battery replacement icon (#134460)

---
 homeassistant/components/matter/icons.json | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/homeassistant/components/matter/icons.json b/homeassistant/components/matter/icons.json
index adcdcd051376d8..ef29601b831e2a 100644
--- a/homeassistant/components/matter/icons.json
+++ b/homeassistant/components/matter/icons.json
@@ -57,6 +57,9 @@
       },
       "valve_position": {
         "default": "mdi:valve"
+      },
+      "battery_replacement_description": {
+        "default": "mdi:battery-sync-outline"
       }
     }
   }

From aafc1ff074e6bae588e7d828dcd37fbfd5e54402 Mon Sep 17 00:00:00 2001
From: peteS-UK <64092177+peteS-UK@users.noreply.github.com>
Date: Fri, 3 Jan 2025 19:28:05 +0000
Subject: [PATCH 02/47] Small fix to allow playing of expandable favorites on
 Squeezebox (#134572)

---
 homeassistant/components/squeezebox/browse_media.py | 10 ++++++----
 tests/components/squeezebox/conftest.py             |  3 +++
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/squeezebox/browse_media.py b/homeassistant/components/squeezebox/browse_media.py
index 4d1c98bc4fcf2d..331bf383c70829 100644
--- a/homeassistant/components/squeezebox/browse_media.py
+++ b/homeassistant/components/squeezebox/browse_media.py
@@ -115,6 +115,7 @@ async def build_item_response(
         item_type = CONTENT_TYPE_TO_CHILD_TYPE[search_type]
 
         children = []
+        list_playable = []
         for item in result["items"]:
             item_id = str(item["id"])
             item_thumbnail: str | None = None
@@ -131,7 +132,7 @@ async def build_item_response(
                     child_media_class = CONTENT_TYPE_MEDIA_CLASS[MediaType.ALBUM]
                     can_expand = True
                     can_play = True
-                elif item["hasitems"]:
+                elif item["hasitems"] and not item["isaudio"]:
                     child_item_type = "Favorites"
                     child_media_class = CONTENT_TYPE_MEDIA_CLASS["Favorites"]
                     can_expand = True
@@ -139,8 +140,8 @@ async def build_item_response(
                 else:
                     child_item_type = "Favorites"
                     child_media_class = CONTENT_TYPE_MEDIA_CLASS[MediaType.TRACK]
-                    can_expand = False
-                    can_play = True
+                    can_expand = item["hasitems"]
+                    can_play = item["isaudio"] and item.get("url")
 
             if artwork_track_id := item.get("artwork_track_id"):
                 if internal_request:
@@ -166,6 +167,7 @@ async def build_item_response(
                     thumbnail=item_thumbnail,
                 )
             )
+            list_playable.append(can_play)
 
     if children is None:
         raise BrowseError(f"Media not found: {search_type} / {search_id}")
@@ -179,7 +181,7 @@ async def build_item_response(
         children_media_class=media_class["children"],
         media_content_id=search_id,
         media_content_type=search_type,
-        can_play=search_type != "Favorites",
+        can_play=any(list_playable),
         children=children,
         can_expand=True,
     )
diff --git a/tests/components/squeezebox/conftest.py b/tests/components/squeezebox/conftest.py
index 2dc0cabeaa61d4..7b007114420081 100644
--- a/tests/components/squeezebox/conftest.py
+++ b/tests/components/squeezebox/conftest.py
@@ -137,6 +137,7 @@ async def mock_async_browse(
             "title": "Fake Item 1",
             "id": FAKE_VALID_ITEM_ID,
             "hasitems": False,
+            "isaudio": True,
             "item_type": child_types[media_type],
             "artwork_track_id": "b35bb9e9",
             "url": "file:///var/lib/squeezeboxserver/music/track_1.mp3",
@@ -145,6 +146,7 @@ async def mock_async_browse(
             "title": "Fake Item 2",
             "id": FAKE_VALID_ITEM_ID + "_2",
             "hasitems": media_type == "favorites",
+            "isaudio": True,
             "item_type": child_types[media_type],
             "image_url": "http://lms.internal:9000/html/images/favorites.png",
             "url": "file:///var/lib/squeezeboxserver/music/track_2.mp3",
@@ -153,6 +155,7 @@ async def mock_async_browse(
             "title": "Fake Item 3",
             "id": FAKE_VALID_ITEM_ID + "_3",
             "hasitems": media_type == "favorites",
+            "isaudio": True,
             "album_id": FAKE_VALID_ITEM_ID if media_type == "favorites" else None,
             "url": "file:///var/lib/squeezeboxserver/music/track_3.mp3",
         },

From 3063f0b565ee2a86af73227626caba02ef7b57f7 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Sat, 4 Jan 2025 00:30:41 -1000
Subject: [PATCH 03/47] Bump bleak-esphome to 2.0.0 (#134580)

---
 homeassistant/components/eq3btsmart/manifest.json | 2 +-
 homeassistant/components/esphome/bluetooth.py     | 4 +---
 homeassistant/components/esphome/domain_data.py   | 5 -----
 homeassistant/components/esphome/manager.py       | 4 +---
 homeassistant/components/esphome/manifest.json    | 2 +-
 requirements_all.txt                              | 2 +-
 requirements_test_all.txt                         | 2 +-
 tests/components/esphome/bluetooth/test_client.py | 2 --
 8 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/homeassistant/components/eq3btsmart/manifest.json b/homeassistant/components/eq3btsmart/manifest.json
index ed80ad9aabfe64..43f18d4fffc033 100644
--- a/homeassistant/components/eq3btsmart/manifest.json
+++ b/homeassistant/components/eq3btsmart/manifest.json
@@ -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"]
 }
diff --git a/homeassistant/components/esphome/bluetooth.py b/homeassistant/components/esphome/bluetooth.py
index 37ae28df0ca3a6..004bea1835ded5 100644
--- a/homeassistant/components/esphome/bluetooth.py
+++ b/homeassistant/components/esphome/bluetooth.py
@@ -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
@@ -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
diff --git a/homeassistant/components/esphome/domain_data.py b/homeassistant/components/esphome/domain_data.py
index aa46469c40e075..ed307b46fd6e91 100644
--- a/homeassistant/components/esphome/domain_data.py
+++ b/homeassistant/components/esphome/domain_data.py
@@ -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
 
@@ -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.
diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py
index 007b4e791e17ec..dfd318c0c7419e 100644
--- a/homeassistant/components/esphome/manager.py
+++ b/homeassistant/components/esphome/manager.py
@@ -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 (
diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json
index 775ffbff4c8ffc..b04fa4db428c77 100644
--- a/homeassistant/components/esphome/manifest.json
+++ b/homeassistant/components/esphome/manifest.json
@@ -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."]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index 36025003d9d98a..8cbee02e3313f1 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -585,7 +585,7 @@ bizkaibus==0.1.1
 
 # homeassistant.components.eq3btsmart
 # homeassistant.components.esphome
-bleak-esphome==1.1.0
+bleak-esphome==2.0.0
 
 # homeassistant.components.bluetooth
 bleak-retry-connector==3.6.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 03e594dcf53d6b..1c55c4c7b23b82 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -516,7 +516,7 @@ bimmer-connected[china]==0.17.2
 
 # homeassistant.components.eq3btsmart
 # homeassistant.components.esphome
-bleak-esphome==1.1.0
+bleak-esphome==2.0.0
 
 # homeassistant.components.bluetooth
 bleak-retry-connector==3.6.0
diff --git a/tests/components/esphome/bluetooth/test_client.py b/tests/components/esphome/bluetooth/test_client.py
index 98993be37d0582..77d315f096d363 100644
--- a/tests/components/esphome/bluetooth/test_client.py
+++ b/tests/components/esphome/bluetooth/test_client.py
@@ -4,7 +4,6 @@
 
 from aioesphomeapi import APIClient, APIVersion, BluetoothProxyFeature, DeviceInfo
 from bleak.exc import BleakError
-from bleak_esphome.backend.cache import ESPHomeBluetoothCache
 from bleak_esphome.backend.client import ESPHomeClient, ESPHomeClientData
 from bleak_esphome.backend.device import ESPHomeBluetoothDevice
 from bleak_esphome.backend.scanner import ESPHomeScanner
@@ -27,7 +26,6 @@ async def client_data_fixture(
     connector = HaBluetoothConnector(ESPHomeClientData, ESP_MAC_ADDRESS, lambda: True)
     return ESPHomeClientData(
         bluetooth_device=ESPHomeBluetoothDevice(ESP_NAME, ESP_MAC_ADDRESS),
-        cache=ESPHomeBluetoothCache(),
         client=mock_client,
         device_info=DeviceInfo(
             mac_address=ESP_MAC_ADDRESS,

From 8c2ec5e7c8305912893d48cb135bbaf7bf238a5d Mon Sep 17 00:00:00 2001
From: Raphael Hehl <7577984+RaHehl@users.noreply.github.com>
Date: Sat, 4 Jan 2025 00:27:06 +0100
Subject: [PATCH 04/47] Bump uiprotect to version 7.2.0 (#134587)

---
 .../components/unifiprotect/manifest.json      |  2 +-
 requirements_all.txt                           |  2 +-
 requirements_test_all.txt                      |  2 +-
 .../fixtures/sample_bootstrap.json             | 18 ++++++++++++++++++
 4 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json
index 1226f96c253631..d4877798208a18 100644
--- a/homeassistant/components/unifiprotect/manifest.json
+++ b/homeassistant/components/unifiprotect/manifest.json
@@ -40,7 +40,7 @@
   "integration_type": "hub",
   "iot_class": "local_push",
   "loggers": ["uiprotect", "unifi_discovery"],
-  "requirements": ["uiprotect==7.1.0", "unifi-discovery==1.2.0"],
+  "requirements": ["uiprotect==7.2.0", "unifi-discovery==1.2.0"],
   "ssdp": [
     {
       "manufacturer": "Ubiquiti Networks",
diff --git a/requirements_all.txt b/requirements_all.txt
index 8cbee02e3313f1..a055be570eaa3d 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2910,7 +2910,7 @@ typedmonarchmoney==0.3.1
 uasiren==0.0.1
 
 # homeassistant.components.unifiprotect
-uiprotect==7.1.0
+uiprotect==7.2.0
 
 # homeassistant.components.landisgyr_heat_meter
 ultraheat-api==0.5.7
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 1c55c4c7b23b82..7ee457a21c445d 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2332,7 +2332,7 @@ typedmonarchmoney==0.3.1
 uasiren==0.0.1
 
 # homeassistant.components.unifiprotect
-uiprotect==7.1.0
+uiprotect==7.2.0
 
 # homeassistant.components.landisgyr_heat_meter
 ultraheat-api==0.5.7
diff --git a/tests/components/unifiprotect/fixtures/sample_bootstrap.json b/tests/components/unifiprotect/fixtures/sample_bootstrap.json
index 240a9938b64961..4c8d86a787d28c 100644
--- a/tests/components/unifiprotect/fixtures/sample_bootstrap.json
+++ b/tests/components/unifiprotect/fixtures/sample_bootstrap.json
@@ -564,6 +564,24 @@
   "legacyUFVs": [],
   "lastUpdateId": "ebf25bac-d5a1-4f1d-a0ee-74c15981eb70",
   "displays": [],
+  "ringtones": [
+    {
+      "id": "66a14fa502d44203e40003eb",
+      "name": "Default",
+      "size": 208,
+      "isDefault": true,
+      "nvrMac": "A1E00C826924",
+      "modelKey": "ringtone"
+    },
+    {
+      "id": "66a14fa502da4203e40003ec",
+      "name": "Traditional",
+      "size": 180,
+      "isDefault": false,
+      "nvrMac": "A1E00C826924",
+      "modelKey": "ringtone"
+    }
+  ],
   "bridges": [
     {
       "mac": "A28D0DB15AE1",

From c46a70fdcff200810512ee59adbe43150882bd2e Mon Sep 17 00:00:00 2001
From: "Teemu R." <tpr@iki.fi>
Date: Sat, 4 Jan 2025 11:12:46 +0100
Subject: [PATCH 05/47] Mention case-sensitivity in tplink credentials prompt
 (#134606)

---
 homeassistant/components/tplink/strings.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/homeassistant/components/tplink/strings.json b/homeassistant/components/tplink/strings.json
index 664d52c16af7ed..c0aef09e8c3243 100644
--- a/homeassistant/components/tplink/strings.json
+++ b/homeassistant/components/tplink/strings.json
@@ -21,7 +21,7 @@
       },
       "user_auth_confirm": {
         "title": "Authenticate",
-        "description": "The device requires authentication, please input your TP-Link credentials below.",
+        "description": "The device requires authentication, please input your TP-Link credentials below. Note, that both e-mail and password are case-sensitive.",
         "data": {
           "username": "[%key:common::config_flow::data::username%]",
           "password": "[%key:common::config_flow::data::password%]"

From 0bd7b793fe4d8b9508ede8579602158cd9490d4e Mon Sep 17 00:00:00 2001
From: Brynley McDonald <brynley+github@zephire.nz>
Date: Sun, 5 Jan 2025 04:21:21 +1300
Subject: [PATCH 06/47] Fix Flick Electric authentication (#134611)

---
 .../components/flick_electric/__init__.py     | 26 ++++++++++---------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/homeassistant/components/flick_electric/__init__.py b/homeassistant/components/flick_electric/__init__.py
index 190947e4c6f640..3ffddee1c7dfd9 100644
--- a/homeassistant/components/flick_electric/__init__.py
+++ b/homeassistant/components/flick_electric/__init__.py
@@ -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
@@ -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
@@ -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)

From 017679abe149cffcfca175acacf01207c27e992d Mon Sep 17 00:00:00 2001
From: epenet <6771947+epenet@users.noreply.github.com>
Date: Sat, 4 Jan 2025 16:19:38 +0100
Subject: [PATCH 07/47] Fix hive color tunable light (#134628)

---
 homeassistant/components/hive/light.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/homeassistant/components/hive/light.py b/homeassistant/components/hive/light.py
index b510569eb47f36..8d09c902f36671 100644
--- a/homeassistant/components/hive/light.py
+++ b/homeassistant/components/hive/light.py
@@ -114,6 +114,7 @@ async def async_update(self) -> None:
                     self._attr_hs_color = color_util.color_RGB_to_hs(*rgb)
                     self._attr_color_mode = ColorMode.HS
                 else:
+                    color_temp = self.device["status"].get("color_temp")
                     self._attr_color_temp_kelvin = (
                         None
                         if color_temp is None

From 9ead6fe36284be21a599d5b15106e945292d31f7 Mon Sep 17 00:00:00 2001
From: Cyrill Raccaud <miaucl@users.noreply.github.com>
Date: Sat, 4 Jan 2025 12:23:22 +0100
Subject: [PATCH 08/47] Set logging in manifest for Cookidoo (#134645)

---
 homeassistant/components/cookidoo/manifest.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/homeassistant/components/cookidoo/manifest.json b/homeassistant/components/cookidoo/manifest.json
index 59d58200fdfdf7..7b2e7c84bbaac6 100644
--- a/homeassistant/components/cookidoo/manifest.json
+++ b/homeassistant/components/cookidoo/manifest.json
@@ -6,6 +6,7 @@
   "documentation": "https://www.home-assistant.io/integrations/cookidoo",
   "integration_type": "service",
   "iot_class": "cloud_polling",
+  "loggers": ["cookidoo", "cookidoo-api"],
   "quality_scale": "silver",
   "requirements": ["cookidoo-api==0.10.0"]
 }

From a4d0794fe4f83b8af12a34db40ae638eb3bbffdf Mon Sep 17 00:00:00 2001
From: Joost Lekkerkerker <joostlek@outlook.com>
Date: Sat, 4 Jan 2025 12:56:58 +0100
Subject: [PATCH 09/47] Remove call to remove slide (#134647)

---
 homeassistant/components/slide_local/config_flow.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/homeassistant/components/slide_local/config_flow.py b/homeassistant/components/slide_local/config_flow.py
index 23c509a02dcfdd..78e2b411153527 100644
--- a/homeassistant/components/slide_local/config_flow.py
+++ b/homeassistant/components/slide_local/config_flow.py
@@ -73,7 +73,6 @@ async def async_test_connection(
             return {}
 
         # API version 2 is not working, try API version 1 instead
-        await slide.slide_del(user_input[CONF_HOST])
         await slide.slide_add(
             user_input[CONF_HOST],
             user_input.get(CONF_PASSWORD, ""),

From a9a14381d379737672a76b7c0d259cf5516ef71f Mon Sep 17 00:00:00 2001
From: Franck Nijhof <git@frenck.dev>
Date: Sat, 4 Jan 2025 14:05:24 +0100
Subject: [PATCH 10/47] Update twentemilieu to 2.2.1 (#134651)

---
 homeassistant/components/twentemilieu/manifest.json | 2 +-
 requirements_all.txt                                | 2 +-
 requirements_test_all.txt                           | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/twentemilieu/manifest.json b/homeassistant/components/twentemilieu/manifest.json
index c04c5492a403f5..b1cb98dbca6d84 100644
--- a/homeassistant/components/twentemilieu/manifest.json
+++ b/homeassistant/components/twentemilieu/manifest.json
@@ -8,5 +8,5 @@
   "iot_class": "cloud_polling",
   "loggers": ["twentemilieu"],
   "quality_scale": "silver",
-  "requirements": ["twentemilieu==2.2.0"]
+  "requirements": ["twentemilieu==2.2.1"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index a055be570eaa3d..6e90c5ac6bfa6a 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2895,7 +2895,7 @@ ttn_client==1.2.0
 tuya-device-sharing-sdk==0.2.1
 
 # homeassistant.components.twentemilieu
-twentemilieu==2.2.0
+twentemilieu==2.2.1
 
 # homeassistant.components.twilio
 twilio==6.32.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 7ee457a21c445d..7622914d070436 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2317,7 +2317,7 @@ ttn_client==1.2.0
 tuya-device-sharing-sdk==0.2.1
 
 # homeassistant.components.twentemilieu
-twentemilieu==2.2.0
+twentemilieu==2.2.1
 
 # homeassistant.components.twilio
 twilio==6.32.0

From a14f6faaaf3ddfbd9b6d811c71fa9cf64f6e5e3e Mon Sep 17 00:00:00 2001
From: starkillerOG <starkiller.og@gmail.com>
Date: Mon, 6 Jan 2025 18:54:32 +0100
Subject: [PATCH 11/47] Fix Reolink playback of recodings (#134652)

---
 homeassistant/components/reolink/views.py | 8 ++++----
 tests/components/reolink/test_views.py    | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/homeassistant/components/reolink/views.py b/homeassistant/components/reolink/views.py
index 3b32ebaf74e5be..1a4585bc997680 100644
--- a/homeassistant/components/reolink/views.py
+++ b/homeassistant/components/reolink/views.py
@@ -2,9 +2,9 @@
 
 from __future__ import annotations
 
+from base64 import urlsafe_b64decode, urlsafe_b64encode
 from http import HTTPStatus
 import logging
-from urllib import parse
 
 from aiohttp import ClientError, ClientTimeout, web
 from reolink_aio.enums import VodRequestType
@@ -31,7 +31,7 @@ def async_generate_playback_proxy_url(
     return url_format.format(
         config_entry_id=config_entry_id,
         channel=channel,
-        filename=parse.quote(filename, safe=""),
+        filename=urlsafe_b64encode(filename.encode("utf-8")).decode("utf-8"),
         stream_res=stream_res,
         vod_type=vod_type,
     )
@@ -66,7 +66,7 @@ async def get(
         """Get playback proxy video response."""
         retry = retry - 1
 
-        filename = parse.unquote(filename)
+        filename_decoded = urlsafe_b64decode(filename.encode("utf-8")).decode("utf-8")
         ch = int(channel)
         try:
             host = get_host(self.hass, config_entry_id)
@@ -77,7 +77,7 @@ async def get(
 
         try:
             mime_type, reolink_url = await host.api.get_vod_source(
-                ch, filename, stream_res, VodRequestType(vod_type)
+                ch, filename_decoded, stream_res, VodRequestType(vod_type)
             )
         except ReolinkError as err:
             _LOGGER.warning("Reolink playback proxy error: %s", str(err))
diff --git a/tests/components/reolink/test_views.py b/tests/components/reolink/test_views.py
index 1eb184950bcae7..c994cc59c5d696 100644
--- a/tests/components/reolink/test_views.py
+++ b/tests/components/reolink/test_views.py
@@ -22,7 +22,7 @@
 TEST_DAY2 = 15
 TEST_HOUR = 13
 TEST_MINUTE = 12
-TEST_FILE_NAME_MP4 = f"{TEST_YEAR}{TEST_MONTH}{TEST_DAY}{TEST_HOUR}{TEST_MINUTE}00.mp4"
+TEST_FILE_NAME_MP4 = f"Mp4Record/{TEST_YEAR}-{TEST_MONTH}-{TEST_DAY}/RecS04_{TEST_YEAR}{TEST_MONTH}{TEST_DAY}{TEST_HOUR}{TEST_MINUTE}00_123456_AB123C.mp4"
 TEST_STREAM = "sub"
 TEST_CHANNEL = "0"
 TEST_VOD_TYPE = VodRequestType.PLAYBACK.value

From ca8416fe503a1f166a970514efaa88d083cb7ad0 Mon Sep 17 00:00:00 2001
From: Franck Nijhof <git@frenck.dev>
Date: Sat, 4 Jan 2025 14:34:45 +0100
Subject: [PATCH 12/47] Update peblar to 0.3.3 (#134658)

---
 homeassistant/components/peblar/manifest.json | 2 +-
 homeassistant/components/peblar/update.py     | 1 +
 requirements_all.txt                          | 2 +-
 requirements_test_all.txt                     | 2 +-
 4 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/peblar/manifest.json b/homeassistant/components/peblar/manifest.json
index 2c3e73ba76eb11..859682d3f1dabc 100644
--- a/homeassistant/components/peblar/manifest.json
+++ b/homeassistant/components/peblar/manifest.json
@@ -7,6 +7,6 @@
   "integration_type": "device",
   "iot_class": "local_polling",
   "quality_scale": "platinum",
-  "requirements": ["peblar==0.3.2"],
+  "requirements": ["peblar==0.3.3"],
   "zeroconf": [{ "type": "_http._tcp.local.", "name": "pblr-*" }]
 }
diff --git a/homeassistant/components/peblar/update.py b/homeassistant/components/peblar/update.py
index 77879030f6c90f..9e132da63bcc37 100644
--- a/homeassistant/components/peblar/update.py
+++ b/homeassistant/components/peblar/update.py
@@ -37,6 +37,7 @@ class PeblarUpdateEntityDescription(UpdateEntityDescription):
         key="firmware",
         device_class=UpdateDeviceClass.FIRMWARE,
         installed_fn=lambda x: x.current.firmware,
+        has_fn=lambda x: x.current.firmware is not None,
         available_fn=lambda x: x.available.firmware,
     ),
     PeblarUpdateEntityDescription(
diff --git a/requirements_all.txt b/requirements_all.txt
index 6e90c5ac6bfa6a..d54ea27f84deeb 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1603,7 +1603,7 @@ panasonic-viera==0.4.2
 pdunehd==1.3.2
 
 # homeassistant.components.peblar
-peblar==0.3.2
+peblar==0.3.3
 
 # homeassistant.components.peco
 peco==0.0.30
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 7622914d070436..434c53f7ba5779 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1330,7 +1330,7 @@ panasonic-viera==0.4.2
 pdunehd==1.3.2
 
 # homeassistant.components.peblar
-peblar==0.3.2
+peblar==0.3.3
 
 # homeassistant.components.peco
 peco==0.0.30

From 0daac090082a5510ff97e6b1a9c9d7d9482c5f32 Mon Sep 17 00:00:00 2001
From: Cyrill Raccaud <miaucl@users.noreply.github.com>
Date: Sat, 4 Jan 2025 15:02:00 +0100
Subject: [PATCH 13/47] Bump cookidoo-api library to 0.11.1 of for Cookidoo
 (#134661)

---
 homeassistant/components/cookidoo/manifest.json | 4 ++--
 requirements_all.txt                            | 2 +-
 requirements_test_all.txt                       | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/cookidoo/manifest.json b/homeassistant/components/cookidoo/manifest.json
index 7b2e7c84bbaac6..0854f0a1b95a4c 100644
--- a/homeassistant/components/cookidoo/manifest.json
+++ b/homeassistant/components/cookidoo/manifest.json
@@ -6,7 +6,7 @@
   "documentation": "https://www.home-assistant.io/integrations/cookidoo",
   "integration_type": "service",
   "iot_class": "cloud_polling",
-  "loggers": ["cookidoo", "cookidoo-api"],
+  "loggers": ["cookidoo_api"],
   "quality_scale": "silver",
-  "requirements": ["cookidoo-api==0.10.0"]
+  "requirements": ["cookidoo-api==0.11.1"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index d54ea27f84deeb..7ebf0efa9db57f 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -704,7 +704,7 @@ connect-box==0.3.1
 construct==2.10.68
 
 # homeassistant.components.cookidoo
-cookidoo-api==0.10.0
+cookidoo-api==0.11.1
 
 # homeassistant.components.backup
 # homeassistant.components.utility_meter
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 434c53f7ba5779..bb02bbb11b76b7 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -600,7 +600,7 @@ colorthief==0.2.1
 construct==2.10.68
 
 # homeassistant.components.cookidoo
-cookidoo-api==0.10.0
+cookidoo-api==0.11.1
 
 # homeassistant.components.backup
 # homeassistant.components.utility_meter

From c022d91baadb28ff2ec4a162152e296f5a615be2 Mon Sep 17 00:00:00 2001
From: Franck Nijhof <git@frenck.dev>
Date: Sat, 4 Jan 2025 16:19:16 +0100
Subject: [PATCH 14/47] Update demetriek to 1.1.1 (#134663)

---
 homeassistant/components/lametric/manifest.json           | 2 +-
 homeassistant/components/lametric/number.py               | 2 +-
 requirements_all.txt                                      | 2 +-
 requirements_test_all.txt                                 | 2 +-
 tests/components/lametric/snapshots/test_diagnostics.ambr | 1 +
 5 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/lametric/manifest.json b/homeassistant/components/lametric/manifest.json
index 5a066d015f22ac..f66ffb0c6aeeaf 100644
--- a/homeassistant/components/lametric/manifest.json
+++ b/homeassistant/components/lametric/manifest.json
@@ -13,7 +13,7 @@
   "integration_type": "device",
   "iot_class": "local_polling",
   "loggers": ["demetriek"],
-  "requirements": ["demetriek==1.1.0"],
+  "requirements": ["demetriek==1.1.1"],
   "ssdp": [
     {
       "deviceType": "urn:schemas-upnp-org:device:LaMetric:1"
diff --git a/homeassistant/components/lametric/number.py b/homeassistant/components/lametric/number.py
index 1025e04a4a8c16..a1d922c2d80d8b 100644
--- a/homeassistant/components/lametric/number.py
+++ b/homeassistant/components/lametric/number.py
@@ -50,7 +50,7 @@ class LaMetricNumberEntityDescription(NumberEntityDescription):
         native_step=1,
         native_min_value=0,
         native_max_value=100,
-        has_fn=lambda device: bool(device.audio),
+        has_fn=lambda device: bool(device.audio and device.audio.available),
         value_fn=lambda device: device.audio.volume if device.audio else 0,
         set_value_fn=lambda api, volume: api.audio(volume=int(volume)),
     ),
diff --git a/requirements_all.txt b/requirements_all.txt
index 7ebf0efa9db57f..bf57d0c2d18151 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -749,7 +749,7 @@ defusedxml==0.7.1
 deluge-client==1.10.2
 
 # homeassistant.components.lametric
-demetriek==1.1.0
+demetriek==1.1.1
 
 # homeassistant.components.denonavr
 denonavr==1.0.1
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index bb02bbb11b76b7..08c3419ccfc41c 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -639,7 +639,7 @@ defusedxml==0.7.1
 deluge-client==1.10.2
 
 # homeassistant.components.lametric
-demetriek==1.1.0
+demetriek==1.1.1
 
 # homeassistant.components.denonavr
 denonavr==1.0.1
diff --git a/tests/components/lametric/snapshots/test_diagnostics.ambr b/tests/components/lametric/snapshots/test_diagnostics.ambr
index 7517cfe035e8d7..8b8f98b5806b3d 100644
--- a/tests/components/lametric/snapshots/test_diagnostics.ambr
+++ b/tests/components/lametric/snapshots/test_diagnostics.ambr
@@ -2,6 +2,7 @@
 # name: test_diagnostics
   dict({
     'audio': dict({
+      'available': True,
       'volume': 100,
       'volume_limit': dict({
         'range_max': 100,

From 27b8b8458bbb6c3d9cbfe172677358a92a5562ae Mon Sep 17 00:00:00 2001
From: Cyrill Raccaud <miaucl@users.noreply.github.com>
Date: Sat, 4 Jan 2025 16:33:42 +0100
Subject: [PATCH 15/47] Cookidoo exotic domains (#134676)

---
 homeassistant/components/cookidoo/__init__.py    | 12 +++++++-----
 homeassistant/components/cookidoo/config_flow.py | 16 ++++++++--------
 homeassistant/components/cookidoo/manifest.json  |  2 +-
 requirements_all.txt                             |  2 +-
 requirements_test_all.txt                        |  2 +-
 5 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/homeassistant/components/cookidoo/__init__.py b/homeassistant/components/cookidoo/__init__.py
index bb78f2a569d6b4..5d3c211e78d510 100644
--- a/homeassistant/components/cookidoo/__init__.py
+++ b/homeassistant/components/cookidoo/__init__.py
@@ -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,
@@ -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],
         ),
     )
 
diff --git a/homeassistant/components/cookidoo/config_flow.py b/homeassistant/components/cookidoo/config_flow.py
index 120ab162a6ccec..80487ed757f8c0 100644
--- a/homeassistant/components/cookidoo/config_flow.py
+++ b/homeassistant/components/cookidoo/config_flow.py
@@ -10,7 +10,6 @@
     Cookidoo,
     CookidooAuthException,
     CookidooConfig,
-    CookidooLocalizationConfig,
     CookidooRequestException,
     get_country_options,
     get_localization_options,
@@ -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:
diff --git a/homeassistant/components/cookidoo/manifest.json b/homeassistant/components/cookidoo/manifest.json
index 0854f0a1b95a4c..b1a3e9c0267a22 100644
--- a/homeassistant/components/cookidoo/manifest.json
+++ b/homeassistant/components/cookidoo/manifest.json
@@ -8,5 +8,5 @@
   "iot_class": "cloud_polling",
   "loggers": ["cookidoo_api"],
   "quality_scale": "silver",
-  "requirements": ["cookidoo-api==0.11.1"]
+  "requirements": ["cookidoo-api==0.11.2"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index bf57d0c2d18151..e3dec070f0ee72 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -704,7 +704,7 @@ connect-box==0.3.1
 construct==2.10.68
 
 # homeassistant.components.cookidoo
-cookidoo-api==0.11.1
+cookidoo-api==0.11.2
 
 # homeassistant.components.backup
 # homeassistant.components.utility_meter
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 08c3419ccfc41c..34f8ec79c0efbb 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -600,7 +600,7 @@ colorthief==0.2.1
 construct==2.10.68
 
 # homeassistant.components.cookidoo
-cookidoo-api==0.11.1
+cookidoo-api==0.11.2
 
 # homeassistant.components.backup
 # homeassistant.components.utility_meter

From 0f0209d4bb0cb197c96674868f6274cd23ac53d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=2E=20Diego=20Rodr=C3=ADguez=20Royo?=
 <jdrr1998@hotmail.com>
Date: Mon, 6 Jan 2025 15:21:02 +0100
Subject: [PATCH 16/47] Iterate over a copy of the list of programs at Home
 Connect select setup entry (#134684)

---
 .../components/home_connect/select.py         |  2 +-
 tests/components/home_connect/test_select.py  | 41 ++++++++++++++++++-
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/home_connect/select.py b/homeassistant/components/home_connect/select.py
index c97b3db28e0c9c..a4a5861afbe098 100644
--- a/homeassistant/components/home_connect/select.py
+++ b/homeassistant/components/home_connect/select.py
@@ -220,7 +220,7 @@ def get_entities() -> list[HomeConnectProgramSelectEntity]:
                 with contextlib.suppress(HomeConnectError):
                     programs = device.appliance.get_programs_available()
                     if programs:
-                        for program in programs:
+                        for program in programs.copy():
                             if program not in PROGRAMS_TRANSLATION_KEYS_MAP:
                                 programs.remove(program)
                                 if program not in programs_not_found:
diff --git a/tests/components/home_connect/test_select.py b/tests/components/home_connect/test_select.py
index 7d5843e9525245..af975979196273 100644
--- a/tests/components/home_connect/test_select.py
+++ b/tests/components/home_connect/test_select.py
@@ -10,11 +10,16 @@
     BSH_ACTIVE_PROGRAM,
     BSH_SELECTED_PROGRAM,
 )
-from homeassistant.components.select import ATTR_OPTION, DOMAIN as SELECT_DOMAIN
+from homeassistant.components.select import (
+    ATTR_OPTION,
+    ATTR_OPTIONS,
+    DOMAIN as SELECT_DOMAIN,
+)
 from homeassistant.config_entries import ConfigEntryState
 from homeassistant.const import ATTR_ENTITY_ID, SERVICE_SELECT_OPTION, Platform
 from homeassistant.core import HomeAssistant
 from homeassistant.exceptions import HomeAssistantError
+from homeassistant.helpers import entity_registry as er
 
 from .conftest import get_all_appliances
 
@@ -52,6 +57,40 @@ async def test_select(
     assert config_entry.state is ConfigEntryState.LOADED
 
 
+async def test_filter_unknown_programs(
+    bypass_throttle: Generator[None],
+    hass: HomeAssistant,
+    config_entry: MockConfigEntry,
+    integration_setup: Callable[[], Awaitable[bool]],
+    setup_credentials: None,
+    get_appliances: Mock,
+    appliance: Mock,
+    entity_registry: er.EntityRegistry,
+) -> None:
+    """Test select that programs that are not part of the official Home Connect API specification are filtered out.
+
+    We use two programs to ensure that programs are iterated over a copy of the list,
+    and it does not raise problems when removing an element from the original list.
+    """
+    appliance.status.update(SETTINGS_STATUS)
+    appliance.get_programs_available.return_value = [
+        PROGRAM,
+        "NonOfficialProgram",
+        "AntotherNonOfficialProgram",
+    ]
+    get_appliances.return_value = [appliance]
+
+    assert config_entry.state is ConfigEntryState.NOT_LOADED
+    assert await integration_setup()
+    assert config_entry.state is ConfigEntryState.LOADED
+
+    entity = entity_registry.async_get("select.washer_selected_program")
+    assert entity
+    assert entity.capabilities.get(ATTR_OPTIONS) == [
+        "dishcare_dishwasher_program_eco_50"
+    ]
+
+
 @pytest.mark.parametrize(
     ("entity_id", "status", "program_to_set"),
     [

From 1c4273ce91104fd6b92315ae4df4423cc6d4b0a0 Mon Sep 17 00:00:00 2001
From: dontinelli <73341522+dontinelli@users.noreply.github.com>
Date: Sat, 4 Jan 2025 21:28:47 +0100
Subject: [PATCH 17/47] Change from host to ip in zeroconf discovery for
 slide_local (#134709)

---
 homeassistant/components/slide_local/config_flow.py | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/homeassistant/components/slide_local/config_flow.py b/homeassistant/components/slide_local/config_flow.py
index 78e2b411153527..a4255f0769f7c8 100644
--- a/homeassistant/components/slide_local/config_flow.py
+++ b/homeassistant/components/slide_local/config_flow.py
@@ -184,14 +184,15 @@ async def async_step_zeroconf(
 
         await self.async_set_unique_id(self._mac)
 
-        self._abort_if_unique_id_configured(
-            {CONF_HOST: discovery_info.host}, reload_on_update=True
-        )
+        ip = str(discovery_info.ip_address)
+        _LOGGER.debug("Slide device discovered, ip %s", ip)
+
+        self._abort_if_unique_id_configured({CONF_HOST: ip}, reload_on_update=True)
 
         errors = {}
         if errors := await self.async_test_connection(
             {
-                CONF_HOST: self._host,
+                CONF_HOST: ip,
             }
         ):
             return self.async_abort(
@@ -201,7 +202,7 @@ async def async_step_zeroconf(
                 },
             )
 
-        self._host = discovery_info.host
+        self._host = ip
 
         return await self.async_step_zeroconf_confirm()
 

From 103960e0a7601071a0652b2afc9fd7b4b4e028fd Mon Sep 17 00:00:00 2001
From: TheJulianJES <TheJulianJES@users.noreply.github.com>
Date: Sun, 5 Jan 2025 16:49:58 +0100
Subject: [PATCH 18/47] Bump ZHA to 0.0.45 (#134726)

---
 homeassistant/components/zha/manifest.json | 2 +-
 requirements_all.txt                       | 2 +-
 requirements_test_all.txt                  | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json
index 45d8f6bb25f9e6..975a1804853c64 100644
--- a/homeassistant/components/zha/manifest.json
+++ b/homeassistant/components/zha/manifest.json
@@ -21,7 +21,7 @@
     "zha",
     "universal_silabs_flasher"
   ],
-  "requirements": ["universal-silabs-flasher==0.0.25", "zha==0.0.44"],
+  "requirements": ["universal-silabs-flasher==0.0.25", "zha==0.0.45"],
   "usb": [
     {
       "vid": "10C4",
diff --git a/requirements_all.txt b/requirements_all.txt
index e3dec070f0ee72..a4ba72784f3403 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -3100,7 +3100,7 @@ zeroconf==0.136.2
 zeversolar==0.3.2
 
 # homeassistant.components.zha
-zha==0.0.44
+zha==0.0.45
 
 # homeassistant.components.zhong_hong
 zhong-hong-hvac==1.0.13
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 34f8ec79c0efbb..60f192f2253148 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2489,7 +2489,7 @@ zeroconf==0.136.2
 zeversolar==0.3.2
 
 # homeassistant.components.zha
-zha==0.0.44
+zha==0.0.45
 
 # homeassistant.components.zwave_js
 zwave-js-server-python==0.60.0

From b461bc2fb583ed16be241d7a59e7d4f5a5e55584 Mon Sep 17 00:00:00 2001
From: Sid <27780930+autinerd@users.noreply.github.com>
Date: Sun, 5 Jan 2025 10:04:59 +0100
Subject: [PATCH 19/47] Bump openwebifpy to 4.3.1 (#134746)

---
 homeassistant/components/enigma2/manifest.json | 2 +-
 requirements_all.txt                           | 2 +-
 requirements_test_all.txt                      | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/enigma2/manifest.json b/homeassistant/components/enigma2/manifest.json
index 7d6887ad14ce93..2bb299722b7c5c 100644
--- a/homeassistant/components/enigma2/manifest.json
+++ b/homeassistant/components/enigma2/manifest.json
@@ -7,5 +7,5 @@
   "integration_type": "device",
   "iot_class": "local_polling",
   "loggers": ["openwebif"],
-  "requirements": ["openwebifpy==4.3.0"]
+  "requirements": ["openwebifpy==4.3.1"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index a4ba72784f3403..d9b221b6b1e5f4 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1561,7 +1561,7 @@ openhomedevice==2.2.0
 opensensemap-api==0.2.0
 
 # homeassistant.components.enigma2
-openwebifpy==4.3.0
+openwebifpy==4.3.1
 
 # homeassistant.components.luci
 openwrt-luci-rpc==1.1.17
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 60f192f2253148..216fcab4854e3f 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1303,7 +1303,7 @@ openerz-api==0.3.0
 openhomedevice==2.2.0
 
 # homeassistant.components.enigma2
-openwebifpy==4.3.0
+openwebifpy==4.3.1
 
 # homeassistant.components.opower
 opower==0.8.7

From 538a2ea057b30e222711875baee20444c0632c23 Mon Sep 17 00:00:00 2001
From: Norbert Rittel <norbert@rittel.de>
Date: Sun, 5 Jan 2025 10:43:32 +0100
Subject: [PATCH 20/47] =?UTF-8?q?Fix=20swapped=20letter=20order=20in=20"?=
 =?UTF-8?q?=C2=B0F"=20and=20"=C2=B0C"=20temperature=20units=20(#134750)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes the wrong order "F°" and "C°" for the temperature units.
---
 homeassistant/components/iron_os/strings.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/iron_os/strings.json b/homeassistant/components/iron_os/strings.json
index 04c5528055057b..967b966e44e401 100644
--- a/homeassistant/components/iron_os/strings.json
+++ b/homeassistant/components/iron_os/strings.json
@@ -128,8 +128,8 @@
       "temp_unit": {
         "name": "Temperature display unit",
         "state": {
-          "celsius": "Celsius (C°)",
-          "fahrenheit": "Fahrenheit (F°)"
+          "celsius": "Celsius (°C)",
+          "fahrenheit": "Fahrenheit (°F)"
         }
       },
       "desc_scroll_speed": {

From bd8ea646a9485926d6765d1ed054aa38059a0683 Mon Sep 17 00:00:00 2001
From: Duco Sebel <74970928+DCSBL@users.noreply.github.com>
Date: Sun, 5 Jan 2025 12:37:06 +0100
Subject: [PATCH 21/47] Bumb python-homewizard-energy to 7.0.1 (#134753)

---
 homeassistant/components/homewizard/manifest.json | 2 +-
 requirements_all.txt                              | 2 +-
 requirements_test_all.txt                         | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/homewizard/manifest.json b/homeassistant/components/homewizard/manifest.json
index 13bfc512551517..83937809b60489 100644
--- a/homeassistant/components/homewizard/manifest.json
+++ b/homeassistant/components/homewizard/manifest.json
@@ -12,6 +12,6 @@
   "iot_class": "local_polling",
   "loggers": ["homewizard_energy"],
   "quality_scale": "platinum",
-  "requirements": ["python-homewizard-energy==v7.0.0"],
+  "requirements": ["python-homewizard-energy==v7.0.1"],
   "zeroconf": ["_hwenergy._tcp.local."]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index d9b221b6b1e5f4..b73a455f7f3515 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2363,7 +2363,7 @@ python-gitlab==1.6.0
 python-homeassistant-analytics==0.8.1
 
 # homeassistant.components.homewizard
-python-homewizard-energy==v7.0.0
+python-homewizard-energy==v7.0.1
 
 # homeassistant.components.hp_ilo
 python-hpilo==4.4.3
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 216fcab4854e3f..2222fe90f7428e 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1905,7 +1905,7 @@ python-fullykiosk==0.0.14
 python-homeassistant-analytics==0.8.1
 
 # homeassistant.components.homewizard
-python-homewizard-energy==v7.0.0
+python-homewizard-energy==v7.0.1
 
 # homeassistant.components.izone
 python-izone==1.2.9

From a4ea25631a33875bbb8d5bd51f0bd5226d3a1b90 Mon Sep 17 00:00:00 2001
From: Michael <35783820+mib1185@users.noreply.github.com>
Date: Sun, 5 Jan 2025 14:16:33 +0100
Subject: [PATCH 22/47] Register base device entry during coordinator setup in
 AVM Fritz!Tools integration (#134764)

* register base device entry during coordinator setup

* make mypy happy
---
 homeassistant/components/fritz/coordinator.py | 12 ++++++++++++
 homeassistant/components/fritz/entity.py      | 11 +----------
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/homeassistant/components/fritz/coordinator.py b/homeassistant/components/fritz/coordinator.py
index 90bd6068ecb13e..272295cd512e30 100644
--- a/homeassistant/components/fritz/coordinator.py
+++ b/homeassistant/components/fritz/coordinator.py
@@ -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."""
 
diff --git a/homeassistant/components/fritz/entity.py b/homeassistant/components/fritz/entity.py
index 45665c786d46d6..33eb60d72cf30f 100644
--- a/homeassistant/components/fritz/entity.py
+++ b/homeassistant/components/fritz/entity.py
@@ -68,23 +68,14 @@ def __init__(self, avm_wrapper: AvmWrapper, device_name: str) -> None:
         """Init device info class."""
         self._avm_wrapper = avm_wrapper
         self._device_name = device_name
-
-    @property
-    def mac_address(self) -> str:
-        """Return the mac address of the main device."""
-        return self._avm_wrapper.mac
+        self.mac_address = self._avm_wrapper.mac
 
     @property
     def device_info(self) -> DeviceInfo:
         """Return the device information."""
         return DeviceInfo(
-            configuration_url=f"http://{self._avm_wrapper.host}",
             connections={(dr.CONNECTION_NETWORK_MAC, self.mac_address)},
             identifiers={(DOMAIN, self._avm_wrapper.unique_id)},
-            manufacturer="AVM",
-            model=self._avm_wrapper.model,
-            name=self._device_name,
-            sw_version=self._avm_wrapper.current_firmware,
         )
 
 

From b32a791ea4c86a09886d8adf6da4ce9fb6533813 Mon Sep 17 00:00:00 2001
From: jb101010-2 <168106462+jb101010-2@users.noreply.github.com>
Date: Mon, 6 Jan 2025 15:27:23 +0100
Subject: [PATCH 23/47] Bump pysuezV2 to 2.0.1 (#134769)

---
 homeassistant/components/suez_water/manifest.json | 2 +-
 requirements_all.txt                              | 2 +-
 requirements_test_all.txt                         | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/suez_water/manifest.json b/homeassistant/components/suez_water/manifest.json
index f39411e8afa49a..176b059f3d5228 100644
--- a/homeassistant/components/suez_water/manifest.json
+++ b/homeassistant/components/suez_water/manifest.json
@@ -7,5 +7,5 @@
   "iot_class": "cloud_polling",
   "loggers": ["pysuez", "regex"],
   "quality_scale": "bronze",
-  "requirements": ["pysuezV2==1.3.5"]
+  "requirements": ["pysuezV2==2.0.1"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index b73a455f7f3515..4e91bca1cdef59 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2309,7 +2309,7 @@ pysqueezebox==0.10.0
 pystiebeleltron==0.0.1.dev2
 
 # homeassistant.components.suez_water
-pysuezV2==1.3.5
+pysuezV2==2.0.1
 
 # homeassistant.components.switchbee
 pyswitchbee==1.8.3
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 2222fe90f7428e..4b601e01647b00 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1875,7 +1875,7 @@ pyspeex-noise==1.0.2
 pysqueezebox==0.10.0
 
 # homeassistant.components.suez_water
-pysuezV2==1.3.5
+pysuezV2==2.0.1
 
 # homeassistant.components.switchbee
 pyswitchbee==1.8.3

From 09ffa38ddf2aff16ce98eef01a5538b95c04c234 Mon Sep 17 00:00:00 2001
From: Norbert Rittel <norbert@rittel.de>
Date: Mon, 6 Jan 2025 14:53:28 +0100
Subject: [PATCH 24/47] Fix missing sentence-casing etc. in several strings
 (#134775)

---
 .../components/waze_travel_time/strings.json  | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/homeassistant/components/waze_travel_time/strings.json b/homeassistant/components/waze_travel_time/strings.json
index cca1789bf7e54a..8f8de694b2dcbe 100644
--- a/homeassistant/components/waze_travel_time/strings.json
+++ b/homeassistant/components/waze_travel_time/strings.json
@@ -3,7 +3,7 @@
   "config": {
     "step": {
       "user": {
-        "description": "For Origin and Destination, enter the address or the GPS coordinates of the location (GPS coordinates has to be separated by a comma). You can also enter an entity id which provides this information in its state, an entity id with latitude and longitude attributes, or zone friendly name.",
+        "description": "For Origin and Destination, enter the address or the GPS coordinates of the location (GPS coordinates has to be separated by a comma). You can also enter an entity ID which provides this information in its state, an entity ID with latitude and longitude attributes, or zone friendly name.",
         "data": {
           "name": "[%key:common::config_flow::data::name%]",
           "origin": "Origin",
@@ -26,13 +26,13 @@
         "description": "Some options will allow you to force the integration to use a particular route or avoid a particular route in its time travel calculation.",
         "data": {
           "units": "Units",
-          "vehicle_type": "Vehicle Type",
+          "vehicle_type": "Vehicle type",
           "incl_filter": "Exact streetname which must be part of the selected route",
           "excl_filter": "Exact streetname which must NOT be part of the selected route",
-          "realtime": "Realtime Travel Time?",
-          "avoid_toll_roads": "Avoid Toll Roads?",
-          "avoid_ferries": "Avoid Ferries?",
-          "avoid_subscription_roads": "Avoid Roads Needing a Vignette / Subscription?"
+          "realtime": "Realtime travel time?",
+          "avoid_toll_roads": "Avoid toll roads?",
+          "avoid_ferries": "Avoid ferries?",
+          "avoid_subscription_roads": "Avoid roads needing a vignette / subscription?"
         }
       }
     }
@@ -47,8 +47,8 @@
     },
     "units": {
       "options": {
-        "metric": "Metric System",
-        "imperial": "Imperial System"
+        "metric": "Metric system",
+        "imperial": "Imperial system"
       }
     },
     "region": {
@@ -63,8 +63,8 @@
   },
   "services": {
     "get_travel_times": {
-      "name": "Get Travel Times",
-      "description": "Get route alternatives and travel times between two locations.",
+      "name": "Get travel times",
+      "description": "Retrieves route alternatives and travel times between two locations.",
       "fields": {
         "origin": {
           "name": "[%key:component::waze_travel_time::config::step::user::data::origin%]",
@@ -76,7 +76,7 @@
         },
         "region": {
           "name": "[%key:component::waze_travel_time::config::step::user::data::region%]",
-          "description": "The region. Controls which waze server is used."
+          "description": "The region. Controls which Waze server is used."
         },
         "units": {
           "name": "[%key:component::waze_travel_time::options::step::init::data::units%]",

From eda60073ee0d3ce28dddfddc07556bf3a3c7b193 Mon Sep 17 00:00:00 2001
From: G Johansson <goran.johansson@shiftit.se>
Date: Mon, 6 Jan 2025 14:52:40 +0100
Subject: [PATCH 25/47] Raise ImportError in python_script (#134792)

---
 homeassistant/components/python_script/__init__.py | 2 +-
 tests/components/python_script/test_init.py        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/python_script/__init__.py b/homeassistant/components/python_script/__init__.py
index af7732780293d9..a45107181dee4c 100644
--- a/homeassistant/components/python_script/__init__.py
+++ b/homeassistant/components/python_script/__init__.py
@@ -180,7 +180,7 @@ def guarded_import(
     # Allow import of _strptime needed by datetime.datetime.strptime
     if name == "_strptime":
         return __import__(name, globals, locals, fromlist, level)
-    raise ScriptError(f"Not allowed to import {name}")
+    raise ImportError(f"Not allowed to import {name}")
 
 
 def guarded_inplacevar(op: str, target: Any, operand: Any) -> Any:
diff --git a/tests/components/python_script/test_init.py b/tests/components/python_script/test_init.py
index 2d151b4b81e6ac..14229e836628e6 100644
--- a/tests/components/python_script/test_init.py
+++ b/tests/components/python_script/test_init.py
@@ -711,4 +711,4 @@ async def test_no_other_imports_allowed(
     source = "import sys"
     hass.async_add_executor_job(execute, hass, "test.py", source, {})
     await hass.async_block_till_done(wait_background_tasks=True)
-    assert "Error executing script: Not allowed to import sys" in caplog.text
+    assert "ImportError: Not allowed to import sys" in caplog.text

From 07f3d939e343e4894e172f785ada0fe28dbbaadd Mon Sep 17 00:00:00 2001
From: Norbert Rittel <norbert@rittel.de>
Date: Mon, 6 Jan 2025 15:10:29 +0100
Subject: [PATCH 26/47] Replace "id" with "ID" for consistency across HA
 (#134798)

---
 homeassistant/components/cambridge_audio/strings.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/cambridge_audio/strings.json b/homeassistant/components/cambridge_audio/strings.json
index 9f5e031815b429..6041232fe65b24 100644
--- a/homeassistant/components/cambridge_audio/strings.json
+++ b/homeassistant/components/cambridge_audio/strings.json
@@ -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.",
@@ -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%]"

From 39d16ed5ceaf6c8b940633fdb45f226bdc63a9fb Mon Sep 17 00:00:00 2001
From: Norbert Rittel <norbert@rittel.de>
Date: Mon, 6 Jan 2025 15:15:08 +0100
Subject: [PATCH 27/47] Fix a few typos or grammar issues in asus_wrt (#134813)

---
 homeassistant/components/asuswrt/strings.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/asuswrt/strings.json b/homeassistant/components/asuswrt/strings.json
index bab40f281f59b8..9d50f50c7e92bd 100644
--- a/homeassistant/components/asuswrt/strings.json
+++ b/homeassistant/components/asuswrt/strings.json
@@ -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": {
@@ -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)"
         }
       }

From 43ffdd0eef847a6f815e2afd65f5d8150989244d Mon Sep 17 00:00:00 2001
From: Raphael Hehl <7577984+RaHehl@users.noreply.github.com>
Date: Sun, 5 Jan 2025 22:25:44 +0100
Subject: [PATCH 28/47] Bump uiprotect to version 7.4.1 (#134829)

---
 homeassistant/components/unifiprotect/manifest.json | 2 +-
 requirements_all.txt                                | 2 +-
 requirements_test_all.txt                           | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json
index d4877798208a18..018a600f037497 100644
--- a/homeassistant/components/unifiprotect/manifest.json
+++ b/homeassistant/components/unifiprotect/manifest.json
@@ -40,7 +40,7 @@
   "integration_type": "hub",
   "iot_class": "local_push",
   "loggers": ["uiprotect", "unifi_discovery"],
-  "requirements": ["uiprotect==7.2.0", "unifi-discovery==1.2.0"],
+  "requirements": ["uiprotect==7.4.1", "unifi-discovery==1.2.0"],
   "ssdp": [
     {
       "manufacturer": "Ubiquiti Networks",
diff --git a/requirements_all.txt b/requirements_all.txt
index 4e91bca1cdef59..4fa7d7719cdc70 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2910,7 +2910,7 @@ typedmonarchmoney==0.3.1
 uasiren==0.0.1
 
 # homeassistant.components.unifiprotect
-uiprotect==7.2.0
+uiprotect==7.4.1
 
 # homeassistant.components.landisgyr_heat_meter
 ultraheat-api==0.5.7
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 4b601e01647b00..e2a0678fc725b1 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2332,7 +2332,7 @@ typedmonarchmoney==0.3.1
 uasiren==0.0.1
 
 # homeassistant.components.unifiprotect
-uiprotect==7.2.0
+uiprotect==7.4.1
 
 # homeassistant.components.landisgyr_heat_meter
 ultraheat-api==0.5.7

From 914c6459dc3a8b4fbb816ec3e676f124e1b861ee Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Sun, 5 Jan 2025 12:44:37 -1000
Subject: [PATCH 29/47] Bump habluetooth to 3.7.0 (#134833)

---
 homeassistant/components/bluetooth/manifest.json | 2 +-
 homeassistant/package_constraints.txt            | 2 +-
 requirements_all.txt                             | 2 +-
 requirements_test_all.txt                        | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json
index e25c077b57fa43..ef1ec6a8936fda 100644
--- a/homeassistant/components/bluetooth/manifest.json
+++ b/homeassistant/components/bluetooth/manifest.json
@@ -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"
   ]
 }
diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt
index b07909e08eb916..5d3c4156a5c5c9 100644
--- a/homeassistant/package_constraints.txt
+++ b/homeassistant/package_constraints.txt
@@ -31,7 +31,7 @@ dbus-fast==2.24.3
 fnv-hash-fast==1.0.2
 go2rtc-client==0.1.2
 ha-ffmpeg==3.2.2
-habluetooth==3.6.0
+habluetooth==3.7.0
 hass-nabucasa==0.87.0
 hassil==2.1.0
 home-assistant-bluetooth==1.13.0
diff --git a/requirements_all.txt b/requirements_all.txt
index 4fa7d7719cdc70..08df15c57c7b21 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1091,7 +1091,7 @@ ha-philipsjs==3.2.2
 habitipy==0.3.3
 
 # homeassistant.components.bluetooth
-habluetooth==3.6.0
+habluetooth==3.7.0
 
 # homeassistant.components.cloud
 hass-nabucasa==0.87.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index e2a0678fc725b1..8c7998faac64a6 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -932,7 +932,7 @@ ha-philipsjs==3.2.2
 habitipy==0.3.3
 
 # homeassistant.components.bluetooth
-habluetooth==3.6.0
+habluetooth==3.7.0
 
 # homeassistant.components.cloud
 hass-nabucasa==0.87.0

From fe1ce3983185f22196a90202dde8c860465043c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=2E=20Diego=20Rodr=C3=ADguez=20Royo?=
 <jdrr1998@hotmail.com>
Date: Mon, 6 Jan 2025 15:03:25 +0100
Subject: [PATCH 30/47] Fix how function arguments are passed on actions at
 Home Connect (#134845)

---
 homeassistant/components/home_connect/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/homeassistant/components/home_connect/__init__.py b/homeassistant/components/home_connect/__init__.py
index 818c4e6fe19384..d7c042c2a91718 100644
--- a/homeassistant/components/home_connect/__init__.py
+++ b/homeassistant/components/home_connect/__init__.py
@@ -168,7 +168,7 @@ async def _run_appliance_service[*_Ts](
     error_translation_placeholders: dict[str, str],
 ) -> None:
     try:
-        await hass.async_add_executor_job(getattr(appliance, method), args)
+        await hass.async_add_executor_job(getattr(appliance, method), *args)
     except api.HomeConnectError as err:
         raise HomeAssistantError(
             translation_domain=DOMAIN,

From fbd031a03d6cce8e83542970da259b864e4d9289 Mon Sep 17 00:00:00 2001
From: Avi Miller <me@dje.li>
Date: Tue, 7 Jan 2025 01:02:57 +1100
Subject: [PATCH 31/47] Bump aiolifx-themes to update colors (#134846)

---
 homeassistant/components/lifx/manifest.json | 2 +-
 requirements_all.txt                        | 2 +-
 requirements_test_all.txt                   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/lifx/manifest.json b/homeassistant/components/lifx/manifest.json
index 2e16eb2082b672..9940ee15dca015 100644
--- a/homeassistant/components/lifx/manifest.json
+++ b/homeassistant/components/lifx/manifest.json
@@ -53,6 +53,6 @@
   "requirements": [
     "aiolifx==1.1.2",
     "aiolifx-effects==0.3.2",
-    "aiolifx-themes==0.5.5"
+    "aiolifx-themes==0.6.0"
   ]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index 08df15c57c7b21..16a3bb8d6b6ad3 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -282,7 +282,7 @@ aiokef==0.2.16
 aiolifx-effects==0.3.2
 
 # homeassistant.components.lifx
-aiolifx-themes==0.5.5
+aiolifx-themes==0.6.0
 
 # homeassistant.components.lifx
 aiolifx==1.1.2
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 8c7998faac64a6..e16cf18fbd0c54 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -264,7 +264,7 @@ aiokafka==0.10.0
 aiolifx-effects==0.3.2
 
 # homeassistant.components.lifx
-aiolifx-themes==0.5.5
+aiolifx-themes==0.6.0
 
 # homeassistant.components.lifx
 aiolifx==1.1.2

From 29989e903481cd4007373a0134661c67db6f892e Mon Sep 17 00:00:00 2001
From: Allen Porter <allen@thebends.org>
Date: Mon, 6 Jan 2025 02:24:06 -0800
Subject: [PATCH 32/47] Update Roborock config flow message when an account is
 already configured (#134854)

---
 .../components/roborock/config_flow.py        |  2 +-
 .../components/roborock/strings.json          |  2 +-
 tests/components/roborock/conftest.py         |  1 +
 tests/components/roborock/test_config_flow.py | 25 +++++++++++++++++++
 4 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/roborock/config_flow.py b/homeassistant/components/roborock/config_flow.py
index 200614b024e2d0..1a6b67286bb1c6 100644
--- a/homeassistant/components/roborock/config_flow.py
+++ b/homeassistant/components/roborock/config_flow.py
@@ -60,7 +60,7 @@ async def async_step_user(
         if user_input is not None:
             username = user_input[CONF_USERNAME]
             await self.async_set_unique_id(username.lower())
-            self._abort_if_unique_id_configured()
+            self._abort_if_unique_id_configured(error="already_configured_account")
             self._username = username
             _LOGGER.debug("Requesting code for Roborock account")
             self._client = RoborockApiClient(username)
diff --git a/homeassistant/components/roborock/strings.json b/homeassistant/components/roborock/strings.json
index 8ff82cae393166..c96a697ce2e0b8 100644
--- a/homeassistant/components/roborock/strings.json
+++ b/homeassistant/components/roborock/strings.json
@@ -28,7 +28,7 @@
       "unknown": "[%key:common::config_flow::error::unknown%]"
     },
     "abort": {
-      "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
+      "already_configured_account": "[%key:common::config_flow::abort::already_configured_account%]",
       "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
     }
   },
diff --git a/tests/components/roborock/conftest.py b/tests/components/roborock/conftest.py
index 357c644e2fe95e..44084574e01cea 100644
--- a/tests/components/roborock/conftest.py
+++ b/tests/components/roborock/conftest.py
@@ -161,6 +161,7 @@ def mock_roborock_entry(hass: HomeAssistant) -> MockConfigEntry:
             CONF_USER_DATA: USER_DATA.as_dict(),
             CONF_BASE_URL: BASE_URL,
         },
+        unique_id=USER_EMAIL,
     )
     mock_entry.add_to_hass(hass)
     return mock_entry
diff --git a/tests/components/roborock/test_config_flow.py b/tests/components/roborock/test_config_flow.py
index 39d8117847c285..13bc23e6e2bf58 100644
--- a/tests/components/roborock/test_config_flow.py
+++ b/tests/components/roborock/test_config_flow.py
@@ -244,3 +244,28 @@ async def test_reauth_flow(
     assert result["type"] is FlowResultType.ABORT
     assert result["reason"] == "reauth_successful"
     assert mock_roborock_entry.data["user_data"]["rriot"]["s"] == "new_password_hash"
+
+
+async def test_account_already_configured(
+    hass: HomeAssistant,
+    bypass_api_fixture,
+    mock_roborock_entry: MockConfigEntry,
+) -> None:
+    """Handle the config flow and make sure it succeeds."""
+    with patch(
+        "homeassistant.components.roborock.async_setup_entry", return_value=True
+    ):
+        result = await hass.config_entries.flow.async_init(
+            DOMAIN, context={"source": config_entries.SOURCE_USER}
+        )
+        assert result["type"] is FlowResultType.FORM
+        assert result["step_id"] == "user"
+        with patch(
+            "homeassistant.components.roborock.config_flow.RoborockApiClient.request_code"
+        ):
+            result = await hass.config_entries.flow.async_configure(
+                result["flow_id"], {CONF_USERNAME: USER_EMAIL}
+            )
+
+            assert result["type"] is FlowResultType.ABORT
+            assert result["reason"] == "already_configured_account"

From 58805f721cd683264b63391b58019a7186d5da39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= <joasoe@gmail.com>
Date: Mon, 6 Jan 2025 14:19:34 +0100
Subject: [PATCH 33/47] Log upload BackupAgentError (#134865)

* Log out BackupAgentError

* Update homeassistant/components/backup/manager.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/backup/manager.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Format

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
---
 homeassistant/components/backup/manager.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/homeassistant/components/backup/manager.py b/homeassistant/components/backup/manager.py
index 1910f8a55fb2fb..ba1c457561f4de 100644
--- a/homeassistant/components/backup/manager.py
+++ b/homeassistant/components/backup/manager.py
@@ -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):

From e5c986171bd961a00df1f10173548ace4e03e3a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= <joasoe@gmail.com>
Date: Mon, 6 Jan 2025 13:10:38 +0100
Subject: [PATCH 34/47] Log cloud backup upload response status (#134871)

Log the status of the upload response
---
 homeassistant/components/cloud/backup.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/homeassistant/components/cloud/backup.py b/homeassistant/components/cloud/backup.py
index 57145e52c448cd..b9da6dfb6a4c12 100644
--- a/homeassistant/components/cloud/backup.py
+++ b/homeassistant/components/cloud/backup.py
@@ -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

From 279785b22eeebaff0066395ccc47b9ca75029449 Mon Sep 17 00:00:00 2001
From: Robin Wohlers-Reichel <me@robinwr.com>
Date: Tue, 7 Jan 2025 00:17:52 +1030
Subject: [PATCH 35/47] Bump solax to 3.2.3 (#134876)

---
 homeassistant/components/solax/manifest.json | 2 +-
 requirements_all.txt                         | 2 +-
 requirements_test_all.txt                    | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/solax/manifest.json b/homeassistant/components/solax/manifest.json
index 631ace3792ffa1..925f11e4c65407 100644
--- a/homeassistant/components/solax/manifest.json
+++ b/homeassistant/components/solax/manifest.json
@@ -6,5 +6,5 @@
   "documentation": "https://www.home-assistant.io/integrations/solax",
   "iot_class": "local_polling",
   "loggers": ["solax"],
-  "requirements": ["solax==3.2.1"]
+  "requirements": ["solax==3.2.3"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index 16a3bb8d6b6ad3..3093615ce8bd65 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2720,7 +2720,7 @@ solaredge-local==0.2.3
 solarlog_cli==0.4.0
 
 # homeassistant.components.solax
-solax==3.2.1
+solax==3.2.3
 
 # homeassistant.components.somfy_mylink
 somfy-mylink-synergy==1.0.6
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index e16cf18fbd0c54..e747ca8f67140b 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2181,7 +2181,7 @@ soco==0.30.6
 solarlog_cli==0.4.0
 
 # homeassistant.components.solax
-solax==3.2.1
+solax==3.2.3
 
 # homeassistant.components.somfy_mylink
 somfy-mylink-synergy==1.0.6

From 2fc489d17dfe7bf253527a308ca710b7113c5ff9 Mon Sep 17 00:00:00 2001
From: Luke Lashley <conway220@gmail.com>
Date: Mon, 6 Jan 2025 09:46:21 -0500
Subject: [PATCH 36/47] Add extra failure exceptions during roborock setup
 (#134889)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
---
 homeassistant/components/roborock/__init__.py | 19 ++++++++-
 .../components/roborock/strings.json          |  6 +++
 tests/components/roborock/test_init.py        | 39 ++++++++++++++++++-
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/roborock/__init__.py b/homeassistant/components/roborock/__init__.py
index d02dddece42f1a..bc82aadffed12e 100644
--- a/homeassistant/components/roborock/__init__.py
+++ b/homeassistant/components/roborock/__init__.py
@@ -9,7 +9,13 @@
 import logging
 from typing import Any
 
-from roborock import HomeDataRoom, RoborockException, RoborockInvalidCredentials
+from roborock import (
+    HomeDataRoom,
+    RoborockException,
+    RoborockInvalidCredentials,
+    RoborockInvalidUserAgreement,
+    RoborockNoUserAgreement,
+)
 from roborock.containers import DeviceData, HomeDataDevice, HomeDataProduct, UserData
 from roborock.version_1_apis.roborock_mqtt_client_v1 import RoborockMqttClientV1
 from roborock.version_a01_apis import RoborockMqttClientA01
@@ -60,12 +66,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
             translation_domain=DOMAIN,
             translation_key="invalid_credentials",
         ) from err
+    except RoborockInvalidUserAgreement as err:
+        raise ConfigEntryNotReady(
+            translation_domain=DOMAIN,
+            translation_key="invalid_user_agreement",
+        ) from err
+    except RoborockNoUserAgreement as err:
+        raise ConfigEntryNotReady(
+            translation_domain=DOMAIN,
+            translation_key="no_user_agreement",
+        ) from err
     except RoborockException as err:
         raise ConfigEntryNotReady(
             "Failed to get Roborock home data",
             translation_domain=DOMAIN,
             translation_key="home_data_fail",
         ) from err
+
     _LOGGER.debug("Got home data %s", home_data)
     all_devices: list[HomeDataDevice] = home_data.devices + home_data.received_devices
     device_map: dict[str, HomeDataDevice] = {
diff --git a/homeassistant/components/roborock/strings.json b/homeassistant/components/roborock/strings.json
index c96a697ce2e0b8..8c66f6ab98683e 100644
--- a/homeassistant/components/roborock/strings.json
+++ b/homeassistant/components/roborock/strings.json
@@ -422,6 +422,12 @@
     },
     "update_options_failed": {
       "message": "Failed to update Roborock options"
+    },
+    "invalid_user_agreement": {
+      "message": "User agreement must be accepted again. Open your Roborock app and accept the agreement."
+    },
+    "no_user_agreement": {
+      "message": "You have not valid user agreement. Open your Roborock app and accept the agreement."
     }
   },
   "services": {
diff --git a/tests/components/roborock/test_init.py b/tests/components/roborock/test_init.py
index cace9a8ed67d7a..4cd2a37effcf30 100644
--- a/tests/components/roborock/test_init.py
+++ b/tests/components/roborock/test_init.py
@@ -4,7 +4,12 @@
 from unittest.mock import patch
 
 import pytest
-from roborock import RoborockException, RoborockInvalidCredentials
+from roborock import (
+    RoborockException,
+    RoborockInvalidCredentials,
+    RoborockInvalidUserAgreement,
+    RoborockNoUserAgreement,
+)
 
 from homeassistant.components.roborock.const import DOMAIN
 from homeassistant.config_entries import ConfigEntryState
@@ -194,3 +199,35 @@ async def test_not_supported_a01_device(
         await async_setup_component(hass, DOMAIN, {})
         await hass.async_block_till_done()
     assert "The device you added is not yet supported" in caplog.text
+
+
+async def test_invalid_user_agreement(
+    hass: HomeAssistant,
+    bypass_api_fixture,
+    mock_roborock_entry: MockConfigEntry,
+) -> None:
+    """Test that we fail setting up if the user agreement is out of date."""
+    with patch(
+        "homeassistant.components.roborock.RoborockApiClient.get_home_data_v2",
+        side_effect=RoborockInvalidUserAgreement(),
+    ):
+        await hass.config_entries.async_setup(mock_roborock_entry.entry_id)
+        assert mock_roborock_entry.state is ConfigEntryState.SETUP_RETRY
+        assert (
+            mock_roborock_entry.error_reason_translation_key == "invalid_user_agreement"
+        )
+
+
+async def test_no_user_agreement(
+    hass: HomeAssistant,
+    bypass_api_fixture,
+    mock_roborock_entry: MockConfigEntry,
+) -> None:
+    """Test that we fail setting up if the user has no agreement."""
+    with patch(
+        "homeassistant.components.roborock.RoborockApiClient.get_home_data_v2",
+        side_effect=RoborockNoUserAgreement(),
+    ):
+        await hass.config_entries.async_setup(mock_roborock_entry.entry_id)
+        assert mock_roborock_entry.state is ConfigEntryState.SETUP_RETRY
+        assert mock_roborock_entry.error_reason_translation_key == "no_user_agreement"

From c40771ba6aa4e2a8b8c5ad57420e068ebb32ea20 Mon Sep 17 00:00:00 2001
From: Norbert Rittel <norbert@rittel.de>
Date: Mon, 6 Jan 2025 16:30:40 +0100
Subject: [PATCH 37/47] Use uppercase for "ID" and sentence-case for "name" /
 "icon" (#134890)

---
 homeassistant/components/androidtv_remote/strings.json | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/androidtv_remote/strings.json b/homeassistant/components/androidtv_remote/strings.json
index 33970171d4018b..e41cbcf9a762a0 100644
--- a/homeassistant/components/androidtv_remote/strings.json
+++ b/homeassistant/components/androidtv_remote/strings.json
@@ -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"
         }
       }

From 4867d3a187652e6cd58eba2dfa695b203ba71f7e Mon Sep 17 00:00:00 2001
From: "Steven B." <51370195+sdb9696@users.noreply.github.com>
Date: Mon, 6 Jan 2025 15:58:33 +0000
Subject: [PATCH 38/47] Bump python-kasa to 0.9.1 (#134893)

Bump tplink python-kasa dependency to 0.9.1
---
 homeassistant/components/tplink/manifest.json | 2 +-
 requirements_all.txt                          | 2 +-
 requirements_test_all.txt                     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/tplink/manifest.json b/homeassistant/components/tplink/manifest.json
index 7797f0a36a3167..a975e675ceb555 100644
--- a/homeassistant/components/tplink/manifest.json
+++ b/homeassistant/components/tplink/manifest.json
@@ -300,5 +300,5 @@
   "documentation": "https://www.home-assistant.io/integrations/tplink",
   "iot_class": "local_polling",
   "loggers": ["kasa"],
-  "requirements": ["python-kasa[speedups]==0.9.0"]
+  "requirements": ["python-kasa[speedups]==0.9.1"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index 3093615ce8bd65..06e784ec9ac76f 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2378,7 +2378,7 @@ python-join-api==0.0.9
 python-juicenet==1.1.0
 
 # homeassistant.components.tplink
-python-kasa[speedups]==0.9.0
+python-kasa[speedups]==0.9.1
 
 # homeassistant.components.linkplay
 python-linkplay==0.1.1
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index e747ca8f67140b..b184e4fbb58cec 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1914,7 +1914,7 @@ python-izone==1.2.9
 python-juicenet==1.1.0
 
 # homeassistant.components.tplink
-python-kasa[speedups]==0.9.0
+python-kasa[speedups]==0.9.1
 
 # homeassistant.components.linkplay
 python-linkplay==0.1.1

From 9288dce7edbfe7565f506c75429b82e4805d6014 Mon Sep 17 00:00:00 2001
From: Manu <4445816+tr4nt0r@users.noreply.github.com>
Date: Mon, 6 Jan 2025 20:37:01 +0100
Subject: [PATCH 39/47] Add `bring_api` to loggers in Bring integration
 (#134897)

Add bring-api to loggers
---
 homeassistant/components/bring/manifest.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/homeassistant/components/bring/manifest.json b/homeassistant/components/bring/manifest.json
index ff24a9913508a3..71fe733ccf5d50 100644
--- a/homeassistant/components/bring/manifest.json
+++ b/homeassistant/components/bring/manifest.json
@@ -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"]
 }

From eb345971b44e6280554b543d4db19faf7e3f3136 Mon Sep 17 00:00:00 2001
From: Manu <4445816+tr4nt0r@users.noreply.github.com>
Date: Mon, 6 Jan 2025 19:55:47 +0100
Subject: [PATCH 40/47] Fix wrong power limit decimal place in IronOS (#134902)

---
 homeassistant/components/iron_os/number.py          | 4 ++--
 tests/components/iron_os/snapshots/test_number.ambr | 8 ++++----
 tests/components/iron_os/test_number.py             | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/homeassistant/components/iron_os/number.py b/homeassistant/components/iron_os/number.py
index 583844223ddab1..e50b227bbef84e 100644
--- a/homeassistant/components/iron_os/number.py
+++ b/homeassistant/components/iron_os/number.py
@@ -188,8 +188,8 @@ def multiply(value: float | None, multiplier: float) -> float | None:
         characteristic=CharSetting.POWER_LIMIT,
         mode=NumberMode.BOX,
         native_min_value=0,
-        native_max_value=12,
-        native_step=0.1,
+        native_max_value=120,
+        native_step=5,
         entity_category=EntityCategory.CONFIG,
         native_unit_of_measurement=UnitOfPower.WATT,
         entity_registry_enabled_default=False,
diff --git a/tests/components/iron_os/snapshots/test_number.ambr b/tests/components/iron_os/snapshots/test_number.ambr
index 24663cc4b0fa56..fc4fe96d746370 100644
--- a/tests/components/iron_os/snapshots/test_number.ambr
+++ b/tests/components/iron_os/snapshots/test_number.ambr
@@ -620,10 +620,10 @@
     }),
     'area_id': None,
     'capabilities': dict({
-      'max': 12,
+      'max': 120,
       'min': 0,
       'mode': <NumberMode.BOX: 'box'>,
-      'step': 0.1,
+      'step': 5,
     }),
     'config_entry_id': <ANY>,
     'device_class': None,
@@ -656,10 +656,10 @@
   StateSnapshot({
     'attributes': ReadOnlyDict({
       'friendly_name': 'Pinecil Power limit',
-      'max': 12,
+      'max': 120,
       'min': 0,
       'mode': <NumberMode.BOX: 'box'>,
-      'step': 0.1,
+      'step': 5,
       'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
     }),
     'context': <ANY>,
diff --git a/tests/components/iron_os/test_number.py b/tests/components/iron_os/test_number.py
index 088b66feb6475e..bdec922a88ccd7 100644
--- a/tests/components/iron_os/test_number.py
+++ b/tests/components/iron_os/test_number.py
@@ -126,7 +126,7 @@ async def test_state(
             2.0,
             2.0,
         ),
-        ("number.pinecil_power_limit", CharSetting.POWER_LIMIT, 12.0, 12.0),
+        ("number.pinecil_power_limit", CharSetting.POWER_LIMIT, 120, 120),
         ("number.pinecil_quick_charge_voltage", CharSetting.QC_IDEAL_VOLTAGE, 9.0, 9.0),
         (
             "number.pinecil_short_press_temperature_step",

From 188def51c66acce3de2fa7b440b9026e7acced79 Mon Sep 17 00:00:00 2001
From: Bram Kragten <mail@bramkragten.nl>
Date: Mon, 6 Jan 2025 19:11:01 +0100
Subject: [PATCH 41/47] Update frontend to 20250106.0 (#134905)

---
 homeassistant/components/frontend/manifest.json | 2 +-
 homeassistant/package_constraints.txt           | 2 +-
 requirements_all.txt                            | 2 +-
 requirements_test_all.txt                       | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json
index 2094f817dcd710..267374aa302c95 100644
--- a/homeassistant/components/frontend/manifest.json
+++ b/homeassistant/components/frontend/manifest.json
@@ -21,5 +21,5 @@
   "documentation": "https://www.home-assistant.io/integrations/frontend",
   "integration_type": "system",
   "quality_scale": "internal",
-  "requirements": ["home-assistant-frontend==20250103.0"]
+  "requirements": ["home-assistant-frontend==20250106.0"]
 }
diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt
index 5d3c4156a5c5c9..dac77fd4276da2 100644
--- a/homeassistant/package_constraints.txt
+++ b/homeassistant/package_constraints.txt
@@ -35,7 +35,7 @@ habluetooth==3.7.0
 hass-nabucasa==0.87.0
 hassil==2.1.0
 home-assistant-bluetooth==1.13.0
-home-assistant-frontend==20250103.0
+home-assistant-frontend==20250106.0
 home-assistant-intents==2025.1.1
 httpx==0.27.2
 ifaddr==0.2.0
diff --git a/requirements_all.txt b/requirements_all.txt
index 06e784ec9ac76f..af648a8993b3e4 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1134,7 +1134,7 @@ hole==0.8.0
 holidays==0.63
 
 # homeassistant.components.frontend
-home-assistant-frontend==20250103.0
+home-assistant-frontend==20250106.0
 
 # homeassistant.components.conversation
 home-assistant-intents==2025.1.1
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index b184e4fbb58cec..6c8de46f147f36 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -963,7 +963,7 @@ hole==0.8.0
 holidays==0.63
 
 # homeassistant.components.frontend
-home-assistant-frontend==20250103.0
+home-assistant-frontend==20250106.0
 
 # homeassistant.components.conversation
 home-assistant-intents==2025.1.1

From 81a669c163a993e9be91640a62bb13a4a24f80f8 Mon Sep 17 00:00:00 2001
From: Klaas Schoute <klaas_schoute@hotmail.com>
Date: Sun, 5 Jan 2025 10:10:55 +0100
Subject: [PATCH 42/47] Bump powerfox to v1.1.0 (#134730)

---
 homeassistant/components/powerfox/manifest.json | 2 +-
 requirements_all.txt                            | 2 +-
 requirements_test_all.txt                       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/homeassistant/components/powerfox/manifest.json b/homeassistant/components/powerfox/manifest.json
index 7083ffe8de7761..c5499c26dd7993 100644
--- a/homeassistant/components/powerfox/manifest.json
+++ b/homeassistant/components/powerfox/manifest.json
@@ -6,7 +6,7 @@
   "documentation": "https://www.home-assistant.io/integrations/powerfox",
   "iot_class": "cloud_polling",
   "quality_scale": "silver",
-  "requirements": ["powerfox==1.0.0"],
+  "requirements": ["powerfox==1.1.0"],
   "zeroconf": [
     {
       "type": "_http._tcp.local.",
diff --git a/requirements_all.txt b/requirements_all.txt
index af648a8993b3e4..9da0a3d62f74bd 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1650,7 +1650,7 @@ pmsensor==0.4
 poolsense==0.0.8
 
 # homeassistant.components.powerfox
-powerfox==1.0.0
+powerfox==1.1.0
 
 # homeassistant.components.reddit
 praw==7.5.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 6c8de46f147f36..19bd9f6c0edc59 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1360,7 +1360,7 @@ plumlightpad==0.0.11
 poolsense==0.0.8
 
 # homeassistant.components.powerfox
-powerfox==1.0.0
+powerfox==1.1.0
 
 # homeassistant.components.reddit
 praw==7.5.0

From b815899fdccbe73593ae17ee36e437dd2c35456c Mon Sep 17 00:00:00 2001
From: Klaas Schoute <klaas_schoute@hotmail.com>
Date: Mon, 6 Jan 2025 20:52:54 +0100
Subject: [PATCH 43/47] Bump powerfox to v1.2.0 (#134908)

---
 homeassistant/components/powerfox/coordinator.py | 3 ++-
 homeassistant/components/powerfox/manifest.json  | 2 +-
 requirements_all.txt                             | 2 +-
 requirements_test_all.txt                        | 2 +-
 4 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/powerfox/coordinator.py b/homeassistant/components/powerfox/coordinator.py
index f7ec5ab67161ac..a4a26759b69b1e 100644
--- a/homeassistant/components/powerfox/coordinator.py
+++ b/homeassistant/components/powerfox/coordinator.py
@@ -7,6 +7,7 @@
     Powerfox,
     PowerfoxAuthenticationError,
     PowerfoxConnectionError,
+    PowerfoxNoDataError,
     Poweropti,
 )
 
@@ -45,5 +46,5 @@ async def _async_update_data(self) -> Poweropti:
             return await self.client.device(device_id=self.device.id)
         except PowerfoxAuthenticationError as err:
             raise ConfigEntryAuthFailed(err) from err
-        except PowerfoxConnectionError as err:
+        except (PowerfoxConnectionError, PowerfoxNoDataError) as err:
             raise UpdateFailed(err) from err
diff --git a/homeassistant/components/powerfox/manifest.json b/homeassistant/components/powerfox/manifest.json
index c5499c26dd7993..bb72d73b5a8c0d 100644
--- a/homeassistant/components/powerfox/manifest.json
+++ b/homeassistant/components/powerfox/manifest.json
@@ -6,7 +6,7 @@
   "documentation": "https://www.home-assistant.io/integrations/powerfox",
   "iot_class": "cloud_polling",
   "quality_scale": "silver",
-  "requirements": ["powerfox==1.1.0"],
+  "requirements": ["powerfox==1.2.0"],
   "zeroconf": [
     {
       "type": "_http._tcp.local.",
diff --git a/requirements_all.txt b/requirements_all.txt
index 9da0a3d62f74bd..bdf5221bd7c4d6 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1650,7 +1650,7 @@ pmsensor==0.4
 poolsense==0.0.8
 
 # homeassistant.components.powerfox
-powerfox==1.1.0
+powerfox==1.2.0
 
 # homeassistant.components.reddit
 praw==7.5.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 19bd9f6c0edc59..0e981563c35af9 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1360,7 +1360,7 @@ plumlightpad==0.0.11
 poolsense==0.0.8
 
 # homeassistant.components.powerfox
-powerfox==1.1.0
+powerfox==1.2.0
 
 # homeassistant.components.reddit
 praw==7.5.0

From 5337ab2e72ccf515693c62093f2e877a46d594c8 Mon Sep 17 00:00:00 2001
From: G Johansson <goran.johansson@shiftit.se>
Date: Mon, 6 Jan 2025 22:45:04 +0100
Subject: [PATCH 44/47] Bump holidays to 0.64 (#134922)

---
 homeassistant/components/holiday/manifest.json | 2 +-
 homeassistant/components/workday/manifest.json | 2 +-
 requirements_all.txt                           | 2 +-
 requirements_test_all.txt                      | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/holiday/manifest.json b/homeassistant/components/holiday/manifest.json
index 33cae2315956eb..09943faf0a2f29 100644
--- a/homeassistant/components/holiday/manifest.json
+++ b/homeassistant/components/holiday/manifest.json
@@ -5,5 +5,5 @@
   "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/holiday",
   "iot_class": "local_polling",
-  "requirements": ["holidays==0.63", "babel==2.15.0"]
+  "requirements": ["holidays==0.64", "babel==2.15.0"]
 }
diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json
index de9cbe694d8995..bb5e6333b8b7ae 100644
--- a/homeassistant/components/workday/manifest.json
+++ b/homeassistant/components/workday/manifest.json
@@ -7,5 +7,5 @@
   "iot_class": "local_polling",
   "loggers": ["holidays"],
   "quality_scale": "internal",
-  "requirements": ["holidays==0.63"]
+  "requirements": ["holidays==0.64"]
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index bdf5221bd7c4d6..e64a48cbb81242 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1131,7 +1131,7 @@ hole==0.8.0
 
 # homeassistant.components.holiday
 # homeassistant.components.workday
-holidays==0.63
+holidays==0.64
 
 # homeassistant.components.frontend
 home-assistant-frontend==20250106.0
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 0e981563c35af9..bf0bcb7f9d3076 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -960,7 +960,7 @@ hole==0.8.0
 
 # homeassistant.components.holiday
 # homeassistant.components.workday
-holidays==0.63
+holidays==0.64
 
 # homeassistant.components.frontend
 home-assistant-frontend==20250106.0

From 9a9514d53b3737530a81908ea6b5d00777cf83b8 Mon Sep 17 00:00:00 2001
From: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Date: Mon, 6 Jan 2025 22:39:24 +0100
Subject: [PATCH 45/47] Revert "Remove deprecated supported features warning in
 LightEntity" (#134927)

---
 homeassistant/components/light/__init__.py |  81 ++++-
 tests/components/light/common.py           |   3 +-
 tests/components/light/test_init.py        | 347 ++++++++++++++++++---
 3 files changed, 381 insertions(+), 50 deletions(-)

diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py
index 33bd259469b915..76fbea70322088 100644
--- a/homeassistant/components/light/__init__.py
+++ b/homeassistant/components/light/__init__.py
@@ -354,7 +354,7 @@ def filter_turn_off_params(
     if not params:
         return params
 
-    supported_features = light.supported_features
+    supported_features = light.supported_features_compat
 
     if LightEntityFeature.FLASH not in supported_features:
         params.pop(ATTR_FLASH, None)
@@ -366,7 +366,7 @@ def filter_turn_off_params(
 
 def filter_turn_on_params(light: LightEntity, params: dict[str, Any]) -> dict[str, Any]:
     """Filter out params not supported by the light."""
-    supported_features = light.supported_features
+    supported_features = light.supported_features_compat
 
     if LightEntityFeature.EFFECT not in supported_features:
         params.pop(ATTR_EFFECT, None)
@@ -1093,7 +1093,7 @@ def effect(self) -> str | None:
     def capability_attributes(self) -> dict[str, Any]:
         """Return capability attributes."""
         data: dict[str, Any] = {}
-        supported_features = self.supported_features
+        supported_features = self.supported_features_compat
         supported_color_modes = self._light_internal_supported_color_modes
 
         if ColorMode.COLOR_TEMP in supported_color_modes:
@@ -1255,11 +1255,12 @@ def __validate_supported_color_modes(
     def state_attributes(self) -> dict[str, Any] | None:
         """Return state attributes."""
         data: dict[str, Any] = {}
-        supported_features = self.supported_features
+        supported_features = self.supported_features_compat
         supported_color_modes = self.supported_color_modes
         legacy_supported_color_modes = (
             supported_color_modes or self._light_internal_supported_color_modes
         )
+        supported_features_value = supported_features.value
         _is_on = self.is_on
         color_mode = self._light_internal_color_mode if _is_on else None
 
@@ -1278,6 +1279,13 @@ def state_attributes(self) -> dict[str, Any] | None:
                 data[ATTR_BRIGHTNESS] = self.brightness
             else:
                 data[ATTR_BRIGHTNESS] = None
+        elif supported_features_value & _DEPRECATED_SUPPORT_BRIGHTNESS.value:
+            # Backwards compatibility for ambiguous / incomplete states
+            # Warning is printed by supported_features_compat, remove in 2025.1
+            if _is_on:
+                data[ATTR_BRIGHTNESS] = self.brightness
+            else:
+                data[ATTR_BRIGHTNESS] = None
 
         if color_temp_supported(supported_color_modes):
             if color_mode == ColorMode.COLOR_TEMP:
@@ -1292,6 +1300,21 @@ def state_attributes(self) -> dict[str, Any] | None:
             else:
                 data[ATTR_COLOR_TEMP_KELVIN] = None
                 data[_DEPRECATED_ATTR_COLOR_TEMP.value] = None
+        elif supported_features_value & _DEPRECATED_SUPPORT_COLOR_TEMP.value:
+            # Backwards compatibility
+            # Warning is printed by supported_features_compat, remove in 2025.1
+            if _is_on:
+                color_temp_kelvin = self.color_temp_kelvin
+                data[ATTR_COLOR_TEMP_KELVIN] = color_temp_kelvin
+                if color_temp_kelvin:
+                    data[_DEPRECATED_ATTR_COLOR_TEMP.value] = (
+                        color_util.color_temperature_kelvin_to_mired(color_temp_kelvin)
+                    )
+                else:
+                    data[_DEPRECATED_ATTR_COLOR_TEMP.value] = None
+            else:
+                data[ATTR_COLOR_TEMP_KELVIN] = None
+                data[_DEPRECATED_ATTR_COLOR_TEMP.value] = None
 
         if color_supported(legacy_supported_color_modes) or color_temp_supported(
             legacy_supported_color_modes
@@ -1329,7 +1352,24 @@ def _light_internal_supported_color_modes(self) -> set[ColorMode] | set[str]:
                 type(self),
                 report_issue,
             )
-        return {ColorMode.ONOFF}
+        supported_features = self.supported_features_compat
+        supported_features_value = supported_features.value
+        supported_color_modes: set[ColorMode] = set()
+
+        if supported_features_value & _DEPRECATED_SUPPORT_COLOR_TEMP.value:
+            supported_color_modes.add(ColorMode.COLOR_TEMP)
+        if supported_features_value & _DEPRECATED_SUPPORT_COLOR.value:
+            supported_color_modes.add(ColorMode.HS)
+        if (
+            not supported_color_modes
+            and supported_features_value & _DEPRECATED_SUPPORT_BRIGHTNESS.value
+        ):
+            supported_color_modes = {ColorMode.BRIGHTNESS}
+
+        if not supported_color_modes:
+            supported_color_modes = {ColorMode.ONOFF}
+
+        return supported_color_modes
 
     @cached_property
     def supported_color_modes(self) -> set[ColorMode] | set[str] | None:
@@ -1341,6 +1381,37 @@ def supported_features(self) -> LightEntityFeature:
         """Flag supported features."""
         return self._attr_supported_features
 
+    @property
+    def supported_features_compat(self) -> LightEntityFeature:
+        """Return the supported features as LightEntityFeature.
+
+        Remove this compatibility shim in 2025.1 or later.
+        """
+        features = self.supported_features
+        if type(features) is not int:  # noqa: E721
+            return features
+        new_features = LightEntityFeature(features)
+        if self._deprecated_supported_features_reported is True:
+            return new_features
+        self._deprecated_supported_features_reported = True
+        report_issue = self._suggest_report_issue()
+        report_issue += (
+            " and reference "
+            "https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
+        )
+        _LOGGER.warning(
+            (
+                "Entity %s (%s) is using deprecated supported features"
+                " values which will be removed in HA Core 2025.1. Instead it should use"
+                " %s and color modes, please %s"
+            ),
+            self.entity_id,
+            type(self),
+            repr(new_features),
+            report_issue,
+        )
+        return new_features
+
     def __should_report_light_issue(self) -> bool:
         """Return if light color mode issues should be reported."""
         if not self.platform:
diff --git a/tests/components/light/common.py b/tests/components/light/common.py
index b29ac0c7c8922f..77411cd637d931 100644
--- a/tests/components/light/common.py
+++ b/tests/components/light/common.py
@@ -26,7 +26,6 @@
     DOMAIN,
     ColorMode,
     LightEntity,
-    LightEntityFeature,
 )
 from homeassistant.const import (
     ATTR_ENTITY_ID,
@@ -157,7 +156,7 @@ class MockLight(MockToggleEntity, LightEntity):
 
     _attr_max_color_temp_kelvin = DEFAULT_MAX_KELVIN
     _attr_min_color_temp_kelvin = DEFAULT_MIN_KELVIN
-    supported_features = LightEntityFeature(0)
+    supported_features = 0
 
     brightness = None
     color_temp_kelvin = None
diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py
index 303bf68f68c684..776995ee523877 100644
--- a/tests/components/light/test_init.py
+++ b/tests/components/light/test_init.py
@@ -1,6 +1,7 @@
 """The tests for the Light component."""
 
 from types import ModuleType
+from typing import Literal
 from unittest.mock import MagicMock, mock_open, patch
 
 import pytest
@@ -137,8 +138,13 @@ async def test_services(
     ent3.supported_color_modes = [light.ColorMode.HS]
     ent1.supported_features = light.LightEntityFeature.TRANSITION
     ent2.supported_features = (
-        light.LightEntityFeature.EFFECT | light.LightEntityFeature.TRANSITION
+        light.SUPPORT_COLOR
+        | light.LightEntityFeature.EFFECT
+        | light.LightEntityFeature.TRANSITION
     )
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    ent2.supported_color_modes = None
+    ent2.color_mode = None
     ent3.supported_features = (
         light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION
     )
@@ -254,7 +260,10 @@ async def test_services(
     }
 
     _, data = ent2.last_call("turn_on")
-    assert data == {light.ATTR_EFFECT: "fun_effect"}
+    assert data == {
+        light.ATTR_EFFECT: "fun_effect",
+        light.ATTR_HS_COLOR: (0, 0),
+    }
 
     _, data = ent3.last_call("turn_on")
     assert data == {light.ATTR_FLASH: "short", light.ATTR_HS_COLOR: (71.059, 100)}
@@ -338,6 +347,8 @@ async def test_services(
 
     _, data = ent2.last_call("turn_on")
     assert data == {
+        light.ATTR_BRIGHTNESS: 100,
+        light.ATTR_HS_COLOR: profile.hs_color,
         light.ATTR_TRANSITION: 1,
     }
 
@@ -915,12 +926,16 @@ async def test_light_brightness_step(hass: HomeAssistant) -> None:
     setup_test_component_platform(hass, light.DOMAIN, entities)
 
     entity0 = entities[0]
-    entity0.supported_color_modes = {light.ColorMode.BRIGHTNESS}
-    entity0.color_mode = light.ColorMode.BRIGHTNESS
+    entity0.supported_features = light.SUPPORT_BRIGHTNESS
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity0.supported_color_modes = None
+    entity0.color_mode = None
     entity0.brightness = 100
     entity1 = entities[1]
-    entity1.supported_color_modes = {light.ColorMode.BRIGHTNESS}
-    entity1.color_mode = light.ColorMode.BRIGHTNESS
+    entity1.supported_features = light.SUPPORT_BRIGHTNESS
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity1.supported_color_modes = None
+    entity1.color_mode = None
     entity1.brightness = 50
     assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
     await hass.async_block_till_done()
@@ -981,8 +996,10 @@ async def test_light_brightness_pct_conversion(
     setup_test_component_platform(hass, light.DOMAIN, mock_light_entities)
 
     entity = mock_light_entities[0]
-    entity.supported_color_modes = {light.ColorMode.BRIGHTNESS}
-    entity.color_mode = light.ColorMode.BRIGHTNESS
+    entity.supported_features = light.SUPPORT_BRIGHTNESS
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity.supported_color_modes = None
+    entity.color_mode = None
     entity.brightness = 100
     assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
     await hass.async_block_till_done()
@@ -1131,6 +1148,167 @@ async def test_profile_load_optional_hs_color(hass: HomeAssistant) -> None:
         assert invalid_profile_name not in profiles.data
 
 
+@pytest.mark.parametrize("light_state", [STATE_ON, STATE_OFF])
+async def test_light_backwards_compatibility_supported_color_modes(
+    hass: HomeAssistant, light_state: Literal["on", "off"]
+) -> None:
+    """Test supported_color_modes if not implemented by the entity."""
+    entities = [
+        MockLight("Test_0", light_state),
+        MockLight("Test_1", light_state),
+        MockLight("Test_2", light_state),
+        MockLight("Test_3", light_state),
+        MockLight("Test_4", light_state),
+    ]
+
+    entity0 = entities[0]
+
+    entity1 = entities[1]
+    entity1.supported_features = light.SUPPORT_BRIGHTNESS
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity1.supported_color_modes = None
+    entity1.color_mode = None
+
+    entity2 = entities[2]
+    entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity2.supported_color_modes = None
+    entity2.color_mode = None
+
+    entity3 = entities[3]
+    entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity3.supported_color_modes = None
+    entity3.color_mode = None
+
+    entity4 = entities[4]
+    entity4.supported_features = (
+        light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP
+    )
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity4.supported_color_modes = None
+    entity4.color_mode = None
+
+    setup_test_component_platform(hass, light.DOMAIN, entities)
+
+    assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
+    await hass.async_block_till_done()
+
+    state = hass.states.get(entity0.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.ONOFF]
+    if light_state == STATE_OFF:
+        assert state.attributes["color_mode"] is None
+    else:
+        assert state.attributes["color_mode"] == light.ColorMode.ONOFF
+
+    state = hass.states.get(entity1.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.BRIGHTNESS]
+    if light_state == STATE_OFF:
+        assert state.attributes["color_mode"] is None
+    else:
+        assert state.attributes["color_mode"] == light.ColorMode.UNKNOWN
+
+    state = hass.states.get(entity2.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.COLOR_TEMP]
+    if light_state == STATE_OFF:
+        assert state.attributes["color_mode"] is None
+    else:
+        assert state.attributes["color_mode"] == light.ColorMode.UNKNOWN
+
+    state = hass.states.get(entity3.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.HS]
+    if light_state == STATE_OFF:
+        assert state.attributes["color_mode"] is None
+    else:
+        assert state.attributes["color_mode"] == light.ColorMode.UNKNOWN
+
+    state = hass.states.get(entity4.entity_id)
+    assert state.attributes["supported_color_modes"] == [
+        light.ColorMode.COLOR_TEMP,
+        light.ColorMode.HS,
+    ]
+    if light_state == STATE_OFF:
+        assert state.attributes["color_mode"] is None
+    else:
+        assert state.attributes["color_mode"] == light.ColorMode.UNKNOWN
+
+
+async def test_light_backwards_compatibility_color_mode(hass: HomeAssistant) -> None:
+    """Test color_mode if not implemented by the entity."""
+    entities = [
+        MockLight("Test_0", STATE_ON),
+        MockLight("Test_1", STATE_ON),
+        MockLight("Test_2", STATE_ON),
+        MockLight("Test_3", STATE_ON),
+        MockLight("Test_4", STATE_ON),
+    ]
+
+    entity0 = entities[0]
+
+    entity1 = entities[1]
+    entity1.supported_features = light.SUPPORT_BRIGHTNESS
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity1.supported_color_modes = None
+    entity1.color_mode = None
+    entity1.brightness = 100
+
+    entity2 = entities[2]
+    entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity2.supported_color_modes = None
+    entity2.color_mode = None
+    entity2.color_temp_kelvin = 10000
+
+    entity3 = entities[3]
+    entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity3.supported_color_modes = None
+    entity3.color_mode = None
+    entity3.hs_color = (240, 100)
+
+    entity4 = entities[4]
+    entity4.supported_features = (
+        light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP
+    )
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity4.supported_color_modes = None
+    entity4.color_mode = None
+    entity4.hs_color = (240, 100)
+    entity4.color_temp_kelvin = 10000
+
+    setup_test_component_platform(hass, light.DOMAIN, entities)
+
+    assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
+    await hass.async_block_till_done()
+
+    state = hass.states.get(entity0.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.ONOFF]
+    assert state.attributes["color_mode"] == light.ColorMode.ONOFF
+
+    state = hass.states.get(entity1.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.BRIGHTNESS]
+    assert state.attributes["color_mode"] == light.ColorMode.BRIGHTNESS
+
+    state = hass.states.get(entity2.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.COLOR_TEMP]
+    assert state.attributes["color_mode"] == light.ColorMode.COLOR_TEMP
+    assert state.attributes["rgb_color"] == (202, 218, 255)
+    assert state.attributes["hs_color"] == (221.575, 20.9)
+    assert state.attributes["xy_color"] == (0.278, 0.287)
+
+    state = hass.states.get(entity3.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.HS]
+    assert state.attributes["color_mode"] == light.ColorMode.HS
+
+    state = hass.states.get(entity4.entity_id)
+    assert state.attributes["supported_color_modes"] == [
+        light.ColorMode.COLOR_TEMP,
+        light.ColorMode.HS,
+    ]
+    # hs color prioritized over color_temp, light should report mode ColorMode.HS
+    assert state.attributes["color_mode"] == light.ColorMode.HS
+
+
 async def test_light_service_call_rgbw(hass: HomeAssistant) -> None:
     """Test rgbw functionality in service calls."""
     entity0 = MockLight("Test_rgbw", STATE_ON)
@@ -1186,7 +1364,7 @@ async def test_light_state_off(hass: HomeAssistant) -> None:
         "color_mode": None,
         "friendly_name": "Test_onoff",
         "supported_color_modes": [light.ColorMode.ONOFF],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
     }
 
     state = hass.states.get(entity1.entity_id)
@@ -1194,7 +1372,7 @@ async def test_light_state_off(hass: HomeAssistant) -> None:
         "color_mode": None,
         "friendly_name": "Test_brightness",
         "supported_color_modes": [light.ColorMode.BRIGHTNESS],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
         "brightness": None,
     }
 
@@ -1203,7 +1381,7 @@ async def test_light_state_off(hass: HomeAssistant) -> None:
         "color_mode": None,
         "friendly_name": "Test_ct",
         "supported_color_modes": [light.ColorMode.COLOR_TEMP],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
         "brightness": None,
         "color_temp": None,
         "color_temp_kelvin": None,
@@ -1221,7 +1399,7 @@ async def test_light_state_off(hass: HomeAssistant) -> None:
         "color_mode": None,
         "friendly_name": "Test_rgbw",
         "supported_color_modes": [light.ColorMode.RGBW],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
         "brightness": None,
         "rgbw_color": None,
         "hs_color": None,
@@ -1252,7 +1430,7 @@ async def test_light_state_rgbw(hass: HomeAssistant) -> None:
         "color_mode": light.ColorMode.RGBW,
         "friendly_name": "Test_rgbw",
         "supported_color_modes": [light.ColorMode.RGBW],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
         "hs_color": (240.0, 25.0),
         "rgb_color": (3, 3, 4),
         "rgbw_color": (1, 2, 3, 4),
@@ -1283,7 +1461,7 @@ async def test_light_state_rgbww(hass: HomeAssistant) -> None:
         "color_mode": light.ColorMode.RGBWW,
         "friendly_name": "Test_rgbww",
         "supported_color_modes": [light.ColorMode.RGBWW],
-        "supported_features": light.LightEntityFeature(0),
+        "supported_features": 0,
         "hs_color": (60.0, 20.0),
         "rgb_color": (5, 5, 4),
         "rgbww_color": (1, 2, 3, 4, 5),
@@ -1299,6 +1477,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
         MockLight("Test_rgb", STATE_ON),
         MockLight("Test_xy", STATE_ON),
         MockLight("Test_all", STATE_ON),
+        MockLight("Test_legacy", STATE_ON),
         MockLight("Test_rgbw", STATE_ON),
         MockLight("Test_rgbww", STATE_ON),
         MockLight("Test_temperature", STATE_ON),
@@ -1322,13 +1501,19 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     }
 
     entity4 = entities[4]
-    entity4.supported_color_modes = {light.ColorMode.RGBW}
+    entity4.supported_features = light.SUPPORT_COLOR
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity4.supported_color_modes = None
+    entity4.color_mode = None
 
     entity5 = entities[5]
-    entity5.supported_color_modes = {light.ColorMode.RGBWW}
+    entity5.supported_color_modes = {light.ColorMode.RGBW}
 
     entity6 = entities[6]
-    entity6.supported_color_modes = {light.ColorMode.COLOR_TEMP}
+    entity6.supported_color_modes = {light.ColorMode.RGBWW}
+
+    entity7 = entities[7]
+    entity7.supported_color_modes = {light.ColorMode.COLOR_TEMP}
 
     assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
     await hass.async_block_till_done()
@@ -1350,12 +1535,15 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     ]
 
     state = hass.states.get(entity4.entity_id)
-    assert state.attributes["supported_color_modes"] == [light.ColorMode.RGBW]
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.HS]
 
     state = hass.states.get(entity5.entity_id)
-    assert state.attributes["supported_color_modes"] == [light.ColorMode.RGBWW]
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.RGBW]
 
     state = hass.states.get(entity6.entity_id)
+    assert state.attributes["supported_color_modes"] == [light.ColorMode.RGBWW]
+
+    state = hass.states.get(entity7.entity_id)
     assert state.attributes["supported_color_modes"] == [light.ColorMode.COLOR_TEMP]
 
     await hass.services.async_call(
@@ -1370,6 +1558,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 100,
             "hs_color": (240, 100),
@@ -1385,10 +1574,12 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 255, "hs_color": (240.0, 100.0)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 255, "rgbw_color": (0, 0, 255, 0)}
+    assert data == {"brightness": 255, "hs_color": (240.0, 100.0)}
     _, data = entity5.last_call("turn_on")
-    assert data == {"brightness": 255, "rgbww_color": (0, 0, 255, 0, 0)}
+    assert data == {"brightness": 255, "rgbw_color": (0, 0, 255, 0)}
     _, data = entity6.last_call("turn_on")
+    assert data == {"brightness": 255, "rgbww_color": (0, 0, 255, 0, 0)}
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 255, "color_temp_kelvin": 1739, "color_temp": 575}
 
     await hass.services.async_call(
@@ -1403,6 +1594,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 100,
             "hs_color": (240, 0),
@@ -1418,11 +1610,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 255, "hs_color": (240.0, 0.0)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)}
+    assert data == {"brightness": 255, "hs_color": (240.0, 0.0)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)}
+    _, data = entity6.last_call("turn_on")
     # The midpoint of the white channels is warm, compensated by adding green + blue
     assert data == {"brightness": 255, "rgbww_color": (0, 76, 141, 255, 255)}
-    _, data = entity6.last_call("turn_on")
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 255, "color_temp_kelvin": 5962, "color_temp": 167}
 
     await hass.services.async_call(
@@ -1437,6 +1631,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgb_color": (128, 0, 0),
@@ -1451,12 +1646,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     assert data == {"brightness": 128, "xy_color": (0.701, 0.299)}
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (128, 0, 0)}
-
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 0)}
+    assert data == {"brightness": 128, "hs_color": (0.0, 100.0)}
     _, data = entity5.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 0, 0)}
+    assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 0)}
     _, data = entity6.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 0, 0)}
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 6279, "color_temp": 159}
 
     await hass.services.async_call(
@@ -1471,6 +1667,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgb_color": (255, 255, 255),
@@ -1486,11 +1683,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (255, 255, 255)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (0, 0, 0, 255)}
+    assert data == {"brightness": 128, "hs_color": (0.0, 0.0)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbw_color": (0, 0, 0, 255)}
+    _, data = entity6.last_call("turn_on")
     # The midpoint the white channels is warm, compensated by adding green + blue
     assert data == {"brightness": 128, "rgbww_color": (0, 76, 141, 255, 255)}
-    _, data = entity6.last_call("turn_on")
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 5962, "color_temp": 167}
 
     await hass.services.async_call(
@@ -1505,6 +1704,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "xy_color": (0.1, 0.8),
@@ -1520,10 +1720,12 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "xy_color": (0.1, 0.8)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (0, 255, 22, 0)}
+    assert data == {"brightness": 128, "hs_color": (125.176, 100.0)}
     _, data = entity5.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbww_color": (0, 255, 22, 0, 0)}
+    assert data == {"brightness": 128, "rgbw_color": (0, 255, 22, 0)}
     _, data = entity6.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbww_color": (0, 255, 22, 0, 0)}
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 8645, "color_temp": 115}
 
     await hass.services.async_call(
@@ -1538,6 +1740,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "xy_color": (0.323, 0.329),
@@ -1553,11 +1756,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "xy_color": (0.323, 0.329)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (1, 0, 0, 255)}
+    assert data == {"brightness": 128, "hs_color": (0.0, 0.392)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbw_color": (1, 0, 0, 255)}
+    _, data = entity6.last_call("turn_on")
     # The midpoint the white channels is warm, compensated by adding green + blue
     assert data == {"brightness": 128, "rgbww_color": (0, 75, 140, 255, 255)}
-    _, data = entity6.last_call("turn_on")
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 5962, "color_temp": 167}
 
     await hass.services.async_call(
@@ -1572,6 +1777,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgbw_color": (128, 0, 0, 64),
@@ -1587,11 +1793,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (128, 43, 43)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 64)}
+    assert data == {"brightness": 128, "hs_color": (0.0, 66.406)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 64)}
+    _, data = entity6.last_call("turn_on")
     # The midpoint the white channels is warm, compensated by adding green + blue
     assert data == {"brightness": 128, "rgbww_color": (128, 0, 30, 117, 117)}
-    _, data = entity6.last_call("turn_on")
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 3011, "color_temp": 332}
 
     await hass.services.async_call(
@@ -1606,6 +1814,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgbw_color": (255, 255, 255, 255),
@@ -1621,11 +1830,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (255, 255, 255)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (255, 255, 255, 255)}
+    assert data == {"brightness": 128, "hs_color": (0.0, 0.0)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbw_color": (255, 255, 255, 255)}
+    _, data = entity6.last_call("turn_on")
     # The midpoint the white channels is warm, compensated by adding green + blue
     assert data == {"brightness": 128, "rgbww_color": (0, 76, 141, 255, 255)}
-    _, data = entity6.last_call("turn_on")
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 5962, "color_temp": 167}
 
     await hass.services.async_call(
@@ -1640,6 +1851,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgbww_color": (128, 0, 0, 64, 32),
@@ -1655,10 +1867,12 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (128, 33, 26)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbw_color": (128, 9, 0, 33)}
+    assert data == {"brightness": 128, "hs_color": (4.118, 79.688)}
     _, data = entity5.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 64, 32)}
+    assert data == {"brightness": 128, "rgbw_color": (128, 9, 0, 33)}
     _, data = entity6.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 64, 32)}
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 3845, "color_temp": 260}
 
     await hass.services.async_call(
@@ -1673,6 +1887,7 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
                 entity4.entity_id,
                 entity5.entity_id,
                 entity6.entity_id,
+                entity7.entity_id,
             ],
             "brightness_pct": 50,
             "rgbww_color": (255, 255, 255, 255, 255),
@@ -1688,11 +1903,13 @@ async def test_light_service_call_color_conversion(hass: HomeAssistant) -> None:
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 128, "rgb_color": (255, 217, 185)}
     _, data = entity4.last_call("turn_on")
+    assert data == {"brightness": 128, "hs_color": (27.429, 27.451)}
+    _, data = entity5.last_call("turn_on")
     # The midpoint the white channels is warm, compensated by decreasing green + blue
     assert data == {"brightness": 128, "rgbw_color": (96, 44, 0, 255)}
-    _, data = entity5.last_call("turn_on")
-    assert data == {"brightness": 128, "rgbww_color": (255, 255, 255, 255, 255)}
     _, data = entity6.last_call("turn_on")
+    assert data == {"brightness": 128, "rgbww_color": (255, 255, 255, 255, 255)}
+    _, data = entity7.last_call("turn_on")
     assert data == {"brightness": 128, "color_temp_kelvin": 3451, "color_temp": 289}
 
 
@@ -1705,6 +1922,7 @@ async def test_light_service_call_color_conversion_named_tuple(
         MockLight("Test_rgb", STATE_ON),
         MockLight("Test_xy", STATE_ON),
         MockLight("Test_all", STATE_ON),
+        MockLight("Test_legacy", STATE_ON),
         MockLight("Test_rgbw", STATE_ON),
         MockLight("Test_rgbww", STATE_ON),
     ]
@@ -1727,10 +1945,16 @@ async def test_light_service_call_color_conversion_named_tuple(
     }
 
     entity4 = entities[4]
-    entity4.supported_color_modes = {light.ColorMode.RGBW}
+    entity4.supported_features = light.SUPPORT_COLOR
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity4.supported_color_modes = None
+    entity4.color_mode = None
 
     entity5 = entities[5]
-    entity5.supported_color_modes = {light.ColorMode.RGBWW}
+    entity5.supported_color_modes = {light.ColorMode.RGBW}
+
+    entity6 = entities[6]
+    entity6.supported_color_modes = {light.ColorMode.RGBWW}
 
     assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
     await hass.async_block_till_done()
@@ -1746,6 +1970,7 @@ async def test_light_service_call_color_conversion_named_tuple(
                 entity3.entity_id,
                 entity4.entity_id,
                 entity5.entity_id,
+                entity6.entity_id,
             ],
             "brightness_pct": 25,
             "rgb_color": color_util.RGBColor(128, 0, 0),
@@ -1761,8 +1986,10 @@ async def test_light_service_call_color_conversion_named_tuple(
     _, data = entity3.last_call("turn_on")
     assert data == {"brightness": 64, "rgb_color": (128, 0, 0)}
     _, data = entity4.last_call("turn_on")
-    assert data == {"brightness": 64, "rgbw_color": (128, 0, 0, 0)}
+    assert data == {"brightness": 64, "hs_color": (0.0, 100.0)}
     _, data = entity5.last_call("turn_on")
+    assert data == {"brightness": 64, "rgbw_color": (128, 0, 0, 0)}
+    _, data = entity6.last_call("turn_on")
     assert data == {"brightness": 64, "rgbww_color": (128, 0, 0, 0, 0)}
 
 
@@ -2131,6 +2358,13 @@ async def test_light_state_color_conversion(hass: HomeAssistant) -> None:
     entity2.rgb_color = "Invalid"  # Should be ignored
     entity2.xy_color = (0.1, 0.8)
 
+    entity3 = entities[3]
+    entity3.hs_color = (240, 100)
+    entity3.supported_features = light.SUPPORT_COLOR
+    # Set color modes to none to trigger backwards compatibility in LightEntity
+    entity3.supported_color_modes = None
+    entity3.color_mode = None
+
     assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
     await hass.async_block_till_done()
 
@@ -2152,6 +2386,12 @@ async def test_light_state_color_conversion(hass: HomeAssistant) -> None:
     assert state.attributes["rgb_color"] == (0, 255, 22)
     assert state.attributes["xy_color"] == (0.1, 0.8)
 
+    state = hass.states.get(entity3.entity_id)
+    assert state.attributes["color_mode"] == light.ColorMode.HS
+    assert state.attributes["hs_color"] == (240, 100)
+    assert state.attributes["rgb_color"] == (0, 0, 255)
+    assert state.attributes["xy_color"] == (0.136, 0.04)
+
 
 async def test_services_filter_parameters(
     hass: HomeAssistant,
@@ -2386,6 +2626,27 @@ def test_filter_supported_color_modes() -> None:
     assert light.filter_supported_color_modes(supported) == {light.ColorMode.BRIGHTNESS}
 
 
+def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
+    """Test deprecated supported features ints."""
+
+    class MockLightEntityEntity(light.LightEntity):
+        @property
+        def supported_features(self) -> int:
+            """Return supported features."""
+            return 1
+
+    entity = MockLightEntityEntity()
+    assert entity.supported_features_compat is light.LightEntityFeature(1)
+    assert "MockLightEntityEntity" in caplog.text
+    assert "is using deprecated supported features values" in caplog.text
+    assert "Instead it should use" in caplog.text
+    assert "LightEntityFeature" in caplog.text
+    assert "and color modes" in caplog.text
+    caplog.clear()
+    assert entity.supported_features_compat is light.LightEntityFeature(1)
+    assert "is using deprecated supported features values" not in caplog.text
+
+
 @pytest.mark.parametrize(
     ("color_mode", "supported_color_modes", "warning_expected"),
     [

From 7a5525951d9f52b5766b3a724041ca70eaeeb8ee Mon Sep 17 00:00:00 2001
From: Franck Nijhof <git@frenck.dev>
Date: Mon, 6 Jan 2025 23:42:21 +0000
Subject: [PATCH 46/47] Bump version to 2025.1.1

---
 homeassistant/const.py | 2 +-
 pyproject.toml         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/homeassistant/const.py b/homeassistant/const.py
index 5a088d364497d1..e641ae4254c3c6 100644
--- a/homeassistant/const.py
+++ b/homeassistant/const.py
@@ -25,7 +25,7 @@
 APPLICATION_NAME: Final = "HomeAssistant"
 MAJOR_VERSION: Final = 2025
 MINOR_VERSION: Final = 1
-PATCH_VERSION: Final = "0"
+PATCH_VERSION: Final = "1"
 __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
 __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
 REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
diff --git a/pyproject.toml b/pyproject.toml
index c87e499155c313..f94d54feb88528 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name        = "homeassistant"
-version     = "2025.1.0"
+version     = "2025.1.1"
 license     = {text = "Apache-2.0"}
 description = "Open-source home automation platform running on Python 3."
 readme      = "README.rst"

From 298f05948808cbf5390c3bd16b004d23f08b453c Mon Sep 17 00:00:00 2001
From: Franck Nijhof <git@frenck.dev>
Date: Tue, 7 Jan 2025 00:08:02 +0100
Subject: [PATCH 47/47] Revert "Remove deprecated supported features warning in
 ..." (multiple) (#134933)

---
 homeassistant/components/camera/__init__.py   | 26 ++++++++--
 homeassistant/components/cover/__init__.py    |  4 ++
 .../components/media_player/__init__.py       | 51 ++++++++++++-------
 homeassistant/components/vacuum/__init__.py   | 17 ++++++-
 homeassistant/helpers/entity.py               | 27 +++++++++-
 tests/components/camera/test_init.py          | 20 ++++++++
 tests/components/cover/test_init.py           | 19 +++++++
 tests/components/media_player/test_init.py    | 22 +++++++-
 tests/components/vacuum/test_init.py          | 36 +++++++++++++
 tests/helpers/test_entity.py                  | 26 ++++++++++
 10 files changed, 221 insertions(+), 27 deletions(-)

diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py
index 725fc84adc341b..4d718433fca2d7 100644
--- a/homeassistant/components/camera/__init__.py
+++ b/homeassistant/components/camera/__init__.py
@@ -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."""
@@ -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
@@ -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:
@@ -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)
@@ -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)
@@ -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()
diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py
index 9ce526712f0363..001bff51991be1 100644
--- a/homeassistant/components/cover/__init__.py
+++ b/homeassistant/components/cover/__init__.py
@@ -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 = (
diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py
index e7bbe1d19bd29f..291b1ec1e2a1ca 100644
--- a/homeassistant/components/media_player/__init__.py
+++ b/homeassistant/components/media_player/__init__.py
@@ -773,6 +773,19 @@ def supported_features(self) -> MediaPlayerEntityFeature:
         """Flag media player features that are supported."""
         return self._attr_supported_features
 
+    @property
+    def supported_features_compat(self) -> MediaPlayerEntityFeature:
+        """Return the supported features as MediaPlayerEntityFeature.
+
+        Remove this compatibility shim in 2025.1 or later.
+        """
+        features = self.supported_features
+        if type(features) is int:  # noqa: E721
+            new_features = MediaPlayerEntityFeature(features)
+            self._report_deprecated_supported_features_values(new_features)
+            return new_features
+        return features
+
     def turn_on(self) -> None:
         """Turn the media player on."""
         raise NotImplementedError
@@ -912,85 +925,87 @@ async def async_set_repeat(self, repeat: RepeatMode) -> None:
     @property
     def support_play(self) -> bool:
         """Boolean if play is supported."""
-        return MediaPlayerEntityFeature.PLAY in self.supported_features
+        return MediaPlayerEntityFeature.PLAY in self.supported_features_compat
 
     @final
     @property
     def support_pause(self) -> bool:
         """Boolean if pause is supported."""
-        return MediaPlayerEntityFeature.PAUSE in self.supported_features
+        return MediaPlayerEntityFeature.PAUSE in self.supported_features_compat
 
     @final
     @property
     def support_stop(self) -> bool:
         """Boolean if stop is supported."""
-        return MediaPlayerEntityFeature.STOP in self.supported_features
+        return MediaPlayerEntityFeature.STOP in self.supported_features_compat
 
     @final
     @property
     def support_seek(self) -> bool:
         """Boolean if seek is supported."""
-        return MediaPlayerEntityFeature.SEEK in self.supported_features
+        return MediaPlayerEntityFeature.SEEK in self.supported_features_compat
 
     @final
     @property
     def support_volume_set(self) -> bool:
         """Boolean if setting volume is supported."""
-        return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
+        return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
 
     @final
     @property
     def support_volume_mute(self) -> bool:
         """Boolean if muting volume is supported."""
-        return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features
+        return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features_compat
 
     @final
     @property
     def support_previous_track(self) -> bool:
         """Boolean if previous track command supported."""
-        return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features
+        return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features_compat
 
     @final
     @property
     def support_next_track(self) -> bool:
         """Boolean if next track command supported."""
-        return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features
+        return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features_compat
 
     @final
     @property
     def support_play_media(self) -> bool:
         """Boolean if play media command supported."""
-        return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features
+        return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features_compat
 
     @final
     @property
     def support_select_source(self) -> bool:
         """Boolean if select source command supported."""
-        return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features
+        return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features_compat
 
     @final
     @property
     def support_select_sound_mode(self) -> bool:
         """Boolean if select sound mode command supported."""
-        return MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features
+        return (
+            MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features_compat
+        )
 
     @final
     @property
     def support_clear_playlist(self) -> bool:
         """Boolean if clear playlist command supported."""
-        return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features
+        return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features_compat
 
     @final
     @property
     def support_shuffle_set(self) -> bool:
         """Boolean if shuffle is supported."""
-        return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features
+        return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features_compat
 
     @final
     @property
     def support_grouping(self) -> bool:
         """Boolean if player grouping is supported."""
-        return MediaPlayerEntityFeature.GROUPING in self.supported_features
+        return MediaPlayerEntityFeature.GROUPING in self.supported_features_compat
 
     async def async_toggle(self) -> None:
         """Toggle the power on the media player."""
@@ -1019,7 +1034,7 @@ async def async_volume_up(self) -> None:
         if (
             self.volume_level is not None
             and self.volume_level < 1
-            and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
+            and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
         ):
             await self.async_set_volume_level(
                 min(1, self.volume_level + self.volume_step)
@@ -1037,7 +1052,7 @@ async def async_volume_down(self) -> None:
         if (
             self.volume_level is not None
             and self.volume_level > 0
-            and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
+            and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
         ):
             await self.async_set_volume_level(
                 max(0, self.volume_level - self.volume_step)
@@ -1080,7 +1095,7 @@ def media_image_local(self) -> str | None:
     def capability_attributes(self) -> dict[str, Any]:
         """Return capability attributes."""
         data: dict[str, Any] = {}
-        supported_features = self.supported_features
+        supported_features = self.supported_features_compat
 
         if (
             source_list := self.source_list
@@ -1286,7 +1301,7 @@ async def websocket_browse_media(
         connection.send_error(msg["id"], "entity_not_found", "Entity not found")
         return
 
-    if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features:
+    if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features_compat:
         connection.send_message(
             websocket_api.error_message(
                 msg["id"], ERR_NOT_SUPPORTED, "Player does not support browsing media"
diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py
index 46e35bb3e1108e..6fe2c3e2a5b3da 100644
--- a/homeassistant/components/vacuum/__init__.py
+++ b/homeassistant/components/vacuum/__init__.py
@@ -312,7 +312,7 @@ def battery_icon(self) -> str:
     @property
     def capability_attributes(self) -> dict[str, Any] | None:
         """Return capability attributes."""
-        if VacuumEntityFeature.FAN_SPEED in self.supported_features:
+        if VacuumEntityFeature.FAN_SPEED in self.supported_features_compat:
             return {ATTR_FAN_SPEED_LIST: self.fan_speed_list}
         return None
 
@@ -330,7 +330,7 @@ def fan_speed_list(self) -> list[str]:
     def state_attributes(self) -> dict[str, Any]:
         """Return the state attributes of the vacuum cleaner."""
         data: dict[str, Any] = {}
-        supported_features = self.supported_features
+        supported_features = self.supported_features_compat
 
         if VacuumEntityFeature.BATTERY in supported_features:
             data[ATTR_BATTERY_LEVEL] = self.battery_level
@@ -369,6 +369,19 @@ def supported_features(self) -> VacuumEntityFeature:
         """Flag vacuum cleaner features that are supported."""
         return self._attr_supported_features
 
+    @property
+    def supported_features_compat(self) -> VacuumEntityFeature:
+        """Return the supported features as VacuumEntityFeature.
+
+        Remove this compatibility shim in 2025.1 or later.
+        """
+        features = self.supported_features
+        if type(features) is int:  # noqa: E721
+            new_features = VacuumEntityFeature(features)
+            self._report_deprecated_supported_features_values(new_features)
+            return new_features
+        return features
+
     def stop(self, **kwargs: Any) -> None:
         """Stop the vacuum cleaner."""
         raise NotImplementedError
diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py
index 91845cdf5214d3..19076c4edc0003 100644
--- a/homeassistant/helpers/entity.py
+++ b/homeassistant/helpers/entity.py
@@ -7,7 +7,7 @@
 from collections import deque
 from collections.abc import Callable, Coroutine, Iterable, Mapping
 import dataclasses
-from enum import Enum, auto
+from enum import Enum, IntFlag, auto
 import functools as ft
 import logging
 import math
@@ -1639,6 +1639,31 @@ def _suggest_report_issue(self) -> str:
             self.hass, integration_domain=platform_name, module=type(self).__module__
         )
 
+    @callback
+    def _report_deprecated_supported_features_values(
+        self, replacement: IntFlag
+    ) -> None:
+        """Report deprecated supported features values."""
+        if self._deprecated_supported_features_reported is True:
+            return
+        self._deprecated_supported_features_reported = True
+        report_issue = self._suggest_report_issue()
+        report_issue += (
+            " and reference "
+            "https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
+        )
+        _LOGGER.warning(
+            (
+                "Entity %s (%s) is using deprecated supported features"
+                " values which will be removed in HA Core 2025.1. Instead it should use"
+                " %s, please %s"
+            ),
+            self.entity_id,
+            type(self),
+            repr(replacement),
+            report_issue,
+        )
+
 
 class ToggleEntityDescription(EntityDescription, frozen_or_thawed=True):
     """A class that describes toggle entities."""
diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py
index a3045e27cf1db3..32520fcad23cb9 100644
--- a/tests/components/camera/test_init.py
+++ b/tests/components/camera/test_init.py
@@ -826,6 +826,26 @@ def test_deprecated_state_constants(
     import_and_test_deprecated_constant_enum(caplog, module, enum, "STATE_", "2025.10")
 
 
+def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
+    """Test deprecated supported features ints."""
+
+    class MockCamera(camera.Camera):
+        @property
+        def supported_features(self) -> int:
+            """Return supported features."""
+            return 1
+
+    entity = MockCamera()
+    assert entity.supported_features_compat is camera.CameraEntityFeature(1)
+    assert "MockCamera" in caplog.text
+    assert "is using deprecated supported features values" in caplog.text
+    assert "Instead it should use" in caplog.text
+    assert "CameraEntityFeature.ON_OFF" in caplog.text
+    caplog.clear()
+    assert entity.supported_features_compat is camera.CameraEntityFeature(1)
+    assert "is using deprecated supported features values" not in caplog.text
+
+
 @pytest.mark.usefixtures("mock_camera")
 async def test_entity_picture_url_changes_on_token_update(hass: HomeAssistant) -> None:
     """Test the token is rotated and entity entity picture cache is cleared."""
diff --git a/tests/components/cover/test_init.py b/tests/components/cover/test_init.py
index e43b64b16a79ad..646c44e4ac2879 100644
--- a/tests/components/cover/test_init.py
+++ b/tests/components/cover/test_init.py
@@ -2,6 +2,8 @@
 
 from enum import Enum
 
+import pytest
+
 from homeassistant.components import cover
 from homeassistant.components.cover import CoverState
 from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM, SERVICE_TOGGLE
@@ -153,3 +155,20 @@ def _create_tuples(enum: type[Enum], constant_prefix: str) -> list[tuple[Enum, s
 def test_all() -> None:
     """Test module.__all__ is correctly set."""
     help_test_all(cover)
+
+
+def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
+    """Test deprecated supported features ints."""
+
+    class MockCoverEntity(cover.CoverEntity):
+        _attr_supported_features = 1
+
+    entity = MockCoverEntity()
+    assert entity.supported_features is cover.CoverEntityFeature(1)
+    assert "MockCoverEntity" in caplog.text
+    assert "is using deprecated supported features values" in caplog.text
+    assert "Instead it should use" in caplog.text
+    assert "CoverEntityFeature.OPEN" in caplog.text
+    caplog.clear()
+    assert entity.supported_features is cover.CoverEntityFeature(1)
+    assert "is using deprecated supported features values" not in caplog.text
diff --git a/tests/components/media_player/test_init.py b/tests/components/media_player/test_init.py
index 7c64f846df1651..a45fa5b6668749 100644
--- a/tests/components/media_player/test_init.py
+++ b/tests/components/media_player/test_init.py
@@ -129,7 +129,7 @@ def test_support_properties(property_suffix: str) -> None:
     entity3 = MediaPlayerEntity()
     entity3._attr_supported_features = feature
     entity4 = MediaPlayerEntity()
-    entity4._attr_supported_features = all_features & ~feature
+    entity4._attr_supported_features = all_features - feature
 
     assert getattr(entity1, f"support_{property_suffix}") is False
     assert getattr(entity2, f"support_{property_suffix}") is True
@@ -447,3 +447,23 @@ async def test_get_async_get_browse_image_quoting(
         url = player.get_browse_image_url("album", media_content_id)
         await client.get(url)
         mock_browse_image.assert_called_with("album", media_content_id, None)
+
+
+def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
+    """Test deprecated supported features ints."""
+
+    class MockMediaPlayerEntity(MediaPlayerEntity):
+        @property
+        def supported_features(self) -> int:
+            """Return supported features."""
+            return 1
+
+    entity = MockMediaPlayerEntity()
+    assert entity.supported_features_compat is MediaPlayerEntityFeature(1)
+    assert "MockMediaPlayerEntity" in caplog.text
+    assert "is using deprecated supported features values" in caplog.text
+    assert "Instead it should use" in caplog.text
+    assert "MediaPlayerEntityFeature.PAUSE" in caplog.text
+    caplog.clear()
+    assert entity.supported_features_compat is MediaPlayerEntityFeature(1)
+    assert "is using deprecated supported features values" not in caplog.text
diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py
index db6cd242f3fa95..8babd9fa2659e2 100644
--- a/tests/components/vacuum/test_init.py
+++ b/tests/components/vacuum/test_init.py
@@ -272,6 +272,42 @@ def send_command(
     assert "test" in strings
 
 
+async def test_supported_features_compat(hass: HomeAssistant) -> None:
+    """Test StateVacuumEntity using deprecated feature constants features."""
+
+    features = (
+        VacuumEntityFeature.BATTERY
+        | VacuumEntityFeature.FAN_SPEED
+        | VacuumEntityFeature.START
+        | VacuumEntityFeature.STOP
+        | VacuumEntityFeature.PAUSE
+    )
+
+    class _LegacyConstantsStateVacuum(StateVacuumEntity):
+        _attr_supported_features = int(features)
+        _attr_fan_speed_list = ["silent", "normal", "pet hair"]
+
+    entity = _LegacyConstantsStateVacuum()
+    assert isinstance(entity.supported_features, int)
+    assert entity.supported_features == int(features)
+    assert entity.supported_features_compat is (
+        VacuumEntityFeature.BATTERY
+        | VacuumEntityFeature.FAN_SPEED
+        | VacuumEntityFeature.START
+        | VacuumEntityFeature.STOP
+        | VacuumEntityFeature.PAUSE
+    )
+    assert entity.state_attributes == {
+        "battery_level": None,
+        "battery_icon": "mdi:battery-unknown",
+        "fan_speed": None,
+    }
+    assert entity.capability_attributes == {
+        "fan_speed_list": ["silent", "normal", "pet hair"]
+    }
+    assert entity._deprecated_supported_features_reported
+
+
 async def test_vacuum_not_log_deprecated_state_warning(
     hass: HomeAssistant,
     mock_vacuum_entity: MockVacuum,
diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py
index dc579ab6e8d76f..2bf441f70fd22b 100644
--- a/tests/helpers/test_entity.py
+++ b/tests/helpers/test_entity.py
@@ -4,6 +4,7 @@
 from collections.abc import Iterable
 import dataclasses
 from datetime import timedelta
+from enum import IntFlag
 import logging
 import threading
 from typing import Any
@@ -2485,6 +2486,31 @@ def _attr_attribution(self):
                 return "🤡"
 
 
+async def test_entity_report_deprecated_supported_features_values(
+    caplog: pytest.LogCaptureFixture,
+) -> None:
+    """Test reporting deprecated supported feature values only happens once."""
+    ent = entity.Entity()
+
+    class MockEntityFeatures(IntFlag):
+        VALUE1 = 1
+        VALUE2 = 2
+
+    ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
+    assert (
+        "is using deprecated supported features values which will be removed"
+        in caplog.text
+    )
+    assert "MockEntityFeatures.VALUE2" in caplog.text
+
+    caplog.clear()
+    ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
+    assert (
+        "is using deprecated supported features values which will be removed"
+        not in caplog.text
+    )
+
+
 async def test_remove_entity_registry(
     hass: HomeAssistant, entity_registry: er.EntityRegistry
 ) -> None: