Smart App Multiple Device Subscriptions

Hi All.

I’ve been successfully developing my webhook smart app in Python.
All is working well outside of subscriptions.

I’m trying to set up multiple subscriptions to switches I select in my app.
From looking at examples and the smart things API docs, I cannot pass multiple device id’s in a single POST request for a DEVICE subscription.

Here’s the API snippet.

Here’s my device subscription function. Works perfectly when passing in a single device ID as per the API.

def switchsubscription(authtoken,component,id,AppID)

BaseURL = 'https://api.smartthings.com/v1/installedapps/'
headers = {'Authorization': 'Bearer '+authtoken}
EndURL = '/subscriptions'
#debug to print what is passed in
print('Heres the full request within the function......'+BaseURL+str(AppID)+EndURL)
datasub = {
            "sourceType": "DEVICE",
            "device":{
            "componentId": component, 
            "deviceId": id,
            "capability": "switch",
            "attribute": "switch",
            "stateChangeOnly": "true",
            "subscriptionName": "switch_subscription",
            "value": "*",
                        }
                    }
r = requests.post(BaseURL+str(AppID)+EndURL,headers=headers,json=datasub)
        #error checking for creation of subscription
print(r.status_code)
print('The below is the content for the fault code')
if r.status_code == 200:
        print('Individual Subscription successfully created')
else:
        print('Error Creating individual subscription')

This is called in the INSTALL AND UPDATE Lifecycle as per the Smart App Flow.
Ive tried calling it multiple times with multiple device ID’s as below but I keep getting a 409 error from the API after the first device subscription is successfully subscribed too.

Update Lifecycle Code with multiple subscription calls (Step 02)…

    elif (content['lifecycle'] == 'UPDATE'):

    print(content['lifecycle'])      

    print('The auth token is:' + str(content['updateData']['authToken']))
    print('The app id is:' + str(content['updateData']['installedApp']['installedAppId']))
    AppID = content['updateData']['installedApp']['installedAppId']  
    
    #Assign Token to variable
    authtoken = content['updateData']['authToken']
    BaseURL = 'https://api.smartthings.com/v1/installedapps/'
    headers = {'Authorization': 'Bearer '+authtoken}
    component = content['updateData']['installedApp']['config']['Switch'][0]['deviceConfig']['componentId']
    
    #assign the selected  sensors to a variable for ID
    id = content['updateData']['installedApp']['config']['Switch'][0]['deviceConfig']['deviceId']
    id1 = content['updateData']['installedApp']['config']['Switch'][1]['deviceConfig']['deviceId']
    id2 = content['updateData']['installedApp']['config']['Switch'][2]['deviceConfig']['deviceId']
    id3 = content['updateData']['installedApp']['config']['Switch'][3]['deviceConfig']['deviceId']
  
    
    print('Heres the full request for the delete......'+BaseURL+AppID+'/subscriptions')
    #Step 01 - Delete all subscriptions
    r = requests.delete(BaseURL+AppID+'/subscriptions',headers=headers)
    #Check if the delete sub done?
    print(r.content)
    if r.status_code == 200:
            print('Subscription successfully deleted')
    else:
            print('Error deleting subscription')            
    
    #Step 02 - Call subscription functions for multiple devices
    switchsubscription(authtoken,component,id,AppID)
    switchsubscription(authtoken,component,id1,AppID)
    switchsubscription(authtoken,component,id2,AppID)
    switchsubscription(authtoken,component,id3,AppID)

Here’s the response. 200 for the first subscription call (Switch[0]) but then a 409 (conflictingfor the other 3.

Note that I don’t want to subscribe to All devices or use a capability subscription.
Any help would really be appreciated as all I really need is subscriptions working for selected devices and im happy :).

Thanks in advance guys.

It appears you are trying to use the same subscription name multiple times. The docs indicate this needs to be unique for each subscription.

Tony.

That was it. Thanks a lot for your help. I just need to pass in a unique subscription name and it works.

My problem now is there is a limit rate on 15 sub requests per minute and a cap of 20 subs per smart app.

I would like to subscribe to a lot more than 20 devices so I am forced to use a CAPABILITY subscription which is now leading me to the error I’ve described here in this thread.

