Coverage for custom_components/bouncie/device_tracker.py: 100%

43 statements  

« prev     ^ index     » next       coverage.py v7.2.4, created at 2026-06-18 16:22 +0000

1"""Support for Bouncie device tracker.""" 

2 

3from homeassistant.components.device_tracker import SourceType 

4from homeassistant.components.device_tracker.config_entry import TrackerEntity 

5from homeassistant.core import HomeAssistant, callback 

6from homeassistant.helpers.entity import DeviceInfo 

7from homeassistant.helpers.update_coordinator import CoordinatorEntity 

8from homeassistant.util import slugify 

9 

10from . import BouncieDataUpdateCoordinator, const 

11 

12ATTRIBUTION = "Data provided by Bouncie" 

13 

14 

15def _get_vehicle_name(vehicle_info: dict) -> str: 

16 """Return a display name for a vehicle.""" 

17 return vehicle_info.get("nickName") or vehicle_info["vin"] 

18 

19 

20async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): 

21 """Set up the Bouncie vehicle trackers by config_entry.""" 

22 coordinator = hass.data[const.DOMAIN][config_entry.entry_id] 

23 async_add_entities( 

24 [ 

25 BouncieVehicleTracker(coordinator, vehicle_info) 

26 for vehicle_info in coordinator.data["vehicles"] 

27 ], 

28 True, 

29 ) 

30 

31 

32class BouncieVehicleTracker( 

33 CoordinatorEntity[BouncieDataUpdateCoordinator], TrackerEntity 

34): 

35 """Representation of a Tesla car location device tracker.""" 

36 

37 _attr_attribution = ATTRIBUTION 

38 

39 def __init__( 

40 self, 

41 coordinator: BouncieDataUpdateCoordinator, 

42 vehicle_info: dict, 

43 ) -> None: 

44 """Initialize car location entity.""" 

45 self._vehicle_info = vehicle_info 

46 vehicle_name = _get_vehicle_name(self._vehicle_info) 

47 self._attr_has_entity_name = True 

48 self._attr_name = None 

49 self._attr_unique_id = slugify(f"{vehicle_name} tracker") 

50 self._attr_device_info = DeviceInfo( 

51 identifiers={(const.DOMAIN, self._vehicle_info["vin"])}, 

52 manufacturer=self._vehicle_info[const.VEHICLE_MODEL_KEY]["make"], 

53 model=self._vehicle_info[const.VEHICLE_MODEL_KEY]["name"], 

54 name=vehicle_name, 

55 hw_version=self._vehicle_info[const.VEHICLE_MODEL_KEY]["year"], 

56 ) 

57 super().__init__(coordinator) 

58 

59 @property 

60 def source_type(self): 

61 """Return device tracker source type.""" 

62 return SourceType.GPS 

63 

64 @property 

65 def longitude(self): 

66 """Return longitude.""" 

67 return self._vehicle_info["stats"]["location"]["lon"] 

68 

69 @property 

70 def latitude(self): 

71 """Return latitude.""" 

72 return self._vehicle_info["stats"]["location"]["lat"] 

73 

74 @property 

75 def extra_state_attributes(self): 

76 """Return device state attributes.""" 

77 return { 

78 "heading": self._vehicle_info["stats"]["location"]["heading"], 

79 "speed": self._vehicle_info["stats"]["speed"], 

80 } 

81 

82 @property 

83 def force_update(self): 

84 """Disable forced updated since we are polling via the coordinator updates.""" 

85 return False 

86 

87 @callback 

88 def _handle_coordinator_update(self) -> None: 

89 self._vehicle_info = [ 

90 vehicle 

91 for vehicle in self.coordinator.data["vehicles"] 

92 if vehicle["vin"] == self._vehicle_info["vin"] 

93 ][0] or self._vehicle_info 

94 self.async_write_ha_state() 

95 return super()._handle_coordinator_update()