diff --git a/.coveragerc b/.coveragerc index f5eabf3b7f6..adb3c59765e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -41,6 +41,9 @@ omit = homeassistant/components/mysensors.py homeassistant/components/*/mysensors.py + homeassistant/components/rpi_gpio.py + homeassistant/components/*/rpi_gpio.py + homeassistant/components/binary_sensor/arest.py homeassistant/components/binary_sensor/rest.py homeassistant/components/browser.py @@ -101,7 +104,6 @@ omit = homeassistant/components/sensor/netatmo.py homeassistant/components/sensor/openweathermap.py homeassistant/components/sensor/rest.py - homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py homeassistant/components/sensor/swiss_public_transport.py homeassistant/components/sensor/systemmonitor.py @@ -117,7 +119,6 @@ omit = homeassistant/components/switch/mystrom.py homeassistant/components/switch/orvibo.py homeassistant/components/switch/rest.py - homeassistant/components/switch/rpi_gpio.py homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py homeassistant/components/thermostat/heatmiser.py diff --git a/homeassistant/components/binary_sensor/rpi_gpio.py b/homeassistant/components/binary_sensor/rpi_gpio.py new file mode 100644 index 00000000000..2bb50fec766 --- /dev/null +++ b/homeassistant/components/binary_sensor/rpi_gpio.py @@ -0,0 +1,73 @@ +""" +homeassistant.components.binary_sensor.rpi_gpio +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a binary_sensor using RPi GPIO. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.rpi_gpio/ +""" + +import logging +import homeassistant.components.rpi_gpio as rpi_gpio +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import (DEVICE_DEFAULT_NAME) + +DEFAULT_PULL_MODE = "UP" +DEFAULT_BOUNCETIME = 50 +DEFAULT_INVERT_LOGIC = False + +DEPENDENCIES = ['rpi_gpio'] +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Raspberry PI GPIO devices. """ + + pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE) + bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME) + invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC) + + binary_sensors = [] + ports = config.get('ports') + for port_num, port_name in ports.items(): + binary_sensors.append(RPiGPIOBinarySensor( + port_name, port_num, pull_mode, bouncetime, invert_logic)) + add_devices(binary_sensors) + + +# pylint: disable=too-many-arguments, too-many-instance-attributes +class RPiGPIOBinarySensor(BinarySensorDevice): + """ Represents a binary sensor that uses Raspberry Pi GPIO. """ + def __init__(self, name, port, pull_mode, bouncetime, invert_logic): + # pylint: disable=no-member + + self._name = name or DEVICE_DEFAULT_NAME + self._port = port + self._pull_mode = pull_mode + self._bouncetime = bouncetime + self._invert_logic = invert_logic + + rpi_gpio.setup_input(self._port, self._pull_mode) + self._state = rpi_gpio.read_input(self._port) + + def read_gpio(port): + """ Reads state from GPIO. """ + self._state = rpi_gpio.read_input(self._port) + self.update_ha_state() + rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime) + + @property + def should_poll(self): + """ No polling needed. """ + return False + + @property + def name(self): + """ The name of the sensor. """ + return self._name + + @property + def is_on(self): + """ Returns the state of the entity. """ + return self._state != self._invert_logic diff --git a/homeassistant/components/rpi_gpio.py b/homeassistant/components/rpi_gpio.py new file mode 100644 index 00000000000..3d0a068f8ca --- /dev/null +++ b/homeassistant/components/rpi_gpio.py @@ -0,0 +1,69 @@ +""" +homeassistant.components.rpi_gpio +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to control the GPIO pins of a Raspberry Pi. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/rpi_gpio/ +""" + +import logging +try: + import RPi.GPIO as GPIO +except ImportError: + GPIO = None +from homeassistant.const import (EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP) +REQUIREMENTS = ['RPi.GPIO==0.6.1'] +DOMAIN = "rpi_gpio" +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=no-member +def setup(hass, config): + """ Sets up the Raspberry PI GPIO component. """ + if GPIO is None: + _LOGGER.error('RPi.GPIO not available. rpi_gpio ports ignored.') + return False + + def cleanup_gpio(event): + """ Stuff to do before stop home assistant. """ + GPIO.cleanup() + + def prepare_gpio(event): + """ Stuff to do when home assistant starts. """ + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio) + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio) + GPIO.setmode(GPIO.BCM) + return True + + +def setup_output(port): + """ Setup a GPIO as output. """ + GPIO.setup(port, GPIO.OUT) + + +def setup_input(port, pull_mode): + """ Setup a GPIO as input. """ + GPIO.setup(port, GPIO.IN, + GPIO.PUD_DOWN if pull_mode == 'DOWN' else GPIO.PUD_UP) + + +def write_output(port, value): + """ Write a value to a GPIO. """ + GPIO.output(port, value) + + +def read_input(port): + """ Read a value from a GPIO. """ + return GPIO.input(port) + + +def edge_detect(port, event_callback, bounce): + """ Adds detection for RISING and FALLING events. """ + GPIO.add_event_detect( + port, + GPIO.BOTH, + callback=event_callback, + bouncetime=bounce) diff --git a/homeassistant/components/sensor/rpi_gpio.py b/homeassistant/components/sensor/rpi_gpio.py deleted file mode 100644 index ef7ea8c33c1..00000000000 --- a/homeassistant/components/sensor/rpi_gpio.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -homeassistant.components.sensor.rpi_gpio -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Allows to configure a binary state sensor using RPi GPIO. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rpi_gpio/ -""" -# pylint: disable=import-error -import logging -from homeassistant.helpers.entity import Entity - -from homeassistant.const import (DEVICE_DEFAULT_NAME, - EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP) - -DEFAULT_PULL_MODE = "UP" -DEFAULT_VALUE_HIGH = "HIGH" -DEFAULT_VALUE_LOW = "LOW" -DEFAULT_BOUNCETIME = 50 - -REQUIREMENTS = ['RPi.GPIO==0.5.11'] -_LOGGER = logging.getLogger(__name__) - - -# pylint: disable=unused-argument -def setup_platform(hass, config, add_devices, discovery_info=None): - """ Sets up the Raspberry PI GPIO ports. """ - import RPi.GPIO as GPIO - GPIO.setmode(GPIO.BCM) - - sensors = [] - pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE) - value_high = config.get('value_high', DEFAULT_VALUE_HIGH) - value_low = config.get('value_low', DEFAULT_VALUE_LOW) - bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME) - ports = config.get('ports') - for port_num, port_name in ports.items(): - sensors.append(RPiGPIOSensor( - port_name, port_num, pull_mode, - value_high, value_low, bouncetime)) - add_devices(sensors) - - def cleanup_gpio(event): - """ Stuff to do before stop home assistant. """ - # pylint: disable=no-member - GPIO.cleanup() - - def prepare_gpio(event): - """ Stuff to do when home assistant starts. """ - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio) - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio) - - -# pylint: disable=too-many-arguments, too-many-instance-attributes -class RPiGPIOSensor(Entity): - """ Sets up the Raspberry PI GPIO ports. """ - def __init__(self, port_name, port_num, pull_mode, - value_high, value_low, bouncetime): - # pylint: disable=no-member - import RPi.GPIO as GPIO - self._name = port_name or DEVICE_DEFAULT_NAME - self._port = port_num - self._pull = GPIO.PUD_DOWN if pull_mode == "DOWN" else GPIO.PUD_UP - self._vhigh = value_high - self._vlow = value_low - self._bouncetime = bouncetime - GPIO.setup(self._port, GPIO.IN, pull_up_down=self._pull) - self._state = self._vhigh if GPIO.input(self._port) else self._vlow - - def edge_callback(channel): - """ port changed state """ - # pylint: disable=no-member - self._state = self._vhigh if GPIO.input(channel) else self._vlow - self.update_ha_state() - - GPIO.add_event_detect( - self._port, - GPIO.BOTH, - callback=edge_callback, - bouncetime=self._bouncetime) - - @property - def should_poll(self): - """ No polling needed. """ - return False - - @property - def name(self): - """ The name of the sensor. """ - return self._name - - @property - def state(self): - """ Returns the state of the entity. """ - return self._state diff --git a/homeassistant/components/switch/rpi_gpio.py b/homeassistant/components/switch/rpi_gpio.py index 1c36aa262f4..dffa4682279 100644 --- a/homeassistant/components/switch/rpi_gpio.py +++ b/homeassistant/components/switch/rpi_gpio.py @@ -1,69 +1,48 @@ """ homeassistant.components.switch.rpi_gpio ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Allows to control the GPIO pins of a Raspberry Pi. +Allows to configure a switch using RPi GPIO. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.rpi_gpio/ """ + import logging -try: - import RPi.GPIO as GPIO -except ImportError: - GPIO = None +import homeassistant.components.rpi_gpio as rpi_gpio from homeassistant.helpers.entity import ToggleEntity -from homeassistant.const import (DEVICE_DEFAULT_NAME, - EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP) +from homeassistant.const import (DEVICE_DEFAULT_NAME) DEFAULT_INVERT_LOGIC = False -REQUIREMENTS = ['RPi.GPIO==0.5.11'] +DEPENDENCIES = ['rpi_gpio'] _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): - """ Sets up the Raspberry PI GPIO ports. """ - if GPIO is None: - _LOGGER.error('RPi.GPIO not available. rpi_gpio ports ignored.') - return - # pylint: disable=no-member - GPIO.setmode(GPIO.BCM) + """ Sets up the Raspberry PI GPIO devices. """ + + invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC) switches = [] - invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC) ports = config.get('ports') - for port_num, port_name in ports.items(): - switches.append(RPiGPIOSwitch(port_name, port_num, invert_logic)) + for port, name in ports.items(): + switches.append(RPiGPIOSwitch(name, port, invert_logic)) add_devices(switches) - def cleanup_gpio(event): - """ Stuff to do before stop home assistant. """ - # pylint: disable=no-member - GPIO.cleanup() - - def prepare_gpio(event): - """ Stuff to do when home assistant starts. """ - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio) - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio) - class RPiGPIOSwitch(ToggleEntity): - """ Represents a port that can be toggled using Raspberry Pi GPIO. """ - - def __init__(self, name, gpio, invert_logic): + """ Represents a switch that can be toggled using Raspberry Pi GPIO. """ + def __init__(self, name, port, invert_logic): self._name = name or DEVICE_DEFAULT_NAME - self._gpio = gpio - self._active_state = not invert_logic - self._state = not self._active_state - # pylint: disable=no-member - GPIO.setup(gpio, GPIO.OUT) + self._port = port + self._invert_logic = invert_logic + self._state = False + rpi_gpio.setup_output(self._port) @property def name(self): - """ The name of the port. """ + """ The name of the switch. """ return self._name @property @@ -76,41 +55,14 @@ class RPiGPIOSwitch(ToggleEntity): """ True if device is on. """ return self._state - def turn_on(self, **kwargs): + def turn_on(self): """ Turn the device on. """ - if self._switch(self._active_state): - self._state = True + rpi_gpio.write_output(self._port, 0 if self._invert_logic else 1) + self._state = True self.update_ha_state() - def turn_off(self, **kwargs): + def turn_off(self): """ Turn the device off. """ - if self._switch(not self._active_state): - self._state = False + rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0) + self._state = False self.update_ha_state() - - def _switch(self, new_state): - """ Change the output value to Raspberry Pi GPIO port. """ - _LOGGER.info('Setting GPIO %s to %s', self._gpio, new_state) - # pylint: disable=bare-except - try: - # pylint: disable=no-member - GPIO.output(self._gpio, 1 if new_state else 0) - except: - _LOGGER.error('GPIO "%s" output failed', self._gpio) - return False - return True - - # pylint: disable=no-self-use - @property - def device_state_attributes(self): - """ Returns device specific state attributes. """ - return None - - @property - def state_attributes(self): - """ Returns optional state attributes. """ - data = {} - device_attr = self.device_state_attributes - if device_attr is not None: - data.update(device_attr) - return data diff --git a/requirements_all.txt b/requirements_all.txt index 4d38ada0daf..268ead4d814 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -122,6 +122,9 @@ dnspython3==1.12.0 # homeassistant.components.rfxtrx https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip#RFXtrx==0.2 +# homeassistant.components.rpi_gpio +# RPi.GPIO==0.6.1 + # homeassistant.components.sensor.bitcoin blockchain==1.1.2 @@ -146,10 +149,6 @@ https://github.com/HydrelioxGitHub/netatmo-api-python/archive/43ff238a0122b0939a # homeassistant.components.sensor.openweathermap pyowm==2.3.0 -# homeassistant.components.sensor.rpi_gpio -# homeassistant.components.switch.rpi_gpio -# RPi.GPIO==0.5.11 - # homeassistant.components.sensor.sabnzbd https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1