diff --git a/custom_components/rpi_gpio/binary_sensor.py b/custom_components/rpi_gpio/binary_sensor.py index c20c51b..36135f0 100644 --- a/custom_components/rpi_gpio/binary_sensor.py +++ b/custom_components/rpi_gpio/binary_sensor.py @@ -6,7 +6,13 @@ import asyncio import voluptuous as vol from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity -from homeassistant.const import DEVICE_DEFAULT_NAME +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 @@ -24,17 +30,34 @@ DEFAULT_BOUNCETIME = 50 DEFAULT_INVERT_LOGIC = False DEFAULT_PULL_MODE = "UP" -_SENSORS_SCHEMA = vol.Schema({cv.positive_int: cv.string}) +_SENSORS_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string}) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( +_SENSOR_SCHEMA = vol.Schema( { - vol.Required(CONF_PORTS): _SENSORS_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_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) +PLATFORM_SCHEMA = vol.All( + PLATFORM_SCHEMA.extend( + { + vol.Exclusive(CONF_PORTS, CONF_SENSORS): _SENSORS_LEGACY_SCHEMA, + vol.Exclusive(CONF_SENSORS, CONF_SENSORS): vol.All( + cv.ensure_list, [_SENSOR_SCHEMA] + ), + vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int, + vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, + vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string, + }, + ), + cv.has_at_least_one_key(CONF_PORTS, CONF_SENSORS), +) + def setup_platform( hass: HomeAssistant, @@ -45,19 +68,38 @@ def setup_platform( """Set up the Raspberry PI GPIO devices.""" setup_reload_service(hass, DOMAIN, PLATFORMS) + sensors = [] + + sensors_conf = config.get(CONF_SENSORS) + if sensors_conf is not None: + for sensor in sensors_conf: + sensors.append( + RPiGPIOBinarySensor( + sensor[CONF_NAME], + sensor[CONF_PORT], + sensor[CONF_PULL_MODE], + sensor[CONF_BOUNCETIME], + sensor[CONF_INVERT_LOGIC], + sensor.get(CONF_UNIQUE_ID), + ) + ) + + add_entities(sensors, True) + return + pull_mode = config[CONF_PULL_MODE] bouncetime = config[CONF_BOUNCETIME] invert_logic = config[CONF_INVERT_LOGIC] - binary_sensors = [] ports = config[CONF_PORTS] for port_num, port_name in ports.items(): - binary_sensors.append( + sensors.append( RPiGPIOBinarySensor( port_name, port_num, pull_mode, bouncetime, invert_logic ) ) - add_entities(binary_sensors, True) + + add_entities(sensors, True) class RPiGPIOBinarySensor(BinarySensorEntity): @@ -69,9 +111,11 @@ class RPiGPIOBinarySensor(BinarySensorEntity): 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): + def __init__(self, name, port, pull_mode, bouncetime, invert_logic, unique_id=None): """Initialize the RPi binary sensor.""" - self._name = name or DEVICE_DEFAULT_NAME + self._attr_name = name or DEVICE_DEFAULT_NAME + self._attr_unique_id = unique_id + self._attr_should_poll = False self._port = port self._pull_mode = pull_mode self._bouncetime = bouncetime @@ -87,16 +131,6 @@ class RPiGPIOBinarySensor(BinarySensorEntity): edge_detect(self._port, edge_detected, self._bouncetime) - @property - def should_poll(self): - """No polling needed.""" - return False - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - @property def is_on(self): """Return the state of the entity.""" diff --git a/custom_components/rpi_gpio/cover.py b/custom_components/rpi_gpio/cover.py index ccaef13..dcd81b3 100644 --- a/custom_components/rpi_gpio/cover.py +++ b/custom_components/rpi_gpio/cover.py @@ -6,7 +6,7 @@ from time import sleep import voluptuous as vol from homeassistant.components.cover import PLATFORM_SCHEMA, CoverEntity -from homeassistant.const import CONF_COVERS, CONF_NAME +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 @@ -34,6 +34,7 @@ _COVERS_SCHEMA = vol.All( CONF_NAME: cv.string, CONF_RELAY_PIN: cv.positive_int, CONF_STATE_PIN: cv.positive_int, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) ], @@ -76,6 +77,7 @@ def setup_platform( relay_time, invert_state, invert_relay, + cover.get(CONF_UNIQUE_ID), ) ) add_entities(covers) @@ -93,9 +95,11 @@ class RPiGPIOCover(CoverEntity): relay_time, invert_state, invert_relay, + unique_id, ): """Initialize the cover.""" - self._name = name + self._attr_name = name + self._attr_unique_id = unique_id self._state = False self._relay_pin = relay_pin self._state_pin = state_pin @@ -107,11 +111,6 @@ class RPiGPIOCover(CoverEntity): setup_input(self._state_pin, self._state_pull_mode) write_output(self._relay_pin, 0 if self._invert_relay else 1) - @property - def name(self): - """Return the name of the cover if any.""" - return self._name - def update(self): """Update the state of the cover.""" self._state = read_input(self._state_pin) diff --git a/custom_components/rpi_gpio/switch.py b/custom_components/rpi_gpio/switch.py index c980be7..6a49872 100644 --- a/custom_components/rpi_gpio/switch.py +++ b/custom_components/rpi_gpio/switch.py @@ -4,7 +4,13 @@ from __future__ import annotations import voluptuous as vol from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity -from homeassistant.const import DEVICE_DEFAULT_NAME +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 @@ -19,16 +25,32 @@ CONF_INVERT_LOGIC = "invert_logic" DEFAULT_INVERT_LOGIC = False -_SWITCHES_SCHEMA = vol.Schema({cv.positive_int: cv.string}) +_SWITCHES_LEGACY_SCHEMA = vol.Schema({cv.positive_int: cv.string}) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( +_SWITCH_SCHEMA = vol.Schema( { - vol.Required(CONF_PORTS): _SWITCHES_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, } ) +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, @@ -38,37 +60,46 @@ def setup_platform( """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] - switches = [] ports = config[CONF_PORTS] for port, name in ports.items(): switches.append(RPiGPIOSwitch(name, port, invert_logic)) + add_entities(switches) class RPiGPIOSwitch(SwitchEntity): """Representation of a Raspberry Pi GPIO.""" - def __init__(self, name, port, invert_logic): + def __init__(self, name, port, invert_logic, unique_id=None): """Initialize the pin.""" - self._name = name or DEVICE_DEFAULT_NAME + 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) - @property - def name(self): - """Return the name of the switch.""" - return self._name - - @property - def should_poll(self): - """No polling needed.""" - return False - @property def is_on(self): """Return true if device is on."""