1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 00:20:30 +01:00

Add fan speed percentage control to SwitchBot Air Purifier (#166953)

This commit is contained in:
Retha Runolfsson
2026-04-01 22:08:40 +08:00
committed by GitHub
parent 783e2f0a00
commit c1a9f293a7
6 changed files with 75 additions and 38 deletions

View File

@@ -114,7 +114,10 @@ PLATFORMS_BY_TYPE = {
SupportedModels.AIR_PURIFIER_US.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.AIR_PURIFIER_TABLE_JP.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.AIR_PURIFIER_TABLE_US.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.EVAPORATIVE_HUMIDIFIER: [Platform.HUMIDIFIER, Platform.SENSOR],
SupportedModels.EVAPORATIVE_HUMIDIFIER.value: [
Platform.HUMIDIFIER,
Platform.SENSOR,
],
SupportedModels.FLOOR_LAMP.value: [Platform.LIGHT, Platform.SENSOR],
SupportedModels.STRIP_LIGHT_3.value: [Platform.LIGHT, Platform.SENSOR],
SupportedModels.RGBICWW_FLOOR_LAMP.value: [Platform.LIGHT, Platform.SENSOR],
@@ -171,7 +174,7 @@ CLASS_BY_DEVICE = {
SupportedModels.AIR_PURIFIER_US.value: switchbot.SwitchbotAirPurifier,
SupportedModels.AIR_PURIFIER_TABLE_JP.value: switchbot.SwitchbotAirPurifier,
SupportedModels.AIR_PURIFIER_TABLE_US.value: switchbot.SwitchbotAirPurifier,
SupportedModels.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier,
SupportedModels.EVAPORATIVE_HUMIDIFIER.value: switchbot.SwitchbotEvaporativeHumidifier,
SupportedModels.FLOOR_LAMP.value: switchbot.SwitchbotStripLight3,
SupportedModels.STRIP_LIGHT_3.value: switchbot.SwitchbotStripLight3,
SupportedModels.RGBICWW_FLOOR_LAMP.value: switchbot.SwitchbotRgbicLight,

View File

@@ -206,3 +206,16 @@ CONF_KEY_ID = "key_id"
CONF_ENCRYPTION_KEY = "encryption_key"
CONF_LOCK_NIGHTLATCH = "lock_force_nightlatch"
CONF_CURTAIN_SPEED = "curtain_speed"
AIRPURIFIER_BASIC_MODELS = {
SwitchbotModel.AIR_PURIFIER_JP,
SwitchbotModel.AIR_PURIFIER_US,
}
AIRPURIFIER_TABLE_MODELS = {
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
SwitchbotModel.AIR_PURIFIER_TABLE_US,
}
AIRPURIFIER_PM25_MODELS = {
SwitchbotModel.AIR_PURIFIER_US,
SwitchbotModel.AIR_PURIFIER_TABLE_US,
}

View File

@@ -131,6 +131,7 @@ class SwitchBotAirPurifierEntity(SwitchbotEntity, FanEntity):
_device: switchbot.SwitchbotAirPurifier
_attr_supported_features = (
FanEntityFeature.PRESET_MODE
| FanEntityFeature.SET_SPEED
| FanEntityFeature.TURN_OFF
| FanEntityFeature.TURN_ON
)
@@ -148,6 +149,11 @@ class SwitchBotAirPurifierEntity(SwitchbotEntity, FanEntity):
"""Return the current preset mode."""
return self._device.get_current_mode()
@property
def percentage(self) -> int | None:
"""Return the speed percentage of the air purifier."""
return self._device.get_current_percentage()
@exception_handler
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the air purifier."""
@@ -160,6 +166,16 @@ class SwitchBotAirPurifierEntity(SwitchbotEntity, FanEntity):
self._last_run_success = bool(await self._device.set_preset_mode(preset_mode))
self.async_write_ha_state()
@exception_handler
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the air purifier."""
_LOGGER.debug(
"Switchbot air purifier to set percentage %d %s", percentage, self._address
)
await self._device.set_percentage(percentage)
self.async_write_ha_state()
@exception_handler
async def async_turn_on(
self,

View File

@@ -785,8 +785,8 @@ LOCK_ULTRA_SERVICE_INFO = BluetoothServiceInfoBleak(
)
AIR_PURIFIER_TABLE_PM25_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier Table PM25",
AIR_PURIFIER_TABLE_US_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier Table US",
manufacturer_data={
2409: b"\xf0\x9e\x9e\x96j\xd6\xa1\x81\x88\xe4\x00\x01\x95\x00\x00",
},
@@ -796,22 +796,22 @@ AIR_PURIFIER_TABLE_PM25_SERVICE_INFO = BluetoothServiceInfoBleak(
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Air Purifier Table PM25",
local_name="Air Purifier Table US",
manufacturer_data={
2409: b"\xf0\x9e\x9e\x96j\xd6\xa1\x81\x88\xe4\x00\x01\x95\x00\x00",
},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"7\x00\x00\x95-\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier Table PM25"),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier Table US"),
time=0,
connectable=True,
tx_power=-127,
)
AIR_PURIFIER_PM25_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier PM25",
AIR_PURIFIER_US_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier US",
manufacturer_data={
2409: b'\xcc\x8d\xa2\xa7\x92>\t"\x80\x000\x00\x0f\x00\x00',
},
@@ -821,22 +821,22 @@ AIR_PURIFIER_PM25_SERVICE_INFO = BluetoothServiceInfoBleak(
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Air Purifier PM25",
local_name="Air Purifier US",
manufacturer_data={
2409: b'\xcc\x8d\xa2\xa7\x92>\t"\x80\x000\x00\x0f\x00\x00',
},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"*\x00\x00\x15\x04\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier PM25"),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier US"),
time=0,
connectable=True,
tx_power=-127,
)
AIR_PURIFIER_VOC_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier VOC",
AIR_PURIFIER_JP_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier JP",
manufacturer_data={
2409: b"\xcc\x8d\xa2\xa7\xe4\xa6\x0b\x83\x88d\x00\xea`\x00\x00",
},
@@ -846,22 +846,22 @@ AIR_PURIFIER_VOC_SERVICE_INFO = BluetoothServiceInfoBleak(
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Air Purifier VOC",
local_name="Air Purifier JP",
manufacturer_data={
2409: b"\xcc\x8d\xa2\xa7\xe4\xa6\x0b\x83\x88d\x00\xea`\x00\x00",
},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"+\x00\x00\x15\x04\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier VOC"),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier JP"),
time=0,
connectable=True,
tx_power=-127,
)
AIR_PURIFIER_TABLE_VOC_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier Table VOC",
AIR_PURIFIER_TABLE_JP_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Air Purifier Table JP",
manufacturer_data={
2409: b"\xcc\x8d\xa2\xa7\xc1\xae\x9b\x81\x8c\xb2\x00\x01\x94\x00\x00",
},
@@ -871,14 +871,14 @@ AIR_PURIFIER_TABLE_VOC_SERVICE_INFO = BluetoothServiceInfoBleak(
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Air Purifier Table VOC",
local_name="Air Purifier Table JP",
manufacturer_data={
2409: b"\xcc\x8d\xa2\xa7\xc1\xae\x9b\x81\x8c\xb2\x00\x01\x94\x00\x00",
},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"8\x00\x00\x95-\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier Table VOC"),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Air Purifier Table JP"),
time=0,
connectable=True,
tx_power=-127,

View File

@@ -21,10 +21,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import (
AIR_PURIFIER_PM25_SERVICE_INFO,
AIR_PURIFIER_TABLE_PM25_SERVICE_INFO,
AIR_PURIFIER_TABLE_VOC_SERVICE_INFO,
AIR_PURIFIER_VOC_SERVICE_INFO,
AIR_PURIFIER_JP_SERVICE_INFO,
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
AIR_PURIFIER_US_SERVICE_INFO,
CIRCULATOR_FAN_SERVICE_INFO,
)
@@ -103,10 +103,10 @@ async def test_circulator_fan_controlling(
@pytest.mark.parametrize(
("service_info", "sensor_type"),
[
(AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier_jp"),
(AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table_jp"),
(AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier_us"),
(AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, "air_purifier_table_us"),
(AIR_PURIFIER_JP_SERVICE_INFO, "air_purifier_jp"),
(AIR_PURIFIER_TABLE_JP_SERVICE_INFO, "air_purifier_table_jp"),
(AIR_PURIFIER_US_SERVICE_INFO, "air_purifier_us"),
(AIR_PURIFIER_TABLE_US_SERVICE_INFO, "air_purifier_table_us"),
],
)
@pytest.mark.parametrize(
@@ -117,6 +117,11 @@ async def test_circulator_fan_controlling(
{ATTR_PRESET_MODE: "sleep"},
"set_preset_mode",
),
(
SERVICE_SET_PERCENTAGE,
{ATTR_PERCENTAGE: 27},
"set_percentage",
),
(
SERVICE_TURN_OFF,
{},
@@ -169,10 +174,10 @@ async def test_air_purifier_controlling(
@pytest.mark.parametrize(
("service_info", "sensor_type"),
[
(AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier_jp"),
(AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table_jp"),
(AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier_us"),
(AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, "air_purifier_table_us"),
(AIR_PURIFIER_JP_SERVICE_INFO, "air_purifier_jp"),
(AIR_PURIFIER_TABLE_JP_SERVICE_INFO, "air_purifier_table_jp"),
(AIR_PURIFIER_US_SERVICE_INFO, "air_purifier_us"),
(AIR_PURIFIER_TABLE_US_SERVICE_INFO, "air_purifier_table_us"),
],
)
@pytest.mark.parametrize(

View File

@@ -20,10 +20,10 @@ from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_SENSOR_TYPE
from homeassistant.core import HomeAssistant
from . import (
AIR_PURIFIER_PM25_SERVICE_INFO,
AIR_PURIFIER_TABLE_PM25_SERVICE_INFO,
AIR_PURIFIER_TABLE_VOC_SERVICE_INFO,
AIR_PURIFIER_VOC_SERVICE_INFO,
AIR_PURIFIER_JP_SERVICE_INFO,
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
AIR_PURIFIER_US_SERVICE_INFO,
HUBMINI_MATTER_SERVICE_INFO,
LOCK_SERVICE_INFO,
WOCURTAIN_SERVICE_INFO,
@@ -253,22 +253,22 @@ async def test_migrate_entry_fails_for_future_version(
[
(
DEPRECATED_SENSOR_TYPE_AIR_PURIFIER,
AIR_PURIFIER_VOC_SERVICE_INFO,
AIR_PURIFIER_JP_SERVICE_INFO,
"air_purifier_jp",
),
(
DEPRECATED_SENSOR_TYPE_AIR_PURIFIER,
AIR_PURIFIER_PM25_SERVICE_INFO,
AIR_PURIFIER_US_SERVICE_INFO,
"air_purifier_us",
),
(
DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE,
AIR_PURIFIER_TABLE_VOC_SERVICE_INFO,
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
"air_purifier_table_jp",
),
(
DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE,
AIR_PURIFIER_TABLE_PM25_SERVICE_INFO,
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
"air_purifier_table_us",
),
],