Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ omit =
homeassistant/components/sensor/sytadin.py
homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/teksavvy.py
homeassistant/components/sensor/temper.py
homeassistant/components/sensor/tibber.py
homeassistant/components/sensor/time_date.py
Expand Down
159 changes: 159 additions & 0 deletions homeassistant/components/sensor/teksavvy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""
Support for TekSavvy Bandwidth Monitor.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.teksavvy/
"""
from datetime import timedelta
import logging

import asyncio
import async_timeout
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_API_KEY, CONF_MONITORED_VARIABLES, 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
import voluptuous as vol

_LOGGER = logging.getLogger(__name__)

DEFAULT_NAME = 'TekSavvy'
CONF_TOTAL_BANDWIDTH = 'total_bandwidth'

GIGABYTES = 'GB' # type: str
PERCENT = '%' # type: str

MIN_TIME_BETWEEN_UPDATES = timedelta(hours=1)
REQUEST_TIMEOUT = 5 # seconds

SENSOR_TYPES = {
'usage': ['Usage', PERCENT, 'mdi:percent'],
'usage_gb': ['Usage', GIGABYTES, 'mdi:download'],
'limit': ['Data limit', GIGABYTES, 'mdi:download'],
'onpeak_download': ['On Peak Download', GIGABYTES, 'mdi:download'],
'onpeak_upload': ['On Peak Upload ', GIGABYTES, 'mdi:upload'],
'onpeak_total': ['On Peak Total', GIGABYTES, 'mdi:download'],
'offpeak_download': ['Off Peak download', GIGABYTES, 'mdi:download'],
'offpeak_upload': ['Off Peak Upload', GIGABYTES, 'mdi:upload'],
'offpeak_total': ['Off Peak Total', GIGABYTES, 'mdi:download'],
'onpeak_remaining': ['Remaining', GIGABYTES, 'mdi:download']
}

API_HA_MAP = (
('OnPeakDownload', 'onpeak_download'),
('OnPeakUpload', 'onpeak_upload'),
('OffPeakDownload', 'offpeak_download'),
('OffPeakUpload', 'offpeak_upload'))

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MONITORED_VARIABLES):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_TOTAL_BANDWIDTH): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})


@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the sensor platform."""
websession = async_get_clientsession(hass)
apikey = config.get(CONF_API_KEY)
bandwidthcap = config.get(CONF_TOTAL_BANDWIDTH)

ts_data = TekSavvyData(hass.loop, websession, apikey, bandwidthcap)
ret = yield from ts_data.async_update()
if ret is False:
_LOGGER.error("Invalid Teksavvy API key: %s", apikey)
return

name = config.get(CONF_NAME)
sensors = []
for variable in config[CONF_MONITORED_VARIABLES]:
sensors.append(TekSavvySensor(ts_data, variable, name))
async_add_devices(sensors, True)


class TekSavvySensor(Entity):
"""Representation of TekSavvy Bandwidth sensor."""

def __init__(self, teksavvydata, sensor_type, name):
"""Initialize the sensor."""
self.client_name = name
self.type = sensor_type
self._name = SENSOR_TYPES[sensor_type][0]
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._icon = SENSOR_TYPES[sensor_type][2]
self.teksavvydata = teksavvydata
self._state = None

@property
def name(self):
"""Return the name of the sensor."""
return '{} {}'.format(self.client_name, 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 self._unit_of_measurement

@property
def icon(self):
"""Icon to use in the frontend, if any."""
return self._icon

@asyncio.coroutine
def async_update(self):
"""Get the latest data from TekSavvy and update the state."""
yield from self.teksavvydata.async_update()
if self.type in self.teksavvydata.data:
self._state = round(self.teksavvydata.data[self.type], 2)


class TekSavvyData(object):
"""Get data from TekSavvy API."""

def __init__(self, loop, websession, api_key, bandwidth_cap):
"""Initialize the data object."""
self.loop = loop
self.websession = websession
self.api_key = api_key
self.bandwidth_cap = bandwidth_cap
self.data = {"limit": self.bandwidth_cap}

@asyncio.coroutine
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def async_update(self):
"""Get the TekSavvy bandwidth data from the web service."""
headers = {"TekSavvy-APIKey": self.api_key}
_LOGGER.debug("Updating TekSavvy data")
url = "https://api.teksavvy.com/"\
"web/Usage/UsageSummaryRecords?$filter=IsCurrent%20eq%20true"
with async_timeout.timeout(REQUEST_TIMEOUT, loop=self.loop):
req = yield from self.websession.get(url, headers=headers)
if req.status != 200:
_LOGGER.error("Request failed with status: %u", req.status)
return False
else:
data = yield from req.json()
for (api, ha_name) in API_HA_MAP:
self.data[ha_name] = float(data["value"][0][api])
on_peak_download = self.data["onpeak_download"]
on_peak_upload = self.data["onpeak_upload"]
off_peak_download = self.data["offpeak_download"]
off_peak_upload = self.data["offpeak_upload"]
limit = self.data["limit"]
self.data["usage"] = 100*on_peak_download/self.bandwidth_cap
self.data["usage_gb"] = on_peak_download
self.data["onpeak_total"] = on_peak_download + on_peak_upload
self.data["offpeak_total"] = off_peak_download + off_peak_upload
self.data["onpeak_remaining"] = limit - on_peak_download
return True