How to trigger a function in an edge driver from another device?

I’d like to trigger a function in a (custom) edge driver based on some form of user input. The driver is used by 3 energy monitor devices, and I want to “synchronize” all the report timings across these 3 devices. (I can get “close” based on manually updating setting preferences for each of the 3, but would like to have a more elegant solution if possible). If I can call a function to update parameters within the driver, that should accomplish this.

I could think of a couple of approaches, but don’t know what is feasible or how to proceed – so any pointers would be appreciated.

Approaches?

  • use a virtual momentary switch that, when pushed, causes the “reset” function to run for each device by their driver.
  • Create some virtual device with a user preference setting, which when updated causes the other driver’s sync function to get run
  • Virtual switch combined with a ST monitor routine that in turns “resets” timing on the 3 devices (not sure how to expose that function), and avoiding routines seems preferable

Create a custom capability with a single command. You can set up the capability presentation so it doesn’t show at all on the device and is only available as an automation action (or include it as a pushButton on the detail screen if you prefer). Set up your driver so that it does whatever’s required to reset the report timing for that device when the command is called.

To reset all three manually, create a scene that calls the reset command for each of the three devices, then just run the scene. Or schedule a routine that does that on whatever frequency you prefer.

So to check my understanding, add a custom capability to my existing driver for the energy monitor devices to cause the reset, and invoke that across all 3 physical devices (which use the common driver) via a scene? Or are you instead saying to create a new driver with the custom capability, and have my existing driver for device reset the device when that capability is invoked in the new driver?

(Sorry if I’m being obtuse… not a developer rather an “advanced” user challenged to get my stuff working in the post groovy world :grinning:)

This. The capability needs to be on the driver that the physical device is using. Below are high level steps for creating one.

2 Likes

I have a custom capability, platinummassive43262.reset, that might be a good template for you to start from. You can pull down the files that you need for the various steps in the post I linked by running the following in the CLI:

smartthings capabilities platinummassive43262.reset -j -o new_cap.json
smartthings capabilities:translations platinummassive43262.reset 1 en -j -o new_cap_translation.json
smartthings capabilities:presentation platinummassive43262.reset -j -o new_cap_presentation.json

That one has a single command, but it’s set up to render on the dashboard and detailview as a pushButton. You can delete the dashboard and detailview sections from the presentation if you don’t want the button to display there. The translation file controls the labels that you see in the app, so you can change “Reset” to however you want yours to be labeled.

Note that platinummassive43262 is my randomly assigned namespace. When you create your own capabilities, they’ll be in your own namespace.

1 Like

Are you writing all the drivers yourself? Are the devices all running the same driver?

If so why not write them as one driver? Then all devices can speak to each other within the driver!

If all devices are running under the same driver, the driver can trigger events across all devices it controls directly.

Or am I getting the wrong end of the stick?

I’m modifying stock ST drivers myself. Not sure how to answer your other questions exactly, so here’s some more context… I have 3 physical devices, two of one model and one of a different model. They are all supported by one main driver (one I’ve tweaked based on ST’s Zwave Electric Meter, adding fingerprints and preferences that I needed), but that driver calls (“requires”) two sub-drivers (one for each model). So I’m not sure if they are all already under “one” driver that can trigger events across all 3 devices.

My next stupid question then is, if they are already under the single driver, how do I address individually the devices from within the driver… currently the device’s param setup (which also happens to reset the report timing which I want to sync across all 3 devices) is handled by

local do_configure = function (self, device)

So I’m guessing to do so the"device" part of the call would replaced by a specific device ID?

You can get a list of devices from the driver like so…

local device_list = driver:get_devices()

You can then loop through the devices and do what ever you need

E.g

for _, d in ipairs(device_list) do
        your_function_to_do_something_to_each_device(d)
end

I often do things like the following to check what type of device it is, when I have multiple device types and I can use component names to figure out what type of device it is

if (d:component_exists("activitylogger")) then

If you have a look at my harmony driver or wiser driver, you’ll see real examples of where I’ve done this

Don’t mess with the configure lifecycle. It gets called initially when the device is joined but not again.

I personally wouldn’t try to cross talk between z-wave devices by hard coding it into the driver. Yes, it’s possible if they’re on the same driver as @lmullineux has described, but it’s liable to cause confusion in the future when it takes action on an unexpected device. That’s your choice though.

