Add service to 17track to get packages (#116067)
* Add service to 17track * Add service to 17track change to select selector add snapshot test * Add service to 17track use strings for the selector * Add service to 17track fix test
This commit is contained in:
parent
24a1f0712f
commit
1f4585cc9e
8 changed files with 262 additions and 11 deletions
|
@ -4,16 +4,81 @@ from py17track import Client as SeventeenTrackClient
|
||||||
from py17track.errors import SeventeenTrackError
|
from py17track.errors import SeventeenTrackError
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
from homeassistant.const import (
|
||||||
from homeassistant.core import HomeAssistant
|
ATTR_FRIENDLY_NAME,
|
||||||
|
ATTR_LOCATION,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_USERNAME,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
ServiceResponse,
|
||||||
|
SupportsResponse,
|
||||||
|
)
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import (
|
||||||
|
ATTR_CONFIG_ENTRY_ID,
|
||||||
|
ATTR_INFO_TEXT,
|
||||||
|
ATTR_PACKAGE_STATE,
|
||||||
|
ATTR_STATUS,
|
||||||
|
ATTR_TIMESTAMP,
|
||||||
|
ATTR_TRACKING_NUMBER,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PACKAGES,
|
||||||
|
)
|
||||||
from .coordinator import SeventeenTrackCoordinator
|
from .coordinator import SeventeenTrackCoordinator
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
"""Set up the 17Track component."""
|
||||||
|
|
||||||
|
async def get_packages(call: ServiceCall) -> ServiceResponse:
|
||||||
|
"""Get packages from 17Track."""
|
||||||
|
config_entry_id = call.data[ATTR_CONFIG_ENTRY_ID]
|
||||||
|
package_states = call.data.get(ATTR_PACKAGE_STATE, [])
|
||||||
|
seventeen_coordinator: SeventeenTrackCoordinator = hass.data[DOMAIN][
|
||||||
|
config_entry_id
|
||||||
|
]
|
||||||
|
live_packages = sorted(
|
||||||
|
await seventeen_coordinator.client.profile.packages(
|
||||||
|
show_archived=seventeen_coordinator.show_archived
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
ATTR_TRACKING_NUMBER: package.tracking_number,
|
||||||
|
ATTR_LOCATION: package.location,
|
||||||
|
ATTR_STATUS: package.status,
|
||||||
|
ATTR_TIMESTAMP: package.timestamp,
|
||||||
|
ATTR_INFO_TEXT: package.info_text,
|
||||||
|
ATTR_FRIENDLY_NAME: package.friendly_name,
|
||||||
|
}
|
||||||
|
for package in live_packages
|
||||||
|
if slugify(package.status) in package_states or package_states == []
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PACKAGES,
|
||||||
|
get_packages,
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up 17Track from a config entry."""
|
"""Set up 17Track from a config entry."""
|
||||||
|
@ -26,10 +91,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
except SeventeenTrackError as err:
|
except SeventeenTrackError as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
coordinator = SeventeenTrackCoordinator(hass, client)
|
seventeen_coordinator = SeventeenTrackCoordinator(hass, client)
|
||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await seventeen_coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = seventeen_coordinator
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -40,3 +40,8 @@ NOTIFICATION_DELIVERED_MESSAGE = (
|
||||||
)
|
)
|
||||||
|
|
||||||
VALUE_DELIVERED = "Delivered"
|
VALUE_DELIVERED = "Delivered"
|
||||||
|
|
||||||
|
SERVICE_GET_PACKAGES = "get_packages"
|
||||||
|
|
||||||
|
ATTR_PACKAGE_STATE = "package_state"
|
||||||
|
ATTR_CONFIG_ENTRY_ID = "config_entry_id"
|
||||||
|
|
|
@ -45,19 +45,19 @@ class SeventeenTrackCoordinator(DataUpdateCoordinator[SeventeenTrackData]):
|
||||||
self.show_delivered = self.config_entry.options[CONF_SHOW_DELIVERED]
|
self.show_delivered = self.config_entry.options[CONF_SHOW_DELIVERED]
|
||||||
self.account_id = client.profile.account_id
|
self.account_id = client.profile.account_id
|
||||||
|
|
||||||
self._show_archived = self.config_entry.options[CONF_SHOW_ARCHIVED]
|
self.show_archived = self.config_entry.options[CONF_SHOW_ARCHIVED]
|
||||||
self._client = client
|
self.client = client
|
||||||
|
|
||||||
async def _async_update_data(self) -> SeventeenTrackData:
|
async def _async_update_data(self) -> SeventeenTrackData:
|
||||||
"""Fetch data from 17Track API."""
|
"""Fetch data from 17Track API."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
summary = await self._client.profile.summary(
|
summary = await self.client.profile.summary(
|
||||||
show_archived=self._show_archived
|
show_archived=self.show_archived
|
||||||
)
|
)
|
||||||
|
|
||||||
live_packages = set(
|
live_packages = set(
|
||||||
await self._client.profile.packages(show_archived=self._show_archived)
|
await self.client.profile.packages(show_archived=self.show_archived)
|
||||||
)
|
)
|
||||||
|
|
||||||
except SeventeenTrackError as err:
|
except SeventeenTrackError as err:
|
||||||
|
|
|
@ -26,5 +26,8 @@
|
||||||
"default": "mdi:package"
|
"default": "mdi:package"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"get_packages": "mdi:package"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
homeassistant/components/seventeentrack/services.yaml
Normal file
20
homeassistant/components/seventeentrack/services.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
get_packages:
|
||||||
|
fields:
|
||||||
|
package_state:
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- "not_found"
|
||||||
|
- "in_transit"
|
||||||
|
- "expired"
|
||||||
|
- "ready_to_be_picked_up"
|
||||||
|
- "undelivered"
|
||||||
|
- "delivered"
|
||||||
|
- "returned"
|
||||||
|
translation_key: package_state
|
||||||
|
config_entry_id:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: seventeentrack
|
|
@ -66,5 +66,34 @@
|
||||||
"name": "Package {name}"
|
"name": "Package {name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"get_packages": {
|
||||||
|
"name": "Get packages",
|
||||||
|
"description": "Get packages from 17Track",
|
||||||
|
"fields": {
|
||||||
|
"package_state": {
|
||||||
|
"name": "Package states",
|
||||||
|
"description": "Only return packages with the specified states. Returns all packages if not specified."
|
||||||
|
},
|
||||||
|
"config_entry_id": {
|
||||||
|
"name": "17Track service",
|
||||||
|
"description": "The packages will be retrieved for the selected service."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"package_state": {
|
||||||
|
"options": {
|
||||||
|
"not_found": "[%key:component::seventeentrack::entity::sensor::not_found::name%]",
|
||||||
|
"in_transit": "[%key:component::seventeentrack::entity::sensor::in_transit::name%]",
|
||||||
|
"expired": "[%key:component::seventeentrack::entity::sensor::expired::name%]",
|
||||||
|
"ready_to_be_picked_up": "[%key:component::seventeentrack::entity::sensor::ready_to_be_picked_up::name%]",
|
||||||
|
"undelivered": "[%key:component::seventeentrack::entity::sensor::undelivered::name%]",
|
||||||
|
"delivered": "[%key:component::seventeentrack::entity::sensor::delivered::name%]",
|
||||||
|
"returned": "[%key:component::seventeentrack::entity::sensor::returned::name%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
tests/components/seventeentrack/snapshots/test_services.ambr
Normal file
53
tests/components/seventeentrack/snapshots/test_services.ambr
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_get_all_packages
|
||||||
|
dict({
|
||||||
|
'packages': list([
|
||||||
|
dict({
|
||||||
|
'friendly_name': 'friendly name 3',
|
||||||
|
'info_text': 'info text 1',
|
||||||
|
'location': 'location 1',
|
||||||
|
'status': 'Expired',
|
||||||
|
'timestamp': datetime.datetime(2020, 8, 10, 10, 32, tzinfo=<UTC>),
|
||||||
|
'tracking_number': '123',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'friendly_name': 'friendly name 1',
|
||||||
|
'info_text': 'info text 1',
|
||||||
|
'location': 'location 1',
|
||||||
|
'status': 'In Transit',
|
||||||
|
'timestamp': datetime.datetime(2020, 8, 10, 10, 32, tzinfo=<UTC>),
|
||||||
|
'tracking_number': '456',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'friendly_name': 'friendly name 2',
|
||||||
|
'info_text': 'info text 1',
|
||||||
|
'location': 'location 1',
|
||||||
|
'status': 'Delivered',
|
||||||
|
'timestamp': datetime.datetime(2020, 8, 10, 10, 32, tzinfo=<UTC>),
|
||||||
|
'tracking_number': '789',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_get_packages_from_list
|
||||||
|
dict({
|
||||||
|
'packages': list([
|
||||||
|
dict({
|
||||||
|
'friendly_name': 'friendly name 1',
|
||||||
|
'info_text': 'info text 1',
|
||||||
|
'location': 'location 1',
|
||||||
|
'status': 'In Transit',
|
||||||
|
'timestamp': datetime.datetime(2020, 8, 10, 10, 32, tzinfo=<UTC>),
|
||||||
|
'tracking_number': '456',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'friendly_name': 'friendly name 2',
|
||||||
|
'info_text': 'info text 1',
|
||||||
|
'location': 'location 1',
|
||||||
|
'status': 'Delivered',
|
||||||
|
'timestamp': datetime.datetime(2020, 8, 10, 10, 32, tzinfo=<UTC>),
|
||||||
|
'tracking_number': '789',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
# ---
|
76
tests/components/seventeentrack/test_services.py
Normal file
76
tests/components/seventeentrack/test_services.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"""Tests for the seventeentrack service."""
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.seventeentrack import DOMAIN, SERVICE_GET_PACKAGES
|
||||||
|
from homeassistant.core import HomeAssistant, SupportsResponse
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.seventeentrack import init_integration
|
||||||
|
from tests.components.seventeentrack.conftest import get_package
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_packages_from_list(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_seventeentrack: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Ensure service returns only the packages in the list."""
|
||||||
|
await _mock_packages(mock_seventeentrack)
|
||||||
|
await init_integration(hass, mock_config_entry)
|
||||||
|
service_response = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PACKAGES,
|
||||||
|
{
|
||||||
|
"config_entry_id": mock_config_entry.entry_id,
|
||||||
|
"package_state": ["in_transit", "delivered"],
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
return_response=SupportsResponse.ONLY,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert service_response == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_all_packages(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_seventeentrack: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Ensure service returns all packages when non provided."""
|
||||||
|
await _mock_packages(mock_seventeentrack)
|
||||||
|
await init_integration(hass, mock_config_entry)
|
||||||
|
service_response = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PACKAGES,
|
||||||
|
{
|
||||||
|
"config_entry_id": mock_config_entry.entry_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
return_response=SupportsResponse.ONLY,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert service_response == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
async def _mock_packages(mock_seventeentrack):
|
||||||
|
package1 = get_package(status=10)
|
||||||
|
package2 = get_package(
|
||||||
|
tracking_number="789",
|
||||||
|
friendly_name="friendly name 2",
|
||||||
|
status=40,
|
||||||
|
)
|
||||||
|
package3 = get_package(
|
||||||
|
tracking_number="123",
|
||||||
|
friendly_name="friendly name 3",
|
||||||
|
status=20,
|
||||||
|
)
|
||||||
|
mock_seventeentrack.return_value.profile.packages.return_value = [
|
||||||
|
package1,
|
||||||
|
package2,
|
||||||
|
package3,
|
||||||
|
]
|
Loading…
Add table
Reference in a new issue