iotagent-node-lib 4.4.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -272
- package/config.js +2 -1
- package/doc/README.md +1 -1
- package/doc/admin.md +19 -22
- package/doc/api.md +621 -155
- package/doc/deprecated.md +4 -0
- package/doc/devel/architecture.md +5 -135
- package/doc/devel/development.md +224 -12
- package/doc/getting-started.md +114 -53
- package/doc/requirements.txt +1 -1
- package/doc/roadmap.md +5 -5
- package/docker/Mosquitto/Dockerfile +2 -2
- package/docker/Mosquitto/README.md +14 -11
- package/lib/commonConfig.js +7 -1
- package/lib/constants.js +3 -0
- package/lib/fiware-iotagent-lib.js +12 -15
- package/lib/jexlTranformsMap.js +3 -1
- package/lib/model/dbConn.js +1 -4
- package/lib/services/common/alarmManagement.js +3 -0
- package/lib/services/devices/deviceRegistryMemory.js +1 -1
- package/lib/services/devices/deviceRegistryMongoDB.js +2 -2
- package/lib/services/devices/deviceService.js +4 -3
- package/lib/services/devices/devices-NGSI-LD.js +5 -5
- package/lib/services/devices/devices-NGSI-mixed.js +3 -3
- package/lib/services/devices/devices-NGSI-v2.js +5 -5
- package/lib/services/groups/groupService.js +1 -1
- package/lib/services/ngsi/entities-NGSI-LD.js +321 -571
- package/lib/services/ngsi/entities-NGSI-v2.js +348 -281
- package/lib/services/ngsi/ngsiService.js +3 -1
- package/lib/services/ngsi/subscription-NGSI-LD.js +2 -2
- package/lib/services/ngsi/subscription-NGSI-v2.js +2 -2
- package/lib/services/northBound/deviceGroupAdministrationServer.js +42 -6
- package/lib/services/northBound/deviceProvisioningServer.js +5 -5
- package/lib/services/northBound/northboundServer.js +4 -2
- package/lib/services/stats/statsRegistry.js +128 -101
- package/lib/templates/createDevice.json +0 -24
- package/lib/templates/createDeviceLax.json +0 -23
- package/lib/templates/deviceGroup.json +1 -25
- package/lib/templates/updateDevice.json +0 -24
- package/lib/templates/updateDeviceLax.json +0 -23
- package/package.json +2 -2
- package/scripts/legacy_expression_tool/README.md +0 -1
- package/test/functional/README.md +75 -47
- package/test/functional/functional-tests-runner.js +9 -4
- package/test/functional/functional-tests.js +4 -4
- package/test/functional/testCases.js +1251 -257
- package/test/functional/testUtils.js +53 -20
- package/test/unit/examples/deviceProvisioningRequests/provisionFullDevice.json +1 -13
- package/test/unit/examples/deviceProvisioningRequests/updateProvisionDeviceWithApikey.json +4 -0
- package/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json +44 -0
- package/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json +35 -0
- package/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json +36 -0
- package/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json +36 -0
- package/test/unit/general/deviceService-test.js +102 -0
- package/test/unit/general/statistics-service_test.js +1 -74
- package/test/unit/mongodb/mongodb-configGroup-registry-test.js +452 -0
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +2 -3
- package/test/unit/mongodb/mongodb-group-registry-test.js +34 -33
- package/test/unit/mongodb/mongodb-service-registry-test.js +477 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +4 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin2.json +22 -22
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +4 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +14 -15
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin1.json +23 -23
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin4.json +11 -16
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +23 -28
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin6.json +8 -13
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin7.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +24 -29
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +12 -17
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticLinkedAttributes.json +12 -10
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -102
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +4 -5
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +5 -5
- package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +0 -4
- package/test/unit/ngsiv2/general/deviceService-test.js +94 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +195 -0
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +259 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +1189 -0
- package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +6 -6
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +0 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +35 -0
- package/test/unit/statsRegistry/openmetrics-test.js +167 -0
- package/lib/templates/queryContext.json +0 -25
- package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +0 -35
- package/test/unit/examples/deviceProvisioningRequests/provisionDeviceBidirectionalGroup.json +0 -17
- package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +0 -31
- package/test/unit/general/statistics-persistence_test.js +0 -121
- package/test/unit/ngsi-ld/examples/contextRequests/createBidirectionalDevice.json +0 -17
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json +0 -12
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotification.json +0 -13
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithDatasetId.json +0 -21
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +0 -17
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json +0 -23
- package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +0 -132
- package/test/unit/ngsiv2/examples/contextRequests/createBidirectionalDevice.json +0 -8
- package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotification.json +0 -13
- package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +0 -19
- package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json +0 -24
package/doc/api.md
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
- [Devices](#devices)
|
|
11
11
|
- [Uniqueness of groups and devices](#uniqueness-of-groups-and-devices)
|
|
12
12
|
- [Special measures and attributes names](#special-measures-and-attributes-names)
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
13
|
+
- [Device to NGSI Mapping](#device-to-ngsi-mapping)
|
|
14
|
+
- [Device autoprovision and entity creation](#device-autoprovision-and-entity-creation)
|
|
15
|
+
- [Multientity support](#multientity-support)
|
|
15
16
|
- [Metadata support](#metadata-support)
|
|
16
17
|
- [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
|
|
17
18
|
- [Advice on Attribute definitions](#advice-on-attribute-definitions)
|
|
@@ -26,12 +27,18 @@
|
|
|
26
27
|
- [Examples of JEXL expressions](#examples-of-jexl-expressions)
|
|
27
28
|
- [Available functions](#available-functions)
|
|
28
29
|
- [Expressions with multiple transformations](#expressions-with-multiple-transformations)
|
|
30
|
+
- [Expression support in metadata](#expression-support-in-metadata)
|
|
29
31
|
- [Measurement transformation](#measurement-transformation)
|
|
30
32
|
- [Measurement transformation definition](#measurement-transformation-definition)
|
|
31
33
|
- [Measurement transformation execution](#measurement-transformation-execution)
|
|
32
34
|
- [Measurement transformation order](#measurement-transformation-order)
|
|
33
35
|
- [Multientity measurement transformation support (`object_id`)](#multientity-measurement-transformation-support-object_id)
|
|
34
|
-
- [
|
|
36
|
+
- [Command execution](#command-execution)
|
|
37
|
+
- [Triggering commands](#triggering-commands)
|
|
38
|
+
- [Command reception](#command-reception)
|
|
39
|
+
- [Command confirmation](#command-confirmation)
|
|
40
|
+
- [TimeInstant and Timestamp flag](#timeinstant-and-timestamp-flag)
|
|
41
|
+
- [Multimeasure support](#multimeasure-support)
|
|
35
42
|
- [Overriding global Context Broker host](#overriding-global-context-broker-host)
|
|
36
43
|
- [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
|
|
37
44
|
- [Secured access to the Context Broker](#secured-access-to-the-context-broker)
|
|
@@ -43,10 +50,10 @@
|
|
|
43
50
|
- [Config group API](#config-group-api)
|
|
44
51
|
- [Config group datamodel](#config-group-datamodel)
|
|
45
52
|
- [Config group operations](#config-group-operations)
|
|
46
|
-
- [Retrieve config groups `GET /iot/
|
|
47
|
-
- [Create config group `POST /iot/
|
|
48
|
-
- [Modify config group `PUT /iot/
|
|
49
|
-
- [Remove config group `DELETE /iot/
|
|
53
|
+
- [Retrieve config groups `GET /iot/groups`](#retrieve-config-groups-get-iotgroups)
|
|
54
|
+
- [Create config group `POST /iot/groups`](#create-config-group-post-iotgroups)
|
|
55
|
+
- [Modify config group `PUT /iot/groups`](#modify-config-group-put-iotgroups)
|
|
56
|
+
- [Remove config group `DELETE /iot/groups`](#remove-config-group-delete-iotgroups)
|
|
50
57
|
- [Device API](#device-api)
|
|
51
58
|
- [Device datamodel](#device-datamodel)
|
|
52
59
|
- [Device operations](#device-operations)
|
|
@@ -63,6 +70,8 @@
|
|
|
63
70
|
- [Retrieve log level `GET /admin/log`](#retrieve-log-level-get-adminlog)
|
|
64
71
|
- [About operations](#about-operations)
|
|
65
72
|
- [List IoTA Information `GET /iot/about`](#list-iota-information-get-iotabout)
|
|
73
|
+
- [Metrics](#metrics)
|
|
74
|
+
- [Retrieve metrics `GET /metrics`](#retrieve-metrics-get-metrics)
|
|
66
75
|
|
|
67
76
|
<!-- /TOC -->
|
|
68
77
|
|
|
@@ -127,9 +136,9 @@ For every config group, the pair (resource, apikey) _must_ be unique (as it is u
|
|
|
127
136
|
which device). Those operations of the API targeting specific resources will need the use of the `resource` and `apikey`
|
|
128
137
|
parameters to select the appropriate instance.
|
|
129
138
|
|
|
130
|
-
Config groups can be created with preconfigured sets of attributes, service information, security
|
|
131
|
-
parameters. The specific parameters that can be configured for a given config group are described
|
|
132
|
-
[Config group datamodel](#config-group-datamodel) section.
|
|
139
|
+
Config groups can be created with preconfigured sets of attributes, service and subservice information, security
|
|
140
|
+
information and other parameters. The specific parameters that can be configured for a given config group are described
|
|
141
|
+
in the [Config group datamodel](#config-group-datamodel) section.
|
|
133
142
|
|
|
134
143
|
### Devices
|
|
135
144
|
|
|
@@ -155,7 +164,7 @@ parameters defined at device level in database, the parameters are inherit from
|
|
|
155
164
|
|
|
156
165
|
### Uniqueness of groups and devices
|
|
157
166
|
|
|
158
|
-
Group
|
|
167
|
+
Group uniqueness is defined by the combination of: service, subservice, resource and apikey
|
|
159
168
|
|
|
160
169
|
Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
|
|
161
170
|
with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
|
|
@@ -170,32 +179,47 @@ applies to autoprovisioned attributes and is also available at JEXL context with
|
|
|
170
179
|
|
|
171
180
|
In case of provisioning attributes using `id` or `type` as names (please don't do that ;), they are ignored.
|
|
172
181
|
|
|
173
|
-
##
|
|
182
|
+
## Device to NGSI Mapping
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
The way to map the information coming or going to the device to the NGSI attributes is defined in the group or device.
|
|
185
|
+
It is possible to define the entity type and the entity ID that a device will use in the Context Broker. It can be
|
|
186
|
+
configured for a single device in the device provisioning, or it can be defined for all the devices in a group.
|
|
187
|
+
|
|
188
|
+
The entity type should be defined both in the group and in the device, but the entity name (entity ID) is not defined in
|
|
189
|
+
the group. In that case, if there is no a existing device the same device ID, the entity name of the device generated
|
|
190
|
+
will be a concatenation of the entity type and the device ID (I.E: `entityType:device_id`). It is possible to define the
|
|
191
|
+
entity name as an expression, using the [Expression Language](#expression-language-support) through the `entityNameExp`
|
|
192
|
+
attribute in the group.
|
|
193
|
+
|
|
194
|
+
It is also possible to configure how each of the measures obtained from the device is mapped to different attributes.
|
|
195
|
+
The name and type of the attribute is configured by the user (globally for all the devices in the group or in a per
|
|
196
|
+
device preprovisioning). Device measures can have four different behaviors:
|
|
177
197
|
|
|
178
198
|
- **`attributes`**: Are measures that are pushed from the device to the IoT agent. This measure changes will be sent
|
|
179
199
|
to the Context Broker as updateContext requests over the device entity. NGSI queries to the context broker will be
|
|
180
200
|
resolved in the Broker database. For each attribute, its `name` and `type` must be provided. Additional `metadata`
|
|
181
|
-
is optional.
|
|
201
|
+
is optional. They are called internally as _active attributes_.
|
|
182
202
|
|
|
183
203
|
- **`lazy`**: Passive measures that are pulled from the device to the IoT agent. When a request for data from a lazy
|
|
184
|
-
attribute arrives to the Context Broker, it forwards the request to the
|
|
185
|
-
|
|
186
|
-
a NGSI format and return it to the Context Broker. This operation will be
|
|
187
|
-
the Context Broker won't return a response until the device has returned
|
|
188
|
-
attribute, its `name` and `type` must be provided.
|
|
204
|
+
attribute arrives to the Context Broker, it forwards the request to the IoT Agent (that behaves as NGSI Context
|
|
205
|
+
Provider for all the lazy attributes or commands). The IoT Agent will then ask the device for the information
|
|
206
|
+
needed, transform that information to a NGSI format and return it to the Context Broker. This operation will be
|
|
207
|
+
synchronous from the customer perspective: the Context Broker won't return a response until the device has returned
|
|
208
|
+
its response to the IoT Agent. For each attribute, its `name` and `type` must be provided. They are called
|
|
209
|
+
internally as _lazy attributes_.
|
|
189
210
|
|
|
190
211
|
- **`static`**: It is static attributes that are persisted in the Context Broker. They are not updated by the device,
|
|
191
212
|
but they can be modified by the user. They are useful to store information about the device that is not updated by
|
|
192
213
|
the device itself. For instance, a `location` static attribute is can be used to store the location of a fixed
|
|
193
214
|
device.
|
|
194
215
|
|
|
195
|
-
- **`commands`**: Commands are actions that can be invoked in the device. They are
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
216
|
+
- **`commands`**: Commands are actions that can be invoked in the device. They are entity attributes, but they are not
|
|
217
|
+
updated by the device, they are updated by the Context Broker. In this case, the interaction will begin by setting
|
|
218
|
+
an attribute in the device's entity, for which the IoT Agent will be regitered as Context Provider. The IoT Agent
|
|
219
|
+
will return an immediate response to the Context Broker, and will be held responsible of contacting the device to
|
|
220
|
+
perform the command itself using the device specific protocol. Special `status` and `info` attributes should be
|
|
221
|
+
update. For each command, its `name` and `type` must be provided. For further information, please refer to
|
|
222
|
+
[Command execution](#command-execution) section.
|
|
199
223
|
|
|
200
224
|
All of them have the same syntax, a list of objects with the following attributes:
|
|
201
225
|
|
|
@@ -236,6 +260,17 @@ Note that, when information coming from devices, this means measures, are not de
|
|
|
236
260
|
device, the IoT agent will store that information into the destination entity using the same attribute name than the
|
|
237
261
|
measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored.
|
|
238
262
|
|
|
263
|
+
## Device autoprovision and entity creation
|
|
264
|
+
|
|
265
|
+
For those agents that uses IoTA Node LIB version 3.4.0 or higher, you should consider that the entity is not created
|
|
266
|
+
automatically when a device is created. This means that all entities into the Context Broker are created when data
|
|
267
|
+
arrives from a device, no matter if the device is explicitly provisioned (via
|
|
268
|
+
[device provisioning API](#create-device-post-iotdevices)) or autoprovisioned.
|
|
269
|
+
|
|
270
|
+
If for any reason you need the entity at CB before the first measure of the corresponding device arrives to the
|
|
271
|
+
IOTAgent, you can create it in advance using the Context Broker
|
|
272
|
+
[NGSI v2 API](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md).
|
|
273
|
+
|
|
239
274
|
## Multientity support
|
|
240
275
|
|
|
241
276
|
The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities
|
|
@@ -286,69 +321,37 @@ e.g.:
|
|
|
286
321
|
|
|
287
322
|
```json
|
|
288
323
|
{
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
{"name": "
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
{
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
],
|
|
305
|
-
"static_attributes": [
|
|
306
|
-
{"name": "category", "type":"Text", "value": ["actuator","sensor"]},
|
|
307
|
-
{"name": "controlledProperty", "type": "Text", "value": ["light"],
|
|
308
|
-
"metadata":{
|
|
309
|
-
"includes":{"type": "Text", "value" :["state", "luminosity"]},
|
|
310
|
-
"alias":{"type": "Text", "value" :"lamp"}
|
|
324
|
+
"entity_type": "Lamp",
|
|
325
|
+
"resource": "/iot/d",
|
|
326
|
+
"protocol": "PDI-IoTA-UltraLight",
|
|
327
|
+
"commands": [
|
|
328
|
+
{ "name": "on", "type": "command" },
|
|
329
|
+
{ "name": "off", "type": "command" }
|
|
330
|
+
],
|
|
331
|
+
"attributes": [
|
|
332
|
+
{ "object_id": "s", "name": "state", "type": "Text" },
|
|
333
|
+
{
|
|
334
|
+
"object_id": "l",
|
|
335
|
+
"name": "luminosity",
|
|
336
|
+
"type": "Integer",
|
|
337
|
+
"metadata": {
|
|
338
|
+
"unitCode": { "type": "Text", "value": "CAL" }
|
|
311
339
|
}
|
|
312
|
-
},
|
|
313
|
-
]
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Metadata could also has `expression` like attributes in order to expand it:
|
|
318
|
-
|
|
319
|
-
e.g.:
|
|
320
|
-
|
|
321
|
-
```json
|
|
322
|
-
{
|
|
323
|
-
"entity_type": "Lamp",
|
|
324
|
-
"resource": "/iot/d",
|
|
325
|
-
"protocol": "PDI-IoTA-UltraLight",
|
|
326
|
-
..etc
|
|
327
|
-
"commands": [
|
|
328
|
-
{"name": "on","type": "command"},
|
|
329
|
-
{"name": "off","type": "command"}
|
|
330
|
-
],
|
|
331
|
-
"attributes": [
|
|
332
|
-
{"object_id": "s", "name": "state", "type":"Text"},
|
|
333
|
-
{"object_id": "l", "name": "luminosity", "type":"Integer",
|
|
334
|
-
"metadata":{
|
|
335
|
-
"unitCode":{"type": "Text", "value" :"CAL"}
|
|
336
|
-
}
|
|
337
340
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
"
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
341
|
+
],
|
|
342
|
+
"static_attributes": [
|
|
343
|
+
{ "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
|
|
344
|
+
{
|
|
345
|
+
"name": "controlledProperty",
|
|
346
|
+
"type": "Text",
|
|
347
|
+
"value": ["light"],
|
|
348
|
+
"metadata": {
|
|
349
|
+
"includes": { "type": "Text", "value": ["state", "luminosity"] },
|
|
350
|
+
"alias": { "type": "Text", "value": "lamp" }
|
|
348
351
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
}
|
|
352
355
|
```
|
|
353
356
|
|
|
354
357
|
### NGSI-LD data and metadata considerations
|
|
@@ -458,6 +461,9 @@ mappings of the provision. If `explicitAttrs` is provided both at device and con
|
|
|
458
461
|
precedence. Additionally `explicitAttrs` can be used to define which measures (identified by their attribute names, not
|
|
459
462
|
by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
|
|
460
463
|
|
|
464
|
+
Note that when `explicitAttrs` is an array or a JEXL expression resulting in to Array, if this array is empty then
|
|
465
|
+
`TimeInstant` is not propaged to CB.
|
|
466
|
+
|
|
461
467
|
The different possibilities are summarized below:
|
|
462
468
|
|
|
463
469
|
Case 1 (default):
|
|
@@ -546,6 +552,7 @@ really useful when you need to adapt measure (for example, to change the units,
|
|
|
546
552
|
of expression in the IoT Agent are:
|
|
547
553
|
|
|
548
554
|
- [Measurement transformation](#measurement-transformation).
|
|
555
|
+
- [Metadata](#expression-support-in-metadata)
|
|
549
556
|
- Commands payload transformation (push and pull).
|
|
550
557
|
- Auto provisioned devices entity name. It is configured at config Group level by setting the `entityNameExp`
|
|
551
558
|
parameter. It defines an expression to generate the Entity Name for autoprovisioned devices.
|
|
@@ -558,12 +565,18 @@ expression. In all cases the following data is available to all expressions:
|
|
|
558
565
|
- `id`: device ID
|
|
559
566
|
- `entity_name`: NGSI entity Name (principal)
|
|
560
567
|
- `type`: NGSI entity type (principal)
|
|
561
|
-
- `service`: device service
|
|
562
|
-
- `subservice`: device subservice
|
|
568
|
+
- `service`: device service (`Fiware-Service`)
|
|
569
|
+
- `subservice`: device subservice (`Fiware-ServicePath`)
|
|
563
570
|
- `staticAttributes`: static attributes defined in the device or config group
|
|
564
571
|
|
|
565
572
|
Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions
|
|
566
|
-
(`expression`)
|
|
573
|
+
(`expression`) the following is available in the **context** used to evalute:
|
|
574
|
+
|
|
575
|
+
- measures, as `<AttributeName>`
|
|
576
|
+
- metadata (both for attribute measurement in the case of NGSI-v2 measurements and static attribute) are available in
|
|
577
|
+
the **context** under the following convention: `metadata.<AttributeName>.<MetadataName>` or
|
|
578
|
+
`metadata.<StaticAttributeName>.<MetadataName>` in a similar way of defined for
|
|
579
|
+
[Context Broker](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md#metadata-support)
|
|
567
580
|
|
|
568
581
|
### Examples of JEXL expressions
|
|
569
582
|
|
|
@@ -609,50 +622,52 @@ to incorporate new transformations from the IoT Agent configuration file in a fa
|
|
|
609
622
|
|
|
610
623
|
Current common transformation set:
|
|
611
624
|
|
|
612
|
-
| JEXL Transformation | Equivalent JavaScript Function
|
|
613
|
-
| ------------------------------------ |
|
|
614
|
-
| jsonparse: (str) | `JSON.parse(str);`
|
|
615
|
-
| jsonstringify: (obj) | `JSON.stringify(obj);`
|
|
616
|
-
| indexOf: (val, char) | `String(val).indexOf(char);`
|
|
617
|
-
| length: (val) | `String(val).length;`
|
|
618
|
-
| trim: (val) | `String(val).trim();`
|
|
619
|
-
| substr: (val, int1, int2) | `String(val).substr(int1, int2);`
|
|
620
|
-
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code>
|
|
621
|
-
| lengtharray: (arr) | `arr.length;`
|
|
622
|
-
| typeof: (val) | `typeof val;`
|
|
623
|
-
| isarray: (arr) | `Array.isArray(arr);`
|
|
624
|
-
| isnan: (val) | `isNaN(val);`
|
|
625
|
-
| parseint: (val) | `parseInt(val);`
|
|
626
|
-
| parsefloat: (val) | `parseFloat(val);`
|
|
627
|
-
| toisodate: (val) | `new Date(val).toISOString();`
|
|
628
|
-
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();`
|
|
629
|
-
| tostring: (val) | `val.toString();`
|
|
630
|
-
| urlencode: (val) | `encodeURI(val);`
|
|
631
|
-
| urldecode: (val) | `decodeURI(val);`
|
|
632
|
-
| replacestr: (str, from, to) | `str.replace(from, to);`
|
|
633
|
-
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);`
|
|
634
|
-
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);`
|
|
635
|
-
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);`
|
|
636
|
-
| split: (str, ch) | `str.split(ch);`
|
|
637
|
-
| joinarrtostr: (arr, ch) | `arr.join(ch);`
|
|
638
|
-
| concatarr: (arr, arr2) | `arr.concat(arr2);`
|
|
639
|
-
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code>
|
|
640
|
-
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code>
|
|
641
|
-
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code>
|
|
642
|
-
| slice: (arr, init, end) | `arr.slice(init,end);`
|
|
643
|
-
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code>
|
|
644
|
-
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code>
|
|
645
|
-
| touppercase: (val) | `String(val).toUpperCase()`
|
|
646
|
-
| tolowercase: (val) | `String(val).toLowerCase()`
|
|
647
|
-
| round: (val) | `Math.round(val)`
|
|
648
|
-
| floor: (val) | `Math.floor(val)`
|
|
649
|
-
| ceil: (val) | `Math.ceil(val)`
|
|
650
|
-
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)`
|
|
651
|
-
| gettime: (d) | `new Date(d).getTime()`
|
|
652
|
-
| toisostring: (d) | `new Date(d).toISOString()`
|
|
653
|
-
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)`
|
|
654
|
-
| now: () | `Date.now()`
|
|
655
|
-
| hextostring: (val) | `new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))`
|
|
625
|
+
| JEXL Transformation | Equivalent JavaScript Function |
|
|
626
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
627
|
+
| jsonparse: (str) | `JSON.parse(str);` |
|
|
628
|
+
| jsonstringify: (obj) | `JSON.stringify(obj);` |
|
|
629
|
+
| indexOf: (val, char) | `String(val).indexOf(char);` |
|
|
630
|
+
| length: (val) | `String(val).length;` |
|
|
631
|
+
| trim: (val) | `String(val).trim();` |
|
|
632
|
+
| substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
|
|
633
|
+
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code> |
|
|
634
|
+
| lengtharray: (arr) | `arr.length;` |
|
|
635
|
+
| typeof: (val) | `typeof val;` |
|
|
636
|
+
| isarray: (arr) | `Array.isArray(arr);` |
|
|
637
|
+
| isnan: (val) | `isNaN(val);` |
|
|
638
|
+
| parseint: (val) | `parseInt(val);` |
|
|
639
|
+
| parsefloat: (val) | `parseFloat(val);` |
|
|
640
|
+
| toisodate: (val) | `new Date(val).toISOString();` |
|
|
641
|
+
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
|
|
642
|
+
| tostring: (val) | `val.toString();` |
|
|
643
|
+
| urlencode: (val) | `encodeURI(val);` |
|
|
644
|
+
| urldecode: (val) | `decodeURI(val);` |
|
|
645
|
+
| replacestr: (str, from, to) | `str.replace(from, to);` |
|
|
646
|
+
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
|
|
647
|
+
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
|
|
648
|
+
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
|
|
649
|
+
| split: (str, ch) | `str.split(ch);` |
|
|
650
|
+
| joinarrtostr: (arr, ch) | `arr.join(ch);` |
|
|
651
|
+
| concatarr: (arr, arr2) | `arr.concat(arr2);` |
|
|
652
|
+
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code> |
|
|
653
|
+
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
|
|
654
|
+
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
|
|
655
|
+
| slice: (arr, init, end) | `arr.slice(init,end);` |
|
|
656
|
+
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
|
|
657
|
+
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
|
|
658
|
+
| touppercase: (val) | `String(val).toUpperCase()` |
|
|
659
|
+
| tolowercase: (val) | `String(val).toLowerCase()` |
|
|
660
|
+
| round: (val) | `Math.round(val)` |
|
|
661
|
+
| floor: (val) | `Math.floor(val)` |
|
|
662
|
+
| ceil: (val) | `Math.ceil(val)` |
|
|
663
|
+
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)` |
|
|
664
|
+
| gettime: (d) | `new Date(d).getTime()` |
|
|
665
|
+
| toisostring: (d) | `new Date(d).toISOString()` |
|
|
666
|
+
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)` |
|
|
667
|
+
| now: () | `Date.now()` |
|
|
668
|
+
| hextostring: (val) | `new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))` |
|
|
669
|
+
| valuePicker: (val,pick) | <code>valuePicker: (val,pick) => Object.entries(val).filter(([_, v]) => v === pick).map(([k, _]) => k)</code> |
|
|
670
|
+
| valuePickerMulti: (val,pick) | <code>valuePickerMulti: (val,pick) => Object.entries(val).filter(([_, v]) => pick.includes(v)).map(([k, _]) => k)</code> |
|
|
656
671
|
|
|
657
672
|
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
658
673
|
test all the functions described above.
|
|
@@ -681,6 +696,50 @@ Another example using functions that return more than one value is the following
|
|
|
681
696
|
|
|
682
697
|
For a location value `"40.4165, -3.70256"`, the result of the previous expression will be `-3.70256`.
|
|
683
698
|
|
|
699
|
+
### Expression support in metadata
|
|
700
|
+
|
|
701
|
+
Metadata could also has `expression` like attributes in order to expand it:
|
|
702
|
+
|
|
703
|
+
e.g.:
|
|
704
|
+
|
|
705
|
+
```json
|
|
706
|
+
{
|
|
707
|
+
"entity_type": "Lamp",
|
|
708
|
+
"resource": "/iot/d",
|
|
709
|
+
"protocol": "PDI-IoTA-UltraLight",
|
|
710
|
+
"commands": [
|
|
711
|
+
{ "name": "on", "type": "command" },
|
|
712
|
+
{ "name": "off", "type": "command" }
|
|
713
|
+
],
|
|
714
|
+
"attributes": [
|
|
715
|
+
{ "object_id": "s", "name": "state", "type": "Text" },
|
|
716
|
+
{
|
|
717
|
+
"object_id": "l",
|
|
718
|
+
"name": "luminosity",
|
|
719
|
+
"type": "Integer",
|
|
720
|
+
"metadata": {
|
|
721
|
+
"unitCode": { "type": "Text", "value": "CAL" }
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
],
|
|
725
|
+
"static_attributes": [
|
|
726
|
+
{ "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
|
|
727
|
+
{
|
|
728
|
+
"name": "controlledProperty",
|
|
729
|
+
"type": "Text",
|
|
730
|
+
"value": ["light"],
|
|
731
|
+
"metadata": {
|
|
732
|
+
"includes": { "type": "Text", "value": ["state", "luminosity"], "expression": "level / 100" },
|
|
733
|
+
"alias": { "type": "Text", "value": "lamp" }
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
]
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
Note that there is no order into metadata structure and there is no warranty about which metadata attribute expression
|
|
741
|
+
will be evaluated first.
|
|
742
|
+
|
|
684
743
|
## Measurement transformation
|
|
685
744
|
|
|
686
745
|
The IoTAgent Library provides support for measurement transformation using a
|
|
@@ -1007,13 +1066,34 @@ Will now generate the following NGSI v2 payload:
|
|
|
1007
1066
|
}
|
|
1008
1067
|
```
|
|
1009
1068
|
|
|
1010
|
-
## Timestamp
|
|
1069
|
+
## TimeInstant and Timestamp flag
|
|
1070
|
+
|
|
1071
|
+
As part of the device to entity mapping process, the IoT Agent creates and updates automatically a special timestamp
|
|
1072
|
+
attribute called `TimeInstant`. This timestamp is represented as two different properties of the mapped entity:
|
|
1011
1073
|
|
|
1012
|
-
|
|
1074
|
+
- An attribute `TimeInstant` is added to updated entities in the case of NGSI-v2, which captures as an ISO8601
|
|
1075
|
+
timestamp when the associated measurement was observed. With NGSI-LD, the Standard `observedAt` property is used
|
|
1076
|
+
instead
|
|
1013
1077
|
|
|
1014
|
-
-
|
|
1015
|
-
|
|
1016
|
-
`observedAt` property-of-a-property is used instead.
|
|
1078
|
+
- With NGSI-v2, an attribute metadata named `TimeInstant` per active or lazy attribute mapped, which captures as an
|
|
1079
|
+
ISO8601 timestamp when the associated measurement (represented as attribute value) was observed. With NGSI-LD, the
|
|
1080
|
+
Standard `observedAt` property-of-a-property is used instead.
|
|
1081
|
+
|
|
1082
|
+
If timestamp is not explicily defined when sending the measures through the IoT Agent (for further details on how to
|
|
1083
|
+
attach timestamp information to the measures, please refer to the particular IoT Agent implementation documentation),
|
|
1084
|
+
the arrival time on the server when receiving the measurement will be used to generate a `TimeInstant` for both the
|
|
1085
|
+
entity attribute and the attribute metadata.
|
|
1086
|
+
|
|
1087
|
+
This functionality can be turned on and off globaly through the use of the `timestamp` configuration flag or
|
|
1088
|
+
`IOTA_TIMESTAMP` variable as well as `timestamp` flag in device or group provision (in this case, the device or group
|
|
1089
|
+
level flag takes precedence over the global one). The default value is `true`.
|
|
1090
|
+
|
|
1091
|
+
The `timestamp` configuration value used, according to the priority:
|
|
1092
|
+
|
|
1093
|
+
1. The one defined at device level
|
|
1094
|
+
2. The one defined at group level (if not defined at device level)
|
|
1095
|
+
3. The one defined at [IoTA configuration level](admin.md#timestamp) / `IOTA_TIMESTAMP` env var (if not defined at
|
|
1096
|
+
group level or device level)
|
|
1017
1097
|
|
|
1018
1098
|
Depending on the `timestamp` configuration and if the measure contains a value named `TimeInstant` with a correct value,
|
|
1019
1099
|
the IoTA behaviour is described in the following table:
|
|
@@ -1027,13 +1107,6 @@ the IoTA behaviour is described in the following table:
|
|
|
1027
1107
|
| Not defined | Yes | TimeInstant and metadata updated with measure value |
|
|
1028
1108
|
| Not defined | No | TimeInstant and metadata updated with server timestamp |
|
|
1029
1109
|
|
|
1030
|
-
The `timestamp` conf value used is:
|
|
1031
|
-
|
|
1032
|
-
- The one defined at device level
|
|
1033
|
-
- The one defined at group level (if not defined at device level)
|
|
1034
|
-
- The one defined at [IoTA configuration level](admin.md#timestamp) / `IOTA_TIMESTAMP` env var (if not defined at
|
|
1035
|
-
group level or device level)
|
|
1036
|
-
|
|
1037
1110
|
Some additional considerations to take into account:
|
|
1038
1111
|
|
|
1039
1112
|
- If there is an attribute which maps a measure to `TimeInstant` attribute (after
|
|
@@ -1043,6 +1116,348 @@ Some additional considerations to take into account:
|
|
|
1043
1116
|
- If the resulting `TimeInstant` not follows [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) (either from a direct
|
|
1044
1117
|
measure of after a mapping, as described in the previous bullet) then it is refused (so a failover to server
|
|
1045
1118
|
timestamp will take place).
|
|
1119
|
+
- the timestamp of different attributes belonging to the same measurement record may not be equal.
|
|
1120
|
+
- the arrival time and the measurement timestamp will not be the same in the general case (when explicitly defining
|
|
1121
|
+
the timestamp in the measurement)
|
|
1122
|
+
- if `timezone` field is defined as part of the provisioning of the device or group, timestamp fields will be
|
|
1123
|
+
generated using it. For instance, if `timezone` is set to `America/Los_Angeles`, a possible timestamp could be
|
|
1124
|
+
`2025-08-05T00:35:01.468-07:00`. If `timezone` field is not defined, by default Zulu Time Zone (UTC +0) will be
|
|
1125
|
+
used. Following the previous example, timestamp could be `2015-08-05T07:35:01.468Z`.
|
|
1126
|
+
|
|
1127
|
+
E.g.: in the case of a device that can take measurements every hour of both temperature and humidity and sends the data
|
|
1128
|
+
once every day, at midnight, the `TimeInstant` reported for each measurement will be the hour when that measurement was
|
|
1129
|
+
observed (e.g. 4:00 PM), while all the measurements will have an arrival time around midnight. If no timestamps were
|
|
1130
|
+
reported with such measurements, the `TimeInstant` attribute would take those values around midnight.
|
|
1131
|
+
|
|
1132
|
+
## Multimeasure support
|
|
1133
|
+
|
|
1134
|
+
A device could receive several measures at the same time.
|
|
1135
|
+
|
|
1136
|
+
For example:
|
|
1137
|
+
|
|
1138
|
+
```json
|
|
1139
|
+
[
|
|
1140
|
+
{
|
|
1141
|
+
"vol": 0
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
"vol": 1
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
"vol": 2
|
|
1148
|
+
}
|
|
1149
|
+
]
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
In this case a batch update (`POST /v2/op/update`) to CB will be generated with the following NGSI v2 payload:
|
|
1153
|
+
|
|
1154
|
+
```json
|
|
1155
|
+
{
|
|
1156
|
+
"actionType": "append",
|
|
1157
|
+
"entities": [
|
|
1158
|
+
{
|
|
1159
|
+
"id": "ws",
|
|
1160
|
+
"type": "WeatherStation",
|
|
1161
|
+
"vol": {
|
|
1162
|
+
"type": "Number",
|
|
1163
|
+
"value": 0
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
"id": "ws",
|
|
1168
|
+
"type": "WeatherStation",
|
|
1169
|
+
"vol": {
|
|
1170
|
+
"type": "Number",
|
|
1171
|
+
"value": 1
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
"id": "ws",
|
|
1176
|
+
"type": "WeatherStation",
|
|
1177
|
+
"vol": {
|
|
1178
|
+
"type": "Number",
|
|
1179
|
+
"value": 1
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
]
|
|
1183
|
+
}
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
Moreover if a multimeasure contains TimeInstant attribute, then CB update is sorted by attribute TimeInstant:
|
|
1187
|
+
|
|
1188
|
+
For example:
|
|
1189
|
+
|
|
1190
|
+
```json
|
|
1191
|
+
[
|
|
1192
|
+
{
|
|
1193
|
+
"vol": 0,
|
|
1194
|
+
"TimeInstant": "2024-04-10T10:15:00Z"
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
"vol": 1,
|
|
1198
|
+
"TimeInstant": "2024-04-10T10:10:00Z"
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
"vol": 2,
|
|
1202
|
+
"TimeInstant": "2024-04-10T10:05:00Z"
|
|
1203
|
+
}
|
|
1204
|
+
]
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
In this case a batch update (`POST /v2/op/update`) to CB will be generated with the following NGSI v2 payload:
|
|
1208
|
+
|
|
1209
|
+
```json
|
|
1210
|
+
{
|
|
1211
|
+
"actionType": "append",
|
|
1212
|
+
"entities": [
|
|
1213
|
+
{
|
|
1214
|
+
"id": "ws",
|
|
1215
|
+
"type": "WeatherStation",
|
|
1216
|
+
"vol": {
|
|
1217
|
+
"type": "Number",
|
|
1218
|
+
"value": 2
|
|
1219
|
+
},
|
|
1220
|
+
"TimeInstant": {
|
|
1221
|
+
"type": "DateTime",
|
|
1222
|
+
"value": "2024-04-10T10:05:00Z"
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
"id": "ws",
|
|
1227
|
+
"type": "WeatherStation",
|
|
1228
|
+
"vol": {
|
|
1229
|
+
"type": "Number",
|
|
1230
|
+
"value": 1
|
|
1231
|
+
},
|
|
1232
|
+
"TimeInstant": {
|
|
1233
|
+
"type": "DateTime",
|
|
1234
|
+
"value": "2024-04-10T10:10:00Z"
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
"id": "ws",
|
|
1239
|
+
"type": "WeatherStation",
|
|
1240
|
+
"vol": {
|
|
1241
|
+
"type": "Number",
|
|
1242
|
+
"value": 0
|
|
1243
|
+
},
|
|
1244
|
+
"TimeInstant": {
|
|
1245
|
+
"type": "DateTime",
|
|
1246
|
+
"value": "2024-04-10T10:15:00Z"
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
]
|
|
1250
|
+
}
|
|
1251
|
+
```
|
|
1252
|
+
|
|
1253
|
+
## Command execution
|
|
1254
|
+
|
|
1255
|
+
This section reviews the end-to-end process to trigger and receive commands into devices. The URL paths and messages
|
|
1256
|
+
format is based on the [IoT Agent JSON](https://github.com/telefonicaid/iotagent-json). It may differ in the case of
|
|
1257
|
+
using any other IoT Agent. In that case, please refer to the specific IoTA documentation.
|
|
1258
|
+
|
|
1259
|
+
### Triggering commands
|
|
1260
|
+
|
|
1261
|
+
This starts the process of sending data to devices. It starts by updating an attribute into the Context Broker defined
|
|
1262
|
+
as `command` in the [config group](#config-group-datamodel) or in the [device provision](#device-datamodel). Commands
|
|
1263
|
+
attributes are created using `command` as attribute type. Also, you can define the protocol you want the commands to be
|
|
1264
|
+
sent (HTTP/MQTT) with the `transport` parameter at the provisioning process.
|
|
1265
|
+
|
|
1266
|
+
For a given device provisioned with a `ping` command defined, any update on this attribute "ping" at the NGSI entity in
|
|
1267
|
+
the Context Broker will send a command to your device. For instance, to send the `ping` command with value
|
|
1268
|
+
`Ping request` you could use the following operation in the Context Broker API:
|
|
1269
|
+
|
|
1270
|
+
```
|
|
1271
|
+
PUT /v2/entities/<entity_id>/attrs/ping?type=<entity_type>
|
|
1272
|
+
|
|
1273
|
+
{
|
|
1274
|
+
"value": "Ping request",
|
|
1275
|
+
"type": "command"
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
It is important to note that parameter `type`, with the entity type must be included.
|
|
1281
|
+
|
|
1282
|
+
Context Broker API is quite flexible and allows to update an attribute in several ways. Please have a look to the
|
|
1283
|
+
[Orion API](<[http://telefonicaid.github.io/fiware-orion/api/v2/stable](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md)>)
|
|
1284
|
+
for details.
|
|
1285
|
+
|
|
1286
|
+
**Important note**: don't use operations in the NGSI API with creation semantics. Otherwise, the entity/attribute will
|
|
1287
|
+
be created locally to Context Broker and the command will not progress to the device (and you will need to delete the
|
|
1288
|
+
created entity/attribute if you want to make it to work again). Thus, the following operations _must not_ be used:
|
|
1289
|
+
|
|
1290
|
+
- `POST /v2/entities`
|
|
1291
|
+
- `POST /v2/entities/<id>/attrs`
|
|
1292
|
+
- `PUT /v2/entities/<id>/attrs`
|
|
1293
|
+
- `POST /v2/op/entites` with `actionType` `append`, `appendStrict` or `replace`
|
|
1294
|
+
|
|
1295
|
+
### Command reception
|
|
1296
|
+
|
|
1297
|
+
Once the command is triggered, it is send to the device. Depending on transport protocol, it is going to be sent to the
|
|
1298
|
+
device in a different way. After sending the command, the IoT Agent will have updated the value of `ping_status` to
|
|
1299
|
+
`PENDING` for entity into the Context Broker. Neither `ping_info` nor `ping` will be updated.
|
|
1300
|
+
|
|
1301
|
+
#### HTTP devices
|
|
1302
|
+
|
|
1303
|
+
**Push commands**
|
|
1304
|
+
|
|
1305
|
+
Push commands are those that are sent to the device once the IoT Agent receives the request from the Context Broker. In
|
|
1306
|
+
order to send push commands it is needed to set the `"endpoint": "http://[DEVICE_IP]:[PORT]/"` in the device or group
|
|
1307
|
+
provision. The device is supposed to be listening for commands at that URL in a synchronous way. Make sure the device
|
|
1308
|
+
endpoint is reachable by the IoT Agent. Push commands are only valid for HTTP devices. For MQTT devices it is not needed
|
|
1309
|
+
to set the `endpoint` parameter.
|
|
1310
|
+
|
|
1311
|
+
Considering using the IoTA-JSON Agent, and given the previous example, the device should receive a POST request to
|
|
1312
|
+
`http://[DEVICE_IP]:[PORT]` with the following payload:
|
|
1313
|
+
|
|
1314
|
+
```
|
|
1315
|
+
POST /
|
|
1316
|
+
Content-Type: application/json
|
|
1317
|
+
|
|
1318
|
+
{"ping":"Ping request"}
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
**Poll commands**
|
|
1322
|
+
|
|
1323
|
+
Poll commands are those that are stored in the IoT Agent waiting to be retrieved by the devices. This kind of commands
|
|
1324
|
+
are typically used for devices that doesn't have a public IP or the IP cannot be reached because of power or netkork
|
|
1325
|
+
constrictions. The device connects to the IoT Agent periodically to retrieve commands. In order to configure the device
|
|
1326
|
+
as poll commands you just need to avoid the usage of `endpoint` parameter in the device provision.
|
|
1327
|
+
|
|
1328
|
+
Once the command request is issued to the IoT agent, the command is stored waiting to be retrieved by the device. In
|
|
1329
|
+
that moment, the status of the command is `"<command>_status": "PENDING"`.
|
|
1330
|
+
|
|
1331
|
+
For HTTP devices, in order to retrieve a poll command, the need to make a GET request to the IoT Agent to the path used
|
|
1332
|
+
as `resource` in the provisioned group (`/iot/json` by default in IoTA-JSON if no `resource` is used) with the following
|
|
1333
|
+
parameters:
|
|
1334
|
+
|
|
1335
|
+
**FIXME [#1524](https://github.com/telefonicaid/iotagent-node-lib/issues/1524)**: `resource` different to the default
|
|
1336
|
+
one (`/iot/json` in the case of the [IoTA-JSON](https://github.com/telefonicaid/iotagent-json)) is not working at the
|
|
1337
|
+
present moment, but it will when this issue gets solved.
|
|
1338
|
+
|
|
1339
|
+
- `k`: API key of the device.
|
|
1340
|
+
- `i`: Device ID.
|
|
1341
|
+
- `getCmd`: This parameter is used to indicate the IoT Agent that the device is requesting a command. It is needed to
|
|
1342
|
+
set it to `1`
|
|
1343
|
+
|
|
1344
|
+
Taking the previous example, and considering the usage of the IoTA-JSON Agent, the device should make the following
|
|
1345
|
+
request, being the response to this request a JSON object with the command name as key and the command value as value:
|
|
1346
|
+
|
|
1347
|
+
**Request:**
|
|
1348
|
+
|
|
1349
|
+
```
|
|
1350
|
+
GET /iot/json?k=<apikey>&i=<deviceId>&getCmd=1
|
|
1351
|
+
Accept: application/json
|
|
1352
|
+
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
**Response:**:
|
|
1356
|
+
|
|
1357
|
+
```
|
|
1358
|
+
200 OK
|
|
1359
|
+
Content-type: application/json
|
|
1360
|
+
|
|
1361
|
+
{"ping":"Ping request"}
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
For IoT Agents different from IoTA-JSON it is exactly the same just changing in the request the resource by the
|
|
1365
|
+
corresponding resource employed by the agent (i.e., IoTA-UL uses `/iot/d` as default resource instead of `/iot/json`)
|
|
1366
|
+
and setting the correct `<apikey>` and `<deviceId>`. The response will be also different depending on the IoT Agent
|
|
1367
|
+
employed.
|
|
1368
|
+
|
|
1369
|
+
**FIXME [#1524](https://github.com/telefonicaid/iotagent-node-lib/issues/1524)**: `resource` different to the default
|
|
1370
|
+
one (`/iot/json` in the case of the [IoTA-JSON](https://github.com/telefonicaid/iotagent-json)) is not working at the
|
|
1371
|
+
present moment, but it will when this issue gets solved.
|
|
1372
|
+
|
|
1373
|
+
**Request**
|
|
1374
|
+
|
|
1375
|
+
```
|
|
1376
|
+
POST /iot/json?k=<apikey>&i=<deviceId>&getCmd=1
|
|
1377
|
+
Content-Type: application/json
|
|
1378
|
+
|
|
1379
|
+
{"t":25,"h":42,"l":"1299"}
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
**Response**
|
|
1383
|
+
|
|
1384
|
+
```
|
|
1385
|
+
200 OK
|
|
1386
|
+
Content-type: application/json
|
|
1387
|
+
|
|
1388
|
+
{"ping":"Ping request"}
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
This is also possible for IoTA-UL Agent changing in the request the resource, setting the correct `<apikey>`,
|
|
1392
|
+
`<deviceId>`, payload and headers.
|
|
1393
|
+
|
|
1394
|
+
Once the command is retrieved by the device the status is updated to `"<command>_status": "DELIVERED"`. Note that status
|
|
1395
|
+
`DELIVERED` only make sense in the case of poll commands. In the case of push command it cannot happen.
|
|
1396
|
+
|
|
1397
|
+
#### MQTT devices
|
|
1398
|
+
|
|
1399
|
+
For MQTT devices, it is not needed to declare an endpoint (i.e. if included in the provisioning request, it is not
|
|
1400
|
+
used). The device is supposed to be subscribed to the following MQTT topic where the IoT Agent will publish the command:
|
|
1401
|
+
|
|
1402
|
+
```
|
|
1403
|
+
/<apiKey>/<deviceId>/cmd
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
In the case of using the IoTA-JSON Agent, the device should subscribe to the previous topic where it is going to receive
|
|
1407
|
+
a message like the following one when a command is triggered in the Context Broker like the previous step:
|
|
1408
|
+
|
|
1409
|
+
```json
|
|
1410
|
+
{ "ping": "Ping request" }
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
Please note that the device should subscribe to the broker using the disabled clean session mode (enabled using
|
|
1414
|
+
`--disable-clean-session` option CLI parameter in `mosquitto_sub`). This option means that all of the subscriptions for
|
|
1415
|
+
the device will be maintained after it disconnects, along with subsequent QoS 1 and QoS 2 commands that arrive. When the
|
|
1416
|
+
device reconnects, it will receive all of the queued commands.
|
|
1417
|
+
|
|
1418
|
+
### Command confirmation
|
|
1419
|
+
|
|
1420
|
+
Once the command is completely processed by the device, it should return the result of the command to the IoT Agent.
|
|
1421
|
+
This result will be progressed to the Context Broker where it will be stored in the `<command>_info` attribute. The
|
|
1422
|
+
status of the command will be stored in the `<command>_status` attribute (`OK` if everything goes right).
|
|
1423
|
+
|
|
1424
|
+
For the IoTA-JSON, the payload of the confirmation message must be a JSON object with name of the command as key and the
|
|
1425
|
+
result of the command as value. For other IoT Agents, the payload must follow the corresponding protocol. For a given
|
|
1426
|
+
`ping` command, with a command result `status_ok`, the response payload should be:
|
|
1427
|
+
|
|
1428
|
+
```JSON
|
|
1429
|
+
{"ping":"status_ok"}
|
|
1430
|
+
```
|
|
1431
|
+
|
|
1432
|
+
Eventually, once the device makes the response request the IoTA would update the attributes `ping_status` to `OK` and
|
|
1433
|
+
`ping_info` to `status_ok` for the previous example.
|
|
1434
|
+
|
|
1435
|
+
#### HTTP
|
|
1436
|
+
|
|
1437
|
+
In order confirm the command execution, the device must make a POST request to the IoT Agent with the result of the
|
|
1438
|
+
command as payload, no matter if it is a push or a poll command. Following with the IoTAgent JSON case, the request must
|
|
1439
|
+
be made to the `/iot/json/commands`, this way:
|
|
1440
|
+
|
|
1441
|
+
```
|
|
1442
|
+
POST /iot/json/commands?k=<apikey>&i=<deviceId>
|
|
1443
|
+
Content-Type: application/json
|
|
1444
|
+
Accept: application/json
|
|
1445
|
+
|
|
1446
|
+
{"ping":"status_ok"}
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
#### MQTT
|
|
1450
|
+
|
|
1451
|
+
The device should publish the result of the command (`{"ping":"status_ok"}` in the previous example) to a topic
|
|
1452
|
+
following the next pattern:
|
|
1453
|
+
|
|
1454
|
+
```
|
|
1455
|
+
/<iotagent-protocol>/<apiKey>/<deviceId>/cmdexe
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
The IoTA is subscribed to that topic, so it gets the result of the command. When this happens, the status is updated
|
|
1459
|
+
to`"<command>_status": "OK"`. Also the result of the command delivered by the device is stored in the `<command>_info`
|
|
1460
|
+
attribute.
|
|
1046
1461
|
|
|
1047
1462
|
## Overriding global Context Broker host
|
|
1048
1463
|
|
|
@@ -1312,7 +1727,7 @@ Config group is represented by a JSON object with the following fields:
|
|
|
1312
1727
|
|
|
1313
1728
|
The following actions are available under the config group endpoint:
|
|
1314
1729
|
|
|
1315
|
-
#### Retrieve config groups `GET /iot/
|
|
1730
|
+
#### Retrieve config groups `GET /iot/groups`
|
|
1316
1731
|
|
|
1317
1732
|
List all the config groups for the given `fiware-service` and `fiware-servicepath`. The config groups that match the
|
|
1318
1733
|
`fiware-servicepath` are returned in any other case.
|
|
@@ -1336,14 +1751,14 @@ Successful operations return `Content-Type` header with `application/json` value
|
|
|
1336
1751
|
|
|
1337
1752
|
_**Response payload**_
|
|
1338
1753
|
|
|
1339
|
-
A JSON object with a
|
|
1340
|
-
[config group datamodel](#
|
|
1754
|
+
A JSON object with a `groups` field that contains an array of groups that match the request. See the
|
|
1755
|
+
[config group datamodel](#config-group-datamodel) for more information.
|
|
1341
1756
|
|
|
1342
1757
|
Example:
|
|
1343
1758
|
|
|
1344
1759
|
```json
|
|
1345
1760
|
{
|
|
1346
|
-
"
|
|
1761
|
+
"groups": [
|
|
1347
1762
|
{
|
|
1348
1763
|
"resource": "/deviceTest",
|
|
1349
1764
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1380,7 +1795,7 @@ Example:
|
|
|
1380
1795
|
}
|
|
1381
1796
|
```
|
|
1382
1797
|
|
|
1383
|
-
#### Create config group `POST /iot/
|
|
1798
|
+
#### Create config group `POST /iot/groups`
|
|
1384
1799
|
|
|
1385
1800
|
Creates a set of config groups for the given service and service path. The service and subservice information will taken
|
|
1386
1801
|
from the headers, overwritting any preexisting values.
|
|
@@ -1394,14 +1809,14 @@ _**Request headers**_
|
|
|
1394
1809
|
|
|
1395
1810
|
_**Request payload**_
|
|
1396
1811
|
|
|
1397
|
-
A JSON object with a `
|
|
1398
|
-
[config group datamodel](#
|
|
1812
|
+
A JSON object with a `groups` field. The value is an array of config groups objects to create. See the
|
|
1813
|
+
[config group datamodel](#config-group-datamodel) for more information.
|
|
1399
1814
|
|
|
1400
1815
|
Example:
|
|
1401
1816
|
|
|
1402
1817
|
```json
|
|
1403
1818
|
{
|
|
1404
|
-
"
|
|
1819
|
+
"groups": [
|
|
1405
1820
|
{
|
|
1406
1821
|
"resource": "/deviceTest",
|
|
1407
1822
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1435,7 +1850,7 @@ _**Response headers**_
|
|
|
1435
1850
|
|
|
1436
1851
|
Successful operations return `Content-Type` header with `application/json` value.
|
|
1437
1852
|
|
|
1438
|
-
#### Modify config group `PUT /iot/
|
|
1853
|
+
#### Modify config group `PUT /iot/groups`
|
|
1439
1854
|
|
|
1440
1855
|
Modifies the information of a config group, identified by the `resource` and `apikey` query parameters. Takes a service
|
|
1441
1856
|
group body as the payload. The body does not have to be complete: for incomplete bodies, just the attributes included in
|
|
@@ -1457,8 +1872,8 @@ _**Request headers**_
|
|
|
1457
1872
|
|
|
1458
1873
|
_**Request payload**_
|
|
1459
1874
|
|
|
1460
|
-
A JSON object with the config group information to be modified. See the
|
|
1461
|
-
|
|
1875
|
+
A JSON object with the config group information to be modified. See the [config group datamodel](config-group-datamodel)
|
|
1876
|
+
for more information.
|
|
1462
1877
|
|
|
1463
1878
|
Example:
|
|
1464
1879
|
|
|
@@ -1475,7 +1890,7 @@ _**Response code**_
|
|
|
1475
1890
|
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
1476
1891
|
- 500 SERVER ERROR if there was any error not contemplated above.:
|
|
1477
1892
|
|
|
1478
|
-
#### Remove config group `DELETE /iot/
|
|
1893
|
+
#### Remove config group `DELETE /iot/groups`
|
|
1479
1894
|
|
|
1480
1895
|
Removes a config group, identified by the `resource` and `apikey` query parameters.
|
|
1481
1896
|
|
|
@@ -1895,6 +2310,57 @@ Example:
|
|
|
1895
2310
|
}
|
|
1896
2311
|
```
|
|
1897
2312
|
|
|
2313
|
+
### Metrics
|
|
2314
|
+
|
|
2315
|
+
The IoT Agent Library exposes a [openmetrics-compatible](https://github.com/OpenObservability/OpenMetrics) endpoint for
|
|
2316
|
+
telemetry collectors to gather application statistics.
|
|
2317
|
+
|
|
2318
|
+
#### Retrieve metrics `GET /metrics`
|
|
2319
|
+
|
|
2320
|
+
_**Response code**_
|
|
2321
|
+
|
|
2322
|
+
- `200` `OK` if successful.
|
|
2323
|
+
- `406` `Wrong Accept Header` If accept format is not supported.
|
|
2324
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
2325
|
+
|
|
2326
|
+
_**Response body**_
|
|
2327
|
+
|
|
2328
|
+
Returns the current value of the server stats,
|
|
2329
|
+
|
|
2330
|
+
- If `Accept` header contains `application/openmetrics-text; version=(1.0.0|0.0.1)`, the response has content-type
|
|
2331
|
+
`application/openmetrics-text; version=<the requested version>; charset=utf-8`
|
|
2332
|
+
- Else, If `Accept` header is missing or supports `text/plain` (explicitly or by `*/*`) , the response has
|
|
2333
|
+
content-type `text/plain; version=0.0.4; charset=utf-8` (legacy format for [prometheus](https://prometheus.io))
|
|
2334
|
+
- In any other case, returns an error message with `406` status.
|
|
2335
|
+
|
|
2336
|
+
For the kind of metrics exposed by the application, the actual payload itself is completely the same for both
|
|
2337
|
+
content-types, and follows the openmetrics specification, e.g:
|
|
2338
|
+
|
|
2339
|
+
```
|
|
2340
|
+
# HELP deviceCreationRequests global metric for deviceCreationRequests
|
|
2341
|
+
# TYPE deviceCreationRequests counter
|
|
2342
|
+
deviceCreationRequests 0
|
|
2343
|
+
# HELP deviceRemovalRequests global metric for deviceRemovalRequests
|
|
2344
|
+
# TYPE deviceRemovalRequests counter
|
|
2345
|
+
deviceRemovalRequests 0
|
|
2346
|
+
# HELP measureRequests global metric for measureRequests
|
|
2347
|
+
# TYPE measureRequests counter
|
|
2348
|
+
measureRequests 0
|
|
2349
|
+
# HELP raiseAlarm global metric for raiseAlarm
|
|
2350
|
+
# TYPE raiseAlarm counter
|
|
2351
|
+
raiseAlarm 0
|
|
2352
|
+
# HELP releaseAlarm global metric for releaseAlarm
|
|
2353
|
+
# TYPE releaseAlarm counter
|
|
2354
|
+
releaseAlarm 0
|
|
2355
|
+
# HELP updateEntityRequestsOk global metric for updateEntityRequestsOk
|
|
2356
|
+
# TYPE updateEntityRequestsOk counter
|
|
2357
|
+
updateEntityRequestsOk 2
|
|
2358
|
+
# HELP updateEntityRequestsError global metric for updateEntityRequestsError
|
|
2359
|
+
# TYPE updateEntityRequestsError counter
|
|
2360
|
+
updateEntityRequestsError 5
|
|
2361
|
+
# EOF
|
|
2362
|
+
```
|
|
2363
|
+
|
|
1898
2364
|
[1]:
|
|
1899
2365
|
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=%7Bcoordinates%3A%20%5Blongitude%2Clatitude%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
|
|
1900
2366
|
[2]:
|