mirror of
https://github.com/thecode/ha-rpi_gpio.git
synced 2025-08-02 15:37:44 +00:00
Compare commits
No commits in common. "main" and "2022.4.0" have entirely different histories.
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
||||
custom: ['https://paypal.me/levyshay']
|
4
.github/workflows/hacs.yml
vendored
4
.github/workflows/hacs.yml
vendored
@ -15,8 +15,10 @@ jobs:
|
||||
name: HACS Action
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@v4.2.2"
|
||||
- uses: "actions/checkout@v3.0.0"
|
||||
- name: HACS Action
|
||||
uses: "hacs/action@main"
|
||||
with:
|
||||
category: "integration"
|
||||
# Can be removed when https://github.com/home-assistant/brands/pull/3132 is merged
|
||||
ignore: "brands"
|
||||
|
2
.github/workflows/hassfest.yml
vendored
2
.github/workflows/hassfest.yml
vendored
@ -10,5 +10,5 @@ jobs:
|
||||
validate:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@v4.2.2"
|
||||
- uses: "actions/checkout@v3.0.0"
|
||||
- uses: "home-assistant/actions/hassfest@master"
|
||||
|
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Drafts your next Release notes as Pull Requests are merged into "main"
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
41
.github/workflows/stale.yaml
vendored
41
.github/workflows/stale.yaml
vendored
@ -1,41 +0,0 @@
|
||||
---
|
||||
name: Stale
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * *"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
name: 🧹 Clean up stale issues and PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🚀 Run stale
|
||||
uses: actions/stale@v9.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-label: "stale"
|
||||
exempt-issue-labels: "no-stale,help-wanted"
|
||||
stale-issue-message: >
|
||||
There hasn't been any activity on this issue recently, so we
|
||||
clean up some of the older and inactive issues.
|
||||
|
||||
Please make sure to update to the latest version and
|
||||
check if that solves the issue. Let us know if that works for you
|
||||
by leaving a comment 👍
|
||||
|
||||
This issue has now been marked as stale and will be closed if no
|
||||
further activity occurs. Thanks!
|
||||
stale-pr-label: "stale"
|
||||
exempt-pr-labels: "no-stale"
|
||||
stale-pr-message: >
|
||||
There hasn't been any activity on this pull request recently. This
|
||||
pull request has been automatically marked as stale because of that
|
||||
and will be closed if no further activity occurs within 7 days.
|
||||
Thank you for your contributions.
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -19,11 +19,11 @@ jobs:
|
||||
- "3.10"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.0.0
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5.6.0
|
||||
uses: actions/setup-python@v3.1.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
|
@ -1,35 +1,35 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-docstring-first
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-docstring-first
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
- id: flake8
|
||||
args:
|
||||
- --max-line-length=500
|
||||
- --ignore=E203,E266,E501,W503
|
||||
- --max-complexity=18
|
||||
- --select=B,C,E,F,W,T4,B9
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 23.1.0
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 21.12b0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.3.1
|
||||
- id: black
|
||||
language_version: python3
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: ["--py39-plus"]
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
- id: pyupgrade
|
||||
args: ['--py39-plus']
|
||||
- repo: https://github.com/pre-commit/mirrors-isort
|
||||
rev: v5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
- id: isort
|
||||
args:
|
||||
- --multi-line=3
|
||||
- --trailing-comma
|
||||
@ -38,34 +38,11 @@ repos:
|
||||
- --line-width=88
|
||||
- -p=homeassistant
|
||||
- --force-sort-within-sections
|
||||
- repo: https://github.com/PyCQA/pydocstyle
|
||||
rev: 6.3.0
|
||||
- repo: https://github.com/PyCQA/pydocstyle
|
||||
rev: 6.1.1
|
||||
hooks:
|
||||
- id: pydocstyle
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: "v0.991"
|
||||
- id: pydocstyle
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: 'v0.930'
|
||||
hooks:
|
||||
- id: mypy
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.2
|
||||
hooks:
|
||||
- id: codespell
|
||||
args:
|
||||
- --ignore-words-list=hass
|
||||
- --skip="./.*,*.csv,*.json"
|
||||
- --quiet-level=2
|
||||
exclude_types: [csv, json]
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.7.4
|
||||
hooks:
|
||||
- id: bandit
|
||||
args:
|
||||
- --quiet
|
||||
- --format=custom
|
||||
- --configfile=bandit.yaml
|
||||
files: ^custom_components/.+\.py$
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.7.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
stages: [manual]
|
||||
- id: mypy
|
||||
|
43
README.md
43
README.md
@ -1,11 +1,9 @@
|
||||
# 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.**
|
||||
**This is a spin-off from the original Home Assistant integration which was marked as deprecated and will be removed in Home Assistant Core 2022.6.**
|
||||
|
||||
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
|
||||
@ -18,32 +16,14 @@ Copy the `rpi_gpio` folder and all of its contents into your Home Assistant's `c
|
||||
|
||||
# Usage
|
||||
|
||||
The `rpi_gpio` 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
|
||||
rpi_gpio:
|
||||
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
|
||||
@ -79,11 +59,10 @@ 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 | 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 |
|
||||
| `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.
|
||||
|
||||
@ -122,8 +101,6 @@ 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"
|
||||
@ -154,6 +131,8 @@ 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
|
||||
@ -175,7 +154,6 @@ switch:
|
||||
- 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"
|
||||
@ -188,12 +166,8 @@ 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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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.
|
||||
|
||||
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.
|
||||
|
||||
@ -208,6 +182,3 @@ 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 and the relevant YAML section.
|
||||
|
21
bandit.yaml
21
bandit.yaml
@ -1,21 +0,0 @@
|
||||
# https://bandit.readthedocs.io/en/latest/config.html
|
||||
|
||||
tests:
|
||||
- B103
|
||||
- B108
|
||||
- B306
|
||||
- B307
|
||||
- B313
|
||||
- B314
|
||||
- B315
|
||||
- B316
|
||||
- B317
|
||||
- B318
|
||||
- B319
|
||||
- B320
|
||||
- B325
|
||||
- B601
|
||||
- B602
|
||||
- B604
|
||||
- B608
|
||||
- B609
|
@ -1,35 +1,59 @@
|
||||
"""Support for controlling GPIO pins of a device."""
|
||||
"""Support for controlling GPIO pins of a Raspberry Pi."""
|
||||
|
||||
import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from RPi import GPIO # pylint: disable=import-error
|
||||
|
||||
from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .hub import Hub
|
||||
DOMAIN = "rpi_gpio"
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.COVER,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
import voluptuous as vol
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from homeassistant.const import CONF_PATH
|
||||
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Raspberry PI GPIO component."""
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_PATH): vol.All(cv.string, vol.PathExists())
|
||||
})
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA
|
||||
)
|
||||
def cleanup_gpio(event):
|
||||
"""Stuff to do before stopping."""
|
||||
GPIO.cleanup()
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the GPIO component."""
|
||||
version = getattr(hass.data["integrations"][DOMAIN], "version", 0)
|
||||
_LOGGER.debug(f"{DOMAIN} integration starting. Version: {version}")
|
||||
path = config.get(DOMAIN, {}).get(CONF_PATH)
|
||||
hub = Hub(hass, path)
|
||||
hass.data[DOMAIN] = hub
|
||||
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):
|
||||
"""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)
|
||||
|
@ -1,103 +1,141 @@
|
||||
"""Support for binary sensor using RPi GPIO."""
|
||||
from __future__ import annotations
|
||||
|
||||
from . import DOMAIN
|
||||
import asyncio
|
||||
|
||||
import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
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.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"
|
||||
DEFAULT_INVERT_LOGIC = False
|
||||
CONF_BOUNCETIME = "bouncetime"
|
||||
DEFAULT_BOUNCETIME = 50
|
||||
CONF_PULL_MODE = "pull_mode"
|
||||
DEFAULT_PULL_MODE = "UP"
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import voluptuous as vol
|
||||
|
||||
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"
|
||||
CONF_INVERT_LOGIC = "invert_logic"
|
||||
CONF_PORTS = "ports"
|
||||
CONF_PULL_MODE = "pull_mode"
|
||||
|
||||
DEFAULT_BOUNCETIME = 50
|
||||
DEFAULT_INVERT_LOGIC = False
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
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,
|
||||
}]
|
||||
)
|
||||
})
|
||||
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),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
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")
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Raspberry PI GPIO devices."""
|
||||
setup_reload_service(hass, DOMAIN, PLATFORMS)
|
||||
|
||||
sensors = []
|
||||
for sensor in config.get(CONF_SENSORS):
|
||||
try:
|
||||
|
||||
sensors_conf = config.get(CONF_SENSORS)
|
||||
if sensors_conf is not None:
|
||||
for sensor in sensors_conf:
|
||||
sensors.append(
|
||||
GPIODBinarySensor(
|
||||
hub,
|
||||
RPiGPIOBinarySensor(
|
||||
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)
|
||||
sensor[CONF_PULL_MODE],
|
||||
sensor[CONF_BOUNCETIME],
|
||||
sensor[CONF_INVERT_LOGIC],
|
||||
sensor.get(CONF_UNIQUE_ID),
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Failed to add binary sensor {sensor[CONF_NAME]} for port {sensor[CONF_PORT]}: {e}")
|
||||
|
||||
async_add_entities(sensors)
|
||||
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():
|
||||
sensors.append(
|
||||
RPiGPIOBinarySensor(
|
||||
port_name, port_num, pull_mode, bouncetime, invert_logic
|
||||
)
|
||||
)
|
||||
|
||||
add_entities(sensors, True)
|
||||
|
||||
|
||||
class GPIODBinarySensor(BinarySensorEntity):
|
||||
_attr_should_poll = False
|
||||
class RPiGPIOBinarySensor(BinarySensorEntity):
|
||||
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
|
||||
|
||||
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
|
||||
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)
|
||||
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._active_low = active_low
|
||||
self._bias = bias
|
||||
self._debounce = debounce
|
||||
self._line, current_is_on = self._hub.add_sensor(self._port, self._active_low, self._bias, self._debounce)
|
||||
self._attr_is_on = current_is_on
|
||||
self._pull_mode = pull_mode
|
||||
self._bouncetime = bouncetime
|
||||
self._invert_logic = invert_logic
|
||||
self._state = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
_LOGGER.debug(f"GPIODBinarySensor async_added_to_hass: Adding fd:{self._line.fd}")
|
||||
self._hub._hass.loop.add_reader(self._line.fd, self.handle_event)
|
||||
setup_input(self._port, self._pull_mode)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
await super().async_will_remove_from_hass()
|
||||
_LOGGER.debug(f"GPIODBinarySensor async_will_remove_from_hass: Removing fd:{self._line.fd}")
|
||||
self._hub._hass.loop.remove_reader(self._line.fd)
|
||||
self._line.release()
|
||||
def edge_detected(port):
|
||||
"""Edge detection handler."""
|
||||
if self.hass is not None:
|
||||
self.hass.add_job(self.async_read_gpio)
|
||||
|
||||
def handle_event(self):
|
||||
for event in self._line.read_edge_events():
|
||||
self._attr_is_on = True if event.event_type is event.Type.RISING_EDGE else False
|
||||
_LOGGER.debug(f"Event: {event}. New line value: {self._attr_is_on}")
|
||||
self.schedule_update_ha_state(False)
|
||||
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)
|
||||
|
@ -1,4 +0,0 @@
|
||||
|
||||
DOMAIN = "rpi_gpio"
|
||||
|
||||
|
@ -1,23 +1,19 @@
|
||||
"""Support for controlling a Raspberry Pi cover."""
|
||||
from __future__ import annotations
|
||||
from functools import cached_property
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
from time import sleep
|
||||
|
||||
import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
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.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
|
||||
import asyncio
|
||||
|
||||
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
|
||||
|
||||
CONF_RELAY_PIN = "relay_pin"
|
||||
CONF_RELAY_TIME = "relay_time"
|
||||
@ -25,11 +21,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,
|
||||
[
|
||||
@ -54,121 +50,88 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
}
|
||||
)
|
||||
|
||||
async def async_setup_platform(
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
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")
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the RPi cover platform."""
|
||||
setup_reload_service(hass, DOMAIN, PLATFORMS)
|
||||
|
||||
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 = []
|
||||
for cover in config.get(CONF_COVERS):
|
||||
try:
|
||||
covers.append(
|
||||
GPIODCover(
|
||||
hub,
|
||||
cover[CONF_NAME],
|
||||
cover.get(CONF_RELAY_PIN),
|
||||
relay_time,
|
||||
invert_relay,
|
||||
"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_PIN)}_{cover[CONF_NAME].lower().replace(' ', '_')}",
|
||||
)
|
||||
covers_conf = config[CONF_COVERS]
|
||||
|
||||
for cover in covers_conf:
|
||||
covers.append(
|
||||
RPiGPIOCover(
|
||||
cover[CONF_NAME],
|
||||
cover[CONF_RELAY_PIN],
|
||||
cover[CONF_STATE_PIN],
|
||||
state_pull_mode,
|
||||
relay_time,
|
||||
invert_state,
|
||||
invert_relay,
|
||||
cover.get(CONF_UNIQUE_ID),
|
||||
)
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Failed to add cover {cover[CONF_NAME]} for port {cover.get(CONF_RELAY_PIN)}:{cover.get(CONF_STATE_PIN)}: {e}")
|
||||
)
|
||||
add_entities(covers)
|
||||
|
||||
async_add_entities(covers)
|
||||
|
||||
class GPIODCover(CoverEntity):
|
||||
_attr_should_poll = False
|
||||
class RPiGPIOCover(CoverEntity):
|
||||
"""Representation of a Raspberry GPIO 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
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
relay_pin,
|
||||
state_pin,
|
||||
state_pull_mode,
|
||||
relay_time,
|
||||
invert_state,
|
||||
invert_relay,
|
||||
unique_id,
|
||||
):
|
||||
"""Initialize the cover."""
|
||||
self._attr_name = name
|
||||
self._attr_unique_id = unique_id
|
||||
self._relay_port = relay_port
|
||||
self._state = False
|
||||
self._relay_pin = relay_pin
|
||||
self._state_pin = state_pin
|
||||
self._state_pull_mode = state_pull_mode
|
||||
self._relay_time = relay_time
|
||||
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._relay_line, self._state_line, current_is_on = self._hub.add_cover(
|
||||
self._relay_port, self._relay_active_low, self._relay_bias, self._relay_drive,
|
||||
self._state_port, self._state_bias, self._state_active_low)
|
||||
self._attr_is_closed = current_is_on
|
||||
self.is_on = current_is_on
|
||||
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)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
_LOGGER.debug(f"GPIODCover async_added_to_hass: Adding fd:{self._state_line.fd}")
|
||||
self._hub._hass.loop.add_reader(self._state_line.fd, self.handle_event)
|
||||
def update(self):
|
||||
"""Update the state of the cover."""
|
||||
self._state = read_input(self._state_pin)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
await super().async_will_remove_from_hass()
|
||||
_LOGGER.debug(f"GPIODCover async_will_remove_from_hass: Removing fd:{self._state_line.fd}")
|
||||
self._hub._hass.loop.remove_reader(self._state_line.fd)
|
||||
self._relay_line.release()
|
||||
self._state_line.release()
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return true if cover is closed."""
|
||||
return self._state != self._invert_state
|
||||
|
||||
def handle_event(self):
|
||||
for event in self._state_line.read_edge_events():
|
||||
self._attr_is_closed = True if event.event_type is event.Type.RISING_EDGE else False
|
||||
_LOGGER.debug(f"Event: {event}. New _attr_is_closed value: {self._attr_is_closed}")
|
||||
self.schedule_update_ha_state(False)
|
||||
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)
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
_LOGGER.debug(f"GPIODCover async_close_cover: is_closed: {self.is_closed}. is_closing: {self.is_closing}, is_opening: {self.is_opening}")
|
||||
if self.is_closed:
|
||||
return
|
||||
self._hub.turn_on(self._relay_line, self._relay_port)
|
||||
self._attr_is_closing = True
|
||||
self.async_write_ha_state()
|
||||
await asyncio.sleep(self._relay_time)
|
||||
if not self.is_closing:
|
||||
# closing stopped
|
||||
return
|
||||
self._hub.turn_off(self._relay_line, self._relay_port)
|
||||
self._attr_is_closing = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
_LOGGER.debug(f"GPIODCover async_open_cover: is_closed: {self.is_closed}. is_closing: {self.is_closing}, is_opening: {self.is_opening}")
|
||||
def close_cover(self, **kwargs):
|
||||
"""Close the cover."""
|
||||
if not self.is_closed:
|
||||
return
|
||||
self._hub.turn_on(self._relay_line, self._relay_port)
|
||||
self._attr_is_opening = True
|
||||
self.async_write_ha_state()
|
||||
await asyncio.sleep(self._relay_time)
|
||||
if not self.is_opening:
|
||||
# opening stopped
|
||||
return
|
||||
self._hub.turn_off(self._relay_line, self._relay_port)
|
||||
self._attr_is_opening = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
_LOGGER.debug(f"GPIODCover async_stop_cover: is_closed: {self.is_closed}. is_closing: {self.is_closing}, is_opening: {self.is_opening}")
|
||||
if not (self.is_closing or self.is_opening):
|
||||
return
|
||||
self._hub.turn_off(self._relay_line, self._relay_port)
|
||||
self._attr_is_opening = False
|
||||
self._attr_is_closing = False
|
||||
self.async_write_ha_state()
|
||||
self._trigger()
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
if self.is_closed:
|
||||
self._trigger()
|
||||
|
@ -1,149 +0,0 @@
|
||||
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,ServiceValidationError
|
||||
|
||||
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
|
||||
|
||||
if path:
|
||||
# use config
|
||||
_LOGGER.debug(f"trying to use configured device: {path}")
|
||||
if self.verify_gpiochip(path):
|
||||
self._online = True
|
||||
self._path = path
|
||||
else:
|
||||
# discover
|
||||
_LOGGER.debug(f"auto discovering gpio device")
|
||||
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
|
||||
|
||||
self.verify_online()
|
||||
_LOGGER.debug(f"using gpio_device: {self._path}")
|
||||
|
||||
def verify_online(self):
|
||||
if not self._online:
|
||||
_LOGGER.error("No gpio device detected, bailing out")
|
||||
raise HomeAssistantError("No gpio device detected")
|
||||
|
||||
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()
|
||||
_LOGGER.debug(f"verify_gpiochip: {path} info is: {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
|
||||
|
||||
def verify_port_ready(self, port: int):
|
||||
info = self._chip.get_line_info(port)
|
||||
_LOGGER.debug(f"original port {port} info: {info}")
|
||||
if info.used:
|
||||
if info.consumer != DOMAIN:
|
||||
raise HomeAssistantError(f"Port {port} already in use by {info.consumer}")
|
||||
else:
|
||||
raise HomeAssistantError(f"Port {port} already in use by another entity, check your config for duplicates port usage")
|
||||
|
||||
@property
|
||||
def hub_id(self) -> str:
|
||||
"""ID for hub"""
|
||||
return self._id
|
||||
|
||||
def add_switch(self, port, active_low, bias, drive_mode, init_state) -> gpiod.LineRequest:
|
||||
_LOGGER.debug(f"add_switch - port: {port}, active_low: {active_low}, bias: {bias}, drive_mode: {drive_mode}, init_state: {init_state}")
|
||||
self.verify_online()
|
||||
self.verify_port_ready(port)
|
||||
|
||||
line_request = self._chip.request_lines(
|
||||
consumer=DOMAIN,
|
||||
config={port: gpiod.LineSettings(
|
||||
direction = Direction.OUTPUT,
|
||||
bias = BIAS[bias],
|
||||
drive = DRIVE[drive_mode],
|
||||
active_low = active_low,
|
||||
output_value = Value.ACTIVE if init_state is not None and init_state else Value.INACTIVE)})
|
||||
_LOGGER.debug(f"add_switch line_request: {line_request}")
|
||||
return line_request
|
||||
|
||||
def turn_on(self, line, port) -> None:
|
||||
_LOGGER.debug(f"in turn_on {port}")
|
||||
self.verify_online()
|
||||
line.set_value(port, Value.ACTIVE)
|
||||
|
||||
def turn_off(self, line, port) -> None:
|
||||
_LOGGER.debug(f"in turn_off {port}")
|
||||
self.verify_online()
|
||||
line.set_value(port, Value.INACTIVE)
|
||||
|
||||
def add_sensor(self, port, active_low, bias, debounce) -> gpiod.LineRequest:
|
||||
_LOGGER.debug(f"add_sensor - port: {port}, active_low: {active_low}, bias: {bias}, debounce: {debounce}")
|
||||
self.verify_online()
|
||||
self.verify_port_ready(port)
|
||||
|
||||
line_request = self._chip.request_lines(
|
||||
consumer=DOMAIN,
|
||||
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)})
|
||||
_LOGGER.debug(f"add_sensor line_request: {line_request}")
|
||||
current_is_on = True if line_request.get_value(port) == Value.ACTIVE else False
|
||||
_LOGGER.debug(f"add_sensor current state: {current_is_on}")
|
||||
return line_request, current_is_on
|
||||
|
||||
def add_cover(self, relay_port, relay_active_low, relay_bias, relay_drive,
|
||||
state_port, state_bias, state_active_low):
|
||||
_LOGGER.debug(f"add_cover - relay_port: {relay_port}, state_port: {state_port}")
|
||||
relay_line = self.add_switch(relay_port, relay_active_low, relay_bias, relay_drive, False)
|
||||
state_line, current_is_on = self.add_sensor(state_port, state_active_low, state_bias, 50)
|
||||
return relay_line, state_line, current_is_on
|
||||
|
@ -1,11 +1,10 @@
|
||||
{
|
||||
"domain": "rpi_gpio",
|
||||
"name": "Raspberry Pi GPIO",
|
||||
"codeowners": [ "@thecode", "@tomer-w" ],
|
||||
"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": [ "gpiod>=2.2.1" ],
|
||||
"version": "2025.2.1"
|
||||
"requirements": ["RPi.GPIO==0.7.1a4"],
|
||||
"codeowners": ["@thecode"],
|
||||
"iot_class": "local_push",
|
||||
"version": "2022.4.0"
|
||||
}
|
||||
|
@ -1,120 +1,118 @@
|
||||
"""Allows to configure a switch using RPi GPIO."""
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
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 .hub import BIAS, DRIVE
|
||||
CONF_INVERT_LOGIC = "invert_logic"
|
||||
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
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import voluptuous as vol
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
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,
|
||||
}]
|
||||
)
|
||||
})
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_PORT,
|
||||
CONF_SWITCHES,
|
||||
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, setup_output, write_output
|
||||
|
||||
CONF_PULL_MODE = "pull_mode"
|
||||
CONF_PORTS = "ports"
|
||||
CONF_INVERT_LOGIC = "invert_logic"
|
||||
|
||||
DEFAULT_INVERT_LOGIC = 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_UNIQUE_ID): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
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,
|
||||
},
|
||||
),
|
||||
cv.has_at_least_one_key(CONF_PORTS, CONF_SWITCHES),
|
||||
)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
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")
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Raspberry PI GPIO devices."""
|
||||
setup_reload_service(hass, DOMAIN, PLATFORMS)
|
||||
|
||||
switches = []
|
||||
for switch in config.get(CONF_SWITCHES):
|
||||
try:
|
||||
|
||||
switches_conf = config.get(CONF_SWITCHES)
|
||||
if switches_conf is not None:
|
||||
for switch in switches_conf:
|
||||
switches.append(
|
||||
GPIODSwitch(
|
||||
hub,
|
||||
RPiGPIOSwitch(
|
||||
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]
|
||||
switch[CONF_INVERT_LOGIC],
|
||||
switch.get(CONF_UNIQUE_ID),
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Failed to add switch {switch[CONF_NAME]} for port {switch[CONF_PORT]}: {e}")
|
||||
|
||||
async_add_entities(switches)
|
||||
add_entities(switches, True)
|
||||
return
|
||||
|
||||
invert_logic = config[CONF_INVERT_LOGIC]
|
||||
|
||||
ports = config[CONF_PORTS]
|
||||
for port, name in ports.items():
|
||||
switches.append(RPiGPIOSwitch(name, port, invert_logic))
|
||||
|
||||
add_entities(switches)
|
||||
|
||||
|
||||
class GPIODSwitch(SwitchEntity, RestoreEntity):
|
||||
_attr_should_poll = False
|
||||
class RPiGPIOSwitch(SwitchEntity):
|
||||
"""Representation of a Raspberry Pi GPIO."""
|
||||
|
||||
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} - persistent: {persistent}")
|
||||
self._hub = hub
|
||||
self._attr_name = name
|
||||
def __init__(self, name, port, invert_logic, unique_id=None):
|
||||
"""Initialize the pin."""
|
||||
self._attr_name = name or DEVICE_DEFAULT_NAME
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_should_poll = False
|
||||
self._port = port
|
||||
self._active_low = active_low
|
||||
self._bias = bias
|
||||
self._drive_mode = drive
|
||||
self._persistent = persistent
|
||||
self._line = None
|
||||
self._hub.verify_port_ready(self._port)
|
||||
|
||||
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 or not self._persistent:
|
||||
self._attr_is_on = False
|
||||
else:
|
||||
_LOGGER.debug(f"setting initial persistent state for: {self._port}. state: {state.state}")
|
||||
self._attr_is_on = True if state.state == STATE_ON else False
|
||||
self.async_write_ha_state()
|
||||
self._line = self._hub.add_switch(self._port, self._active_low, self._bias, self._drive_mode, self._attr_is_on)
|
||||
self._invert_logic = invert_logic
|
||||
self._state = False
|
||||
setup_output(self._port)
|
||||
write_output(self._port, 1 if self._invert_logic else 0)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
await super().async_will_remove_from_hass()
|
||||
_LOGGER.debug(f"GPIODSwitch async_will_remove_from_hass")
|
||||
if self._line:
|
||||
self._line.release()
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return self._state
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
self._hub.turn_on(self._line, self._port)
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the device on."""
|
||||
write_output(self._port, 0 if self._invert_logic else 1)
|
||||
self._state = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
self._hub.turn_off(self._line, self._port)
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the device off."""
|
||||
write_output(self._port, 1 if self._invert_logic else 0)
|
||||
self._state = False
|
||||
self.schedule_update_ha_state()
|
||||
|
@ -1,4 +1,6 @@
|
||||
{
|
||||
"name": "Raspberry Pi GPIO",
|
||||
"homeassistant": "2024.9.0"
|
||||
}
|
||||
"domains": ["binary_sensor", "cover", "switch"],
|
||||
"iot_class": "Local Push",
|
||||
"homeassistant": "2022.2.0"
|
||||
}
|
||||
|
172
info.md
Normal file
172
info.md
Normal file
@ -0,0 +1,172 @@
|
||||
# Home Assistant Raspberry Pi GPIO custom integration
|
||||
|
||||
**This is a spin-off from the original Home Assistant integration which was marked as deprecated and will be removed in Home Assistant Core 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"
|
||||
- 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 |
|
||||
|
||||
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"
|
||||
```
|
92
legacy-config.md
Normal file
92
legacy-config.md
Normal file
@ -0,0 +1,92 @@
|
||||
# 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
|
||||
```
|
@ -1,7 +1,7 @@
|
||||
black==25.1.0
|
||||
flake8==7.3.0
|
||||
isort==6.0.1
|
||||
mypy==1.17.0
|
||||
pre-commit==4.2.0
|
||||
pydocstyle==6.3.0
|
||||
pylint==3.3.7
|
||||
black==22.1.0
|
||||
flake8==4.0.1
|
||||
isort==5.10.1
|
||||
mypy==0.942
|
||||
pre-commit==2.17.0
|
||||
pydocstyle==6.1.1
|
||||
pylint==2.13.2
|
||||
|
Loading…
x
Reference in New Issue
Block a user