diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 32f9d0ff5a5926..48e98303dfdc2d 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==41.15.0", + "aioesphomeapi==41.16.0", "esphome-dashboard-api==1.3.0", "bleak-esphome==3.4.0" ], diff --git a/homeassistant/components/hue/strings.json b/homeassistant/components/hue/strings.json index b70d4feb526ece..80817583ad69a0 100644 --- a/homeassistant/components/hue/strings.json +++ b/homeassistant/components/hue/strings.json @@ -68,6 +68,7 @@ "initial_press": "\"{subtype}\" pressed initially", "repeat": "\"{subtype}\" held down", "short_release": "\"{subtype}\" released after short press", + "long_press": "\"{subtype}\" long pressed", "long_release": "[%key:component::hue::device_automation::trigger_type::remote_button_long_release%]", "double_short_release": "[%key:component::hue::device_automation::trigger_type::remote_double_button_short_press%]", "start": "[%key:component::hue::device_automation::trigger_type::initial_press%]" diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index 4cf1bc848e0f6d..12163917a9dfec 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -749,17 +749,13 @@ def hvac_action(self) -> HVACAction: async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" - await self.call_rpc( - "Thermostat.SetConfig", - {"config": {"id": self._id, "target_C": kwargs[ATTR_TEMPERATURE]}}, + await self.coordinator.device.climate_set_target_temperature( + self._id, kwargs[ATTR_TEMPERATURE] ) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set hvac mode.""" - mode = hvac_mode in (HVACMode.COOL, HVACMode.HEAT) - await self.call_rpc( - "Thermostat.SetConfig", {"config": {"id": self._id, "enable": mode}} - ) + await self.coordinator.device.climate_set_hvac_mode(self._id, str(hvac_mode)) class RpcBluTrvClimate(ShellyRpcEntity, ClimateEntity): diff --git a/homeassistant/components/shelly/icons.json b/homeassistant/components/shelly/icons.json index 47db02e06dc947..c223e9344c74c7 100644 --- a/homeassistant/components/shelly/icons.json +++ b/homeassistant/components/shelly/icons.json @@ -68,6 +68,9 @@ "on": "mdi:valve-open" } }, + "cury_boost": { + "default": "mdi:rocket-launch" + }, "cury_slot": { "default": "mdi:scent", "state": { diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index 9ee2f096fb826f..9dba2a32e82caa 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -9,7 +9,7 @@ "iot_class": "local_push", "loggers": ["aioshelly"], "quality_scale": "silver", - "requirements": ["aioshelly==13.13.0"], + "requirements": ["aioshelly==13.14.0"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 20b72ce8933bdf..61b8796cf8b368 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -243,6 +243,19 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription): available=lambda status: (left := status["left"]) is not None and left.get("vial", {}).get("level", -1) != -1, ), + "cury_left_boost": RpcSwitchDescription( + key="cury", + sub_key="slots", + name="Left slot boost", + translation_key="cury_boost", + is_on=lambda status: status["slots"]["left"]["boost"] is not None, + method_on="cury_boost", + method_off="cury_stop_boost", + method_params_fn=lambda id, _: (id, "left"), + entity_registry_enabled_default=True, + available=lambda status: (left := status["left"]) is not None + and left.get("vial", {}).get("level", -1) != -1, + ), "cury_right": RpcSwitchDescription( key="cury", sub_key="slots", @@ -256,6 +269,19 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription): available=lambda status: (right := status["right"]) is not None and right.get("vial", {}).get("level", -1) != -1, ), + "cury_right_boost": RpcSwitchDescription( + key="cury", + sub_key="slots", + name="Right slot boost", + translation_key="cury_boost", + is_on=lambda status: status["slots"]["right"]["boost"] is not None, + method_on="cury_boost", + method_off="cury_stop_boost", + method_params_fn=lambda id, _: (id, "right"), + entity_registry_enabled_default=True, + available=lambda status: (right := status["right"]) is not None + and right.get("vial", {}).get("level", -1) != -1, + ), } diff --git a/homeassistant/components/switchbot_cloud/__init__.py b/homeassistant/components/switchbot_cloud/__init__.py index d0fb79ebdde7c4..23247be935b918 100644 --- a/homeassistant/components/switchbot_cloud/__init__.py +++ b/homeassistant/components/switchbot_cloud/__init__.py @@ -158,6 +158,7 @@ async def make_device_data( "Robot Vacuum Cleaner K10+ Pro Combo", "Robot Vacuum Cleaner S10", "S20", + "Robot Vacuum Cleaner K11 Plus", ]: coordinator = await coordinator_for_device( hass, entry, api, device, coordinators_by_id, True diff --git a/homeassistant/components/switchbot_cloud/vacuum.py b/homeassistant/components/switchbot_cloud/vacuum.py index 7bc4c7d0ea2ae0..cc1f4c13e0bd4c 100644 --- a/homeassistant/components/switchbot_cloud/vacuum.py +++ b/homeassistant/components/switchbot_cloud/vacuum.py @@ -129,8 +129,8 @@ def _set_attributes(self) -> None: self._attr_fan_speed = VACUUM_FAN_SPEED_QUIET -class SwitchBotCloudVacuumK20PlusPro(SwitchBotCloudVacuum): - """Representation of a SwitchBot K20+ Pro.""" +class SwitchBotCloudVacuumV2(SwitchBotCloudVacuum): + """Representation of a SwitchBot K20+ Pro & Robot Vacuum Cleaner K11 Plus.""" async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: """Set fan speed.""" @@ -176,7 +176,7 @@ async def async_start(self) -> None: await self.coordinator.async_request_refresh() -class SwitchBotCloudVacuumK10PlusProCombo(SwitchBotCloudVacuumK20PlusPro): +class SwitchBotCloudVacuumK10PlusProCombo(SwitchBotCloudVacuumV2): """Representation of a SwitchBot vacuum K10+ Pro Combo.""" async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: @@ -194,7 +194,7 @@ async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: await self.coordinator.async_request_refresh() -class SwitchBotCloudVacuumV3(SwitchBotCloudVacuumK20PlusPro): +class SwitchBotCloudVacuumV3(SwitchBotCloudVacuumV2): """Representation of a SwitchBot vacuum Robot Vacuum Cleaner S10 & S20.""" async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: @@ -236,16 +236,15 @@ def _async_make_entity( api: SwitchBotAPI, device: Device | Remote, coordinator: SwitchBotCoordinator ) -> ( SwitchBotCloudVacuum - | SwitchBotCloudVacuumK20PlusPro + | SwitchBotCloudVacuumV2 | SwitchBotCloudVacuumV3 | SwitchBotCloudVacuumK10PlusProCombo ): """Make a SwitchBotCloudVacuum.""" - if device.device_type in VacuumCleanerV2Commands.get_supported_devices(): - if device.device_type == "K20+ Pro": - return SwitchBotCloudVacuumK20PlusPro(api, device, coordinator) + if device.device_type in ["K20+ Pro", "Robot Vacuum Cleaner K11 Plus"]: + return SwitchBotCloudVacuumV2(api, device, coordinator) + if device.device_type == "Robot Vacuum Cleaner K10+ Pro Combo": return SwitchBotCloudVacuumK10PlusProCombo(api, device, coordinator) - if device.device_type in VacuumCleanerV3Commands.get_supported_devices(): return SwitchBotCloudVacuumV3(api, device, coordinator) return SwitchBotCloudVacuum(api, device, coordinator) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 5693b6614edede..5227a58b8e63d9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -130,7 +130,7 @@ multidict>=6.0.2 backoff>=2.0 # ensure pydantic version does not float since it might have breaking changes -pydantic==2.12.0 +pydantic==2.12.1 # Required for Python 3.12.4 compatibility (#119223). mashumaro>=3.13.1 diff --git a/requirements_all.txt b/requirements_all.txt index b870b0a70c7e1b..efaed1186fd92f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -250,7 +250,7 @@ aioelectricitymaps==1.1.1 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==41.15.0 +aioesphomeapi==41.16.0 # homeassistant.components.flo aioflo==2021.11.0 @@ -387,7 +387,7 @@ aioruuvigateway==0.1.0 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==13.13.0 +aioshelly==13.14.0 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/requirements_test.txt b/requirements_test.txt index 799b697e7533e8..a7bd4b6b2a9a8b 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -17,7 +17,7 @@ license-expression==30.4.3 mock-open==1.4.0 mypy-dev==1.19.0a4 pre-commit==4.2.0 -pydantic==2.12.0 +pydantic==2.12.1 pylint==4.0.0 pylint-per-file-ignores==1.4.0 pipdeptree==2.26.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cb8f3e1c8a50d9..6168fd3505e0b6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -238,7 +238,7 @@ aioelectricitymaps==1.1.1 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==41.15.0 +aioesphomeapi==41.16.0 # homeassistant.components.flo aioflo==2021.11.0 @@ -369,7 +369,7 @@ aioruuvigateway==0.1.0 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==13.13.0 +aioshelly==13.14.0 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 6b96e3b5e53da0..62982b1a11c595 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -155,7 +155,7 @@ backoff>=2.0 # ensure pydantic version does not float since it might have breaking changes -pydantic==2.12.0 +pydantic==2.12.1 # Required for Python 3.12.4 compatibility (#119223). mashumaro>=3.13.1 diff --git a/tests/components/shelly/snapshots/test_switch.ambr b/tests/components/shelly/snapshots/test_switch.ambr index a85f7c12b4d63d..704d86720c935b 100644 --- a/tests/components/shelly/snapshots/test_switch.ambr +++ b/tests/components/shelly/snapshots/test_switch.ambr @@ -47,6 +47,54 @@ 'state': 'on', }) # --- +# name: test_cury_switch_entity[switch.test_name_left_slot_boost-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.test_name_left_slot_boost', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Left slot boost', + 'platform': 'shelly', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cury_boost', + 'unique_id': '123456789ABC-cury:0-cury_left_boost', + 'unit_of_measurement': None, + }) +# --- +# name: test_cury_switch_entity[switch.test_name_left_slot_boost-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test name Left slot boost', + }), + 'context': , + 'entity_id': 'switch.test_name_left_slot_boost', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- # name: test_cury_switch_entity[switch.test_name_right_slot-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -95,3 +143,51 @@ 'state': 'off', }) # --- +# name: test_cury_switch_entity[switch.test_name_right_slot_boost-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.test_name_right_slot_boost', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Right slot boost', + 'platform': 'shelly', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cury_boost', + 'unique_id': '123456789ABC-cury:0-cury_right_boost', + 'unit_of_measurement': None, + }) +# --- +# name: test_cury_switch_entity[switch.test_name_right_slot_boost-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test name Right slot boost', + }), + 'context': , + 'entity_id': 'switch.test_name_right_slot_boost', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/shelly/test_climate.py b/tests/components/shelly/test_climate.py index 54cf44c6155ecd..c5ac132809b73f 100644 --- a/tests/components/shelly/test_climate.py +++ b/tests/components/shelly/test_climate.py @@ -665,9 +665,7 @@ async def test_rpc_climate_hvac_mode( ) mock_rpc_device.mock_update() - mock_rpc_device.call_rpc.assert_called_once_with( - "Thermostat.SetConfig", {"config": {"id": 0, "enable": False}} - ) + mock_rpc_device.climate_set_hvac_mode.assert_called_once_with(0, str(HVACMode.OFF)) assert (state := hass.states.get(entity_id)) assert state.state == HVACMode.OFF @@ -717,9 +715,7 @@ async def test_rpc_climate_set_temperature( ) mock_rpc_device.mock_update() - mock_rpc_device.call_rpc.assert_called_once_with( - "Thermostat.SetConfig", {"config": {"id": 0, "target_C": 28}} - ) + mock_rpc_device.climate_set_target_temperature.assert_called_once_with(0, 28) assert (state := hass.states.get(entity_id)) assert state.attributes[ATTR_TEMPERATURE] == 28 diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index dd8f80ee6b294c..9daf557cf4527b 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -832,11 +832,13 @@ async def test_cury_switch_entity( "left": { "intensity": 70, "on": True, + "boost": {"started_at": 1760365354, "duration": 1800}, "vial": {"level": 27, "name": "Forest Dream"}, }, "right": { "intensity": 70, "on": False, + "boost": None, "vial": {"level": 84, "name": "Velvet Rose"}, }, }, @@ -845,7 +847,7 @@ async def test_cury_switch_entity( monkeypatch.setattr(mock_rpc_device, "status", status) await init_integration(hass, 3) - for entity in ("left_slot", "right_slot"): + for entity in ("left_slot", "left_slot_boost", "right_slot", "right_slot_boost"): entity_id = f"{SWITCH_DOMAIN}.test_name_{entity}" state = hass.states.get(entity_id) @@ -872,6 +874,24 @@ async def test_cury_switch_entity( mock_rpc_device.mock_update() mock_rpc_device.cury_set.assert_called_with(0, "right", True) + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.test_name_left_slot_boost"}, + blocking=True, + ) + mock_rpc_device.mock_update() + mock_rpc_device.cury_stop_boost.assert_called_with(0, "left") + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.test_name_right_slot_boost"}, + blocking=True, + ) + mock_rpc_device.mock_update() + mock_rpc_device.cury_boost.assert_called_with(0, "right") + async def test_cury_switch_availability( hass: HomeAssistant, @@ -883,11 +903,13 @@ async def test_cury_switch_availability( "left": { "intensity": 70, "on": True, + "boost": None, "vial": {"level": 27, "name": "Forest Dream"}, }, "right": { "intensity": 70, "on": False, + "boost": None, "vial": {"level": 84, "name": "Velvet Rose"}, }, } @@ -924,6 +946,7 @@ async def test_cury_switch_availability( slots["left"] = { "intensity": 70, "on": True, + "boost": None, "vial": {"level": 27, "name": "Forest Dream"}, } mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cury:0", "slots", slots)