Modern forms switch platform (#52061)

* Add switch platform to Modern Forms integration

* Add reboot switch

* Update  lib to catch status from switches

* lint ignore

* Removed reboot switch

* bump aiomodernforms for dependency cleanup
This commit is contained in:
Brian Towles 2021-06-28 02:47:41 -05:00 committed by GitHub
parent bef8be9256
commit 9c84c2889f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 3 deletions

View File

@ -13,6 +13,7 @@ from aiomodernforms.models import Device as ModernFormsDeviceState
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MODEL, ATTR_NAME, ATTR_SW_VERSION, CONF_HOST
from homeassistant.core import HomeAssistant
@ -30,6 +31,7 @@ SCAN_INTERVAL = timedelta(seconds=5)
PLATFORMS = [
LIGHT_DOMAIN,
FAN_DOMAIN,
SWITCH_DOMAIN,
]
_LOGGER = logging.getLogger(__name__)

View File

@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/modern_forms",
"requirements": [
"aiomodernforms==0.1.5"
"aiomodernforms==0.1.8"
],
"zeroconf": [
{"type":"_easylink._tcp.local.", "name":"wac*"}

View File

@ -0,0 +1,113 @@
"""Support for Modern Forms switches."""
from __future__ import annotations
from typing import Any
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import (
ModernFormsDataUpdateCoordinator,
ModernFormsDeviceEntity,
modernforms_exception_handler,
)
from .const import DOMAIN
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Modern Forms switch based on a config entry."""
coordinator: ModernFormsDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
switches = [
ModernFormsAwaySwitch(entry.entry_id, coordinator),
ModernFormsAdaptiveLearningSwitch(entry.entry_id, coordinator),
]
async_add_entities(switches)
class ModernFormsSwitch(ModernFormsDeviceEntity, SwitchEntity):
"""Defines a Modern Forms switch."""
def __init__(
self,
*,
entry_id: str,
coordinator: ModernFormsDataUpdateCoordinator,
name: str,
icon: str,
key: str,
) -> None:
"""Initialize Modern Forms switch."""
self._key = key
super().__init__(
entry_id=entry_id, coordinator=coordinator, name=name, icon=icon
)
self._attr_unique_id = f"{self.coordinator.data.info.mac_address}_{self._key}"
class ModernFormsAwaySwitch(ModernFormsSwitch):
"""Defines a Modern Forms Away mode switch."""
def __init__(
self, entry_id: str, coordinator: ModernFormsDataUpdateCoordinator
) -> None:
"""Initialize Modern Forms Away mode switch."""
super().__init__(
coordinator=coordinator,
entry_id=entry_id,
icon="mdi:airplane-takeoff",
key="away_mode",
name=f"{coordinator.data.info.device_name} Away Mode",
)
@property
def is_on(self) -> bool:
"""Return the state of the switch."""
return bool(self.coordinator.data.state.away_mode_enabled)
@modernforms_exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the Modern Forms Away mode switch."""
await self.coordinator.modern_forms.away(away=False)
@modernforms_exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the Modern Forms Away mode switch."""
await self.coordinator.modern_forms.away(away=True)
class ModernFormsAdaptiveLearningSwitch(ModernFormsSwitch):
"""Defines a Modern Forms Adaptive Learning switch."""
def __init__(
self, entry_id: str, coordinator: ModernFormsDataUpdateCoordinator
) -> None:
"""Initialize Modern Forms Adaptive Learning switch."""
super().__init__(
coordinator=coordinator,
entry_id=entry_id,
icon="mdi:school-outline",
key="adaptive_learning",
name=f"{coordinator.data.info.device_name} Adaptive Learning",
)
@property
def is_on(self) -> bool:
"""Return the state of the switch."""
return bool(self.coordinator.data.state.adaptive_learning_enabled)
@modernforms_exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the Modern Forms Adaptive Learning switch."""
await self.coordinator.modern_forms.adaptive_learning(adaptive_learning=False)
@modernforms_exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the Modern Forms Adaptive Learning switch."""
await self.coordinator.modern_forms.adaptive_learning(adaptive_learning=True)

View File

@ -206,7 +206,7 @@ aiolip==1.1.4
aiolyric==1.0.7
# homeassistant.components.modern_forms
aiomodernforms==0.1.5
aiomodernforms==0.1.8
# homeassistant.components.yamaha_musiccast
aiomusiccast==0.6

View File

@ -131,7 +131,7 @@ aiolip==1.1.4
aiolyric==1.0.7
# homeassistant.components.modern_forms
aiomodernforms==0.1.5
aiomodernforms==0.1.8
# homeassistant.components.yamaha_musiccast
aiomusiccast==0.6

View File

@ -0,0 +1,144 @@
"""Tests for the Modern Forms switch platform."""
from unittest.mock import patch
from aiomodernforms import ModernFormsConnectionError
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_ICON,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.components.modern_forms import init_integration
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_switch_state(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test the creation and values of the Modern Forms switches."""
await init_integration(hass, aioclient_mock)
entity_registry = er.async_get(hass)
state = hass.states.get("switch.modernformsfan_away_mode")
assert state
assert state.attributes.get(ATTR_ICON) == "mdi:airplane-takeoff"
assert state.state == STATE_OFF
entry = entity_registry.async_get("switch.modernformsfan_away_mode")
assert entry
assert entry.unique_id == "AA:BB:CC:DD:EE:FF_away_mode"
state = hass.states.get("switch.modernformsfan_adaptive_learning")
assert state
assert state.attributes.get(ATTR_ICON) == "mdi:school-outline"
assert state.state == STATE_OFF
entry = entity_registry.async_get("switch.modernformsfan_adaptive_learning")
assert entry
assert entry.unique_id == "AA:BB:CC:DD:EE:FF_adaptive_learning"
async def test_switch_change_state(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test the change of state of the Modern Forms switches."""
await init_integration(hass, aioclient_mock)
# Away Mode
with patch("aiomodernforms.ModernFormsDevice.away") as away_mock:
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.modernformsfan_away_mode"},
blocking=True,
)
await hass.async_block_till_done()
away_mock.assert_called_once_with(away=True)
with patch("aiomodernforms.ModernFormsDevice.away") as away_mock:
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.modernformsfan_away_mode"},
blocking=True,
)
await hass.async_block_till_done()
away_mock.assert_called_once_with(away=False)
# Adaptive Learning
with patch(
"aiomodernforms.ModernFormsDevice.adaptive_learning"
) as adaptive_learning_mock:
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.modernformsfan_adaptive_learning"},
blocking=True,
)
await hass.async_block_till_done()
adaptive_learning_mock.assert_called_once_with(adaptive_learning=True)
with patch(
"aiomodernforms.ModernFormsDevice.adaptive_learning"
) as adaptive_learning_mock:
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.modernformsfan_adaptive_learning"},
blocking=True,
)
await hass.async_block_till_done()
adaptive_learning_mock.assert_called_once_with(adaptive_learning=False)
async def test_switch_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
) -> None:
"""Test error handling of the Modern Forms switches."""
await init_integration(hass, aioclient_mock)
aioclient_mock.clear_requests()
aioclient_mock.post("http://192.168.1.123:80/mf", text="", status=400)
with patch("homeassistant.components.modern_forms.ModernFormsDevice.update"):
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.modernformsfan_away_mode"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("switch.modernformsfan_away_mode")
assert state.state == STATE_OFF
assert "Invalid response from API" in caplog.text
async def test_switch_connection_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test error handling of the Modern Forms switches."""
await init_integration(hass, aioclient_mock)
with patch("homeassistant.components.modern_forms.ModernFormsDevice.update"), patch(
"homeassistant.components.modern_forms.ModernFormsDevice.away",
side_effect=ModernFormsConnectionError,
):
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.modernformsfan_away_mode"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("switch.modernformsfan_away_mode")
assert state.state == STATE_UNAVAILABLE