Permissions in defining SmartApp()

Still trying to get my head around all this new api for creating SmartApps (it just seemed so easier with the IDE and groovy…).

With defining the smartapp, I’m attempting to subscribe to subscribeToSecuritySystem and setup my .subscribedSecurityArmStateEventHandler, but things don’t seem to work. In the developer workspace, there is no scope for the security armstate, so I add my .permissions(“r:security:locations:*:armstate”), and also add my .appId, .clientId and .clientSecret, but it just results in an error.
Commenting out the .permissions in defining the SmartApp() allows things to work.

What am I doing wrong, or not understanding? Here is some of my code:

const smartapp = new SmartApp()
.appId(“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”)
.clientId(“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”)
.clientSecret(“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”)
.permissions([
“r:security:locations:*:armstate”
])
.enableEventLogging(2) // Log all lifecycle event requests and responses as pretty printed JSON
.configureI18n() // Use language file in locales folder
.disableCustomDisplayName(true)

.page("mainPage", (context, page, configData) => {
    page.section("envisalink", section => {
        section.textSetting("envisalinkIP")
            .description("IP address of Envisalink device")
            .defaultValue("envisalink")
            .required(true)
        section.numberSetting("envisalinkPort")
            .defaultValue("4025")
            .required(true)
        section.passwordSetting("envisalinkPassword")
            .description("Password for Envisalink device")
            .defaultValue("user")
            .required(true)
    });

// Called for both INSTALLED and UPDATED lifecycle events
.updated(async (context, updateData) => {
await context.api.subscriptions.unsubscribeAll();
await context.api.subscriptions.subscribeToSecuritySystem(“secuirityStateHandler”);
})

.subscribedSecurityArmStateEventHandler("securityStateHandler", (context, event) => {
    const value = event.armState;
    console.log(value);
});

Hi there! You’re right, this scope isn’t at the Developer Workspace, add it to your SmartApp by following these steps using the SmartThings CLI:

  1. Get the current OAuth scopes for the desired app and save it to a file, where xxxxx-xxxx-xxxx is the UUID of the app (located in the Developer Workspace)
smartthings apps:oauth  xxxxx-xxxx-xxxx -j > appOauth.json
  1. Edit the file to add the r:security:locations:*:armstate scope.
{
    "clientName": "smartapp_name",
    "scope": [
        "r:devices:$",
        "r:devices:*",
        "r:locations:*",
        "x:devices:$",
        "x:devices:*",
        "r:security:locations:*:armstate"
    ],
    "redirectUris": []
}
  1. Save the new configuration.
smartthings apps:oauth:generate xxxxx-xxxx-xxxx -i appOauth.json

This scope must be added to the SmartApp definition too, here’s my example:

You should see the security scope included in the authorization page:

1 Like

Thanks for the great walk-through. I was able to easily follow it.
I am still having a problem with getting my config screen to show. If I comment out the .permissions attribute then it renders, if I leave it I get this response:

2020-12-09T20:09:53.456Z debug: RESPONSE: {
  "statusCode": 200,
  "configurationData": {
    "initialize": {
      "id": "a5c4e69e-3a76-4119-90d2-f89e97575b8b",
      "firstPageId": "mainPage",
      "permissions": [
        "i:deviceprofiles:$",
        "i:deviceprofiles:*",
        "r:customcapability",
        "r:devices:$",
        "r:devices:*",
        "r:locations:*",
        "r:security:locations:*:armstate",
        "w:devices:$",
        "w:devices:*",
        "x:devices:$",
        "x:devices:*"
      ],
      "disableCustomDisplayName": true,
      "disableRemoveApp": false
    }
  }
}

Not sure why it errors with the .permisions attribute.

When using permissions() and appId() in your SmartApp definition, configureI18n() should be removed. Otherwise, you will receive the error “We’re having trouble connecting…”
Also, ClientId and clientSecret are not mandatory.
If the previous post helped you, please don’t forget to mark it as the Solution. Either way, I’ll be here to answer other questions you may have.

I removed .configurel18n() and still have the same problem. (Not sure why this should be removed though, as isn’t this the preferred manner to localize app for different languages?)

I have also removed .clientId and .clientSecret, still no go. I remove the .permissions and it works to display my settings screen.

I’m really confused on why this is happening…

Can you share your SmartApp definition, please?

I have a server.js script that handles the setup and the https web server. My smartapp.js is:

const SmartApp = require("@smartthings/smartapp")
const smartApp = new SmartApp()
.enableEventLogging(2)
.configureI18n() // Language file in ./locales folder
.appId(“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”)
.disableCustomDisplayName(true)
.permissions([
“i:deviceprofiles:", "i:deviceprofiles:*", "r:customcapability", "r:devices:”,
“r:devices:",
"r:locations:
”,
“r:security:locations::armstate",
“w:devices:", "w:devices:*", "x:devices:”,
"x:devices:

])
.page(“mainPage”, (context, page, configData) => {
page.section(“envisalink”, section => {
section.textSetting(“envisalinkIP”)
.description(“IP address of Envisalink device”)
.defaultValue(“envisalink”)
.required(true)
section.numberSetting(“envisalinkPort”)
.description(“Port number for Envisalink device”)
.defaultValue(“4025”)
.required(true)
section.passwordSetting(“envisalinkPassword”)
.description(“Password for Envisalink device”)
.defaultValue(“user”)
.required(true)
})
})
.updated(async (context, updateData) => {
await context.api.subscriptions.unsubscribeAll();
await context.api.subscriptions.subscribeToSecuritySystem(“securityStateHandler”);
})
.subscribedSecurityArmStateEventHandler(“securityStateHandler”, (context, event) => {
const value = event.armState;
console.log(value);
});

module.exports = smartApp

I don’t know if when the code was formatted here, it lost some symbols, like here:

Anyway, I think that the problem is the permissions list, you only need to include those you selected in the Developer Workspace and the armsate scope (remember to add it back every time you edit the SmartApp from there - change URL, other scopes, name, etc.). Here’s how it works for me:

app.enableEventLogging(2)
.configureI18n() // Language file in ./locales folder
.appId("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
.disableCustomDisplayName(true)
.permissions([
    "i:deviceprofiles:*",
    "r:customcapability",
    "r:devices:*",
    "r:locations:*",
    "w:devices:*",
    "x:devices:*",
    "r:security:locations:*:armstate"
])
.page("mainPage", (context, page, configData) => {
    page.section("envisalink", section => {
        section.textSetting("envisalinkIP")
            .description("IP address of Envisalink device")
            .defaultValue("envisalink")
            .required(true)
        section.numberSetting("envisalinkPort")
            .description("Port number for Envisalink device")
            .defaultValue("4025")
            .required(true)
        section.passwordSetting("envisalinkPassword")
            .description("Password for Envisalink device")
            .defaultValue("user")
            .required(true)
    })
})
.updated(async (context, updateData) => {
    await context.api.subscriptions.unsubscribeAll();
    await context.api.subscriptions.subscribeToSecuritySystem("securityStateHandler");
})
.subscribedSecurityArmStateEventHandler("securityStateHandler", (context, event) => {
    const value = event.armState;
    console.log(value);
});

Note: It works correctly using .configureI18n() now.

1 Like

Thank-you! for your help. I just redid the permissions and tried it and all appears to be working now. :grinning_face_with_smiling_eyes:

2 Likes

Looks like I replied too soon. My smartapp seems to be stuck in a configuration loop.
I see the configuration screen, I hit “DONE” and I see the spinning blue dots, and then I’m placed back into the configuration screen. Live logging is useless as nothing show in it, and the console output doesn’t show anything wrong.

It appears like the .updated isn’t called, but the .uninstall is as can see that from the console output.

{I’m really missing the old groovy IDE, flaws and everything…)

Don’t worry, I’ve got your back :smiley:
I get the same result on my side, I saw that it’s caused by two elements in the permissions list (i:deviceprofiles:* and r:customcapability). Once I removed them, I was able to install the SmartApp.
Can you tell me what you are going to use them for, please?

1 Like

Again thank-you so much for the help.
So, I’m in the process of moving/converting this code:…

…to the new api, as the Groovy IDE will be deprecated sometime next year.
I currently have a SmartApp to do some basic things (and as I teach myself the workings of the new API), but eventually I’ll have to have a smartapp for a cloud connector using webhooks, from what I understand reading the docs.

(As such, I need access to the custom capabilities (which I’ve created in updated the groovy code) and devices. Is there a bug/issue with these permissions, as I had the same thing happen in attempting to create a cloud connected smartapp).

As this isn’t my day job, it’ll take some time, and help form the experts! :grin:

I checked the code you shared. As I understand, your SmartApp creates 4 devices using to visualize the status of the panel, and based on the SHM subscription you send the update to it, right?
There are different approaches:

  1. Use the SmartApp itself to visualize the status of the security panel.
    You could define different pages, one for the configuration input, and the others to visualize the current status using other elements, such as paragraph.
  2. Use a SmartApp connector.
    It is possible to install devices using this type of SmartApp
  3. Use a Cloud-Connector as you mentioned (SmartThings Schema)
    This also creates a device, you could send commands from ST to the panel and it can receive state updates from a third-party cloud. This one doesn’t include subscriptions.

Either one you choose, I’ll be here to help you with the process.

1 Like

Again, thank-you for your continued assistance.

So, yes, my SmartApp will create 5 basic types of devices (motion, contact, water, smoke, carbon-monoxide) to reflect the security panel, and subscribe to the SHM to arm/disarm.

I currently looking at doing a SmartApp connector, but I see potential in using the ST Schema, so I just need to do more research on which path would be better.

I also was wonder with the security permissions set in the app, I can subscribe to changes in SHM and react to changes in it to arm/disarm the security panel device, but I don’t see a manner to change the status of SHM if the security panel device changes its status. Is this not documented, or not allowed from SmartApps for “security reasons”?

Great! I’ll be here in case you have questions.

Let me check more details about this, I’ll let you know.

Thanks! Looking forward to good news

Hi @rtorch, you were right, changing the SHM arm state using the SmartThings API is not allowed for security reasons. However, in your case, the “Automations” of the mobile app could help you. There is an option to change the security state based on a condition as shown in the picture below:

Once you install your devices, you can create automations for them. Just remember that the automation section is not displayed for custom capabilities yet.

This is what I’m currently doing (and many other users) to sync the SHM with their DSC/Envisalink security system. This kludge works, but has issues.

The problem I’ve encountered is that since the SmartApp subscribes to any changes in the SHM, having an automation that changes the SHM, leads to a duplication of commands.

For example, the SmartApp receives a “Disarm” command from one of its devices, and sends the command to DSC/Envisalink. The automation sees a change in the device and sends changes the SHM state to disarm. The SmartApp now gets an event that the SHM changed to “Disarm” and sends the disarm command to DSC/Envisalink. (and the DSC/Envisalink issues an error as the system has been disarmed already).

With the classic app, when the Alarm System capability was active, the SmartApp could set the state and avoid this. It was a much more elegant way of doing things.

Since the SHM cannot be changed by a SmartApp, is there a way of creating my own security panel like the SHM to display the three buttons/states of Arm Stay, Arm Away, Disarm?

Up to now, this is not possible because SHM uses a Custom UI.

Allow me some time to see if there’s an alternative to avoid this.

Hi, @rtorch! I’ve been thinking…and I found an alternative to avoid duplicates. As I understand, there are two paths to change the arm state:

  1. From the SmartThings app using the SHM. Workflow:
    a. User changes the SHM status.
    b. The event for the subscription is generated.
    c. It is sent to the physical device and the devices in ST are updated too.
    d. With the automation, the status of the SHM is “modified” again, which causes a new event of the subscription.
  2. From the physical device. Workflow:
    a. The status of the physical device is modified.
    b. The devices in ST are updated, with the automation, the SHM status is modified too.
    c. The event of the subscription is generated.

Based on the previous points, the alternative could be a subscription to the devices, when an event is received, it can act as a condition so the duplicated event from the SHM isn’t sent.

For example:

//...
.subscribedEventHandler('deviceHandler', (context, devEvent) => {
    console.log('device event',devEvent)
    sendCommand=false;
})
.subscribedSecurityArmStateEventHandler('securityStateHandler', (context, secEvent) => {
    if(sendCommand){
       console.log('security event',secEvent)
    }
    sendCommand=true;
})