diff --git a/.coveragerc b/.coveragerc index 169b73b7899697e9cb9d57c62fc59046809d9d6a..b0d6a40c7f7fb39a8c161e69d94c38ad4718a5fe 100644 --- a/.coveragerc +++ b/.coveragerc @@ -499,6 +499,7 @@ omit = homeassistant/components/panasonic_bluray/media_player.py homeassistant/components/panasonic_viera/media_player.py homeassistant/components/pandora/media_player.py + homeassistant/components/pcal9535a/* homeassistant/components/pencom/switch.py homeassistant/components/philips_js/media_player.py homeassistant/components/pi_hole/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 0a02fbc53218d9eaf6d5a8df740c4dba188580fc..cb3d0817d59fea4b36e8044d675203c81127c974 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -225,6 +225,7 @@ homeassistant/components/oru/* @bvlaicu homeassistant/components/owlet/* @oblogic7 homeassistant/components/panel_custom/* @home-assistant/frontend homeassistant/components/panel_iframe/* @home-assistant/frontend +homeassistant/components/pcal9535a/* @Shulyaka homeassistant/components/persistent_notification/* @home-assistant/core homeassistant/components/philips_js/* @elupus homeassistant/components/pi_hole/* @fabaff @johnluetke diff --git a/homeassistant/components/pcal9535a/__init__.py b/homeassistant/components/pcal9535a/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fa1295939be2e156c35b2d328a8ca9f45a88fcc9 --- /dev/null +++ b/homeassistant/components/pcal9535a/__init__.py @@ -0,0 +1,3 @@ +"""Support for I2C PCAL9535A chip.""" + +DOMAIN = "pcal9535a" diff --git a/homeassistant/components/pcal9535a/binary_sensor.py b/homeassistant/components/pcal9535a/binary_sensor.py new file mode 100644 index 0000000000000000000000000000000000000000..fd4e92ccf0335d83cb4c1f22056ac8d9c703aa38 --- /dev/null +++ b/homeassistant/components/pcal9535a/binary_sensor.py @@ -0,0 +1,93 @@ +"""Support for binary sensor using I2C PCAL9535A chip.""" +import logging + +import voluptuous as vol +from pcal9535a import PCAL9535A + +from homeassistant.components.binary_sensor import BinarySensorDevice, PLATFORM_SCHEMA +from homeassistant.const import DEVICE_DEFAULT_NAME +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +CONF_INVERT_LOGIC = "invert_logic" +CONF_I2C_ADDRESS = "i2c_address" +CONF_I2C_BUS = "i2c_bus" +CONF_PINS = "pins" +CONF_PULL_MODE = "pull_mode" + +MODE_UP = "UP" +MODE_DOWN = "DOWN" +MODE_DISABLED = "DISABLED" + +DEFAULT_INVERT_LOGIC = False +DEFAULT_I2C_ADDRESS = 0x20 +DEFAULT_I2C_BUS = 1 +DEFAULT_PULL_MODE = MODE_DISABLED + +_SENSORS_SCHEMA = vol.Schema({cv.positive_int: cv.string}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_PINS): _SENSORS_SCHEMA, + vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, + vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): vol.All( + vol.Upper, vol.In([MODE_UP, MODE_DOWN, MODE_DISABLED]) + ), + vol.Optional(CONF_I2C_ADDRESS, default=DEFAULT_I2C_ADDRESS): vol.Coerce(int), + vol.Optional(CONF_I2C_BUS, default=DEFAULT_I2C_BUS): cv.positive_int, + } +) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the PCAL9535A binary sensors.""" + pull_mode = config[CONF_PULL_MODE] + invert_logic = config[CONF_INVERT_LOGIC] + i2c_address = config[CONF_I2C_ADDRESS] + bus = config[CONF_I2C_BUS] + + pcal = PCAL9535A(bus, i2c_address) + + binary_sensors = [] + pins = config[CONF_PINS] + + for pin_num, pin_name in pins.items(): + pin = pcal.get_pin(pin_num // 8, pin_num % 8) + binary_sensors.append( + PCAL9535ABinarySensor(pin_name, pin, pull_mode, invert_logic) + ) + + add_entities(binary_sensors, True) + + +class PCAL9535ABinarySensor(BinarySensorDevice): + """Represent a binary sensor that uses PCAL9535A.""" + + def __init__(self, name, pin, pull_mode, invert_logic): + """Initialize the PCAL9535A binary sensor.""" + self._name = name or DEVICE_DEFAULT_NAME + self._pin = pin + self._pin.input = True + self._pin.inverted = invert_logic + if pull_mode == "DISABLED": + self._pin.pullup = 0 + elif pull_mode == "DOWN": + self._pin.pullup = -1 + else: + self._pin.pullup = 1 + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def is_on(self): + """Return the cached state of the entity.""" + return self._state + + def update(self): + """Update the GPIO state.""" + self._state = self._pin.level diff --git a/homeassistant/components/pcal9535a/manifest.json b/homeassistant/components/pcal9535a/manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..e2fb140c7a91dc9e1fab3e47085d75137c6f21e7 --- /dev/null +++ b/homeassistant/components/pcal9535a/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pcal9535a", + "name": "PCAL9535A I/O Expander", + "documentation": "https://www.home-assistant.io/components/pcal9535a", + "requirements": [ + "pcal9535a==0.7" + ], + "dependencies": [], + "codeowners": ["@Shulyaka"] +} diff --git a/homeassistant/components/pcal9535a/switch.py b/homeassistant/components/pcal9535a/switch.py new file mode 100644 index 0000000000000000000000000000000000000000..faebce5d67e396efe4b93211cd858886b945cae0 --- /dev/null +++ b/homeassistant/components/pcal9535a/switch.py @@ -0,0 +1,102 @@ +"""Support for switch sensor using I2C PCAL9535A chip.""" +import logging + +import voluptuous as vol +from pcal9535a import PCAL9535A + +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import DEVICE_DEFAULT_NAME +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +CONF_INVERT_LOGIC = "invert_logic" +CONF_I2C_ADDRESS = "i2c_address" +CONF_I2C_BUS = "i2c_bus" +CONF_PINS = "pins" +CONF_STRENGTH = "strength" + +STRENGTH_025 = "0.25" +STRENGTH_050 = "0.5" +STRENGTH_075 = "0.75" +STRENGTH_100 = "1.0" + +DEFAULT_INVERT_LOGIC = False +DEFAULT_I2C_ADDRESS = 0x20 +DEFAULT_I2C_BUS = 1 +DEFAULT_STRENGTH = STRENGTH_100 + +_SWITCHES_SCHEMA = vol.Schema({cv.positive_int: cv.string}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_PINS): _SWITCHES_SCHEMA, + vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, + vol.Optional(CONF_STRENGTH, default=DEFAULT_STRENGTH): vol.In( + [STRENGTH_025, STRENGTH_050, STRENGTH_075, STRENGTH_100] + ), + vol.Optional(CONF_I2C_ADDRESS, default=DEFAULT_I2C_ADDRESS): vol.Coerce(int), + vol.Optional(CONF_I2C_BUS, default=DEFAULT_I2C_BUS): cv.positive_int, + } +) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the PCAL9535A devices.""" + invert_logic = config[CONF_INVERT_LOGIC] + i2c_address = config[CONF_I2C_ADDRESS] + bus = config[CONF_I2C_BUS] + + pcal = PCAL9535A(bus, i2c_address) + + switches = [] + pins = config[CONF_PINS] + for pin_num, pin_name in pins.items(): + pin = pcal.get_pin(pin_num // 8, pin_num % 8) + switches.append(PCAL9535ASwitch(pin_name, pin, invert_logic)) + + add_entities(switches) + + +class PCAL9535ASwitch(SwitchDevice): + """Representation of a PCAL9535A output pin.""" + + def __init__(self, name, pin, invert_logic): + """Initialize the pin.""" + self._name = name or DEVICE_DEFAULT_NAME + self._pin = pin + self._pin.inverted = invert_logic + self._pin.input = False + self._state = self._pin.level + + @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.""" + return self._state + + @property + def assumed_state(self): + """Return true if optimistic updates are used.""" + return True + + def turn_on(self, **kwargs): + """Turn the device on.""" + self._pin.level = True + self._state = True + self.schedule_update_ha_state() + + def turn_off(self, **kwargs): + """Turn the device off.""" + self._pin.level = False + self._state = False + self.schedule_update_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index a34191c4a5c197a84bccf4ffa5810d31864230e8..6f0b40230ededec73d40b15876f718a86f7c022f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -944,6 +944,9 @@ panacotta==0.1 # homeassistant.components.panasonic_viera panasonic_viera==0.3.2 +# homeassistant.components.pcal9535a +pcal9535a==0.7 + # homeassistant.components.dunehd pdunehd==1.3