Rotating brightness for lights triggered by events

Hi,

I have a regular lamp, which can be turned on and off by pressing a button. Additionally I can hold down the button, which at first dims the light step by step, and at the darkest point the direction flips and brightness is incremented.
To me this seems like a simple approach to control dimmable lights with just one button available (given it supports differentiating between a single press and a held down press, which mine does), and I would like to recreate this using the events of a button I have.

Here’s a simplistic Python-class which provides this functionality by calling its state-property as an example of what exactly I mean.

class Rotator(object):
    def __init__(self, start, end, step):
        self.start = start
        self.end = end
        self.step = step
        self.current = self.start
        self.direction = 1
    @property
    def state(self):
        if self.direction:
            if (self.current + self.step) <= self.end:
                self.current += self.step
            else:
                self.direction = 0
                self.current -= self.step
        else:
            if (self.current - self.step) >= self.start:
                self.current -= self.step
            else:
                self.direction = 1
                self.current += self.step
        return self.current

If you create an instance with tmp = Rotator(0,255,50), the state rotates like this: 0, 50, 100, 150, 200, 250, 200, 150, 100, 50, 0, 50, 100… etc…
So if this somehow would be available in HASS, a single press would toggle a light on and off, and keeping it pressed would set the brightness of a turned on light to the value return by the rotator with each event.

Is this already somehow possible and I just haven’t looked at the right place?

You could probably use this code in AppDaemon.

<@aimc appears from nowhere… :laughing:>

Seriously though, I can’t see why it wouldn’t work.

For Hass (AppDaemon or otherwise) you need to think in terms of events - for this to make sense in terms of a connected button signalling HASS to do something it would need to send out an event regularly while pressed to trigger any kind of automation. A single “I have been pressed for a long press” event can trigger a single response, which can differ form a short press (the Minimote can do this) but to continuously step brightness down you would need to get regular events. If that were the case you could probably do something similar to the above in AppDaemon yes :slight_smile:

1 Like

I’m aware of that (the event-requirements for this automation), but thanks for going into detail anyway. :slight_smile:

The button I use are HomeMatic buttons, which do exatly that. If pressed short, they send PRESS_SHORT. If pressend long, they send PRES_LONG. If you keep holding, they send PRESS_CONT in a short interval, and when you release they send PRESS_LONG_RELEASE.
So from the hardware side I’m good to go. Didn’t hink of AppDaemon though while having that idea. Thanks for the hint. I’ll try that out. :slight_smile:

Excellent - sound like you have what you need :slight_smile:

Thanks Andrew!

1 Like

Took a moment since I’ve never used AppDaemon before, but now it’s working like a charm. In case someone is curious and want’s to try it out:
The app (tailored for HomeMatic regarding the event that I’m listening for):

import appdaemon.appapi as appapi

class Rotator(appapi.AppDaemon):
    def initialize(self):
        self.start = int(self.args["start"])
        self.end = int(self.args["end"])
        self.step = int(self.args["step"])
        self.current = self.start
        self.direction = 1
        self.light = self.args["light"]
        self.listen_event(self.light_cb, self.args["event"], name=self.args["name"], param=self.args["param"], channel=int(self.args["channel"]))
        self.log("Initialized %i %i %i %s" % (self.start, self.end, self.step, self.light))

    def light_cb(self, *args):
        if self.direction:
            if (self.current + self.step) <= self.end:
                self.current += self.step
            else:
                self.direction = 0
                self.current = self.end
        else:
            if (self.current - self.step) >= self.start:
                self.current -= self.step
            else:
                self.direction = 1
                self.current = self.start
        if self.current == 0:
            self.call_service("light/turn_off", entity_id=self.light)
        else:
            self.call_service("light/turn_on", entity_id=self.light, brightness=str(self.current))

And the app-definition:

[rotator]
module = rotator
class = Rotator
start = 0
end = 255
step = 100
light = light.testlight
event = homematic.keypress
name = LEQ123456
param = PRESS_CONT
channel = 1

Turns out, that this is useful even if you don’t have buttons that differentiate between short and long presses. When using a bigger stepping, you can use any button to just switch between off, medium brightness, high brightness and back.
There may be room for improments, but for now this solves my issue. :slight_smile:

