DeviceGroupHelperRK (community library)

Summary

Name Value
Name DeviceGroupHelperRK
Version 0.0.3
Installs 191
License MIT
Author Rick Kaseguma rickkas7@rickkas7.com
URL https://github.com/rickkas7/DeviceGroupHelperRK
Repository https://github.com/rickkas7/DeviceGroupHelperRK.git
Download .tar.gz
All Versions 0.0.3, 0.0.2, 0.0.1

Retrieve a device's device group from the device using a webhook

Example Build Testing

Device OS Version:

This table is generated from an automated build. Success only indicates that the code compiled successfully.

Library Read Me

This content is provided by the library maintainer and has not been validated or approved.

DeviceGroupHelperRK

Retrieve a device's device group from the device using a webhook

This is useful when you have a product and are using the device groups feature. Normally, use this to group related devices and control firmware releases. However, you can use this technique to read the device groups on-device, which would allow you to make decisions in device firmware based on group membership.

  • You can choose when to update groups (manually, at startup, or periodically).
  • You can then either query whether the device is in a specific group using the previously cached group list. This is fast and does not require network access so you can use it in your code liberally.
  • Or, if you prefer, you can register a notification function that will call your function with an indication that the list was updated, and when individual groups are added or removed from the previous retrieval.

Requirements

  • This is only useful for product devices, as developer devices do not have device groups.

  • Because the device needs to subscribe to an event, the device must be claimed to an account. It can be a single account used for all devices, but it must be claimed. Unclaimed devices cannot subscribe to events.

  • A webhook is required, described below. The webhook needs to have a product access token in it.

  • Retrieving the group list will require at least two data operations (request and webhook response).

Getting an access token

Since you probably will want a product access token (that allows access to a specific product only) and also a non-expiring one, you will probably want to use an oAuth client and the command line to generate one.

  • In your product, open the Authentication tab. Make sure you've selected the one in your product, not in your developer account.

Authentication (image removed)

  • Use the New Client button to create a new oAuth client. Select Two-Legged Auth (Server). The Name is just for identifying it in the console, I entered DeviceGroup.

Authentication (image removed)

  • Note the Client ID and Client Secret in the next screen. You'll need them in the next step.

  • Use the Particle APU to create a non-expiring product bearer token:

curl https://api.particle.io/oauth/token -u "devicegroup-5835:3138afffffffffffffffffffffffffffffff7528" -d grant_type=client_credentials -d expires_in=0
  • Replace devicegroup-5835 with the Client ID you just created
  • Replace 3138afffffffffffffffffffffffffffffff7528 with the Client Secret

You should get back something like:

{"token_type":"bearer","access_token":"0a795effffffffffffffffffffffffffffff8b5b","expires_in":0}
  • Note the access_token 0a795effffffffffffffffffffffffffffff8b5b, you'll need that in the next step.

Creating the webhook

  • In the Integrations tab for your product, create a new Webhook.

  • Set the Event Name to be the event name you've configured in the library. The default is G52ES20Q_DeviceGroup. You do not need to change it from the default.

  • Set the URL. Be sure to change 7615 to the product ID of your product!

https://api.particle.io/v1/products/7615/devices/
  • Change Request Type to GET.

  • The Request Format should change the Query Parameters which is correct.

  • The checkbox Only the device that triggers the webhook should receive its response should be checked.

  • Click the disclosure triangle to expand the Advanced Settings.

Create Webhook (image removed)

  • In the Query Parameters select Custom and add access_token (case-sensitive, and note the underscore) and the value is what you got in the access_token field of the curl response.

  • Leave the *HTTP Basic Auth and HTTP Headers** fields empty.

  • The Response Topic should already be /hook-response/ and you can leave it set to that.

  • Edit the Response Template to be as follows. Be careful with the square and curly brackets; they must be exactly as shown.

