Refactor USPS into component with Sensors+Camera (#8679)

* Inital USPS Camera expansion

* Cleanup debugging, add camera change interval

* Change to local nomail image

* Explicitly pass in date

* Move camera date info to model property

* Fix copy typo

* Fix hound line-length

* Fix lint whitespace

* Fix requirements

* Bump myusps version, clarify interval, alter update scheme

* Add units

* Code cleanup, address comments

* Use built-in scan interval, remove nomail image

* Remove logging line
This commit is contained in:
John Mihalic 2017-08-18 17:47:36 -04:00 committed by Pascal Vizeli
parent 6215e27de4
commit ecc249aa27
5 changed files with 226 additions and 50 deletions

View file

@ -176,6 +176,9 @@ omit =
homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py
homeassistant/components/usps.py
homeassistant/components/*/usps.py
homeassistant/components/velbus.py
homeassistant/components/*/velbus.py
@ -519,7 +522,6 @@ omit =
homeassistant/components/sensor/uber.py
homeassistant/components/sensor/upnp.py
homeassistant/components/sensor/ups.py
homeassistant/components/sensor/usps.py
homeassistant/components/sensor/vasttrafik.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/xbox_live.py

View file

@ -0,0 +1,94 @@
"""
Support for a camera made up of usps mail images.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera.usps/
"""
from datetime import timedelta
import logging
from homeassistant.components.camera import Camera
from homeassistant.components.usps import DATA_USPS
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['usps']
SCAN_INTERVAL = timedelta(seconds=10)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up USPS mail camera."""
if discovery_info is None:
return
usps = hass.data[DATA_USPS]
add_devices([USPSCamera(usps)])
class USPSCamera(Camera):
"""Representation of the images available from USPS."""
def __init__(self, usps):
"""Initialize the USPS camera images."""
super().__init__()
self._usps = usps
self._name = self._usps.name
self._session = self._usps.session
self._mail_img = []
self._last_mail = None
self._mail_index = 0
self._mail_count = 0
self._timer = None
def camera_image(self):
"""Update the camera's image if it has changed."""
self._usps.update()
try:
self._mail_count = len(self._usps.mail)
except TypeError:
# No mail
return None
if self._usps.mail != self._last_mail:
# Mail items must have changed
self._mail_img = []
if len(self._usps.mail) >= 1:
self._last_mail = self._usps.mail
for article in self._usps.mail:
_LOGGER.debug("Fetching article image: %s", article)
img = self._session.get(article['image']).content
self._mail_img.append(img)
try:
return self._mail_img[self._mail_index]
except IndexError:
return None
@property
def name(self):
"""Return the name of this camera."""
return '{} mail'.format(self._name)
@property
def model(self):
"""Return date of mail as model."""
try:
return 'Date: {}'.format(self._usps.mail[0]['date'])
except IndexError:
return None
@property
def should_poll(self):
"""Update the mail image index periodically."""
return True
def update(self):
"""Update mail image index."""
if self._mail_index < (self._mail_count - 1):
self._mail_index += 1
else:
self._mail_index = 0

View file

@ -6,65 +6,44 @@ https://home-assistant.io/components/sensor.usps/
"""
from collections import defaultdict
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
ATTR_ATTRIBUTION)
from homeassistant.components.usps import DATA_USPS
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DATE
from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from homeassistant.util.dt import now, parse_datetime
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['myusps==1.1.2']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'usps'
SCAN_INTERVAL = timedelta(minutes=30)
COOKIE = 'usps_cookies.pickle'
DEPENDENCIES = ['usps']
STATUS_DELIVERED = 'delivered'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME): cv.string
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the USPS platform."""
import myusps
try:
cookie = hass.config.path(COOKIE)
session = myusps.get_session(
config.get(CONF_USERNAME), config.get(CONF_PASSWORD),
cookie_path=cookie)
except myusps.USPSError:
_LOGGER.exception('Could not connect to My USPS')
return False
if discovery_info is None:
return
add_devices([USPSPackageSensor(session, config.get(CONF_NAME)),
USPSMailSensor(session, config.get(CONF_NAME))], True)
usps = hass.data[DATA_USPS]
add_devices([USPSPackageSensor(usps),
USPSMailSensor(usps)], True)
class USPSPackageSensor(Entity):
"""USPS Package Sensor."""
def __init__(self, session, name):
def __init__(self, usps):
"""Initialize the sensor."""
self._session = session
self._name = name
self._usps = usps
self._name = self._usps.name
self._attributes = None
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return '{} packages'.format(self._name or DOMAIN)
return '{} packages'.format(self._name)
@property
def state(self):
@ -73,16 +52,16 @@ class USPSPackageSensor(Entity):
def update(self):
"""Update device state."""
import myusps
self._usps.update()
status_counts = defaultdict(int)
for package in myusps.get_packages(self._session):
for package in self._usps.packages:
status = slugify(package['primary_status'])
if status == STATUS_DELIVERED and \
parse_datetime(package['date']).date() < now().date():
continue
status_counts[status] += 1
self._attributes = {
ATTR_ATTRIBUTION: myusps.ATTRIBUTION
ATTR_ATTRIBUTION: self._usps.attribution
}
self._attributes.update(status_counts)
self._state = sum(status_counts.values())
@ -97,21 +76,26 @@ class USPSPackageSensor(Entity):
"""Icon to use in the frontend."""
return 'mdi:package-variant-closed'
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return 'packages'
class USPSMailSensor(Entity):
"""USPS Mail Sensor."""
def __init__(self, session, name):
def __init__(self, usps):
"""Initialize the sensor."""
self._session = session
self._name = name
self._usps = usps
self._name = self._usps.name
self._attributes = None
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return '{} mail'.format(self._name or DOMAIN)
return '{} mail'.format(self._name)
@property
def state(self):
@ -120,18 +104,29 @@ class USPSMailSensor(Entity):
def update(self):
"""Update device state."""
import myusps
self._state = len(myusps.get_mail(self._session))
self._usps.update()
if self._usps.mail is not None:
self._state = len(self._usps.mail)
else:
self._state = 0
@property
def device_state_attributes(self):
"""Return the state attributes."""
import myusps
return {
ATTR_ATTRIBUTION: myusps.ATTRIBUTION
}
attr = {}
attr[ATTR_ATTRIBUTION] = self._usps.attribution
try:
attr[ATTR_DATE] = self._usps.mail[0]['date']
except IndexError:
pass
return attr
@property
def icon(self):
"""Icon to use in the frontend."""
return 'mdi:mailbox'
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return 'pieces'

View file

@ -0,0 +1,85 @@
"""
Support for USPS packages and mail.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/usps/
"""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD)
from homeassistant.helpers import (config_validation as cv, discovery)
from homeassistant.util import Throttle
from homeassistant.util.dt import now
REQUIREMENTS = ['myusps==1.1.3']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'usps'
DATA_USPS = 'data_usps'
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
COOKIE = 'usps_cookies.pickle'
USPS_TYPE = ['sensor', 'camera']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME, default=DOMAIN): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Use config values to set up a function enabling status retrieval."""
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
name = conf.get(CONF_NAME)
import myusps
try:
cookie = hass.config.path(COOKIE)
session = myusps.get_session(username, password, cookie_path=cookie)
except myusps.USPSError:
_LOGGER.exception('Could not connect to My USPS')
return False
hass.data[DATA_USPS] = USPSData(session, name)
for component in USPS_TYPE:
discovery.load_platform(hass, component, DOMAIN, {}, config)
return True
class USPSData(object):
"""Stores the data retrieved from USPS.
For each entity to use, acts as the single point responsible for fetching
updates from the server.
"""
def __init__(self, session, name):
"""Initialize the data oject."""
self.session = session
self.name = name
self.packages = []
self.mail = []
self.attribution = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""Fetch the latest info from USPS."""
import myusps
self.packages = myusps.get_packages(self.session)
self.mail = myusps.get_mail(self.session, now().date())
self.attribution = myusps.ATTRIBUTION
_LOGGER.debug("Mail, request date: %s, list: %s",
now().date(), self.mail)
_LOGGER.debug("Package list: %s", self.packages)

View file

@ -412,8 +412,8 @@ miniupnpc==1.9
# homeassistant.components.tts
mutagen==1.38
# homeassistant.components.sensor.usps
myusps==1.1.2
# homeassistant.components.usps
myusps==1.1.3
# homeassistant.components.media_player.nad
# homeassistant.components.media_player.nadtcp