Adding support for Plum Lightpad (#16576)

* Adding basic Plum Lightpad support - https://plumlife.com/

* Used Const values
is_on is a bool

* Added LightpadPowerMeter Sensor to the plum_lightpad platform

* Moved to async setup, Introduced a PlumManager, events, subscription, Light and Power meter working

* Added PlumMotionSensor

* Added Glow Ring support

* Updated plum library and re-normalized

* set the glow-ring's icon

* Naming the glow ring

* Formatting and linting

* Cleaned up a number of linting issues.  Left a number of documentation warnings

* setup_platform migrated to async_setup_platform Plum discovery run as a job

* bumped plumlightpad version

* On shutdown disconnect the telnet session from each plum lightpad

* Cleanup & formatting. Worked on parallell cloud update

* Moved setup from async to non-async

* Utilize async_call_later from the helpers

* Cleanedup and linted, down to documentation & one #TODO

* Remove commented out debug lines

* Fixed Linting issues

* Remove TODO

* Updated comments & fixed Linting issues

* Added plumlightpad to requirements_all.txt

* Fixing imports with isort

* Added components to .coveragerc

* Added PLUM_DATA constant for accessing hass.data[PLUM_DATA]

* used dictionary syntax vs get(...) for config

* Linting needed an additonal line

* Fully async_setup now. removed @callback utilize bus events for detecting new devices found.

* Upgraded to plumlightpad 0.0.10

* Removed extra unused PLATFORM_SCHEMA declarations

* Moved listener attachment to `async_added_to_hass` and removed unused properties & device_state_attributes

* Utilized Discovery when devices were located

* Linting and cleanup

* used `hass.async_create_task` instead of `hass.async_add_job` per Martin

* Removed redundant criteria in if block

* Without discovery info, there is no need to setup

* Better state management and async on/off for Glow Ring

* renamed async_set_config back to set_config, fixed cleanup callback and Plum Initialization

* Fixed flake8 linting issues

* plumlightpad package update

* Add 'motion' device_class to Motion Sensor

* Fixed last known Linting issue

* let homeassistant handle setting the brightness state

* String formatting vs concatenation

* use shared aiohttp session from homeassistant

* Updating to use new formatting style

* looks like @cleanup isn't neccesary

* ditch the serial awaits

* Ensure async_add_entities is only called once per async_setup_platform

* Creating tasks to wait for vs coroutines

* Remove unused white component in the GlowRing

* Used local variables for GlowRing colors & added a setter for the hs_color property to keep the values in sync

* Linted and added docstring

* Update the documentation path to point to the component page

* Removed the extra sensor and binary_sensor platforms as requested. (To be added in later PRs)

* Update plum_lightpad.py

* Update plum_lightpad.py
This commit is contained in:
Colin Harrington 2018-12-14 07:42:04 -06:00 committed by Paulus Schoutsen
parent 027920ff52
commit 9c62149b00
4 changed files with 267 additions and 0 deletions

View file

@ -265,6 +265,9 @@ omit =
homeassistant/components/openuv/__init__.py
homeassistant/components/*/openuv.py
homeassistant/components/plum_lightpad.py
homeassistant/components/*/plum_lightpad.py
homeassistant/components/pilight.py
homeassistant/components/*/pilight.py

View file

@ -0,0 +1,185 @@
"""
Support for Plum Lightpad switches.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/light.plum_lightpad/
"""
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light)
from homeassistant.components.plum_lightpad import PLUM_DATA
import homeassistant.util.color as color_util
DEPENDENCIES = ['plum_lightpad']
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Initialize the Plum Lightpad Light and GlowRing."""
if discovery_info is None:
return
plum = hass.data[PLUM_DATA]
entities = []
if 'lpid' in discovery_info:
lightpad = plum.get_lightpad(discovery_info['lpid'])
entities.append(GlowRing(lightpad=lightpad))
if 'llid' in discovery_info:
logical_load = plum.get_load(discovery_info['llid'])
entities.append(PlumLight(load=logical_load))
if entities:
async_add_entities(entities)
class PlumLight(Light):
"""Represenation of a Plum Lightpad dimmer."""
def __init__(self, load):
"""Initialize the light."""
self._load = load
self._brightness = load.level
async def async_added_to_hass(self):
"""Subscribe to dimmerchange events."""
self._load.add_event_listener('dimmerchange', self.dimmerchange)
def dimmerchange(self, event):
"""Change event handler updating the brightness."""
self._brightness = event['level']
self.schedule_update_ha_state()
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the name of the switch if any."""
return self._load.name
@property
def brightness(self) -> int:
"""Return the brightness of this switch between 0..255."""
return self._brightness
@property
def is_on(self) -> bool:
"""Return true if light is on."""
return self._brightness > 0
@property
def supported_features(self):
"""Flag supported features."""
if self._load.dimmable:
return SUPPORT_BRIGHTNESS
return None
async def async_turn_on(self, **kwargs):
"""Turn the light on."""
if ATTR_BRIGHTNESS in kwargs:
await self._load.turn_on(kwargs[ATTR_BRIGHTNESS])
else:
await self._load.turn_on()
async def async_turn_off(self, **kwargs):
"""Turn the light off."""
await self._load.turn_off()
class GlowRing(Light):
"""Represenation of a Plum Lightpad dimmer glow ring."""
def __init__(self, lightpad):
"""Initialize the light."""
self._lightpad = lightpad
self._name = '{} Glow Ring'.format(lightpad.friendly_name)
self._state = lightpad.glow_enabled
self._brightness = lightpad.glow_intensity * 255.0
self._red = lightpad.glow_color['red']
self._green = lightpad.glow_color['green']
self._blue = lightpad.glow_color['blue']
async def async_added_to_hass(self):
"""Subscribe to configchange events."""
self._lightpad.add_event_listener('configchange',
self.configchange_event)
def configchange_event(self, event):
"""Handle Configuration change event."""
config = event['changes']
self._state = config['glowEnabled']
self._brightness = config['glowIntensity'] * 255.0
self._red = config['glowColor']['red']
self._green = config['glowColor']['green']
self._blue = config['glowColor']['blue']
self.schedule_update_ha_state()
@property
def hs_color(self):
"""Return the hue and saturation color value [float, float]."""
return color_util.color_RGB_to_hs(self._red, self._green, self._blue)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the name of the switch if any."""
return self._name
@property
def brightness(self) -> int:
"""Return the brightness of this switch between 0..255."""
return self._brightness
@property
def glow_intensity(self):
"""Brightness in float form."""
return self._brightness / 255.0
@property
def is_on(self) -> bool:
"""Return true if light is on."""
return self._state
@property
def icon(self):
"""Return the crop-portait icon representing the glow ring."""
return 'mdi:crop-portrait'
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR
async def async_turn_on(self, **kwargs):
"""Turn the light on."""
if ATTR_BRIGHTNESS in kwargs:
await self._lightpad.set_config(
{"glowIntensity": kwargs[ATTR_BRIGHTNESS]})
elif ATTR_HS_COLOR in kwargs:
hs_color = kwargs[ATTR_HS_COLOR]
red, green, blue = color_util.color_hs_to_RGB(*hs_color)
await self._lightpad.set_glow_color(red, green, blue, 0)
else:
await self._lightpad.set_config({"glowEnabled": True})
async def async_turn_off(self, **kwargs):
"""Turn the light off."""
if ATTR_BRIGHTNESS in kwargs:
await self._lightpad.set_config(
{"glowIntensity": kwargs[ATTR_BRIGHTNESS]})
else:
await self._lightpad.set_config(
{"glowEnabled": False})

View file

@ -0,0 +1,76 @@
"""
Support for Plum Lightpad switches.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/plum_lightpad
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['plumlightpad==0.0.11']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'plum_lightpad'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
PLUM_DATA = 'plum'
async def async_setup(hass, config):
"""Plum Lightpad Platform initialization."""
from plumlightpad import Plum
conf = config[DOMAIN]
plum = Plum(conf[CONF_USERNAME], conf[CONF_PASSWORD])
hass.data[PLUM_DATA] = plum
def cleanup(event):
"""Clean up resources."""
plum.cleanup()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, cleanup)
cloud_web_sesison = async_get_clientsession(hass, verify_ssl=True)
await plum.loadCloudData(cloud_web_sesison)
async def new_load(device):
"""Load light and sensor platforms when LogicalLoad is detected."""
await asyncio.wait([
hass.async_create_task(
discovery.async_load_platform(
hass, 'light', DOMAIN,
discovered=device, hass_config=conf))
])
async def new_lightpad(device):
"""Load light and binary sensor platforms when Lightpad detected."""
await asyncio.wait([
hass.async_create_task(
discovery.async_load_platform(
hass, 'light', DOMAIN,
discovered=device, hass_config=conf))
])
device_web_session = async_get_clientsession(hass, verify_ssl=False)
hass.async_create_task(
plum.discover(hass.loop,
loadListener=new_load, lightpadListener=new_lightpad,
websession=device_web_session))
return True

View file

@ -767,6 +767,9 @@ pizzapi==0.0.3
# homeassistant.components.sensor.plex
plexapi==3.0.6
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11
# homeassistant.components.sensor.mhz19
# homeassistant.components.sensor.serial_pm
pmsensor==0.4