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

Add Remote RPi Component #23518

Merged
merged 15 commits into from May 26, 2019
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -481,6 +481,7 @@ omit =
homeassistant/components/reddit/*
homeassistant/components/rejseplanen/sensor.py
homeassistant/components/remember_the_milk/__init__.py
homeassistant/components/remote_rpi_gpio/*
homeassistant/components/rest/binary_sensor.py
homeassistant/components/rest/notify.py
homeassistant/components/rest/switch.py
Expand Down
63 changes: 63 additions & 0 deletions homeassistant/components/remote_rpi_gpio/__init__.py
@@ -0,0 +1,63 @@
"""Support for controlling GPIO pins of a Raspberry Pi."""
import logging

_LOGGER = logging.getLogger(__name__)

CONF_BOUNCETIME = 'bouncetime'
CONF_INVERT_LOGIC = 'invert_logic'
CONF_PULL_MODE = 'pull_mode'

DEFAULT_BOUNCETIME = 50
DEFAULT_INVERT_LOGIC = False
DEFAULT_PULL_MODE = "UP"

DOMAIN = 'remote_rpi_gpio'


def setup(hass, config):
"""Set up the Raspberry Pi Remote GPIO component."""
return True


def setup_output(address, port, invert_logic):
"""Set up a GPIO as output."""
from gpiozero import LED
from gpiozero.pins.pigpio import PiGPIOFactory

try:
return LED(port, active_high=invert_logic,
pin_factory=PiGPIOFactory(address))
except (ValueError, IndexError, KeyError):
return None


def setup_input(address, port, pull_mode, bouncetime):
"""Set up a GPIO as input."""
from gpiozero import Button
from gpiozero.pins.pigpio import PiGPIOFactory

if pull_mode == "UP":
pull_gpio_up = True
elif pull_mode == "DOWN":
pull_gpio_up = False

try:
return Button(port,
pull_up=pull_gpio_up,
bounce_time=bouncetime,
pin_factory=PiGPIOFactory(address))
except (ValueError, IndexError, KeyError, IOError):
return None


def write_output(switch, value):
"""Write a value to a GPIO."""
if value == 1:
switch.on()
if value == 0:
switch.off()


def read_input(button):
"""Read a value from a GPIO."""
return button.is_pressed
106 changes: 106 additions & 0 deletions homeassistant/components/remote_rpi_gpio/binary_sensor.py
@@ -0,0 +1,106 @@
"""Support for binary sensor using RPi GPIO."""
import logging

import voluptuous as vol

import requests

from homeassistant.const import CONF_HOST
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)

import homeassistant.helpers.config_validation as cv

from . import (CONF_BOUNCETIME, CONF_PULL_MODE, CONF_INVERT_LOGIC,
DEFAULT_BOUNCETIME, DEFAULT_INVERT_LOGIC, DEFAULT_PULL_MODE)
from .. import remote_rpi_gpio

_LOGGER = logging.getLogger(__name__)

CONF_PORTS = 'ports'

_SENSORS_SCHEMA = vol.Schema({
cv.positive_int: cv.string,
})

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORTS): _SENSORS_SCHEMA,
vol.Optional(CONF_INVERT_LOGIC,
default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_BOUNCETIME,
default=DEFAULT_BOUNCETIME): cv.positive_int,
vol.Optional(CONF_PULL_MODE,
default=DEFAULT_PULL_MODE): cv.string,
})


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Raspberry PI GPIO devices."""
address = config['host']
invert_logic = config[CONF_INVERT_LOGIC]
pull_mode = config[CONF_PULL_MODE]
ports = config['ports']
bouncetime = config[CONF_BOUNCETIME]/1000

devices = []
for port_num, port_name in ports.items():
try:
button = remote_rpi_gpio.setup_input(address,
port_num,
pull_mode,
bouncetime)
except (ValueError, IndexError, KeyError, IOError):
return
new_sensor = RemoteRPiGPIOBinarySensor(port_name, button, invert_logic)
devices.append(new_sensor)

add_entities(devices, True)


class RemoteRPiGPIOBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that uses a Remote Raspberry Pi GPIO."""

def __init__(self, name, button, invert_logic):
"""Initialize the RPi binary sensor."""
self._name = name
self._invert_logic = invert_logic
self._state = False
self._button = button

async def async_added_to_hass(self):
"""Run when entity about to be added to hass."""
def read_gpio():
"""Read state from GPIO."""
self._state = remote_rpi_gpio.read_input(self._button)
self.schedule_update_ha_state()

self._button.when_released = read_gpio
self._button.when_pressed = read_gpio

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def name(self):
"""Return the name of the sensor."""
return self._name

@property
def is_on(self):
"""Return the state of the entity."""
return self._state != self._invert_logic

@property
def device_class(self):
Copy link
Member

Choose a reason for hiding this comment

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

This should just be removed.

"""Return the class of this sensor, from DEVICE_CLASSES."""
return

def update(self):
"""Update the GPIO state."""
try:
self._state = remote_rpi_gpio.read_input(self._button)
except requests.exceptions.ConnectionError:
return
10 changes: 10 additions & 0 deletions homeassistant/components/remote_rpi_gpio/manifest.json
@@ -0,0 +1,10 @@
{
"domain": "remote_rpi_gpio",
"name": "remote_rpi_gpio",
"documentation": "https://www.home-assistant.io/components/remote_rpi_gpio",
"requirements": [
"gpiozero==1.4.1"
],
"dependencies": [],
"codeowners": []
}
91 changes: 91 additions & 0 deletions homeassistant/components/remote_rpi_gpio/switch.py
@@ -0,0 +1,91 @@
"""Allows to configure a switch using RPi GPIO."""
import logging

import voluptuous as vol

from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA
from homeassistant.const import DEVICE_DEFAULT_NAME, CONF_HOST

import homeassistant.helpers.config_validation as cv

from . import CONF_INVERT_LOGIC, DEFAULT_INVERT_LOGIC
from .. import remote_rpi_gpio

_LOGGER = logging.getLogger(__name__)

CONF_PORTS = 'ports'

_SENSORS_SCHEMA = vol.Schema({
cv.positive_int: cv.string,
})

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORTS): _SENSORS_SCHEMA,
vol.Optional(CONF_INVERT_LOGIC,
default=DEFAULT_INVERT_LOGIC): cv.boolean
})


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Remote Raspberry PI GPIO devices."""
address = config[CONF_HOST]
invert_logic = config[CONF_INVERT_LOGIC]
ports = config[CONF_PORTS]

devices = []
for port, name in ports.items():
try:
led = remote_rpi_gpio.setup_output(
address, port, invert_logic)
except (ValueError, IndexError, KeyError, IOError):
return
new_switch = RemoteRPiGPIOSwitch(name, led, invert_logic)
devices.append(new_switch)

add_entities(devices)


class RemoteRPiGPIOSwitch(SwitchDevice):
"""Representation of a Remtoe Raspberry Pi GPIO."""

def __init__(self, name, led, invert_logic):
"""Initialize the pin."""
self._name = name or DEVICE_DEFAULT_NAME
self._state = False
self._invert_logic = invert_logic
self._switch = led

@property
def name(self):
"""Return the name of the switch."""
return self._name

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def assumed_state(self):
"""If unable to access real state of the entity."""
return True

@property
def is_on(self):
"""Return true if device is on."""
return self._state

def turn_on(self, **kwargs):
"""Turn the device on."""
remote_rpi_gpio.write_output(self._switch,
0 if self._invert_logic else 1)
self._state = True
self.schedule_update_ha_state()

def turn_off(self, **kwargs):
"""Turn the device off."""
remote_rpi_gpio.write_output(self._switch,
1 if self._invert_logic else 0)
self._state = False
self.schedule_update_ha_state()
3 changes: 3 additions & 0 deletions requirements_all.txt
Expand Up @@ -499,6 +499,9 @@ googledevices==1.0.2
# homeassistant.components.google_travel_time
googlemaps==2.5.1

# homeassistant.components.remote_rpi_gpio
gpiozero==1.4.1

# homeassistant.components.gpsd
gps3==0.33.3

Expand Down