I’m using a few a few of these switches, im using the following custom component to allow dimming.
import voluptuous as vol
import asyncio
import logging
import homeassistant.helpers.config_validation as cv
from homeassistant.core import callback
from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_state_change
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_ENTITY_ID, ATTR_ENTITY_ID,
EVENT_STATE_CHANGED, SERVICE_TURN_ON,
SERVICE_TURN_OFF)
from homeassistant.components.zwave.const import (EVENT_SCENE_ACTIVATED,
ATTR_SCENE_ID)
from homeassistant.components.light import (is_on, ATTR_BRIGHTNESS,
ATTR_TRANSITION, DOMAIN)
STATE_OFF = 0
_LOGGER = logging.getLogger(__name__)
ICON = "mdi:debug-step-over"
# add öpggomg all around this place!
# change log level for my component!
CONF_DIMMERS = "dimmers"
CONF_START = "start"
CONF_NAME = "name"
CONF_STOP = "stop"
CONF_SCENE_ID = ATTR_SCENE_ID
CONF_DELAY = "delay"
CONF_STEP = "step"
CONF_LIGHTS = "lights"
CONF_ON = "on"
CONF_OFF = "off"
CONF_DEFAULT_BRIGHTNESS = "default_brightness"
CONF_BRIGHTEN = "brighten"
CONF_DARKEN = "darken"
# Tradfri has a max of 254 for some reason.
MAX_BRIGHTNESS = 254
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_DIMMERS): [{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_BRIGHTEN): {
vol.Required(CONF_START): {
vol.Required(CONF_SCENE_ID): cv.byte},
vol.Required(CONF_STOP): {
vol.Required(CONF_SCENE_ID): cv.byte}
},
vol.Required(CONF_DARKEN): {
vol.Required(CONF_START): {
vol.Required(CONF_SCENE_ID): cv.byte},
vol.Required(CONF_STOP): {
vol.Required(CONF_SCENE_ID): cv.byte}
},
vol.Required(CONF_ON): {
vol.Required(CONF_SCENE_ID): cv.byte},
vol.Required(CONF_OFF): {
vol.Required(CONF_SCENE_ID): cv.byte},
vol.Required(CONF_DEFAULT_BRIGHTNESS): cv.byte,
vol.Required(CONF_DELAY): cv.time_period,
vol.Required(CONF_STEP): cv.byte,
vol.Required(CONF_LIGHTS): cv.entity_ids
}]
})
@callback
@bind_hass
def async_turn_on(hass, entity_id=None, transition=None, brightness=None):
"""Turn all or specified light on."""
data = {
key: value for key, value in [
(ATTR_ENTITY_ID, entity_id),
(ATTR_TRANSITION, transition),
(ATTR_BRIGHTNESS, brightness),
] if value is not None
}
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data))
@callback
@bind_hass
def async_turn_off(hass, entity_id=None, transition=None):
"""Turn all or specified light off."""
data = {
key: value for key, value in [
(ATTR_ENTITY_ID, entity_id),
(ATTR_TRANSITION, transition),
] if value is not None
}
hass.async_add_job(hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, data))
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup the dimmer"""
_LOGGER.info("start async_setup_platform")
sensors = []
for dimmer in config.get(CONF_DIMMERS):
name = dimmer.get(CONF_NAME)
entity_id = dimmer.get(CONF_ENTITY_ID)
brighten = dimmer.get(CONF_BRIGHTEN)
start_brighten_scene_id = brighten.get(CONF_START).get(CONF_SCENE_ID)
stop_brighten_scene_id = brighten.get(CONF_STOP).get(CONF_SCENE_ID)
darken = dimmer.get(CONF_DARKEN)
start_darken_scene_id = darken.get(CONF_START).get(CONF_SCENE_ID)
stop_darken_scene_id = darken.get(CONF_STOP).get(CONF_SCENE_ID)
on_scene_id = dimmer.get(CONF_ON).get(CONF_SCENE_ID)
off_scene_id = dimmer.get(CONF_OFF).get(CONF_SCENE_ID)
default_brightness = dimmer.get(CONF_DEFAULT_BRIGHTNESS)
delay = dimmer.get(CONF_DELAY)
step = dimmer.get(CONF_STEP)
light_entity_ids = dimmer.get(CONF_LIGHTS)
sensor = Dimmer(hass, name, entity_id,
start_brighten_scene_id, stop_brighten_scene_id,
start_darken_scene_id, stop_darken_scene_id,
on_scene_id, off_scene_id, default_brightness,
delay, step, light_entity_ids)
sensors.append(sensor)
# For some reason the function below is never called with `yield from` in
# the code base?
async_add_devices(sensors)
class Dimmer(Entity):
def __init__(self, hass, name, entity_id,
start_brighten_scene_id, stop_brighten_scene_id,
start_darken_scene_id, stop_darken_scene_id,
on_scene_id, off_scene_id, default_brightness,
delay, step, light_entity_ids):
_LOGGER.info("creating dimmer")
self._hass = hass
self._name = name
self._entity_id = entity_id
self._brighten_loop_running = False
self._brighten_loop_task = None
self._start_brighten_scene_id = start_brighten_scene_id
self._stop_brighten_scene_id = stop_brighten_scene_id
self._darken_loop_running = False
self._darken_loop_task = None
self._start_darken_scene_id = start_darken_scene_id
self._stop_darken_scene_id = stop_darken_scene_id
self._on_scene_id = on_scene_id
self._off_scene_id = off_scene_id
self._default_brightness = default_brightness
self._delay = delay
self._step = step
self._light_entity_ids = light_entity_ids
# contains true or false when all are true trigger the next stage!
self._light_states = {}
_LOGGER.info("dimmer created")
def get_current_brightness(self):
_LOGGER.info("get_current_brightness")
total_brightness = 0
count = 0
for light_entity_id in self._light_entity_ids:
light_state = self._hass.states.get(light_entity_id)
if light_state.state != 'off':
brightness = light_state.attributes.get('brightness')
total_brightness += brightness
count += 1
if (count == 0):
return None
return total_brightness / count
def is_light_off(self):
for light_entity_id in self._light_entity_ids:
light_state = self._hass.states.get(light_entity_id)
if light_state.state != 'off':
return False
return True
@asyncio.coroutine
def async_added_to_hass(self):
_LOGGER.info("start listen")
self._hass.bus.async_listen(EVENT_SCENE_ACTIVATED, self.async_scene_activated)
async_track_state_change(self._hass, self._light_entity_ids,
self.async_light_state_changed_listener)
_LOGGER.info("listen setup")
@callback
def async_light_state_changed_listener(self, entity, old_state, new_state):
_LOGGER.info("async_light_state_changed_listener")
if not self.is_brighten_task_running() and not self.is_darken_task_running():
return
entity_id = new_state.domain + '.' + new_state.object_id
_LOGGER.info("light state changed")
self._light_states[entity_id] = True
for light_entity_id in self._light_entity_ids:
if not self._light_states[light_entity_id]:
_LOGGER.info(light_entity_id + " has not changed state yet!")
return
if self.is_brighten_task_running():
self.brighten_step()
elif self.is_darken_task_running():
self.darken_step()
@callback
def async_scene_activated(self, event):
_LOGGER.info("scene_activated")
scene_id = event.data.get(ATTR_SCENE_ID)
entity_id = event.data.get(ATTR_ENTITY_ID)
# _LOGGER.error("scene scene_id = " + str(scene_id) + ", entity_id: " + str(entity_id))
# _LOGGER.error("expected entity_id = " + self._entity_id)
# _LOGGER.info("expected start_brighten_scene_id = " + str(self._start_brighten_scene_id))
# _LOGGER.info("expected stop_brighten_scene_id = " + str(self._stop_brighten_scene_id))
# _LOGGER.info("expected start_darken_scene_id = " + str(self._start_darken_scene_id))
# _LOGGER.info("expected stop_darken_scene_id = " + str(self._stop_darken_scene_id))
is_start_brighten = (scene_id == self._start_brighten_scene_id and
entity_id == self._entity_id)
is_stop_brighten = (scene_id == self._stop_brighten_scene_id and
entity_id == self._entity_id)
is_start_darken = (scene_id == self._start_darken_scene_id and
entity_id == self._entity_id)
is_stop_darken = (scene_id == self._stop_darken_scene_id and
entity_id == self._entity_id)
is_on = (scene_id == self._on_scene_id and
entity_id == self._entity_id)
is_off = (scene_id == self._off_scene_id and
entity_id == self._entity_id)
# sanity check
if is_start_brighten and self.is_brighten_task_running():
raise Exception("Can't start brighten when its already running")
elif is_start_brighten and self.is_darken_task_running():
raise Exception("Can't start brighten when darken already is running")
elif is_start_darken and self.is_brighten_task_running():
raise Exception("Can't start darken when brighten already is running")
elif is_start_darken and self.is_darken_task_running():
raise Exception("Can't start darken when its already running")
# _LOGGER.info("is_start_brighten: " + str(is_start_brighten))
# _LOGGER.info("is_stop_brighten: " + str(is_stop_brighten))
# _LOGGER.info("is_start_darken: " + str(is_start_darken))
# _LOGGER.info("is_stop_darken: " + str(is_stop_darken))
# _LOGGER.info("is_on: " + str(is_on))
# _LOGGER.info("is_off: " + str(is_off))
if is_start_brighten and not self.is_brighten_task_running():
self.start_brighten_loop()
elif is_stop_brighten and self.is_brighten_task_running():
self.stop_brighten_loop()
elif is_start_darken and not self.is_darken_task_running():
self.start_darken_loop()
elif is_stop_darken and self.is_darken_task_running():
self.stop_darken_loop()
elif is_on and not self.is_brighten_task_running() and not self.is_darken_task_running():
if self.is_light_off():
self.turn_on(self._default_brightness)
else:
self.turn_on(MAX_BRIGHTNESS)
elif is_off and not self.is_brighten_task_running() and not self.is_darken_task_running():
self.turn_off()
def is_brighten_task_running(self):
return self._brighten_loop_running
def is_darken_task_running(self):
return self._darken_loop_running
def start_brighten_loop(self):
_LOGGER.info("start_brighten_loop")
self._brighten_loop_running = True
self.brighten_step()
def stop_brighten_loop(self):
_LOGGER.info("stop_brighten_loop")
self._brighten_loop_running = False
def brighten_step(self):
_LOGGER.info("brighten_step")
current_brightness = self.get_current_brightness()
if current_brightness is None:
current_brightness = 128
# check if any lights are off
all_on = True
for light_entity_id in self._light_entity_ids:
if is_on(self._hass, light_entity_id):
self._light_states[light_entity_id] = True
else:
self._light_states[light_entity_id] = False
all_on = False
if not all_on:
for light_entity_id in self._light_entity_ids:
if not self._light_states[light_entity_id]:
async_turn_on(self._hass, light_entity_id,
brightness=current_brightness)
elif current_brightness == MAX_BRIGHTNESS:
self.stop_brighten_loop()
else:
new_brightness = min(current_brightness + self._step, MAX_BRIGHTNESS)
for light_entity_id in self._light_entity_ids:
self._light_states[light_entity_id] = False
async_turn_on(self._hass, light_entity_id,
brightness=new_brightness,
transition=self._delay.total_seconds())
def turn_on(self, brightness):
_LOGGER.info("turn_on")
new_brightness = min(brightness, MAX_BRIGHTNESS)
for light_entity_id in self._light_entity_ids:
async_turn_on(self._hass, light_entity_id,
brightness=new_brightness,
transition=1)
def turn_off(self):
_LOGGER.info("turn_off")
for light_entity_id in self._light_entity_ids:
async_turn_off(self._hass, light_entity_id)
def start_darken_loop(self):
_LOGGER.info("start_darken_loop")
self._darken_loop_running = True
self.darken_step()
def stop_darken_loop(self):
_LOGGER.info("stop_darken_loop")
self._darken_loop_running = False
def darken_step(self):
_LOGGER.info("darken_step")
current_brightness = self.get_current_brightness()
if current_brightness is None:
_LOGGER.info("lights are already off")
return
new_brightness = max(current_brightness - self._step, 0)
all_on = True
for light_entity_id in self._light_entity_ids:
if is_on(self._hass, light_entity_id):
self._light_states[light_entity_id] = True
else:
self._light_states[light_entity_id] = False
all_on = False
if not all_on:
_LOGGER.info("not all lights are on turning the rest on")
for light_entity_id in self._light_entity_ids:
if not self._light_states[light_entity_id]:
async_turn_on(self._hass, light_entity_id,
brightness=current_brightness)
elif new_brightness == 0:
_LOGGER.info("target brightness is 0 turning all lights off")
self.stop_brighten_loop()
for light_entity_id in self._light_entity_ids:
self._light_states[light_entity_id] = False
async_turn_off(self._hass, light_entity_id)
else:
_LOGGER.info("lowering brightness from " + str(current_brightness) + " to " + str(new_brightness))
for light_entity_id in self._light_entity_ids:
self._light_states[light_entity_id] = False
async_turn_on(self._hass, light_entity_id,
brightness=new_brightness,
transition=self._delay.total_seconds())
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
"""Return the icon for the frontend."""
return ICON
@property
def state(self):
if self.is_brighten_task_running():
return "Brightening"
elif self.is_darken_task_running():
return "Darkening"
else:
return "Off"
@asyncio.coroutine
def shutdown(self):
_LOGGER.info("shutdown")
if self.is_brighten_task_running():
self.stop_darken_loop()
self._brighten_loop_task.cancel()
if self.is_darken_task_running():
self.stop_darken_loop()
self._darken_loop_task.cancel()
on tap to turn on the light half way, one more tap to go all the way.
keeping the button down to dim up or down.
I’m not that good with python so there might be some issues in the component but it seems to work!
I’m using it with tradfri bulbs.
with the following configuration.
- platform: dimmer
dimmers:
- name: living_room_wallswitch_1_left_switch
entity_id: zwave.living_room_wallswitch_1
brighten:
start:
scene_id: 13
stop:
scene_id: 15
darken:
start:
scene_id: 14
stop:
scene_id: 16
'on':
scene_id: 11
'off':
scene_id: 12
default_brightness: 128
delay:
milliseconds: 100
step: 10
lights:
- light.living_room_ceiling_1_left
- light.living_room_ceiling_1_middle
- light.living_room_ceiling_1_right
- name: living_room_wallswitch_1_right_switch
entity_id: zwave.living_room_wallswitch_1
brighten:
start:
scene_id: 23
stop:
scene_id: 25
darken:
start:
scene_id: 24
stop:
scene_id: 26
'on':
scene_id: 21
'off':
scene_id: 22
default_brightness: 128
delay:
milliseconds: 100
step: 10
lights:
- light.living_room_ceiling_2_left
- light.living_room_ceiling_2_middle
- light.living_room_ceiling_2_right