Can't get contactSensor capability to run - "index a nil value" error

I am trying to use the contactSensor to work in a custom driver, but it cannot start.

I have:
capability_handlers = {
[capabilities.contactSensor.ID] = {
[capabilities.contactSensor.attributes.contact.NAME] = handle_contact_event,
[capabilities.contactSensor.commands.open.NAME] = handle_contact_event,
[capabilities.contactSensor.commands.close.NAME] = handle_contact_event

And get:
FATAL Gauge Edge Driver runtime error: [string “init.lua”]:40: attempt to index a nil value (field ‘open’)
stack traceback:
[string “init.lua”]:40: in local ‘init_driver’
[string “init.lua”]:50: in main chunk

Hi, @Meltinis

Do you have a physical contact sensor or you’re trying to create a virtual contact sensor?
If you have a physical contact sensor, which protocol does it use?

I ask because capability handlers are for capabilities that can receive commands from SmartThings to be sent to the device and this capability doesn’t have any commands configured in it because the status of the contact attribute comes from the physical device and if you want to change the current value you need to emit a capability event like:

device:emit_event(capabilities.contactSensor.contact.open())

You can see this stock capability’s configuration using this CLI command:

smartthings capabilities contactSensor -j
Remember to replace contactSensor with any other capability ID to see others.

And for physical devices, depending on the protocol, they have their own handlers. The SmartThings Lua libraries already offer default handlers that help you handle the messages from the device, you just need to register to them, for example, this is the SmartThings driver for a Zigbee contact-sensor:

The line highlighted is the one that is used to register to the default handlers.
You can also define your own handlers but it depends on which cluster/command class your device uses.

I am using a physical device the aqara contact sensor. I still get the error.

I have tried to use the zigbee-contact standard code from Github, but I can see I still get the same error when I incorporate my handler.

Are you including the same code you shared above in a subdriver?
Because it is incorrect, the command open doesn’t exist in the SmartThings capability ContactSensor (see here), it only has one attribute which is contact.
To handle the events of this device you need to select one of these options:

  1. Use the default handlers SmartThings Lua library offers by calling register_for_default_handlers as I mentioned above.
    You can download its code here if you want to see their configuration, the ones used in your hub correspond to its firmware version.
    For example, the default handler for this cluster in version 53 is found in this folder:

...\lua_libs-api_v10\st\zigbee\defaults\contactSensor_defaults.lua

In this case, the default handlers do everything for you and it’s suggested to reduce the driver’s size because you would be using already existing code.

  1. Define your own if you want to modify the default behavior.
    In this example, you can see how we defined custom zigbee_handlers which override the default ones even if you use the command register_for_default_handlers:
    SampleDrivers/st-multipurpose-sensor/src/init.lua at main · SmartThingsDevelopers/SampleDrivers · GitHub
    The purpose in that case was to intercept messages and act based on the current value of a device preference.

If the standard Zigbe-contact driver doesn’t include your device’s fingerprints, I would only include the fingerprints in fingerprints.yml (selecting a profile that matches the capabilities of the device) then, connect the device to it to see how it works because the base init.lua file (I mean this one) registers for the defaults so it might not need further configuration.
The need to add subdrivers is only in cases where the device requires special configuration.

The capability_handlers map is for defining capability command handlers whose job is to turn commands issued in SmartThings and defined in capabilities (e.g. on for the switch capability) into a protocol specific command (e.g. Zigbee) to be sent to the device.

So your [capabilities.contactSensor.attributes.contact.NAME] = handle_contact_event line is completely out of place here. Attribute handlers are defined in other sections that are protocol specific. So although it would appear to be syntactically correct, semantically it isn’t.

The specific error you are getting is that [capabilities.contactSensor.commands.open.NAME] = handle_contact_event requires the contactSensor capability to have an open command. It hasn’t so it is syntactically incorrect. Indeed Contact Sensors, in the SmartThings sense, do not have commands so there isn’t a correct way of expressing it.

Thanks - getting closer as it is now registering and I get a log - I do still get an error and it does not register a change from the sensor:

ERROR Rain Gauge Edge Driver .Rain thread encountered error: [string “st/dispatcher.lua”]:267: Error encountered while processing event for <ZigbeeDevice: 9741b9bd-275d-4c3f-8549-ea620d291c47 [0xD7A0] (.Rain)>:
arg1: init
“[string “init.lua”]:26: attempt to call a nil value (field ‘value’)”

Thanks - learning it slowly, so I get the problem with what I had.

I seem to have a hard time understanding the flow here - when and how the emit and commands get fired. I have been searching the documentation, but I seem to get the feeling that it is written for someone who knows this part. Can you help with some links or pointers?

Did you define your own handlers or you’re using custom ones? If the last, what do you have in line 26 of your init.lua file?

About this, @orangebucket can explain eloquently from his experience. I think we can review some concepts to make sure we’re on the same page:

  1. In the Zigbee protocol the capabilities/functionalities a device supports are called clusters. The Zigbee Alliance provides this document where we can find the details of each cluster
  2. Zigbee devices have something called reporting configuration which based on the cluster must receive certain parameters, but all include minimum and maximum reporting intervals. In the default handlers, this is already included.
  3. Based on #2 we know that the device reports its state every specific period and in the case of IASZone, when the status changes as well.
  4. In the driver, we must receive those Zigbee messages and send the status to SmartThings.
    • The Zigbee_handlers are for incoming (Rx) messages
    • Capability_handlers are for outgoing (Tx) messages
  5. So, first, you need to make sure the device is reporting its status correctly, you can identify it because you’ll see Rx messages of type “ReportAttribute”, with the cluster name/ID and its new status
  6. Then, if you’re using the defaults, the messages will be handled accordingly, if not, as shown in the sample, you get the new status as a parameter and you get the raw message (zb_rx) as the last one.
  7. Once you have the new status, you need to update the contactSensor capability which can be easily done with
capabilities.contactSensor.contact.open()

or

capabilities.contactSensor.contact.close()

In the logs, you’ll see an “emitting event” message with the capability’s ID and new value (among others) which means it’s sent from the driver to SmartThings.
In this case, this capability’s new status only comes from an event in the driver (eg. a change in the physical device or a forced event emission) but we cannot send open/close events to the physical device because the capability contactSensor and the corresponding cluster (IASZone) don’t receive/have commands

Amazing - thank you very much for this detailed walk-through. I will try using this to debug my setup and return if (probably when) something does not do as expected.