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 homeassistant.helpers import issue_registry as ir
|
||||||
|
|
||||||
from .const import DOMAIN
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ class FliprData:
|
|||||||
"""The Flipr data class."""
|
"""The Flipr data class."""
|
||||||
|
|
||||||
flipr_coordinators: list[FliprDataUpdateCoordinator]
|
flipr_coordinators: list[FliprDataUpdateCoordinator]
|
||||||
|
hub_coordinators: list[FliprHubDataUpdateCoordinator]
|
||||||
|
|
||||||
|
|
||||||
type FliprConfigEntry = ConfigEntry[FliprData]
|
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()
|
await flipr_coordinator.async_config_entry_first_refresh()
|
||||||
flipr_coordinators.append(flipr_coordinator)
|
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)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
@ -6,5 +6,3 @@ ATTRIBUTION = "Flipr Data"
|
|||||||
|
|
||||||
MANUFACTURER = "CTAC-TECH"
|
MANUFACTURER = "CTAC-TECH"
|
||||||
NAME = "Flipr"
|
NAME = "Flipr"
|
||||||
|
|
||||||
CONF_ENTRY_FLIPR_COORDINATORS = "flipr_coordinators"
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from flipr_api import FliprAPIRestClient
|
from flipr_api import FliprAPIRestClient
|
||||||
from flipr_api.exceptions import FliprError
|
from flipr_api.exceptions import FliprError
|
||||||
@ -13,8 +14,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
class BaseDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
||||||
"""Class to hold Flipr data retrieval."""
|
"""Parent class to hold Flipr and Hub data retrieval."""
|
||||||
|
|
||||||
config_entry: ConfigEntry
|
config_entry: ConfigEntry
|
||||||
|
|
||||||
@ -32,7 +33,11 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
update_interval=timedelta(minutes=15),
|
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."""
|
"""Fetch data from API endpoint."""
|
||||||
try:
|
try:
|
||||||
data = await self.hass.async_add_executor_job(
|
data = await self.hass.async_add_executor_job(
|
||||||
@ -42,3 +47,18 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
raise UpdateFailed(error) from error
|
raise UpdateFailed(error) from error
|
||||||
|
|
||||||
return data
|
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 homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import ATTRIBUTION, DOMAIN, MANUFACTURER
|
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."""
|
"""Implements a common class elements representing the Flipr component."""
|
||||||
|
|
||||||
_attr_attribution = ATTRIBUTION
|
_attr_attribution = ATTRIBUTION
|
||||||
@ -16,7 +16,7 @@ class FliprEntity(CoordinatorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: FliprDataUpdateCoordinator,
|
coordinator: BaseDataUpdateCoordinator,
|
||||||
description: EntityDescription,
|
description: EntityDescription,
|
||||||
is_flipr_hub: bool = False,
|
is_flipr_hub: bool = False,
|
||||||
) -> None:
|
) -> 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