Add Homeassistant Analytics Insights integration (#107634)

* Add Homeassistant Analytics integration

* Add Homeassistant Analytics integration

* Add Homeassistant Analytics integration

* Fix feedback

* Fix test

* Update conftest.py

* Add some testcases

* Make code clear

* log exception

* Bump python-homeassistant-analytics to 0.2.1

* Bump python-homeassistant-analytics to 0.3.0

* Change domain to homeassistant_analytics_consumer

* Add integration name to config flow selector

* Update homeassistant/components/homeassistant_analytics_consumer/manifest.json

Co-authored-by: Sid <27780930+autinerd@users.noreply.github.com>

* Fix hassfest

* Apply suggestions from code review

Co-authored-by: Robert Resch <robert@resch.dev>

* Bump python-homeassistant-analytics to 0.4.0

* Rename to Home Assistant Analytics Insights

* Update homeassistant/components/analytics_insights/config_flow.py

Co-authored-by: Robert Resch <robert@resch.dev>

* Update homeassistant/components/analytics_insights/manifest.json

Co-authored-by: Robert Resch <robert@resch.dev>

* Rename to Home Assistant Analytics Insights

* add test

* Fallback to 0 when there is no data found

* Allow to select any integration

* Fix tests

* Fix tests

* Update tests/components/analytics_insights/conftest.py

Co-authored-by: Robert Resch <robert@resch.dev>

* Update tests/components/analytics_insights/test_sensor.py

Co-authored-by: Robert Resch <robert@resch.dev>

* Fix format

* Fix tests

---------

Co-authored-by: Sid <27780930+autinerd@users.noreply.github.com>
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
Joost Lekkerkerker 2024-01-23 10:32:31 +01:00 committed by GitHub
parent 52ede95c4f
commit d9f1450ee6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 3226 additions and 0 deletions

View File

@ -69,6 +69,7 @@ homeassistant.components.ambient_station.*
homeassistant.components.amcrest.*
homeassistant.components.ampio.*
homeassistant.components.analytics.*
homeassistant.components.analytics_insights.*
homeassistant.components.android_ip_webcam.*
homeassistant.components.androidtv.*
homeassistant.components.androidtv_remote.*

View File

@ -76,6 +76,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/amcrest/ @flacjacket
/homeassistant/components/analytics/ @home-assistant/core @ludeeus
/tests/components/analytics/ @home-assistant/core @ludeeus
/homeassistant/components/analytics_insights/ @joostlek
/tests/components/analytics_insights/ @joostlek
/homeassistant/components/android_ip_webcam/ @engrbm87
/tests/components/android_ip_webcam/ @engrbm87
/homeassistant/components/androidtv/ @JeffLIrion @ollo69

View File

@ -0,0 +1,58 @@
"""The Homeassistant Analytics integration."""
from __future__ import annotations
from dataclasses import dataclass
from python_homeassistant_analytics import HomeassistantAnalyticsClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_TRACKED_INTEGRATIONS, DOMAIN
from .coordinator import HomeassistantAnalyticsDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR]
@dataclass(frozen=True)
class AnalyticsData:
"""Analytics data class."""
coordinator: HomeassistantAnalyticsDataUpdateCoordinator
names: dict[str, str]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Homeassistant Analytics from a config entry."""
client = HomeassistantAnalyticsClient(session=async_get_clientsession(hass))
integrations = await client.get_integrations()
names = {}
for integration in entry.options[CONF_TRACKED_INTEGRATIONS]:
if integration not in integrations:
names[integration] = integration
continue
names[integration] = integrations[integration].title
coordinator = HomeassistantAnalyticsDataUpdateCoordinator(hass, client)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = AnalyticsData(
coordinator=coordinator, names=names
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,74 @@
"""Config flow for Homeassistant Analytics integration."""
from __future__ import annotations
from typing import Any
from python_homeassistant_analytics import (
HomeassistantAnalyticsClient,
HomeassistantAnalyticsConnectionError,
)
from python_homeassistant_analytics.models import IntegrationType
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
)
from .const import CONF_TRACKED_INTEGRATIONS, DOMAIN, LOGGER
INTEGRATION_TYPES_WITHOUT_ANALYTICS = (
IntegrationType.BRAND,
IntegrationType.ENTITY,
IntegrationType.VIRTUAL,
)
class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Homeassistant Analytics."""
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
self._async_abort_entries_match()
if user_input:
return self.async_create_entry(
title="Home Assistant Analytics Insights", data={}, options=user_input
)
client = HomeassistantAnalyticsClient(
session=async_get_clientsession(self.hass)
)
try:
integrations = await client.get_integrations()
except HomeassistantAnalyticsConnectionError:
LOGGER.exception("Error connecting to Home Assistant analytics")
return self.async_abort(reason="cannot_connect")
options = [
SelectOptionDict(
value=domain,
label=integration.title,
)
for domain, integration in integrations.items()
if integration.integration_type not in INTEGRATION_TYPES_WITHOUT_ANALYTICS
]
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_TRACKED_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=options,
multiple=True,
sort=True,
)
),
}
),
)

