mirror of
https://github.com/thecode/ha-rpi_gpio.git
synced 2025-07-29 13:46:39 +00:00
Add config flow to Raspberry pi GPIO (#103)
* Add config flow to rpi_gpio * remove title * fix removing entities in config_flow and in cover.py * Set config entry per port Limit PR to binary_sensor * restore original files * Add name key to give the device a name
This commit is contained in:
parent
8f1d3116d7
commit
c87c8595fa
@ -1,59 +1,128 @@
|
|||||||
"""Support for controlling GPIO pins of a Raspberry Pi."""
|
"""The Raspberry Pi GPIO integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from RPi import GPIO # pylint: disable=import-error
|
from RPi import GPIO # pylint: disable=import-error
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START,
|
CONF_PLATFORM,
|
||||||
|
CONF_PORT,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
Platform,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
|
||||||
DOMAIN = "rpi_gpio"
|
from .const import (
|
||||||
PLATFORMS = [
|
CONF_BOUNCETIME,
|
||||||
Platform.BINARY_SENSOR,
|
CONF_CONFIGURED_PORTS,
|
||||||
Platform.COVER,
|
CONF_GPIO,
|
||||||
Platform.SWITCH,
|
CONF_PULL_MODE,
|
||||||
]
|
DEFAULT_BOUNCETIME,
|
||||||
|
DEFAULT_PULL_MODE,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR]
|
||||||
|
|
||||||
|
|
||||||
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up the Raspberry PI GPIO component."""
|
"""Set up Raspberry Pi GPIO from a config entry."""
|
||||||
|
|
||||||
def cleanup_gpio(event):
|
@callback
|
||||||
"""Stuff to do before stopping."""
|
def cleanup_gpio(event: Any) -> None:
|
||||||
|
"""Cleanup before stopping."""
|
||||||
GPIO.cleanup()
|
GPIO.cleanup()
|
||||||
|
|
||||||
def prepare_gpio(event):
|
if DOMAIN not in hass.data:
|
||||||
"""Stuff to do when Home Assistant starts."""
|
# actions that should be done the first time only
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio)
|
hass.data[DOMAIN] = {}
|
||||||
|
hass.data[DOMAIN][CONF_CONFIGURED_PORTS] = []
|
||||||
|
hass.data[DOMAIN][CONF_GPIO] = RpiGPIO(hass)
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio)
|
||||||
|
|
||||||
|
await hass.config_entries.async_forward_entry_setup(
|
||||||
|
entry, entry.data[CONF_PLATFORM]
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.data[DOMAIN][CONF_CONFIGURED_PORTS].append(entry.data[CONF_PORT])
|
||||||
|
|
||||||
|
entry.async_on_unload(entry.add_update_listener(options_updated))
|
||||||
|
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio)
|
|
||||||
GPIO.setmode(GPIO.BCM)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_output(port):
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up a GPIO as output."""
|
"""Unload a config entry."""
|
||||||
GPIO.setup(port, GPIO.OUT)
|
if unload_ok := await hass.config_entries.async_forward_entry_unload(
|
||||||
|
entry, entry.data[CONF_PLATFORM]
|
||||||
|
):
|
||||||
|
if not hass.data[DOMAIN][CONF_CONFIGURED_PORTS]:
|
||||||
|
del hass.data[DOMAIN]
|
||||||
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
def setup_input(port, pull_mode):
|
async def options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
"""Set up a GPIO as input."""
|
"""Update when config_entry options update."""
|
||||||
GPIO.setup(port, GPIO.IN, GPIO.PUD_DOWN if pull_mode == "DOWN" else GPIO.PUD_UP)
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
def write_output(port, value):
|
class RpiGPIO:
|
||||||
"""Write a value to a GPIO."""
|
"""Base class for Rpi GPIOs."""
|
||||||
GPIO.output(port, value)
|
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
|
"""Initialize the class."""
|
||||||
|
self.hass = hass
|
||||||
|
GPIO.setmode(GPIO.BCM)
|
||||||
|
|
||||||
def read_input(port):
|
async def async_reset_port(self, port: str) -> None:
|
||||||
"""Read a value from a GPIO."""
|
"""Reset removed port to input."""
|
||||||
return GPIO.input(port)
|
await self.hass.async_add_executor_job(GPIO.setup, int(port), GPIO.IN)
|
||||||
|
|
||||||
|
async def async_read_input(self, port: str) -> bool:
|
||||||
|
"""Read a value from a GPIO."""
|
||||||
|
return await self.hass.async_add_executor_job(GPIO.input, int(port))
|
||||||
|
|
||||||
def edge_detect(port, event_callback, bounce):
|
async def async_write_output(self, port: str, value: int) -> None:
|
||||||
"""Add detection for RISING and FALLING events."""
|
"""Write value to a GPIO."""
|
||||||
GPIO.add_event_detect(port, GPIO.BOTH, callback=event_callback, bouncetime=bounce)
|
await self.hass.async_add_executor_job(GPIO.output, int(port), value)
|
||||||
|
|
||||||
|
async def async_remove_edge_detection(self, port: str) -> None:
|
||||||
|
"""Remove edge detection if input is deleted."""
|
||||||
|
await self.hass.async_add_executor_job(GPIO.remove_event_detect, int(port))
|
||||||
|
|
||||||
|
@callback
|
||||||
|
async def async_signal_edge_detected(self, port: int, bounce_time: int) -> None:
|
||||||
|
"""Send signal that input edge is detected."""
|
||||||
|
await asyncio.sleep(float(bounce_time / 1000))
|
||||||
|
async_dispatcher_send(self.hass, f"port_{port}_edge_detected")
|
||||||
|
|
||||||
|
def setup_port(self, entry: ConfigEntry) -> None:
|
||||||
|
"""Setup GPIO ports."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def edge_detected(port: int) -> None:
|
||||||
|
"""Edge detection handler."""
|
||||||
|
self.hass.add_job(
|
||||||
|
self.async_signal_edge_detected,
|
||||||
|
port,
|
||||||
|
entry.options.get(CONF_BOUNCETIME, DEFAULT_BOUNCETIME),
|
||||||
|
)
|
||||||
|
|
||||||
|
if entry.data[CONF_PLATFORM] == Platform.BINARY_SENSOR:
|
||||||
|
# Setup input
|
||||||
|
GPIO.setup(
|
||||||
|
int(entry.data[CONF_PORT]),
|
||||||
|
GPIO.IN,
|
||||||
|
int(entry.options.get(CONF_PULL_MODE, DEFAULT_PULL_MODE)),
|
||||||
|
)
|
||||||
|
# Add edge detection
|
||||||
|
GPIO.add_event_detect(
|
||||||
|
int(entry.data[CONF_PORT]),
|
||||||
|
GPIO.BOTH,
|
||||||
|
callback=edge_detected,
|
||||||
|
bouncetime=entry.options.get(CONF_BOUNCETIME, DEFAULT_BOUNCETIME),
|
||||||
|
)
|
||||||
|
@ -1,34 +1,38 @@
|
|||||||
"""Support for binary sensor using RPi GPIO."""
|
"""Support for binary sensor using RPi GPIO."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
|
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
CONF_PLATFORM,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_SENSORS,
|
CONF_SENSORS,
|
||||||
CONF_UNIQUE_ID,
|
CONF_UNIQUE_ID,
|
||||||
DEVICE_DEFAULT_NAME,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.reload import setup_reload_service
|
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import DOMAIN, PLATFORMS, edge_detect, read_input, setup_input
|
from . import RpiGPIO
|
||||||
|
from .const import (
|
||||||
CONF_BOUNCETIME = "bouncetime"
|
CONF_BOUNCETIME,
|
||||||
CONF_INVERT_LOGIC = "invert_logic"
|
CONF_GPIO,
|
||||||
CONF_PORTS = "ports"
|
CONF_INVERT_LOGIC,
|
||||||
CONF_PULL_MODE = "pull_mode"
|
CONF_PORTS,
|
||||||
|
CONF_PULL_MODE,
|
||||||
DEFAULT_BOUNCETIME = 50
|
DEFAULT_BOUNCETIME,
|
||||||
DEFAULT_INVERT_LOGIC = False
|
DEFAULT_INVERT_LOGIC,
|
||||||
DEFAULT_PULL_MODE = "UP"
|
DEFAULT_PULL_MODE,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
from .entity import RpiGPIOEntity
|
||||||
|
|
||||||
_SENSORS_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string})
|
_SENSORS_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string})
|
||||||
|
|
||||||
@ -59,83 +63,62 @@ PLATFORM_SCHEMA = vol.All(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(
|
async def async_setup_platform(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
add_entities: AddEntitiesCallback,
|
add_entities: AddEntitiesCallback,
|
||||||
discovery_info: DiscoveryInfoType | None = None,
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Raspberry PI GPIO devices."""
|
"""Set up the Raspberry PI GPIO devices."""
|
||||||
setup_reload_service(hass, DOMAIN, PLATFORMS)
|
|
||||||
|
|
||||||
sensors = []
|
async_create_issue(
|
||||||
|
hass,
|
||||||
sensors_conf = config.get(CONF_SENSORS)
|
DOMAIN,
|
||||||
if sensors_conf is not None:
|
"deprecated_yaml",
|
||||||
for sensor in sensors_conf:
|
breaks_in_ha_version="2022.11.0",
|
||||||
sensors.append(
|
is_fixable=False,
|
||||||
RPiGPIOBinarySensor(
|
severity=IssueSeverity.WARNING,
|
||||||
sensor[CONF_NAME],
|
translation_key="deprecated_yaml",
|
||||||
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():
|
|
||||||
sensors.append(
|
|
||||||
RPiGPIOBinarySensor(
|
|
||||||
port_name, port_num, pull_mode, bouncetime, invert_logic
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
add_entities(sensors, True)
|
|
||||||
|
|
||||||
|
|
||||||
class RPiGPIOBinarySensor(BinarySensorEntity):
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up rpi_power binary sensor."""
|
||||||
|
rpi_gpio: RpiGPIO = hass.data[DOMAIN][CONF_GPIO]
|
||||||
|
await hass.async_add_executor_job(rpi_gpio.setup_port, entry)
|
||||||
|
|
||||||
|
async_add_entities([RPiGPIOBinarySensor(hass, entry, rpi_gpio)], True)
|
||||||
|
|
||||||
|
|
||||||
|
class RPiGPIOBinarySensor(RpiGPIOEntity, BinarySensorEntity):
|
||||||
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
|
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
|
||||||
|
|
||||||
async def async_read_gpio(self):
|
async def async_update(self) -> None:
|
||||||
"""Read state from GPIO."""
|
"""Update entity."""
|
||||||
await asyncio.sleep(float(self._bouncetime) / 1000)
|
self._attr_is_on = (
|
||||||
self._state = await self.hass.async_add_executor_job(read_input, self._port)
|
await self.rpi_gpio.async_read_input(self.port) != self.invert_logic
|
||||||
self.async_write_ha_state()
|
)
|
||||||
|
|
||||||
def __init__(self, name, port, pull_mode, bouncetime, invert_logic, unique_id=None):
|
@callback
|
||||||
"""Initialize the RPi binary sensor."""
|
def _update_callback(self) -> None:
|
||||||
self._attr_name = name or DEVICE_DEFAULT_NAME
|
"""Call update method."""
|
||||||
self._attr_unique_id = unique_id
|
self.async_schedule_update_ha_state(True)
|
||||||
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)
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register callbacks"""
|
||||||
|
|
||||||
def edge_detected(port):
|
self.async_on_remove(
|
||||||
"""Edge detection handler."""
|
async_dispatcher_connect(
|
||||||
if self.hass is not None:
|
self.hass, f"port_{self.port}_edge_detected", self._update_callback
|
||||||
self.hass.add_job(self.async_read_gpio)
|
)
|
||||||
|
)
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
edge_detect(self._port, edge_detected, self._bouncetime)
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Remove edge detection."""
|
||||||
@property
|
await self.rpi_gpio.async_remove_edge_detection(self.port)
|
||||||
def is_on(self):
|
await super().async_will_remove_from_hass()
|
||||||
"""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)
|
|
||||||
|
179
custom_components/rpi_gpio/config_flow.py
Normal file
179
custom_components/rpi_gpio/config_flow.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
"""Config flow for Raspberry Pi Power Supply Checker."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PLATFORM,
|
||||||
|
CONF_PORT,
|
||||||
|
TIME_MILLISECONDS,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
|
from homeassistant.helpers import selector
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CONF_BOUNCETIME,
|
||||||
|
CONF_CONFIGURED_PORTS,
|
||||||
|
CONF_INVERT_LOGIC,
|
||||||
|
CONF_PULL_MODE,
|
||||||
|
DEFAULT_BOUNCETIME,
|
||||||
|
DEFAULT_INVERT_LOGIC,
|
||||||
|
DEFAULT_PULL_MODE,
|
||||||
|
DOMAIN,
|
||||||
|
GPIO_PIN_MAP,
|
||||||
|
PUD_DOWN,
|
||||||
|
PUD_UP,
|
||||||
|
)
|
||||||
|
|
||||||
|
PULL_MODES = [
|
||||||
|
selector.SelectOptionDict(value=PUD_UP, label="UP"),
|
||||||
|
selector.SelectOptionDict(value=PUD_DOWN, label="DOWN"),
|
||||||
|
]
|
||||||
|
|
||||||
|
BINARY_SENSOR_OPTIONS_SCHEMA = {
|
||||||
|
vol.Optional(CONF_INVERT_LOGIC, default=False): selector.BooleanSelector(),
|
||||||
|
vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): vol.All(
|
||||||
|
selector.NumberSelector(
|
||||||
|
selector.NumberSelectorConfig(
|
||||||
|
min=50,
|
||||||
|
mode=selector.NumberSelectorMode.BOX,
|
||||||
|
unit_of_measurement=TIME_MILLISECONDS,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Coerce(int),
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_PULL_MODE, default=PUD_UP): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(options=PULL_MODES)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_options_schema(platform: Platform, options: dict[str, Any]) -> vol.Schema:
|
||||||
|
"""Return options schema based on platform."""
|
||||||
|
if platform == Platform.BINARY_SENSOR:
|
||||||
|
return vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(
|
||||||
|
CONF_INVERT_LOGIC,
|
||||||
|
default=options.get(CONF_INVERT_LOGIC, DEFAULT_INVERT_LOGIC),
|
||||||
|
): selector.BooleanSelector(),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_BOUNCETIME,
|
||||||
|
default=options.get(CONF_BOUNCETIME, DEFAULT_BOUNCETIME),
|
||||||
|
): vol.All(
|
||||||
|
selector.NumberSelector(
|
||||||
|
selector.NumberSelectorConfig(
|
||||||
|
min=50,
|
||||||
|
mode=selector.NumberSelectorMode.BOX,
|
||||||
|
unit_of_measurement=TIME_MILLISECONDS,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Coerce(int),
|
||||||
|
),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_PULL_MODE,
|
||||||
|
default=options.get(CONF_PULL_MODE, DEFAULT_PULL_MODE),
|
||||||
|
): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(options=PULL_MODES)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_avaiable_ports(hass: HomeAssistant) -> list[selector.SelectOptionDict]:
|
||||||
|
"""Return schema with availble ports."""
|
||||||
|
if DOMAIN in hass.data:
|
||||||
|
configured_ports = hass.data[DOMAIN][CONF_CONFIGURED_PORTS]
|
||||||
|
else:
|
||||||
|
configured_ports = []
|
||||||
|
|
||||||
|
return [
|
||||||
|
selector.SelectOptionDict(
|
||||||
|
value=port,
|
||||||
|
label=f"GPIO{port} - PIN {GPIO_PIN_MAP[port]}",
|
||||||
|
)
|
||||||
|
for port in list(GPIO_PIN_MAP)
|
||||||
|
if port not in configured_ports
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for Raspberry Pi GPIO."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, str] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Handle a flow initialized by the user."""
|
||||||
|
return self.async_show_menu(
|
||||||
|
step_id="user",
|
||||||
|
menu_options=["add_binary_sensor"],
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_add_binary_sensor(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Handle adding a binary sensor entry."""
|
||||||
|
|
||||||
|
if user_input is None:
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="add_binary_sensor",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_NAME): selector.TextSelector(),
|
||||||
|
vol.Required(CONF_PORT, default=[]): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=_get_avaiable_ports(self.hass),
|
||||||
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.async_set_unique_id(user_input[CONF_PORT])
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=f"{user_input[CONF_NAME]} (GPIO {user_input[CONF_PORT]})",
|
||||||
|
data={CONF_PLATFORM: Platform.BINARY_SENSOR, **user_input},
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@callback
|
||||||
|
def async_get_options_flow(
|
||||||
|
config_entry: config_entries.ConfigEntry,
|
||||||
|
) -> RpiGPIOOptionsFlowHandler:
|
||||||
|
"""Options callback for AccuWeather."""
|
||||||
|
return RpiGPIOOptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
|
|
||||||
|
class RpiGPIOOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
|
"""Handle integration options."""
|
||||||
|
|
||||||
|
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
|
||||||
|
"""Initialize integration options flow."""
|
||||||
|
self.config_entry = config_entry
|
||||||
|
|
||||||
|
async def async_step_init(
|
||||||
|
self, user_input: dict[str, str] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Manage the options."""
|
||||||
|
if user_input is not None:
|
||||||
|
return self.async_create_entry(title="", data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="init",
|
||||||
|
data_schema=_get_options_schema(
|
||||||
|
self.config_entry.data[CONF_PLATFORM],
|
||||||
|
dict(self.config_entry.options),
|
||||||
|
),
|
||||||
|
)
|
52
custom_components/rpi_gpio/const.py
Normal file
52
custom_components/rpi_gpio/const.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""Constants for the Raspberry Pi GPIO integration."""
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
|
||||||
|
DOMAIN: Final = "rpi_gpio"
|
||||||
|
|
||||||
|
CONF_GPIO: Final = "gpio"
|
||||||
|
CONF_CONFIGURED_PORTS: Final = "configured_ports"
|
||||||
|
CONF_PORTS: Final = "ports"
|
||||||
|
|
||||||
|
CONF_BOUNCETIME: Final = "bouncetime"
|
||||||
|
CONF_PULL_MODE: Final = "pull_mode"
|
||||||
|
CONF_INVERT_LOGIC: Final = "invert_logic"
|
||||||
|
|
||||||
|
PUD_DOWN: Final = "21"
|
||||||
|
PUD_UP: Final = "22"
|
||||||
|
|
||||||
|
DEFAULT_BOUNCETIME: Final = 50
|
||||||
|
DEFAULT_INVERT_LOGIC: Final = False
|
||||||
|
DEFAULT_PULL_MODE: Final = PUD_UP
|
||||||
|
|
||||||
|
GPIO_PIN_MAP: Final = {
|
||||||
|
"0": "27",
|
||||||
|
"1": "28",
|
||||||
|
"2": "3",
|
||||||
|
"3": "5",
|
||||||
|
"4": "7",
|
||||||
|
"5": "29",
|
||||||
|
"6": "31",
|
||||||
|
"7": "26",
|
||||||
|
"8": "24",
|
||||||
|
"9": "21",
|
||||||
|
"10": "19",
|
||||||
|
"11": "23",
|
||||||
|
"12": "32",
|
||||||
|
"13": "33",
|
||||||
|
"14": "8",
|
||||||
|
"15": "10",
|
||||||
|
"16": "36",
|
||||||
|
"17": "11",
|
||||||
|
"18": "12",
|
||||||
|
"19": "35",
|
||||||
|
"20": "38",
|
||||||
|
"21": "40",
|
||||||
|
"22": "15",
|
||||||
|
"23": "16",
|
||||||
|
"24": "18",
|
||||||
|
"25": "22",
|
||||||
|
"26": "37",
|
||||||
|
"27": "13",
|
||||||
|
}
|
46
custom_components/rpi_gpio/entity.py
Normal file
46
custom_components/rpi_gpio/entity.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"""Base entity for Rpi GPIO ports."""
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_NAME, CONF_PORT
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||||
|
|
||||||
|
from . import RpiGPIO
|
||||||
|
from .const import (
|
||||||
|
CONF_CONFIGURED_PORTS,
|
||||||
|
CONF_INVERT_LOGIC,
|
||||||
|
DEFAULT_INVERT_LOGIC,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RpiGPIOEntity(Entity):
|
||||||
|
"""Representation of a Raspberry Pi GPIO."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
_attr_should_poll = False
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, entry: ConfigEntry, rpi_gpio: RpiGPIO
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the RPi GPIO entity."""
|
||||||
|
self.hass = hass
|
||||||
|
self.entry = entry
|
||||||
|
self.rpi_gpio = rpi_gpio
|
||||||
|
self.port: str = entry.data[CONF_PORT]
|
||||||
|
self._attr_unique_id = self.port
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||||
|
name=f"{entry.data[CONF_NAME]} (GPIO {self.port})",
|
||||||
|
manufacturer="Raspberry Pi",
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def invert_logic(self) -> bool:
|
||||||
|
"""Return if port state should be inverted."""
|
||||||
|
return self.entry.options.get(CONF_INVERT_LOGIC, DEFAULT_INVERT_LOGIC)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Reset port to input."""
|
||||||
|
await self.rpi_gpio.async_reset_port(self.port)
|
||||||
|
self.hass.data[DOMAIN][CONF_CONFIGURED_PORTS].remove(self.port)
|
@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"domain": "rpi_gpio",
|
"domain": "rpi_gpio",
|
||||||
"name": "Raspberry Pi GPIO",
|
"name": "Raspberry Pi GPIO",
|
||||||
|
"config_flow": true,
|
||||||
"documentation": "https://github.com/thecode/ha-rpi_gpio",
|
"documentation": "https://github.com/thecode/ha-rpi_gpio",
|
||||||
"issue_tracker": "https://github.com/thecode/ha-rpi_gpio/issues",
|
"issue_tracker": "https://github.com/thecode/ha-rpi_gpio/issues",
|
||||||
"requirements": ["RPi.GPIO==0.7.1"],
|
"requirements": ["RPi.GPIO==0.7.1"],
|
||||||
"codeowners": ["@thecode"],
|
"codeowners": ["@thecode"],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"version": "2022.7.0"
|
"version": "2022.9.0"
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
reload:
|
|
||||||
name: Reload
|
|
||||||
description: Reload all rpi_gpio entities.
|
|
36
custom_components/rpi_gpio/strings.json
Normal file
36
custom_components/rpi_gpio/strings.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"add_binary_sensor": {
|
||||||
|
"data": {
|
||||||
|
"port": "Select port to set as binary sensor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"menu_options": {
|
||||||
|
"add_binary_sensor": "Add Binary sensor",
|
||||||
|
"add_switch": "Add Switch",
|
||||||
|
"add_cover": "Add Cover",
|
||||||
|
"remove": "Remove entity"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"bouncetime": "Port debounce time (milliseconds)",
|
||||||
|
"invert_logic": "Invert port state (default is ACTIVE_HIGH)",
|
||||||
|
"pull_mode": "Set internal pull resistor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"deprecated_yaml": {
|
||||||
|
"title": "The Raspberry Pi GPIO YAML configuration is being removed",
|
||||||
|
"description": "Configuring Raspberry Pi GPIO using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Raspberry Pi GPIO YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
custom_components/rpi_gpio/translations/en.json
Normal file
36
custom_components/rpi_gpio/translations/en.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"add_binary_sensor": {
|
||||||
|
"data": {
|
||||||
|
"port": "Select port to set as binary sensor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"menu_options": {
|
||||||
|
"add_binary_sensor": "Add Binary sensor",
|
||||||
|
"add_cover": "Add Cover",
|
||||||
|
"add_switch": "Add Switch",
|
||||||
|
"remove": "Remove entity"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"deprecated_yaml": {
|
||||||
|
"description": "Configuring Raspberry Pi GPIO using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Raspberry Pi GPIO YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.",
|
||||||
|
"title": "The Raspberry Pi GPIO YAML configuration is being removed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"bouncetime": "Port debounce time (milliseconds)",
|
||||||
|
"invert_logic": "Invert port state (default is ACTIVE_HIGH)",
|
||||||
|
"pull_mode": "Set internal pull resistor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user