SmartApp: How to list rooms or groups under page.section

Hi,

since everything I have found so far was either too old or not really informative, I’ll open a new topic.

I would like to show a list of all rooms/groups in my SmartApp, if you then tap on one of these rooms you will get into a corresponding room/group setting.

How can I realize this as page.section? And if that doesn’t work, what alternatives do I have?

Thank you in advance.

You have to call the sdk to get rooms and groups and then use these lists to populate page.section.enumsettings.

I think I had a timing issue, and I had to guard the enumsetting in the page code to check the lists had been populated (Dont think context is set up when page code is first set up so you have to call the sdk from the updated handler)

@Davec I’m new to SmartApps and actually crawling through the documentation and examples.

Do you mean this with “call the SDK”?

Currently, I’m just working with SmartApp SDK NodeJS.

Ah…you have a frustrating but hopefully fun journey ahead…

So in the smartapp example it calls

await context.api.subscriptions.delete()

This context.api is the wrapper for the core sdk. this makes available the various endpoints for rules, rooms, groups, devices etc

coresdk If I was you I’d skim the documentation and then use that example for subscriptions as a template to code what you need. The code in the endpoints was most useful to me Endpoints
The coresdk links a wiki which is a bit helpful but stops short of actually explaining how to call stuff

Good luck!

However it is wrapped up, it is going to require an authorization token for the call to the API. I don’t think you get one in the request until later lifecycles. So I think it would be easiest to skip that functionality first time out, then pull and save the list of rooms in the update lifecycle. Then just update the app with the rooms available.

You can, of course, request a Personal Access Token on the first page or hide one in the app settings, but it seems kind of sucky.

1 Like

What @orangebucket said -:slight_smile: . I tried to say as well. I just set a variable to be the required list, then wrapped the settings in an ‘if’ checking if is null or not

Hey guys,

thanks for your help and sorry for my late reply :see_no_evil:

I have now set up a context store and API authentication via appId, clientId, clientSecret which is working so far.

Since the API needs to be authenticated first, I am using an initial page to prompt the user to authorize the app permissions first and then the user can start the smart app configuration. After that, the user can launch the smart app configuration. But of course, the user has to edit the app again under Automations.

Is there a better way to do this?

I can retrieve the rooms now, but that is asynchronous… Is there any way to fetch them first before loading the rest of the page sections? Otherwise, the list is empty every time the page is loaded the first time.

What I’m trying to implement is a list of rooms:

Room A >
Room B >
Room C >

where each room is linked to a generic configuration page “roomSetupPage” referenced by the room ID:

Room A > “tap” > Generic configuration page for room A
Room B > “tap” > Generic configuration page for room B

which I currently implemented with the following:

for (r in newRoomsList) {
            section.pageSetting(`room_${r}`)
                .page('roomSetupPage')
                .name(newRoomsList[r].name)
                .description('')
        };

Is there a way to store it in the config somehow?

Hi, @ronie_pilot !
You can condition the display of the rest of settings after the user has selected a room (because, to select a room the list must be filled). That can be done using the property submitOnChange(true) in the setting that you want to have first.
For example, considering the sample below, this is the behavior expected:

  1. Only the page section mainSwitches will appear when you first open the SmartApp.
  2. Once you select a value, the page is submitted and refreshed, this means the comparison will be evaluated to true and show the other one called contactSection.
.page('mainPage', (context, page, configData) => {
    page.section('mainSwitches', section => {
        section.deviceSetting('mSwitches').capability("switch").required(true).submitOnChange(true);
    });

    if(context.config.mSwitches){
      //show the rest of the page settings
      page.section(`contactSection`, section => {
        section.deviceSetting('contactSens').capability("contactSensor").required(true).description("...")
      });
    }
})

Hi @nayelyz ,

ok, this can be a workaround if there is no better way.

What about my “roomSetupPage”? Is it possible to use it as a template for each room?
Currently I realized that the different room links “Room A >”, “Room B >”, “Room C >” all redirect to “roomSetupPage” which is correct but not my intention.

My app does not know what rooms are available, but wants the user to create a different configuration for each room. I need different instances of “roomSetupPage”. How can I implement this using the SDK?

Thank you.

Ah, do you mean that depending on the room selected you want to present a different configuration for the user?
why are they different, how would you identify the differences among them, only by the room’s name?

Exactly.

I would like to let the user configure different temperatures for each presence state.
That’s why I need different room configurations.

This is how I currently try to fetch the rooms API and create the room page links:

let roomsList = [];

async function setRooms(context) {

   context.api.rooms.list(context.locationId).then(res => {
       roomsList = res;

       console.log('### roomsList', roomsList);
   });

   return roomsList;
}

module.exports = (async (context, page, configData) => {
   context.retrieveTokens();
   await setRooms(context)

   page.section('dashboard', section => {
       section.paragraphSetting('initDescription')
       section.booleanSetting('enableRoomSetup')
           .submitOnChange(true)
           .required(true)
           .defaultValue(0)
       
       let enableRoomSetup = context.configStringValue('enableRoomSetup')
       if (enableRoomSetup === 'true') {
           for (r in roomsList) {
               section.pageSetting(`room_${r}`)
                   .page('roomSetupPage')
                   .name(roomsList[r].name)
                   .description('')
           };
       }
   })
})

Theoretically, I could use the “roomId” entries from the “context.locationId” response as a reference. But yeah, how can I implement this using the SDK?

Also, it would be cool to somehow store the retrieved API data to reduce requests. Is this possible? I am using the context store and realized that all other settings made with a section setting method are stored there after a submit.

Sorry for the delay.

Where are you storing the context? Is it in a Cloud DB like DynamoDB (example)? I think it should be possible to create another entry or table, you could use the installedAppId as an identifier because it is unique per SmartApp instance. However, there would be an issue to sync the rooms as the user can create/delete them and the SmartApp wouldn’t notice.

So, do you have a presence sensor in each room?

Where do you want to reference the roomId?
I see that you’re using the name in the pageSetting:

I’m using the @smartthings/file-context-store
The automatically added data-file then looks like this:

{
  "installedAppId": "installedAppId",
  "locationId": "locationId",
  "authToken": "authToken",
  "refreshToken": "refreshToken",
  "config": {
    "scheduleStartSchedule": [
      {
        "valueType": "STRING",
        "stringConfig": {
          "value": "2022-11-02T08:00:00.000+0100"
        }
      }
    ],
    "indexEnableAutomatedReset": [
      {
        "valueType": "STRING",
        "stringConfig": {
          "value": "0"
        }
      }
    ],
    "roomNightModeTemperature": [
      {
        "valueType": "STRING",
        "stringConfig": {
          "value": "18"
        }
      }
    ],
    "scheduleSetHeatingTrigger": [
      {
        "valueType": "STRING",
        "stringConfig": {
          "value": "2"
        }
      },
      {
        "valueType": "STRING",
        "stringConfig": {
          "value": "3"
        }
      }
...
}

Each submitted change of a configurable page field is stored here. But I don’t understand if I can also access and use it to store the rooms list for example.

Do you have an example of that?

That’s what I worry about when the rest is working :wink:

No, I use the SmartThings presence states: home, away, night

For each room page template, I need a different room page object that stores the room-specific configuration. Viewing the correct name is working so far, but is there also away to store it like that?

room A link to page > [room page template] with reference to A > save with reference to room A
room B link to page > [room page template] with reference to B > save with reference to room B
room C ... and so on

I hope you can follow me :slight_smile:

@nayelyz

any inputs here? :slight_smile: