Pico Link — A Controller for Lutron Pico Remotes

Pico Link — A Controller for Lutron Pico Remotes

UPDATE: v1.0 Introduces full UI configuration for this integration.

This project provides a flexible way to use Lutron Caseta Pico remotes (including the newer Paddle Pico) as general-purpose controllers inside Home Assistant. The integration listens to lutron_caseta_button_event and translates presses, holds, and releases into more predictable, domain-aware actions.


What the Integration Does

The goal of Pico Link is to take the raw button events exposed by the Lutron Caseta integration and provide a structured, deterministic control model. It handles the timing, ramping, and action decisions so YAML configuration can remain concise.

Key capabilities

  • Tap, hold, and release handling
  • Ramping (brightness, volume, cover travel)
  • Brightness- and volume-step logic
  • Fan speed stepping
  • Optional middle-button override for 5-button models
  • Support for one or multiple entities per device
  • Auto-detection of Pico layout (5-button, 4-button, 2-button, paddle)

How Configuration Works

All configuration is done in configuration.yaml instead of using a UI flow.
The structure is intentionally simple:

pico_link:
  defaults:
    step_pct: 10
    hold_time_ms: 300

  devices:
    - device_id: "12ab34cd56ef78..." (or name: Kitchen Lights Pico)
      type: 3BRL
      lights:
        - light.kitchen_lights
      middle_button:
        - action: light.turn_on
          data:
            color_name: red
          target:
            entity_id:
              - light.kitchen_lights

Defaults Layering

When building the final configuration for a device, the integration merges:

  1. Hardcoded dataclass defaults
  2. Integration level defaults (global)
  3. Device-level overrides

This reduces duplication and ensures consistent behavior across devices unless intentionally changed.


Domain Behavior Overview

The integration adapts actions based on the domain assigned to the device:

Lights

  • Tap on/off
  • Tap raise/lower → step brightness
  • Hold raise/lower → ramp brightness

Fans

  • Tap on/off
  • Tap raise/lower → step speed

Covers

  • Tap on → open
  • Tap off → close
  • Tap on/off while moving → stop
  • Tap middle → stop
  • Tap raise/lower → step position
  • Hold raise/lower → open/close until released

Media Players

  • Tap on → play/pause
  • Tap off → next track
  • Tap raise/lower → step volume

Execution Model

Internally, the integration uses:

  • Per-button press state tracking
  • Token-based hold lifecycle (prevents stray/overlapping tasks)
  • Async tasks for ramping/stepping
  • Automatic cancellation on release
  • Domain-aware action handlers

The design prioritizes consistency, determinism, and minimal latency from button press to action.


Where to Find the Code

Full source, documentation, examples, and ongoing changes are available here:

:backhand_index_pointing_right: https://github.com/smartqasa/pico-link

This integration is young. Issues and contributions are welcome.

4 Likes

EDIT: Turns out the Pico Link isn’t working at all for me. The apparent success from lights was because the Pico was mapped to control lights in the Lutron app. Removing that, and it’s not triggering the Pico Link automation in configuration.yaml at all.

The pico link controller is great. But I could use some help getting my integration working. I can control light on/off. But I can’t get it to command other non-light devices to power on/off.

Here’s the code. The light.turn_on / _off works. But the media player and remote commands don’t have any effect.

I’m using a Pico Paddle.

Can this be made to work? Thanks!


# pico link automations
pico_link:
  defaults:

  devices:
    # P2B Paddle — lights
    - name: Theater Switch
      type: P2B
      buttons: 
          on:
              actions:
                - action: remote.turn_on
                  metadata: {}
                  target:
                    entity_id: remote.jvc_projector
                  data: {}
                - action: media_player.turn_on
                  metadata: {}
                  target:
                    entity_id: media_player.marantz_av7702mkii
                  data: {}
                - action: light.turn_on
                  metadata: {}
                  target:
                    area_id: theater
                    data: {}
          off:
              actions:
                - action: remote.turn_off
                  metadata: {}
                  target:
                    entity_id: remote.jvc_projector
                  data: {}
                - action: media_player.turn_off
                  metadata: {}
                  target:
                    entity_id: media_player.marantz_av7702mkii
                  data: {}
                  mode: single
                - action: light.turn_off
                  metadata: {}
                  target:
                    area_id: theater
                    data: {}

I need pointers on the basic install. Pico Link doesn’t respond to anything.

The info says its files are to be installed to:
config/custom_components/pico_link/

But my Home Assistant install doesn’t have a config folder. Instead it’s installed here:
/homeassistant/custom_components/pico_link

Never mind. I couldn’t get Pico Link to work. I found an easier and native solution, at least for a simple Pico Paddle Switch:

