Add support for Bot in SwitchBot Cloud (#135606)

This commit is contained in:
Mick Montorier-Aberman 2025-01-21 13:08:38 +01:00 committed by GitHub
parent e822f5de6e
commit 33a2fa2c85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 187 additions and 1 deletions

View File

@ -16,6 +16,7 @@ from .coordinator import SwitchBotCoordinator
_LOGGER = getLogger(__name__)
PLATFORMS: list[Platform] = [
Platform.BUTTON,
Platform.CLIMATE,
Platform.LOCK,
Platform.SENSOR,
@ -28,6 +29,7 @@ PLATFORMS: list[Platform] = [
class SwitchbotDevices:
"""Switchbot devices data."""
buttons: list[Device] = field(default_factory=list)
climates: list[Remote] = field(default_factory=list)
switches: list[Device | Remote] = field(default_factory=list)
sensors: list[Device] = field(default_factory=list)
@ -136,6 +138,16 @@ async def make_device_data(
)
devices_data.locks.append((device, coordinator))
if isinstance(device, Device) and device.device_type in ["Bot"]:
coordinator = await coordinator_for_device(
hass, api, device, coordinators_by_id
)
if coordinator.data is not None:
if coordinator.data.get("deviceMode") == "pressMode":
devices_data.buttons.append((device, coordinator))
else:
devices_data.switches.append((device, coordinator))
async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool:
"""Set up SwitchBot via API from a config entry."""

View File

@ -0,0 +1,41 @@
"""Support for the Switchbot Bot as a Button."""
from typing import Any
from switchbot_api import BotCommands
from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import SwitchbotCloudData
from .const import DOMAIN
from .entity import SwitchBotCloudEntity
async def async_setup_entry(
hass: HomeAssistant,
config: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up SwitchBot Cloud entry."""
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
async_add_entities(
SwitchBotCloudBot(data.api, device, coordinator)
for device, coordinator in data.devices.buttons
)
class SwitchBotCloudBot(SwitchBotCloudEntity, ButtonEntity):
"""Representation of a SwitchBot Bot."""
_attr_name = None
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
async def async_press(self, **kwargs: Any) -> None:
"""Bot press command."""
await self.send_api_command(BotCommands.PRESS)

View File

@ -90,4 +90,6 @@ def _async_make_entity(
"Relay Switch 1",
]:
return SwitchBotCloudRelaySwitchSwitch(api, device, coordinator)
if "Bot" in device.device_type:
return SwitchBotCloudSwitch(api, device, coordinator)
raise NotImplementedError(f"Unsupported device type: {device.device_type}")

View File

@ -0,0 +1,71 @@
"""Test for the switchbot_cloud bot as a button."""
from unittest.mock import patch
from switchbot_api import BotCommands, Device
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.switchbot_cloud import SwitchBotAPI
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from . import configure_integration
async def test_pressmode_bot(
hass: HomeAssistant, mock_list_devices, mock_get_status
) -> None:
"""Test press."""
mock_list_devices.return_value = [
Device(
deviceId="bot-id-1",
deviceName="bot-1",
deviceType="Bot",
hubDeviceId="test-hub-id",
),
]
mock_get_status.return_value = {"deviceMode": "pressMode"}
entry = configure_integration(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
entity_id = "button.bot_1"
assert hass.states.get(entity_id).state == STATE_UNKNOWN
with patch.object(SwitchBotAPI, "send_command") as mock_send_command:
await hass.services.async_call(
BUTTON_DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
mock_send_command.assert_called_once_with(
"bot-id-1", BotCommands.PRESS, "command", "default"
)
assert hass.states.get(entity_id).state != STATE_UNKNOWN
async def test_switchmode_bot_no_button_entity(
hass: HomeAssistant, mock_list_devices, mock_get_status
) -> None:
"""Test a switchMode bot isn't added as a button."""
mock_list_devices.return_value = [
Device(
deviceId="bot-id-1",
deviceName="bot-1",
deviceType="Bot",
hubDeviceId="test-hub-id",
),
]
mock_get_status.return_value = {"deviceMode": "switchMode"}
entry = configure_integration(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
assert not hass.states.async_entity_ids(BUTTON_DOMAIN)

View File

@ -1,4 +1,4 @@
"""Test for the switchbot_cloud relay switch."""
"""Test for the switchbot_cloud relay switch & bot."""
from unittest.mock import patch
@ -54,3 +54,63 @@ async def test_relay_switch(
SWITCH_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert hass.states.get(entity_id).state == STATE_OFF
async def test_switchmode_bot(
hass: HomeAssistant, mock_list_devices, mock_get_status
) -> None:
"""Test turn on and turn off."""
mock_list_devices.return_value = [
Device(
deviceId="bot-id-1",
deviceName="bot-1",
deviceType="Bot",
hubDeviceId="test-hub-id",
),
]
mock_get_status.return_value = {"deviceMode": "switchMode", "power": "off"}
entry = configure_integration(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
entity_id = "switch.bot_1"
assert hass.states.get(entity_id).state == STATE_OFF
with patch.object(SwitchBotAPI, "send_command"):
await hass.services.async_call(
SWITCH_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert hass.states.get(entity_id).state == STATE_ON
with patch.object(SwitchBotAPI, "send_command"):
await hass.services.async_call(
SWITCH_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert hass.states.get(entity_id).state == STATE_OFF
async def test_pressmode_bot_no_switch_entity(
hass: HomeAssistant, mock_list_devices, mock_get_status
) -> None:
"""Test a pressMode bot isn't added as a switch."""
mock_list_devices.return_value = [
Device(
deviceId="bot-id-1",
deviceName="bot-1",
deviceType="Bot",
hubDeviceId="test-hub-id",
),
]
mock_get_status.return_value = {"deviceMode": "pressMode"}
entry = configure_integration(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
assert not hass.states.async_entity_ids(SWITCH_DOMAIN)