[pyscript] Dim lights with single push button

Hi,

I have created a pyscript solution to dim lights with a single pushbutton: as long as you keep pushing the button, the brightness slowly increases or decreases. When you release the button, the light stays at the brightness of that moment.

I use this script with:

But it should work with any dimmable light and any pushbutton that can be read as a binary input.

I couldn’t get pyscript’s @state_trigger working, and decided to use an ordinary automation rule and call the python function as a service. So, here’s my script:

directions = {}

@service
def increase_decrease(switch_id, lamp_id):
    """Increase or decrease the brightness as long as the button is pressed"""
    log.info(f"Increase/decrease - switch: {switch_id}, lamp: {lamp_id}")
    step = 16

    if not lamp_id in directions:
        directions[lamp_id] = True
    counter = 0

    current_state = state.get(switch_id)

    while current_state == "on" and counter < 1000:
        current_state = state.get(switch_id)
        current_brightness = int(state.get(f"{lamp_id}.brightness"))
        if current_brightness >= 254:
            directions[lamp_id] = False
        elif current_brightness <= 16:
            directions[lamp_id] = True
        log.debug(f"current state: {current_state}, current brightness: {current_brightness}, direction: {directions[lamp_id]}")

        change = step if directions[lamp_id] else -1 * step
        light.turn_on(entity_id=lamp_id, brightness=current_brightness+change)
        counter += 1
        task.sleep(0.2)
    current_brightness = int(state.get(f"{lamp_id}.brightness"))
    log.info(f"finished; brightness is now {current_brightness}.")

And here’s a snippet from my automations.yaml, so you can see how to call the service:

- id: '123456789'
  alias: Start press (sfeerverlichting)
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.shellyswitch25_xxxxxxxxxxxx_channel_1_input
    from: 'off'
    to: 'on'
  condition: []
  action:
  - service: pyscript.increase_decrease
    data:
      switch_id: binary_sensor.shellyswitch25_xxxxxxxxxxxx_channel_1_input
      lamp_id: light.sfeerverlichting
  mode: single

I’m still fiddling with different values of step and task.sleep to see what works best for me.

Let me know if you have ideas for improvement or if you have questions.

Best regards,
Bart

3 Likes

Additional notes:

  • It works with entity id’s, you can also use the entity id from a light group. (light.sfeerverlichting in my example is actually a group.)
  • It took me a while to figure out to which log pyscript writes. It turns out to be home-assistant.log. This one can be viewed via the UI at settings > logs, but I find it more convenient to connect via SSH and use the command:
    tail -f /config/home-assistant.log
    

thank you very much for the script, that is exactly I was looking for. After small adjustments works perfectly for my setup.

Thanks for letting me know! Would you mind sharing what type of hardware you are using and what adjustments you made? That info might be of help for others…

sure, I switch my lights using dmx but firstly HA sends instructions via tcp to dmx gateway using art-net protocol. I have used GitHub - jnimmo/hass-dmx: Home Assistant DMX over IP Integration for art-net integration.

Pushing buttons events are gathered from rs485 bus using mysensors library. They distinguish type of press, how many clicks happend or wheather it was long press and send this information over the bus (V_SCENE_ON). Number 10 means, long press started.

I had to apply fast-dirty boundaries to make adjustment more friendly, sorry.

directions = {}

@service
def increase_decrease(switch_id, lamp_id):
    """Increase or decrease the brightness as long as the button is pressed"""
    step = 4

    if not lamp_id in directions:
        directions[lamp_id] = True
    counter = 0
    directions[lamp_id] = not directions[lamp_id]

    current_action = int(state.get(switch_id + ".V_SCENE_ON"))
    log.info(f"Increase/decrease - switch: {switch_id}, lamp: {lamp_id}")

    while current_action == 10 and counter < 1000:
        current_action = int(state.get(switch_id + ".V_SCENE_ON"))
        current_brightness = int(state.get(f"{lamp_id}.dmx_values"))
        current_light_state = state.get(f"{lamp_id}")
        if current_light_state == "off":
            current_brightness = 45

        if current_brightness >= 180:
            directions[lamp_id] = False
        elif current_brightness <= 45:
            directions[lamp_id] = True

        change = step if directions[lamp_id] else -1 * step
        light.turn_on(entity_id=lamp_id, brightness=current_brightness+change, transition=0)
        counter += 1
        task.sleep(0.1)
    current_brightness = int(state.get(f"{lamp_id}.dmx_values"))
    log.info(f"finished; brightness is now {current_brightness}.")
1 Like

Nice! Thanks for sharing!

Hey Bart,
Thank you for sharing this. I’m bloody new to HA and wanted exactly this behaviour.
Right now I’m using it with Slashback’s “Short long double click” on the long press.
Quite a learning curve to get pyscript and everything running when you are not knowing your way around in a software.
Anyway, I can see the brightness slider moving in HA UI, but the light itself is not changing unless I release the button. I’m using ESPhome lights on a ESP01s board.
Would you know a way to publish the new brightness to the light itself while still pressing the button.
Cheers,
Klaus

Hi Klaus,

I am using this with Zigbee lights (using the ZHA integration). With Zigbee lights, the lights do change while the button is pressed. So this must be something specific to your ESPhome setup. I don’t have any experience with that, so I can’t help you with it.

Best,
Bart

Hallo Bart,

Do you think this will work with the following switch?

Thank you in advance :slight_smile:

Hi @Albastraoz,

That’s hard to say. It looks like this unit has an input and can switch a light on and off. However, it does not dim a light. So you’ll need a dimmable bulb at least. And it needs to be possible to decouple the button input from the switched output. I know Shelly modules can do that, but I don’t know about this unit. The documentation of the unit should give some clarity on this.

Good luck!
Bart

@bartkummel thanks for sharing this, I was looking how to do exactly the same thing.

Did you ever get the @state_trigger to work? I think I might have a solution as I was trying to do exactly the same for my Shelly devices. It looks like the trigger to use is binary_sensor.{name}_channel_n_input . However by default this is disabled by the integration, however if I enable it and and do the following I get the kwargs dumped to the logs

@state_trigger("binary_sensor.test_sw0_channel_1_input")
def sw0_channel_changed(**kwargs):
    log.info(f"sw0_channel_changed kwargs={kwargs}")

There is another alternative to detecting the press in the pyscript file without enabling the binary_sensors, and that is the following:

@event_trigger("shelly.click")
def shelly_clicked(**kwargs):
    log.info(f"shelly_clicked kwargs={kwargs}")

Here you can look at the click_type to determine if it was a short or long click

Edit: Note, the shelly.click method works, only if the device is set up as a momentary switch, where as the first would work if the switch are not set up as a momentary switch, this is due to Homeassistant not having binary sensors for momentary switch events.

@kingamajick Thanks for sharing! I have switched to another approach in the meantime, but this is undoubtedly very useful for other people using the combination of pyscript and Shelly!

Hey what approach did you switch to in the end, I’ve actually ran into a issue with trying to implement this which I’ve detailed here.