{"groups":[]}
  • In version 0.0.2, there are other fields you can add to the response template:
{"groups":[],"name":"","product_id":,"development":,"notes":""}

You can also include a subset of these fields if you prefer. This can save data and is useful if you have very long device notes that would exceed the size of the publish with the other fields.

{"groups":[],"product_id":

Advanced Settings (image removed)

Device firmware

This is the code in the examples/1-simple directory:

#include "DeviceGroupHelperRK.h"

SerialLogHandler logHandler(LOG_LEVEL_TRACE);

SYSTEM_THREAD(ENABLED);

PRODUCT_ID(7615);   // Change this to your product ID!
PRODUCT_VERSION(1);


void setup() {
DeviceGroupHelper::instance()
.withRetrievalModeAtStart()
.setup();
}

void loop() {
DeviceGroupHelper::instance().loop();

if (DeviceGroupHelper::instance().isInGroup("dev")) {
static bool notified = false;
if (!notified) {
Log.info("is in group dev!");
notified = true;
}
}
}

Digging in

You need to include the library, such as by using the Particle: Install Library function in Particle Workbench or search the community libraries in the Web IDE for DeviceGroupHelperRK. Then you can include the header file

#include "DeviceGroupHelperRK.h"

This library is only useful for products, so you must update the PRODUCT_ID macro to match your product.

PRODUCT_ID(7615);   // Change this to your product ID!
PRODUCT_VERSION(1);

You must initialize the library in setup()! The following configuration retrieves the device groups at startup once connected to the cloud.

void setup() {
DeviceGroupHelper::instance()
.withRetrievalModeAtStart()
.setup();
}

Make sure you also call DeviceGroupHelper::instance().loop() from global loop()! If you don't call the setup and loop methods, the library will not work properly. You should call the loop frequently, preferably on every loop. It returns quickly if it does not have anything to do.

void loop() {
DeviceGroupHelper::instance().loop();
// ...

This code checks to see if the device belongs to a specific group and logs it if it is (once). You'd probably want to do something more useful here. isInGroup() is fast and efficient and you can call it frequently. It uses the previously retrieved value and does not access the network.

if (DeviceGroupHelper::instance().isInGroup("dev")) {
static bool notified = false;
if (!notified) {
Log.info("is in group dev!");
notified = true;
}
}
With callback

Alternatively, you can register a callback to be notified when group membership changes. This checks every 5 minutes for groups. You probably don't want to do it that frequently in real life.

void setup() {
DeviceGroupHelper::instance()
.withRetrievalModePeriodic(5min)
.withNotifyCallback(groupCallback)
.setup();
}

The groupCallbacks function in examples/2-notify looks like this. It only logs messages; you probably want to do something more useful here.

void groupCallback(DeviceGroupHelper::NotificationType notificationType, const char *group) {
switch(notificationType) {
case DeviceGroupHelper::NotificationType::UPDATED:
Log.info("updated groups");
break;

case DeviceGroupHelper::NotificationType::ADDED:
Log.info("added %s", group);
break;

case DeviceGroupHelper::NotificationType::REMOVED:
Log.info("removed %s", group);
break;
}
}

You can respond to the UPDATED notification if you want to know if the list has been refreshed.

You can also use the ADDED or REMOVED notification to know if the group membership changed. You also get ADDED calls on the first retrieval of the list.

Other fields

If you add other fields to your webhook, you can also use the accessors:

const char *getDeviceName() { return name; };

int getProductId() const { return product_id; };

String getDeviceNotes() const { return notes; };

bool getIsDevelopment() const { return development; };

These will return the values sent by the webhook response if you've included them in the response template.

See example 3-extra-fields for more information.

Version History

0.0.3 (2023-04-07)
  • Added withFunction() to allow the device groups to be set by function call as well as subscription
0.0.2 (2021-08-20)
  • Added optional feature to include the name, notes, development flag, and product_id
0.0.1 (2021-03-22)
  • Initial version

Browse Library Files