In HA, go to Developer Tools, “subscribe” to this event and click the start listening button:
lutron_caseta_button_event

Press up to get that event info:

event_type: lutron_caseta_button_event
data:
  serial: 255912313
  type: PaddleSwitchPico
  button_number: 2
  leap_button_number: 0
  device_name: Switch
  device_id: ab38470d06c8319551eca64991fe5264
  area_name: SomeRoom
  button_type: "on"
  action: press
origin: LOCAL
time_fired: "2026-07-03T10:15:15.194623+00:00"
context:
  id: 01KFKQKCZTAUXENCFYQZ0J3EWT
  parent_id: null
  user_id: null

Create an Automation triggering on the device_id and button_number for the On and Off cases. Create a separate script if desired to separate the logic from the specific action.

alias: Pico Turn On
description: ""
triggers:
  - event_type: lutron_caseta_button_event
    event_data:
      action: press
      device_id: ab38470d06c8319551eca64991fe5264
    trigger: event
conditions:
  - condition: template
    value_template: "{{ trigger.event.data.button_number == 2 }}"
actions:
  - action: script.turn_on
    target:
      entity_id: script.pico_turn_on
mode: single

Repeat process for the Off button process.

Pico Link has had several updates since the original post, and the next major addition is now available for testing: an optional configuration UI in the 1.0 beta.

The recent stable releases added:

  • Tap, hold, and double-tap overrides for every supported button. Keep the normal controls or replace individual gestures with your own action sequences.
  • Configurable double-tap timing, with a 300 ms default. Only buttons with double-tap actions get the additional single-tap delay.
  • Shared Stop/middle-button actions, with separate defaults for tap, hold, and double tap that each Pico can opt into.
  • Automatic Pico layout detection. Specifying type is now optional; an explicit setting still takes precedence.
  • A minimum-brightness fix: raising brightness from off now starts at the configured minimum.

Those changes are included in stable v0.3.14. For a setup like the theater example discussed above, custom On/Off actions can now control lights, a receiver, and a projector from the same Pico.

The new UI

The beta adds a dedicated Pico Link workspace inside Home Assistant. It has a searchable remote list, an Add Pico button, shared defaults, and a visual button editor matching each supported Pico layout.

Select a remote, assign what it controls, then select a button and its Tap, Hold, or Double tap gesture. Separate tabs cover timing, device settings, and run behavior.

These screenshots use simulated remotes and sample settings.

Home Assistant’s script engine

The beta also runs custom sequences through Home Assistant’s script engine. Button actions can include conditions, delays, templates, loops, waits, and calls to existing scripts, using Home Assistant’s action editor.

Run behavior is configurable per Pico: single, restart, queued, or parallel. The beta defaults to single, so another custom gesture on that Pico is ignored while its current sequence is running. Choose restart when the newest gesture should take over, or parallel when overlapping sequences are wanted.

Shared defaults let you reuse timing and control settings, while individual Picos can override them. Shared Stop actions remain an explicit choice for each gesture.

YAML is still supported

Updating does not automatically migrate an existing YAML configuration. On first UI setup, Pico Link offers to import your remotes, actions, and defaults; you confirm the import and save before the UI takes over.

To keep using YAML, set config_method: yaml under pico_link. That explicit file setting takes precedence over saved UI settings. New UI installations need no YAML entry.

UI edits remain in a draft until Save changes, which validates the configuration and reloads Pico Link without restarting Home Assistant.

Trying the beta

The UI and script engine are available in v1.0.0b9. This beta requires Home Assistant 2026.4.0 or newer and the Lutron Caséta integration.

After installing the beta, restart Home Assistant and refresh your browser. Open Settings → Devices & services → Add integration → Pico Link to set up or import your configuration. Afterward, open the workspace from the Pico Link sidebar item or the integration’s Configure button.

The updated README covers setup, migration, button behavior, and configuration examples.

2 Likes

The UI update I shared above is now available in the stable Pico Link 1.0.1 release. It also adds native color cycling for the 3BRL Stop/middle button, with no extra script or helper needed.

You can build palettes of colors and white temperatures in the UI, with separate palettes for Tap, Hold, and Double tap:

  • For one Pico: assign its lights, open Button actions → Stop, choose the gesture, and set Behavior → Cycle light colors. The local palette appears for editing.
  • For several Picos: configure the palette and Cycle light colors action for that gesture under Shared defaults, then choose Use shared Stop action on each participating Pico. It uses the matching shared gesture’s action and palette.

Each gesture advances one entry, and each Pico/gesture remembers its own position across Home Assistant restarts. A hold advances once rather than continuously. Use white-temperature entries for tunable-white lights; color entries require color-capable lights.

Palette setup details and screenshot. Existing button behavior stays unchanged until you select color cycling and save.