Add sensor platform to Bring integration (#126642)

* Add sensor platform to Bring integration

* Add more tests

* unignore typedef check

* Update language sensor

* update snapshot

* changes

* add entities

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* add units

* lowercase

* snapshot

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
pull/126699/head
Manu 2024-09-24 22:55:48 +02:00 committed by GitHub
parent c66e2dc076
commit 20030ab604
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 910 additions and 9 deletions

View File

@ -20,7 +20,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .coordinator import BringDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.TODO]
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.TODO]
_LOGGER = logging.getLogger(__name__)

View File

@ -9,3 +9,4 @@ ATTR_ITEM_NAME: Final = "item"
ATTR_NOTIFICATION_TYPE: Final = "message"
SERVICE_PUSH_NOTIFICATION = "send_message"
UNIT_ITEMS = "items"

View File

@ -11,7 +11,7 @@ from bring_api import (
BringParseException,
BringRequestException,
)
from bring_api.types import BringItemsResponse, BringList
from bring_api.types import BringItemsResponse, BringList, BringUserSettingsResponse
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL
@ -32,6 +32,7 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
"""A Bring Data Update Coordinator."""
config_entry: ConfigEntry
user_settings: BringUserSettingsResponse
def __init__(self, hass: HomeAssistant, bring: Bring) -> None:
"""Initialize the Bring data coordinator."""
@ -81,3 +82,17 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
list_dict[lst["listUuid"]] = BringData(**lst, **items)
return list_dict
async def _async_setup(self) -> None:
"""Set up coordinator."""
await self.async_refresh_user_settings()
async def async_refresh_user_settings(self) -> None:
"""Refresh user settings."""
try:
self.user_settings = await self.bring.get_all_user_settings()
except (BringAuthException, BringRequestException, BringParseException) as e:
raise UpdateFailed(
"Unable to connect and retrieve user settings from bring"
) from e

View File

@ -23,7 +23,6 @@ class BringBaseEntity(CoordinatorEntity[BringDataUpdateCoordinator]):
super().__init__(coordinator)
self._list_uuid = bring_list["listUuid"]
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_{self._list_uuid}"
self.device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,

View File

