Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TekSavvy Sensor unlimited bandwidth support #12325

Merged
merged 2 commits into from Mar 1, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion .coveragerc
Expand Up @@ -626,7 +626,6 @@ 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
46 changes: 29 additions & 17 deletions homeassistant/components/sensor/teksavvy.py
Expand Up @@ -31,11 +31,11 @@
REQUEST_TIMEOUT = 5 # seconds

SENSOR_TYPES = {
'usage': ['Usage', PERCENT, 'mdi:percent'],
'usage': ['Usage Ratio', 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_upload': ['On Peak Upload', GIGABYTES, 'mdi:upload'],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and line 35 would be breaking changes?

How do I ensure that makes it into the release notes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notably sensor.teksavvy_on_peak_upload_ (which is incorrect with the trailing underscore) becomes sensor.teksavvy_on_peak_upload

Additionally with both the percentage and GB values both being named usage you would get a non-deterministic result in tests for sensor.teksavvy_usage and sensor.teksavvy_usage_2.
Renaming to Usage Ratio makes it very deterministic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a section to your PR description and I'll add the label "Breaking Change"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a description, thanks!

'onpeak_total': ['On Peak Total', GIGABYTES, 'mdi:download'],
'offpeak_download': ['Off Peak download', GIGABYTES, 'mdi:download'],
'offpeak_upload': ['Off Peak Upload', GIGABYTES, 'mdi:upload'],
Expand Down Expand Up @@ -128,7 +128,9 @@ def __init__(self, loop, websession, api_key, bandwidth_cap):
self.websession = websession
self.api_key = api_key
self.bandwidth_cap = bandwidth_cap
self.data = {"limit": self.bandwidth_cap}
# Set unlimited users to infinite, otherwise the cap.
self.data = {"limit": self.bandwidth_cap} if self.bandwidth_cap > 0 \
else {"limit": float('inf')}

@asyncio.coroutine
@Throttle(MIN_TIME_BETWEEN_UPDATES)
Expand All @@ -143,17 +145,27 @@ def async_update(self):
if req.status != 200:
_LOGGER.error("Request failed with status: %u", req.status)
return False
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

try:
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"]
# Support "unlimited" users
if self.bandwidth_cap > 0:
self.data["usage"] = 100*on_peak_download/self.bandwidth_cap
else:
self.data["usage"] = 0
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
except ValueError:
_LOGGER.error("JSON Decode Failed")
return False
185 changes: 185 additions & 0 deletions tests/components/sensor/test_teksavvy.py
@@ -0,0 +1,185 @@
"""Tests for the TekSavvy sensor platform."""
import asyncio
from homeassistant.bootstrap import async_setup_component
from homeassistant.components.sensor.teksavvy import TekSavvyData
from homeassistant.helpers.aiohttp_client import async_get_clientsession


@asyncio.coroutine
def test_capped_setup(hass, aioclient_mock):
"""Test the default setup."""
config = {'platform': 'teksavvy',
'api_key': 'NOTAKEY',
'total_bandwidth': 400,
'monitored_variables': [
'usage',
'usage_gb',
'limit',
'onpeak_download',
'onpeak_upload',
'onpeak_total',
'offpeak_download',
'offpeak_upload',
'offpeak_total',
'onpeak_remaining']}

result = '{"odata.metadata":"http://api.teksavvy.com/web/Usage/$metadata'\
'#UsageSummaryRecords","value":[{'\
'"StartDate":"2018-01-01T00:00:00",'\
'"EndDate":"2018-01-31T00:00:00",'\
'"OID":"999999","IsCurrent":true,'\
'"OnPeakDownload":226.75,'\
'"OnPeakUpload":8.82,'\
'"OffPeakDownload":36.24,"OffPeakUpload":1.58'\
'}]}'
aioclient_mock.get("https://api.teksavvy.com/"
"web/Usage/UsageSummaryRecords?"
"$filter=IsCurrent%20eq%20true",
text=result)

yield from async_setup_component(hass, 'sensor', {'sensor': config})

state = hass.states.get('sensor.teksavvy_data_limit')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '400'

state = hass.states.get('sensor.teksavvy_off_peak_download')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '36.24'

state = hass.states.get('sensor.teksavvy_off_peak_upload')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '1.58'

state = hass.states.get('sensor.teksavvy_off_peak_total')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '37.82'

state = hass.states.get('sensor.teksavvy_on_peak_download')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '226.75'

state = hass.states.get('sensor.teksavvy_on_peak_upload')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '8.82'

state = hass.states.get('sensor.teksavvy_on_peak_total')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '235.57'

state = hass.states.get('sensor.teksavvy_usage_ratio')
assert state.attributes.get('unit_of_measurement') == '%'
assert state.state == '56.69'

state = hass.states.get('sensor.teksavvy_usage')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '226.75'

state = hass.states.get('sensor.teksavvy_remaining')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '173.25'


@asyncio.coroutine
def test_unlimited_setup(hass, aioclient_mock):
"""Test the default setup."""
config = {'platform': 'teksavvy',
'api_key': 'NOTAKEY',
'total_bandwidth': 0,
'monitored_variables': [
'usage',
'usage_gb',
'limit',
'onpeak_download',
'onpeak_upload',
'onpeak_total',
'offpeak_download',
'offpeak_upload',
'offpeak_total',
'onpeak_remaining']}

result = '{"odata.metadata":"http://api.teksavvy.com/web/Usage/$metadata'\
'#UsageSummaryRecords","value":[{'\
'"StartDate":"2018-01-01T00:00:00",'\
'"EndDate":"2018-01-31T00:00:00",'\
'"OID":"999999","IsCurrent":true,'\
'"OnPeakDownload":226.75,'\
'"OnPeakUpload":8.82,'\
'"OffPeakDownload":36.24,"OffPeakUpload":1.58'\
'}]}'
aioclient_mock.get("https://api.teksavvy.com/"
"web/Usage/UsageSummaryRecords?"
"$filter=IsCurrent%20eq%20true",
text=result)

yield from async_setup_component(hass, 'sensor', {'sensor': config})

state = hass.states.get('sensor.teksavvy_data_limit')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == 'inf'

state = hass.states.get('sensor.teksavvy_off_peak_download')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '36.24'

state = hass.states.get('sensor.teksavvy_off_peak_upload')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '1.58'

state = hass.states.get('sensor.teksavvy_off_peak_total')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '37.82'

state = hass.states.get('sensor.teksavvy_on_peak_download')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '226.75'

state = hass.states.get('sensor.teksavvy_on_peak_upload')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '8.82'

state = hass.states.get('sensor.teksavvy_on_peak_total')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '235.57'

state = hass.states.get('sensor.teksavvy_usage')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == '226.75'

state = hass.states.get('sensor.teksavvy_usage_ratio')
assert state.attributes.get('unit_of_measurement') == '%'
assert state.state == '0'

state = hass.states.get('sensor.teksavvy_remaining')
assert state.attributes.get('unit_of_measurement') == 'GB'
assert state.state == 'inf'


@asyncio.coroutine
def test_bad_return_code(hass, aioclient_mock):
"""Test handling a return code that isn't HTTP OK."""
aioclient_mock.get("https://api.teksavvy.com/"
"web/Usage/UsageSummaryRecords?"
"$filter=IsCurrent%20eq%20true",
status=404)

tsd = TekSavvyData(hass.loop, async_get_clientsession(hass),
'notakey', 400)

result = yield from tsd.async_update()
assert result is False


@asyncio.coroutine
def test_bad_json_decode(hass, aioclient_mock):
"""Test decoding invalid json result."""
aioclient_mock.get("https://api.teksavvy.com/"
"web/Usage/UsageSummaryRecords?"
"$filter=IsCurrent%20eq%20true",
text='this is not json')

tsd = TekSavvyData(hass.loop, async_get_clientsession(hass),
'notakey', 400)

result = yield from tsd.async_update()
assert result is False