The custom capability needs a capability handler. If there are already capability handlers then it would add to that list (changing to the name of your capability):

  capability_handlers = {
     [capabilities["platinummassive43262.reset"]
.ID] = { 
       [capabilities["platinummassive43262.reset"]
.commands.reset.NAME] = reset_handler, 
     }, 
   },

Then define your function to handle when the command is called:

local function reset_handler(driver,device,command) 
   -- do something 
end

One concern with the custom capability approach is my understanding from ST that they are not supported in 3rd party integrations… I’m not sure if that impacts all data streams for the 3rd party tool ConstantGraph (which I’m testing as a replacement for my soon to die Grovestreams data logging), not clear why it would. I should probably give it a try and find out.

The current driver doesn’t have any capability handlers.

Glad I have at least two possible approaches with hints now; I’ll report back after a bit more research, hacking and testing.

Capabilities are the way to interact with devices in the new framework. Any data that comes out of the device or commands that go into it have to go through a capability.

You can use a stock capability if you find one that fits your needs. Most of the stock capabilities aren’t supported by 3rd party integrations though. The list of supported ones is usually just the extremely common - switch, contact, motion, etc.

Custom capabilities are supported by some 3rd parties like SharpTools, but most don’t.

So what happens if your device has a capability that a 3rd party doesn’t support? The 3rd party just doesn’t see that capability. It still handles switch, contact, motion, etc the same as it otherwise would.

2 Likes

You could do it with a standard capability, just add a reset button (using either momentary or a switch capability) that does what you need. Then use a scene to press the momentary on each device at the same time. As @philh30 said

1 Like

Got it working nicely with the stock momentary capability and a scene. Next minor cleanup is to change the provided button’s label to something meaningful, which I gather from other posts is possible albeit a bit involved. Thanks again for the pointers.

1 Like

You can’t change the label on a stock capability. This would require a custom capability.

1 Like

A while ago, we could do this using the Device presentation, there’s an example in the post below:

I’m not sure if with the latest changes this still works, could you help us confirm, @georgeh, please?

I’ve give it a shot later this week.

@nayelyz
So I did review that thread (again). The simple approach I have in mind is to:

  1. get the existing device-config presentation for the stock driver
  2. modify the label from that template
  3. create a new presentation and do whatever next is needed for my driver to use it (presumably referencing the presentationID provided as output from the smartthings presentation command.)

For step 1, I’m not sure where to find this. I think the smartthings presentation command can provide the template based on a presentationID, but I could not see where I find the existing presentationID for the stock driver.

The stock drivers don’t contain the VID property because the presentation is generated automatically, do you have a device installed using this driver?
If so, there are two ways of getting the device-config for you to modify it:

  1. With the profileId you can use the command below to generate the device-config based on the profile’s config.
smartthings presentation:device-config:generate profileId -j -o=deviceConfig.json

Note: You can also select the YAML format (-y -o=deviceConfig.yaml)

  1. With the presentationId, you can use the command below to get the original device-config used to create that specific presentation. This is super helpful when working with custom presentations because of the special configuration we can use:
smartthings presentation:device-config presentationId -j -o=deviceConfig.json

First attempt did not seem to work following those steps, the label in the app’s detailed view was unchanged. (Yes, I had a device installed using the driver). I used the first option to get the original device-config. I then modified the detail view and automations view to add the key and value. Note I am not entirely sure the “key” I parameter I used was correct (followed other examples for that).

{
    "dashboard": {
        "states": [
            {
                "component": "main",
                "capability": "powerMeter",
                "version": 1,
                "idx": 0,
                "group": "main",
                "values": [],
                "composite": false
            }
        ],
        "actions": [],
        "basicPlus": []
    },
    "detailView": [
        {
            "component": "main",
            "capability": "momentary",
            "version": 1,
            "values": [
                {
                    "key": "momentary.value",
                    "label":"Apply Settings"
                }
            ],
            "patch": []
        },
        {
            "component": "main",
            "capability": "powerMeter",
            "version": 1,
            "values": [],
            "patch": []
        },
        {
            "component": "main",
            "capability": "energyMeter",
            "version": 1,
            "values": [],
            "patch": []
        },
        {
            "component": "main",
            "capability": "refresh",
            "version": 1,
            "values": [],
            "patch": []
        }
    ],
    "automation": {
        "conditions": [
            {
                "component": "main",
                "capability": "powerMeter",
                "version": 1,
                "values": [],
                "patch": [],
                "exclusion": []
            },
            {
                "component": "main",
                "capability": "energyMeter",
                "version": 1,
                "values": [],
                "patch": [],
                "exclusion": []
            }
        ],
        "actions": [
            {
                "component": "main",
                "capability": "momentary",
                "version": 1,
                "values": [
                    {
                    "key": "momentary.value",
                    "label":"Apply Settings"
                    }
                ],
                "patch": [],
                "exclusion": []
            },
            {
                "component": "main",
                "capability": "energyMeter",
                "version": 1,
                "values": [],
                "patch": [],
                "exclusion": []
            },
            {
                "component": "main",
                "capability": "refresh",
                "version": 1,
                "values": [],
                "patch": [],
                "exclusion": []
            }
        ]
    },
    "type": "profile"
}

