diff --git a/.coveragerc b/.coveragerc index c8213378e91..8bf5509c126 100644 --- a/.coveragerc +++ b/.coveragerc @@ -157,6 +157,7 @@ omit = homeassistant/components/eight_sleep/* homeassistant/components/eliqonline/sensor.py homeassistant/components/elkm1/* + homeassistant/components/elv/switch.py homeassistant/components/emby/media_player.py homeassistant/components/emoncms/sensor.py homeassistant/components/emoncms_history/* diff --git a/CODEOWNERS b/CODEOWNERS index 3945feb927c..60703b8cf42 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -72,6 +72,7 @@ homeassistant/components/ecovacs/* @OverloadUT homeassistant/components/edp_redy/* @abmantis homeassistant/components/egardia/* @jeroenterheerdt homeassistant/components/eight_sleep/* @mezz64 +homeassistant/components/elv/* @majuss homeassistant/components/emby/* @mezz64 homeassistant/components/enigma2/* @fbradyirl homeassistant/components/enocean/* @bdurrer diff --git a/homeassistant/components/elv/manifest.json b/homeassistant/components/elv/manifest.json new file mode 100644 index 00000000000..4c9ed56352e --- /dev/null +++ b/homeassistant/components/elv/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "elv", + "name": "ELV PCA", + "documentation": "https://www.home-assistant.io/components/pca", + "dependencies": [], + "codeowners": ["@majuss"], + "requirements": ["pypca==0.0.4"] + } \ No newline at end of file diff --git a/homeassistant/components/elv/switch.py b/homeassistant/components/elv/switch.py new file mode 100644 index 00000000000..bd97d10cecf --- /dev/null +++ b/homeassistant/components/elv/switch.py @@ -0,0 +1,103 @@ +"""Support for PCA 301 smart switch.""" +import logging + +import voluptuous as vol + +from homeassistant.components.switch import ( + SwitchDevice, PLATFORM_SCHEMA, ATTR_CURRENT_POWER_W) +from homeassistant.const import ( + CONF_NAME, CONF_DEVICE, EVENT_HOMEASSISTANT_STOP) +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +ATTR_TOTAL_ENERGY_KWH = 'total_energy_kwh' + +DEFAULT_NAME = 'PCA 301' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_DEVICE): cv.string +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the PCA switch platform.""" + import pypca + from serial import SerialException + + name = config[CONF_NAME] + usb_device = config[CONF_DEVICE] + + try: + pca = pypca.PCA(usb_device) + pca.open() + entities = [SmartPlugSwitch(pca, device, name) + for device in pca.get_devices()] + add_entities(entities, True) + + except SerialException as exc: + _LOGGER.warning("Unable to open serial port: %s", exc) + return + + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, pca.close) + + pca.start_scan() + + +class SmartPlugSwitch(SwitchDevice): + """Representation of a PCA Smart Plug switch.""" + + def __init__(self, pca, device_id, name): + """Initialize the switch.""" + self._device_id = device_id + self._name = name + self._state = None + self._available = True + self._emeter_params = {} + self._pca = pca + + @property + def name(self): + """Return the name of the Smart Plug, if any.""" + return self._name + + @property + def available(self) -> bool: + """Return if switch is available.""" + return self._available + + @property + def is_on(self): + """Return true if switch is on.""" + return self._state + + def turn_on(self, **kwargs): + """Turn the switch on.""" + self._pca.turn_on(self._device_id) + + def turn_off(self, **kwargs): + """Turn the switch off.""" + self._pca.turn_off(self._device_id) + + @property + def device_state_attributes(self): + """Return the state attributes of the device.""" + return self._emeter_params + + def update(self): + """Update the PCA switch's state.""" + try: + self._emeter_params[ATTR_CURRENT_POWER_W] = "{:.1f}".format( + self._pca.get_current_power(self._device_id)) + self._emeter_params[ATTR_TOTAL_ENERGY_KWH] = "{:.2f}".format( + self._pca.get_total_consumption(self._device_id)) + + self._available = True + self._state = self._pca.get_state(self._device_id) + + except (OSError) as ex: + if self._available: + _LOGGER.warning( + "Could not read state for %s: %s", self.name, ex) + self._available = False diff --git a/requirements_all.txt b/requirements_all.txt index c8d6911e712..bf4c98191b7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1290,6 +1290,9 @@ pyowlet==1.0.2 # homeassistant.components.openweathermap pyowm==2.10.0 +# homeassistant.components.elv +pypca==0.0.4 + # homeassistant.components.lcn pypck==0.6.1