Home Assistant Unofficial Reference 2024.12.1
services.py
Go to the documentation of this file.
1 """Services for the seventeentrack integration."""
2 
3 from typing import Any, Final
4 
5 from pyseventeentrack.package import PACKAGE_STATUS_MAP, Package
6 import voluptuous as vol
7 
8 from homeassistant.config_entries import ConfigEntry, ConfigEntryState
9 from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_LOCATION
10 from homeassistant.core import (
11  HomeAssistant,
12  ServiceCall,
13  ServiceResponse,
14  SupportsResponse,
15 )
16 from homeassistant.exceptions import ServiceValidationError
17 from homeassistant.helpers import config_validation as cv, selector
18 from homeassistant.util import slugify
19 
20 from . import SeventeenTrackCoordinator
21 from .const import (
22  ATTR_CONFIG_ENTRY_ID,
23  ATTR_DESTINATION_COUNTRY,
24  ATTR_INFO_TEXT,
25  ATTR_ORIGIN_COUNTRY,
26  ATTR_PACKAGE_STATE,
27  ATTR_PACKAGE_TRACKING_NUMBER,
28  ATTR_PACKAGE_TYPE,
29  ATTR_STATUS,
30  ATTR_TIMESTAMP,
31  ATTR_TRACKING_INFO_LANGUAGE,
32  ATTR_TRACKING_NUMBER,
33  DOMAIN,
34  SERVICE_ARCHIVE_PACKAGE,
35  SERVICE_GET_PACKAGES,
36 )
37 
38 SERVICE_ADD_PACKAGES_SCHEMA: Final = vol.Schema(
39  {
40  vol.Required(ATTR_CONFIG_ENTRY_ID): cv.string,
41  vol.Optional(ATTR_PACKAGE_STATE): selector.SelectSelector(
42  selector.SelectSelectorConfig(
43  multiple=True,
44  options=[
45  value.lower().replace(" ", "_")
46  for value in PACKAGE_STATUS_MAP.values()
47  ],
48  mode=selector.SelectSelectorMode.DROPDOWN,
49  translation_key=ATTR_PACKAGE_STATE,
50  )
51  ),
52  }
53 )
54 
55 SERVICE_ARCHIVE_PACKAGE_SCHEMA: Final = vol.Schema(
56  {
57  vol.Required(ATTR_CONFIG_ENTRY_ID): cv.string,
58  vol.Required(ATTR_PACKAGE_TRACKING_NUMBER): cv.string,
59  }
60 )
61 
62 
63 def setup_services(hass: HomeAssistant) -> None:
64  """Set up the services for the seventeentrack integration."""
65 
66  async def get_packages(call: ServiceCall) -> ServiceResponse:
67  """Get packages from 17Track."""
68  config_entry_id = call.data[ATTR_CONFIG_ENTRY_ID]
69  package_states = call.data.get(ATTR_PACKAGE_STATE, [])
70 
71  await _validate_service(config_entry_id)
72 
73  seventeen_coordinator: SeventeenTrackCoordinator = hass.data[DOMAIN][
74  config_entry_id
75  ]
76  live_packages = sorted(
77  await seventeen_coordinator.client.profile.packages(
78  show_archived=seventeen_coordinator.show_archived
79  )
80  )
81 
82  return {
83  "packages": [
84  package_to_dict(package)
85  for package in live_packages
86  if slugify(package.status) in package_states or package_states == []
87  ]
88  }
89 
90  async def archive_package(call: ServiceCall) -> None:
91  config_entry_id = call.data[ATTR_CONFIG_ENTRY_ID]
92  tracking_number = call.data[ATTR_PACKAGE_TRACKING_NUMBER]
93 
94  await _validate_service(config_entry_id)
95 
96  seventeen_coordinator: SeventeenTrackCoordinator = hass.data[DOMAIN][
97  config_entry_id
98  ]
99 
100  await seventeen_coordinator.client.profile.archive_package(tracking_number)
101 
102  def package_to_dict(package: Package) -> dict[str, Any]:
103  result = {
104  ATTR_DESTINATION_COUNTRY: package.destination_country,
105  ATTR_ORIGIN_COUNTRY: package.origin_country,
106  ATTR_PACKAGE_TYPE: package.package_type,
107  ATTR_TRACKING_INFO_LANGUAGE: package.tracking_info_language,
108  ATTR_TRACKING_NUMBER: package.tracking_number,
109  ATTR_LOCATION: package.location,
110  ATTR_STATUS: package.status,
111  ATTR_INFO_TEXT: package.info_text,
112  ATTR_FRIENDLY_NAME: package.friendly_name,
113  }
114  if timestamp := package.timestamp:
115  result[ATTR_TIMESTAMP] = timestamp.isoformat()
116  return result
117 
118  async def _validate_service(config_entry_id):
119  entry: ConfigEntry | None = hass.config_entries.async_get_entry(config_entry_id)
120  if not entry:
122  translation_domain=DOMAIN,
123  translation_key="invalid_config_entry",
124  translation_placeholders={
125  "config_entry_id": config_entry_id,
126  },
127  )
128  if entry.state != ConfigEntryState.LOADED:
130  translation_domain=DOMAIN,
131  translation_key="unloaded_config_entry",
132  translation_placeholders={
133  "config_entry_id": entry.title,
134  },
135  )
136 
137  hass.services.async_register(
138  DOMAIN,
139  SERVICE_GET_PACKAGES,
140  get_packages,
141  schema=SERVICE_ADD_PACKAGES_SCHEMA,
142  supports_response=SupportsResponse.ONLY,
143  )
144 
145  hass.services.async_register(
146  DOMAIN,
147  SERVICE_ARCHIVE_PACKAGE,
148  archive_package,
149  schema=SERVICE_ARCHIVE_PACKAGE_SCHEMA,
150  )