Gpiod rpi5 (#300)

* Integrate the work done by @jdeneef back to this repo (#281)

* initial setup, config_flow only

* initial setup, reading config

* renamed to ha_gpiod

* added gpiod_config

* switches working

* added invert_logic for switch

* initial setup for binarysensor added

* button working in thread

* added some timeouts to avoid resource busy

* button working! inc state

* doc update

* version nr updated in manifest

* fix for wrong state, tested invert_logic for binary_sensor

* updated docs and unique_id

* simplified tables

* found nothread solution, test a bit more first

* working without thread

* removed  thread, simpeler solution

* added cover ..

* added cover to docs

* default fix relay_time and doc cleanups

* doc cleanups

* moved listener to hub, no longer blocking

* cleanups

* fixed comma in manifest file, and added version n manifest

* fix for hass.async_create_task for hass 2024.5

* Update README.md

* add rpi5 info to readme

* added gpiodevice discovery since rpi5 uses different gpiodevice from rpi3 and 4

* readme update

* readme typo, license update to mit

* fix for cover, updates from gpiod instead of calculated, readme updates

* update manifest version

* add stop_cover for cover, readme update gpiod no longer mandatory

* add github actions hacs validation

* limit hacs validation to on push

* remove brands check for github action

* updated readme, 1.0 version

* add hassfest check

* sorted manifest

* follow blueprint a bit more

* update github actions

* feat: add bias and drive mode options to switches

* updated code adding feature from askz

* add bias/drive to cover

* gpiod bias and drive added

* code cleanup more inline with gpiod, with some deprecations in the future

* highlight deprecated

* update todo

* fixed drive info in readme

* readme fix

* update readme

* retrofit

* cover update not init is_closing/opening

* readme update

* remove changelog

* update readme refer to bias for switch

* markdown cleanups, markdown render

* fix for port ranges, back to int

* rename README-DEV

* Update README.md

* alternative solution removing the loop waiting for gpio events

* cleanup of config_validations, eases backward compatibility in configs

* added changelog to readme

* changelog creation from git

* removed changelog, added generation howto to README-DEV, generate from there

* read initial status from gpio status (check bias on errors)

* typo in changelog git line

* added possible bias issue in readme

* Add persistentcy support for switches

* Make the persistent example more relevant

* removed previous persistence via gpiod

* updated sensor reading at startup

* updated readme for persistence over reboots

* fix to remove state error

* Align old configuration for sensor and switch

* Fix manifest

* Align cover configuration with old format

* Fix readme attribution

* Fix break in cover

* Manifest fix

* remove commented code

* fixed version in manifest

* Revert not needed files

* Revert LICENSE

* CR comments

* Another CR fix

* More CR comments

* Fix doc CR comments

* Update custom_components/rpi_gpio/hub.py

* Update custom_components/rpi_gpio/hub.py

* CR comments

* Fix break caused by removing manufacturer.

* Update custom_components/rpi_gpio/hub.py

* Fixed last CR comments

---------

Co-authored-by: J de Neef <6884662+jdeneef@users.noreply.github.com>
Co-authored-by: Maxime Saddok <saddok.ma@gmail.com>
Co-authored-by: gwhiteCL <whitegc5@comcast.net>
Co-authored-by: Shay Levy <levyshay1@gmail.com>

* rename update to handle_event to make it less confusion with HA naming.

* Fix cover break after my rename

* Rename update to handle_event to make it less confusion with HA naming (#287)

* rename update to handle_event to make it less confusion with HA naming.

* Fix cover break after my rename

* Merge from main (#296)

* Bump pylint from 3.3.0 to 3.3.1 (#283)

Bumps [pylint](https://github.com/pylint-dev/pylint) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pre-commit from 3.8.0 to 4.0.0 (#288)

Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.8.0 to 4.0.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.8.0...v4.0.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/checkout from 4.2.0 to 4.2.1 (#289)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.0...v4.2.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump black from 24.8.0 to 24.10.0 (#290)

Bumps [black](https://github.com/psf/black) from 24.8.0 to 24.10.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/24.8.0...24.10.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pre-commit from 4.0.0 to 4.0.1 (#291)

Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump mypy from 1.11.2 to 1.12.0 (#292)

Bumps [mypy](https://github.com/python/mypy) from 1.11.2 to 1.12.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump mypy from 1.12.0 to 1.12.1 (#295)

Bumps [mypy](https://github.com/python/mypy) from 1.12.0 to 1.12.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Integrate the fix for cover (#297)

* fix for cover

* simpler cover fix, not based on cover state

* Fix minor bug in the cover code

* Refactor cover init

---------

Co-authored-by: J de Neef <6884662+jdeneef@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Tomer <57483589+tomer-w@users.noreply.github.com>
Co-authored-by: J de Neef <6884662+jdeneef@users.noreply.github.com>
Co-authored-by: Maxime Saddok <saddok.ma@gmail.com>
Co-authored-by: gwhiteCL <whitegc5@comcast.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Shay Levy 2024-10-24 18:15:58 +03:00 committed by GitHub
parent 24f7d397b1
commit 745a4f09fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 481 additions and 634 deletions

View File

@ -4,6 +4,8 @@
The `rpi_gpio` integration supports the following platforms: `Binary Sensor`, `Cover`, `Switch`
`rpi_gpio` is based on `gpiod` in [ha-gpio](https://codeberg.org/raboof/ha-gpio) and `ha_gpiod` in [ha_gpiod](https://github.com/jdeneef/ha_gpiod)
# Installation
### HACS
@ -16,14 +18,32 @@ Copy the `rpi_gpio` folder and all of its contents into your Home Assistant's `c
# Usage
The `gpiod` platform will be initialized using the path to the gpio chip. When path is not in the config `/dev/gpiochip[0-5]` are tested for a gpiodevice having `pinctrl`, in sequence `[0,4,1,2,3,5]`. So with a raspberry pi you should be OK to leave the path empty.
Raspberry Pi | GPIO Device
--- | ---
RPi3, RPi4 | `/dev/gpiochip0`
RPi5 | `/dev/gpiochip4`
```yaml
# setup gpiod chip; mostly not required
gpiod:
path: '/dev/gpiochip0'
```
### Options
Key | Required | Default | Type | Description
--- | --- | --- | --- | ---
`gpiod` | only for path|- |- | `gpiod` platform config and initialization, only required when you need to specify a specific gpiodevice path (see path)
`path` | no | discovered | string | path to gpio device, if not set auto discovered
## Binary Sensor
The `rpi_gpio` binary sensor platform allows you to read sensor values of the GPIOs of your [Raspberry Pi](https://www.raspberrypi.org/).
### Configuration
[Legacy binary sensor configuration](https://github.com/thecode/ha-rpi_gpio/blob/main/legacy-config.md#binary-sensor)
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
@ -59,10 +79,11 @@ binary_sensor:
| -------------- | -------- | --------------------- | --------|------------------------------------------------------------------------------------------------------------ |
| `sensors` | yes | | list | List of sensor IO ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) |
| `name` | yes | | string | The name for the binary sensor entity |
| `port` | yes | | integer | the GPIO port to be used |
| `unique_id` | no | | string | An ID that uniquely identifies the sensor. Set this to a unique value to allow customization through the UI |
| `bouncetime` | no | `50` | integer | The time in milliseconds for port debouncing |
| `invert_logic` | no | `false` (ACTIVE HIGH) | boolean | If `true`, inverts the output logic to ACTIVE LOW |
| `pull_mode` | no | `UP` | string | Type of internal pull resistor to use: `UP` - pull-up resistor, `DOWN` - pull-down resistor |
| `pull_mode` | no | `UP` | string | control bias setting of GPIO, used to define the electrical state of a GPIO line when not actively driven; `UP` set weak pull-up resistor on the line, ensuring that the line is pulled to a high level (3.3V or 5V) when not actively driven; `DOWN` sets weak pull-down resistor to pull to low level (0V), `DISABLED` remains floating, `AS_IS` not changed |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
@ -101,6 +122,8 @@ cover:
covers:
- relay_pin: 10
state_pin: 11
name: "Left door"
unique_id: "left_door_cover_port_11"
- relay_pin: 12
state_pin: 13
name: "Right door"
@ -131,8 +154,6 @@ The `rpi_gpio` switch platform allows you to control the GPIOs of your [Raspberr
### Configuration
[Legacy switch configuration](https://github.com/thecode/ha-rpi_gpio/blob/main/legacy-config.md#switch)
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
@ -167,9 +188,13 @@ switch:
| -------------- | -------- | ------- | --------| ----------------------------------------------------------------------------------------------------------- |
| `switches` | yes | | list | List of switch IO ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) |
| `name` | yes | | string | The name for the switch entity |
| `unique_id` | no | | string | An ID that uniquely identifies the switch. Set this to a unique value to allow customization through the UI |
| `port` | yes | | integer | the GPIO port to be used |
| `unique_id` | no | | string | An ID that uniquely identifies the switch. Set this to a unique value to allow customization through the UI, auto generated when not set manually in config |
| `invert_logic` | no | `false` | boolean | If true, inverts the output logic to ACTIVE LOW |
| `persistent` | no | `false` | boolean | If true, the switch state will be persistent in HA and will be restored if HA restart / crash |
| `pull_mode` | no | `AS_IS` | string | Type of internal pull resistor to use: `UP` - pull-up resistor, `DOWN` - pull-down resistor, `AS-IS` no change |
| `drive` |no | `PUSH_PULL`|string | control drive configuration of the GPIO, determines how the line behaves when it is set to output mode; `PUSH_PULL`, GPIO line can both source and sink current, can actively drive the line to both high and low states. `OPEN-DRAIN`, GPPIO can only sink current (drive the line to low) and is otherwise left floating, and `OPEN-SOURCE` the reverse.
|`persistent` | no | `false` | boolean | If true, the switch state will be persistent in HA and will be restored if HA restart / crash. |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
@ -184,3 +209,6 @@ switch:
- port: 17
name: "Speaker Relay"
```
# Reporting issues
*Before* reporting issues please enable debug logging as described [here](https://www.home-assistant.io/docs/configuration/troubleshooting/#enabling-debug-logging), check logs and report issue attaching the log file.

View File

@ -1,59 +1,33 @@
"""Support for controlling GPIO pins of a Raspberry Pi."""
"""Support for controlling GPIO pins of a device."""
from RPi import GPIO # pylint: disable=import-error
import logging
_LOGGER = logging.getLogger(__name__)
from homeassistant.const import (
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
DOMAIN = "rpi_gpio"
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.COVER,
Platform.SWITCH,
]
from .const import DOMAIN
from .hub import Hub
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Raspberry PI GPIO component."""
from homeassistant.const import CONF_PATH
def cleanup_gpio(event):
"""Stuff to do before stopping."""
GPIO.cleanup()
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema({
vol.Optional(CONF_PATH): vol.All(cv.string, vol.PathExists())
})
},
extra=vol.ALLOW_EXTRA
)
def prepare_gpio(event):
"""Stuff to do when Home Assistant starts."""
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the GPIO component."""
path = config.get(DOMAIN, {}).get(CONF_PATH)
hub = Hub(hass, path)
hass.data[DOMAIN] = hub
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio)
GPIO.setmode(GPIO.BCM)
return True
def setup_output(port):
"""Set up a GPIO as output."""
GPIO.setup(port, GPIO.OUT)
def setup_input(port, pull_mode):
"""Set up 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):
"""Add detection for RISING and FALLING events."""
GPIO.add_event_detect(port, GPIO.BOTH, callback=event_callback, bouncetime=bounce)

View File

@ -1,141 +1,89 @@
"""Support for binary sensor using RPi GPIO."""
from __future__ import annotations
import asyncio
from . import DOMAIN
import voluptuous as vol
import logging
_LOGGER = logging.getLogger(__name__)
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
from homeassistant.const import (
CONF_NAME,
CONF_PORT,
CONF_SENSORS,
CONF_UNIQUE_ID,
DEVICE_DEFAULT_NAME,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.reload import setup_reload_service
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN, PLATFORMS, edge_detect, read_input, setup_input
CONF_BOUNCETIME = "bouncetime"
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.const import CONF_SENSORS, CONF_NAME, CONF_PORT, CONF_UNIQUE_ID
from .hub import BIAS
CONF_INVERT_LOGIC = "invert_logic"
CONF_PORTS = "ports"
CONF_PULL_MODE = "pull_mode"
DEFAULT_BOUNCETIME = 50
DEFAULT_INVERT_LOGIC = False
CONF_BOUNCETIME = "bouncetime"
DEFAULT_BOUNCETIME = 50
CONF_PULL_MODE = "pull_mode"
DEFAULT_PULL_MODE = "UP"
_SENSORS_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string})
_SENSOR_SCHEMA = vol.Schema(
{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_PORT): cv.positive_int,
vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string,
vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend(
{
vol.Exclusive(CONF_PORTS, CONF_SENSORS): _SENSORS_LEGACY_SCHEMA,
vol.Exclusive(CONF_SENSORS, CONF_SENSORS): vol.All(
cv.ensure_list, [_SENSOR_SCHEMA]
),
vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string,
},
),
cv.has_at_least_one_key(CONF_PORTS, CONF_SENSORS),
PLATFORM_SCHEMA.extend({
vol.Exclusive(CONF_SENSORS, CONF_SENSORS): vol.All(
cv.ensure_list, [{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_PORT): cv.positive_int,
vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string,
vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}]
)
})
)
def setup_platform(
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Raspberry PI GPIO devices."""
setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None) -> None:
_LOGGER.debug(f"setup_platform: {config}")
hub = hass.data[DOMAIN]
if not hub._online:
_LOGGER.error("hub not online, bailing out")
sensors = []
sensors_conf = config.get(CONF_SENSORS)
if sensors_conf is not None:
for sensor in sensors_conf:
sensors.append(
RPiGPIOBinarySensor(
sensor[CONF_NAME],
sensor[CONF_PORT],
sensor[CONF_PULL_MODE],
sensor[CONF_BOUNCETIME],
sensor[CONF_INVERT_LOGIC],
sensor.get(CONF_UNIQUE_ID),
)
)
add_entities(sensors, True)
return
pull_mode = config[CONF_PULL_MODE]
bouncetime = config[CONF_BOUNCETIME]
invert_logic = config[CONF_INVERT_LOGIC]
ports = config[CONF_PORTS]
for port_num, port_name in ports.items():
for sensor in config.get(CONF_SENSORS):
sensors.append(
RPiGPIOBinarySensor(
port_name, port_num, pull_mode, bouncetime, invert_logic
GPIODBinarySensor(
hub,
sensor[CONF_NAME],
sensor[CONF_PORT],
sensor.get(CONF_UNIQUE_ID) or f"{DOMAIN}_{sensor[CONF_PORT]}_{sensor[CONF_NAME].lower().replace(' ', '_')}",
sensor.get(CONF_INVERT_LOGIC),
sensor.get(CONF_PULL_MODE),
sensor.get(CONF_BOUNCETIME)
)
)
add_entities(sensors, True)
async_add_entities(sensors)
class RPiGPIOBinarySensor(BinarySensorEntity):
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
class GPIODBinarySensor(BinarySensorEntity):
_attr_should_poll = False
async def async_read_gpio(self):
"""Read state from GPIO."""
await asyncio.sleep(float(self._bouncetime) / 1000)
self._state = await self.hass.async_add_executor_job(read_input, self._port)
def __init__(self, hub, name, port, unique_id, active_low, bias, debounce):
_LOGGER.debug(f"GPIODBinarySensor init: {port} - {name} - {unique_id}")
self._hub = hub
self._attr_name = name
self._attr_unique_id = unique_id
self._port = port
self._active_low = active_low
self._bias = bias
self._debounce = debounce
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()
self._hub.add_sensor(self, self._port, self._active_low, self._bias, self._debounce)
self.async_write_ha_state()
def __init__(self, name, port, pull_mode, bouncetime, invert_logic, unique_id=None):
"""Initialize the RPi binary sensor."""
self._attr_name = name or DEVICE_DEFAULT_NAME
self._attr_unique_id = unique_id
self._attr_should_poll = False
self._port = port
self._pull_mode = pull_mode
self._bouncetime = bouncetime
self._invert_logic = invert_logic
self._state = None
setup_input(self._port, self._pull_mode)
def edge_detected(port):
"""Edge detection handler."""
if self.hass is not None:
self.hass.add_job(self.async_read_gpio)
edge_detect(self._port, edge_detected, self._bouncetime)
@property
def is_on(self):
"""Return the state of the entity."""
return self._state != self._invert_logic
def update(self):
"""Update the GPIO state."""
self._state = read_input(self._port)
def handle_event(self):
self._attr_is_on = self._hub.update(self._port)
self.schedule_update_ha_state(False)

View File

@ -0,0 +1,4 @@
DOMAIN = "rpi_gpio"

View File

@ -1,19 +1,22 @@
"""Support for controlling a Raspberry Pi cover."""
from __future__ import annotations
from functools import cached_property
from . import DOMAIN
from time import sleep
import voluptuous as vol
import logging
_LOGGER = logging.getLogger(__name__)
from homeassistant.components.cover import PLATFORM_SCHEMA, CoverEntity
from homeassistant.const import CONF_COVERS, CONF_NAME, CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.reload import setup_reload_service
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN, PLATFORMS, read_input, setup_input, setup_output, write_output
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.components.cover import CoverEntity
from homeassistant.const import CONF_COVERS, CONF_NAME, CONF_UNIQUE_ID
from .hub import BIAS, DRIVE
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
CONF_RELAY_PIN = "relay_pin"
CONF_RELAY_TIME = "relay_time"
@ -21,11 +24,11 @@ CONF_STATE_PIN = "state_pin"
CONF_STATE_PULL_MODE = "state_pull_mode"
CONF_INVERT_STATE = "invert_state"
CONF_INVERT_RELAY = "invert_relay"
DEFAULT_RELAY_TIME = 0.2
DEFAULT_STATE_PULL_MODE = "UP"
DEFAULT_INVERT_STATE = False
DEFAULT_INVERT_RELAY = False
_COVERS_SCHEMA = vol.All(
cv.ensure_list,
[
@ -50,88 +53,103 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
}
)
def setup_platform(
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the RPi cover platform."""
setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None) -> None:
_LOGGER.debug(f"setup_platform: {config}")
hub = hass.data[DOMAIN]
if not hub._online:
_LOGGER.error("hub not online, bailing out")
relay_time = config[CONF_RELAY_TIME]
state_pull_mode = config[CONF_STATE_PULL_MODE]
invert_state = config[CONF_INVERT_STATE]
invert_relay = config[CONF_INVERT_RELAY]
covers = []
covers_conf = config[CONF_COVERS]
for cover in covers_conf:
for cover in config.get(CONF_COVERS):
covers.append(
RPiGPIOCover(
GPIODCover(
hub,
cover[CONF_NAME],
cover[CONF_RELAY_PIN],
cover[CONF_STATE_PIN],
state_pull_mode,
cover.get(CONF_RELAY_PIN),
relay_time,
invert_state,
invert_relay,
cover.get(CONF_UNIQUE_ID),
"AS_IS",
"PUSH_PULL",
cover.get(CONF_STATE_PIN),
state_pull_mode,
invert_state,
cover.get(CONF_UNIQUE_ID) or f"{DOMAIN}_{cover.get(CONF_RELAY_PORT) or cover.get("relay_pin")}_{cover[CONF_NAME].lower().replace(' ', '_')}",
)
)
add_entities(covers)
async_add_entities(covers)
class RPiGPIOCover(CoverEntity):
"""Representation of a Raspberry GPIO cover."""
class GPIODCover(CoverEntity):
_attr_should_poll = False
def __init__(
self,
name,
relay_pin,
state_pin,
state_pull_mode,
relay_time,
invert_state,
invert_relay,
unique_id,
):
"""Initialize the cover."""
def __init__(self, hub, name, relay_port, relay_time, relay_active_low, relay_bias, relay_drive,
state_port, state_bias, state_active_low, unique_id):
_LOGGER.debug(f"GPIODCover init: {relay_port}:{state_port} - {name} - {unique_id} - {relay_time}")
self._hub = hub
self._attr_name = name
self._attr_unique_id = unique_id
self._state = False
self._relay_pin = relay_pin
self._state_pin = state_pin
self._state_pull_mode = state_pull_mode
self._relay_port = relay_port
self._relay_time = relay_time
self._invert_state = invert_state
self._invert_relay = invert_relay
setup_output(self._relay_pin)
setup_input(self._state_pin, self._state_pull_mode)
write_output(self._relay_pin, 0 if self._invert_relay else 1)
self._relay_active_low = relay_active_low
self._relay_bias = relay_bias
self._relay_drive = relay_drive
self._state_port = state_port
self._state_bias = state_bias
self._state_active_low = state_active_low
self._attr_is_closed = False != state_active_low
def update(self):
"""Update the state of the cover."""
self._state = read_input(self._state_pin)
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()
self._hub.add_cover(self, self._relay_port, self._relay_active_low, self._relay_bias,
self._relay_drive, self._state_port, self._state_bias, self._state_active_low)
self.async_write_ha_state()
@property
def is_closed(self):
"""Return true if cover is closed."""
return self._state != self._invert_state
def _trigger(self):
"""Trigger the cover."""
write_output(self._relay_pin, 1 if self._invert_relay else 0)
sleep(self._relay_time)
write_output(self._relay_pin, 0 if self._invert_relay else 1)
def handle_event(self):
self._attr_is_closed = self._hub.update(self._state_port)
self.schedule_update_ha_state(False)
def close_cover(self, **kwargs):
"""Close the cover."""
if not self.is_closed:
self._trigger()
if self.is_closed:
return
self._hub.turn_on(self._relay_port)
self._attr_is_closing = True
self.schedule_update_ha_state(False)
sleep(self._relay_time)
if not self.is_closing:
# closing stopped
return
self._hub.turn_off(self._relay_port)
self._attr_is_closing = False
self.handle_event()
def open_cover(self, **kwargs):
"""Open the cover."""
if self.is_closed:
self._trigger()
if not self.is_closed:
return
self._hub.turn_on(self._relay_port)
self._attr_is_opening = True
self.schedule_update_ha_state(False)
sleep(self._relay_time)
if not self.is_opening:
# opening stopped
return
self._hub.turn_off(self._relay_port)
self._attr_is_opening = False
self.handle_event()
def stop_cover(self, **kwargs):
if not (self.is_closing or self.is_opening):
return
self._hub.turn_off(self._relay_port)
self._attr_is_opening = False
self._attr_is_closing = False
self.schedule_update_ha_state(False)

View File

@ -0,0 +1,187 @@
from __future__ import annotations
from . import DOMAIN
import logging
_LOGGER = logging.getLogger(__name__)
from homeassistant.core import HomeAssistant
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START
from homeassistant.exceptions import HomeAssistantError
from typing import Dict
from datetime import timedelta
import gpiod
from gpiod.line import Direction, Value, Bias, Drive, Edge, Clock
EventType = gpiod.EdgeEvent.Type
BIAS = {
"UP": Bias.PULL_UP,
"DOWN": Bias.PULL_DOWN,
"DISABLED": Bias.DISABLED,
"AS_IS": Bias.AS_IS,
}
DRIVE = {
"OPEN_DRAIN": Drive.OPEN_DRAIN,
"OPEN_SOURCE": Drive.OPEN_SOURCE,
"PUSH_PULL": Drive.PUSH_PULL,
}
class Hub:
def __init__(self, hass: HomeAssistant, path: str) -> None:
"""GPIOD Hub"""
self._path = path
self._chip : gpiod.Chip
self._name = path
self._id = path
self._hass = hass
self._online = False
self._lines : gpiod.LineRequest = None
self._config : Dict[int, gpiod.LineSettings] = {}
self._edge_events = False
self._entities = {}
if path:
# use config
if self.verify_gpiochip(path):
self._online = True
self._path = path
else:
# discover
for d in [0,4,1,2,3,5]:
# rpi3,4 using 0. rpi5 using 4
path = f"/dev/gpiochip{d}"
if self.verify_gpiochip(path):
self._online = True
self._path = path
break
if not self._online:
_LOGGER.error("No gpio device detected, bailing out")
raise HomeAssistantError("No gpio device detected")
_LOGGER.debug(f"using gpio_device: {self._path}")
# startup and shutdown triggers of hass
self._hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, self.startup)
self._hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.cleanup)
def verify_gpiochip(self, path):
if not gpiod.is_gpiochip_device(path):
_LOGGER.debug(f"verify_gpiochip: {path} not a gpiochip_device")
return False
_LOGGER.debug(f"verify_gpiochip: {path} is a gpiochip_device")
self._chip = gpiod.Chip(path)
info = self._chip.get_info()
if not "pinctrl" in info.label:
_LOGGER.debug(f"verify_gpiochip: {path} no pinctrl {info.label}")
return False
_LOGGER.debug(f"verify_gpiochip gpiodevice: {path} has pinctrl")
return True
async def startup(self, _):
"""Stuff to do after starting."""
_LOGGER.debug(f"startup {DOMAIN} hub")
if not self._online:
return
# setup lines
self.update_lines()
if not self._edge_events:
return
_LOGGER.debug("Start listener")
self._hass.loop.add_reader(self._lines.fd, self.handle_events)
def cleanup(self, _):
"""Stuff to do before stopping."""
_LOGGER.debug(f"cleanup {DOMAIN} hub")
if self._config:
self._config.clear()
if self._lines:
self._lines.release()
if self._chip:
self._chip.close()
self._online = False
@property
def hub_id(self) -> str:
"""ID for hub"""
return self._id
def update_lines(self) -> None:
if not self._online:
_LOGGER.debug(f"gpiod hub not online {self._path}")
if not self._config:
_LOGGER.debug(f"gpiod config is empty")
if self._lines:
self._lines.release()
_LOGGER.debug(f"updating lines: {self._config}")
self._lines = gpiod.request_lines(
self._path,
consumer = "rpi_gpio",
config = self._config
)
def handle_events(self):
for event in self._lines.read_edge_events():
_LOGGER.debug(f"Event: {event}")
self._entities[event.line_offset].handle_event()
def add_switch(self, entity, port, active_low, bias, drive_mode, init_output_value = True) -> None:
_LOGGER.debug(f"in add_switch {port}")
self._entities[port] = entity
self._config[port] = gpiod.LineSettings(
direction = Direction.OUTPUT,
bias = BIAS[bias],
drive = DRIVE[drive_mode],
active_low = active_low,
output_value = Value.ACTIVE if init_output_value and entity.is_on else Value.INACTIVE
)
def turn_on(self, port) -> None:
_LOGGER.debug(f"in turn_on {port}")
self._lines.set_value(port, Value.ACTIVE)
def turn_off(self, port) -> None:
_LOGGER.debug(f"in turn_off {port}")
self._lines.set_value(port, Value.INACTIVE)
def add_sensor(self, entity, port, active_low, bias, debounce) -> None:
_LOGGER.debug(f"in add_sensor {port}")
# read current status of the sensor
line = self._chip.request_lines({ port: {} })
value = True if line.get_value(port) == Value.ACTIVE else False
entity.is_on = True if value ^ active_low else False
line.release()
_LOGGER.debug(f"current value for port {port}: {entity.is_on}")
self._entities[port] = entity
self._config[port] = gpiod.LineSettings(
direction = Direction.INPUT,
edge_detection = Edge.BOTH,
bias = BIAS[bias],
active_low = active_low,
debounce_period = timedelta(milliseconds=debounce),
event_clock = Clock.REALTIME,
output_value = Value.ACTIVE if entity.is_on else Value.INACTIVE,
)
self._edge_events = True
def update(self, port, **kwargs):
return self._lines.get_value(port) == Value.ACTIVE
def add_cover(self, entity, relay_port, relay_active_low, relay_bias, relay_drive,
state_port, state_bias, state_active_low) -> None:
_LOGGER.debug(f"in add_cover {relay_port} {state_port}")
self.add_switch(entity, relay_port, relay_active_low, relay_bias, relay_drive, init_output_value = False)
self.add_sensor(entity, state_port, state_active_low, state_bias, 50)
self.update_lines()

View File

@ -1,10 +1,11 @@
{
"domain": "rpi_gpio",
"name": "Raspberry Pi GPIO",
"codeowners": ["@thecode"],
"codeowners": [ "@thecode" ],
"documentation": "https://github.com/thecode/ha-rpi_gpio",
"integration_type": "hub",
"iot_class": "local_push",
"issue_tracker": "https://github.com/thecode/ha-rpi_gpio/issues",
"requirements": ["RPi.GPIO==0.7.1"],
"version": "2022.7.0"
"requirements": [ "gpiod>=2.0.2" ],
"version": "2024.9.0"
}

View File

@ -1,160 +1,113 @@
"""Allows to configure a switch using RPi GPIO."""
from __future__ import annotations
from typing import Any
import voluptuous as vol
from . import DOMAIN
import logging
_LOGGER = logging.getLogger(__name__)
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
from homeassistant.const import (
CONF_NAME,
CONF_PORT,
CONF_SWITCHES,
CONF_UNIQUE_ID,
DEVICE_DEFAULT_NAME,
STATE_ON,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.reload import setup_reload_service
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.components.switch import SwitchEntity
from homeassistant.const import CONF_SWITCHES, CONF_NAME, CONF_PORT, CONF_UNIQUE_ID, STATE_ON
from homeassistant.helpers.restore_state import RestoreEntity
from . import DOMAIN, PLATFORMS, setup_output, write_output
CONF_PULL_MODE = "pull_mode"
CONF_PORTS = "ports"
from .hub import BIAS, DRIVE
CONF_INVERT_LOGIC = "invert_logic"
CONF_PERSISTENT = "persistent"
DEFAULT_INVERT_LOGIC = False
CONF_PULL_MODE="pull_mode"
DEFAULT_PULL_MODE = "AS_IS"
CONF_DRIVE ="drive"
DEFAULT_DRIVE = "PUSH_PULL"
CONF_PERSISTENT = "persistent"
DEFAULT_PERSISTENT = False
_SWITCHES_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string})
_SWITCH_SCHEMA = vol.Schema(
{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_PORT): cv.positive_int,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_PERSISTENT, default=DEFAULT_PERSISTENT): cv.boolean,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend(
{
vol.Exclusive(CONF_PORTS, CONF_SWITCHES): _SWITCHES_LEGACY_SCHEMA,
vol.Exclusive(CONF_SWITCHES, CONF_SWITCHES): vol.All(
cv.ensure_list, [_SWITCH_SCHEMA]
),
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_PERSISTENT, default=DEFAULT_PERSISTENT): cv.boolean,
},
),
cv.has_at_least_one_key(CONF_PORTS, CONF_SWITCHES),
PLATFORM_SCHEMA.extend({
vol.Exclusive(CONF_SWITCHES, CONF_SWITCHES): vol.All(
cv.ensure_list, [{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_PORT): cv.positive_int,
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): vol.In(BIAS.keys()),
vol.Optional(CONF_DRIVE, default=DEFAULT_DRIVE): vol.In(DRIVE.keys()),
vol.Optional(CONF_PERSISTENT, default=DEFAULT_PERSISTENT): cv.boolean,
}]
)
})
)
def setup_platform(
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Raspberry PI GPIO devices."""
setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None) -> None:
_LOGGER.debug(f"setup_platform: {config}")
hub = hass.data[DOMAIN]
if not hub._online:
_LOGGER.error("hub not online, bailing out")
switches = []
for switch in config.get(CONF_SWITCHES):
switches.append(
GPIODSwitch(
hub,
switch[CONF_NAME],
switch[CONF_PORT],
switch.get(CONF_UNIQUE_ID) or f"{DOMAIN}_{switch[CONF_PORT]}_{switch[CONF_NAME].lower().replace(' ', '_')}",
switch.get(CONF_INVERT_LOGIC),
switch.get(CONF_PULL_MODE),
switch.get(CONF_DRIVE),
switch[CONF_PERSISTENT]
)
)
switches_conf = config.get(CONF_SWITCHES)
if switches_conf is not None:
for switch in switches_conf:
if switch[CONF_PERSISTENT]:
switches.append(
PersistentRPiGPIOSwitch(
switch[CONF_NAME],
switch[CONF_PORT],
switch[CONF_INVERT_LOGIC],
switch.get(CONF_UNIQUE_ID),
)
)
else:
switches.append(
RPiGPIOSwitch(
switch[CONF_NAME],
switch[CONF_PORT],
switch[CONF_INVERT_LOGIC],
switch.get(CONF_UNIQUE_ID),
)
)
add_entities(switches, True)
return
invert_logic = config[CONF_INVERT_LOGIC]
persistent = config[CONF_PERSISTENT]
ports = config[CONF_PORTS]
for port, name in ports.items():
if persistent:
switches.append(PersistentRPiGPIOSwitch(name, port, invert_logic))
else:
switches.append(RPiGPIOSwitch(name, port, invert_logic))
add_entities(switches)
async_add_entities(switches)
class RPiGPIOSwitch(SwitchEntity):
"""Representation of a Raspberry Pi GPIO."""
class GPIODSwitch(SwitchEntity, RestoreEntity):
_attr_should_poll = False
def __init__(self, name, port, invert_logic, unique_id=None, skip_reset=False):
"""Initialize the pin."""
self._attr_name = name or DEVICE_DEFAULT_NAME
def __init__(self, hub, name, port, unique_id, active_low, bias, drive, persistent):
_LOGGER.debug(f"GPIODSwitch init: {port} - {name} - {unique_id} - active_low: {active_low} - bias: {bias} - drive: {drive}")
self._hub = hub
self._attr_name = name
self._attr_unique_id = unique_id
self._attr_should_poll = False
self._port = port
self._invert_logic = invert_logic
self._state = False
setup_output(self._port)
if not skip_reset:
write_output(self._port, 1 if self._invert_logic else 0)
@property
def is_on(self) -> bool | None:
"""Return true if device is on."""
return self._state
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
write_output(self._port, 0 if self._invert_logic else 1)
self._state = True
self.async_write_ha_state()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
write_output(self._port, 1 if self._invert_logic else 0)
self._state = False
self.async_write_ha_state()
class PersistentRPiGPIOSwitch(RPiGPIOSwitch, RestoreEntity):
"""Representation of a persistent Raspberry Pi GPIO."""
def __init__(self, name, port, invert_logic, unique_id=None):
"""Initialize the pin."""
super().__init__(name, port, invert_logic, unique_id, True)
self._active_low = active_low
self._bias = bias
self._drive_mode = drive
self._persistent = persistent
async def async_added_to_hass(self) -> None:
"""Call when the switch is added to hass."""
await super().async_added_to_hass()
state = await self.async_get_last_state()
if not state:
return
self._state = True if state.state == STATE_ON else False
if self._state:
await self.async_turn_on()
else:
await self.async_turn_off()
if not state or not self._persistent:
self._attr_is_on = False
else:
_LOGGER.debug(f"GPIODSwitch async_added_to_has initial port: {self._port} persistent: {self._persistent} state: {state.state}")
self._attr_is_on = True if state.state == STATE_ON else False
self._hub.add_switch(self, self._port, self._active_low, self._bias, self._drive_mode)
self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None:
self._hub.turn_on(self._port)
self._attr_is_on = True
self.async_write_ha_state()
async def async_turn_off(self, **kwargs: Any) -> None:
self._hub.turn_off(self._port)
self._attr_is_on = False
self.async_write_ha_state()
def handle_event(self):
self._attr_is_on = self._hub.update(self._port)
self.schedule_update_ha_state(False)

View File

@ -1,4 +1,4 @@
{
"name": "Raspberry Pi GPIO",
"homeassistant": "2022.7.0"
}
}

174
info.md
View File

@ -1,174 +0,0 @@
# Home Assistant Raspberry Pi GPIO custom integration
**This is a spin-off from the original Home Assistant integration, which was removed in Home Assistant Core version 2022.6.**
The `rpi_gpio` integration supports the following platforms: `Binary Sensor`, `Cover`, `Switch`
## Binary Sensor
The `rpi_gpio` binary sensor platform allows you to read sensor values of the GPIOs of your [Raspberry Pi](https://www.raspberrypi.org/).
### Configuration
[Legacy binary sensor configuration](https://github.com/thecode/ha-rpi_gpio/blob/main/legacy-config.md#binary-sensor)
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
# Basic configuration.yaml entry
binary_sensor:
- platform: rpi_gpio
sensors:
- port: 11
name: "PIR Office"
- port: 12
name: "PIR Bedroom"
```
```yaml
# Full configuration.yaml entry
binary_sensor:
- platform: rpi_gpio
sensors:
- port: 11
name: "PIR Office"
unique_id: "pir_office_sensor_port_11"
bouncetime: 80
invert_logic: true
pull_mode: "DOWN"
- port: 12
name: "PIR Bedroom"
unique_id: "pir_bedroom_sensor_port_12"
```
### Options
| Key | Required | Default | Type | Description |
| -------------- | -------- | --------------------- | --------|------------------------------------------------------------------------------------------------------------ |
| `sensors` | yes | | list | List of sensor IO ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) |
| `name` | yes | | string | The name for the binary sensor entity |
| `unique_id` | no | | string | An ID that uniquely identifies the sensor. Set this to a unique value to allow customization through the UI |
| `bouncetime` | no | `50` | integer | The time in milliseconds for port debouncing |
| `invert_logic` | no | `false` (ACTIVE HIGH) | boolean | If `true`, inverts the output logic to ACTIVE LOW |
| `pull_mode` | no | `UP` | string | Type of internal pull resistor to use: `UP` - pull-up resistor, `DOWN` - pull-down resistor |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
## Cover
The `rpi_gpio` cover platform allows you to use a Raspberry Pi to control your cover such as Garage doors.
It uses two pins on the Raspberry Pi.
- The `state_pin` will detect if the cover is closed, and
- the `relay_pin` will trigger the cover to open or close.
Although you do not need Andrews Hilliday's software controller when you run Home Assistant, he has written clear instructions on how to hook your garage door and sensors up to your Raspberry Pi, which can be found [here](https://github.com/andrewshilliday/garage-door-controller#hardware-setup).
### Configuration
To enable Raspberry Pi Covers in your installation, add the following to your `configuration.yaml` file:
```yaml
# Basic configuration.yaml entry
cover:
- platform: rpi_gpio
covers:
- relay_pin: 10
state_pin: 11
```
```yaml
# Full configuration.yaml entry
cover:
- platform: rpi_gpio
relay_time: 0.2
invert_relay: false
state_pull_mode: "UP"
invert_state: true
covers:
- relay_pin: 10
state_pin: 11
- relay_pin: 12
state_pin: 13
name: "Right door"
unique_id: "right_door_cover_port_13"
```
### Options
| Key | Required | Default | Type | Description |
| ----------------- | -------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `relay_time` | no | `0.2` | float | The time that the relay will be on for in seconds |
| `invert_relay` | no | `false` | boolean | Invert the relay pin output so that it is active-high (True) |
| `state_pull_mode` | no | `UP` | string | The direction the State pin is pulling. It can be `UP` or `DOWN` |
| `invert_state` | no | `false` | boolean | Invert the value of the State pin so that 0 means closed |
| `covers` | yes | | list | List of covers |
| `relay_pin` | yes | | integer | The pin of your Raspberry Pi where the relay is connected |
| `state_pin` | yes | | integer | The pin of your Raspberry Pi to retrieve the state |
| `name` | no | | string | The name for the cover entity |
| `unique_id` | no | | string | An ID that uniquely identifies the cover. Set this to a unique value to allow customization through the UI |
### Remote Raspberry Pi Cover
If you don't have Home Assistant running on your Raspberry Pi and you want to use it as a remote cover instead, there is a project called [GarageQTPi](https://github.com/Jerrkawz/GarageQTPi) that will work remotely with the [MQTT Cover Component](/integrations/cover.mqtt/). Follow the GitHub instructions to install and configure GarageQTPi and once configured follow the Home Assistant instructions to configure the MQTT Cover.
## Switch
The `rpi_gpio` switch platform allows you to control the GPIOs of your [Raspberry Pi](https://www.raspberrypi.org/).
### Configuration
[Legacy switch configuration](https://github.com/thecode/ha-rpi_gpio/blob/main/legacy-config.md#switch)
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
# Basic configuration.yaml entry
switch:
- platform: rpi_gpio
switches:
- port: 11
name: "Fan Office"
- port: 12
name: "Light Desk"
```
```yaml
# Full configuration.yaml entry
switch:
- platform: rpi_gpio
switches:
- port: 11
name: "Fan Office"
unique_id: "fan_office_switch_port_11"
persistent: true
- port: 12
name: "Light Desk"
unique_id: "light_desk_switch_port_12"
invert_logic: true
```
### Options
| Key | Required | Default | Type | Description |
| -------------- | -------- | ------- | --------| ----------------------------------------------------------------------------------------------------------- |
| `switches` | yes | | list | List of switch IO ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) |
| `name` | yes | | string | The name for the switch entity |
| `unique_id` | no | | string | An ID that uniquely identifies the switch. Set this to a unique value to allow customization through the UI |
| `invert_logic` | no | `false` | boolean | If true, inverts the output logic to ACTIVE LOW |
| `persistent` | no | `false` | boolean | If true, the switch state will be persistent in HA and will be restored if HA restart / crash |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
A common question is what does Port refer to, this number is the actual GPIO #, not the pin #.
For example, if you have a relay connected to pin 11 its GPIO # is 17.
```yaml
# Basic configuration.yaml entry
switch:
- platform: rpi_gpio
switches:
- port: 17
name: "Speaker Relay"
```

View File

@ -1,92 +0,0 @@
# Legacy binary sensor and switch configuration
**This configuration is used for backward compatibility and should not be used for new installations.**
## Binary Sensor
The `rpi_gpio` binary sensor platform allows you to read sensor values of the GPIOs of your [Raspberry Pi](https://www.raspberrypi.org/).
### Configuration
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
# Basic configuration.yaml entry
binary_sensor:
- platform: rpi_gpio
ports:
11: PIR Office
12: PIR Bedroom
```
```yaml
# Full configuration.yaml entry
binary_sensor:
- platform: rpi_gpio
bouncetime: 80
invert_logic: true
pull_mode: "DOWN"
ports:
11: PIR Office
12: PIR Bedroom
```
### Options
| Key | Required | Default | Type | Description |
| -------------- | -------- | --------------------- | --------|------------------------------------------------------------------ |
| `bouncetime` | no | `50` | integer | The time in milliseconds for port debouncing |
| `invert_logic` | no | `false` (ACTIVE HIGH) | boolean | If `true`, inverts the output logic to ACTIVE LOW |
| `pull_mode` | no | `UP` | string | Type of internal pull resistor to use: `UP` - pull-up resistor, `DOWN` - pull-down resistor |
| `ports` | yes | | list | List of used ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) and corresponding names |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
## Switch
The `rpi_gpio` switch platform allows you to control the GPIOs of your [Raspberry Pi](https://www.raspberrypi.org/).
### Configuration
To use your Raspberry Pi's GPIO in your installation, add the following to your `configuration.yaml` file:
```yaml
# Basic configuration.yaml entry
switch:
- platform: rpi_gpio
ports:
11: Fan Office
12: Light Desk
```
```yaml
# Full configuration.yaml entry
switch:
- platform: rpi_gpio
invert_logic: true
ports:
11: Fan Office
12: Light Desk
```
### Options
| Key | Required | Default | Type | Description |
| -------------- | -------- | ------- | --------------- | --------------------------------------------------- |
| `invert_logic` | no | `false` | boolean | If true, inverts the output logic to ACTIVE LOW |
| `ports` | yes | | list | List of used ports ([BCM mode pin numbers](https://pinout.xyz/resources/raspberry-pi-pinout.png)) and corresponding names |
For more details about the GPIO layout, visit the Wikipedia [article](https://en.wikipedia.org/wiki/Raspberry_Pi#General_purpose_input-output_(GPIO)_connector) about the Raspberry Pi.
**Note that a pin managed by Home Assistant is expected to be exclusive to Home Assistant.**
A common question is what does Port refer to, this number is the actual GPIO #, not the pin #.
For example, if you have a relay connected to pin 11 its GPIO # is 17.
```yaml
# Basic configuration.yaml entry
switch:
- platform: rpi_gpio
ports:
17: Speaker Relay
```