From 5c5fa6f2b48497f83f6b51063c5f161ae7e04911 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Wed, 5 Oct 2022 16:12:35 +0300 Subject: [PATCH] Config-flow-switch (#112) * 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 * Add switch to config flow --- custom_components/rpi_gpio/__init__.py | 10 ++ custom_components/rpi_gpio/binary_sensor.py | 4 +- custom_components/rpi_gpio/config_flow.py | 42 ++++++- custom_components/rpi_gpio/strings.json | 7 ++ custom_components/rpi_gpio/switch.py | 114 ++++++++---------- .../rpi_gpio/translations/en.json | 7 ++ 6 files changed, 112 insertions(+), 72 deletions(-) diff --git a/custom_components/rpi_gpio/__init__.py b/custom_components/rpi_gpio/__init__.py index a5c082d..45fcc6d 100644 --- a/custom_components/rpi_gpio/__init__.py +++ b/custom_components/rpi_gpio/__init__.py @@ -20,8 +20,10 @@ from .const import ( CONF_BOUNCETIME, CONF_CONFIGURED_PORTS, CONF_GPIO, + CONF_INVERT_LOGIC, CONF_PULL_MODE, DEFAULT_BOUNCETIME, + DEFAULT_INVERT_LOGIC, DEFAULT_PULL_MODE, DOMAIN, ) @@ -126,3 +128,11 @@ class RpiGPIO: callback=edge_detected, bouncetime=entry.options.get(CONF_BOUNCETIME, DEFAULT_BOUNCETIME), ) + elif entry.data[CONF_PLATFORM] == Platform.SWITCH: + GPIO.setup( + int(entry.data[CONF_PORT]), + GPIO.OUT, + initial=GPIO.HIGH + if entry.options.get(CONF_INVERT_LOGIC, DEFAULT_INVERT_LOGIC) + else GPIO.LOW, + ) diff --git a/custom_components/rpi_gpio/binary_sensor.py b/custom_components/rpi_gpio/binary_sensor.py index 502a03d..a2dffa4 100644 --- a/custom_components/rpi_gpio/binary_sensor.py +++ b/custom_components/rpi_gpio/binary_sensor.py @@ -7,11 +7,9 @@ from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensor from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_NAME, - CONF_PLATFORM, CONF_PORT, CONF_SENSORS, CONF_UNIQUE_ID, - Platform, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -87,7 +85,7 @@ async def async_setup_entry( entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up rpi_power binary sensor.""" + """Set up rpi_gpio binary sensor.""" rpi_gpio: RpiGPIO = hass.data[DOMAIN][CONF_GPIO] await hass.async_add_executor_job(rpi_gpio.setup_port, entry) diff --git a/custom_components/rpi_gpio/config_flow.py b/custom_components/rpi_gpio/config_flow.py index d105127..546dc3b 100644 --- a/custom_components/rpi_gpio/config_flow.py +++ b/custom_components/rpi_gpio/config_flow.py @@ -84,6 +84,15 @@ def _get_options_schema(platform: Platform, options: dict[str, Any]) -> vol.Sche ), } ) + if platform == Platform.SWITCH: + return vol.Schema( + { + vol.Optional( + CONF_INVERT_LOGIC, + default=options.get(CONF_INVERT_LOGIC, DEFAULT_INVERT_LOGIC), + ): selector.BooleanSelector() + } + ) def _get_avaiable_ports(hass: HomeAssistant) -> list[selector.SelectOptionDict]: @@ -114,14 +123,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a flow initialized by the user.""" return self.async_show_menu( step_id="user", - menu_options=["add_binary_sensor"], + menu_options=["add_binary_sensor", "add_switch"], ) 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( @@ -147,12 +155,40 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data={CONF_PLATFORM: Platform.BINARY_SENSOR, **user_input}, ) + async def async_step_add_switch( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle adding a switch.""" + if user_input is None: + return self.async_show_form( + step_id="add_switch", + 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.SWITCH, **user_input}, + ) + @staticmethod @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, ) -> RpiGPIOOptionsFlowHandler: - """Options callback for AccuWeather.""" + """Options callback for Rpi GPIO.""" return RpiGPIOOptionsFlowHandler(config_entry) diff --git a/custom_components/rpi_gpio/strings.json b/custom_components/rpi_gpio/strings.json index 47c8e1f..241672f 100644 --- a/custom_components/rpi_gpio/strings.json +++ b/custom_components/rpi_gpio/strings.json @@ -3,9 +3,16 @@ "step": { "add_binary_sensor": { "data": { + "name": "[%key:common::config_flow::data::name%]", "port": "Select port to set as binary sensor" } }, + "add_switch": { + "data": { + "name": "[%key:common::config_flow::data::name%]", + "port": "Select port to set as switch" + } + }, "user": { "menu_options": { "add_binary_sensor": "Add Binary sensor", diff --git a/custom_components/rpi_gpio/switch.py b/custom_components/rpi_gpio/switch.py index 6a49872..a87de1c 100644 --- a/custom_components/rpi_gpio/switch.py +++ b/custom_components/rpi_gpio/switch.py @@ -1,29 +1,28 @@ """Allows to configure a switch using RPi GPIO.""" from __future__ import annotations +from typing import Any + import voluptuous as vol 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.config_entries import ConfigEntry +from homeassistant.const import CONF_NAME, CONF_PORT, CONF_SWITCHES, 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.issue_registry import IssueSeverity, async_create_issue 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 +from . import RpiGPIO +from .const import ( + CONF_GPIO, + CONF_INVERT_LOGIC, + CONF_PORTS, + DEFAULT_INVERT_LOGIC, + DOMAIN, +) +from .entity import RpiGPIOEntity _SWITCHES_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string}) @@ -51,68 +50,51 @@ PLATFORM_SCHEMA = vol.All( ) -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) - - switches = [] - - switches_conf = config.get(CONF_SWITCHES) - if switches_conf is not None: - for switch in switches_conf: - 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] - - ports = config[CONF_PORTS] - for port, name in ports.items(): - switches.append(RPiGPIOSwitch(name, port, invert_logic)) - - add_entities(switches) + async_create_issue( + hass, + DOMAIN, + "deprecated_yaml", + breaks_in_ha_version="2022.11.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + ) -class RPiGPIOSwitch(SwitchEntity): - """Representation of a Raspberry Pi GPIO.""" +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up rpi_gpio switch.""" + rpi_gpio: RpiGPIO = hass.data[DOMAIN][CONF_GPIO] + await hass.async_add_executor_job(rpi_gpio.setup_port, entry) - 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._invert_logic = invert_logic - self._state = False - setup_output(self._port) - write_output(self._port, 1 if self._invert_logic else 0) + async_add_entities([RPiGPIOSwitch(hass, entry, rpi_gpio)], True) - @property - def is_on(self): - """Return true if device is on.""" - return self._state - def turn_on(self, **kwargs): +class RPiGPIOSwitch(RpiGPIOEntity, SwitchEntity): + """Representation of an output port as switch.""" + + async def async_update(self) -> None: + """Update entity.""" + self._attr_is_on = ( + await self.rpi_gpio.async_read_input(self.port) != self.invert_logic + ) + + 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.schedule_update_ha_state() + await self.rpi_gpio.async_write_output(self.port, 0 if self.invert_logic else 1) + self.async_schedule_update_ha_state(True) - def turn_off(self, **kwargs): + 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.schedule_update_ha_state() + await self.rpi_gpio.async_write_output(self.port, 1 if self.invert_logic else 0) + self.async_schedule_update_ha_state(True) diff --git a/custom_components/rpi_gpio/translations/en.json b/custom_components/rpi_gpio/translations/en.json index 1d06e54..304316d 100644 --- a/custom_components/rpi_gpio/translations/en.json +++ b/custom_components/rpi_gpio/translations/en.json @@ -3,9 +3,16 @@ "step": { "add_binary_sensor": { "data": { + "name": "Name", "port": "Select port to set as binary sensor" } }, + "add_switch": { + "data": { + "name": "Name", + "port": "Select port to set as switch" + } + }, "user": { "menu_options": { "add_binary_sensor": "Add Binary sensor",