-
-
Notifications
You must be signed in to change notification settings - Fork 29.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Why: * I receive a lot of packages from many different shipping companies. * I would like to see in haas how many packages are being delivered. This change addreses the need by: * Adding a sensor for AfterShip (aftership.com) * AfterShip supports ~490 couriers world wide thus should cover almost any sensible tracking. Notes: - For now this sensor assumes you somehow have added trackings to aftership manually. - Future idea is to expose service that allows adding a tracking based on incoming mails. - Other improvments would be to add map markers for package locations. Related: - https://community.home-assistant.io/t/package-tracking/858 - https://community.home-assistant.io/t/aftership-package-tracking/24068 - https://community.home-assistant.io/t/aftership-shipment-tracking-platform/14074 - https://community.home-assistant.io/t/aftership-state-card/57912
- Loading branch information
1 parent
145677e
commit 25cb2fb
Showing
3 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
""" | ||
Sensor for AfterShip. | ||
Gives a sensor of all non-delivered packages recorded in AfterShip. | ||
Requires an api key which can be aquired from | ||
https://secure.aftership.com/#/settings/api. | ||
Example configuration: | ||
sensor: | ||
- platform: aftership | ||
api_key: AFTERSHIP_API_KEY | ||
""" | ||
from datetime import timedelta | ||
import logging | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.components.sensor import (DOMAIN, PLATFORM_SCHEMA) | ||
|
||
from homeassistant.const import ( | ||
ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME) | ||
|
||
import homeassistant.helpers.config_validation as cv | ||
from homeassistant.helpers.entity import Entity | ||
from homeassistant.util import Throttle | ||
from homeassistant.exceptions import PlatformNotReady | ||
|
||
REQUIREMENTS = ['pyaftership==0.0.5'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
ATTRIBUTION = 'Information provided by AfterShip' | ||
|
||
DEFAULT_NAME = 'aftership' | ||
|
||
TITLE = 'title' | ||
SLUG = 'slug' | ||
TRACKING_NUMBER = 'tracking_number' | ||
|
||
ICON = 'mdi:package-variant-closed' | ||
|
||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) | ||
|
||
SERVICE_NEW_TRACKING = 'aftership_new_tracking' | ||
|
||
NEW_TRACKING_SERVICE_SCHEMA = vol.Schema({ | ||
vol.Required(TITLE): cv.string, | ||
vol.Required(SLUG): cv.string, | ||
vol.Required(TRACKING_NUMBER): cv.string, | ||
}) | ||
|
||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
vol.Required(CONF_API_KEY): cv.string, | ||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, | ||
}) | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
def setup_platform(hass, config, add_entities, discovery_info=None): | ||
"""Set up the AfterShip sensor platform.""" | ||
from pyaftership import AfterShip | ||
|
||
apikey = config.get(CONF_API_KEY) | ||
name = config.get(CONF_NAME) | ||
|
||
## todo: pyaftership hides api-key errors. For now just warn if result is empty | ||
result = AfterShip().get_trackings(apikey) | ||
|
||
if not result['success']: | ||
_LOGGER.warn("Connection error for AfterShip during startup. Could just be intermittent.") | ||
raise PlatformNotReady | ||
elif not result['data']: | ||
_LOGGER.error("No tracking data found. Check AfterShip API key is correct") | ||
return | ||
|
||
add_entities([AfterShipSensor(apikey, name)], True) | ||
|
||
def handle_new_tracking(call): | ||
"""Call when a user creates a new Afterhip tracking from HASS.""" | ||
from pyaftership import AfterShip | ||
title = call.data[TITLE] | ||
slug = call.data[SLUG] | ||
tracking_number = call.data[TRACKING_NUMBER] | ||
|
||
_aftership = AfterShip() | ||
result = _aftership.add_tracking(apikey, slug, title, tracking_number) | ||
|
||
if not result['success']: | ||
_LOGGER.debug("Created Aftership tracking") | ||
else: | ||
_LOGGER.error("Failed to create new tracking") | ||
|
||
hass.services.register(DOMAIN, SERVICE_NEW_TRACKING, handle_new_tracking, | ||
schema=NEW_TRACKING_SERVICE_SCHEMA) | ||
|
||
|
||
class AfterShipSensor(Entity): | ||
"""Representation of a AfterShip sensor.""" | ||
|
||
def __init__(self, apikey, name): | ||
"""Initialize the AfterShip sensor.""" | ||
from pyaftership import AfterShip | ||
self._name = name | ||
self._attributes = None | ||
self._state = None | ||
self._api = AfterShip() | ||
self._apikey = apikey | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the sensor.""" | ||
return self._name | ||
|
||
@property | ||
def state(self): | ||
"""Return the state of the sensor.""" | ||
return self._state | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
"""Return the unit of measurement of this entity, if any.""" | ||
return 'packages' | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes.""" | ||
return self._attributes | ||
|
||
@property | ||
def icon(self): | ||
"""Icon to use in the frontend.""" | ||
return ICON | ||
|
||
@Throttle(MIN_TIME_BETWEEN_UPDATES) | ||
def update(self): | ||
"""Update device state.""" | ||
from pyaftership import AfterShip | ||
|
||
status_to_ignore = { 'Delivered' } | ||
trackingstop = self._api.get_trackings(self._apikey) | ||
status_counts = {} | ||
not_delivered_count = 0 | ||
|
||
for tracking in trackingstop['data']['trackings']: | ||
status = tracking['tag'] | ||
name = tracking['tracking_number'] | ||
status_counts[status] = status_counts.get(status,0)+1 | ||
if status not in status_to_ignore: | ||
not_delivered_count += 1 | ||
else: | ||
_LOGGER.debug("Ignoring %s as it has status: %s",name, status) | ||
|
||
self._attributes = { | ||
ATTR_ATTRIBUTION: ATTRIBUTION, | ||
**status_counts | ||
} | ||
|
||
self._state = not_delivered_count | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters