Add AfterShip sensor for packages (#18034)
* Add AfterShip sensor for packages 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 * Fix typo and update ordering
This commit is contained in:
parent
d05450487c
commit
b32e6fe0d5
3 changed files with 132 additions and 0 deletions
|
@ -693,6 +693,7 @@ omit =
|
||||||
homeassistant/components/route53.py
|
homeassistant/components/route53.py
|
||||||
homeassistant/components/scene/hunterdouglas_powerview.py
|
homeassistant/components/scene/hunterdouglas_powerview.py
|
||||||
homeassistant/components/scene/lifx_cloud.py
|
homeassistant/components/scene/lifx_cloud.py
|
||||||
|
homeassistant/components/sensor/aftership.py
|
||||||
homeassistant/components/sensor/airvisual.py
|
homeassistant/components/sensor/airvisual.py
|
||||||
homeassistant/components/sensor/alpha_vantage.py
|
homeassistant/components/sensor/alpha_vantage.py
|
||||||
homeassistant/components/sensor/ambient_station.py
|
homeassistant/components/sensor/ambient_station.py
|
||||||
|
|
128
homeassistant/components/sensor/aftership.py
Normal file
128
homeassistant/components/sensor/aftership.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
"""
|
||||||
|
Support for non-delivered packages recorded in AfterShip.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://www.home-assistant.io/components/sensor.aftership/
|
||||||
|
"""
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
|
from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
REQUIREMENTS = ['pyaftership==0.1.2']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTRIBUTION = 'Information provided by AfterShip'
|
||||||
|
|
||||||
|
CONF_SLUG = 'slug'
|
||||||
|
CONF_TITLE = 'title'
|
||||||
|
CONF_TRACKING_NUMBER = 'tracking_number'
|
||||||
|
|
||||||
|
DEFAULT_NAME = 'aftership'
|
||||||
|
|
||||||
|
ICON = 'mdi:package-variant-closed'
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
vol.Required(CONF_API_KEY): cv.string,
|
||||||
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(
|
||||||
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
|
"""Set up the AfterShip sensor platform."""
|
||||||
|
from pyaftership.tracker import Tracking
|
||||||
|
|
||||||
|
apikey = config[CONF_API_KEY]
|
||||||
|
name = config[CONF_NAME]
|
||||||
|
|
||||||
|
session = async_get_clientsession(hass)
|
||||||
|
aftership = Tracking(hass.loop, session, apikey)
|
||||||
|
|
||||||
|
await aftership.get_trackings()
|
||||||
|
|
||||||
|
if not aftership.meta or aftership.meta['code'] != 200:
|
||||||
|
_LOGGER.error("No tracking data found. Check API key is correct: %s",
|
||||||
|
aftership.meta)
|
||||||
|
return
|
||||||
|
|
||||||
|
async_add_entities([AfterShipSensor(aftership, name)], True)
|
||||||
|
|
||||||
|
|
||||||
|
class AfterShipSensor(Entity):
|
||||||
|
"""Representation of a AfterShip sensor."""
|
||||||
|
|
||||||
|
def __init__(self, aftership, name):
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
self._attributes = {}
|
||||||
|
self._name = name
|
||||||
|
self._state = None
|
||||||
|
self.aftership = aftership
|
||||||
|
|
||||||
|
@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 attributes for the sensor."""
|
||||||
|
return self._attributes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Icon to use in the frontend."""
|
||||||
|
return ICON
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
async def async_update(self):
|
||||||
|
"""Get the latest data from the AfterShip API."""
|
||||||
|
await self.aftership.get_trackings()
|
||||||
|
|
||||||
|
if not self.aftership.meta:
|
||||||
|
_LOGGER.error("Unknown errors when querying")
|
||||||
|
return
|
||||||
|
if self.aftership.meta['code'] != 200:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Errors when querying AfterShip. %s", str(self.aftership.meta))
|
||||||
|
return
|
||||||
|
|
||||||
|
status_to_ignore = {'delivered'}
|
||||||
|
status_counts = {}
|
||||||
|
not_delivered_count = 0
|
||||||
|
|
||||||
|
for tracking in self.aftership.trackings['trackings']:
|
||||||
|
status = tracking['tag'].lower()
|
||||||
|
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
|
|
@ -881,6 +881,9 @@ pyW800rf32==0.1
|
||||||
# homeassistant.components.ads
|
# homeassistant.components.ads
|
||||||
pyads==2.2.6
|
pyads==2.2.6
|
||||||
|
|
||||||
|
# homeassistant.components.sensor.aftership
|
||||||
|
pyaftership==0.1.2
|
||||||
|
|
||||||
# homeassistant.components.sensor.airvisual
|
# homeassistant.components.sensor.airvisual
|
||||||
pyairvisual==2.0.1
|
pyairvisual==2.0.1
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue