iotagent-node-lib 4.5.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/doc/README.md +1 -1
- package/doc/admin.md +3 -15
- package/doc/api.md +469 -134
- 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/roadmap.md +5 -5
- package/docker/Mosquitto/Dockerfile +2 -2
- package/docker/Mosquitto/README.md +14 -11
- 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/groups/groupService.js +1 -1
- package/lib/services/ngsi/entities-NGSI-LD.js +320 -570
- package/lib/services/ngsi/entities-NGSI-v2.js +36 -1
- package/lib/services/ngsi/ngsiService.js +3 -1
- package/lib/services/northBound/deviceGroupAdministrationServer.js +42 -6
- package/lib/services/northBound/deviceProvisioningServer.js +0 -1
- package/lib/services/northBound/northboundServer.js +2 -0
- 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 +22 -17
- package/test/functional/functional-tests-runner.js +9 -4
- package/test/functional/functional-tests.js +4 -4
- package/test/functional/testCases.js +245 -4
- package/test/unit/examples/deviceProvisioningRequests/provisionFullDevice.json +1 -13
- 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/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/listProvisionedDevices-test.js +0 -4
- 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,7 +10,8 @@
|
|
|
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
|
-
- [
|
|
13
|
+
- [Device to NGSI Mapping](#device-to-ngsi-mapping)
|
|
14
|
+
- [Device autoprovision and entity creation](#device-autoprovision-and-entity-creation)
|
|
14
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)
|
|
@@ -26,12 +27,17 @@
|
|
|
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)
|
|
35
41
|
- [Multimeasure support](#multimeasure-support)
|
|
36
42
|
- [Overriding global Context Broker host](#overriding-global-context-broker-host)
|
|
37
43
|
- [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
|
|
@@ -44,10 +50,10 @@
|
|
|
44
50
|
- [Config group API](#config-group-api)
|
|
45
51
|
- [Config group datamodel](#config-group-datamodel)
|
|
46
52
|
- [Config group operations](#config-group-operations)
|
|
47
|
-
- [Retrieve config groups `GET /iot/
|
|
48
|
-
- [Create config group `POST /iot/
|
|
49
|
-
- [Modify config group `PUT /iot/
|
|
50
|
-
- [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)
|
|
51
57
|
- [Device API](#device-api)
|
|
52
58
|
- [Device datamodel](#device-datamodel)
|
|
53
59
|
- [Device operations](#device-operations)
|
|
@@ -64,6 +70,8 @@
|
|
|
64
70
|
- [Retrieve log level `GET /admin/log`](#retrieve-log-level-get-adminlog)
|
|
65
71
|
- [About operations](#about-operations)
|
|
66
72
|
- [List IoTA Information `GET /iot/about`](#list-iota-information-get-iotabout)
|
|
73
|
+
- [Metrics](#metrics)
|
|
74
|
+
- [Retrieve metrics `GET /metrics`](#retrieve-metrics-get-metrics)
|
|
67
75
|
|
|
68
76
|
<!-- /TOC -->
|
|
69
77
|
|
|
@@ -128,9 +136,9 @@ For every config group, the pair (resource, apikey) _must_ be unique (as it is u
|
|
|
128
136
|
which device). Those operations of the API targeting specific resources will need the use of the `resource` and `apikey`
|
|
129
137
|
parameters to select the appropriate instance.
|
|
130
138
|
|
|
131
|
-
Config groups can be created with preconfigured sets of attributes, service information, security
|
|
132
|
-
parameters. The specific parameters that can be configured for a given config group are described
|
|
133
|
-
[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.
|
|
134
142
|
|
|
135
143
|
### Devices
|
|
136
144
|
|
|
@@ -156,7 +164,7 @@ parameters defined at device level in database, the parameters are inherit from
|
|
|
156
164
|
|
|
157
165
|
### Uniqueness of groups and devices
|
|
158
166
|
|
|
159
|
-
Group
|
|
167
|
+
Group uniqueness is defined by the combination of: service, subservice, resource and apikey
|
|
160
168
|
|
|
161
169
|
Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
|
|
162
170
|
with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
|
|
@@ -171,32 +179,47 @@ applies to autoprovisioned attributes and is also available at JEXL context with
|
|
|
171
179
|
|
|
172
180
|
In case of provisioning attributes using `id` or `type` as names (please don't do that ;), they are ignored.
|
|
173
181
|
|
|
174
|
-
##
|
|
182
|
+
## Device to NGSI Mapping
|
|
175
183
|
|
|
176
|
-
|
|
177
|
-
|
|
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:
|
|
178
197
|
|
|
179
198
|
- **`attributes`**: Are measures that are pushed from the device to the IoT agent. This measure changes will be sent
|
|
180
199
|
to the Context Broker as updateContext requests over the device entity. NGSI queries to the context broker will be
|
|
181
200
|
resolved in the Broker database. For each attribute, its `name` and `type` must be provided. Additional `metadata`
|
|
182
|
-
is optional.
|
|
201
|
+
is optional. They are called internally as _active attributes_.
|
|
183
202
|
|
|
184
203
|
- **`lazy`**: Passive measures that are pulled from the device to the IoT agent. When a request for data from a lazy
|
|
185
|
-
attribute arrives to the Context Broker, it forwards the request to the
|
|
186
|
-
|
|
187
|
-
a NGSI format and return it to the Context Broker. This operation will be
|
|
188
|
-
the Context Broker won't return a response until the device has returned
|
|
189
|
-
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_.
|
|
190
210
|
|
|
191
211
|
- **`static`**: It is static attributes that are persisted in the Context Broker. They are not updated by the device,
|
|
192
212
|
but they can be modified by the user. They are useful to store information about the device that is not updated by
|
|
193
213
|
the device itself. For instance, a `location` static attribute is can be used to store the location of a fixed
|
|
194
214
|
device.
|
|
195
215
|
|
|
196
|
-
- **`commands`**: Commands are actions that can be invoked in the device. They are
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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.
|
|
200
223
|
|
|
201
224
|
All of them have the same syntax, a list of objects with the following attributes:
|
|
202
225
|
|
|
@@ -237,6 +260,17 @@ Note that, when information coming from devices, this means measures, are not de
|
|
|
237
260
|
device, the IoT agent will store that information into the destination entity using the same attribute name than the
|
|
238
261
|
measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored.
|
|
239
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
|
+
|
|
240
274
|
## Multientity support
|
|
241
275
|
|
|
242
276
|
The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities
|
|
@@ -320,45 +354,6 @@ e.g.:
|
|
|
320
354
|
}
|
|
321
355
|
```
|
|
322
356
|
|
|
323
|
-
Metadata could also has `expression` like attributes in order to expand it:
|
|
324
|
-
|
|
325
|
-
e.g.:
|
|
326
|
-
|
|
327
|
-
```json
|
|
328
|
-
{
|
|
329
|
-
"entity_type": "Lamp",
|
|
330
|
-
"resource": "/iot/d",
|
|
331
|
-
"protocol": "PDI-IoTA-UltraLight",
|
|
332
|
-
"commands": [
|
|
333
|
-
{ "name": "on", "type": "command" },
|
|
334
|
-
{ "name": "off", "type": "command" }
|
|
335
|
-
],
|
|
336
|
-
"attributes": [
|
|
337
|
-
{ "object_id": "s", "name": "state", "type": "Text" },
|
|
338
|
-
{
|
|
339
|
-
"object_id": "l",
|
|
340
|
-
"name": "luminosity",
|
|
341
|
-
"type": "Integer",
|
|
342
|
-
"metadata": {
|
|
343
|
-
"unitCode": { "type": "Text", "value": "CAL" }
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
],
|
|
347
|
-
"static_attributes": [
|
|
348
|
-
{ "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
|
|
349
|
-
{
|
|
350
|
-
"name": "controlledProperty",
|
|
351
|
-
"type": "Text",
|
|
352
|
-
"value": ["light"],
|
|
353
|
-
"metadata": {
|
|
354
|
-
"includes": { "type": "Text", "value": ["state", "luminosity"], "expression": "level / 100" },
|
|
355
|
-
"alias": { "type": "Text", "value": "lamp" }
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
]
|
|
359
|
-
}
|
|
360
|
-
```
|
|
361
|
-
|
|
362
357
|
### NGSI-LD data and metadata considerations
|
|
363
358
|
|
|
364
359
|
When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the
|
|
@@ -466,7 +461,8 @@ mappings of the provision. If `explicitAttrs` is provided both at device and con
|
|
|
466
461
|
precedence. Additionally `explicitAttrs` can be used to define which measures (identified by their attribute names, not
|
|
467
462
|
by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
|
|
468
463
|
|
|
469
|
-
Note that when `explicitAttrs` is an array or a JEXL expression resulting in to Array, if this array is empty then
|
|
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.
|
|
470
466
|
|
|
471
467
|
The different possibilities are summarized below:
|
|
472
468
|
|
|
@@ -556,6 +552,7 @@ really useful when you need to adapt measure (for example, to change the units,
|
|
|
556
552
|
of expression in the IoT Agent are:
|
|
557
553
|
|
|
558
554
|
- [Measurement transformation](#measurement-transformation).
|
|
555
|
+
- [Metadata](#expression-support-in-metadata)
|
|
559
556
|
- Commands payload transformation (push and pull).
|
|
560
557
|
- Auto provisioned devices entity name. It is configured at config Group level by setting the `entityNameExp`
|
|
561
558
|
parameter. It defines an expression to generate the Entity Name for autoprovisioned devices.
|
|
@@ -568,12 +565,18 @@ expression. In all cases the following data is available to all expressions:
|
|
|
568
565
|
- `id`: device ID
|
|
569
566
|
- `entity_name`: NGSI entity Name (principal)
|
|
570
567
|
- `type`: NGSI entity type (principal)
|
|
571
|
-
- `service`: device service
|
|
572
|
-
- `subservice`: device subservice
|
|
568
|
+
- `service`: device service (`Fiware-Service`)
|
|
569
|
+
- `subservice`: device subservice (`Fiware-ServicePath`)
|
|
573
570
|
- `staticAttributes`: static attributes defined in the device or config group
|
|
574
571
|
|
|
575
572
|
Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions
|
|
576
|
-
(`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)
|
|
577
580
|
|
|
578
581
|
### Examples of JEXL expressions
|
|
579
582
|
|
|
@@ -619,50 +622,52 @@ to incorporate new transformations from the IoT Agent configuration file in a fa
|
|
|
619
622
|
|
|
620
623
|
Current common transformation set:
|
|
621
624
|
|
|
622
|
-
| JEXL Transformation | Equivalent JavaScript Function
|
|
623
|
-
| ------------------------------------ |
|
|
624
|
-
| jsonparse: (str) | `JSON.parse(str);`
|
|
625
|
-
| jsonstringify: (obj) | `JSON.stringify(obj);`
|
|
626
|
-
| indexOf: (val, char) | `String(val).indexOf(char);`
|
|
627
|
-
| length: (val) | `String(val).length;`
|
|
628
|
-
| trim: (val) | `String(val).trim();`
|
|
629
|
-
| substr: (val, int1, int2) | `String(val).substr(int1, int2);`
|
|
630
|
-
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code>
|
|
631
|
-
| lengtharray: (arr) | `arr.length;`
|
|
632
|
-
| typeof: (val) | `typeof val;`
|
|
633
|
-
| isarray: (arr) | `Array.isArray(arr);`
|
|
634
|
-
| isnan: (val) | `isNaN(val);`
|
|
635
|
-
| parseint: (val) | `parseInt(val);`
|
|
636
|
-
| parsefloat: (val) | `parseFloat(val);`
|
|
637
|
-
| toisodate: (val) | `new Date(val).toISOString();`
|
|
638
|
-
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();`
|
|
639
|
-
| tostring: (val) | `val.toString();`
|
|
640
|
-
| urlencode: (val) | `encodeURI(val);`
|
|
641
|
-
| urldecode: (val) | `decodeURI(val);`
|
|
642
|
-
| replacestr: (str, from, to) | `str.replace(from, to);`
|
|
643
|
-
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);`
|
|
644
|
-
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);`
|
|
645
|
-
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);`
|
|
646
|
-
| split: (str, ch) | `str.split(ch);`
|
|
647
|
-
| joinarrtostr: (arr, ch) | `arr.join(ch);`
|
|
648
|
-
| concatarr: (arr, arr2) | `arr.concat(arr2);`
|
|
649
|
-
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code>
|
|
650
|
-
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code>
|
|
651
|
-
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code>
|
|
652
|
-
| slice: (arr, init, end) | `arr.slice(init,end);`
|
|
653
|
-
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code>
|
|
654
|
-
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code>
|
|
655
|
-
| touppercase: (val) | `String(val).toUpperCase()`
|
|
656
|
-
| tolowercase: (val) | `String(val).toLowerCase()`
|
|
657
|
-
| round: (val) | `Math.round(val)`
|
|
658
|
-
| floor: (val) | `Math.floor(val)`
|
|
659
|
-
| ceil: (val) | `Math.ceil(val)`
|
|
660
|
-
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)`
|
|
661
|
-
| gettime: (d) | `new Date(d).getTime()`
|
|
662
|
-
| toisostring: (d) | `new Date(d).toISOString()`
|
|
663
|
-
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)`
|
|
664
|
-
| now: () | `Date.now()`
|
|
665
|
-
| 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> |
|
|
666
671
|
|
|
667
672
|
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
668
673
|
test all the functions described above.
|
|
@@ -691,6 +696,50 @@ Another example using functions that return more than one value is the following
|
|
|
691
696
|
|
|
692
697
|
For a location value `"40.4165, -3.70256"`, the result of the previous expression will be `-3.70256`.
|
|
693
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
|
+
|
|
694
743
|
## Measurement transformation
|
|
695
744
|
|
|
696
745
|
The IoTAgent Library provides support for measurement transformation using a
|
|
@@ -1017,13 +1066,34 @@ Will now generate the following NGSI v2 payload:
|
|
|
1017
1066
|
}
|
|
1018
1067
|
```
|
|
1019
1068
|
|
|
1020
|
-
## Timestamp
|
|
1069
|
+
## TimeInstant and Timestamp flag
|
|
1021
1070
|
|
|
1022
|
-
|
|
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:
|
|
1023
1073
|
|
|
1024
|
-
- An attribute `TimeInstant` is added to updated entities
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
|
1077
|
+
|
|
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)
|
|
1027
1097
|
|
|
1028
1098
|
Depending on the `timestamp` configuration and if the measure contains a value named `TimeInstant` with a correct value,
|
|
1029
1099
|
the IoTA behaviour is described in the following table:
|
|
@@ -1037,13 +1107,6 @@ the IoTA behaviour is described in the following table:
|
|
|
1037
1107
|
| Not defined | Yes | TimeInstant and metadata updated with measure value |
|
|
1038
1108
|
| Not defined | No | TimeInstant and metadata updated with server timestamp |
|
|
1039
1109
|
|
|
1040
|
-
The `timestamp` conf value used is:
|
|
1041
|
-
|
|
1042
|
-
- The one defined at device level
|
|
1043
|
-
- The one defined at group level (if not defined at device level)
|
|
1044
|
-
- The one defined at [IoTA configuration level](admin.md#timestamp) / `IOTA_TIMESTAMP` env var (if not defined at
|
|
1045
|
-
group level or device level)
|
|
1046
|
-
|
|
1047
1110
|
Some additional considerations to take into account:
|
|
1048
1111
|
|
|
1049
1112
|
- If there is an attribute which maps a measure to `TimeInstant` attribute (after
|
|
@@ -1053,6 +1116,18 @@ Some additional considerations to take into account:
|
|
|
1053
1116
|
- If the resulting `TimeInstant` not follows [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) (either from a direct
|
|
1054
1117
|
measure of after a mapping, as described in the previous bullet) then it is refused (so a failover to server
|
|
1055
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.
|
|
1056
1131
|
|
|
1057
1132
|
## Multimeasure support
|
|
1058
1133
|
|
|
@@ -1175,6 +1250,215 @@ In this case a batch update (`POST /v2/op/update`) to CB will be generated with
|
|
|
1175
1250
|
}
|
|
1176
1251
|
```
|
|
1177
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.
|
|
1461
|
+
|
|
1178
1462
|
## Overriding global Context Broker host
|
|
1179
1463
|
|
|
1180
1464
|
**cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific types
|
|
@@ -1443,7 +1727,7 @@ Config group is represented by a JSON object with the following fields:
|
|
|
1443
1727
|
|
|
1444
1728
|
The following actions are available under the config group endpoint:
|
|
1445
1729
|
|
|
1446
|
-
#### Retrieve config groups `GET /iot/
|
|
1730
|
+
#### Retrieve config groups `GET /iot/groups`
|
|
1447
1731
|
|
|
1448
1732
|
List all the config groups for the given `fiware-service` and `fiware-servicepath`. The config groups that match the
|
|
1449
1733
|
`fiware-servicepath` are returned in any other case.
|
|
@@ -1467,14 +1751,14 @@ Successful operations return `Content-Type` header with `application/json` value
|
|
|
1467
1751
|
|
|
1468
1752
|
_**Response payload**_
|
|
1469
1753
|
|
|
1470
|
-
A JSON object with a
|
|
1471
|
-
[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.
|
|
1472
1756
|
|
|
1473
1757
|
Example:
|
|
1474
1758
|
|
|
1475
1759
|
```json
|
|
1476
1760
|
{
|
|
1477
|
-
"
|
|
1761
|
+
"groups": [
|
|
1478
1762
|
{
|
|
1479
1763
|
"resource": "/deviceTest",
|
|
1480
1764
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1511,7 +1795,7 @@ Example:
|
|
|
1511
1795
|
}
|
|
1512
1796
|
```
|
|
1513
1797
|
|
|
1514
|
-
#### Create config group `POST /iot/
|
|
1798
|
+
#### Create config group `POST /iot/groups`
|
|
1515
1799
|
|
|
1516
1800
|
Creates a set of config groups for the given service and service path. The service and subservice information will taken
|
|
1517
1801
|
from the headers, overwritting any preexisting values.
|
|
@@ -1525,14 +1809,14 @@ _**Request headers**_
|
|
|
1525
1809
|
|
|
1526
1810
|
_**Request payload**_
|
|
1527
1811
|
|
|
1528
|
-
A JSON object with a `
|
|
1529
|
-
[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.
|
|
1530
1814
|
|
|
1531
1815
|
Example:
|
|
1532
1816
|
|
|
1533
1817
|
```json
|
|
1534
1818
|
{
|
|
1535
|
-
"
|
|
1819
|
+
"groups": [
|
|
1536
1820
|
{
|
|
1537
1821
|
"resource": "/deviceTest",
|
|
1538
1822
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1566,7 +1850,7 @@ _**Response headers**_
|
|
|
1566
1850
|
|
|
1567
1851
|
Successful operations return `Content-Type` header with `application/json` value.
|
|
1568
1852
|
|
|
1569
|
-
#### Modify config group `PUT /iot/
|
|
1853
|
+
#### Modify config group `PUT /iot/groups`
|
|
1570
1854
|
|
|
1571
1855
|
Modifies the information of a config group, identified by the `resource` and `apikey` query parameters. Takes a service
|
|
1572
1856
|
group body as the payload. The body does not have to be complete: for incomplete bodies, just the attributes included in
|
|
@@ -1588,8 +1872,8 @@ _**Request headers**_
|
|
|
1588
1872
|
|
|
1589
1873
|
_**Request payload**_
|
|
1590
1874
|
|
|
1591
|
-
A JSON object with the config group information to be modified. See the
|
|
1592
|
-
|
|
1875
|
+
A JSON object with the config group information to be modified. See the [config group datamodel](config-group-datamodel)
|
|
1876
|
+
for more information.
|
|
1593
1877
|
|
|
1594
1878
|
Example:
|
|
1595
1879
|
|
|
@@ -1606,7 +1890,7 @@ _**Response code**_
|
|
|
1606
1890
|
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
1607
1891
|
- 500 SERVER ERROR if there was any error not contemplated above.:
|
|
1608
1892
|
|
|
1609
|
-
#### Remove config group `DELETE /iot/
|
|
1893
|
+
#### Remove config group `DELETE /iot/groups`
|
|
1610
1894
|
|
|
1611
1895
|
Removes a config group, identified by the `resource` and `apikey` query parameters.
|
|
1612
1896
|
|
|
@@ -2026,6 +2310,57 @@ Example:
|
|
|
2026
2310
|
}
|
|
2027
2311
|
```
|
|
2028
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
|
+
|
|
2029
2364
|
[1]:
|
|
2030
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
|
|
2031
2366
|
[2]:
|