I’ve raised a ticket but I’m guessing it will be a long while before I get a reply. It seems adding the r:devices permission to the INITIALIZE lifecycle of my app causes it to crash entirely and I need this to add a CAPABILITY subscription. :frowning:

Does any smart things staff on the forum have some feedback on my issue with capability subscriptions?.

I’ve tried everything at this stage but continue to get this Smart Things error. It’s all I need to finish my app. Any help would be greatly appreciated.

I’ve rewritten most of the app and tried a few other things. Removing scopes, adding them but no matter what I cannot successfully add Switch/Motion/Contact or any Capability subscriptions to my smart app.

I’m trying to individually subscribe but get the expected 422 (limit exceeded) error when subscribing to too many individual switches.

Hoping that Smart things respond to my ticket soon ….

A ticket to whom? Samsung developer support (in your Samsung SmartThings Developer Workspace) is the only valid channel for new API (not SmartThings Support).

Thanks for the advice.

I’ve just followed the email from the error message that was generated on the smart app…

image

I’ll log a ticket now in the developer workspace.

That email address is listed for the benefit of your eventual users/customers. It is the consumer support address for SmartThings - not the developer support address.

Ok thanks. Lesson learned. I’ve logged the ticket in the developers workspace now.

1 Like

Keep us posted, please!

I know all the developers here are anxious to learn how responsive Samsung Support is going to be…

Thanks!

1 Like

So its been a month and 10 days since raising the support ticket but still no feedback for my issue.

I’m looking at the documentation again and to me it looks like the example for the CAPABILITY subscription is commented incorrectly because it gives indication about selecting an individual contact sensor but the entire purpose of a CAPABILITY subscription is so you don’t need to select an individual device.

Here’s the link to the documentation for subscriptions.

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const baseUrl = 'https://api.smartthing.com';

app.use(bodyParser.json());

// handle all incoming requests to our app
app.post('/', function(req, resp, err) {
  let evt = req.body;
  let lifecycle = evt.lifecycle;
  let res = null;

  switch(lifecycle) {
    case 'CONFIGURE':
      res = handleConfig(evt.configurationData);
      resp.json({statusCode: 200, configurationData: res});
      break;
    case 'INSTALL':
      handleInstall(evt.installData.installedApp, evt.installData.authToken);
      resp.json({statusCode: 200, installData: {}});
      break;

    // handle other lifecycles...

    default:
      console.log(`lifecycle ${lifecycle} not supported`);
  }
});

function handleConfig(configData) {
  if (!configData.config) {
    throw new Error('No config section set in request.');
  }
  let config = {};
  const phase = configData.phase;
  const pageId = configData.pageId;
  const settings = configData.config;
  switch (phase) {
    case 'INITIALIZE':
      config.initialize = createConfigInitializeSetting();
      break;
    case 'PAGE':
      config.page = createConfigPage(pageId, settings);
      break;
    default:
      throw new Error(`Unsupported config phase: ${phase}`);
      break;
  }
  return config;
}

function createConfigInitializeSetting() {
  return {
    name: 'Your app name',
    description: 'Some app description',
    id: 'app',
    permissions:['r:devices'], // Need permission to read all devices
    firstPageId: '1'
  }
}

/**
 * Creates a simple one page configuration screen where the user can
 * select a contact sensor device, and we will request read access to this
 * device.
 */
function createConfigPage(pageId, currentConfig) {
  if (pageId !== '1') {
    throw new Error(`Unsupported page name: ${pageId}`);
  }

  return {
    // some page info for other configuration needed
  };
}

/**
 * Once the user has selected the device and agreed to the requested
 * permissions, our app will create a subscription for the "open" value
 * of the "contact" attribute for the contact sensor.
 */
function handleInstall(installedApp, authToken) {
    let deviceConfig = installedApp.config.contactSensor[0].deviceConfig;
    createSubscription(deviceConfig);
}

function createSubscription(deviceConfig, authToken) {
  const path = `/installedapps/${installedApp.installedAppId}/subscriptions`;

  let subRequest = {
    sourceType: 'CAPABILITY',
    capability: {
      locationId: "76fa4215-f9f5-4532-897e-5207db0da124",
      capability: "contactSensor",
      attribute: "contact",
      value: "*",
      stateChangeOnly: true,
      subscriptionName: "all_contacts_sub"
    }
  };

  request.post({
    url: `${ baseUrl }${ path }`,
    json: true,
    body: subRequest,
    headers: {
      'Authorization': 'Bearer ' + authToken
    }
  },
  function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log('subscription created')
    } else {
      console.log('failed to created subscriptions');
      console.log(error);
    }
  });
}

