Lutron RadioRa2 not seeing SeeTouch Keypad button presses

I’m running Home Assistant 2023.7.2 in a standalone OS on a Dell Optiplex. I have a large Lutron RadioRa 2 install with two Main Repeaters, a bridge and ~150 devices. I have the Inclusive software and am Lutron certified.

I’ve read the integration doc. The Lutron integration with HA is working. I have all the devices, scenes, keypad LEDs, etc as entities. I can control devices from HA and can detect state changes of devices changed physically or by other integrations in HA.

I want to use the SeeTouch keypads to trigger automations. HA is not seeing the button presses. So the lutron_events are not firing. I deleted the integration and reloaded everything with no change.

There is a similar thread from a couple of years ago, the OP was experiencing something very similar, replies indicated their integrations worked fine, and there was no resolution to the problem.

I know the Lutron bridge is sending out the events when a button is pressed. I also have a Hubitat hub integrated with my Lutron bridge and the log tracks every “Button 3 pressed (physical)” event.

The HA log shows when the Lutron devices states change and also the keypad LED state changes. But the log shows nothing when the buttons are pressed. It’s as if the Lutron integration in HA is not listening for the button press messages.

In the RadioRA software I’ve tried naming the buttons, and not. I’ve assigned them to control scenes, and assigned them no action. HA NEVER sees the button presses.

I can do a kludgy work around and have Hubitat tell HA when a button is pressed, but I shouldn’t have to.

I don’t think this is a basic configuration problem because everything else is working, bidirectional.
And the Lutron bridge is sending out the activity messages.
Is there a core configuration file for the Lutron integration that might need editing? Is there a protocol or message format the bridge sends out that HA is not monitoring?

Pulling my hair out, thanks for any help.

Accessing the lutron files requires two-part key developer root access via port 22222

“components/lutron/init.py”

Here is the Hubitat log showing the Lutron Bridge activity (notes added):

Virtual switch OFF
dev:10412023-07-21 09:13:51.982 AMinfoHA Guest Mode Notifier was turned off <----Hubitat reset virtual switch

LEDs commanded ON
dev:9782023-07-21 09:13:50.233 AMinforcvd: DEVICE,28,83,9,1 <----LED 3 ON event commanded by HA
dev:9782023-07-21 09:13:50.135 AMinforcvd: DEVICE,32,83,9,1 <----LED 3 ON event commanded by HA

Virtual switch ON
dev:10412023-07-21 09:13:49.890 AMinfoHA Guest Mode Notifier was turned on <----work around Hubitat virtual switch to HA

Keypad 1 Button 3 pressed
dev:9912023-07-21 09:13:49.767 AMinfoFamily Room Keypad 1 button 3 was pushed [physical] <----button 3 pressed event interpreted by Hubitat - no scene attached to button
dev:9782023-07-21 09:13:48.584 AMinforcvd: DEVICE,32,3,4 <----button 3 released? event sent by Lutron Bridge
dev:9782023-07-21 09:13:47.619 AMinforcvd: DEVICE,32,3,3 <----button 3 pressed event sent by Lutron Bridge

Here is the HA log for the same period - NO receipt of keypad button press events:
HA Guest Mode Notifier turned off
9:13:51 AM - 1 hour ago
Family Room Keypad 2: Guests LED turned on triggered by automation Guest Mode Set triggered by state of HA Guest Mode Notifier
9:13:49 AM - 1 hour ago
Family Room Keypad 1: Guests LED turned on triggered by automation Guest Mode Set triggered by state of HA Guest Mode Notifier <---- HA commanded LED ON
9:13:49 AM - 1 hour ago
Guest Mode Flag turned on triggered by automation Guest Mode Set triggered by state of HA Guest Mode Notifier
9:13:49 AM - 1 hour ago
Guest Mode Set triggered by state of HA Guest Mode Notifier turned on <---- HA automation
9:13:49 AM - 1 hour ago - Traces
HA Guest Mode Notifier turned on <---- Hubitat virtual notifier
9:13:49 AM - 1 hour ago
<---- No Lutron event fired at 09:13:47.619
Internet Time changed to @676
9:13:26 AM - 1 hour ago

I have no good answer for you. I’m also a Hubitat user that uses some capabilities of Home Assistant. For whatever reason support for RadioRA 2 on Home Assistant is pretty thin with questions often going unanswered. As opposed to Hubitat where RadioRA 2 support is robust. FWIW I find that most lighting automation is usually easier and more straightforward within Hubitat and its apps.

1 Like

