Cloud to Cloud - How to update devices periodically / trigger device data update

Hello,
I made a cloud Integration for the Netatmo Weather Station.
I successfully added the devices with data, but I’m not able to refresh the devices.
Here’s my Lambda function:

'use strict';
const { SchemaConnector } = require('st-schema')
const axios = require('axios')

const connector = new SchemaConnector()
  .enableEventLogging(2)
  .discoveryHandler(async (accessToken, response) => {
    const axiosInstance = axios.create({
      baseURL: 'https://api.netatmo.com/api',
      headers: {
        'Authorization': 'Bearer ' + accessToken,
        'Content-Type': 'application/json'
      }
    })

    const stations = await axiosInstance.get('/getstationsdata?get_favorite=false')

    for (const station of stations.data.body.devices) {
      response.addDevice(station._id, station.module_name, '29a6f358-b677-4918-9fa5-437aa9e61a26')
        .manufacturerName('Netatmo')
        .modelName(station.type)
        .hwVersion('v1')
        .swVersion('1.0.0')

      if (station.modules) {
        for (const module of station.modules) {
          if (module.type === 'NAModule1') {
            response.addDevice(module._id, module.module_name, '78c75c34-24cd-44bd-89f6-d7c5a7f2e03b')
              .manufacturerName('Netatmo')
              .modelName(station.type)
              .hwVersion('v1')
              .swVersion('1.0.0')
          }

          if (module.type === 'NAModule4') {
            response.addDevice(module._id, module.module_name, 'aa6afe0c-24ee-4055-9699-01de5140d6f5')
              .manufacturerName('Netatmo')
              .modelName(station.type)
              .hwVersion('v1')
              .swVersion('1.0.0')
          }
        }
      }
    }
  }).stateRefreshHandler(async (accessToken, response) => {
    const axiosInstance = axios.create({
      baseURL: 'https://api.netatmo.com/api',
      headers: {
        'Authorization': 'Bearer ' + accessToken,
        'Content-Type': 'application/json'
      }
    })

    const stations = await axiosInstance.get('/getstationsdata?get_favorite=false')

    console.log(stations.data.body.devices.length , "Station(s) found")

    for (const station of stations.data.body.devices) {
      const mainStation = response.addDevice(station._id);
      const mainStationComponent = mainStation.addComponent('main');

      if (station.dashboard_data) {
        const dashboardData = station.dashboard_data;

        if (dashboardData.hasOwnProperty('Temperature')) {
          mainStationComponent.addState('st.temperatureMeasurement', 'temperature', dashboardData.Temperature, "C");
        }

        if (dashboardData.hasOwnProperty('max_temp') && dashboardData.hasOwnProperty('min_temp')) {
          mainStationComponent.addState('st.temperatureMeasurement', 'temperatureRange', {
            maximum: dashboardData.max_temp,
            minimum: dashboardData.min_temp,
            step: 0.1
          }, "C");
        }

        if (dashboardData.hasOwnProperty('Humidity')) {
          mainStationComponent.addState('st.relativeHumidityMeasurement', 'humidity', dashboardData.Humidity, "%");
        }

        if (dashboardData.hasOwnProperty('CO2')) {
          mainStationComponent.addState('st.carbonDioxideMeasurement', 'carbonDioxide', dashboardData.CO2);

          if (dashboardData.CO2 <= 900) {
            mainStationComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'good');
          } else if (dashboardData.CO2 <= 1150) {
            mainStationComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'moderate');
          } else if (dashboardData.CO2 <= 1600) {
            mainStationComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'slightlyUnhealthy');
          } else if (dashboardData.CO2 <= 2000) {
            mainStationComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'unhealthy');
          } else {
            mainStationComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'veryUnhealthy');
          }
        }

        if (dashboardData.hasOwnProperty('Noise')) {
          mainStationComponent.addState('st.soundPressureLevel', 'soundPressureLevel', dashboardData.Noise);
        }

        if (dashboardData.hasOwnProperty('Pressure')) {
          mainStationComponent.addState('st.atmosphericPressureMeasurement', 'atmosphericPressure', dashboardData.Pressure / 10);
        }
      }

      console.log(station.modules.length , "Module(s) found")

      if (station.modules.length > 0) {
        for (const module of station.modules) {
          if (module.type === 'NAModule1') {
            const outdoorModule = response.addDevice(module._id);
            const outdoorModuleComponent = outdoorModule.addComponent('main');

            outdoorModuleComponent.addState('st.battery', 'battery', module.battery_percent);

            if (module.dashboard_data) {
              const dashboardData = module.dashboard_data;

              if (dashboardData.hasOwnProperty('Temperature')) {
                outdoorModuleComponent.addState('st.temperatureMeasurement', 'temperature', dashboardData.Temperature, "C");
              }
              
              if (dashboardData.hasOwnProperty('max_temp') && dashboardData.hasOwnProperty('min_temp')) {
                outdoorModuleComponent.addState('st.temperatureMeasurement', 'temperatureRange', {
                  maximum: dashboardData.max_temp,
                  minimum: dashboardData.min_temp,
                  step: 0.1
                }, "C");
              }

              if (dashboardData.hasOwnProperty('Humidity')) {
                outdoorModuleComponent.addState('st.relativeHumidityMeasurement', 'humidity', dashboardData.Humidity, "%");
              }
            }
          }

          if (module.type === 'NAModule4') {
            const indoorModule = response.addDevice(module._id);
            const indoorModuleComponent = indoorModule.addComponent('main');

            indoorModuleComponent.addState('st.battery', 'battery', module.battery_percent);

            if (module.dashboard_data) {
              const dashboardData = module.dashboard_data;

              if (dashboardData.hasOwnProperty('Temperature')) {
                indoorModuleComponent.addState('st.temperatureMeasurement', 'temperature', dashboardData.Temperature, "C");
              }
              
              if (dashboardData.hasOwnProperty('max_temp') && dashboardData.hasOwnProperty('min_temp')) {
                indoorModuleComponent.addState('st.temperatureMeasurement', 'temperatureRange', {
                  maximum: dashboardData.max_temp,
                  minimum: dashboardData.min_temp,
                  step: 0.1
                }, "C");
              }

              if (dashboardData.hasOwnProperty('Humidity')) {
                indoorModuleComponent.addState('st.relativeHumidityMeasurement', 'humidity', dashboardData.Humidity, "%");
              }

              if (dashboardData.hasOwnProperty('CO2')) {
                indoorModuleComponent.addState('st.carbonDioxideMeasurement', 'carbonDioxide', dashboardData.CO2);

                if (dashboardData.CO2 <= 900) {
                  indoorModuleComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'good');
                } else if (dashboardData.CO2 <= 1150) {
                  indoorModuleComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'moderate');
                } else if (dashboardData.CO2 <= 1600) {
                  indoorModuleComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'slightlyUnhealthy');
                } else if (dashboardData.CO2 <= 2000) {
                  indoorModuleComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'unhealthy');
                } else {
                  indoorModuleComponent.addState('st.carbonDioxideHealthConcern', 'carbonDioxideHealthConcern', 'veryUnhealthy');
                }
              }
            }
          }
        }
      }
    }
  }).commandHandler((accessToken, response, devices) => {
    console.log("Hello")
  }).callbackAccessHandler((accessToken, callbackAuthentication, callbackUrls) => {
    console.log("World")
  });
  

