Skip to content

Commit fbf1950

Browse files
authored
Merge branch 'master' into test-break
2 parents 205c817 + 5232688 commit fbf1950

6 files changed

Lines changed: 139 additions & 47 deletions

File tree

custom_components/pettracer/binary_sensor.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,30 @@ class PetTracerAtHomeBinarySensor(CoordinatorEntity, BinarySensorEntity):
3636
"""Representation of a PetTracer at home binary sensor."""
3737

3838
_attr_device_class = BinarySensorDeviceClass.PRESENCE
39+
_attr_has_entity_name = True
3940

4041
def __init__(self, coordinator, device):
4142
"""Initialize the binary sensor."""
4243
super().__init__(coordinator)
4344
self._device = device
4445
self._device_id = device.id
45-
self._device_name = (
46-
device.details.name if device.details else f"PetTracer {device.id}"
47-
)
4846
self._attr_unique_id = f"pettracer_{device.id}_at_home"
49-
self._attr_name = f"{self._device_name} At Home"
47+
self._attr_name = "At Home"
48+
self._attr_suggested_object_id = f"pettracer_{device.id}_at_home"
5049

5150
@property
5251
def device_info(self) -> dict[str, Any]:
5352
"""Return device information about this sensor."""
53+
device = self._get_device_data() or self._device
54+
device_name = (
55+
device.details.name if device.details else f"PetTracer {self._device_id}"
56+
)
5457
return {
5558
"identifiers": {(DOMAIN, self._device_id)},
56-
"name": self._device_name,
59+
"name": device_name,
5760
"manufacturer": "PetTracer",
5861
"model": "GPS Collar",
59-
"sw_version": self._device.sw if self._device.sw else None,
62+
"sw_version": device.sw if device.sw else None,
6063
}
6164

6265
def _get_device_data(self):
@@ -79,27 +82,30 @@ class PetTracerChargingBinarySensor(CoordinatorEntity, BinarySensorEntity):
7982
"""Representation of a PetTracer charging binary sensor."""
8083

8184
_attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING
85+
_attr_has_entity_name = True
8286

8387
def __init__(self, coordinator, device):
8488
"""Initialize the binary sensor."""
8589
super().__init__(coordinator)
8690
self._device = device
8791
self._device_id = device.id
88-
self._device_name = (
89-
device.details.name if device.details else f"PetTracer {device.id}"
90-
)
9192
self._attr_unique_id = f"pettracer_{device.id}_charging"
92-
self._attr_name = f"{self._device_name} Charging"
93+
self._attr_name = "Charging"
94+
self._attr_suggested_object_id = f"pettracer_{device.id}_charging"
9395

9496
@property
9597
def device_info(self) -> dict[str, Any]:
9698
"""Return device information about this sensor."""
99+
device = self._get_device_data() or self._device
100+
device_name = (
101+
device.details.name if device.details else f"PetTracer {self._device_id}"
102+
)
97103
return {
98104
"identifiers": {(DOMAIN, self._device_id)},
99-
"name": self._device_name,
105+
"name": device_name,
100106
"manufacturer": "PetTracer",
101107
"model": "GPS Collar",
102-
"sw_version": self._device.sw if self._device.sw else None,
108+
"sw_version": device.sw if device.sw else None,
103109
}
104110

105111
def _get_device_data(self):