If I could find the file I might be able to determine why it isn’t listening to the messages from the bridge. The hubitat log shows a raw formatted message (device number, button number, on, off, pressed, released, etc.) And a plain language internal interpretation of the press. If I could see the format HA is using, it should be straightforward to edit it.

I recall rooting around in a raspberry pi on another project and there appeared to be two root directory structures. The hidden one had the backend files in it, but I can’t remember how I got into them.

ETA:
This is it: Debugging the Home Assistant Operating System | Home Assistant Developer Docs (home-assistant.io)

ETTA:

I’m in root access via port 2222 and used the find command - the usr/src/homeassistant directory is inside a folder with a long alpha-numeric name inside the mnt directory at the root level


binary_sensor
cover
light
scene
switch

The binary_sensor is the occupancy sensor, the other entities all work correctly.

There are multiple lutron folders in multiple aplha-numeric directories - i finally found the .py files and opened them with vi.

Here is the content of the init.py file
It looks like it “should” be listening for button presses, but no lutron_events are fired and there is nothing in the log.

"""Component for interacting with a Lutron RadioRA 2 system."""
import logging

from pylutron import Button, Lutron
import voluptuous as vol

from homeassistant.const import (
    ATTR_ID,
    CONF_HOST,
    CONF_PASSWORD,
    CONF_USERNAME,
    Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import slugify

DOMAIN = "lutron"

PLATFORMS = [
    Platform.LIGHT,
    Platform.COVER,
    Platform.SWITCH,
    Platform.SCENE,
    Platform.BINARY_SENSOR,
]

_LOGGER = logging.getLogger(__name__)

LUTRON_BUTTONS = "lutron_buttons"
LUTRON_CONTROLLER = "lutron_controller"
LUTRON_DEVICES = "lutron_devices"

# Attribute on events that indicates what action was taken with the button.
ATTR_ACTION = "action"
ATTR_FULL_ID = "full_id"
ATTR_UUID = "uuid"

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


def setup(hass: HomeAssistant, base_config: ConfigType) -> bool:
    """Set up the Lutron integration."""
    hass.data[LUTRON_BUTTONS] = []
    hass.data[LUTRON_CONTROLLER] = None
    hass.data[LUTRON_DEVICES] = {
        "light": [],
        "cover": [],
        "switch": [],
        "scene": [],
        "binary_sensor": [],
    }

    config = base_config[DOMAIN]
    hass.data[LUTRON_CONTROLLER] = Lutron(
        config[CONF_HOST], config[CONF_USERNAME], config[CONF_PASSWORD]
    )

    hass.data[LUTRON_CONTROLLER].load_xml_db()
    hass.data[LUTRON_CONTROLLER].connect()
    _LOGGER.info("Connected to main repeater at %s", config[CONF_HOST])

    # Sort our devices into types
    for area in hass.data[LUTRON_CONTROLLER].areas:
        for output in area.outputs:
            if output.type == "SYSTEM_SHADE":
                hass.data[LUTRON_DEVICES]["cover"].append((area.name, output))
            elif output.is_dimmable:
                hass.data[LUTRON_DEVICES]["light"].append((area.name, output))
            else:
                hass.data[LUTRON_DEVICES]["switch"].append((area.name, output))
        for keypad in area.keypads:
            for button in keypad.buttons:
                # If the button has a function assigned to it, add it as a scene
                if button.name != "Unknown Button" and button.button_type in (
                    "SingleAction",
                    "Toggle",
                    "SingleSceneRaiseLower",
                    "MasterRaiseLower",
                ):
                    # Associate an LED with a button if there is one
                    led = next(
                        (led for led in keypad.leds if led.number == button.number),
                        None,
                    )
                    hass.data[LUTRON_DEVICES]["scene"].append(
                        (area.name, keypad.name, button, led)
                    )

                hass.data[LUTRON_BUTTONS].append(
                    LutronButton(hass, area.name, keypad, button)
                )
        if area.occupancy_group is not None:
            hass.data[LUTRON_DEVICES]["binary_sensor"].append(
                (area.name, area.occupancy_group)
            )

    for platform in PLATFORMS:
        discovery.load_platform(hass, platform, DOMAIN, {}, base_config)
    return True


class LutronDevice(Entity):
    """Representation of a Lutron device entity."""

    _attr_should_poll = False

    def __init__(self, area_name, lutron_device, controller):
        """Initialize the device."""
        self._lutron_device = lutron_device
        self._controller = controller
        self._area_name = area_name

    async def async_added_to_hass(self) -> None:
        """Register callbacks."""
        self._lutron_device.subscribe(self._update_callback, None)

    def _update_callback(self, _device, _context, _event, _params):
        """Run when invoked by pylutron when the device state changes."""
        self.schedule_update_ha_state()

    @property
    def name(self) -> str:
        """Return the name of the device."""
        return f"{self._area_name} {self._lutron_device.name}"

    @property
    def unique_id(self):
        """Return a unique ID."""
        # Temporary fix for https://github.com/thecynic/pylutron/issues/70
        if self._lutron_device.uuid is None:
            return None
        return f"{self._controller.guid}_{self._lutron_device.uuid}"

class LutronButton:
    """Representation of a button on a Lutron keypad.

    This is responsible for firing events as keypad buttons are pressed
    (and possibly released, depending on the button type). It is not
    represented as an entity; it simply fires events.
    """

    def __init__(self, hass, area_name, keypad, button):
        """Register callback for activity on the button."""
        name = f"{keypad.name}: {button.name}"
        if button.name == "Unknown Button":
            name += f" {button.number}"
        self._hass = hass
        self._has_release_event = (
            button.button_type is not None and "RaiseLower" in button.button_type
        )
        self._id = slugify(name)
        self._keypad = keypad
        self._area_name = area_name
        self._button_name = button.name
        self._button = button
        self._event = "lutron_event"
        self._full_id = slugify(f"{area_name} {name}")
        self._uuid = button.uuid

        button.subscribe(self.button_callback, None)

    def button_callback(self, button, context, event, params):
        """Fire an event about a button being pressed or released."""
        # Events per button type:
        #   RaiseLower -> pressed/released
        #   SingleAction -> single
        action = None
        if self._has_release_event:
            if event == Button.Event.PRESSED:
                action = "pressed"
            else:
                action = "released"
        elif event == Button.Event.PRESSED:
            action = "single"

        if action:
            data = {
                ATTR_ID: self._id,
                ATTR_ACTION: action,
                ATTR_FULL_ID: self._full_id,
                ATTR_UUID: self._uuid,
            }
            self._hass.bus.fire(self._event, data)

Is this a typo?

In the initialization routine, it calls the internal class “LutronButton()” with 4 variables

“LutronButton(hass, area.name, keypad, button)”
It’s using area.name

In the class definition in the “init” area it is uing the format with an underscore
“class LutronButton:
def init(self, hass, area_name, keypad, button):”
It’s using area_name here

But it looks like the internal class parameters are using underscore names on the left and assigning them value from the dot formatted info on the right (except for the area name element)
“self._id = slugify(name)
self._keypad = keypad
self._area_name = area_name <—should the right side read area.name?
self._button_name = button.name
self._button = button
self._event = “lutron_event”
self._full_id = slugify(f”{area_name} {name}")
self._uuid = button.uuid"

Since the events are not firing - and entities are more intuitive, I think a better approach would be to add a button entity for each of the buttons. The Lutron bridge is sending button press messages, that’s clear from the Hubitat log.

The integration for the keypad LEDs is in the switch.py script

"""Support for Lutron switches."""
from __future__ import annotations

from typing import Any

from homeassistant.components.switch import SwitchEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from . import LUTRON_CONTROLLER, LUTRON_DEVICES, LutronDevice


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Lutron switches."""
    devs = []

    # Add Lutron Switches
    for area_name, device in hass.data[LUTRON_DEVICES]["switch"]:
        dev = LutronSwitch(area_name, device, hass.data[LUTRON_CONTROLLER])
        devs.append(dev)

    # Add the indicator LEDs for scenes (keypad buttons)
    for scene_data in hass.data[LUTRON_DEVICES]["scene"]:
        (area_name, keypad_name, scene, led) = scene_data
        if led is not None:
            led = LutronLed(
                area_name, keypad_name, scene, led, hass.data[LUTRON_CONTROLLER]
            )
            devs.append(led)

    add_entities(devs, True)

class LutronSwitch(LutronDevice, SwitchEntity):
    """Representation of a Lutron Switch."""

    def __init__(self, area_name, lutron_device, controller):
        """Initialize the switch."""
        self._prev_state = None
        super().__init__(area_name, lutron_device, controller)

    def turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on."""
        self._lutron_device.level = 100

    def turn_off(self, **kwargs: Any) -> None:
        """Turn the switch off."""
        self._lutron_device.level = 0

    @property
    def extra_state_attributes(self):
        """Return the state attributes."""
        return {"lutron_integration_id": self._lutron_device.id}

    @property
    def is_on(self):
        """Return true if device is on."""
        return self._lutron_device.last_level() > 0

    def update(self) -> None:
        """Call when forcing a refresh of the device."""
        if self._prev_state is None:
            self._prev_state = self._lutron_device.level > 0

class LutronLed(LutronDevice, SwitchEntity):
    """Representation of a Lutron Keypad LED."""

    def __init__(self, area_name, keypad_name, scene_device, led_device, controller):
        """Initialize the switch."""
        self._keypad_name = keypad_name
        self._scene_name = scene_device.name
        super().__init__(area_name, led_device, controller)

    def turn_on(self, **kwargs: Any) -> None:
        """Turn the LED on."""
        self._lutron_device.state = 1

    def turn_off(self, **kwargs: Any) -> None:
        """Turn the LED off."""
        self._lutron_device.state = 0

    @property
    def extra_state_attributes(self):
        """Return the state attributes."""
        return {
            "keypad": self._keypad_name,
            "scene": self._scene_name,
            "led": self._lutron_device.name,
        }

    @property
    def is_on(self):
        """Return true if device is on."""
        return self._lutron_device.last_state

    @property
    def name(self) -> str:
        """Return the name of the LED."""
        return f"{self._area_name} {self._keypad_name}: {self._scene_name} LED"

    def update(self) -> None:
        """Call when forcing a refresh of the device."""
        # The following property getter actually triggers an update in Lutron
        self._lutron_device.state  # pylint: disable=pointless-statement

I wonder if the implementation differences between Hubitat and HA are in the configuration.
In Hubitat you load in a configuration list which includes the device integration ID, device type and name.
So it can listen for the Lutron Hub messages related to the raw device integration ID number. The Elk M1 has a similar scheme.
In HA they are relying on automatically parsing the Repeater files in memory by names only. This might create a confusion or omission.
If we could manually configure the keypads with device IDs, HA could listen for button prep messages from those Device IDs specifically.

That might be the uuid: briefly mentioned in the docs, but the documentation/explanation of uuid: is sparse and there is no example presented for its use.

Maybe the recent changes to events will help.

Nope, still doesn’t work

I’m also struggling with the core “Lutron” (non Caseta) integration and using Ra2. I think possibly rather than posting here, it might be worth trying to open a bug/request on the Git page for the integration:

Thanks for the tip

Submitted
(1) Lutron RadioRa2 (integration lutron) not seeing SeeTouch Keypad button presses · Issue #101017 · home-assistant/core (github.com)

I have encountered the same issue with a RadioRA2 installation where keypad button presses are not reported as lutron_events.

For me it seems that the issue is related to button “released” events not being captured unless there is a preceding button “pressed” event.

Some keypad buttons (raise lower) report both whereas most programmed buttons in RA2 only broadcast on RF a released event.

(I have posted this to the git thread as well)

Just for clarity - If you log into the Ra2 hub yourself and read the telnet output, most all the buttons (Keypad, Pico, etc) transmit on both press and release. Whether the software platform reads those other messages or not is a different matter altogether. Top, middle, and lower buttons on Pico remotes will even trigger a “held” event in the Lutron telnet environment, if you hold the button on the pico down for 5 seconds.

It’s actually more information than the official integration protocol/spec would lead you to believe exists, in some instances.

I am on RadioRA2 and monitoring the telnet on my computer and most of my keypad buttons do not transmit a pressed event.
e.g. in my circumstance I have a table top keypad (RR-T10RL) with integration id of 9, pushing scene button (8) generates
~DEVICE,9,8,4 followed by all the individual control instructions to different dimmers.

Whereas pushing the lower button generates
~DEVICE,9,24,3
and releasing the button generates
~DEVICE,9,24,4
where 24 is the button and 3,4 is the button state.

I am also watching the 434 MHz on an SDR and can see that the keypad is not transmitting on pressed for scene buttons but is transmitting only on released.

Picos and the raise/lower on keypads transmit RF on both.

I think my issue is related to HA lutron_event not being captured on the release

New to HA and also have RA2 and would like to try and integrate it. You guys have any tips just to get lighting control into HA? This is the only thing I have been able to find:

HA did pickup that I have a Lutron Connect Bridge but not sure if that helps control the lights as the link above only talks about interfacing with the RA2 Main repeater.

You likely have “Scene Saver” aka “Save changes on button hold” enabled on your keypads. Turn it off (Lutron software) and your keypad should report the pushes as desired.