mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Refactor Bring! integration to poll activity data at a slower interval (#142621)
* Refactor Bring integration to poll activity with slower interval * add test
This commit is contained in:
parent
4cecb6c851
commit
9a2f17c2b2
@ -10,7 +10,12 @@ from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .coordinator import BringConfigEntry, BringDataUpdateCoordinator
|
from .coordinator import (
|
||||||
|
BringActivityCoordinator,
|
||||||
|
BringConfigEntry,
|
||||||
|
BringCoordinators,
|
||||||
|
BringDataUpdateCoordinator,
|
||||||
|
)
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.EVENT, Platform.SENSOR, Platform.TODO]
|
PLATFORMS: list[Platform] = [Platform.EVENT, Platform.SENSOR, Platform.TODO]
|
||||||
|
|
||||||
@ -26,7 +31,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: BringConfigEntry) -> boo
|
|||||||
coordinator = BringDataUpdateCoordinator(hass, entry, bring)
|
coordinator = BringDataUpdateCoordinator(hass, entry, bring)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
entry.runtime_data = coordinator
|
activity_coordinator = BringActivityCoordinator(hass, entry, coordinator)
|
||||||
|
await activity_coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
entry.runtime_data = BringCoordinators(coordinator, activity_coordinator)
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
@ -30,7 +30,15 @@ from .const import DOMAIN
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
type BringConfigEntry = ConfigEntry[BringDataUpdateCoordinator]
|
type BringConfigEntry = ConfigEntry[BringCoordinators]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BringCoordinators:
|
||||||
|
"""Data class holding coordinators."""
|
||||||
|
|
||||||
|
data: BringDataUpdateCoordinator
|
||||||
|
activity: BringActivityCoordinator
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -39,17 +47,28 @@ class BringData(DataClassORJSONMixin):
|
|||||||
|
|
||||||
lst: BringList
|
lst: BringList
|
||||||
content: BringItemsResponse
|
content: BringItemsResponse
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class BringActivityData(DataClassORJSONMixin):
|
||||||
|
"""Coordinator data class."""
|
||||||
|
|
||||||
activity: BringActivityResponse
|
activity: BringActivityResponse
|
||||||
users: BringUsersResponse
|
users: BringUsersResponse
|
||||||
|
|
||||||
|
|
||||||
class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
|
class BringBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
||||||
"""A Bring Data Update Coordinator."""
|
"""Bring base coordinator."""
|
||||||
|
|
||||||
config_entry: BringConfigEntry
|
config_entry: BringConfigEntry
|
||||||
user_settings: BringUserSettingsResponse
|
|
||||||
lists: list[BringList]
|
lists: list[BringList]
|
||||||
|
|
||||||
|
|
||||||
|
class BringDataUpdateCoordinator(BringBaseCoordinator[dict[str, BringData]]):
|
||||||
|
"""A Bring Data Update Coordinator."""
|
||||||
|
|
||||||
|
user_settings: BringUserSettingsResponse
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, config_entry: BringConfigEntry, bring: Bring
|
self, hass: HomeAssistant, config_entry: BringConfigEntry, bring: Bring
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -90,16 +109,19 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
|
|||||||
current_lists := {lst.listUuid for lst in self.lists}
|
current_lists := {lst.listUuid for lst in self.lists}
|
||||||
):
|
):
|
||||||
self._purge_deleted_lists()
|
self._purge_deleted_lists()
|
||||||
|
new_lists = current_lists - self.previous_lists
|
||||||
self.previous_lists = current_lists
|
self.previous_lists = current_lists
|
||||||
|
|
||||||
list_dict: dict[str, BringData] = {}
|
list_dict: dict[str, BringData] = {}
|
||||||
for lst in self.lists:
|
for lst in self.lists:
|
||||||
if (ctx := set(self.async_contexts())) and lst.listUuid not in ctx:
|
if (
|
||||||
|
(ctx := set(self.async_contexts()))
|
||||||
|
and lst.listUuid not in ctx
|
||||||
|
and lst.listUuid not in new_lists
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
items = await self.bring.get_list(lst.listUuid)
|
items = await self.bring.get_list(lst.listUuid)
|
||||||
activity = await self.bring.get_activity(lst.listUuid)
|
|
||||||
users = await self.bring.get_list_users(lst.listUuid)
|
|
||||||
except BringRequestException as e:
|
except BringRequestException as e:
|
||||||
raise UpdateFailed(
|
raise UpdateFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=DOMAIN,
|
||||||
@ -111,7 +133,7 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
|
|||||||
translation_key="setup_parse_exception",
|
translation_key="setup_parse_exception",
|
||||||
) from e
|
) from e
|
||||||
else:
|
else:
|
||||||
list_dict[lst.listUuid] = BringData(lst, items, activity, users)
|
list_dict[lst.listUuid] = BringData(lst, items)
|
||||||
|
|
||||||
return list_dict
|
return list_dict
|
||||||
|
|
||||||
@ -156,3 +178,60 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
|
|||||||
device_reg.async_update_device(
|
device_reg.async_update_device(
|
||||||
device.id, remove_config_entry_id=self.config_entry.entry_id
|
device.id, remove_config_entry_id=self.config_entry.entry_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BringActivityCoordinator(BringBaseCoordinator[dict[str, BringActivityData]]):
|
||||||
|
"""A Bring Activity Data Update Coordinator."""
|
||||||
|
|
||||||
|
user_settings: BringUserSettingsResponse
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: BringConfigEntry,
|
||||||
|
coordinator: BringDataUpdateCoordinator,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Bring Activity data coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
config_entry=config_entry,
|
||||||
|
name=DOMAIN,
|
||||||
|
update_interval=timedelta(minutes=10),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.coordinator = coordinator
|
||||||
|
self.lists = coordinator.lists
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> dict[str, BringActivityData]:
|
||||||
|
"""Fetch activity data from bring."""
|
||||||
|
|
||||||
|
list_dict: dict[str, BringActivityData] = {}
|
||||||
|
for lst in self.lists:
|
||||||
|
if (
|
||||||
|
ctx := set(self.coordinator.async_contexts())
|
||||||
|
) and lst.listUuid not in ctx:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
activity = await self.coordinator.bring.get_activity(lst.listUuid)
|
||||||
|
users = await self.coordinator.bring.get_list_users(lst.listUuid)
|
||||||
|
except BringAuthException as e:
|
||||||
|
raise ConfigEntryAuthFailed(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="setup_authentication_exception",
|
||||||
|
translation_placeholders={CONF_EMAIL: self.coordinator.bring.mail},
|
||||||
|
) from e
|
||||||
|
except BringRequestException as e:
|
||||||
|
raise UpdateFailed(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="setup_request_exception",
|
||||||
|
) from e
|
||||||
|
except BringParseException as e:
|
||||||
|
raise UpdateFailed(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="setup_parse_exception",
|
||||||
|
) from e
|
||||||
|
else:
|
||||||
|
list_dict[lst.listUuid] = BringActivityData(activity, users)
|
||||||
|
|
||||||
|
return list_dict
|
||||||
|
@ -20,9 +20,12 @@ async def async_get_config_entry_diagnostics(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"data": {
|
"data": {
|
||||||
k: async_redact_data(v.to_dict(), TO_REDACT)
|
k: v.to_dict() for k, v in config_entry.runtime_data.data.data.items()
|
||||||
for k, v in config_entry.runtime_data.data.items()
|
|
||||||
},
|
},
|
||||||
"lists": [lst.to_dict() for lst in config_entry.runtime_data.lists],
|
"activity": {
|
||||||
"user_settings": config_entry.runtime_data.user_settings.to_dict(),
|
k: async_redact_data(v.to_dict(), TO_REDACT)
|
||||||
|
for k, v in config_entry.runtime_data.activity.data.items()
|
||||||
|
},
|
||||||
|
"lists": [lst.to_dict() for lst in config_entry.runtime_data.data.lists],
|
||||||
|
"user_settings": config_entry.runtime_data.data.user_settings.to_dict(),
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,17 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
|||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import BringDataUpdateCoordinator
|
from .coordinator import BringBaseCoordinator
|
||||||
|
|
||||||
|
|
||||||
class BringBaseEntity(CoordinatorEntity[BringDataUpdateCoordinator]):
|
class BringBaseEntity(CoordinatorEntity[BringBaseCoordinator]):
|
||||||
"""Bring base entity."""
|
"""Bring base entity."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: BringDataUpdateCoordinator,
|
coordinator: BringBaseCoordinator,
|
||||||
bring_list: BringList,
|
bring_list: BringList,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
@ -34,5 +34,7 @@ class BringBaseEntity(CoordinatorEntity[BringDataUpdateCoordinator]):
|
|||||||
},
|
},
|
||||||
manufacturer="Bring! Labs AG",
|
manufacturer="Bring! Labs AG",
|
||||||
model="Bring! Grocery Shopping List",
|
model="Bring! Grocery Shopping List",
|
||||||
configuration_url=f"https://web.getbring.com/app/lists/{list(self.coordinator.lists).index(bring_list)}",
|
configuration_url=f"https://web.getbring.com/app/lists/{list(self.coordinator.lists).index(bring_list)}"
|
||||||
|
if bring_list in self.coordinator.lists
|
||||||
|
else None,
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
from . import BringConfigEntry
|
from . import BringConfigEntry
|
||||||
from .coordinator import BringDataUpdateCoordinator
|
from .coordinator import BringActivityCoordinator
|
||||||
from .entity import BringBaseEntity
|
from .entity import BringBaseEntity
|
||||||
|
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
@ -32,18 +32,18 @@ async def async_setup_entry(
|
|||||||
"""Add event entities."""
|
"""Add event entities."""
|
||||||
nonlocal lists_added
|
nonlocal lists_added
|
||||||
|
|
||||||
if new_lists := {lst.listUuid for lst in coordinator.lists} - lists_added:
|
if new_lists := {lst.listUuid for lst in coordinator.data.lists} - lists_added:
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
BringEventEntity(
|
BringEventEntity(
|
||||||
coordinator,
|
coordinator.activity,
|
||||||
bring_list,
|
bring_list,
|
||||||
)
|
)
|
||||||
for bring_list in coordinator.lists
|
for bring_list in coordinator.data.lists
|
||||||
if bring_list.listUuid in new_lists
|
if bring_list.listUuid in new_lists
|
||||||
)
|
)
|
||||||
lists_added |= new_lists
|
lists_added |= new_lists
|
||||||
|
|
||||||
coordinator.async_add_listener(add_entities)
|
coordinator.activity.async_add_listener(add_entities)
|
||||||
add_entities()
|
add_entities()
|
||||||
|
|
||||||
|
|
||||||
@ -51,10 +51,11 @@ class BringEventEntity(BringBaseEntity, EventEntity):
|
|||||||
"""An event entity."""
|
"""An event entity."""
|
||||||
|
|
||||||
_attr_translation_key = "activities"
|
_attr_translation_key = "activities"
|
||||||
|
coordinator: BringActivityCoordinator
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: BringDataUpdateCoordinator,
|
coordinator: BringActivityCoordinator,
|
||||||
bring_list: BringList,
|
bring_list: BringList,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
|
@ -88,7 +88,7 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the sensor platform."""
|
"""Set up the sensor platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data.data
|
||||||
lists_added: set[str] = set()
|
lists_added: set[str] = set()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -117,6 +117,7 @@ class BringSensorEntity(BringBaseEntity, SensorEntity):
|
|||||||
"""A sensor entity."""
|
"""A sensor entity."""
|
||||||
|
|
||||||
entity_description: BringSensorEntityDescription
|
entity_description: BringSensorEntityDescription
|
||||||
|
coordinator: BringDataUpdateCoordinator
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -44,7 +44,7 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the sensor from a config entry created in the integrations UI."""
|
"""Set up the sensor from a config entry created in the integrations UI."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data.data
|
||||||
lists_added: set[str] = set()
|
lists_added: set[str] = set()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -88,6 +88,7 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
|
|||||||
| TodoListEntityFeature.DELETE_TODO_ITEM
|
| TodoListEntityFeature.DELETE_TODO_ITEM
|
||||||
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
|
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
|
||||||
)
|
)
|
||||||
|
coordinator: BringDataUpdateCoordinator
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: BringDataUpdateCoordinator, bring_list: BringList
|
self, coordinator: BringDataUpdateCoordinator, bring_list: BringList
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
# name: test_diagnostics
|
# name: test_diagnostics
|
||||||
dict({
|
dict({
|
||||||
'data': dict({
|
'activity': dict({
|
||||||
'b4776778-7f6c-496e-951b-92a35d3db0dd': dict({
|
'b4776778-7f6c-496e-951b-92a35d3db0dd': dict({
|
||||||
'activity': dict({
|
'activity': dict({
|
||||||
'timeline': list([
|
'timeline': list([
|
||||||
@ -79,58 +79,6 @@
|
|||||||
'timestamp': '2025-01-01T03:09:33.036000+00:00',
|
'timestamp': '2025-01-01T03:09:33.036000+00:00',
|
||||||
'totalEvents': 3,
|
'totalEvents': 3,
|
||||||
}),
|
}),
|
||||||
'content': dict({
|
|
||||||
'items': dict({
|
|
||||||
'purchase': list([
|
|
||||||
dict({
|
|
||||||
'attributes': list([
|
|
||||||
dict({
|
|
||||||
'content': dict({
|
|
||||||
'convenient': True,
|
|
||||||
'discounted': True,
|
|
||||||
'urgent': True,
|
|
||||||
}),
|
|
||||||
'type': 'PURCHASE_CONDITIONS',
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
'itemId': 'Paprika',
|
|
||||||
'specification': 'Rot',
|
|
||||||
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
|
|
||||||
}),
|
|
||||||
dict({
|
|
||||||
'attributes': list([
|
|
||||||
dict({
|
|
||||||
'content': dict({
|
|
||||||
'convenient': True,
|
|
||||||
'discounted': True,
|
|
||||||
'urgent': True,
|
|
||||||
}),
|
|
||||||
'type': 'PURCHASE_CONDITIONS',
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
'itemId': 'Pouletbrüstli',
|
|
||||||
'specification': 'Bio',
|
|
||||||
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
'recently': list([
|
|
||||||
dict({
|
|
||||||
'attributes': list([
|
|
||||||
]),
|
|
||||||
'itemId': 'Ananas',
|
|
||||||
'specification': '',
|
|
||||||
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
'status': 'REGISTERED',
|
|
||||||
'uuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
|
|
||||||
}),
|
|
||||||
'lst': dict({
|
|
||||||
'listUuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
|
|
||||||
'name': '**REDACTED**',
|
|
||||||
'theme': 'ch.publisheria.bring.theme.home',
|
|
||||||
}),
|
|
||||||
'users': dict({
|
'users': dict({
|
||||||
'users': list([
|
'users': list([
|
||||||
dict({
|
dict({
|
||||||
@ -246,6 +194,101 @@
|
|||||||
'timestamp': '2025-01-01T03:09:33.036000+00:00',
|
'timestamp': '2025-01-01T03:09:33.036000+00:00',
|
||||||
'totalEvents': 3,
|
'totalEvents': 3,
|
||||||
}),
|
}),
|
||||||
|
'users': dict({
|
||||||
|
'users': list([
|
||||||
|
dict({
|
||||||
|
'country': 'DE',
|
||||||
|
'email': '**REDACTED**',
|
||||||
|
'language': 'de',
|
||||||
|
'name': '**REDACTED**',
|
||||||
|
'photoPath': '',
|
||||||
|
'plusExpiry': None,
|
||||||
|
'plusTryOut': False,
|
||||||
|
'publicUuid': '9a21fdfc-63a4-441a-afc1-ef3030605a9d',
|
||||||
|
'pushEnabled': True,
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'country': 'US',
|
||||||
|
'email': '**REDACTED**',
|
||||||
|
'language': 'en',
|
||||||
|
'name': '**REDACTED**',
|
||||||
|
'photoPath': '',
|
||||||
|
'plusExpiry': None,
|
||||||
|
'plusTryOut': False,
|
||||||
|
'publicUuid': '73af455f-c158-4004-a5e0-79f4f8a6d4bd',
|
||||||
|
'pushEnabled': True,
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'country': 'US',
|
||||||
|
'email': None,
|
||||||
|
'language': 'en',
|
||||||
|
'name': None,
|
||||||
|
'photoPath': None,
|
||||||
|
'plusExpiry': None,
|
||||||
|
'plusTryOut': False,
|
||||||
|
'publicUuid': '7d5e9d08-877a-4c36-8740-a9bf74ec690a',
|
||||||
|
'pushEnabled': True,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'data': dict({
|
||||||
|
'b4776778-7f6c-496e-951b-92a35d3db0dd': dict({
|
||||||
|
'content': dict({
|
||||||
|
'items': dict({
|
||||||
|
'purchase': list([
|
||||||
|
dict({
|
||||||
|
'attributes': list([
|
||||||
|
dict({
|
||||||
|
'content': dict({
|
||||||
|
'convenient': True,
|
||||||
|
'discounted': True,
|
||||||
|
'urgent': True,
|
||||||
|
}),
|
||||||
|
'type': 'PURCHASE_CONDITIONS',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'itemId': 'Paprika',
|
||||||
|
'specification': 'Rot',
|
||||||
|
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'attributes': list([
|
||||||
|
dict({
|
||||||
|
'content': dict({
|
||||||
|
'convenient': True,
|
||||||
|
'discounted': True,
|
||||||
|
'urgent': True,
|
||||||
|
}),
|
||||||
|
'type': 'PURCHASE_CONDITIONS',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'itemId': 'Pouletbrüstli',
|
||||||
|
'specification': 'Bio',
|
||||||
|
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'recently': list([
|
||||||
|
dict({
|
||||||
|
'attributes': list([
|
||||||
|
]),
|
||||||
|
'itemId': 'Ananas',
|
||||||
|
'specification': '',
|
||||||
|
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'status': 'REGISTERED',
|
||||||
|
'uuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
|
||||||
|
}),
|
||||||
|
'lst': dict({
|
||||||
|
'listUuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
|
||||||
|
'name': 'Baumarkt',
|
||||||
|
'theme': 'ch.publisheria.bring.theme.home',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'e542eef6-dba7-4c31-a52c-29e6ab9d83a5': dict({
|
||||||
'content': dict({
|
'content': dict({
|
||||||
'items': dict({
|
'items': dict({
|
||||||
'purchase': list([
|
'purchase': list([
|
||||||
@ -295,46 +338,9 @@
|
|||||||
}),
|
}),
|
||||||
'lst': dict({
|
'lst': dict({
|
||||||
'listUuid': 'e542eef6-dba7-4c31-a52c-29e6ab9d83a5',
|
'listUuid': 'e542eef6-dba7-4c31-a52c-29e6ab9d83a5',
|
||||||
'name': '**REDACTED**',
|
'name': 'Einkauf',
|
||||||
'theme': 'ch.publisheria.bring.theme.home',
|
'theme': 'ch.publisheria.bring.theme.home',
|
||||||
}),
|
}),
|
||||||
'users': dict({
|
|
||||||
'users': list([
|
|
||||||
dict({
|
|
||||||
'country': 'DE',
|
|
||||||
'email': '**REDACTED**',
|
|
||||||
'language': 'de',
|
|
||||||
'name': '**REDACTED**',
|
|
||||||
'photoPath': '',
|
|
||||||
'plusExpiry': None,
|
|
||||||
'plusTryOut': False,
|
|
||||||
'publicUuid': '9a21fdfc-63a4-441a-afc1-ef3030605a9d',
|
|
||||||
'pushEnabled': True,
|
|
||||||
}),
|
|
||||||
dict({
|
|
||||||
'country': 'US',
|
|
||||||
'email': '**REDACTED**',
|
|
||||||
'language': 'en',
|
|
||||||
'name': '**REDACTED**',
|
|
||||||
'photoPath': '',
|
|
||||||
'plusExpiry': None,
|
|
||||||
'plusTryOut': False,
|
|
||||||
'publicUuid': '73af455f-c158-4004-a5e0-79f4f8a6d4bd',
|
|
||||||
'pushEnabled': True,
|
|
||||||
}),
|
|
||||||
dict({
|
|
||||||
'country': 'US',
|
|
||||||
'email': None,
|
|
||||||
'language': 'en',
|
|
||||||
'name': None,
|
|
||||||
'photoPath': None,
|
|
||||||
'plusExpiry': None,
|
|
||||||
'plusTryOut': False,
|
|
||||||
'publicUuid': '7d5e9d08-877a-4c36-8740-a9bf74ec690a',
|
|
||||||
'pushEnabled': True,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'lists': list([
|
'lists': list([
|
||||||
|
@ -139,6 +139,31 @@ async def test_config_entry_not_ready_udpdate_failed(
|
|||||||
assert bring_config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert bring_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("exception", "state"),
|
||||||
|
[
|
||||||
|
(BringRequestException, ConfigEntryState.SETUP_RETRY),
|
||||||
|
(BringParseException, ConfigEntryState.SETUP_RETRY),
|
||||||
|
(BringAuthException, ConfigEntryState.SETUP_ERROR),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_activity_coordinator_errors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
bring_config_entry: MockConfigEntry,
|
||||||
|
mock_bring_client: AsyncMock,
|
||||||
|
exception: Exception,
|
||||||
|
state: ConfigEntryState,
|
||||||
|
) -> None:
|
||||||
|
"""Test config entry not ready from update failed in _async_update_data."""
|
||||||
|
mock_bring_client.get_activity.side_effect = exception
|
||||||
|
|
||||||
|
bring_config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(bring_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert bring_config_entry.state is state
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("exception", "state"),
|
("exception", "state"),
|
||||||
[
|
[
|
||||||
@ -263,3 +288,44 @@ async def test_create_devices(
|
|||||||
assert device_registry.async_get_device(
|
assert device_registry.async_get_device(
|
||||||
{(DOMAIN, f"{bring_config_entry.unique_id}_{list_uuid}")}
|
{(DOMAIN, f"{bring_config_entry.unique_id}_{list_uuid}")}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_bring_client")
|
||||||
|
async def test_coordinator_update_intervals(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
bring_config_entry: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
mock_bring_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the coordinator updates at the specified intervals."""
|
||||||
|
await setup_integration(hass, bring_config_entry)
|
||||||
|
|
||||||
|
assert bring_config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
# fetch 2 lists on first refresh
|
||||||
|
assert mock_bring_client.load_lists.await_count == 2
|
||||||
|
assert mock_bring_client.get_activity.await_count == 2
|
||||||
|
|
||||||
|
mock_bring_client.load_lists.reset_mock()
|
||||||
|
mock_bring_client.get_activity.reset_mock()
|
||||||
|
|
||||||
|
mock_bring_client.load_lists.return_value = BringListResponse.from_json(
|
||||||
|
load_fixture("lists2.json", DOMAIN)
|
||||||
|
)
|
||||||
|
freezer.tick(timedelta(seconds=90))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# main coordinator refreshes, activity does not
|
||||||
|
assert mock_bring_client.load_lists.await_count == 1
|
||||||
|
assert mock_bring_client.get_activity.await_count == 0
|
||||||
|
|
||||||
|
mock_bring_client.load_lists.reset_mock()
|
||||||
|
mock_bring_client.get_activity.reset_mock()
|
||||||
|
|
||||||
|
freezer.tick(timedelta(seconds=510))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# assert activity refreshes after 10min and has up-to-date lists data
|
||||||
|
assert mock_bring_client.get_activity.await_count == 1
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
"""Test for utility functions of the Bring! integration."""
|
"""Test for utility functions of the Bring! integration."""
|
||||||
|
|
||||||
from bring_api import (
|
from bring_api import BringItemsResponse, BringListResponse, BringUserSettingsResponse
|
||||||
BringActivityResponse,
|
|
||||||
BringItemsResponse,
|
|
||||||
BringListResponse,
|
|
||||||
BringUserSettingsResponse,
|
|
||||||
)
|
|
||||||
from bring_api.types import BringUsersResponse
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.bring.const import DOMAIN
|
from homeassistant.components.bring.const import DOMAIN
|
||||||
@ -47,10 +41,8 @@ def test_sum_attributes(attribute: str, expected: int) -> None:
|
|||||||
"""Test function sum_attributes."""
|
"""Test function sum_attributes."""
|
||||||
items = BringItemsResponse.from_json(load_fixture("items.json", DOMAIN))
|
items = BringItemsResponse.from_json(load_fixture("items.json", DOMAIN))
|
||||||
lst = BringListResponse.from_json(load_fixture("lists.json", DOMAIN))
|
lst = BringListResponse.from_json(load_fixture("lists.json", DOMAIN))
|
||||||
activity = BringActivityResponse.from_json(load_fixture("activity.json", DOMAIN))
|
|
||||||
users = BringUsersResponse.from_json(load_fixture("users.json", DOMAIN))
|
|
||||||
result = sum_attributes(
|
result = sum_attributes(
|
||||||
BringData(lst.lists[0], items, activity, users),
|
BringData(lst.lists[0], items),
|
||||||
attribute,
|
attribute,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user