mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Addition of Flipr hub with switch platform (#125866)
* Addition of Flipr hub with switch platform * Remove of loggers in tests * Review corrections * Review corrections * Apply suggestions from code review --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
f3facac016
commit
2ae4989031
@ -13,9 +13,9 @@ from homeassistant.exceptions import ConfigEntryError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import FliprDataUpdateCoordinator
|
||||
from .coordinator import FliprDataUpdateCoordinator, FliprHubDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -25,6 +25,7 @@ class FliprData:
|
||||
"""The Flipr data class."""
|
||||
|
||||
flipr_coordinators: list[FliprDataUpdateCoordinator]
|
||||
hub_coordinators: list[FliprHubDataUpdateCoordinator]
|
||||
|
||||
|
||||
type FliprConfigEntry = ConfigEntry[FliprData]
|
||||
@ -53,7 +54,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: FliprConfigEntry) -> boo
|
||||
await flipr_coordinator.async_config_entry_first_refresh()
|
||||
flipr_coordinators.append(flipr_coordinator)
|
||||
|
||||
entry.runtime_data = FliprData(flipr_coordinators)
|
||||
hub_coordinators = []
|
||||
for hub_id in ids["hub"]:
|
||||
hub_coordinator = FliprHubDataUpdateCoordinator(hass, client, hub_id)
|
||||
await hub_coordinator.async_config_entry_first_refresh()
|
||||
hub_coordinators.append(hub_coordinator)
|
||||
|
||||
entry.runtime_data = FliprData(flipr_coordinators, hub_coordinators)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
|
@ -6,5 +6,3 @@ ATTRIBUTION = "Flipr Data"
|
||||
|
||||
MANUFACTURER = "CTAC-TECH"
|
||||
NAME = "Flipr"
|
||||
|
||||
CONF_ENTRY_FLIPR_COORDINATORS = "flipr_coordinators"
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from flipr_api import FliprAPIRestClient
|
||||
from flipr_api.exceptions import FliprError
|
||||
@ -13,8 +14,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to hold Flipr data retrieval."""
|
||||
class BaseDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
||||
"""Parent class to hold Flipr and Hub data retrieval."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
@ -32,7 +33,11 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
update_interval=timedelta(minutes=15),
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
|
||||
class FliprDataUpdateCoordinator(BaseDataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Class to hold Flipr data retrieval."""
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Fetch data from API endpoint."""
|
||||
try:
|
||||
data = await self.hass.async_add_executor_job(
|
||||
@ -42,3 +47,18 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class FliprHubDataUpdateCoordinator(BaseDataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Class to hold Flipr hub data retrieval."""
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Fetch data from API endpoint."""
|
||||
try:
|
||||
data = await self.hass.async_add_executor_job(
|
||||
self.client.get_hub_state, self.device_id
|
||||
)
|
||||
except FliprError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
return data
|
||||
|
@ -5,10 +5,10 @@ from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import ATTRIBUTION, DOMAIN, MANUFACTURER
|
||||
from .coordinator import FliprDataUpdateCoordinator
|
||||
from .coordinator import BaseDataUpdateCoordinator
|
||||
|
||||
|
||||
class FliprEntity(CoordinatorEntity):
|
||||
class FliprEntity(CoordinatorEntity[BaseDataUpdateCoordinator]):
|
||||
"""Implements a common class elements representing the Flipr component."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
@ -16,7 +16,7 @@ class FliprEntity(CoordinatorEntity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: FliprDataUpdateCoordinator,
|
||||
coordinator: BaseDataUpdateCoordinator,
|
||||
description: EntityDescription,
|
||||
is_flipr_hub: bool = False,
|
||||
) -> None:
|
||||
|
67
homeassistant/components/flipr/switch.py
Normal file
67
homeassistant/components/flipr/switch.py
Normal file
@ -0,0 +1,67 @@
|
||||
"""Switch platform for the Flipr's Hub."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import FliprConfigEntry
|
||||
from .entity import FliprEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
||||
SwitchEntityDescription(
|
||||
key="hubState",
|
||||
name=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: FliprConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switch for Flipr hub."""
|
||||
coordinators = config_entry.runtime_data.hub_coordinators
|
||||
|
||||
async_add_entities(
|
||||
FliprHubSwitch(coordinator, description, True)
|
||||
for description in SWITCH_TYPES
|
||||
for coordinator in coordinators
|
||||
)
|
||||
|
||||
|
||||
class FliprHubSwitch(FliprEntity, SwitchEntity):
|
||||
"""Switch representing Hub state."""
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return state of the switch."""
|
||||
_LOGGER.debug("coordinator data = %s", self.coordinator.data)
|
||||
return self.coordinator.data["state"]
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the switch."""
|
||||
_LOGGER.debug("Switching off %s", self.device_id)
|
||||
data = await self.hass.async_add_executor_job(
|
||||
self.coordinator.client.set_hub_state,
|
||||
self.device_id,
|
||||
False,
|
||||
)
|
||||
_LOGGER.debug("New hub infos are %s", data)
|
||||
self.coordinator.async_set_updated_data(data)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the switch."""
|
||||
_LOGGER.debug("Switching on %s", self.device_id)
|
||||
data = await self.hass.async_add_executor_job(
|
||||
self.coordinator.client.set_hub_state,
|
||||
self.device_id,
|
||||
True,
|
||||
)
|
||||
_LOGGER.debug("New hub infos are %s", data)
|
||||
self.coordinator.async_set_updated_data(data)
|
110
tests/components/flipr/test_switch.py
Normal file
110
tests/components/flipr/test_switch.py
Normal file
@ -0,0 +1,110 @@
|
||||
"""Test the Flipr switch for Hub."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from flipr_api.exceptions import FliprError
|
||||
|
||||
from homeassistant.components.switch import (
|
||||
DOMAIN as SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from .conftest import MOCK_HUB_STATE_OFF
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
SWITCH_ENTITY_ID = "switch.flipr_hub_myhubid"
|
||||
|
||||
|
||||
async def test_entities(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_flipr_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test the creation and values of the Flipr switch."""
|
||||
|
||||
mock_flipr_client.search_all_ids.return_value = {"flipr": [], "hub": ["myhubid"]}
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Check entity unique_id value that is generated in FliprEntity base class.
|
||||
entity = entity_registry.async_get(SWITCH_ENTITY_ID)
|
||||
assert entity.unique_id == "myhubid-hubState"
|
||||
|
||||
state = hass.states.get(SWITCH_ENTITY_ID)
|
||||
assert state
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_switch_actions(
|
||||
hass: HomeAssistant,
|
||||
mock_flipr_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test the actions on the Flipr Hub switch."""
|
||||
|
||||
mock_flipr_client.search_all_ids.return_value = {"flipr": [], "hub": ["myhubid"]}
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: SWITCH_ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get(SWITCH_ENTITY_ID)
|
||||
assert state.state == STATE_ON
|
||||
|
||||
mock_flipr_client.set_hub_state.return_value = MOCK_HUB_STATE_OFF
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: SWITCH_ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get(SWITCH_ENTITY_ID)
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
async def test_no_switch_found(
|
||||
hass: HomeAssistant,
|
||||
mock_flipr_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test the switch absence."""
|
||||
|
||||
mock_flipr_client.search_all_ids.return_value = {"flipr": [], "hub": []}
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert not hass.states.async_entity_ids(SWITCH_DOMAIN)
|
||||
|
||||
|
||||
async def test_error_flipr_api(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_flipr_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test the Flipr sensors error."""
|
||||
|
||||
mock_flipr_client.search_all_ids.return_value = {"flipr": [], "hub": ["myhubid"]}
|
||||
|
||||
mock_flipr_client.get_hub_state.side_effect = FliprError(
|
||||
"Error during flipr data retrieval..."
|
||||
)
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Check entity is not generated because of the FliprError raised.
|
||||
entity = entity_registry.async_get(SWITCH_ENTITY_ID)
|
||||
assert entity is None
|
Loading…
x
Reference in New Issue
Block a user