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

42 statements  

« prev     ^ index     » next       coverage.py v7.2.4, created at 2026-07-21 22:31 +0000

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

2 

3from homeassistant.components.device_tracker import SourceType, TrackerEntity 

4from homeassistant.core import HomeAssistant, callback 

5from homeassistant.helpers.entity import DeviceInfo 

6from homeassistant.helpers.update_coordinator import CoordinatorEntity 

7from homeassistant.util import slugify 

8 

9from . import BouncieDataUpdateCoordinator, const 

10 

11ATTRIBUTION = "Data provided by Bouncie" 

12 

13 

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

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

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

17 

18 

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

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

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

22 async_add_entities( 

23 [ 

24 BouncieVehicleTracker(coordinator, vehicle_info) 

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

26 ], 

27 True, 

28 ) 

29 

30 

31class BouncieVehicleTracker( 

32 CoordinatorEntity[BouncieDataUpdateCoordinator], TrackerEntity 

33): 

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

35 

36 _attr_attribution = ATTRIBUTION 

37 

38 def __init__( 

39 self, 

40 coordinator: BouncieDataUpdateCoordinator, 

41 vehicle_info: dict, 

42 ) -> None: 

43 """Initialize car location entity.""" 

44 self._vehicle_info = vehicle_info 

45 vehicle_name = _get_vehicle_name(self._vehicle_info) 

46 self._attr_has_entity_name = True 

47 self._attr_name = None 

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

49 self._attr_device_info = DeviceInfo( 

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

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

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

53 name=vehicle_name, 

54 hw_version=str(self._vehicle_info[const.VEHICLE_MODEL_KEY]["year"]), 

55 ) 

56 super().__init__(coordinator) 

57 

58 @property 

59 def source_type(self): 

60 """Return device tracker source type.""" 

61 return SourceType.GPS 

62 

63 @property 

64 def longitude(self): 

65 """Return longitude.""" 

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

67 

68 @property 

69 def latitude(self): 

70 """Return latitude.""" 

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

72 

73 @property 

74 def extra_state_attributes(self): 

75 """Return device state attributes.""" 

76 return { 

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

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

79 } 

80 

81 @property 

82 def force_update(self): 

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

84 return False 

85 

86 @callback 

87 def _handle_coordinator_update(self) -> None: 

88 self._vehicle_info = [ 

89 vehicle 

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

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

92 ][0] or self._vehicle_info 

93 self.async_write_ha_state() 

94 return super()._handle_coordinator_update()