From 9f74471d22ab47c3d45fefd298d4d533261bc52b Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 19 Oct 2025 12:40:33 +0200 Subject: [PATCH 1/9] Rename the Shelly switch from `Start Charging` to `Charging` (#154815) --- homeassistant/components/shelly/switch.py | 1 + tests/components/shelly/test_switch.py | 31 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 2f6c76995f15c7..681d80ec49eb27 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -143,6 +143,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription): "boolean_start_charging": RpcSwitchDescription( key="boolean", sub_key="value", + name="Charging", is_on=lambda status: bool(status["value"]), method_on="boolean_set", method_off="boolean_set", diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index 9daf557cf4527b..babb15b1123042 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -14,6 +14,7 @@ from homeassistant.components.shelly.const import ( DOMAIN, ENTRY_RELOAD_COOLDOWN, + MODEL_TOP_EV_CHARGER_EVE01, MODEL_WALL_DISPLAY, MOTION_MODELS, ) @@ -954,3 +955,33 @@ async def test_cury_switch_availability( assert (state := hass.states.get(entity_id)) assert state.state == STATE_ON + + +async def test_rpc_ev_charging_switch( + hass: HomeAssistant, + mock_rpc_device: Mock, + monkeypatch: pytest.MonkeyPatch, + entity_registry: EntityRegistry, +) -> None: + """Test the charging switch for EV charger.""" + config = deepcopy(mock_rpc_device.config) + config["boolean:200"] = { + "name": "Start Charging", + "meta": {"ui": {"view": "toggle"}}, + "role": "start_charging", + } + monkeypatch.setattr(mock_rpc_device, "config", config) + + status = deepcopy(mock_rpc_device.status) + status["boolean:200"] = {"value": False} + monkeypatch.setattr(mock_rpc_device, "status", status) + + entity_id = "switch.test_name_charging" + + await init_integration(hass, 3, model=MODEL_TOP_EV_CHARGER_EVE01) + + assert (state := hass.states.get(entity_id)) + assert state.state == STATE_OFF + + assert (entry := entity_registry.async_get(entity_id)) + assert entry.unique_id == "123456789ABC-boolean:200-boolean_start_charging" From 05277aa708d39ae89d5b72c67e72818ff6a3d7b7 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 19 Oct 2025 12:51:18 +0200 Subject: [PATCH 2/9] Fix Shelly enum sensors (#154814) --- homeassistant/components/shelly/entity.py | 1 - homeassistant/components/shelly/sensor.py | 25 ++++++++----------- .../shelly/snapshots/test_sensor.ambr | 2 +- tests/components/shelly/test_sensor.py | 14 +++++------ 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index f9eb51c2ac8d7e..b350407f117ce1 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -323,7 +323,6 @@ class RpcEntityDescription(EntityDescription): use_polling_coordinator: bool = False supported: Callable = lambda _: False unit: Callable[[dict], str | None] | None = None - options_fn: Callable[[dict], list[str]] | None = None entity_class: Callable | None = None role: str | None = None models: set[str] | None = None diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index ec1abd415ae699..c4ee99faa9170d 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -104,7 +104,10 @@ def __init__( super().__init__(coordinator, key, attribute, description) if self.option_map: - self._attr_options = list(self.option_map.values()) + if description.role == ROLE_GENERIC: + self._attr_options = list(self.option_map.values()) + else: + self._attr_options = list(self.option_map) @property def native_value(self) -> StateType: @@ -117,7 +120,10 @@ def native_value(self) -> StateType: if not isinstance(attribute_value, str): return None - return self.option_map[attribute_value] + if self.entity_description.role == ROLE_GENERIC: + return self.option_map[attribute_value] + + return attribute_value class RpcEnergyConsumedSensor(RpcSensor): @@ -1442,7 +1448,6 @@ def __init__( removal_condition=lambda config, _, key: not is_view_for_platform( config, key, SENSOR_PLATFORM ), - options_fn=lambda config: config["options"], device_class=SensorDeviceClass.ENUM, role=ROLE_GENERIC, ), @@ -1532,21 +1537,11 @@ def __init__( state_class=SensorStateClass.MEASUREMENT, role="water_temperature", ), - "number_work_state": RpcSensorDescription( - key="number", + "enum_work_state": RpcSensorDescription( + key="enum", sub_key="value", translation_key="charger_state", device_class=SensorDeviceClass.ENUM, - options=[ - "charger_charging", - "charger_end", - "charger_fault", - "charger_free", - "charger_free_fault", - "charger_insert", - "charger_pause", - "charger_wait", - ], role="work_state", ), "number_energy_charge": RpcSensorDescription( diff --git a/tests/components/shelly/snapshots/test_sensor.ambr b/tests/components/shelly/snapshots/test_sensor.ambr index 11892f586a6612..93be3c9103e712 100644 --- a/tests/components/shelly/snapshots/test_sensor.ambr +++ b/tests/components/shelly/snapshots/test_sensor.ambr @@ -399,7 +399,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'charger_state', - 'unique_id': '123456789ABC-number:200-number_work_state', + 'unique_id': '123456789ABC-enum:200-enum_work_state', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index 640d7439eb9f3b..3aace4a88170dc 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -1693,7 +1693,7 @@ async def test_rpc_shelly_ev_sensors( ) -> None: """Test Shelly EV sensors.""" config = deepcopy(mock_rpc_device.config) - config["number:200"] = { + config["enum:200"] = { "name": "Charger state", "meta": { "ui": { @@ -1711,14 +1711,14 @@ async def test_rpc_shelly_ev_sensors( } }, "options": [ - "charger_free", - "charger_insert", - "charger_free_fault", - "charger_wait", "charger_charging", - "charger_pause", "charger_end", "charger_fault", + "charger_free", + "charger_free_fault", + "charger_insert", + "charger_pause", + "charger_wait", ], "role": "work_state", } @@ -1735,7 +1735,7 @@ async def test_rpc_shelly_ev_sensors( monkeypatch.setattr(mock_rpc_device, "config", config) status = deepcopy(mock_rpc_device.status) - status["number:200"] = {"value": "charger_charging"} + status["enum:200"] = {"value": "charger_charging"} status["number:201"] = {"value": 5.0} status["number:202"] = {"value": 60} monkeypatch.setattr(mock_rpc_device, "status", status) From 71f94cad975ba3d24987a81a0b554468088345b8 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sun, 19 Oct 2025 13:54:25 +0300 Subject: [PATCH 3/9] Fix Todoist test failure (#154808) --- tests/components/todoist/test_calendar.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/components/todoist/test_calendar.py b/tests/components/todoist/test_calendar.py index e342df5f6b79e6..cfd8f55df2d2e8 100644 --- a/tests/components/todoist/test_calendar.py +++ b/tests/components/todoist/test_calendar.py @@ -7,6 +7,7 @@ import urllib import zoneinfo +from freezegun import freeze_time from freezegun.api import FrozenDateTimeFactory import pytest from todoist_api_python.models import Due @@ -38,6 +39,13 @@ TIMEZONE = zoneinfo.ZoneInfo(TZ_NAME) +@pytest.fixture(autouse=True) +def freeze_the_time(): + """Freeze the time.""" + with freeze_time("2024-05-24 12:00:00"): + yield + + @pytest.fixture(autouse=True) def platforms() -> list[Platform]: """Override platforms.""" From fcd07902b0119384b72422e2e99f03d906b020fd Mon Sep 17 00:00:00 2001 From: tronikos Date: Sun, 19 Oct 2025 03:55:54 -0700 Subject: [PATCH 4/9] Bump opower to 0.15.8 (#154811) --- homeassistant/components/opower/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index 7295a41da0f451..175fdfe63cd172 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["opower"], "quality_scale": "bronze", - "requirements": ["opower==0.15.7"] + "requirements": ["opower==0.15.8"] } diff --git a/requirements_all.txt b/requirements_all.txt index 874e06233b9a9f..af542128866054 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1664,7 +1664,7 @@ openwrt-luci-rpc==1.1.17 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.15.7 +opower==0.15.8 # homeassistant.components.oralb oralb-ble==0.17.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 020072a36d4ed6..f6390e52daff16 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1423,7 +1423,7 @@ openrgb-python==0.3.5 openwebifpy==4.3.1 # homeassistant.components.opower -opower==0.15.7 +opower==0.15.8 # homeassistant.components.oralb oralb-ble==0.17.6 From 2eb3360e8c5ff3efdbd74e50e6f164337158df00 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 01:04:23 -1000 Subject: [PATCH 5/9] Bump aioesphomeapi to 42.2.0 (#154803) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index e47fdfe08147f0..34c6f45de72f42 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -17,7 +17,7 @@ "mqtt": ["esphome/discover/#"], "quality_scale": "platinum", "requirements": [ - "aioesphomeapi==42.1.0", + "aioesphomeapi==42.2.0", "esphome-dashboard-api==1.3.0", "bleak-esphome==3.4.0" ], diff --git a/requirements_all.txt b/requirements_all.txt index af542128866054..11da225da1d2cf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -256,7 +256,7 @@ aioelectricitymaps==1.1.1 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==42.1.0 +aioesphomeapi==42.2.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f6390e52daff16..e51d6fbb57f9b5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -244,7 +244,7 @@ aioelectricitymaps==1.1.1 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==42.1.0 +aioesphomeapi==42.2.0 # homeassistant.components.flo aioflo==2021.11.0 From 41c95247ec4d8c4d65cfe1e9ae487d32f098d42b Mon Sep 17 00:00:00 2001 From: Chris Carini <6374067+ChrisCarini@users.noreply.github.com> Date: Sun, 19 Oct 2025 04:07:59 -0700 Subject: [PATCH 6/9] Fix typo in test function name for invalid URL (#154810) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/components/remote_calendar/test_config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/remote_calendar/test_config_flow.py b/tests/components/remote_calendar/test_config_flow.py index 9bea46ab27e647..3619251654d27e 100644 --- a/tests/components/remote_calendar/test_config_flow.py +++ b/tests/components/remote_calendar/test_config_flow.py @@ -83,7 +83,7 @@ async def test_form_import_webcal(hass: HomeAssistant, ics_content: str) -> None ], ) @respx.mock -async def test_form_inavild_url( +async def test_form_invalid_url( hass: HomeAssistant, side_effect: Exception, ics_content: str, From 8165ac196f44af60ed4d3947a866626d468f5969 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Sun, 19 Oct 2025 12:18:49 +0100 Subject: [PATCH 7/9] Move url out of nightscout strings and change to field descriptions (#154812) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- homeassistant/components/nightscout/config_flow.py | 7 ++++++- homeassistant/components/nightscout/strings.json | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nightscout/config_flow.py b/homeassistant/components/nightscout/config_flow.py index 0c0e8b296cd006..c5797e3f8f184f 100644 --- a/homeassistant/components/nightscout/config_flow.py +++ b/homeassistant/components/nightscout/config_flow.py @@ -63,7 +63,12 @@ async def async_step_user( return self.async_create_entry(title=info["title"], data=user_input) return self.async_show_form( - step_id="user", data_schema=DATA_SCHEMA, errors=errors + step_id="user", + data_schema=DATA_SCHEMA, + errors=errors, + description_placeholders={ + "example_url": "https://myhomeassistant.duckdns.org:5423", + }, ) diff --git a/homeassistant/components/nightscout/strings.json b/homeassistant/components/nightscout/strings.json index fd1d81e1273f19..1f13ed17054a18 100644 --- a/homeassistant/components/nightscout/strings.json +++ b/homeassistant/components/nightscout/strings.json @@ -3,10 +3,13 @@ "step": { "user": { "title": "Enter your Nightscout server information.", - "description": "- URL: the address of your nightscout instance. I.e.: https://myhomeassistant.duckdns.org:5423\n- API Key (optional): Only use if your instance is protected (auth_default_roles != readable).", "data": { "url": "[%key:common::config_flow::data::url%]", "api_key": "[%key:common::config_flow::data::api_key%]" + }, + "data_description": { + "url": "The address of your Nightscout instance. For example: {example_url}.", + "api_key": "Optional; only use it if your instance is protected (auth_default_roles != readable)." } } }, From 81572c6a84a8d16e2a25503a48659e08da0d2a5a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 19 Oct 2025 14:15:21 +0200 Subject: [PATCH 8/9] Build aarch64 wheels on ubuntu-arm (#154819) --- .github/workflows/wheels.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 41ceb1481c541e..b176633526ca90 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -128,19 +128,23 @@ jobs: name: Build Core wheels ${{ matrix.abi }} for ${{ matrix.arch }} (musllinux_1_2) if: github.repository_owner == 'home-assistant' needs: init - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: &matrix-build abi: ["cp313", "cp314"] arch: ${{ fromJson(needs.init.outputs.architectures) }} + include: + - os: ubuntu-latest + - arch: aarch64 + os: ubuntu-24.04-arm exclude: - - abi: "cp314" - arch: "armv7" - - abi: "cp314" - arch: "armhf" - - abi: "cp314" - arch: "i386" + - abi: cp314 + arch: armv7 + - abi: cp314 + arch: armhf + - abi: cp314 + arch: i386 steps: - *checkout @@ -187,7 +191,7 @@ jobs: name: Build wheels ${{ matrix.abi }} for ${{ matrix.arch }} if: github.repository_owner == 'home-assistant' needs: init - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: *matrix-build From d999dd05d197f81e891843c20be8c75b9b7815f7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 19 Oct 2025 17:20:16 +0200 Subject: [PATCH 9/9] Improve bluesound conftest function (#154828) --- tests/components/bluesound/conftest.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/components/bluesound/conftest.py b/tests/components/bluesound/conftest.py index 4a793967645398..8baa75d0ed8d65 100644 --- a/tests/components/bluesound/conftest.py +++ b/tests/components/bluesound/conftest.py @@ -1,9 +1,11 @@ """Common fixtures for the Bluesound tests.""" +from __future__ import annotations + from collections.abc import AsyncGenerator, Generator from dataclasses import dataclass import ipaddress -from typing import Any +from typing import Any, Self from unittest.mock import AsyncMock, patch from pyblu import Input, Player, Preset, Status, SyncStatus @@ -27,8 +29,8 @@ class PlayerMockData: status_long_polling_mock: LongPollingMock[Status] sync_status_long_polling_mock: LongPollingMock[SyncStatus] - @staticmethod - async def generate(host: str) -> "PlayerMockData": + @classmethod + async def generate(cls, host: str) -> Self: """Generate player mock data.""" host_ip = ipaddress.ip_address(host) assert host_ip.version == 4 @@ -110,7 +112,7 @@ async def generate(host: str) -> "PlayerMockData": ] ) - return PlayerMockData( + return cls( host, player, status_long_polling_mock, sync_status_long_polling_mock )