From fd6c2598a7fa8297c7d5de80703e6f624f0b1a2d Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Sat, 14 Oct 2017 14:06:44 -0400 Subject: [PATCH] Uptime sensor (#9856) * Added uptime sensor for homeassistant * Fixed pylint and flake8 errors * Made requested changes from PR - Fixed stale docstrings - Changed default state to None - Added ability for user to use hours or days * Fixed typo * Added unit_of_measurement check to test * Converted to async - Changed tests to work with async * Minor updates --- homeassistant/components/sensor/uptime.py | 78 ++++++++++++++++++++ tests/components/sensor/test_uptime.py | 88 +++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 homeassistant/components/sensor/uptime.py create mode 100644 tests/components/sensor/test_uptime.py diff --git a/homeassistant/components/sensor/uptime.py b/homeassistant/components/sensor/uptime.py new file mode 100644 index 00000000000..89c0fbffd8e --- /dev/null +++ b/homeassistant/components/sensor/uptime.py @@ -0,0 +1,78 @@ +""" +Component to retrieve uptime for Home Assistant. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.uptime/ +""" +import asyncio +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_NAME, CONF_UNIT_OF_MEASUREMENT) +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Uptime' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default='days'): + vol.All(cv.string, vol.In(['hours', 'days'])) +}) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up the uptime sensor platform.""" + name = config.get(CONF_NAME) + units = config.get(CONF_UNIT_OF_MEASUREMENT) + async_add_devices([UptimeSensor(name, units)], True) + + +class UptimeSensor(Entity): + """Representation of an uptime sensor.""" + + def __init__(self, name, units): + """Initialize the uptime sensor.""" + self._name = name + self._icon = 'mdi:clock' + self._units = units + self.initial = dt_util.now() + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def icon(self): + """Icon to display in the front end.""" + return self._icon + + @property + def unit_of_measurement(self): + """Return the unit of measurement the value is expressed in.""" + return self._units + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @asyncio.coroutine + def async_update(self): + """Update the state of the sensor.""" + delta = dt_util.now() - self.initial + div_factor = 3600 + if self.unit_of_measurement == 'days': + div_factor *= 24 + delta = delta.total_seconds() / div_factor + self._state = round(delta, 2) + _LOGGER.debug("New value: %s", delta) diff --git a/tests/components/sensor/test_uptime.py b/tests/components/sensor/test_uptime.py new file mode 100644 index 00000000000..991ecd3960b --- /dev/null +++ b/tests/components/sensor/test_uptime.py @@ -0,0 +1,88 @@ +"""The tests for the uptime sensor platform.""" +import unittest +from unittest.mock import patch +from datetime import timedelta + +from homeassistant.util.async import run_coroutine_threadsafe +from homeassistant.setup import setup_component +from homeassistant.components.sensor.uptime import UptimeSensor +from tests.common import get_test_home_assistant + + +class TestUptimeSensor(unittest.TestCase): + """Test the uptime sensor.""" + + def setUp(self): + """Set up things to run when tests begin.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + def test_uptime_min_config(self): + """Test minimum uptime configutation.""" + config = { + 'sensor': { + 'platform': 'uptime', + } + } + assert setup_component(self.hass, 'sensor', config) + + def test_uptime_sensor_name_change(self): + """Test uptime sensor with different name.""" + config = { + 'sensor': { + 'platform': 'uptime', + 'name': 'foobar', + } + } + assert setup_component(self.hass, 'sensor', config) + + def test_uptime_sensor_config_hours(self): + """Test uptime sensor with hours defined in config.""" + config = { + 'sensor': { + 'platform': 'uptime', + 'unit_of_measurement': 'hours', + } + } + assert setup_component(self.hass, 'sensor', config) + + def test_uptime_sensor_days_output(self): + """Test uptime sensor output data.""" + sensor = UptimeSensor('test', 'days') + self.assertEqual(sensor.unit_of_measurement, 'days') + new_time = sensor.initial + timedelta(days=1) + with patch('homeassistant.util.dt.now', return_value=new_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop + ).result() + self.assertEqual(sensor.state, 1.00) + new_time = sensor.initial + timedelta(days=111.499) + with patch('homeassistant.util.dt.now', return_value=new_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop + ).result() + self.assertEqual(sensor.state, 111.50) + + def test_uptime_sensor_hours_output(self): + """Test uptime sensor output data.""" + sensor = UptimeSensor('test', 'hours') + self.assertEqual(sensor.unit_of_measurement, 'hours') + new_time = sensor.initial + timedelta(hours=16) + with patch('homeassistant.util.dt.now', return_value=new_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop + ).result() + self.assertEqual(sensor.state, 16.00) + new_time = sensor.initial + timedelta(hours=72.499) + with patch('homeassistant.util.dt.now', return_value=new_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop + ).result() + self.assertEqual(sensor.state, 72.50)