Next I ran the “create” command, and used the resulting vid & mnmn in the driver’s profile.yml (below), and repackaged/assigned/installed the new driver, then changed to using it in the app. No errors were seen in the logs during that process, but the value on the screen did not change (still the default “Standby” text for the stock momentary capability).

name: base-electric-meter
components:
- id: main
  capabilities:
  - id: momentary
    version: 1
  - id: powerMeter
    version: 1
  - id: energyMeter
    version: 1
  - id: refresh
    version: 1
  categories:
  - name: CurbPowerMeter
preferences:
  - name: powerReportInterval
    title: Power report interval (seconds)
    description: Time between power (w) reports in seconds
    required: false
    preferenceType: integer
    definition:
      minimum: 1
      maximum: 86400
      default: 300
  - name: energyReportInterval
    title: Energy report interval (seconds)
    description: Time between energy (kwHr) reports in seconds
    required: false
    preferenceType: integer
    definition:
      minimum: 1
      maximum: 86400
      default: 3600
metadata:
  vid: e830f1c4-eb5f-34a4-a38f-6024c6419a22
  mnmn: SmartThingsCommunity

As a check, I tried running the …presentation:generate tool using the same vid that I was provided at time of creation, and it barfed on the attempt. Perhaps that indicates some problem with my device-config.

C:\Users\grhay\SynologyDrive\ST_Driver_WIP\GRH_ST_EdgeDrivers-main\MyDriverWIP>smartthings presentation:device-config:generate e830f1c4-eb5f-34a4-a38f-6024c6419a22
    Error: Request failed with status code 500: {"requestId":"0CAAD803-442C-431E-A332-5DBC275990AC","error":{"code":"UnexpectedError","message":"Error generating device
    configuration","details":[]}}

One odd but unrelated thing I did notice I also want to ask about, but it seems unrelated, is that when I checked my devices to get their ID’s I noticed one of the 3 meters (#5 below) that are using this modified driver shows the “generic-sensor” name, whereas the other two meters (#3&4) using the same driver show “base-electric-meter”. This predates any changes I made for the label stuff, and all 3 devices are behaving similarly and show they are using the same driver in the app query… any ideas on cause or whether I need to fix that (&how)?


───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 #  Label                     Name                                             Type   Device Id
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 1  Family Room Multi-Sensor  motion-battery-temperature-illuminance-humidity  ZWAVE  ce196f6d-098f-45a1-b9cf-21fb329b1543
 2  Front porch multi-sensor  multi-functional-motion                          ZWAVE  9fb8511b-9ee8-42ef-b11a-fdf264c5234b
 3  MR Energy Meter           base-electric-meter                              ZWAVE  5fc34a44-72a1-4234-964a-ea0b8b42c81d
 4  Net (-MR) Energy Meter    base-electric-meter                              ZWAVE  f7ce5f1d-1e0b-44e0-86c5-4d77f7602e01
 5  Solar Energy Meter        generic-sensor                                   ZWAVE  c65654e3-701e-433d-997e-6da9b8b0a194
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

ok, first, sorry, I didn’t notice you were trying to change the label of the momentary capability. This is a special case because it only has one command (push) and no attributes. So, the key momentary.value doesn’t seem effective.
I already asked the team about this. I’ll let you know their comments.

About the devices, there are two properties each of them has, “name” and “label”. For Edge-based devices, the name used corresponds to the device profile’s name. The label should have the text you see in the app.
If you use the -j or -y flags in the command, you’ll see the complete details of all the devices or one by one if you add the device’s ID.

//all
smartthings devices -j
smartthings devices -y

//single device
smartthings devices deviceID -j
smartthings devices deviceID -y