HTTP API Asset/Attributes query with JSON objects including historic Datapoints

Let’s assume I have Datapoints (I hope I’m using the right word) in one Asset like the following two:

[
  {
    "id": "example",
    "version": 20,
    "createdOn": 1700211591610,
    "name": "Example Name",
    "accessPublicRead": false,
    "realm": "examplerealm",
    "type": "ThingAsset",
    "path": [
      "example"
    ],
    "attributes": {
      "carData": {
        "type": "JSONObject",
        "value": {
          "color": "green",
          "shape": "sedan",
          "speed": 100
        },
        "name": "carData",
        "meta": {
          "storeDataPoints": true
        },
        "timestamp": 1707483616321
      }
    }
  }
]
[
  {
    "id": "example",
    "version": 20,
    "createdOn": 1700211591610,
    "name": "Example Name",
    "accessPublicRead": false,
    "realm": "examplerealm",
    "type": "ThingAsset",
    "path": [
      "example"
    ],
    "attributes": {
      "carData": {
        "type": "JSONObject",
        "value": {
          "color": "red",
          "shape": "truck",
          "speed": 60
        },
        "name": "carData",
        "meta": {
          "storeDataPoints": true
        },
        "timestamp": 1707483616322
      }
    }
  }
]

Is it possible to get all Datapoints (including historic) of "carData" with "color": "green" AND "shape": "sedan" ordered by their "speed" descending?

I tried the asset/query/ like this and it gives me the latest Datapoint with "color": "green":

{
  "realm": {
    "name": "examplerealm"
  },
  "types": [
    "ThingAsset"
  ],
  "names": [
    {
      "match": "EXACT",
      "caseSensitive": true,
      "value": "Example Name",
      "negate": false,
      "predicateType": "string"
    }
  ],
  "attributes": {
    "items": [
      {
        "path": {
          "paths": [
            "color"
          ]
        },
        "name": {
          "match": "EXACT",
          "caseSensitive": true,
          "value": "carData",
          "negate": false,
          "predicateType": "string"
        },
        "value": {
          "predicateType": "string",
          "value": "green"
        }
      }
    ]
  }
}

If I throw an AND in there like in the following query, it just gives me the latest Datapoint ignoring the "color": "green" and "shape": "sedan".

{
  "realm": {
    "name": "examplerealm"
  },
  "types": [
    "ThingAsset"
  ],
  "names": [
    {
      "match": "EXACT",
      "caseSensitive": true,
      "value": "Example Name",
      "negate": false,
      "predicateType": "string"
    }
  ],
  "attributes": {
    "items": [
      {
        "operator": "AND",
        "items": [
          {
            "path": {
              "paths": [
                "color"
              ]
            },
            "name": {
              "match": "EXACT",
              "caseSensitive": true,
              "value": "carData",
              "negate": false,
              "predicateType": "string"
            },
            "value": {
              "predicateType": "string",
              "value": "green"
            }
          },
          {
            "path": {
              "paths": [
                "shape"
              ]
            },
            "name": {
              "match": "EXACT",
              "caseSensitive": true,
              "value": "carData",
              "negate": false,
              "predicateType": "string"
            },
            "value": {
              "predicateType": "string",
              "value": "sedan"
            }
          }
        ]
      }
    ]
  }
}

Also the asset/query/ endpoint seems only get me one Asset with the latest Datapoint.
There’s the /asset/datapoint/{assetId}/attribute/{attributeName} endpoint, but that one seems to support only time-based filtering.

If it’s not possible to do this with just the HTTP API, is there any other way with for example Rules? Or is the only solution to scrape all of the data and do the filtering and ordering outside of Openremote?

Hi Cristoph,

First of all welcome to the forum! :wave:

Yes there are many HTTP API endpoints available to request OpenRemote data from.
You might already have seen the swagger documentation of it, see here.

However, I will say that the swagger documentation is not amazing nor complete.

You’re indeed looking for POST /asset/datapoint/{assetId}/attribute/{attributeName}
It functions as a ‘query endpoint’ where you supply details of what datapoints it should return.
You can use one of these data decimation functions (or simply ‘all’);

For example;

{
  "type": "lttb",
  "fromTimestamp": 1707642000000,
  "toTimestamp": 1707728400000,
  "amountOfPoints": 100
}

.
On your question;
“Is it possible to get all Datapoints (including historic) of "carData" with "color": "green" AND "shape": "sedan" ordered by their "speed" descending?”

The API only provides the list of datapoints of 1 attribute (doesn’t matter which type),
so it would require processing in your own project to order / filter the results.

.
Other solutions I know is using a MetaItem called “Attribute links”;
with this you can have a JSON attribute, and tunnel values of them to separate ones.
In your case you would have a JSON attribute with the full payload, and the attributes ‘color’, ‘shape’, and ‘truck’ with individual values.

Hi Martin,

Thank you very much for your warm welcome and your response!

Yes I indeed went through the swagger documentation already. However I haven’t noticed the different ÀssetDatapointQuery functions. I’m getting a HTTP 400 when I use “lttb” like you explained. Maybe because interpolation isn’t supported with JSON?

I guess the “Attribute links” won’t be of much use in my case because I need the data in the JSON as a set and there might be multiple datapoints with the same timestamp, so I can’t identify where one tunneled value belongs to, to get the full JSON again?

You can check the logs of the manager, to see what specific error message it has.

A 400 error would mean it is either the JSON payload you sent with the POST request (make sure it has the type field). Or the attribute you’re requesting doesn’t have the STORE_DATAPOINTS MetaItem, which is required to save historical data at all.

.

That is indeed more tricky. If you want to process the data on the platform,
you could write rules in Groovy to process values programmatically.

And of course you can always process the values yourself outside of OpenRemote;
either beforehand, or afterwards by listening for attribute updates with our WebSocket / MQTT APIs.