mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Fix and upgrade surepetcare (#49223)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
3f3f77c6e6
commit
ebbcfb1bc7
@ -1,26 +1,17 @@
|
|||||||
"""Support for Sure Petcare cat/pet flaps."""
|
"""The surepetcare integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from surepy import (
|
from surepy import Surepy
|
||||||
MESTART_RESOURCE,
|
from surepy.enums import LockState
|
||||||
SureLockStateID,
|
from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError
|
||||||
SurePetcare,
|
|
||||||
SurePetcareAuthenticationError,
|
|
||||||
SurePetcareError,
|
|
||||||
SurepyProduct,
|
|
||||||
)
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME
|
||||||
CONF_ID,
|
from homeassistant.core import HomeAssistant
|
||||||
CONF_PASSWORD,
|
|
||||||
CONF_SCAN_INTERVAL,
|
|
||||||
CONF_TYPE,
|
|
||||||
CONF_USERNAME,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
@ -31,11 +22,7 @@ from .const import (
|
|||||||
ATTR_LOCK_STATE,
|
ATTR_LOCK_STATE,
|
||||||
CONF_FEEDERS,
|
CONF_FEEDERS,
|
||||||
CONF_FLAPS,
|
CONF_FLAPS,
|
||||||
CONF_PARENT,
|
|
||||||
CONF_PETS,
|
CONF_PETS,
|
||||||
CONF_PRODUCT_ID,
|
|
||||||
DATA_SURE_PETCARE,
|
|
||||||
DEFAULT_SCAN_INTERVAL,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_SET_LOCK_STATE,
|
SERVICE_SET_LOCK_STATE,
|
||||||
SPC,
|
SPC,
|
||||||
@ -45,50 +32,49 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PLATFORMS = ["binary_sensor", "sensor"]
|
||||||
|
SCAN_INTERVAL = timedelta(minutes=3)
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.Schema(
|
DOMAIN: vol.Schema(
|
||||||
{
|
vol.All(
|
||||||
vol.Required(CONF_USERNAME): cv.string,
|
{
|
||||||
vol.Required(CONF_PASSWORD): cv.string,
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
vol.Optional(CONF_FEEDERS, default=[]): vol.All(
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
cv.ensure_list, [cv.positive_int]
|
vol.Optional(CONF_FEEDERS): vol.All(
|
||||||
),
|
cv.ensure_list, [cv.positive_int]
|
||||||
vol.Optional(CONF_FLAPS, default=[]): vol.All(
|
),
|
||||||
cv.ensure_list, [cv.positive_int]
|
vol.Optional(CONF_FLAPS): vol.All(
|
||||||
),
|
cv.ensure_list, [cv.positive_int]
|
||||||
vol.Optional(CONF_PETS): vol.All(cv.ensure_list, [cv.positive_int]),
|
),
|
||||||
vol.Optional(
|
vol.Optional(CONF_PETS): vol.All(cv.ensure_list, [cv.positive_int]),
|
||||||
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
|
vol.Optional(CONF_SCAN_INTERVAL): cv.time_period,
|
||||||
): cv.time_period,
|
},
|
||||||
}
|
cv.deprecated(CONF_FEEDERS),
|
||||||
|
cv.deprecated(CONF_FLAPS),
|
||||||
|
cv.deprecated(CONF_PETS),
|
||||||
|
cv.deprecated(CONF_SCAN_INTERVAL),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config) -> bool:
|
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||||
"""Initialize the Sure Petcare component."""
|
"""Set up the Sure Petcare integration."""
|
||||||
conf = config[DOMAIN]
|
conf = config[DOMAIN]
|
||||||
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
|
||||||
# update interval
|
|
||||||
scan_interval = conf[CONF_SCAN_INTERVAL]
|
|
||||||
|
|
||||||
# shared data
|
|
||||||
hass.data[DOMAIN] = hass.data[DATA_SURE_PETCARE] = {}
|
|
||||||
|
|
||||||
# sure petcare api connection
|
|
||||||
try:
|
try:
|
||||||
surepy = SurePetcare(
|
surepy = Surepy(
|
||||||
conf[CONF_USERNAME],
|
conf[CONF_USERNAME],
|
||||||
conf[CONF_PASSWORD],
|
conf[CONF_PASSWORD],
|
||||||
hass.loop,
|
auth_token=None,
|
||||||
async_get_clientsession(hass),
|
|
||||||
api_timeout=SURE_API_TIMEOUT,
|
api_timeout=SURE_API_TIMEOUT,
|
||||||
|
session=async_get_clientsession(hass),
|
||||||
)
|
)
|
||||||
|
|
||||||
except SurePetcareAuthenticationError:
|
except SurePetcareAuthenticationError:
|
||||||
_LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!")
|
_LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!")
|
||||||
return False
|
return False
|
||||||
@ -96,50 +82,12 @@ async def async_setup(hass, config) -> bool:
|
|||||||
_LOGGER.error("Unable to connect to surepetcare.io: Wrong %s!", error)
|
_LOGGER.error("Unable to connect to surepetcare.io: Wrong %s!", error)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# add feeders
|
spc = SurePetcareAPI(hass, surepy)
|
||||||
things = [
|
hass.data[DOMAIN][SPC] = spc
|
||||||
{CONF_ID: feeder, CONF_TYPE: SurepyProduct.FEEDER}
|
|
||||||
for feeder in conf[CONF_FEEDERS]
|
|
||||||
]
|
|
||||||
|
|
||||||
# add flaps (don't differentiate between CAT and PET for now)
|
|
||||||
things.extend(
|
|
||||||
[
|
|
||||||
{CONF_ID: flap, CONF_TYPE: SurepyProduct.PET_FLAP}
|
|
||||||
for flap in conf[CONF_FLAPS]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# discover hubs the flaps/feeders are connected to
|
|
||||||
hub_ids = set()
|
|
||||||
for device in things.copy():
|
|
||||||
device_data = await surepy.device(device[CONF_ID])
|
|
||||||
if (
|
|
||||||
CONF_PARENT in device_data
|
|
||||||
and device_data[CONF_PARENT][CONF_PRODUCT_ID] == SurepyProduct.HUB
|
|
||||||
and device_data[CONF_PARENT][CONF_ID] not in hub_ids
|
|
||||||
):
|
|
||||||
things.append(
|
|
||||||
{
|
|
||||||
CONF_ID: device_data[CONF_PARENT][CONF_ID],
|
|
||||||
CONF_TYPE: SurepyProduct.HUB,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
hub_ids.add(device_data[CONF_PARENT][CONF_ID])
|
|
||||||
|
|
||||||
# add pets
|
|
||||||
things.extend(
|
|
||||||
[{CONF_ID: pet, CONF_TYPE: SurepyProduct.PET} for pet in conf[CONF_PETS]]
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER.debug("Devices and Pets to setup: %s", things)
|
|
||||||
|
|
||||||
spc = hass.data[DATA_SURE_PETCARE][SPC] = SurePetcareAPI(hass, surepy, things)
|
|
||||||
|
|
||||||
# initial update
|
|
||||||
await spc.async_update()
|
await spc.async_update()
|
||||||
|
|
||||||
async_track_time_interval(hass, spc.async_update, scan_interval)
|
async_track_time_interval(hass, spc.async_update, SCAN_INTERVAL)
|
||||||
|
|
||||||
# load platforms
|
# load platforms
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -164,10 +112,12 @@ async def async_setup(hass, config) -> bool:
|
|||||||
vol.Lower,
|
vol.Lower,
|
||||||
vol.In(
|
vol.In(
|
||||||
[
|
[
|
||||||
SureLockStateID.UNLOCKED.name.lower(),
|
# https://github.com/PyCQA/pylint/issues/2062
|
||||||
SureLockStateID.LOCKED_IN.name.lower(),
|
# pylint: disable=no-member
|
||||||
SureLockStateID.LOCKED_OUT.name.lower(),
|
LockState.UNLOCKED.name.lower(),
|
||||||
SureLockStateID.LOCKED_ALL.name.lower(),
|
LockState.LOCKED_IN.name.lower(),
|
||||||
|
LockState.LOCKED_OUT.name.lower(),
|
||||||
|
LockState.LOCKED_ALL.name.lower(),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -187,50 +137,32 @@ async def async_setup(hass, config) -> bool:
|
|||||||
class SurePetcareAPI:
|
class SurePetcareAPI:
|
||||||
"""Define a generic Sure Petcare object."""
|
"""Define a generic Sure Petcare object."""
|
||||||
|
|
||||||
def __init__(self, hass, surepy: SurePetcare, ids: list[dict[str, Any]]) -> None:
|
def __init__(self, hass: HomeAssistant, surepy: Surepy) -> None:
|
||||||
"""Initialize the Sure Petcare object."""
|
"""Initialize the Sure Petcare object."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.surepy = surepy
|
self.surepy = surepy
|
||||||
self.ids = ids
|
self.states = {}
|
||||||
self.states: dict[str, Any] = {}
|
|
||||||
|
|
||||||
async def async_update(self, arg: Any = None) -> None:
|
async def async_update(self, _: Any = None) -> None:
|
||||||
"""Refresh Sure Petcare data."""
|
"""Get the latest data from Sure Petcare."""
|
||||||
|
|
||||||
# Fetch all data from SurePet API, refreshing the surepy cache
|
try:
|
||||||
# TODO: get surepy upstream to add a method to clear the cache explicitly pylint: disable=fixme
|
self.states = await self.surepy.get_entities()
|
||||||
await self.surepy._get_resource( # pylint: disable=protected-access
|
except SurePetcareError as error:
|
||||||
resource=MESTART_RESOURCE
|
_LOGGER.error("Unable to fetch data: %s", error)
|
||||||
)
|
|
||||||
for thing in self.ids:
|
|
||||||
sure_id = thing[CONF_ID]
|
|
||||||
sure_type = thing[CONF_TYPE]
|
|
||||||
|
|
||||||
try:
|
|
||||||
type_state = self.states.setdefault(sure_type, {})
|
|
||||||
|
|
||||||
if sure_type in [
|
|
||||||
SurepyProduct.CAT_FLAP,
|
|
||||||
SurepyProduct.PET_FLAP,
|
|
||||||
SurepyProduct.FEEDER,
|
|
||||||
SurepyProduct.HUB,
|
|
||||||
]:
|
|
||||||
type_state[sure_id] = await self.surepy.device(sure_id)
|
|
||||||
elif sure_type == SurepyProduct.PET:
|
|
||||||
type_state[sure_id] = await self.surepy.pet(sure_id)
|
|
||||||
|
|
||||||
except SurePetcareError as error:
|
|
||||||
_LOGGER.error("Unable to retrieve data from surepetcare.io: %s", error)
|
|
||||||
|
|
||||||
async_dispatcher_send(self.hass, TOPIC_UPDATE)
|
async_dispatcher_send(self.hass, TOPIC_UPDATE)
|
||||||
|
|
||||||
async def set_lock_state(self, flap_id: int, state: str) -> None:
|
async def set_lock_state(self, flap_id: int, state: str) -> None:
|
||||||
"""Update the lock state of a flap."""
|
"""Update the lock state of a flap."""
|
||||||
if state == SureLockStateID.UNLOCKED.name.lower():
|
|
||||||
|
# https://github.com/PyCQA/pylint/issues/2062
|
||||||
|
# pylint: disable=no-member
|
||||||
|
if state == LockState.UNLOCKED.name.lower():
|
||||||
await self.surepy.unlock(flap_id)
|
await self.surepy.unlock(flap_id)
|
||||||
elif state == SureLockStateID.LOCKED_IN.name.lower():
|
elif state == LockState.LOCKED_IN.name.lower():
|
||||||
await self.surepy.lock_in(flap_id)
|
await self.surepy.lock_in(flap_id)
|
||||||
elif state == SureLockStateID.LOCKED_OUT.name.lower():
|
elif state == LockState.LOCKED_OUT.name.lower():
|
||||||
await self.surepy.lock_out(flap_id)
|
await self.surepy.lock_out(flap_id)
|
||||||
elif state == SureLockStateID.LOCKED_ALL.name.lower():
|
elif state == LockState.LOCKED_ALL.name.lower():
|
||||||
await self.surepy.lock(flap_id)
|
await self.surepy.lock(flap_id)
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
"""Support for Sure PetCare Flaps/Pets binary sensors."""
|
"""Support for Sure PetCare Flaps/Pets binary sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from surepy import SureLocationID, SurepyProduct
|
from surepy.entities import SurepyEntity
|
||||||
|
from surepy.enums import EntityType, Location, SureEnum
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASS_CONNECTIVITY,
|
DEVICE_CLASS_CONNECTIVITY,
|
||||||
DEVICE_CLASS_PRESENCE,
|
DEVICE_CLASS_PRESENCE,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_ID, CONF_TYPE
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from . import SurePetcareAPI
|
from . import SurePetcareAPI
|
||||||
from .const import DATA_SURE_PETCARE, SPC, TOPIC_UPDATE
|
from .const import DOMAIN, SPC, TOPIC_UPDATE
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -29,30 +28,27 @@ async def async_setup_platform(
|
|||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
entities = []
|
entities: list[SurepyEntity] = []
|
||||||
|
|
||||||
spc = hass.data[DATA_SURE_PETCARE][SPC]
|
spc: SurePetcareAPI = hass.data[DOMAIN][SPC]
|
||||||
|
|
||||||
for thing in spc.ids:
|
for surepy_entity in spc.states.values():
|
||||||
sure_id = thing[CONF_ID]
|
|
||||||
sure_type = thing[CONF_TYPE]
|
|
||||||
|
|
||||||
# connectivity
|
# connectivity
|
||||||
if sure_type in [
|
if surepy_entity.type in [
|
||||||
SurepyProduct.CAT_FLAP,
|
EntityType.CAT_FLAP,
|
||||||
SurepyProduct.PET_FLAP,
|
EntityType.PET_FLAP,
|
||||||
SurepyProduct.FEEDER,
|
EntityType.FEEDER,
|
||||||
|
EntityType.FELAQUA,
|
||||||
]:
|
]:
|
||||||
entities.append(DeviceConnectivity(sure_id, sure_type, spc))
|
entities.append(
|
||||||
|
DeviceConnectivity(surepy_entity.id, surepy_entity.type, spc)
|
||||||
|
)
|
||||||
|
|
||||||
if sure_type == SurepyProduct.PET:
|
if surepy_entity.type == EntityType.PET:
|
||||||
entity = Pet(sure_id, spc)
|
entities.append(Pet(surepy_entity.id, spc))
|
||||||
elif sure_type == SurepyProduct.HUB:
|
elif surepy_entity.type == EntityType.HUB:
|
||||||
entity = Hub(sure_id, spc)
|
entities.append(Hub(surepy_entity.id, spc))
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
entities.append(entity)
|
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
@ -65,35 +61,29 @@ class SurePetcareBinarySensor(BinarySensorEntity):
|
|||||||
_id: int,
|
_id: int,
|
||||||
spc: SurePetcareAPI,
|
spc: SurePetcareAPI,
|
||||||
device_class: str,
|
device_class: str,
|
||||||
sure_type: SurepyProduct,
|
sure_type: EntityType,
|
||||||
):
|
):
|
||||||
"""Initialize a Sure Petcare binary sensor."""
|
"""Initialize a Sure Petcare binary sensor."""
|
||||||
|
|
||||||
self._id = _id
|
self._id = _id
|
||||||
self._sure_type = sure_type
|
|
||||||
self._device_class = device_class
|
self._device_class = device_class
|
||||||
|
|
||||||
self._spc: SurePetcareAPI = spc
|
self._spc: SurePetcareAPI = spc
|
||||||
self._spc_data: dict[str, Any] = self._spc.states[self._sure_type].get(self._id)
|
|
||||||
self._state: dict[str, Any] = {}
|
self._surepy_entity: SurepyEntity = self._spc.states[self._id]
|
||||||
|
self._state: SureEnum | dict[str, Any] = None
|
||||||
|
|
||||||
# cover special case where a device has no name set
|
# cover special case where a device has no name set
|
||||||
if "name" in self._spc_data:
|
if self._surepy_entity.name:
|
||||||
name = self._spc_data["name"]
|
name = self._surepy_entity.name
|
||||||
else:
|
else:
|
||||||
name = f"Unnamed {self._sure_type.name.capitalize()}"
|
name = f"Unnamed {self._surepy_entity.type.name.capitalize()}"
|
||||||
|
|
||||||
self._name = f"{self._sure_type.name.capitalize()} {name.capitalize()}"
|
self._name = f"{self._surepy_entity.type.name.capitalize()} {name.capitalize()}"
|
||||||
|
|
||||||
self._async_unsub_dispatcher_connect = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self) -> bool | None:
|
|
||||||
"""Return true if entity is on/unlocked."""
|
|
||||||
return bool(self._state)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self) -> bool:
|
def should_poll(self) -> bool:
|
||||||
"""Return true."""
|
"""Return if the entity should use default polling."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -109,30 +99,21 @@ class SurePetcareBinarySensor(BinarySensorEntity):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return an unique ID."""
|
"""Return an unique ID."""
|
||||||
return f"{self._spc_data['household_id']}-{self._id}"
|
return f"{self._surepy_entity.household_id}-{self._id}"
|
||||||
|
|
||||||
async def async_update(self) -> None:
|
@callback
|
||||||
|
def _async_update(self) -> None:
|
||||||
"""Get the latest data and update the state."""
|
"""Get the latest data and update the state."""
|
||||||
self._spc_data = self._spc.states[self._sure_type].get(self._id)
|
self._surepy_entity = self._spc.states[self._id]
|
||||||
self._state = self._spc_data.get("status")
|
self._state = self._surepy_entity.raw_data()["status"]
|
||||||
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
|
self.async_on_remove(
|
||||||
@callback
|
async_dispatcher_connect(self.hass, TOPIC_UPDATE, self._async_update)
|
||||||
def update() -> None:
|
|
||||||
"""Update the state."""
|
|
||||||
self.async_schedule_update_ha_state(True)
|
|
||||||
|
|
||||||
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
|
|
||||||
self.hass, TOPIC_UPDATE, update
|
|
||||||
)
|
)
|
||||||
|
self._async_update()
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
|
||||||
"""Disconnect dispatcher listener when removed."""
|
|
||||||
if self._async_unsub_dispatcher_connect:
|
|
||||||
self._async_unsub_dispatcher_connect()
|
|
||||||
|
|
||||||
|
|
||||||
class Hub(SurePetcareBinarySensor):
|
class Hub(SurePetcareBinarySensor):
|
||||||
@ -140,7 +121,7 @@ class Hub(SurePetcareBinarySensor):
|
|||||||
|
|
||||||
def __init__(self, _id: int, spc: SurePetcareAPI) -> None:
|
def __init__(self, _id: int, spc: SurePetcareAPI) -> None:
|
||||||
"""Initialize a Sure Petcare Hub."""
|
"""Initialize a Sure Petcare Hub."""
|
||||||
super().__init__(_id, spc, DEVICE_CLASS_CONNECTIVITY, SurepyProduct.HUB)
|
super().__init__(_id, spc, DEVICE_CLASS_CONNECTIVITY, EntityType.HUB)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
@ -156,10 +137,12 @@ class Hub(SurePetcareBinarySensor):
|
|||||||
def extra_state_attributes(self) -> dict[str, Any] | None:
|
def extra_state_attributes(self) -> dict[str, Any] | None:
|
||||||
"""Return the state attributes of the device."""
|
"""Return the state attributes of the device."""
|
||||||
attributes = None
|
attributes = None
|
||||||
if self._state:
|
if self._surepy_entity.raw_data():
|
||||||
attributes = {
|
attributes = {
|
||||||
"led_mode": int(self._state["led_mode"]),
|
"led_mode": int(self._surepy_entity.raw_data()["status"]["led_mode"]),
|
||||||
"pairing_mode": bool(self._state["pairing_mode"]),
|
"pairing_mode": bool(
|
||||||
|
self._surepy_entity.raw_data()["status"]["pairing_mode"]
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
@ -170,13 +153,13 @@ class Pet(SurePetcareBinarySensor):
|
|||||||
|
|
||||||
def __init__(self, _id: int, spc: SurePetcareAPI) -> None:
|
def __init__(self, _id: int, spc: SurePetcareAPI) -> None:
|
||||||
"""Initialize a Sure Petcare Pet."""
|
"""Initialize a Sure Petcare Pet."""
|
||||||
super().__init__(_id, spc, DEVICE_CLASS_PRESENCE, SurepyProduct.PET)
|
super().__init__(_id, spc, DEVICE_CLASS_PRESENCE, EntityType.PET)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if entity is at home."""
|
"""Return true if entity is at home."""
|
||||||
try:
|
try:
|
||||||
return bool(SureLocationID(self._state["where"]) == SureLocationID.INSIDE)
|
return bool(Location(self._state.where) == Location.INSIDE)
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -185,19 +168,15 @@ class Pet(SurePetcareBinarySensor):
|
|||||||
"""Return the state attributes of the device."""
|
"""Return the state attributes of the device."""
|
||||||
attributes = None
|
attributes = None
|
||||||
if self._state:
|
if self._state:
|
||||||
attributes = {
|
attributes = {"since": self._state.since, "where": self._state.where}
|
||||||
"since": str(
|
|
||||||
datetime.fromisoformat(self._state["since"]).replace(tzinfo=None)
|
|
||||||
),
|
|
||||||
"where": SureLocationID(self._state["where"]).name.capitalize(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
async def async_update(self) -> None:
|
@callback
|
||||||
|
def _async_update(self) -> None:
|
||||||
"""Get the latest data and update the state."""
|
"""Get the latest data and update the state."""
|
||||||
self._spc_data = self._spc.states[self._sure_type].get(self._id)
|
self._surepy_entity = self._spc.states[self._id]
|
||||||
self._state = self._spc_data.get("position")
|
self._state = self._surepy_entity.location
|
||||||
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
||||||
|
|
||||||
|
|
||||||
@ -207,7 +186,7 @@ class DeviceConnectivity(SurePetcareBinarySensor):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
_id: int,
|
_id: int,
|
||||||
sure_type: SurepyProduct,
|
sure_type: EntityType,
|
||||||
spc: SurePetcareAPI,
|
spc: SurePetcareAPI,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Sure Petcare Device."""
|
"""Initialize a Sure Petcare Device."""
|
||||||
@ -221,7 +200,7 @@ class DeviceConnectivity(SurePetcareBinarySensor):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return an unique ID."""
|
"""Return an unique ID."""
|
||||||
return f"{self._spc_data['household_id']}-{self._id}-connectivity"
|
return f"{self._surepy_entity.household_id}-{self._id}-connectivity"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
|
@ -1,24 +1,11 @@
|
|||||||
"""Constants for the Sure Petcare component."""
|
"""Constants for the Sure Petcare component."""
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
DOMAIN = "surepetcare"
|
DOMAIN = "surepetcare"
|
||||||
DEFAULT_DEVICE_CLASS = "lock"
|
|
||||||
DEFAULT_ICON = "mdi:cat"
|
|
||||||
DEFAULT_SCAN_INTERVAL = timedelta(minutes=3)
|
|
||||||
|
|
||||||
DATA_SURE_PETCARE = f"data_{DOMAIN}"
|
|
||||||
SPC = "spc"
|
SPC = "spc"
|
||||||
SUREPY = "surepy"
|
|
||||||
|
|
||||||
CONF_HOUSEHOLD_ID = "household_id"
|
|
||||||
CONF_FEEDERS = "feeders"
|
CONF_FEEDERS = "feeders"
|
||||||
CONF_FLAPS = "flaps"
|
CONF_FLAPS = "flaps"
|
||||||
CONF_PARENT = "parent"
|
|
||||||
CONF_PETS = "pets"
|
CONF_PETS = "pets"
|
||||||
CONF_PRODUCT_ID = "product_id"
|
|
||||||
CONF_DATA = "data"
|
|
||||||
|
|
||||||
SURE_IDS = "sure_ids"
|
|
||||||
|
|
||||||
# platforms
|
# platforms
|
||||||
TOPIC_UPDATE = f"{DOMAIN}_data_update"
|
TOPIC_UPDATE = f"{DOMAIN}_data_update"
|
||||||
@ -27,7 +14,6 @@ TOPIC_UPDATE = f"{DOMAIN}_data_update"
|
|||||||
SURE_API_TIMEOUT = 60
|
SURE_API_TIMEOUT = 60
|
||||||
|
|
||||||
# flap
|
# flap
|
||||||
BATTERY_ICON = "mdi:battery"
|
|
||||||
SURE_BATT_VOLTAGE_FULL = 1.6 # voltage
|
SURE_BATT_VOLTAGE_FULL = 1.6 # voltage
|
||||||
SURE_BATT_VOLTAGE_LOW = 1.25 # voltage
|
SURE_BATT_VOLTAGE_LOW = 1.25 # voltage
|
||||||
SURE_BATT_VOLTAGE_DIFF = SURE_BATT_VOLTAGE_FULL - SURE_BATT_VOLTAGE_LOW
|
SURE_BATT_VOLTAGE_DIFF = SURE_BATT_VOLTAGE_FULL - SURE_BATT_VOLTAGE_LOW
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
"name": "Sure Petcare",
|
"name": "Sure Petcare",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/surepetcare",
|
"documentation": "https://www.home-assistant.io/integrations/surepetcare",
|
||||||
"codeowners": ["@benleb"],
|
"codeowners": ["@benleb"],
|
||||||
"requirements": ["surepy==0.4.0"],
|
"requirements": ["surepy==0.6.0"],
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
}
|
}
|
||||||
|
@ -4,22 +4,17 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from surepy import SureLockStateID, SurepyProduct
|
from surepy.entities import SurepyEntity
|
||||||
|
from surepy.enums import EntityType
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.const import (
|
from homeassistant.const import ATTR_VOLTAGE, DEVICE_CLASS_BATTERY, PERCENTAGE
|
||||||
ATTR_VOLTAGE,
|
|
||||||
CONF_ID,
|
|
||||||
CONF_TYPE,
|
|
||||||
DEVICE_CLASS_BATTERY,
|
|
||||||
PERCENTAGE,
|
|
||||||
)
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from . import SurePetcareAPI
|
from . import SurePetcareAPI
|
||||||
from .const import (
|
from .const import (
|
||||||
DATA_SURE_PETCARE,
|
DOMAIN,
|
||||||
SPC,
|
SPC,
|
||||||
SURE_BATT_VOLTAGE_DIFF,
|
SURE_BATT_VOLTAGE_DIFF,
|
||||||
SURE_BATT_VOLTAGE_LOW,
|
SURE_BATT_VOLTAGE_LOW,
|
||||||
@ -34,56 +29,39 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
entities = []
|
entities: list[SurepyEntity] = []
|
||||||
|
|
||||||
spc = hass.data[DATA_SURE_PETCARE][SPC]
|
spc: SurePetcareAPI = hass.data[DOMAIN][SPC]
|
||||||
|
|
||||||
for entity in spc.ids:
|
for surepy_entity in spc.states.values():
|
||||||
sure_type = entity[CONF_TYPE]
|
|
||||||
|
|
||||||
if sure_type in [
|
if surepy_entity.type in [
|
||||||
SurepyProduct.CAT_FLAP,
|
EntityType.CAT_FLAP,
|
||||||
SurepyProduct.PET_FLAP,
|
EntityType.PET_FLAP,
|
||||||
SurepyProduct.FEEDER,
|
EntityType.FEEDER,
|
||||||
|
EntityType.FELAQUA,
|
||||||
]:
|
]:
|
||||||
entities.append(SureBattery(entity[CONF_ID], sure_type, spc))
|
entities.append(SureBattery(surepy_entity.id, spc))
|
||||||
|
|
||||||
if sure_type in [SurepyProduct.CAT_FLAP, SurepyProduct.PET_FLAP]:
|
async_add_entities(entities)
|
||||||
entities.append(Flap(entity[CONF_ID], sure_type, spc))
|
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
|
||||||
|
|
||||||
|
|
||||||
class SurePetcareSensor(SensorEntity):
|
class SurePetcareSensor(SensorEntity):
|
||||||
"""A binary sensor implementation for Sure Petcare Entities."""
|
"""A binary sensor implementation for Sure Petcare Entities."""
|
||||||
|
|
||||||
def __init__(self, _id: int, sure_type: SurepyProduct, spc: SurePetcareAPI):
|
def __init__(self, _id: int, spc: SurePetcareAPI):
|
||||||
"""Initialize a Sure Petcare sensor."""
|
"""Initialize a Sure Petcare sensor."""
|
||||||
|
|
||||||
self._id = _id
|
self._id = _id
|
||||||
self._sure_type = sure_type
|
self._spc: SurePetcareAPI = spc
|
||||||
|
|
||||||
self._spc = spc
|
self._surepy_entity: SurepyEntity = self._spc.states[_id]
|
||||||
self._spc_data: dict[str, Any] = self._spc.states[self._sure_type].get(self._id)
|
|
||||||
self._state: dict[str, Any] = {}
|
self._state: dict[str, Any] = {}
|
||||||
|
|
||||||
self._name = (
|
self._name = (
|
||||||
f"{self._sure_type.name.capitalize()} "
|
f"{self._surepy_entity.type.name.capitalize()} "
|
||||||
f"{self._spc_data['name'].capitalize()}"
|
f"{self._surepy_entity.name.capitalize()}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._async_unsub_dispatcher_connect = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
"""Return the name of the device if any."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return an unique ID."""
|
|
||||||
return f"{self._spc_data['household_id']}-{self._id}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return true if entity is available."""
|
"""Return true if entity is available."""
|
||||||
@ -94,46 +72,19 @@ class SurePetcareSensor(SensorEntity):
|
|||||||
"""Return true."""
|
"""Return true."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def async_update(self) -> None:
|
@callback
|
||||||
|
def _async_update(self) -> None:
|
||||||
"""Get the latest data and update the state."""
|
"""Get the latest data and update the state."""
|
||||||
self._spc_data = self._spc.states[self._sure_type].get(self._id)
|
self._surepy_entity = self._spc.states[self._id]
|
||||||
self._state = self._spc_data.get("status")
|
self._state = self._surepy_entity.raw_data()["status"]
|
||||||
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
_LOGGER.debug("%s -> self._state: %s", self._name, self._state)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
|
self.async_on_remove(
|
||||||
@callback
|
async_dispatcher_connect(self.hass, TOPIC_UPDATE, self._async_update)
|
||||||
def update() -> None:
|
|
||||||
"""Update the state."""
|
|
||||||
self.async_schedule_update_ha_state(True)
|
|
||||||
|
|
||||||
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
|
|
||||||
self.hass, TOPIC_UPDATE, update
|
|
||||||
)
|
)
|
||||||
|
self._async_update()
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
|
||||||
"""Disconnect dispatcher listener when removed."""
|
|
||||||
if self._async_unsub_dispatcher_connect:
|
|
||||||
self._async_unsub_dispatcher_connect()
|
|
||||||
|
|
||||||
|
|
||||||
class Flap(SurePetcareSensor):
|
|
||||||
"""Sure Petcare Flap."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self) -> int | None:
|
|
||||||
"""Return battery level in percent."""
|
|
||||||
return SureLockStateID(self._state["locking"]["mode"]).name.capitalize()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self) -> dict[str, Any] | None:
|
|
||||||
"""Return the state attributes of the device."""
|
|
||||||
attributes = None
|
|
||||||
if self._state:
|
|
||||||
attributes = {"learn_mode": bool(self._state["learn_mode"])}
|
|
||||||
|
|
||||||
return attributes
|
|
||||||
|
|
||||||
|
|
||||||
class SureBattery(SurePetcareSensor):
|
class SureBattery(SurePetcareSensor):
|
||||||
@ -160,7 +111,7 @@ class SureBattery(SurePetcareSensor):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return an unique ID."""
|
"""Return an unique ID."""
|
||||||
return f"{self._spc_data['household_id']}-{self._id}-battery"
|
return f"{self._surepy_entity.household_id}-{self._surepy_entity.id}-battery"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self) -> str:
|
def device_class(self) -> str:
|
||||||
|
@ -2178,7 +2178,7 @@ sucks==0.9.4
|
|||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.surepetcare
|
# homeassistant.components.surepetcare
|
||||||
surepy==0.4.0
|
surepy==0.6.0
|
||||||
|
|
||||||
# homeassistant.components.swiss_hydrological_data
|
# homeassistant.components.swiss_hydrological_data
|
||||||
swisshydrodata==0.1.0
|
swisshydrodata==0.1.0
|
||||||
|
@ -1165,7 +1165,7 @@ subarulink==0.3.12
|
|||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.surepetcare
|
# homeassistant.components.surepetcare
|
||||||
surepy==0.4.0
|
surepy==0.6.0
|
||||||
|
|
||||||
# homeassistant.components.synology_dsm
|
# homeassistant.components.synology_dsm
|
||||||
synologydsm-api==1.0.2
|
synologydsm-api==1.0.2
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
"""Tests for Sure Petcare integration."""
|
"""Tests for Sure Petcare integration."""
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.surepetcare.const import DOMAIN
|
from homeassistant.components.surepetcare.const import DOMAIN
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
HOUSEHOLD_ID = "household-id"
|
HOUSEHOLD_ID = 987654321
|
||||||
HUB_ID = "hub-id"
|
HUB_ID = 123456789
|
||||||
|
|
||||||
MOCK_HUB = {
|
MOCK_HUB = {
|
||||||
"id": HUB_ID,
|
"id": HUB_ID,
|
||||||
@ -79,10 +77,3 @@ MOCK_CONFIG = {
|
|||||||
"pets": [24680],
|
"pets": [24680],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _patch_sensor_setup():
|
|
||||||
return patch(
|
|
||||||
"homeassistant.components.surepetcare.sensor.async_setup_platform",
|
|
||||||
return_value=True,
|
|
||||||
)
|
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
"""Define fixtures available for all tests."""
|
"""Define fixtures available for all tests."""
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pytest import fixture
|
import pytest
|
||||||
from surepy import SurePetcare
|
from surepy import MESTART_RESOURCE
|
||||||
|
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from . import MOCK_API_DATA
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@pytest.fixture
|
||||||
async def surepetcare(hass):
|
async def surepetcare():
|
||||||
"""Mock the SurePetcare for easier testing."""
|
"""Mock the SurePetcare for easier testing."""
|
||||||
with patch("homeassistant.components.surepetcare.SurePetcare") as mock_surepetcare:
|
with patch("surepy.SureAPIClient", autospec=True) as mock_client_class, patch(
|
||||||
instance = mock_surepetcare.return_value = SurePetcare(
|
"surepy.find_token"
|
||||||
"test-username",
|
):
|
||||||
"test-password",
|
client = mock_client_class.return_value
|
||||||
hass.loop,
|
client.resources = {MESTART_RESOURCE: {"data": MOCK_API_DATA}}
|
||||||
async_get_clientsession(hass),
|
yield client
|
||||||
api_timeout=1,
|
|
||||||
)
|
|
||||||
instance._get_resource = AsyncMock(return_value=None)
|
|
||||||
yield mock_surepetcare
|
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
"""The tests for the Sure Petcare binary sensor platform."""
|
"""The tests for the Sure Petcare binary sensor platform."""
|
||||||
from surepy import MESTART_RESOURCE
|
|
||||||
|
|
||||||
from homeassistant.components.surepetcare.const import DOMAIN
|
from homeassistant.components.surepetcare.const import DOMAIN
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import MOCK_API_DATA, MOCK_CONFIG, _patch_sensor_setup
|
from . import HOUSEHOLD_ID, HUB_ID, MOCK_CONFIG
|
||||||
|
|
||||||
EXPECTED_ENTITY_IDS = {
|
EXPECTED_ENTITY_IDS = {
|
||||||
"binary_sensor.pet_flap_pet_flap_connectivity": "household-id-13576-connectivity",
|
"binary_sensor.pet_flap_pet_flap_connectivity": f"{HOUSEHOLD_ID}-13576-connectivity",
|
||||||
"binary_sensor.pet_flap_cat_flap_connectivity": "household-id-13579-connectivity",
|
"binary_sensor.cat_flap_cat_flap_connectivity": f"{HOUSEHOLD_ID}-13579-connectivity",
|
||||||
"binary_sensor.feeder_feeder_connectivity": "household-id-12345-connectivity",
|
"binary_sensor.feeder_feeder_connectivity": f"{HOUSEHOLD_ID}-12345-connectivity",
|
||||||
"binary_sensor.pet_pet": "household-id-24680",
|
"binary_sensor.pet_pet": f"{HOUSEHOLD_ID}-24680",
|
||||||
"binary_sensor.hub_hub": "household-id-hub-id",
|
"binary_sensor.hub_hub": f"{HOUSEHOLD_ID}-{HUB_ID}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensors(hass, surepetcare) -> None:
|
async def test_binary_sensors(hass, surepetcare) -> None:
|
||||||
"""Test the generation of unique ids."""
|
"""Test the generation of unique ids."""
|
||||||
instance = surepetcare.return_value
|
assert await async_setup_component(hass, DOMAIN, MOCK_CONFIG)
|
||||||
instance._resource[MESTART_RESOURCE] = {"data": MOCK_API_DATA}
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
with _patch_sensor_setup():
|
|
||||||
assert await async_setup_component(hass, DOMAIN, MOCK_CONFIG)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
state_entity_ids = hass.states.async_entity_ids()
|
state_entity_ids = hass.states.async_entity_ids()
|
||||||
|
|
||||||
for entity_id, unique_id in EXPECTED_ENTITY_IDS.items():
|
for entity_id, unique_id in EXPECTED_ENTITY_IDS.items():
|
||||||
assert entity_id in state_entity_ids
|
assert entity_id in state_entity_ids
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "on"
|
||||||
entity = entity_registry.async_get(entity_id)
|
entity = entity_registry.async_get(entity_id)
|
||||||
assert entity.unique_id == unique_id
|
assert entity.unique_id == unique_id
|
||||||
|
29
tests/components/surepetcare/test_sensor.py
Normal file
29
tests/components/surepetcare/test_sensor.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""Test the surepetcare sensor platform."""
|
||||||
|
from homeassistant.components.surepetcare.const import DOMAIN
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from . import HOUSEHOLD_ID, MOCK_CONFIG
|
||||||
|
|
||||||
|
EXPECTED_ENTITY_IDS = {
|
||||||
|
"sensor.pet_flap_pet_flap_battery_level": f"{HOUSEHOLD_ID}-13576-battery",
|
||||||
|
"sensor.cat_flap_cat_flap_battery_level": f"{HOUSEHOLD_ID}-13579-battery",
|
||||||
|
"sensor.feeder_feeder_battery_level": f"{HOUSEHOLD_ID}-12345-battery",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_binary_sensors(hass, surepetcare) -> None:
|
||||||
|
"""Test the generation of unique ids."""
|
||||||
|
assert await async_setup_component(hass, DOMAIN, MOCK_CONFIG)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
state_entity_ids = hass.states.async_entity_ids()
|
||||||
|
|
||||||
|
for entity_id, unique_id in EXPECTED_ENTITY_IDS.items():
|
||||||
|
assert entity_id in state_entity_ids
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "100"
|
||||||
|
entity = entity_registry.async_get(entity_id)
|
||||||
|
assert entity.unique_id == unique_id
|
Loading…
x
Reference in New Issue
Block a user