From 76c7eef7d8fadee84f32ed4f6752f56d443a5fb1 Mon Sep 17 00:00:00 2001 From: Kris Molendyke Date: Wed, 30 Aug 2017 16:21:54 -0400 Subject: [PATCH] Add Tank Utility sensor (#9132) * Add Tank Utility sensor * Fix, disable Pylint errors * Move coverage omission to single platform section * Do not catch unknown exceptions * Check for invalid credentials in setup * Update tank_utility.py --- .coveragerc | 1 + .../components/sensor/tank_utility.py | 138 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 142 insertions(+) create mode 100644 homeassistant/components/sensor/tank_utility.py diff --git a/.coveragerc b/.coveragerc index 93a422d6d5b..37d4fd831dc 100644 --- a/.coveragerc +++ b/.coveragerc @@ -520,6 +520,7 @@ omit = homeassistant/components/sensor/swiss_public_transport.py homeassistant/components/sensor/synologydsm.py homeassistant/components/sensor/systemmonitor.py + homeassistant/components/sensor/tank_utility.py homeassistant/components/sensor/ted5000.py homeassistant/components/sensor/temper.py homeassistant/components/sensor/time_date.py diff --git a/homeassistant/components/sensor/tank_utility.py b/homeassistant/components/sensor/tank_utility.py new file mode 100644 index 00000000000..01ace415159 --- /dev/null +++ b/homeassistant/components/sensor/tank_utility.py @@ -0,0 +1,138 @@ +""" +Support for the Tank Utility propane monitor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.tank_utility/ +""" + +import datetime +import logging + +import requests +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (CONF_DEVICES, CONF_EMAIL, CONF_PASSWORD, + STATE_UNKNOWN) +from homeassistant.helpers.entity import Entity + + +REQUIREMENTS = [ + "tank_utility==1.4.0" +] + +_LOGGER = logging.getLogger(__name__) + +SCAN_INTERVAL = datetime.timedelta(hours=1) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_EMAIL): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_DEVICES): vol.All(cv.ensure_list, vol.Length(min=1)) +}) + +SENSOR_TYPE = "tank" +SENSOR_ROUNDING_PRECISION = 1 +SENSOR_UNIT_OF_MEASUREMENT = "%" +SENSOR_ATTRS = [ + "name", + "address", + "capacity", + "fuelType", + "orientation", + "status", + "time", + "time_iso" +] + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Tank Utility sensor.""" + from tank_utility import auth + email = config.get(CONF_EMAIL) + password = config.get(CONF_PASSWORD) + devices = config.get(CONF_DEVICES) + + try: + token = auth.get_token(email, password) + except requests.exceptions.HTTPError as http_error: + if (http_error.response.status_code == + requests.codes.unauthorized): # pylint: disable=no-member + _LOGGER.error("Invalid credentials") + return + + all_sensors = [] + for device in devices: + sensor = TankUtilitySensor(email, password, token, device) + all_sensors.append(sensor) + add_devices(all_sensors, True) + + +class TankUtilitySensor(Entity): + """Representation of a Tank Utility sensor.""" + + def __init__(self, email, password, token, device): + """Initialize the sensor.""" + self._email = email + self._password = password + self._token = token + self._device = device + self._state = STATE_UNKNOWN + self._name = "Tank Utility " + self.device + self._unit_of_measurement = SENSOR_UNIT_OF_MEASUREMENT + self._attributes = {} + + @property + def device(self): + """Return the device identifier.""" + return self._device + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def unit_of_measurement(self): + """Return the unit of measurement of the device.""" + return self._unit_of_measurement + + @property + def device_state_attributes(self): + """Return the attributes of the device.""" + return self._attributes + + def get_data(self): + """Get data from the device. + + Flatten dictionary to map device to map of device data. + + """ + from tank_utility import auth, device + data = {} + try: + data = device.get_device_data(self._token, self.device) + except requests.exceptions.HTTPError as http_error: + if (http_error.response.status_code == + requests.codes.unauthorized): # pylint: disable=no-member + _LOGGER.info("Getting new token") + self._token = auth.get_token(self._email, self._password, + force=True) + data = device.get_device_data(self._token, self.device) + else: + raise http_error + data.update(data.pop("device", {})) + data.update(data.pop("lastReading", {})) + return data + + def update(self): + """Set the device state and attributes.""" + data = self.get_data() + self._state = round(data[SENSOR_TYPE], SENSOR_ROUNDING_PRECISION) + self._attributes = {k: v for k, v in data.items() if k in SENSOR_ATTRS} diff --git a/requirements_all.txt b/requirements_all.txt index 363bc1b3111..d9444d511a4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -930,6 +930,9 @@ steamodd==4.21 # homeassistant.components.camera.onvif suds-py3==1.3.3.0 +# homeassistant.components.sensor.tank_utility +tank_utility==1.4.0 + # homeassistant.components.binary_sensor.tapsaff tapsaff==0.1.3