Skip to content

Commit 0ccfd77

Browse files
authored
add switch platform to fressnapf_tracker (home-assistant#157971)
1 parent 4805b33 commit 0ccfd77

File tree

7 files changed

+243
-2
lines changed

7 files changed

+243
-2
lines changed

homeassistant/components/fressnapf_tracker/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
Platform.DEVICE_TRACKER,
1818
Platform.LIGHT,
1919
Platform.SENSOR,
20+
Platform.SWITCH,
2021
]
2122

2223

homeassistant/components/fressnapf_tracker/icons.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
"pet": {
55
"default": "mdi:paw"
66
}
7+
},
8+
"switch": {
9+
"energy_saving": {
10+
"default": "mdi:sleep",
11+
"state": {
12+
"off": "mdi:sleep-off"
13+
}
14+
}
715
}
816
}
917
}

homeassistant/components/fressnapf_tracker/strings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@
5151
"led": {
5252
"name": "Flashlight"
5353
}
54+
},
55+
"switch": {
56+
"energy_saving": {
57+
"name": "Sleep mode"
58+
}
5459
}
5560
},
5661
"exceptions": {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Switch platform for Fressnapf Tracker."""
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
from homeassistant.components.switch import (
6+
SwitchDeviceClass,
7+
SwitchEntity,
8+
SwitchEntityDescription,
9+
)
10+
from homeassistant.const import EntityCategory
11+
from homeassistant.core import HomeAssistant
12+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
13+
14+
from . import FressnapfTrackerConfigEntry
15+
from .entity import FressnapfTrackerEntity
16+
17+
SWITCH_ENTITY_DESCRIPTION = SwitchEntityDescription(
18+
translation_key="energy_saving",
19+
entity_category=EntityCategory.CONFIG,
20+
device_class=SwitchDeviceClass.SWITCH,
21+
key="energy_saving",
22+
)
23+
24+
25+
async def async_setup_entry(
26+
hass: HomeAssistant,
27+
entry: FressnapfTrackerConfigEntry,
28+
async_add_entities: AddConfigEntryEntitiesCallback,
29+
) -> None:
30+
"""Set up the Fressnapf Tracker switches."""
31+
32+
async_add_entities(
33+
FressnapfTrackerSwitch(coordinator, SWITCH_ENTITY_DESCRIPTION)
34+
for coordinator in entry.runtime_data
35+
if coordinator.data.tracker_settings.features.energy_saving_mode
36+
)
37+
38+
39+
class FressnapfTrackerSwitch(FressnapfTrackerEntity, SwitchEntity):
40+
"""Fressnapf Tracker switch."""
41+
42+
async def async_turn_on(self, **kwargs: Any) -> None:
43+
"""Turn on the device."""
44+
await self.coordinator.client.set_energy_saving(True)
45+
await self.coordinator.async_request_refresh()
46+
47+
async def async_turn_off(self, **kwargs: Any) -> None:
48+
"""Turn off the device."""
49+
await self.coordinator.client.set_energy_saving(False)
50+
await self.coordinator.async_request_refresh()
51+
52+
@property
53+
def is_on(self) -> bool:
54+
"""Return true if device is on."""
55+
if TYPE_CHECKING:
56+
# The entity is not created if energy_saving is None
57+
assert self.coordinator.data.energy_saving is not None
58+
return self.coordinator.data.energy_saving.value == 1

tests/components/fressnapf_tracker/conftest.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from fressnapftracker import (
77
Device,
8+
EnergySaving,
89
LedActivatable,
910
LedBrightness,
1011
PhoneVerificationResponse,
@@ -44,9 +45,12 @@
4445
),
4546
tracker_settings=TrackerSettings(
4647
generation="GPS Tracker 2.0",
47-
features=TrackerFeatures(flash_light=True, live_tracking=True),
48+
features=TrackerFeatures(
49+
flash_light=True, energy_saving_mode=True, live_tracking=True
50+
),
4851
),
49-
led_brightness=LedBrightness(value=50),
52+
led_brightness=LedBrightness(status="ok", value=50),
53+
energy_saving=EnergySaving(status="ok", value=1),
5054
deep_sleep=None,
5155
led_activatable=LedActivatable(
5256
has_led=True,
@@ -133,6 +137,7 @@ def mock_api_client() -> Generator[MagicMock]:
133137
client = mock_api_client.return_value
134138
client.get_tracker = AsyncMock(return_value=MOCK_TRACKER)
135139
client.set_led_brightness = AsyncMock(return_value=None)
140+
client.set_energy_saving = AsyncMock(return_value=None)
136141
yield client
137142

138143

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# serializer version: 1
2+
# name: test_state_entity_device_snapshots[switch.fluffy_sleep_mode-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': None,
8+
'config_entry_id': <ANY>,
9+
'config_subentry_id': <ANY>,
10+
'device_class': None,
11+
'device_id': <ANY>,
12+
'disabled_by': None,
13+
'domain': 'switch',
14+
'entity_category': <EntityCategory.CONFIG: 'config'>,
15+
'entity_id': 'switch.fluffy_sleep_mode',
16+
'has_entity_name': True,
17+
'hidden_by': None,
18+
'icon': None,
19+
'id': <ANY>,
20+
'labels': set({
21+
}),
22+
'name': None,
23+
'options': dict({
24+
}),
25+
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
26+
'original_icon': None,
27+
'original_name': 'Sleep mode',
28+
'platform': 'fressnapf_tracker',
29+
'previous_unique_id': None,
30+
'suggested_object_id': None,
31+
'supported_features': 0,
32+
'translation_key': 'energy_saving',
33+
'unique_id': 'ABC123456_energy_saving',
34+
'unit_of_measurement': None,
35+
})
36+
# ---
37+
# name: test_state_entity_device_snapshots[switch.fluffy_sleep_mode-state]
38+
StateSnapshot({
39+
'attributes': ReadOnlyDict({
40+
'device_class': 'switch',
41+
'friendly_name': 'Fluffy Sleep mode',
42+
}),
43+
'context': <ANY>,
44+
'entity_id': 'switch.fluffy_sleep_mode',
45+
'last_changed': <ANY>,
46+
'last_reported': <ANY>,
47+
'last_updated': <ANY>,
48+
'state': 'on',
49+
})
50+
# ---
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""Test the Fressnapf Tracker switch platform."""
2+
3+
from collections.abc import AsyncGenerator
4+
from unittest.mock import MagicMock, patch
5+
6+
from fressnapftracker import Tracker, TrackerFeatures, TrackerSettings
7+
import pytest
8+
from syrupy.assertion import SnapshotAssertion
9+
10+
from homeassistant.components.switch import (
11+
DOMAIN as SWITCH_DOMAIN,
12+
SERVICE_TURN_OFF,
13+
SERVICE_TURN_ON,
14+
)
15+
from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, Platform
16+
from homeassistant.core import HomeAssistant
17+
from homeassistant.helpers import entity_registry as er
18+
19+
from tests.common import MockConfigEntry, snapshot_platform
20+
21+
TRACKER_NO_ENERGY_SAVING_MODE = Tracker(
22+
name="Fluffy",
23+
battery=0,
24+
charging=False,
25+
position=None,
26+
tracker_settings=TrackerSettings(
27+
generation="GPS Tracker 2.0",
28+
features=TrackerFeatures(energy_saving_mode=False),
29+
),
30+
)
31+
32+
33+
@pytest.fixture(autouse=True)
34+
async def platforms() -> AsyncGenerator[None]:
35+
"""Return the platforms to be loaded for this test."""
36+
with patch(
37+
"homeassistant.components.fressnapf_tracker.PLATFORMS", [Platform.SWITCH]
38+
):
39+
yield
40+
41+
42+
@pytest.mark.usefixtures("init_integration")
43+
async def test_state_entity_device_snapshots(
44+
hass: HomeAssistant,
45+
entity_registry: er.EntityRegistry,
46+
mock_config_entry: MockConfigEntry,
47+
snapshot: SnapshotAssertion,
48+
) -> None:
49+
"""Test switch entity is created correctly."""
50+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
51+
52+
53+
@pytest.mark.usefixtures("mock_auth_client")
54+
async def test_not_added_when_no_energy_saving_mode(
55+
hass: HomeAssistant,
56+
entity_registry: er.EntityRegistry,
57+
mock_config_entry: MockConfigEntry,
58+
mock_api_client: MagicMock,
59+
) -> None:
60+
"""Test switch entity is created correctly."""
61+
mock_api_client.get_tracker.return_value = TRACKER_NO_ENERGY_SAVING_MODE
62+
63+
mock_config_entry.add_to_hass(hass)
64+
await hass.config_entries.async_setup(mock_config_entry.entry_id)
65+
await hass.async_block_till_done()
66+
67+
entity_entries = er.async_entries_for_config_entry(
68+
entity_registry, mock_config_entry.entry_id
69+
)
70+
assert len(entity_entries) == 0
71+
72+
73+
@pytest.mark.usefixtures("init_integration")
74+
async def test_turn_on(
75+
hass: HomeAssistant,
76+
mock_api_client: MagicMock,
77+
) -> None:
78+
"""Test turning the switch on."""
79+
entity_id = "switch.fluffy_sleep_mode"
80+
81+
state = hass.states.get(entity_id)
82+
assert state is not None
83+
assert state.state == STATE_ON
84+
85+
await hass.services.async_call(
86+
SWITCH_DOMAIN,
87+
SERVICE_TURN_ON,
88+
{ATTR_ENTITY_ID: entity_id},
89+
blocking=True,
90+
)
91+
92+
mock_api_client.set_energy_saving.assert_called_once_with(True)
93+
94+
95+
@pytest.mark.usefixtures("init_integration")
96+
async def test_turn_off(
97+
hass: HomeAssistant,
98+
mock_api_client: MagicMock,
99+
) -> None:
100+
"""Test turning the switch off."""
101+
entity_id = "switch.fluffy_sleep_mode"
102+
103+
state = hass.states.get(entity_id)
104+
assert state is not None
105+
assert state.state == STATE_ON
106+
107+
await hass.services.async_call(
108+
SWITCH_DOMAIN,
109+
SERVICE_TURN_OFF,
110+
{ATTR_ENTITY_ID: entity_id},
111+
blocking=True,
112+
)
113+
114+
mock_api_client.set_energy_saving.assert_called_once_with(False)

0 commit comments

Comments
 (0)