View File

@ -0,0 +1,8 @@
"""Constants for the Homeassistant Analytics integration."""
import logging
DOMAIN = "analytics_insights"
CONF_TRACKED_INTEGRATIONS = "tracked_integrations"
LOGGER = logging.getLogger(__package__)

View File

@ -0,0 +1,53 @@
"""DataUpdateCoordinator for the Homeassistant Analytics integration."""
from __future__ import annotations
from datetime import timedelta
from python_homeassistant_analytics import (
HomeassistantAnalyticsClient,
HomeassistantAnalyticsConnectionError,
HomeassistantAnalyticsNotModifiedError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_TRACKED_INTEGRATIONS, DOMAIN, LOGGER
class HomeassistantAnalyticsDataUpdateCoordinator(
DataUpdateCoordinator[dict[str, int]]
):
"""A Homeassistant Analytics Data Update Coordinator."""
config_entry: ConfigEntry
def __init__(
self, hass: HomeAssistant, client: HomeassistantAnalyticsClient
) -> None:
"""Initialize the Homeassistant Analytics data coordinator."""
super().__init__(
hass,
LOGGER,
name=DOMAIN,
update_interval=timedelta(hours=12),
)
self._client = client
self._tracked_integrations = self.config_entry.options[
CONF_TRACKED_INTEGRATIONS
]
async def _async_update_data(self) -> dict[str, int]:
try:
data = await self._client.get_current_analytics()
except HomeassistantAnalyticsConnectionError as err:
raise UpdateFailed(
"Error communicating with Homeassistant Analytics"
) from err
except HomeassistantAnalyticsNotModifiedError:
return self.data
return {
integration: data.integrations.get(integration, 0)
for integration in self._tracked_integrations
}

View File

@ -0,0 +1,11 @@
{
"domain": "analytics_insights",
"name": "Home Assistant Analytics Insights",
"codeowners": ["@joostlek"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/analytics_insights",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["python_homeassistant_analytics"],
"requirements": ["python-homeassistant-analytics==0.5.0"]
}

View File

@ -0,0 +1,62 @@
"""Sensor for Home Assistant analytics."""
from __future__ import annotations
from homeassistant.components.sensor import SensorEntity, SensorStateClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AnalyticsData
from .const import DOMAIN
from .coordinator import HomeassistantAnalyticsDataUpdateCoordinator
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Initialize the entries."""
analytics_data: AnalyticsData = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
HomeassistantAnalyticsSensor(
analytics_data.coordinator,
integration_domain,
analytics_data.names[integration_domain],
)
for integration_domain in analytics_data.coordinator.data
)
class HomeassistantAnalyticsSensor(
CoordinatorEntity[HomeassistantAnalyticsDataUpdateCoordinator], SensorEntity
):
"""Home Assistant Analytics Sensor."""
_attr_has_entity_name = True
_attr_state_class = SensorStateClass.TOTAL
_attr_native_unit_of_measurement = "active installations"
def __init__(
self,
coordinator: HomeassistantAnalyticsDataUpdateCoordinator,
integration_domain: str,
name: str,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_name = name
self._attr_unique_id = f"core_{integration_domain}_active_installations"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, DOMAIN)},
entry_type=DeviceEntryType.SERVICE,
)
self._integration_domain = integration_domain
@property
def native_value(self) -> int | None:
"""Return the state of the sensor."""
return self.coordinator.data.get(self._integration_domain)

View File

@ -0,0 +1,18 @@
{
"config": {
"step": {
"user": {
"data": {
"tracked_integrations": "Integrations"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}

View File

@ -42,6 +42,7 @@ FLOWS = {
"amberelectric",
"ambiclimate",
"ambient_station",
"analytics_insights",
"android_ip_webcam",
"androidtv",
"androidtv_remote",

View File

@ -255,6 +255,12 @@
"config_flow": false,
"iot_class": "cloud_polling"
},
"analytics_insights": {
"name": "Home Assistant Analytics Insights",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
"android_ip_webcam": {
"name": "Android IP Webcam",
"integration_type": "hub",

View File

@ -450,6 +450,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.analytics_insights.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.android_ip_webcam.*]
check_untyped_defs = true
disallow_incomplete_defs = true

View File

@ -2197,6 +2197,9 @@ python-gc100==1.0.3a0
# homeassistant.components.gitlab_ci
python-gitlab==1.6.0
# homeassistant.components.analytics_insights
python-homeassistant-analytics==0.5.0
# homeassistant.components.homewizard
python-homewizard-energy==4.1.0

View File

@ -1673,6 +1673,9 @@ python-ecobee-api==0.2.17
# homeassistant.components.fully_kiosk
python-fullykiosk==0.0.12
# homeassistant.components.analytics_insights
python-homeassistant-analytics==0.5.0
# homeassistant.components.homewizard
python-homewizard-energy==4.1.0

View File

@ -0,0 +1,11 @@
"""Tests for the Homeassistant Analytics integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Fixture for setting up the component."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)

View File

@ -0,0 +1,54 @@
"""Common fixtures for the Homeassistant Analytics tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
import pytest
from python_homeassistant_analytics import CurrentAnalytics
from python_homeassistant_analytics.models import Integration
from homeassistant.components.analytics_insights import DOMAIN
from homeassistant.components.analytics_insights.const import CONF_TRACKED_INTEGRATIONS
from tests.common import MockConfigEntry, load_fixture, load_json_object_fixture
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.analytics_insights.async_setup_entry",
return_value=True,
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_analytics_client() -> Generator[AsyncMock, None, None]:
"""Mock a Homeassistant Analytics client."""
with patch(
"homeassistant.components.analytics_insights.HomeassistantAnalyticsClient",
autospec=True,
) as mock_client, patch(
"homeassistant.components.analytics_insights.config_flow.HomeassistantAnalyticsClient",
new=mock_client,
):
client = mock_client.return_value
client.get_current_analytics.return_value = CurrentAnalytics.from_json(
load_fixture("analytics_insights/current_data.json")
)
integrations = load_json_object_fixture("analytics_insights/integrations.json")
client.get_integrations.return_value = {
key: Integration.from_dict(value) for key, value in integrations.items()
}
yield client
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock a config entry."""
return MockConfigEntry(
domain=DOMAIN,
title="Homeassistant Analytics",
data={},
options={CONF_TRACKED_INTEGRATIONS: ["youtube", "spotify", "myq"]},
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
{
"youtube": {
"title": "YouTube",
"description": "Instructions on how to integrate YouTube within Home Assistant.",
"quality_scale": "",
"iot_class": "Cloud Polling",
"integration_type": "service"
}
}

View File

@ -0,0 +1,142 @@
# serializer version: 1
# name: test_all_entities[sensor.homeassistant_analytics_myq-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.homeassistant_analytics_myq',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'myq',
'platform': 'analytics_insights',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'core_myq_active_installations',
'unit_of_measurement': 'active installations',
})
# ---
# name: test_all_entities[sensor.homeassistant_analytics_myq-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Homeassistant Analytics myq',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': 'active installations',
}),
'context': <ANY>,
'entity_id': 'sensor.homeassistant_analytics_myq',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.homeassistant_analytics_spotify-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.homeassistant_analytics_spotify',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'spotify',
'platform': 'analytics_insights',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'core_spotify_active_installations',
'unit_of_measurement': 'active installations',
})
# ---
# name: test_all_entities[sensor.homeassistant_analytics_spotify-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Homeassistant Analytics spotify',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': 'active installations',
}),
'context': <ANY>,
'entity_id': 'sensor.homeassistant_analytics_spotify',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '24388',
})
# ---
# name: test_all_entities[sensor.homeassistant_analytics_youtube-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.homeassistant_analytics_youtube',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'YouTube',
'platform': 'analytics_insights',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'core_youtube_active_installations',
'unit_of_measurement': 'active installations',
})
# ---
# name: test_all_entities[sensor.homeassistant_analytics_youtube-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Homeassistant Analytics YouTube',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': 'active installations',
}),
'context': <ANY>,
'entity_id': 'sensor.homeassistant_analytics_youtube',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '339',
})
# ---