@ -1,5 +1,19 @@
{
"entity": {
"sensor": {
"urgent": {
"default": "mdi:run-fast"
},
"discounted": {
"default": "mdi:brightness-percent"
},
"convenient": {
"default": "mdi:fridge-outline"
},
"list_language": {
"default": "mdi:earth"
}
},
"todo": {
"shopping_list": {
"default": "mdi:cart"

View File

@ -0,0 +1,121 @@
"""Sensor platform for the Bring! integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
from bring_api import BringUserSettingsResponse
from bring_api.const import BRING_SUPPORTED_LOCALES
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import BringConfigEntry
from .const import UNIT_ITEMS
from .coordinator import BringData, BringDataUpdateCoordinator
from .entity import BringBaseEntity
from .util import list_language, sum_attributes
@dataclass(kw_only=True, frozen=True)
class BringSensorEntityDescription(SensorEntityDescription):
"""Bring Sensor Description."""
value_fn: Callable[[BringData, BringUserSettingsResponse], StateType]
class BringSensor(StrEnum):
"""Bring sensors."""
URGENT = "urgent"
CONVENIENT = "convenient"
DISCOUNTED = "discounted"
LIST_LANGUAGE = "list_language"
SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
BringSensorEntityDescription(
key=BringSensor.URGENT,
translation_key=BringSensor.URGENT,
value_fn=lambda lst, _: sum_attributes(lst, "urgent"),
native_unit_of_measurement=UNIT_ITEMS,
),
BringSensorEntityDescription(
key=BringSensor.CONVENIENT,
translation_key=BringSensor.CONVENIENT,
value_fn=lambda lst, _: sum_attributes(lst, "convenient"),
native_unit_of_measurement=UNIT_ITEMS,
),
BringSensorEntityDescription(
key=BringSensor.DISCOUNTED,
translation_key=BringSensor.DISCOUNTED,
value_fn=lambda lst, _: sum_attributes(lst, "discounted"),
native_unit_of_measurement=UNIT_ITEMS,
),
BringSensorEntityDescription(
key=BringSensor.LIST_LANGUAGE,
translation_key=BringSensor.LIST_LANGUAGE,
value_fn=(
lambda lst, settings: x.lower()
if (x := list_language(lst["listUuid"], settings))
else None
),
entity_category=EntityCategory.DIAGNOSTIC,
options=[x.lower() for x in BRING_SUPPORTED_LOCALES],
device_class=SensorDeviceClass.ENUM,
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BringConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the sensor platform."""
coordinator = config_entry.runtime_data
async_add_entities(
BringSensorEntity(
coordinator,
bring_list,
description,
)
for description in SENSOR_DESCRIPTIONS
for bring_list in coordinator.data.values()
)
class BringSensorEntity(BringBaseEntity, SensorEntity):
"""A sensor entity."""
entity_description: BringSensorEntityDescription
def __init__(
self,
coordinator: BringDataUpdateCoordinator,
bring_list: BringData,
entity_description: BringSensorEntityDescription,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator, bring_list)
self.entity_description = entity_description
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_{self._list_uuid}_{self.entity_description.key}"
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.entity_description.value_fn(
self.coordinator.data[self._list_uuid],
self.coordinator.user_settings,
)

View File

@ -26,6 +26,44 @@
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"entity": {
"sensor": {
"urgent": {
"name": "Urgent"
},
"convenient": {
"name": "On occasion"
},
"discounted": {
"name": "Discount only"
},
"list_language": {
"name": "Region & language",
"state": {
"de-at": "Austria",
"de-ch": "Switzerland (German)",
"de-de": "Germany",
"en-au": "Australia",
"en-ca": "Canada",
"en-gb": "United Kingdom",
"en-us": "United States",
"es-es": "Spain",
"fr-ch": "Switzerland (French)",
"fr-fr": "France",
"hu-hu": "Hungary",
"it-ch": "Switzerland (Italian)",
"it-it": "Italy",
"nb-no": "Norway",
"nl-nl": "Netherlands",
"pl-pl": "Poland",
"pt-br": "Portugal",
"ru-ru": "Russia",
"sv-se": "Sweden",
"tr-tr": "Turkey"
}
}
}
},
"exceptions": {
"todo_save_item_failed": {
"message": "Failed to save item {name} to Bring! list"

View File

@ -31,7 +31,7 @@ from .const import (
DOMAIN,
SERVICE_PUSH_NOTIFICATION,
)
from .coordinator import BringData
from .coordinator import BringData, BringDataUpdateCoordinator
from .entity import BringBaseEntity
@ -77,6 +77,13 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
)
def __init__(
self, coordinator: BringDataUpdateCoordinator, bring_list: BringData
) -> None:
"""Initialize the entity."""
super().__init__(coordinator, bring_list)
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_{self._list_uuid}"
@property
def todo_items(self) -> list[TodoItem]:
"""Return the todo items."""

View File

@ -0,0 +1,40 @@
"""Utility functions for Bring."""
from __future__ import annotations
from bring_api import BringUserSettingsResponse
from .coordinator import BringData
def list_language(
list_uuid: str,
user_settings: BringUserSettingsResponse,
) -> str | None:
"""Get the lists language setting."""
try:
list_settings = next(
filter(
lambda x: x["listUuid"] == list_uuid,
user_settings["userlistsettings"],
)
)
return next(
filter(
lambda x: x["key"] == "listArticleLanguage",
list_settings["usersettings"],
)
)["value"]
except (StopIteration, KeyError):
return None
def sum_attributes(bring_list: BringData, attribute: str) -> int:
"""Count items with given attribute set."""
return sum(
item["attributes"][0]["content"][attribute]
for item in bring_list["purchase"]
if len(item.get("attributes", []))
)

View File

@ -46,6 +46,9 @@ def mock_bring_client() -> Generator[AsyncMock]:
client.login.return_value = cast(BringAuthResponse, {"name": "Bring"})
client.load_lists.return_value = load_json_object_fixture("lists.json", DOMAIN)
client.get_list.return_value = load_json_object_fixture("items.json", DOMAIN)
client.get_all_user_settings.return_value = load_json_object_fixture(
"usersettings.json", DOMAIN
)
yield client

View File

@ -6,13 +6,31 @@
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": []
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": []
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
]
}
],
"recently": [

View File

@ -0,0 +1,60 @@
{
"userlistsettings": [
{
"listUuid": "e542eef6-dba7-4c31-a52c-29e6ab9d83a5",
"usersettings": [
{
"key": "listSectionOrder",
"value": "[\"Früchte & Gemüse\",\"Brot & Gebäck\",\"Milch & Käse\",\"Fleisch & Fisch\",\"Zutaten & Gewürze\",\"Fertig- & Tiefkühlprodukte\",\"Getreideprodukte\",\"Snacks & Süsswaren\",\"Getränke & Tabak\",\"Haushalt & Gesundheit\",\"Pflege & Gesundheit\",\"Tierbedarf\",\"Baumarkt & Garten\",\"Eigene Artikel\"]"
},
{
"key": "listArticleLanguage",
"value": "de-DE"
}
]
},
{
"listUuid": "b4776778-7f6c-496e-951b-92a35d3db0dd",
"usersettings": [
{
"key": "listSectionOrder",
"value": "[\"Früchte & Gemüse\",\"Brot & Gebäck\",\"Milch & Käse\",\"Fleisch & Fisch\",\"Zutaten & Gewürze\",\"Fertig- & Tiefkühlprodukte\",\"Getreideprodukte\",\"Snacks & Süsswaren\",\"Getränke & Tabak\",\"Haushalt & Gesundheit\",\"Pflege & Gesundheit\",\"Tierbedarf\",\"Baumarkt & Garten\",\"Eigene Artikel\"]"
},
{
"key": "listArticleLanguage",
"value": "en-US"
}
]
}
],
"usersettings": [
{
"key": "autoPush",
"value": "ON"
},
{
"key": "premiumHideOffersBadge",
"value": "ON"
},
{
"key": "premiumHideSponsoredCategories",
"value": "ON"
},
{
"key": "premiumHideInspirationsBadge",
"value": "ON"
},
{
"key": "onboardClient",
"value": "android"
},
{
"key": "premiumHideOffersOnMain",
"value": "ON"
},
{
"key": "defaultListUUID",
"value": "e542eef6-dba7-4c31-a52c-29e6ab9d83a5"
}
]
}

View File

@ -0,0 +1,467 @@
# serializer version: 1
# name: test_setup[sensor.baumarkt_discount_only-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.baumarkt_discount_only',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Discount only',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.DISCOUNTED: 'discounted'>,
'unique_id': '00000000-00000000-00000000-00000000_b4776778-7f6c-496e-951b-92a35d3db0dd_discounted',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.baumarkt_discount_only-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Baumarkt Discount only',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.baumarkt_discount_only',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_setup[sensor.baumarkt_on_occasion-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.baumarkt_on_occasion',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'On occasion',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.CONVENIENT: 'convenient'>,
'unique_id': '00000000-00000000-00000000-00000000_b4776778-7f6c-496e-951b-92a35d3db0dd_convenient',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.baumarkt_on_occasion-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Baumarkt On occasion',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.baumarkt_on_occasion',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_setup[sensor.baumarkt_region_language-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'de-at',
'de-ch',
'de-de',
'en-au',
'en-ca',
'en-gb',
'en-us',
'es-es',
'fr-ch',
'fr-fr',
'hu-hu',
'it-ch',
'it-it',
'nb-no',
'nl-nl',
'pl-pl',
'pt-br',
'ru-ru',
'sv-se',
'tr-tr',
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.baumarkt_region_language',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Region & language',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.LIST_LANGUAGE: 'list_language'>,
'unique_id': '00000000-00000000-00000000-00000000_b4776778-7f6c-496e-951b-92a35d3db0dd_list_language',
'unit_of_measurement': None,
})
# ---
# name: test_setup[sensor.baumarkt_region_language-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Baumarkt Region & language',
'options': list([
'de-at',
'de-ch',
'de-de',
'en-au',
'en-ca',
'en-gb',
'en-us',
'es-es',
'fr-ch',
'fr-fr',
'hu-hu',
'it-ch',
'it-it',
'nb-no',
'nl-nl',
'pl-pl',
'pt-br',
'ru-ru',
'sv-se',
'tr-tr',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.baumarkt_region_language',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'en-us',
})
# ---
# name: test_setup[sensor.baumarkt_urgent-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.baumarkt_urgent',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Urgent',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.URGENT: 'urgent'>,
'unique_id': '00000000-00000000-00000000-00000000_b4776778-7f6c-496e-951b-92a35d3db0dd_urgent',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.baumarkt_urgent-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Baumarkt Urgent',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.baumarkt_urgent',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_setup[sensor.einkauf_discount_only-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.einkauf_discount_only',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Discount only',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.DISCOUNTED: 'discounted'>,
'unique_id': '00000000-00000000-00000000-00000000_e542eef6-dba7-4c31-a52c-29e6ab9d83a5_discounted',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.einkauf_discount_only-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Einkauf Discount only',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.einkauf_discount_only',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_setup[sensor.einkauf_on_occasion-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.einkauf_on_occasion',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'On occasion',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.CONVENIENT: 'convenient'>,
'unique_id': '00000000-00000000-00000000-00000000_e542eef6-dba7-4c31-a52c-29e6ab9d83a5_convenient',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.einkauf_on_occasion-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Einkauf On occasion',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.einkauf_on_occasion',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_setup[sensor.einkauf_region_language-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'de-at',
'de-ch',
'de-de',
'en-au',
'en-ca',
'en-gb',
'en-us',
'es-es',
'fr-ch',
'fr-fr',
'hu-hu',
'it-ch',
'it-it',
'nb-no',
'nl-nl',
'pl-pl',
'pt-br',
'ru-ru',
'sv-se',
'tr-tr',
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.einkauf_region_language',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Region & language',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.LIST_LANGUAGE: 'list_language'>,
'unique_id': '00000000-00000000-00000000-00000000_e542eef6-dba7-4c31-a52c-29e6ab9d83a5_list_language',
'unit_of_measurement': None,
})
# ---
# name: test_setup[sensor.einkauf_region_language-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Einkauf Region & language',
'options': list([
'de-at',
'de-ch',
'de-de',
'en-au',
'en-ca',
'en-gb',
'en-us',
'es-es',
'fr-ch',
'fr-fr',
'hu-hu',
'it-ch',
'it-it',
'nb-no',
'nl-nl',
'pl-pl',
'pt-br',
'ru-ru',
'sv-se',
'tr-tr',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.einkauf_region_language',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'de-de',
})
# ---
# name: test_setup[sensor.einkauf_urgent-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.einkauf_urgent',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Urgent',
'platform': 'bring',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <BringSensor.URGENT: 'urgent'>,
'unique_id': '00000000-00000000-00000000-00000000_e542eef6-dba7-4c31-a52c-29e6ab9d83a5_urgent',
'unit_of_measurement': 'items',
})
# ---
# name: test_setup[sensor.einkauf_urgent-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Einkauf Urgent',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.einkauf_urgent',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---

View File

@ -90,7 +90,14 @@ async def test_init_exceptions(
@pytest.mark.parametrize("exception", [BringRequestException, BringParseException])
@pytest.mark.parametrize("bring_method", ["load_lists", "get_list"])
@pytest.mark.parametrize(
"bring_method",
[
"load_lists",
"get_list",
"get_all_user_settings",
],
)
async def test_config_entry_not_ready(
hass: HomeAssistant,
bring_config_entry: MockConfigEntry,

View File

@ -0,0 +1,44 @@
"""Test for sensor platform of the Bring! integration."""
from collections.abc import Generator
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
@pytest.fixture(autouse=True)
def sensor_only() -> Generator[None]:
"""Enable only the sensor platform."""
with patch(
"homeassistant.components.bring.PLATFORMS",
[Platform.SENSOR],
):
yield
@pytest.mark.usefixtures("mock_bring_client")
async def test_setup(
hass: HomeAssistant,
bring_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Snapshot test states of sensor platform."""
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 ConfigEntryState.LOADED
await snapshot_platform(
hass, entity_registry, snapshot, bring_config_entry.entry_id
)

View File

@ -1,7 +1,8 @@
"""Test for todo platform of the Bring! integration."""
from collections.abc import Generator
import re
from unittest.mock import AsyncMock
from unittest.mock import AsyncMock, patch
from bring_api import BringItemOperation, BringRequestException
import pytest
@ -15,7 +16,7 @@ from homeassistant.components.todo import (
TodoServices,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
@ -23,6 +24,16 @@ from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
@pytest.fixture(autouse=True)
def todo_only() -> Generator[None]:
"""Enable only the todo platform."""
with patch(
"homeassistant.components.bring.PLATFORMS",
[Platform.TODO],
):
yield
@pytest.mark.usefixtures("mock_bring_client")
async def test_todo(
hass: HomeAssistant,

View File

@ -0,0 +1,56 @@
"""Test for utility functions of the Bring! integration."""
from typing import cast
from bring_api import BringUserSettingsResponse
import pytest
from homeassistant.components.bring import DOMAIN
from homeassistant.components.bring.coordinator import BringData
from homeassistant.components.bring.util import list_language, sum_attributes
from tests.common import load_json_object_fixture
@pytest.mark.parametrize(
("list_uuid", "expected"),
[
("e542eef6-dba7-4c31-a52c-29e6ab9d83a5", "de-DE"),
("b4776778-7f6c-496e-951b-92a35d3db0dd", "en-US"),
("00000000-0000-0000-0000-00000000", None),
],
)
def test_list_language(list_uuid: str, expected: str | None) -> None:
"""Test function list_language."""
result = list_language(
list_uuid,
cast(
BringUserSettingsResponse,
load_json_object_fixture("usersettings.json", DOMAIN),
),
)
assert result == expected
@pytest.mark.parametrize(
("attribute", "expected"),
[
("urgent", 2),
("convenient", 2),
("discounted", 2),
],
)
def test_sum_attributes(attribute: str, expected: int) -> None:
"""Test function sum_attributes."""
result = sum_attributes(
cast(
BringData,
load_json_object_fixture("items.json", DOMAIN),
),
attribute,
)
assert result == expected