mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add sensor platform for Mastodon (#123434)
* Add account sensors * Sensor icons * Change sensors to use value_fn * Add native unit of measurement * Update native unit of measurement * Change toots to posts * Fix sensor icons * Add device entry type * Explain conditional naming * Fixes from review * Remove unnecessary constructor
This commit is contained in:
parent
55eb11055c
commit
aee5d5126f
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from mastodon.Mastodon import Mastodon, MastodonError
|
from mastodon.Mastodon import Mastodon, MastodonError
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -17,14 +20,33 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
|
|
||||||
from .const import CONF_BASE_URL, DOMAIN
|
from .const import CONF_BASE_URL, DOMAIN
|
||||||
|
from .coordinator import MastodonCoordinator
|
||||||
from .utils import create_mastodon_client
|
from .utils import create_mastodon_client
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [Platform.NOTIFY, Platform.SENSOR]
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
||||||
|
@dataclass
|
||||||
|
class MastodonData:
|
||||||
|
"""Mastodon data type."""
|
||||||
|
|
||||||
|
client: Mastodon
|
||||||
|
instance: dict
|
||||||
|
account: dict
|
||||||
|
coordinator: MastodonCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
type MastodonConfigEntry = ConfigEntry[MastodonData]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import MastodonConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: MastodonConfigEntry) -> bool:
|
||||||
"""Set up Mastodon from a config entry."""
|
"""Set up Mastodon from a config entry."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client, _, _ = await hass.async_add_executor_job(
|
client, instance, account = await hass.async_add_executor_job(
|
||||||
setup_mastodon,
|
setup_mastodon,
|
||||||
entry,
|
entry,
|
||||||
)
|
)
|
||||||
@ -34,6 +56,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
assert entry.unique_id
|
assert entry.unique_id
|
||||||
|
|
||||||
|
coordinator = MastodonCoordinator(hass, client)
|
||||||
|
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
entry.runtime_data = MastodonData(client, instance, account, coordinator)
|
||||||
|
|
||||||
await discovery.async_load_platform(
|
await discovery.async_load_platform(
|
||||||
hass,
|
hass,
|
||||||
Platform.NOTIFY,
|
Platform.NOTIFY,
|
||||||
@ -42,9 +70,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await hass.config_entries.async_forward_entry_setups(
|
||||||
|
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: MastodonConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
return await hass.config_entries.async_unload_platforms(
|
||||||
|
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_mastodon(entry: ConfigEntry) -> tuple[Mastodon, dict, dict]:
|
def setup_mastodon(entry: ConfigEntry) -> tuple[Mastodon, dict, dict]:
|
||||||
"""Get mastodon details."""
|
"""Get mastodon details."""
|
||||||
client = create_mastodon_client(
|
client = create_mastodon_client(
|
||||||
|
@ -16,3 +16,6 @@ INSTANCE_VERSION: Final = "version"
|
|||||||
INSTANCE_URI: Final = "uri"
|
INSTANCE_URI: Final = "uri"
|
||||||
INSTANCE_DOMAIN: Final = "domain"
|
INSTANCE_DOMAIN: Final = "domain"
|
||||||
ACCOUNT_USERNAME: Final = "username"
|
ACCOUNT_USERNAME: Final = "username"
|
||||||
|
ACCOUNT_FOLLOWERS_COUNT: Final = "followers_count"
|
||||||
|
ACCOUNT_FOLLOWING_COUNT: Final = "following_count"
|
||||||
|
ACCOUNT_STATUSES_COUNT: Final = "statuses_count"
|
||||||
|
35
homeassistant/components/mastodon/coordinator.py
Normal file
35
homeassistant/components/mastodon/coordinator.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""Define an object to manage fetching Mastodon data."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from mastodon import Mastodon
|
||||||
|
from mastodon.Mastodon import MastodonError
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import LOGGER
|
||||||
|
|
||||||
|
|
||||||
|
class MastodonCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
|
"""Class to manage fetching Mastodon data."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, client: Mastodon) -> None:
|
||||||
|
"""Initialize coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass, logger=LOGGER, name="Mastodon", update_interval=timedelta(hours=1)
|
||||||
|
)
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> dict[str, Any]:
|
||||||
|
try:
|
||||||
|
account: dict = await self.hass.async_add_executor_job(
|
||||||
|
self.client.account_verify_credentials
|
||||||
|
)
|
||||||
|
except MastodonError as ex:
|
||||||
|
raise UpdateFailed(ex) from ex
|
||||||
|
|
||||||
|
return account
|
48
homeassistant/components/mastodon/entity.py
Normal file
48
homeassistant/components/mastodon/entity.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""Base class for Mastodon entities."""
|
||||||
|
|
||||||
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||||
|
from homeassistant.helpers.entity import EntityDescription
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from . import MastodonConfigEntry
|
||||||
|
from .const import DEFAULT_NAME, DOMAIN, INSTANCE_VERSION
|
||||||
|
from .coordinator import MastodonCoordinator
|
||||||
|
from .utils import construct_mastodon_username
|
||||||
|
|
||||||
|
|
||||||
|
class MastodonEntity(CoordinatorEntity[MastodonCoordinator]):
|
||||||
|
"""Defines a base Mastodon entity."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: MastodonCoordinator,
|
||||||
|
entity_description: EntityDescription,
|
||||||
|
data: MastodonConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize Mastodon entity."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
unique_id = data.unique_id
|
||||||
|
assert unique_id is not None
|
||||||
|
self._attr_unique_id = f"{unique_id}_{entity_description.key}"
|
||||||
|
|
||||||
|
# Legacy yaml config default title is Mastodon, don't make name Mastodon Mastodon
|
||||||
|
name = "Mastodon"
|
||||||
|
if data.title != DEFAULT_NAME:
|
||||||
|
name = f"Mastodon {data.title}"
|
||||||
|
|
||||||
|
full_account_name = construct_mastodon_username(
|
||||||
|
data.runtime_data.instance, data.runtime_data.account
|
||||||
|
)
|
||||||
|
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
identifiers={(DOMAIN, unique_id)},
|
||||||
|
manufacturer="Mastodon gGmbH",
|
||||||
|
model=full_account_name,
|
||||||
|
entry_type=DeviceEntryType.SERVICE,
|
||||||
|
sw_version=data.runtime_data.instance[INSTANCE_VERSION],
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.entity_description = entity_description
|
15
homeassistant/components/mastodon/icons.json
Normal file
15
homeassistant/components/mastodon/icons.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"followers": {
|
||||||
|
"default": "mdi:account-multiple"
|
||||||
|
},
|
||||||
|
"following": {
|
||||||
|
"default": "mdi:account-multiple"
|
||||||
|
},
|
||||||
|
"posts": {
|
||||||
|
"default": "mdi:message-text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
homeassistant/components/mastodon/sensor.py
Normal file
85
homeassistant/components/mastodon/sensor.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""Mastodon platform for sensor components."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
|
from . import MastodonConfigEntry
|
||||||
|
from .const import (
|
||||||
|
ACCOUNT_FOLLOWERS_COUNT,
|
||||||
|
ACCOUNT_FOLLOWING_COUNT,
|
||||||
|
ACCOUNT_STATUSES_COUNT,
|
||||||
|
)
|
||||||
|
from .entity import MastodonEntity
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class MastodonSensorEntityDescription(SensorEntityDescription):
|
||||||
|
"""Describes Mastodon sensor entity."""
|
||||||
|
|
||||||
|
value_fn: Callable[[dict[str, Any]], StateType]
|
||||||
|
|
||||||
|
|
||||||
|
ENTITY_DESCRIPTIONS = (
|
||||||
|
MastodonSensorEntityDescription(
|
||||||
|
key="followers",
|
||||||
|
translation_key="followers",
|
||||||
|
native_unit_of_measurement="accounts",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
value_fn=lambda data: data.get(ACCOUNT_FOLLOWERS_COUNT),
|
||||||
|
),
|
||||||
|
MastodonSensorEntityDescription(
|
||||||
|
key="following",
|
||||||
|
translation_key="following",
|
||||||
|
native_unit_of_measurement="accounts",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
value_fn=lambda data: data.get(ACCOUNT_FOLLOWING_COUNT),
|
||||||
|
),
|
||||||
|
MastodonSensorEntityDescription(
|
||||||
|
key="posts",
|
||||||
|
translation_key="posts",
|
||||||
|
native_unit_of_measurement="posts",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
value_fn=lambda data: data.get(ACCOUNT_STATUSES_COUNT),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: MastodonConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the sensor platform for entity."""
|
||||||
|
coordinator = entry.runtime_data.coordinator
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
MastodonSensorEntity(
|
||||||
|
coordinator=coordinator,
|
||||||
|
entity_description=entity_description,
|
||||||
|
data=entry,
|
||||||
|
)
|
||||||
|
for entity_description in ENTITY_DESCRIPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MastodonSensorEntity(MastodonEntity, SensorEntity):
|
||||||
|
"""A Mastodon sensor entity."""
|
||||||
|
|
||||||
|
entity_description: MastodonSensorEntityDescription
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> StateType:
|
||||||
|
"""Return the native value of the sensor."""
|
||||||
|
return self.entity_description.value_fn(self.coordinator.data)
|
@ -35,5 +35,18 @@
|
|||||||
"title": "YAML import failed with unknown error",
|
"title": "YAML import failed with unknown error",
|
||||||
"description": "Configuring {integration_title} using YAML is being removed but there was an unknown error while importing your existing configuration.\nPlease use the UI to configure Mastodon. Don't forget to delete the YAML configuration."
|
"description": "Configuring {integration_title} using YAML is being removed but there was an unknown error while importing your existing configuration.\nPlease use the UI to configure Mastodon. Don't forget to delete the YAML configuration."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"followers": {
|
||||||
|
"name": "Followers"
|
||||||
|
},
|
||||||
|
"following": {
|
||||||
|
"name": "Following"
|
||||||
|
},
|
||||||
|
"posts": {
|
||||||
|
"name": "Posts"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
151
tests/components/mastodon/snapshots/test_sensor.ambr
Normal file
151
tests/components/mastodon/snapshots/test_sensor.ambr
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_followers-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.mastodon_trwnh_mastodon_social_followers',
|
||||||
|
'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': 'Followers',
|
||||||
|
'platform': 'mastodon',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'followers',
|
||||||
|
'unique_id': 'client_id_followers',
|
||||||
|
'unit_of_measurement': 'accounts',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_followers-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mastodon @trwnh@mastodon.social Followers',
|
||||||
|
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||||
|
'unit_of_measurement': 'accounts',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mastodon_trwnh_mastodon_social_followers',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '821',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_following-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.mastodon_trwnh_mastodon_social_following',
|
||||||
|
'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': 'Following',
|
||||||
|
'platform': 'mastodon',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'following',
|
||||||
|
'unique_id': 'client_id_following',
|
||||||
|
'unit_of_measurement': 'accounts',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_following-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mastodon @trwnh@mastodon.social Following',
|
||||||
|
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||||
|
'unit_of_measurement': 'accounts',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mastodon_trwnh_mastodon_social_following',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '178',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_posts-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.mastodon_trwnh_mastodon_social_posts',
|
||||||
|
'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': 'Posts',
|
||||||
|
'platform': 'mastodon',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'posts',
|
||||||
|
'unique_id': 'client_id_posts',
|
||||||
|
'unit_of_measurement': 'posts',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mastodon_trwnh_mastodon_social_posts-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mastodon @trwnh@mastodon.social Posts',
|
||||||
|
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||||
|
'unit_of_measurement': 'posts',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mastodon_trwnh_mastodon_social_posts',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '33120',
|
||||||
|
})
|
||||||
|
# ---
|
27
tests/components/mastodon/test_sensor.py
Normal file
27
tests/components/mastodon/test_sensor.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""Tests for the Mastodon sensors."""
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from . import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
|
async def test_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_mastodon_client: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test the sensor entities."""
|
||||||
|
with patch("homeassistant.components.mastodon.PLATFORMS", [Platform.SENSOR]):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
Loading…
x
Reference in New Issue
Block a user