custom_components/pettracer/device_tracker.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ async def async_setup_entry(
3535
class PetTracerDeviceTracker(CoordinatorEntity, TrackerEntity):
3636
"""Representation of a PetTracer device tracker."""
3737

38+
_attr_has_entity_name = True
39+
_attr_name = None
40+
3841
def __init__(self, coordinator, device):
3942
"""Initialize the tracker."""
4043
super().__init__(coordinator)
4144
self._device = device
4245
self._device_id = device.id
4346
self._attr_unique_id = f"pettracer_{device.id}"
44-
self._attr_name = (
45-
device.details.name if device.details else f"PetTracer {device.id}"
46-
)
47+
self._attr_suggested_object_id = f"pettracer_{device.id}"
4748

4849
def _get_device_data(self):
4950
"""Get updated device data from coordinator."""
@@ -55,12 +56,16 @@ def _get_device_data(self):
5556
@property
5657
def device_info(self) -> dict[str, Any]:
5758
"""Return device information about this tracker."""
59+
device = self._get_device_data() or self._device
60+
device_name = (
61+
device.details.name if device.details else f"PetTracer {self._device_id}"
62+
)
5863
return {
59-
"identifiers": {(DOMAIN, self._device.id)},
60-
"name": self._attr_name,
64+
"identifiers": {(DOMAIN, self._device_id)},
65+
"name": device_name,
6166
"manufacturer": "PetTracer",
6267
"model": "GPS Collar",
63-
"sw_version": self._device.sw if self._device.sw else None,
68+
"sw_version": device.sw if device.sw else None,
6469
}
6570

6671
@property

custom_components/pettracer/sensor.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,28 +267,31 @@ class PetTracerSensor(CoordinatorEntity, SensorEntity):
267267
"""Representation of a PetTracer sensor."""
268268

269269
entity_description: PetTracerSensorEntityDescription
270+
_attr_has_entity_name = True
270271

271272
def __init__(self, coordinator, device, description: PetTracerSensorEntityDescription):
272273
"""Initialize the sensor."""
273274
super().__init__(coordinator)
274275
self.entity_description = description
275276
self._device = device
276277
self._device_id = device.id
277-
self._device_name = (
278-
device.details.name if device.details else f"PetTracer {device.id}"
279-
)
280278
self._attr_unique_id = f"pettracer_{device.id}_{description.key}"
281-
self._attr_name = f"{self._device_name} {description.display_name}"
279+
self._attr_name = description.display_name
280+
self._attr_suggested_object_id = f"pettracer_{device.id}_{description.key}"
282281

283282
@property
284283
def device_info(self) -> dict[str, Any]:
285284
"""Return device information about this sensor."""
285+
device = self._get_device_data() or self._device
286+
device_name = (
287+
device.details.name if device.details else f"PetTracer {self._device_id}"
288+
)
286289
return {
287290
"identifiers": {(DOMAIN, self._device_id)},
288-
"name": self._device_name,
291+
"name": device_name,
289292
"manufacturer": "PetTracer",
290293
"model": "GPS Collar",
291-
"sw_version": self._device.sw if self._device.sw else None,
294+
"sw_version": device.sw if device.sw else None,
292295
}
293296

294297
def _get_device_data(self):

tests/test_binary_sensor.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ async def test_at_home_binary_sensor_true(hass, mock_device):
6464
sensor = PetTracerAtHomeBinarySensor(coordinator, mock_device)
6565

6666
assert sensor.unique_id == "pettracer_12345_at_home"
67-
assert sensor.name == "Fluffy At Home"
67+
assert sensor.name == "At Home"
68+
assert sensor._attr_has_entity_name is True
69+
assert sensor._attr_suggested_object_id == "pettracer_12345_at_home"
6870
assert sensor.device_class == BinarySensorDeviceClass.PRESENCE
6971
assert sensor.is_on is True
7072

@@ -77,7 +79,8 @@ async def test_at_home_binary_sensor_false(hass, mock_device_no_position):
7779
sensor = PetTracerAtHomeBinarySensor(coordinator, mock_device_no_position)
7880

7981
assert sensor.unique_id == "pettracer_12346_at_home"
80-
assert sensor.name == "Rex At Home"
82+
assert sensor.name == "At Home"
83+
assert sensor._attr_suggested_object_id == "pettracer_12346_at_home"
8184
assert sensor.is_on is False
8285

8386

@@ -111,6 +114,24 @@ async def test_at_home_binary_sensor_device_info(hass, mock_device):
111114
assert device_info["model"] == "GPS Collar"
112115

113116

117+
async def test_at_home_binary_sensor_device_info_reflects_pet_rename(hass, mock_device):
118+
"""Renaming the pet must update device_info without changing identifiers."""
119+
coordinator = MagicMock()
120+
coordinator.data = {"devices": [mock_device]}
121+
122+
sensor = PetTracerAtHomeBinarySensor(coordinator, mock_device)
123+
assert sensor.device_info["name"] == "Fluffy"
124+
125+
mock_device.details.name = "Buddy"
126+
coordinator.data = {"devices": [mock_device]}
127+
128+
assert sensor.device_info["name"] == "Buddy"
129+
# Stable identifiers never move, regardless of the pet's current name.
130+
assert sensor.unique_id == "pettracer_12345_at_home"
131+
assert sensor._attr_suggested_object_id == "pettracer_12345_at_home"
132+
assert sensor.device_info["identifiers"] == {(DOMAIN, 12345)}
133+
134+
114135
async def test_charging_binary_sensor_true(hass, mock_device):
115136
"""Test charging binary sensor when collar is charging."""
116137
coordinator = MagicMock()
@@ -119,7 +140,8 @@ async def test_charging_binary_sensor_true(hass, mock_device):
119140
sensor = PetTracerChargingBinarySensor(coordinator, mock_device)
120141

121142
assert sensor.unique_id == "pettracer_12345_charging"
122-
assert sensor.name == "Fluffy Charging"
143+
assert sensor.name == "Charging"
144+
assert sensor._attr_suggested_object_id == "pettracer_12345_charging"
123145
assert sensor.device_class == BinarySensorDeviceClass.BATTERY_CHARGING
124146
assert sensor.is_on is True
125147

@@ -132,7 +154,8 @@ async def test_charging_binary_sensor_false(hass, mock_device_no_position):
132154
sensor = PetTracerChargingBinarySensor(coordinator, mock_device_no_position)
133155

134156
assert sensor.unique_id == "pettracer_12346_charging"
135-
assert sensor.name == "Rex Charging"
157+
assert sensor.name == "Charging"
158+
assert sensor._attr_suggested_object_id == "pettracer_12346_charging"
136159
assert sensor.is_on is False
137160

138161

tests/test_device_tracker.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ async def test_device_tracker_properties(hass, mock_device):
6464
tracker = PetTracerDeviceTracker(coordinator, mock_device)
6565

6666
assert tracker.unique_id == "pettracer_12345"
67-
assert tracker.name == "Fluffy"
67+
# name is None so has_entity_name composes the friendly name from the
68+
# device name alone (the pet's name), rather than baking it into the entity.
69+
assert tracker.name is None
70+
assert tracker._attr_has_entity_name is True
71+
assert tracker.device_info["name"] == "Fluffy"
72+
assert tracker._attr_suggested_object_id == "pettracer_12345"
6873
assert tracker.source_type == SourceType.GPS
6974
assert tracker.latitude == 51.5074
7075
assert tracker.longitude == -0.1278
@@ -114,7 +119,8 @@ async def test_device_tracker_no_position(hass, mock_device_no_position):
114119
tracker = PetTracerDeviceTracker(coordinator, mock_device_no_position)
115120

116121
assert tracker.unique_id == "pettracer_12346"
117-
assert tracker.name == "Rex"
122+
assert tracker.name is None
123+
assert tracker.device_info["name"] == "Rex"
118124
assert tracker.latitude is None
119125
assert tracker.longitude is None
120126
assert tracker.location_accuracy == 0
@@ -192,8 +198,8 @@ async def test_device_tracker_multiple_devices(hass, mock_device, mock_device_no
192198

193199
assert tracker1.unique_id == "pettracer_12345"
194200
assert tracker2.unique_id == "pettracer_12346"
195-
assert tracker1.name == "Fluffy"
196-
assert tracker2.name == "Rex"
201+
assert tracker1.device_info["name"] == "Fluffy"
202+
assert tracker2.device_info["name"] == "Rex"
197203
assert tracker1.latitude == 51.5074
198204
assert tracker2.latitude is None
199205

@@ -221,6 +227,26 @@ async def test_device_tracker_coordinator_update(hass, mock_device):
221227
assert tracker.longitude == -0.1300
222228

223229

230+
async def test_device_tracker_device_info_reflects_pet_rename(hass, mock_device):
231+
"""Renaming the pet must update device_info without changing identifiers."""
232+
from custom_components.pettracer.device_tracker import PetTracerDeviceTracker
233+
234+
coordinator = MagicMock()
235+
coordinator.data = {"devices": [mock_device]}
236+
237+
tracker = PetTracerDeviceTracker(coordinator, mock_device)
238+
assert tracker.device_info["name"] == "Fluffy"
239+
240+
mock_device.details.name = "Buddy"
241+
coordinator.data = {"devices": [mock_device]}
242+
243+
assert tracker.device_info["name"] == "Buddy"
244+
# Stable identifiers never move, regardless of the pet's current name.
245+
assert tracker.unique_id == "pettracer_12345"
246+
assert tracker._attr_suggested_object_id == "pettracer_12345"
247+
assert tracker.device_info["identifiers"] == {(DOMAIN, 12345)}
248+
249+
224250
async def test_device_tracker_no_details(hass):
225251
"""Test device tracker when device has no details."""
226252
from custom_components.pettracer.device_tracker import PetTracerDeviceTracker
@@ -238,7 +264,9 @@ async def test_device_tracker_no_details(hass):
238264
tracker = PetTracerDeviceTracker(coordinator, device)
239265

240266
assert tracker.unique_id == "pettracer_99999"
241-
assert tracker.name == "PetTracer 99999"
267+
assert tracker.name is None
268+
assert tracker.device_info["name"] == "PetTracer 99999"
269+
assert tracker._attr_suggested_object_id == "pettracer_99999"
242270
assert tracker.latitude is None
243271
assert tracker.longitude is None
244272
assert tracker.battery_level is None

0 commit comments

Comments
 (0)