Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Lutron Homeworks component #18311

Merged
merged 10 commits into from Dec 21, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .coveragerc
Expand Up @@ -154,6 +154,9 @@ omit =
homeassistant/components/homematicip_cloud.py
homeassistant/components/*/homematicip_cloud.py

homeassistant/components/homeworks.py
homeassistant/components/*/homeworks.py

homeassistant/components/huawei_lte.py
homeassistant/components/*/huawei_lte.py

Expand Down
94 changes: 94 additions & 0 deletions homeassistant/components/binary_sensor/homeworks.py
@@ -0,0 +1,94 @@
"""Component for interfacing to Lutron Homeworks keypads.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.homeworks/
"""
import logging

import voluptuous as vol
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved

from homeassistant.core import callback
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.homeworks import (
HomeworksDevice, HOMEWORKS_CONTROLLER, ENTITY_SIGNAL)

DEPENDENCIES = ['homeworks']

_LOGGER = logging.getLogger(__name__)

EVENT_BUTTON_PRESSED = 'button_pressed'
CONF_KEYPADS = 'keypads'
CONF_ADDR = 'addr'
CONF_BUTTONS = 'buttons'

BUTTON_SCHEMA = vol.Schema({cv.positive_int: cv.string})
BUTTONS_SCHEMA = vol.Schema({
vol.Required(CONF_ADDR): cv.string,
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_BUTTONS): vol.All(cv.ensure_list, [BUTTON_SCHEMA])
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to keep all the config under the component and load platforms via discovery. See the rainmachine component for an example.

vol.Required(CONF_KEYPADS): vol.All(cv.ensure_list, [BUTTONS_SCHEMA])
})


def setup_platform(hass, config, add_entities, discover_info=None):
"""Set up the Homeworks keypads."""
controller = hass.data[HOMEWORKS_CONTROLLER]
devs = []
for keypad in config[CONF_KEYPADS]:
name = keypad[CONF_NAME]
addr = keypad[CONF_ADDR]
buttons = keypad[CONF_BUTTONS]
for button in buttons:
for num, title in button.items():
devname = '{}_{}'.format(name, title)
dev = HomeworksKeypad(controller, addr, num, devname)
devs.append(dev)
add_entities(devs, True)


class HomeworksKeypad(HomeworksDevice, BinarySensorDevice):
"""Homeworks Keypad."""

def __init__(self, controller, addr, num, name):
"""Create keypad with addr, num, and name."""
super().__init__(controller, addr, name)
self._num = num
self._state = None

async def async_def_added_to_hass(self):
"""Called when entity is added to hass."""
signal = ENTITY_SIGNAL.format(self._addr)
async_dispatcher_connect(
self.hass, signal, self._update_callback)

@property
def is_on(self):
"""Return state of the button."""
return self._state

@property
def device_state_attributes(self):
"""Return state attributes."""
return {"HomeworksAddress": self._addr,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return {"HomeworksAddress": self._addr,
return {'homeworks_address': self._addr,

"ButtonNumber": self._num}

@callback
def _update_callback(self, msg_type, data):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _update_callback(self, msg_type, data):
def _update_callback(self, msg_type, values):

"""Dispatch messages from the controller."""
from pyhomeworks.pyhomeworks import (
HW_BUTTON_PRESSED, HW_BUTTON_RELEASED)

msg_type, values = data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have accesst to msg_type as an argument. The dispatch helper can send any number of arguments to the target _update_callback. So we can remove this line.

if msg_type == HW_BUTTON_PRESSED and values[1] == self._num:
self._state = True
self.async_schedule_ha_state(True)
elif msg_type == HW_BUTTON_RELEASED and values[1] == self._num:
self._state = False
self.async_schedule_ha_state(True)
87 changes: 87 additions & 0 deletions homeassistant/components/homeworks.py
@@ -0,0 +1,87 @@
"""Component for interfacing to Lutron Homeworks Series 4 and 8 systems.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/homeworks/
"""
import logging

import voluptuous as vol
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved

from homeassistant.const import (
CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
dispatcher_send)

REQUIREMENTS = ['pyhomeworks==0.0.6']

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'homeworks'

HOMEWORKS_CONTROLLER = 'homeworks'
ENTITY_SIGNAL = 'homeworks_entity_{}'

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port,
}),
}, extra=vol.ALLOW_EXTRA)


def setup(hass, base_config):
"""Start Homeworks controller."""
from pyhomeworks.pyhomeworks import Homeworks

class HomeworksController(Homeworks):
"""Interface between HASS and Homeworks controller."""

def __init__(self, host, port):
"""Host and port of Lutron Homeworks controller."""
super().__init__(host, port, self.callback)

def callback(self, msg_type, values):
"""Dispatch state changes."""
_LOGGER.debug('callback: %s, %s', msg_type, values)
addr = values[0]
signal = ENTITY_SIGNAL.format(addr)
dispatcher_send(hass, signal, (msg_type, values))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send the arguments separately.

Suggested change
dispatcher_send(hass, signal, (msg_type, values))
dispatcher_send(hass, signal, msg_type, values)

_LOGGER.debug('callback returned')
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved

config = base_config.get(DOMAIN)
host = config[CONF_HOST]
port = config[CONF_PORT]

controller = HomeworksController(host, port)
hass.data[HOMEWORKS_CONTROLLER] = controller

def cleanup(event):
controller.close()

hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup)
return True


class HomeworksDevice():
"""Base class of a Homeworks device."""

def __init__(self, controller, addr, name):
"""Controller, address, and name of the device."""
self._addr = addr
self._name = name
self._controller = controller

def addr(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the property and just make this a regular instance attribute.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just remove this method. We don't seem to need it.

"""Device address."""
return self._addr

@property
def name(self):
"""Device name."""
return self._name

@property
def should_poll(self):
"""No need to poll."""
return False
116 changes: 116 additions & 0 deletions homeassistant/components/light/homeworks.py
@@ -0,0 +1,116 @@
"""Component for interfacing to Lutron Homeworks lights.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/light.homeworks/
"""
import logging

import voluptuous as vol
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'voluptuous as vol' imported but unused


from homeassistant.core import callback
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect)
from homeassistant.components.homeworks import (
HomeworksDevice, HOMEWORKS_CONTROLLER, ENTITY_SIGNAL)
from homeassistant.components.light import (
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, PLATFORM_SCHEMA)

DEPENDENCIES = ['homeworks']

_LOGGER = logging.getLogger(__name__)

FADE_RATE = 2.

CONF_DIMMERS = 'dimmers'
CONF_ADDR = 'addr'
CONF_RATE = 'rate'

CV_FADE_RATE = vol.All(vol.Coerce(float), vol.Range(min=0, max=20))

DIMMER_SCHEMA = vol.Schema({
vol.Required(CONF_ADDR): cv.string,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_RATE, default=FADE_RATE): CV_FADE_RATE,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_DIMMERS): vol.All(cv.ensure_list, [DIMMER_SCHEMA])
})


def setup_platform(hass, config, add_entities, discover_info=None):
"""Set up the Homeworks lights."""
controller = hass.data[HOMEWORKS_CONTROLLER]
devs = []
for dimmer in config.get(CONF_DIMMERS):
dev = HomeworksLight(controller, dimmer[CONF_ADDR],
dimmer[CONF_NAME], dimmer[CONF_RATE])
devs.append(dev)
add_entities(devs, True)


class HomeworksLight(HomeworksDevice, Light):
"""Homeworks Light."""

def __init__(self, controller, addr, name, rate):
"""Create device with Addr, name, and rate."""
super().__init__(controller, addr, name)
self._rate = rate
self._level = 0

async def async_added_to_hass(self):
"""Called when entity is added to hass."""
signal = ENTITY_SIGNAL.format(self._addr)
_LOGGER.debug('connecting %s', signal)
async_dispatcher_connect(
self.hass, signal, self._update_callback)
self._controller.request_dimmer_level(self._addr)

@property
def supported_features(self):
"""Supported features."""
return SUPPORT_BRIGHTNESS

def turn_on(self, **kwargs):
"""Turn on the light."""
if ATTR_BRIGHTNESS in kwargs:
self._set_brightness(kwargs[ATTR_BRIGHTNESS])
else:
self._set_brightness(255)

def turn_off(self, **kwargs):
"""Turn off the light."""
self._set_brightness(0)

@property
def brightness(self):
"""Control the brightness."""
return self._level

def _set_brightness(self, level):
"""Send the brightness level to the device."""
self._controller.fade_dim(
float((level*100.)/255.), self._rate,
0, self._addr)

@property
def device_state_attributes(self):
"""Supported attributes."""
return {'HomeworksAddress': self._addr}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return {'HomeworksAddress': self._addr}
return {'homeworks_address': self._addr}


@property
def is_on(self):
"""Is the light on/off."""
return self._level != 0

@callback
def _update_callback(self, data):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _update_callback(self, data):
def _update_callback(self, msg_type, values):

"""Process device specific messages."""
from pyhomeworks.pyhomeworks import HW_LIGHT_CHANGED

_LOGGER.debug('_update_callback %s', data)
msg_type, values = data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.

if msg_type == HW_LIGHT_CHANGED:
self._level = int((values[1] * 255.)/100.)
self.async_schedule_update_ha_state(True)
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions requirements_all.txt
Expand Up @@ -919,6 +919,11 @@ pyhiveapi==0.2.14
# homeassistant.components.homematic
pyhomematic==0.1.51

# homeassistant.components.homeworks
# homeassistant.components.binary_sensor.homeworks
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
# homeassistant.components.light.homeworks
pyhomeworks==0.0.1

# homeassistant.components.sensor.hydroquebec
pyhydroquebec==2.2.2

Expand Down