2 Likes

This is exactly what I need to dim a group of lights. Only problem is that I have no clue how to make this script work with my Fibaro Dimmer 2.

I have grouped a set of hue lights (entity name: group.woonkamer_lampen) in the living room and a light above the dining table.

The Fibaro Dimmer 2 is a dimmer with an S1 port and a S2 port.

  • The S1 port is turning on/off and dimming (hold switch button) my dining table light via a pulse-switch on the wall.
  • The S2 port is firing different scene id’s depending how you operate the wall switch.
    If you press the S2 switch once, it fires scene id 26
    If you hold the S2 switch it fires scene id 22
    if you release the S2 switch it fires scene id 23

Now I want to have it configured so if I hold S2 it runs danielperna84’s script and the group of light begin to cycle to the brightness levels until I release the wall switch.

I have been trying for hours now without any luck. Any help would be very much preciated.

Ow and please let me know if there is crucial information missing in my post. Thanks !

Since I don’t know how exactly your switches work I can only say, that my script requires repeated events. My switch sends about 5 PRESS_LONG events per second if I hold the button. My script won’t work with a switch that only sends a single event when held down. But I doubt any script could do that with just a single event, since there would be no indicator for how long the cycling should be done.
Of course if your switch repeats the events, then it should work if set up correctly.

Thanks for your reply. Ah I misunderstood then.

Looks like I can’t use your script after all. Bummer.

But thanks for the reply anyway!

Hello Daniel,

did you modified anything on your CCU, e.g CuxD, to receive the “PRESS_CONT” event, cause all wall switches I have, Dimmers, Switches, 6button and 2 Button wall devices does not transmitt PRESS_CONT Event.

No, I have the HM-Sen-MDIR-WM55, and there PRESS_CONT is available. No changes done to the CCU. Bump up the log-level for the homematic component and look at the logs. If I recall correctly there are devices where the event is called PRESS_LONG_CONT.

Ok, got my issue for not having “PRESS_CONT” as event…

if homematic buttons are secured for data transmission, a “PRESS_CONT” event will not be fired.

That’s good to know. Thanks for figuring that out. :slight_smile:

sure… once I changed transmission mode for that button in that device, from “secure” to standard, PRESS_CONT were fired.

Can you confirm, that your buttons at your HM-Sen-MDIR-WM55 is not in secure mode?

Okay,

I improved your script, @danielperna84 , to have a very smooth transistion with ability to release the button at desired brightness.

import appdaemon.appapi as appapi
import time
‘’’
rotator.py

Example for apps.yaml
Dimmbutton1:
module: rotator
class: Rotator
light: light.deviceName
start: ‘0’
step: ‘10’
end: ‘255’
delay: ‘1.5’
event: homematic.keypress
name: Taster Schlafzimmer
param: PRESS_CONT
channel: ‘1’

‘’’
class Rotator(appapi.AppDaemon):
def initialize(self):
self.start = int(self.args[“start”])
self.end = int(self.args[“end”])
self.step = int(self.args[“step”])
self.current = self.start
self.direction = 1
self.delay = float(self.args[“delay”])
self.light = self.args[“light”]
self.listen_event(self.light_cb, self.args[“event”], name=self.args[“name”], param=self.args[“param”], channel=int(self.args[“channel”]))
self.log(“Initialized %i %i %i %s” % (self.start, self.end, self.step, self.light))

def light_cb(self, *args):
    if self.direction:
        if (self.current + self.step) <= self.end:
            self.current += self.step
        else:
            self.direction = 0
            self.current = self.end
    else:
        if (self.current - self.step) >= self.start:
            self.current -= self.step
        else:
            self.direction = 1
            self.current = self.start
    if self.current == 0:
        self.call_service("light/turn_off", entity_id=self.light)
        self.log("switched light: {}".format(self.light))
    else:
        self.call_service("light/turn_on", entity_id=self.light, brightness=str(self.current))
        self.log("{} changed to brightness: {}".format(self.light, self.current))
    time.sleep(self.delay)
1 Like