1 """Support for package tracking sensors from 17track.net."""
3 from __future__
import annotations
18 from .
import SeventeenTrackCoordinator
20 ATTR_DESTINATION_COUNTRY,
27 ATTR_TRACKING_INFO_LANGUAGE,
33 NOTIFICATION_DELIVERED_MESSAGE,
34 NOTIFICATION_DELIVERED_TITLE,
42 config_entry: ConfigEntry,
43 async_add_entities: AddEntitiesCallback,
45 """Set up a 17Track sensor entry."""
47 coordinator: SeventeenTrackCoordinator = hass.data[DOMAIN][config_entry.entry_id]
48 previous_tracking_numbers: set[str] = set()
52 def _async_create_remove_entities():
53 if config_entry.data.get(DEPRECATED_KEY):
54 remove_packages(hass, coordinator.account_id, previous_tracking_numbers)
56 live_tracking_numbers = set(coordinator.data.live_packages.keys())
58 new_tracking_numbers = live_tracking_numbers - previous_tracking_numbers
59 old_tracking_numbers = previous_tracking_numbers - live_tracking_numbers
61 previous_tracking_numbers.update(live_tracking_numbers)
64 coordinator.data.live_packages[tracking_number]
65 for tracking_number
in new_tracking_numbers
68 for package_data
in coordinator.data.live_packages.values():
70 package_data.status == VALUE_DELIVERED
71 and not coordinator.show_delivered
73 old_tracking_numbers.add(package_data.tracking_number)
76 package_data.friendly_name,
77 package_data.tracking_number,
85 package_data.tracking_number,
87 for package_data
in packages_to_add
89 not coordinator.show_delivered
and package_data.status ==
"Delivered"
95 for status, summary_data
in coordinator.data.summary.items()
98 if not config_entry.data.get(DEPRECATED_KEY):
100 _async_create_remove_entities()
101 config_entry.async_on_unload(
102 coordinator.async_add_listener(_async_create_remove_entities)
107 """Define a 17Track sensor."""
109 _attr_attribution = ATTRIBUTION
110 _attr_has_entity_name =
True
112 def __init__(self, coordinator: SeventeenTrackCoordinator) ->
None:
113 """Initialize the sensor."""
116 identifiers={(DOMAIN, coordinator.account_id)},
117 entry_type=DeviceEntryType.SERVICE,
123 """Define a summary sensor."""
125 _attr_native_unit_of_measurement =
"packages"
130 coordinator: SeventeenTrackCoordinator,
132 """Initialize the sensor."""
140 """Return whether the entity is available."""
141 return self.
_status_status
in self.coordinator.data.summary
145 """Return the state of the sensor."""
146 return self.coordinator.data.summary[self.
_status_status][
"quantity"]
151 """Return the state attributes."""
152 packages = self.coordinator.data.summary[self.
_status_status][
"packages"]
156 ATTR_TRACKING_NUMBER: package.tracking_number,
157 ATTR_LOCATION: package.location,
158 ATTR_STATUS: package.status,
159 ATTR_TIMESTAMP: package.timestamp,
160 ATTR_INFO_TEXT: package.info_text,
161 ATTR_FRIENDLY_NAME: package.friendly_name,
163 for package
in packages
170 """Define an individual package sensor."""
172 _attr_translation_key =
"package"
176 coordinator: SeventeenTrackCoordinator,
177 tracking_number: str,
179 """Initialize the sensor."""
182 self.
_previous_status_previous_status = coordinator.data.live_packages[tracking_number].status
184 coordinator.account_id, tracking_number
186 package = coordinator.data.live_packages[tracking_number]
187 if not (name := package.friendly_name):
188 name = tracking_number
193 """Return whether the entity is available."""
194 return self.
_tracking_number_tracking_number
in self.coordinator.data.live_packages
198 """Return the state."""
199 return self.coordinator.data.live_packages[self.
_tracking_number_tracking_number].status
203 """Return the state attributes."""
204 package = self.coordinator.data.live_packages[self.
_tracking_number_tracking_number]
206 ATTR_DESTINATION_COUNTRY: package.destination_country,
207 ATTR_INFO_TEXT: package.info_text,
208 ATTR_TIMESTAMP: package.timestamp,
209 ATTR_LOCATION: package.location,
210 ATTR_ORIGIN_COUNTRY: package.origin_country,
211 ATTR_PACKAGE_TYPE: package.package_type,
212 ATTR_TRACKING_INFO_LANGUAGE: package.tracking_info_language,
213 ATTR_TRACKING_NUMBER: package.tracking_number,
217 def remove_packages(hass: HomeAssistant, account_id: str, packages: set[str]) ->
None:
218 """Remove entity itself."""
219 reg = er.async_get(hass)
220 for package
in packages:
221 entity_id = reg.async_get_entity_id(
224 UNIQUE_ID_TEMPLATE.format(account_id, package),
227 reg.async_remove(entity_id)
231 """Notify when package is delivered."""
232 LOGGER.debug(
"Package delivered: %s", tracking_number)
234 identification = friendly_name
if friendly_name
else tracking_number
235 message = NOTIFICATION_DELIVERED_MESSAGE.format(identification, tracking_number)
236 title = NOTIFICATION_DELIVERED_TITLE.format(identification)
237 notification_id = NOTIFICATION_DELIVERED_TITLE.format(tracking_number)
239 persistent_notification.create(
240 hass, message, title=title, notification_id=notification_id
246 """Ensure an issue is registered."""
247 ir.async_create_issue(
250 f
"deprecate_sensor_{entry_id}",
251 breaks_in_ha_version=
"2025.2.0",
255 translation_key=
"deprecate_sensor",
256 severity=ir.IssueSeverity.WARNING,
257 data={
"entry_id": entry_id},
StateType native_value(self)
dict[str, Any]|None extra_state_attributes(self)
None __init__(self, SeventeenTrackCoordinator coordinator, str tracking_number)
_attr_translation_placeholders
None __init__(self, SeventeenTrackCoordinator coordinator)
dict[str, Any]|None extra_state_attributes(self)
None __init__(self, str status, SeventeenTrackCoordinator coordinator)
StateType native_value(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
None remove_packages(HomeAssistant hass, str account_id, set[str] packages)
None deprecate_sensor_issue(HomeAssistant hass, str entry_id)
def notify_delivered(HomeAssistant hass, str friendly_name, str tracking_number)