From e7edd51a65af4ad53525ae885984ede33509eb37 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 1 Dec 2025 12:37:12 +0200 Subject: [PATCH] Refactor Shelly number platform to use upstream set_thermostat_state (#157527) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- homeassistant/components/shelly/const.py | 2 ++ homeassistant/components/shelly/number.py | 26 +++++++---------------- tests/components/shelly/test_number.py | 8 +++---- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 6f43c5044ab..c7678a4b57a 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -341,3 +341,5 @@ MODEL_TOP_EV_CHARGER_EVE01 = "EVE01" MODEL_FRANKEVER_IRRIGATION_CONTROLLER = "Irrigation" ROLE_GENERIC = "generic" + +TRV_CHANNEL = 0 diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index b959f4bd7e1..e1e4a2e2c9a 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Final, cast +from typing import TYPE_CHECKING, Final, cast from aioshelly.block_device import Block from aioshelly.const import RPC_GENERATIONS @@ -34,6 +34,7 @@ from .const import ( MODEL_LINKEDGO_ST1820_THERMOSTAT, MODEL_TOP_EV_CHARGER_EVE01, ROLE_GENERIC, + TRV_CHANNEL, VIRTUAL_NUMBER_MODE_MAP, ) from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator @@ -62,9 +63,6 @@ PARALLEL_UPDATES = 0 class BlockNumberDescription(BlockEntityDescription, NumberEntityDescription): """Class to describe a BLOCK sensor.""" - rest_path: str = "" - rest_arg: str = "" - @dataclass(frozen=True, kw_only=True) class RpcNumberDescription(RpcEntityDescription, NumberEntityDescription): @@ -178,7 +176,7 @@ class RpcBluTrvExtTempNumber(RpcBluTrvNumber): self.async_write_ha_state() -NUMBERS: dict[tuple[str, str], BlockNumberDescription] = { +BLOCK_NUMBERS: dict[tuple[str, str], BlockNumberDescription] = { ("device", "valvePos"): BlockNumberDescription( key="device|valvepos", translation_key="valve_position", @@ -189,8 +187,6 @@ NUMBERS: dict[tuple[str, str], BlockNumberDescription] = { native_max_value=100, native_step=1, mode=NumberMode.SLIDER, - rest_path="thermostat/0", - rest_arg="pos", ), } @@ -357,7 +353,7 @@ def _async_setup_block_entry( hass, config_entry, async_add_entities, - NUMBERS, + BLOCK_NUMBERS, BlockSleepingNumber, ) @@ -426,18 +422,11 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, RestoreNumber): async def async_set_native_value(self, value: float) -> None: """Set value.""" - # Example for Shelly Valve: http://192.168.188.187/thermostat/0?pos=13.0 - await self._set_state_full_path( - self.entity_description.rest_path, - {self.entity_description.rest_arg: value}, + LOGGER.debug( + "Setting thermostat position for entity %s to %s", self.name, value ) - self.async_write_ha_state() - - async def _set_state_full_path(self, path: str, params: Any) -> Any: - """Set block state (HTTP request).""" - LOGGER.debug("Setting state for entity %s, state: %s", self.name, params) try: - return await self.coordinator.device.http_request("get", path, params) + await self.coordinator.device.set_thermostat_state(TRV_CHANNEL, pos=value) except DeviceConnectionError as err: self.coordinator.last_update_success = False raise HomeAssistantError( @@ -450,3 +439,4 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, RestoreNumber): ) from err except InvalidAuthError: await self.coordinator.async_shutdown_device_and_start_reauth() + self.async_write_ha_state() diff --git a/tests/components/shelly/test_number.py b/tests/components/shelly/test_number.py index d267c6a46c7..2fda1cc8454 100644 --- a/tests/components/shelly/test_number.py +++ b/tests/components/shelly/test_number.py @@ -192,9 +192,7 @@ async def test_block_number_set_value( {ATTR_ENTITY_ID: "number.test_name_valve_position", ATTR_VALUE: 30}, blocking=True, ) - mock_block_device.http_request.assert_called_once_with( - "get", "thermostat/0", {"pos": 30.0} - ) + mock_block_device.set_thermostat_state.assert_called_once_with(0, pos=30.0) async def test_block_set_value_connection_error( @@ -208,7 +206,7 @@ async def test_block_set_value_connection_error( ) monkeypatch.setattr( mock_block_device, - "http_request", + "set_thermostat_state", AsyncMock(side_effect=DeviceConnectionError), ) await init_integration(hass, 1, sleep_period=3600) @@ -240,7 +238,7 @@ async def test_block_set_value_auth_error( ) monkeypatch.setattr( mock_block_device, - "http_request", + "set_thermostat_state", AsyncMock(side_effect=InvalidAuthError), ) entry = await init_integration(hass, 1, sleep_period=3600)