/**
 * If the user has updated their configuration, for example they may
 * have selected a different contact sensor, we need to delete the
 * old subscriptions and create a new subscription.
 */
function handleUpdate(installedApp, authToken) {
  // no need to handle capability subscriptions in update.
}

Can anyone shed some light on this for me?. Is the commenting in this example wrong or am I completely misunderstanding what a CAPABILITY subscription is.

All I want to do is make sure my Smart App receives events from All my contact sensors and ALL my Switches so to me all I need is a CAPABILITY subscription for both which is not working due to the error my app returns as per above. sigh!

There are very few (if any?) folks here that are experienced with the new API.

This is puzzling because there are hundreds familiar with the classic Groovy API, and you’d think more people would be exploring the new one.

But if you’re not getting any response from Samsung Developer Support, that’s probably a sign that the new API is not yet mature enough for use.

It’s difficult for anyone here to answer a question that isn’t somewhat evident from the documentation, or hasn’t been worked out with Support, or does not have a Developer Evangelist / Advocate assigned… :confused:

1 Like

Thanks for your reply.

I naively thought that there was lots of experience on the new API as this is my first endeavor and didn’t realize that it’s still not widely adopted.

I’m thinking at this stage that this is defiantly an issue that I’m hoping my support ticket can help them fix and hence the lack of response. I’ve stopped all development on my application because of this which is a shame because I was enjoying working through the Smart App workflow.

Oh well. I’ll keep this thread alive when there’s any update and hopefully it helps future API adopters.

2 Likes

I think the permissions should be 'r:devices:’, not ‘r:devices’, you’re missing the :

Thanks a lot for your reply but unfortunately this does not work. When I make this change the app fails at the INITIALIZE step.

Also in the docs it shows explicitly to use r:devices without the additional :. See below.

Out of curiosity are you developing with a Capability Subscription currently working?.

Thanks in advance

I actually landed here because I wanted to create event subscriptions, but from what I’m seeing, you need the ‘w:installedapps’ permission per here: https://smartthings.developer.samsung.com/docs/api-ref/st-api.html#operation/saveSubscription but to me it’s impossible, because when registering the app, this is not even an option:

I don’t know how you were able to do it.

Also it is worth noting that it wasn’t just the colon I was referring to (like in the image above), it was colon start (asterisk), for some reason the forum blocks it.

Are you developing your smart app in python by any chance?.

What you are showing above are the permissions set in the App scope in the Developers Workspace which I have also enabled all.

In the app code , in the INITIALIZE lifecycle, the docs explicitly state that you also need to add “r:devices” to use a Capability subscription.

I can create individual “Device” subscriptions without any problems. For device descriptions I enable all the permissions in the app scope but don’t add anything else in the code.

I cannot create a Capability subscription because when I do add this line in the code the app crashes at the point where the configuration is finished and the permissions are verified.

Yes, python. This is what I have in the code:
if request_json[‘configurationData’][‘phase’] == ‘INITIALIZE’: # Process the initialize phase
body = {
“configurationData”: {
“initialize”: {
“name”: app_name,
“description”: app_desc,
“id”: app_id,
“permissions”: [
‘r:devices: *’, # read all devices
‘x:devices: *’, # use all devices
‘r:locations: *’ # subscribe to events
],
“firstPageId”: “1”
}
}
}

ignore the space between the colon and asterisk, this is the forum parsing.

1 Like

You my good man are a legend!!. This has solved my problem!. I added r:devices:* instead of r:devices to INITIALIZE permissions and I am now creating capability subscriptions without any crashes. I cant thank you enough for your feedback on this. The doc example is clearly wrong and that’s what put me off for so long. I never thought of taking the actual scope.

Hopefully I can now help you get your issue sorted because I can successfully create individual and Capability subscriptions. Can you send/post your code or snippet of the sub request that returns your 403 error.

Thanks again!

1 Like