exports.handler = async (evt, context, callback) => {
    return connector.handleLambdaCallback(evt, context, callback);
};

When I refresh manually the device on the app, nothing is triggerred, and I don’t know how to update the station periodically

1 Like

Hi, @romrom21
I checked your profile 29a6f358-b677-4918-9fa5-437aa9e61a26 and I see it doesn’t include the capability “refresh” which is “triggered” when we pull down the screen. It sends a “stateRefreshRequest” where you can send the new values.

To trigger this stateRefresh automatically, you could create a Rule that calls the refresh capability every 15 mins for example:

{
    "name": "Refresh Interval",
    "actions": [
        {
            "every": {
                "interval": {
                    "value": {
                        "integer": 15
                    },
                    "unit": "Minute"
                },
                "actions": [
                    {
                        "command": {
                            "devices": [
                                "deviceId"
                            ],
                            "commands": [
                                {
                                    "component": "main",
                                    "capability": "refresh",
                                    "command": "refresh"
                                }
                            ]
                        }
                    }
                ]
            }
        }
    ]
}

Or, create a SmartApp that creates a schedule and calls the corresponding API to update the capabilities’ status.

2 Likes

Ohhhh I see, thank you very much I’ll try this tomorrow!!

1 Like

I have the same code as above, but when I trigger an update it doesn’t work. It is working the first time I add the device, so after the discovery a first state refresh is triggerred and it works great.
But when I manually trigger an update on the smartthings app, it says that some capabilities are not valid values or some devices don’t exist.
Here are the logs :

