From 6dc877d8def472b95bafbfc1385dbde891cc7639 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 13 Sep 2015 11:38:06 +0200 Subject: [PATCH 1/6] Add command sensor --- .../components/sensor/command_sensor.py | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 homeassistant/components/sensor/command_sensor.py diff --git a/homeassistant/components/sensor/command_sensor.py b/homeassistant/components/sensor/command_sensor.py new file mode 100644 index 00000000000..752b2e21a20 --- /dev/null +++ b/homeassistant/components/sensor/command_sensor.py @@ -0,0 +1,124 @@ +""" +homeassistant.components.sensor.command_sensor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure custom shell commands to turn a value for a sensor. + +Configuration: + +To use the command_line sensor you will need to add something like the +following to your configuration.yaml file. + +sensor: + platform: command_sensor + name: "Command sensor" + command: sensor_command + unit_of_measurement: "°C" + correction_factor: 0.0001 + +Variables: + +name +*Optional +Name of the command sensor. + +command +*Required +The action to take to get the value. + +unit_of_measurement +*Optional +Defines the units of measurement of the sensor, if any. + +correction_factor +*Optional +A float value to do some basic calculations. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.command_sensor.html +""" +import logging +from subprocess import check_output, CalledProcessError +from datetime import timedelta + +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "Command Sensor" + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add the Command Sensor. """ + + if config.get('command') is None: + _LOGGER.error('Missing required variable: "command"') + return False + + data = CommandSensorData(config.get('command')) + + add_devices_callback([CommandSensor( + data, + config.get('name', DEFAULT_NAME), + config.get('unit_of_measurement'), + config.get('correction_factor', 1.0) + )]) + + +class CommandSensor(Entity): + """ Represents a sensor that is returning a value of a shell commands. """ + def __init__(self, data, name, unit_of_measurement, corr_factor): + self.data = data + self._name = name + self._state = False + self._unit_of_measurement = unit_of_measurement + self._corr_factor = corr_factor + self.update() + + @property + def name(self): + """ The name of the sensor. """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit the value is expressed in. """ + return self._unit_of_measurement + + @property + def state(self): + """ Returns the state of the device. """ + return self._state + + def update(self): + """ Gets the latest data and updates the state. """ + self.data.update() + value = self.data.value + + if value is not None: + self._state = int(round(float(value)) * self._corr_factor) + + +# pylint: disable=too-few-public-methods +class CommandSensorData(object): + """ Class for handling the data retrieval. """ + + def __init__(self, command): + self.command = command + self.value = None + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Gets the latest data with a shell command. """ + _LOGGER.info('Running command: %s', self.command) + + try: + return_value = check_output(self.command.split()) + self.value = return_value.strip().decode('utf-8') + except CalledProcessError: + _LOGGER.error('Command failed: %s', self.command) From 6606d2a73cad4d7d67bd6cd69dfae54a941038e6 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 13 Sep 2015 11:38:27 +0200 Subject: [PATCH 2/6] Add command sensor --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 9cb5bdc63dd..97d1f87582d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -62,6 +62,7 @@ omit = homeassistant/components/notify/xmpp.py homeassistant/components/sensor/arest.py homeassistant/components/sensor/bitcoin.py + homeassistant/components/sensor/command_sensor.py homeassistant/components/sensor/dht.py homeassistant/components/sensor/efergy.py homeassistant/components/sensor/forecast.py From 27845d3fc5b2a423b9810124b69dbac8026dd6e6 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 14 Sep 2015 10:44:07 +0200 Subject: [PATCH 3/6] Allow decimal places to be set --- .../components/sensor/command_sensor.py | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sensor/command_sensor.py b/homeassistant/components/sensor/command_sensor.py index 752b2e21a20..5503d78deb1 100644 --- a/homeassistant/components/sensor/command_sensor.py +++ b/homeassistant/components/sensor/command_sensor.py @@ -14,6 +14,7 @@ sensor: command: sensor_command unit_of_measurement: "°C" correction_factor: 0.0001 + decimal_places: 0 Variables: @@ -33,17 +34,20 @@ correction_factor *Optional A float value to do some basic calculations. +decimal_places +*Optional +Number of decimal places of the value. Default is 0. + For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.command_sensor.html """ import logging -from subprocess import check_output, CalledProcessError +import subprocess from datetime import timedelta from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "Command Sensor" @@ -66,18 +70,21 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): data, config.get('name', DEFAULT_NAME), config.get('unit_of_measurement'), - config.get('correction_factor', 1.0) + config.get('correction_factor', None), + config.get('decimal_places', 0) )]) class CommandSensor(Entity): """ Represents a sensor that is returning a value of a shell commands. """ - def __init__(self, data, name, unit_of_measurement, corr_factor): + def __init__(self, data, name, unit_of_measurement, corr_factor, + decimal_places): self.data = data self._name = name self._state = False self._unit_of_measurement = unit_of_measurement - self._corr_factor = corr_factor + self._corr_factor = float(corr_factor) + self._decimal_places = decimal_places self.update() @property @@ -101,7 +108,11 @@ class CommandSensor(Entity): value = self.data.value if value is not None: - self._state = int(round(float(value)) * self._corr_factor) + if self._corr_factor is not None: + self._state = round((int(value) * self._corr_factor), + self._decimal_places) + else: + self._state = value # pylint: disable=too-few-public-methods @@ -118,7 +129,7 @@ class CommandSensorData(object): _LOGGER.info('Running command: %s', self.command) try: - return_value = check_output(self.command.split()) + return_value = subprocess.check_output(self.command.split()) self.value = return_value.strip().decode('utf-8') - except CalledProcessError: + except subprocess.CalledProcessError: _LOGGER.error('Command failed: %s', self.command) From fe074835f0ec339df35a0a4cb6ab63e9441b43c0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 15 Sep 2015 07:56:08 +0200 Subject: [PATCH 4/6] Fix pylint issue --- homeassistant/components/switch/command_switch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switch/command_switch.py b/homeassistant/components/switch/command_switch.py index 52baeebb476..3a1964ad4d0 100644 --- a/homeassistant/components/switch/command_switch.py +++ b/homeassistant/components/switch/command_switch.py @@ -36,9 +36,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.command_switch.html """ import logging -from homeassistant.components.switch import SwitchDevice import subprocess +from homeassistant.components.switch import SwitchDevice + _LOGGER = logging.getLogger(__name__) From 039c5cd847c4ed07d1238705abb4dc537c5d57bc Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 15 Sep 2015 07:56:08 +0200 Subject: [PATCH 5/6] Change import ordering --- homeassistant/components/switch/command_switch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switch/command_switch.py b/homeassistant/components/switch/command_switch.py index 52baeebb476..3a1964ad4d0 100644 --- a/homeassistant/components/switch/command_switch.py +++ b/homeassistant/components/switch/command_switch.py @@ -36,9 +36,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.command_switch.html """ import logging -from homeassistant.components.switch import SwitchDevice import subprocess +from homeassistant.components.switch import SwitchDevice + _LOGGER = logging.getLogger(__name__) From 1a73c1b9918a604c4bbd25d9a56d8a81d787853e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 15 Sep 2015 08:37:43 +0200 Subject: [PATCH 6/6] Fix pylint issue --- homeassistant/components/sensor/command_sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/sensor/command_sensor.py b/homeassistant/components/sensor/command_sensor.py index 5503d78deb1..3a3199be627 100644 --- a/homeassistant/components/sensor/command_sensor.py +++ b/homeassistant/components/sensor/command_sensor.py @@ -75,6 +75,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): )]) +# pylint: disable=too-many-arguments class CommandSensor(Entity): """ Represents a sensor that is returning a value of a shell commands. """ def __init__(self, data, name, unit_of_measurement, corr_factor,