Developer Portal
Documentation
API ReferenceConsole

Points

Most devices output data via points - a point represents a particular output at a particular time, which could be as simple as "Yes, I'm on" or "No, I'm off" for a device like a light. For something more complex like a thermostat, you'll have a much wider range of possible points. Ambient temperature in the room, what temperature is set, is the thermostat set to heat or set to cool - all possible points.

To get to point data, you can either start with a thing - we already know the id for a VAV (Variable Air Volume HVAC system), so we'll pull the list of points using it for our filter:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
{
  things(filter: {id: {eq: "THGJjbQG52zKvVYif43R7SCSu"}}) {
    id
    name
    points {
      id
      name
      type
    }
  }
}

or you can find points in a place, like starting with a building:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
{
  buildings(filter: {id: {eq: "BLDG5o26DguWKu5T9nRvSYn5Em"}}) {
    name
    id
    points {
      id
      name
      exactType
    }
  }
}

You can also filter for a specific type of point:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
{
  buildings(filter: {id: {eq: "BLDG5o26DguWKu5T9nRvSYn5Em"}}) {
    name
    id
    points(filter: {type: {eq: "Supply_Air_Temperature_Sensor"}}) {
      id
      name
      exactType
    }
  }
}

Series

A point by itself can be useful - knowing if a security camera was on at a particular moment, or if a door was locked - but in a lot of cases you're going to want to know point data across a a window of time. This is called time series, and there are couple of ways you can work with them.

In this example, we're retrieving the series data from a Supply_Air_Temperature_Sensor for the VAV we looked up earlier, restricted to a specific window of time. The response will contain float values that represent what the temperature was set to every 15 minutes within that specified hour. Note that 15 minute interval is not defined by the API, rather the device itself will determine how often to store the value.

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  things(filter: {id: {eq: "THGJjbQG52zKvVYif43R7SCSu"}}) {
    id
    name
    points(filter: {id: {eq: "PNTXK13hHH6RUK4sp2csBqSyv"}}) {
      id
      name
      type
      series(startTime: "2023-04-13T17:00:00.000", endTime: "2023-04-14T17:00:00.000") {
        timestamp
        value {
          float64Value
        }
      }
    }
  }
}

If you're polling for the most recent value on a regular basis, you don't really need a date range - just the latest result. There's a filter option for that as well:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  things(filter: {id: {eq: "THGJjbQG52zKvVYif43R7SCSu"}}) {
    id
    name
    points(filter: {id: {eq: "PNTXK13hHH6RUK4sp2csBqSyv"}}) {
      id
      name
      type
      series(latest: true) {
        timestamp
        value {
          float64Value
        }
      }
    }
  }
}

Aggregation

The second option is to use aggregation, which takes point data and performs functions on it, like finding the average, sum, min, max or count. You'll need to define the period as well, which accepts DAY, HOUR and MINUTE as values. In this example, we'll look for the average hourly temperature recorded by an air temperature sensor:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  things(filter: {id: {eq: "THGJjbQG52zKvVYif43R7SCSu"}}) {
    id
    name
    points(filter: {id: {eq: "PNTXK13hHH6RUK4sp2csBqSyv"}}) {
      id
      name
      type
      aggregation(
        startTime: "2023-04-13T17:00:00.000"
        endTime: "2023-04-14T17:00:00.000"
        period: HOUR
      ) {
        timestamp
        avg
      }
    }
  }
}

You can use this kind of data to determine when the AC might be struggling to keep up - maybe there are more people in the space than normal, maybe the time of day is a factor, or maybe the AC itself needs servicing; all useful data when you're trying to keep people comfortable, or trying to save on energy costs.

Using the State Text Field

For some points, the result data is a numeric value that doesn't have enough context on its own to make sense - like a 0 or a 1 for an Occupancy Sensor, vs something like 75 for a Temperature Setpoint. For these types of points, we include a stateTexts field. This field provides the missing context; here's an example:

Request Response
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  things(filter: {id: {eq: "THGD5NLUsAndK3EAcAuJwABcd"}}) {
    name
    exactType
    points {
      id
      name
      exactType
      stateTexts
      series(latest: true) {
        timestamp
        value {
          float64Value
        }
      }
    }
  }
}

In the above example, the stateTexts field includes the values "Unoccupied, Occupied, Inactive, and Unknown" which correspond to the float values "0, 1, 2, 3" in the series data. So in the above example, the float value of "1" means translates into "Occupied".

Identifying the Unit of Measure

When you start to retrieve point data, you'll get a numerical value returned. Identifying the unit of measure for that data just looking at it could be difficult. Even with a temperature sensor, is the data in Fahrenheit or Celsius? To help with that, there's a unit field available for each thing that provides those details:

RequestLive Response
Copy
1
2
3
4
5
6
7
8
9
10
11
12
{
  things(filter: {id: {eq: "THGJjbQG52zKvVYif43R7SCSu"}}) {
    name
    points {
      name
      unit {
        name
        description
      }
    }
  }
}

Note that the ID format, including the number of characters, may vary from those displayed in the samples here. Once a point or a thing has an ID assigned, that ID will never change, however we do not recommend placing any kind of validation on format or character count.

If you're interested in viewing all of the potential units, you can make a call to retrieve the full list as well:

RequestLive Response
Copy
1
2
3
4
5
6
{
  units {
    name
    description
  }
}