Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/fressnapf_tracker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
FressnapfTrackerDataUpdateCoordinator,
)

PLATFORMS: list[Platform] = [Platform.DEVICE_TRACKER]
PLATFORMS: list[Platform] = [Platform.DEVICE_TRACKER, Platform.SENSOR]


async def async_setup_entry(
Expand Down
15 changes: 15 additions & 0 deletions homeassistant/components/fressnapf_tracker/entity.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""fressnapf_tracker class."""

from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import FressnapfTrackerDataUpdateCoordinator
Expand All @@ -25,3 +26,17 @@ def __init__(self, coordinator: FressnapfTrackerDataUpdateCoordinator) -> None:
manufacturer="Fressnapf",
serial_number=str(self.id),
)


class FressnapfTrackerEntity(FressnapfTrackerBaseEntity):
"""Entity for fressnapf_tracker."""

def __init__(
self,
coordinator: FressnapfTrackerDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self.entity_description = entity_description
self._attr_unique_id = f"{self.id}_{entity_description.key}"
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ rules:
entity-category: todo
entity-device-class: todo
entity-disabled-by-default: todo
entity-translations:
status: exempt
comment: No entities to translate
entity-translations: done
exception-translations: todo
icon-translations: todo
reconfiguration-flow: done
Expand Down
63 changes: 63 additions & 0 deletions homeassistant/components/fressnapf_tracker/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Sensor platform for fressnapf_tracker."""

from collections.abc import Callable
from dataclasses import dataclass

from fressnapftracker import Tracker

from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import FressnapfTrackerConfigEntry
from .entity import FressnapfTrackerEntity


@dataclass(frozen=True, kw_only=True)
class FressnapfTrackerSensorDescription(SensorEntityDescription):
"""Class describing Fressnapf Tracker sensor entities."""

value_fn: Callable[[Tracker], int]


SENSOR_ENTITY_DESCRIPTIONS: tuple[FressnapfTrackerSensorDescription, ...] = (
FressnapfTrackerSensorDescription(
key="battery",
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.battery,
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: FressnapfTrackerConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Fressnapf Tracker sensors."""

async_add_entities(
FressnapfTrackerSensor(coordinator, sensor_description)
for sensor_description in SENSOR_ENTITY_DESCRIPTIONS
for coordinator in entry.runtime_data
)


class FressnapfTrackerSensor(FressnapfTrackerEntity, SensorEntity):
"""fressnapf_tracker sensor for general information."""

entity_description: FressnapfTrackerSensorDescription

@property
def native_value(self) -> int:
"""Return the state of the resources if it has been received yet."""
return self.entity_description.value_fn(self.coordinator.data)
Original file line number Diff line number Diff line change
@@ -1,35 +1,4 @@
# serializer version: 1
# name: test_state_entity_device_snapshots[Fluffy-entry]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'fressnapf_tracker',
'ABC123456',
),
}),
'labels': set({
}),
'manufacturer': 'Fressnapf',
'model': 'GPS Tracker 2.0',
'model_id': None,
'name': 'Fluffy',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': 'ABC123456',
'sw_version': None,
'via_device_id': None,
})
# ---
# name: test_state_entity_device_snapshots[device_tracker.fluffy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
Expand Down
32 changes: 32 additions & 0 deletions tests/components/fressnapf_tracker/snapshots/test_init.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# serializer version: 1
# name: test_state_entity_device_snapshots[Fluffy-entry]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'fressnapf_tracker',
'ABC123456',
),
}),
'labels': set({
}),
'manufacturer': 'Fressnapf',
'model': 'GPS Tracker 2.0',
'model_id': None,
'name': 'Fluffy',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': 'ABC123456',
'sw_version': None,
'via_device_id': None,
})
# ---
54 changes: 54 additions & 0 deletions tests/components/fressnapf_tracker/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# serializer version: 1
# name: test_state_entity_device_snapshots[sensor.fluffy_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.fluffy_battery',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
'original_icon': None,
'original_name': 'Battery',
'platform': 'fressnapf_tracker',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'ABC123456_battery',
'unit_of_measurement': '%',
})
# ---
# name: test_state_entity_device_snapshots[sensor.fluffy_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Fluffy Battery',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.fluffy_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '85',
})
# ---
27 changes: 14 additions & 13 deletions tests/components/fressnapf_tracker/test_device_tracker.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
"""Test the Fressnapf Tracker device tracker platform."""

from unittest.mock import AsyncMock, MagicMock
from collections.abc import AsyncGenerator
from unittest.mock import AsyncMock, MagicMock, patch

from fressnapftracker import Tracker
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.const import STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers import entity_registry as er

from tests.common import MockConfigEntry, snapshot_platform


@pytest.fixture(autouse=True)
async def platforms() -> AsyncGenerator[None]:
"""Return the platforms to be loaded for this test."""
with patch(
"homeassistant.components.fressnapf_tracker.PLATFORMS",
[Platform.DEVICE_TRACKER],
):
yield


@pytest.mark.usefixtures("init_integration")
async def test_state_entity_device_snapshots(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test device tracker entity is created correctly."""
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

device_entries = dr.async_entries_for_config_entry(
device_registry, mock_config_entry.entry_id
)
assert device_entries
for device_entry in device_entries:
assert device_entry == snapshot(name=f"{device_entry.name}-entry"), (
f"device entry snapshot failed for {device_entry.name}"
)


@pytest.mark.usefixtures("mock_auth_client")
async def test_device_tracker_no_position(
Expand Down
19 changes: 19 additions & 0 deletions tests/components/fressnapf_tracker/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from unittest.mock import AsyncMock, MagicMock

import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -59,3 +61,20 @@ async def test_setup_entry_api_error(
await hass.async_block_till_done()

assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY


@pytest.mark.usefixtures("init_integration")
async def test_state_entity_device_snapshots(
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test sensor entity is created correctly."""
device_entries = dr.async_entries_for_config_entry(
device_registry, mock_config_entry.entry_id
)
assert device_entries
for device_entry in device_entries:
assert device_entry == snapshot(name=f"{device_entry.name}-entry"), (
f"device entry snapshot failed for {device_entry.name}"
)
33 changes: 33 additions & 0 deletions tests/components/fressnapf_tracker/test_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Test the Fressnapf Tracker sensor platform."""

from collections.abc import AsyncGenerator
from unittest.mock import patch

import pytest
from syrupy.assertion import SnapshotAssertion

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)
async def platforms() -> AsyncGenerator[None]:
"""Return the platforms to be loaded for this test."""
with patch(
"homeassistant.components.fressnapf_tracker.PLATFORMS", [Platform.SENSOR]
):
yield


@pytest.mark.usefixtures("init_integration")
async def test_state_entity_device_snapshots(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test sensor entity is created correctly."""
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
Loading