{
    "headers": {
        "schema": "st-schema",
        "version": "1.0",
        "interactionType": "stateRefreshRequest",
        "requestId": "02c2c66b-d653-439c-bc6f-1e902cc06ca6"
    },
    "authentication": {
        "tokenType": "Bearer",
        "token": "token"
    },
    "devices": [
        {
            "externalDeviceId": "03:00:00:0e:2f:08",
            "deviceCookie": {}
        }
    ]
}
{
    "headers": {
        "schema": "st-schema",
        "version": "1.0",
        "interactionType": "stateRefreshResponse",
        "requestId": "02c2c66b-d653-439c-bc6f-1e902cc06ca6"
    },
    "deviceState": [
        {
            "externalDeviceId": "70:ee:50:a5:bc:5a",
            "states": [
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperature",
                    "value": 21.9,
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperatureRange",
                    "value": {
                        "maximum": 22.3,
                        "minimum": 21.2,
                        "step": 0.1
                    },
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.relativeHumidityMeasurement",
                    "attribute": "humidity",
                    "value": 63,
                    "unit": "%"
                },
                {
                    "component": "main",
                    "capability": "st.carbonDioxideMeasurement",
                    "attribute": "carbonDioxide",
                    "value": 632
                },
                {
                    "component": "main",
                    "capability": "st.carbonDioxideHealthConcern",
                    "attribute": "carbonDioxideHealthConcern",
                    "value": "good"
                },
                {
                    "component": "main",
                    "capability": "st.soundPressureLevel",
                    "attribute": "soundPressureLevel",
                    "value": 52
                },
                {
                    "component": "main",
                    "capability": "st.atmosphericPressureMeasurement",
                    "attribute": "atmosphericPressure",
                    "value": 101.65
                }
            ]
        },
        {
            "externalDeviceId": "02:00:00:a4:7b:e8",
            "states": [
                {
                    "component": "main",
                    "capability": "st.battery",
                    "attribute": "battery",
                    "value": 92
                },
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperature",
                    "value": 21.3,
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperatureRange",
                    "value": {
                        "maximum": 21.4,
                        "minimum": 19.5,
                        "step": 0.1
                    },
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.relativeHumidityMeasurement",
                    "attribute": "humidity",
                    "value": 67,
                    "unit": "%"
                }
            ]
        },
        {
            "externalDeviceId": "03:00:00:0e:2f:08",
            "states": [
                {
                    "component": "main",
                    "capability": "st.battery",
                    "attribute": "battery",
                    "value": 92
                },
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperature",
                    "value": 20.7,
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.temperatureMeasurement",
                    "attribute": "temperatureRange",
                    "value": {
                        "maximum": 21.8,
                        "minimum": 20.7,
                        "step": 0.1
                    },
                    "unit": "C"
                },
                {
                    "component": "main",
                    "capability": "st.relativeHumidityMeasurement",
                    "attribute": "humidity",
                    "value": 57,
                    "unit": "%"
                },
                {
                    "component": "main",
                    "capability": "st.carbonDioxideMeasurement",
                    "attribute": "carbonDioxide",
                    "value": 458
                },
                {
                    "component": "main",
                    "capability": "st.carbonDioxideHealthConcern",
                    "attribute": "carbonDioxideHealthConcern",
                    "value": "good"
                }
            ]
        }
    ]
}
{
    "headers": {
        "schema": "st-schema",
        "version": "1.0",
        "interactionType": "interactionResult",
        "requestId": "02c2c66b-d653-439c-bc6f-1e902cc06ca6"
    },
    "authentication": {
        "tokenType": "Bearer",
        "token": "token"
    },
    "deviceState": [
        {
            "externalDeviceId": "70:ee:50:a5:bc:5a",
            "deviceError": [
                {
                    "errorEnum": "BAD-REQUEST",
                    "detail": "states[5].capability: soundPressureLevel is not a valid value."
                },
                {
                    "errorEnum": "BAD-REQUEST",
                    "detail": "states[6].capability: atmosphericPressureMeasurement is not a valid value."
                }
            ]
        },
        {
            "externalDeviceId": "02:00:00:a4:7b:e8",
            "deviceError": [
                {
                    "errorEnum": "BAD-REQUEST",
                    "detail": "deviceState[1] does not correspond to any ST device"
                }
            ]
        },
        {
            "externalDeviceId": "03:00:00:0e:2f:08",
            "deviceError": [
                {
                    "errorEnum": "BAD-REQUEST",
                    "detail": "deviceState[2] does not correspond to any ST device"
                }
            ]
        }
    ],
    "originatingInteractionType": "stateRefreshResponse"
}

Ohh, I found the solution, I should not update other devices than the one that requested the update. It is now working correctly ! I just have to implement the rules !
Thank you very much !!

2 Likes

Yes, the stateRefreshResponse should be done for the device that requested it. Every 24 hrs a discoveryRequest is triggered and this will call a stateRefreshRequest for every device discovered so, you’ll have that event to sync the devices as well.

Happy to help! Glad it works :smiley:

3 Likes