View File

@ -0,0 +1,70 @@
"""Test the Homeassistant Analytics config flow."""
from unittest.mock import AsyncMock
from python_homeassistant_analytics import HomeassistantAnalyticsConnectionError
from homeassistant import config_entries
from homeassistant.components.analytics_insights.const import (
CONF_TRACKED_INTEGRATIONS,
DOMAIN,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
async def test_form(
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_analytics_client: AsyncMock
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_TRACKED_INTEGRATIONS: ["youtube"]},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "Home Assistant Analytics Insights"
assert result["data"] == {}
assert result["options"] == {CONF_TRACKED_INTEGRATIONS: ["youtube"]}
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_cannot_connect(
hass: HomeAssistant, mock_analytics_client: AsyncMock
) -> None:
"""Test we handle cannot connect error."""
mock_analytics_client.get_integrations.side_effect = (
HomeassistantAnalyticsConnectionError
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_form_already_configured(
hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test we handle cannot connect error."""
entry = MockConfigEntry(
domain=DOMAIN,
data={},
options={CONF_TRACKED_INTEGRATIONS: ["youtube", "spotify"]},
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"

View File

@ -0,0 +1,28 @@
"""Test the Home Assistant analytics init module."""
from __future__ import annotations
from unittest.mock import AsyncMock
from homeassistant.components.analytics_insights.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.components.analytics_insights import setup_integration
async def test_load_unload_entry(
hass: HomeAssistant,
mock_analytics_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test load and unload entry."""
await setup_integration(hass, mock_config_entry)
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.state == ConfigEntryState.LOADED
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.NOT_LOADED

View File

@ -0,0 +1,86 @@
"""Test the Home Assistant analytics sensor module."""
from datetime import timedelta
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
from python_homeassistant_analytics import (
HomeassistantAnalyticsConnectionError,
HomeassistantAnalyticsNotModifiedError,
)
from syrupy import SnapshotAssertion
from homeassistant.const import STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_all_entities(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_analytics_client: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test all entities."""
with patch(
"homeassistant.components.analytics_insights.PLATFORMS",
[Platform.SENSOR],
):
await setup_integration(hass, mock_config_entry)
entity_entries = er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
assert entity_entries
for entity_entry in entity_entries:
assert hass.states.get(entity_entry.entity_id) == snapshot(
name=f"{entity_entry.entity_id}-state"
)
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
async def test_connection_error(
hass: HomeAssistant,
mock_analytics_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test connection error."""
await setup_integration(hass, mock_config_entry)
mock_analytics_client.get_current_analytics.side_effect = (
HomeassistantAnalyticsConnectionError()
)
freezer.tick(delta=timedelta(hours=12))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (
hass.states.get("sensor.homeassistant_analytics_spotify").state
== STATE_UNAVAILABLE
)
async def test_data_not_modified(
hass: HomeAssistant,
mock_analytics_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test not updating data if its not modified."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("sensor.homeassistant_analytics_spotify").state == "24388"
mock_analytics_client.get_current_analytics.side_effect = (
HomeassistantAnalyticsNotModifiedError
)
freezer.tick(delta=timedelta(hours=12))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_analytics_client.get_current_analytics.assert_called()
assert hass.states.get("sensor.homeassistant_analytics_spotify").state == "24388"