diff --git a/homeassistant/components/freedompro/__init__.py b/homeassistant/components/freedompro/__init__.py index 47c0bda1b1c..6e5dd438e2d 100644 --- a/homeassistant/components/freedompro/__init__.py +++ b/homeassistant/components/freedompro/__init__.py @@ -14,7 +14,7 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -PLATFORMS = ["light"] +PLATFORMS = ["light", "switch"] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): diff --git a/homeassistant/components/freedompro/switch.py b/homeassistant/components/freedompro/switch.py new file mode 100644 index 00000000000..4e3ffb1a2eb --- /dev/null +++ b/homeassistant/components/freedompro/switch.py @@ -0,0 +1,93 @@ +"""Support for Freedompro switch.""" +import json + +from pyfreedompro import put_state + +from homeassistant.components.switch import SwitchEntity +from homeassistant.const import CONF_API_KEY +from homeassistant.core import callback +from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up Freedompro switch.""" + api_key = entry.data[CONF_API_KEY] + coordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + Device(hass, api_key, device, coordinator) + for device in coordinator.data + if device["type"] == "switch" or device["type"] == "outlet" + ) + + +class Device(CoordinatorEntity, SwitchEntity): + """Representation of an Freedompro switch.""" + + def __init__(self, hass, api_key, device, coordinator): + """Initialize the Freedompro switch.""" + super().__init__(coordinator) + self._hass = hass + self._session = aiohttp_client.async_get_clientsession(self._hass) + self._api_key = api_key + self._attr_name = device["name"] + self._attr_unique_id = device["uid"] + self._type = device["type"] + self._characteristics = device["characteristics"] + self._attr_device_info = { + "name": self._attr_name, + "identifiers": { + (DOMAIN, self._attr_unique_id), + }, + "model": self._type, + "manufacturer": "Freedompro", + } + self._attr_is_on = False + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + device = next( + ( + device + for device in self.coordinator.data + if device["uid"] == self._attr_unique_id + ), + None, + ) + if device is not None and "state" in device: + state = device["state"] + if "on" in state: + self._attr_is_on = state["on"] + super()._handle_coordinator_update() + + async def async_added_to_hass(self) -> None: + """When entity is added to hass.""" + await super().async_added_to_hass() + self._handle_coordinator_update() + + async def async_turn_on(self, **kwargs): + """Async function to set on to switch.""" + payload = {"on": True} + payload = json.dumps(payload) + await put_state( + self._session, + self._api_key, + self._attr_unique_id, + payload, + ) + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs): + """Async function to set off to switch.""" + payload = {"on": False} + payload = json.dumps(payload) + await put_state( + self._session, + self._api_key, + self._attr_unique_id, + payload, + ) + await self.coordinator.async_request_refresh() diff --git a/tests/components/freedompro/const.py b/tests/components/freedompro/const.py index 6cf67c932dc..0e8f5c4aa52 100644 --- a/tests/components/freedompro/const.py +++ b/tests/components/freedompro/const.py @@ -39,7 +39,7 @@ DEVICES = [ }, { "uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*1JKU1MVWHQL-Z9SCUS85VFXMRGNDCDNDDUVVDKBU31W", - "name": "irrigation", + "name": "Irrigation switch", "type": "switch", "characteristics": ["on"], }, diff --git a/tests/components/freedompro/test_switch.py b/tests/components/freedompro/test_switch.py new file mode 100644 index 00000000000..4674b684c41 --- /dev/null +++ b/tests/components/freedompro/test_switch.py @@ -0,0 +1,112 @@ +"""Tests for the Freedompro switch.""" +from datetime import timedelta +from unittest.mock import ANY, patch + +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_ON +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, STATE_OFF, STATE_ON +from homeassistant.helpers import entity_registry as er +from homeassistant.util.dt import utcnow + +from tests.common import async_fire_time_changed +from tests.components.freedompro.const import DEVICES_STATE + +uid = "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*1JKU1MVWHQL-Z9SCUS85VFXMRGNDCDNDDUVVDKBU31W" + + +async def test_switch_get_state(hass, init_integration): + """Test states of the switch.""" + init_integration + registry = er.async_get(hass) + + entity_id = "switch.irrigation_switch" + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_OFF + assert state.attributes.get("friendly_name") == "Irrigation switch" + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + get_states_response = list(DEVICES_STATE) + for state_response in get_states_response: + if state_response["uid"] == uid: + state_response["state"]["on"] = True + with patch( + "homeassistant.components.freedompro.get_states", + return_value=get_states_response, + ): + async_fire_time_changed(hass, utcnow() + timedelta(hours=2)) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.attributes.get("friendly_name") == "Irrigation switch" + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + assert state.state == STATE_ON + + +async def test_switch_set_off(hass, init_integration): + """Test set off of the switch.""" + init_integration + registry = er.async_get(hass) + + entity_id = "switch.irrigation_switch" + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_ON + assert state.attributes.get("friendly_name") == "Irrigation switch" + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + with patch( + "homeassistant.components.freedompro.switch.put_state" + ) as mock_put_state: + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + mock_put_state.assert_called_once_with(ANY, ANY, ANY, '{"on": false}') + + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + +async def test_switch_set_on(hass, init_integration): + """Test set on of the switch.""" + init_integration + registry = er.async_get(hass) + + entity_id = "switch.irrigation_switch" + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_ON + assert state.attributes.get("friendly_name") == "Irrigation switch" + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + with patch( + "homeassistant.components.freedompro.switch.put_state" + ) as mock_put_state: + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + mock_put_state.assert_called_once_with(ANY, ANY, ANY, '{"on": true}') + + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_ON