mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 17:31:15 -04:00
Add switchbot cloud integration (#99607)
* Switches via API * Using external library * UT and checlist * Updating file .coveragerc * Update homeassistant/components/switchbot_via_api/switch.py Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/switchbot_via_api/switch.py Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/switchbot_via_api/switch.py Co-authored-by: J. Nick Koston <nick@koston.org> * Review fixes * Apply suggestions from code review Co-authored-by: J. Nick Koston <nick@koston.org> * This base class shouldn't know about Remote * Fixing suggestion * Sometimes, the state from the API is not updated immediately * Review changes * Some review changes * Review changes * Review change: Adding type on commands * Parameterizing some tests * Review changes * Updating .coveragerc * Fixing error handling in coordinator * Review changes * Review changes * Adding switchbot brand * Apply suggestions from code review Co-authored-by: J. Nick Koston <nick@koston.org> * Review changes * Adding strict typing * Removing log in constructor --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
J. Nick Koston
parent
568974fcc4
commit
f99dedfb42
@@ -0,0 +1,20 @@
|
||||
"""Tests for the SwitchBot Cloud integration."""
|
||||
from homeassistant.components.switchbot_cloud.const import DOMAIN
|
||||
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
def configure_integration(hass: HomeAssistant) -> MockConfigEntry:
|
||||
"""Configure the integration."""
|
||||
config = {
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_API_KEY: "test-api-key",
|
||||
}
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=config, entry_id="123456", unique_id="123456"
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
return entry
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Common fixtures for the SwitchBot via API tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.switchbot_cloud.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Test the SwitchBot via API config flow."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.switchbot_cloud.config_flow import (
|
||||
CannotConnect,
|
||||
InvalidAuth,
|
||||
)
|
||||
from homeassistant.components.switchbot_cloud.const import DOMAIN, ENTRY_TITLE
|
||||
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
|
||||
async def _fill_out_form_and_assert_entry_created(
|
||||
hass: HomeAssistant, flow_id: str, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Util function to fill out a form and assert that a config entry is created."""
|
||||
with patch(
|
||||
"homeassistant.components.switchbot_cloud.config_flow.SwitchBotAPI.list_devices",
|
||||
return_value=[],
|
||||
):
|
||||
result_configure = await hass.config_entries.flow.async_configure(
|
||||
flow_id,
|
||||
{
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_API_KEY: "test-secret-key",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result_configure["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result_configure["title"] == ENTRY_TITLE
|
||||
assert result_configure["data"] == {
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_API_KEY: "test-secret-key",
|
||||
}
|
||||
mock_setup_entry.assert_called_once()
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||
"""Test we get the form."""
|
||||
result_init = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result_init["type"] == FlowResultType.FORM
|
||||
assert not result_init["errors"]
|
||||
|
||||
await _fill_out_form_and_assert_entry_created(
|
||||
hass, result_init["flow_id"], mock_setup_entry
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "message"),
|
||||
[
|
||||
(InvalidAuth, "invalid_auth"),
|
||||
(CannotConnect, "cannot_connect"),
|
||||
(Exception, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_form_fails(
|
||||
hass: HomeAssistant, error: Exception, message: str, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test we handle error cases."""
|
||||
result_init = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.switchbot_cloud.config_flow.SwitchBotAPI.list_devices",
|
||||
side_effect=error,
|
||||
):
|
||||
result_configure = await hass.config_entries.flow.async_configure(
|
||||
result_init["flow_id"],
|
||||
{
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_API_KEY: "test-secret-key",
|
||||
},
|
||||
)
|
||||
|
||||
assert result_configure["type"] == FlowResultType.FORM
|
||||
assert result_configure["errors"] == {"base": message}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await _fill_out_form_and_assert_entry_created(
|
||||
hass, result_init["flow_id"], mock_setup_entry
|
||||
)
|
||||
@@ -0,0 +1,100 @@
|
||||
"""Tests for the SwitchBot Cloud integration init."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from switchbot_api import CannotConnect, Device, InvalidAuth, PowerState
|
||||
|
||||
from homeassistant.components.switchbot_cloud import SwitchBotAPI
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import configure_integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_list_devices():
|
||||
"""Mock list_devices."""
|
||||
with patch.object(SwitchBotAPI, "list_devices") as mock_list_devices:
|
||||
yield mock_list_devices
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_status():
|
||||
"""Mock get_status."""
|
||||
with patch.object(SwitchBotAPI, "get_status") as mock_get_status:
|
||||
yield mock_get_status
|
||||
|
||||
|
||||
async def test_setup_entry_success(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test successful setup of entry."""
|
||||
mock_list_devices.return_value = [
|
||||
Device(
|
||||
deviceId="test-id",
|
||||
deviceName="test-name",
|
||||
deviceType="Plug",
|
||||
hubDeviceId="test-hub-id",
|
||||
)
|
||||
]
|
||||
mock_get_status.return_value = {"power": PowerState.ON.value}
|
||||
entry = configure_integration(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state == ConfigEntryState.LOADED
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
mock_list_devices.assert_called_once()
|
||||
mock_get_status.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "state"),
|
||||
[
|
||||
(InvalidAuth, ConfigEntryState.SETUP_ERROR),
|
||||
(CannotConnect, ConfigEntryState.SETUP_RETRY),
|
||||
],
|
||||
)
|
||||
async def test_setup_entry_fails_when_listing_devices(
|
||||
hass: HomeAssistant,
|
||||
error: Exception,
|
||||
state: ConfigEntryState,
|
||||
mock_list_devices,
|
||||
mock_get_status,
|
||||
) -> None:
|
||||
"""Test error handling when list_devices in setup of entry."""
|
||||
mock_list_devices.side_effect = error
|
||||
entry = configure_integration(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state == state
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
mock_list_devices.assert_called_once()
|
||||
mock_get_status.assert_not_called()
|
||||
|
||||
|
||||
async def test_setup_entry_fails_when_refreshing(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test error handling in get_status in setup of entry."""
|
||||
mock_list_devices.return_value = [
|
||||
Device(
|
||||
deviceId="test-id",
|
||||
deviceName="test-name",
|
||||
deviceType="Plug",
|
||||
hubDeviceId="test-hub-id",
|
||||
)
|
||||
]
|
||||
mock_get_status.side_effect = CannotConnect
|
||||
entry = configure_integration(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state == ConfigEntryState.LOADED
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
mock_list_devices.assert_called_once()
|
||||
mock_get_status.assert_called()
|
||||
Reference in New Issue
Block a user