iotagent-node-lib 4.5.0 → 4.7.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/.github/workflows/ci.yml +0 -1
- package/Changelog +12 -0
- package/README.md +67 -272
- package/config.js +3 -1
- package/doc/README.md +1 -1
- package/doc/admin.md +40 -18
- package/doc/api.md +532 -136
- 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 +21 -2
- package/lib/constants.js +3 -0
- package/lib/fiware-iotagent-lib.js +12 -15
- package/lib/jexlTranformsMap.js +3 -1
- package/lib/model/Command.js +2 -2
- package/lib/model/Device.js +7 -3
- package/lib/model/Group.js +5 -3
- package/lib/model/dbConn.js +53 -115
- package/lib/services/commands/commandRegistryMongoDB.js +115 -75
- package/lib/services/common/alarmManagement.js +3 -0
- package/lib/services/common/iotManagerService.js +3 -1
- package/lib/services/devices/deviceRegistryMemory.js +36 -0
- package/lib/services/devices/deviceRegistryMongoDB.js +160 -87
- package/lib/services/devices/deviceService.js +33 -3
- package/lib/services/devices/devices-NGSI-v2.js +6 -1
- package/lib/services/groups/groupRegistryMongoDB.js +120 -83
- 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 +51 -3
- package/lib/services/ngsi/ngsiService.js +34 -1
- package/lib/services/northBound/deviceGroupAdministrationServer.js +42 -6
- package/lib/services/northBound/deviceProvisioningServer.js +12 -4
- 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 +12 -24
- package/lib/templates/updateDeviceLax.json +12 -23
- package/package.json +5 -5
- package/scripts/legacy_expression_tool/README.md +0 -1
- package/test/functional/README.md +22 -17
- package/test/functional/config-test.js +3 -2
- 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/functional/testUtils.js +2 -2
- 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/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
- package/test/unit/general/config-multi-core-test.js +1 -2
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
- package/test/unit/general/deviceService-test.js +106 -3
- package/test/unit/general/statistics-service_test.js +1 -74
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
- package/test/unit/mongodb/mongodb-configGroup-registry-test.js +452 -0
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +9 -42
- 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 +1 -104
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
- 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/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
- package/test/unit/ngsiv2/general/deviceService-test.js +98 -4
- package/test/unit/ngsiv2/general/https-support-test.js +1 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +195 -0
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +265 -4
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
- package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +1190 -0
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -9
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
- 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,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
|
-
- [
|
|
13
|
+
- [Device to NGSI Mapping](#device-to-ngsi-mapping)
|
|
14
|
+
- [Device autoprovision and entity creation](#device-autoprovision-and-entity-creation)
|
|
15
|
+
- [Entity Name expression support](#entity-name-expression-support)
|
|
14
16
|
- [Multientity support](#multientity-support)
|
|
15
17
|
- [Metadata support](#metadata-support)
|
|
16
18
|
- [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
|
|
@@ -26,12 +28,17 @@
|
|
|
26
28
|
- [Examples of JEXL expressions](#examples-of-jexl-expressions)
|
|
27
29
|
- [Available functions](#available-functions)
|
|
28
30
|
- [Expressions with multiple transformations](#expressions-with-multiple-transformations)
|
|
31
|
+
- [Expression support in metadata](#expression-support-in-metadata)
|
|
29
32
|
- [Measurement transformation](#measurement-transformation)
|
|
30
33
|
- [Measurement transformation definition](#measurement-transformation-definition)
|
|
31
34
|
- [Measurement transformation execution](#measurement-transformation-execution)
|
|
32
35
|
- [Measurement transformation order](#measurement-transformation-order)
|
|
33
36
|
- [Multientity measurement transformation support (`object_id`)](#multientity-measurement-transformation-support-object_id)
|
|
34
|
-
- [
|
|
37
|
+
- [Command execution](#command-execution)
|
|
38
|
+
- [Triggering commands](#triggering-commands)
|
|
39
|
+
- [Command reception](#command-reception)
|
|
40
|
+
- [Command confirmation](#command-confirmation)
|
|
41
|
+
- [TimeInstant and Timestamp flag](#timeinstant-and-timestamp-flag)
|
|
35
42
|
- [Multimeasure support](#multimeasure-support)
|
|
36
43
|
- [Overriding global Context Broker host](#overriding-global-context-broker-host)
|
|
37
44
|
- [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
|
|
@@ -44,10 +51,10 @@
|
|
|
44
51
|
- [Config group API](#config-group-api)
|
|
45
52
|
- [Config group datamodel](#config-group-datamodel)
|
|
46
53
|
- [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/
|
|
54
|
+
- [Retrieve config groups `GET /iot/groups`](#retrieve-config-groups-get-iotgroups)
|
|
55
|
+
- [Create config group `POST /iot/groups`](#create-config-group-post-iotgroups)
|
|
56
|
+
- [Modify config group `PUT /iot/groups`](#modify-config-group-put-iotgroups)
|
|
57
|
+
- [Remove config group `DELETE /iot/groups`](#remove-config-group-delete-iotgroups)
|
|
51
58
|
- [Device API](#device-api)
|
|
52
59
|
- [Device datamodel](#device-datamodel)
|
|
53
60
|
- [Device operations](#device-operations)
|
|
@@ -64,6 +71,8 @@
|
|
|
64
71
|
- [Retrieve log level `GET /admin/log`](#retrieve-log-level-get-adminlog)
|
|
65
72
|
- [About operations](#about-operations)
|
|
66
73
|
- [List IoTA Information `GET /iot/about`](#list-iota-information-get-iotabout)
|
|
74
|
+
- [Metrics](#metrics)
|
|
75
|
+
- [Retrieve metrics `GET /metrics`](#retrieve-metrics-get-metrics)
|
|
67
76
|
|
|
68
77
|
<!-- /TOC -->
|
|
69
78
|
|
|
@@ -128,9 +137,9 @@ For every config group, the pair (resource, apikey) _must_ be unique (as it is u
|
|
|
128
137
|
which device). Those operations of the API targeting specific resources will need the use of the `resource` and `apikey`
|
|
129
138
|
parameters to select the appropriate instance.
|
|
130
139
|
|
|
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.
|
|
140
|
+
Config groups can be created with preconfigured sets of attributes, service and subservice information, security
|
|
141
|
+
information and other parameters. The specific parameters that can be configured for a given config group are described
|
|
142
|
+
in the [Config group datamodel](#config-group-datamodel) section.
|
|
134
143
|
|
|
135
144
|
### Devices
|
|
136
145
|
|
|
@@ -156,7 +165,7 @@ parameters defined at device level in database, the parameters are inherit from
|
|
|
156
165
|
|
|
157
166
|
### Uniqueness of groups and devices
|
|
158
167
|
|
|
159
|
-
Group
|
|
168
|
+
Group uniqueness is defined by the combination of: resource and apikey. This is so because given a measure (identified by an apikey) there is no way to identify group related if apikey is not unique for all services and subservices.
|
|
160
169
|
|
|
161
170
|
Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
|
|
162
171
|
with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
|
|
@@ -171,32 +180,47 @@ applies to autoprovisioned attributes and is also available at JEXL context with
|
|
|
171
180
|
|
|
172
181
|
In case of provisioning attributes using `id` or `type` as names (please don't do that ;), they are ignored.
|
|
173
182
|
|
|
174
|
-
##
|
|
183
|
+
## Device to NGSI Mapping
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
The way to map the information coming or going to the device to the NGSI attributes is defined in the group or device.
|
|
186
|
+
It is possible to define the entity type and the entity ID that a device will use in the Context Broker. It can be
|
|
187
|
+
configured for a single device in the device provisioning, or it can be defined for all the devices in a group.
|
|
188
|
+
|
|
189
|
+
The entity type should be defined both in the group and in the device, but the entity name (entity ID) is not defined in
|
|
190
|
+
the group. In that case, if there is no a existing device the same device ID, the entity name of the device generated
|
|
191
|
+
will be a concatenation of the entity type and the device ID (I.E: `entityType:device_id`). If you need to generate the
|
|
192
|
+
entity name differently, it is possible to define an expression to generate it, using the parameter `entityNameExp` in
|
|
193
|
+
the group as described in the [Entity Name expression support](#entity-name-expression-support) section.
|
|
194
|
+
|
|
195
|
+
It is also possible to configure how each of the measures obtained from the device is mapped to different attributes.
|
|
196
|
+
The name and type of the attribute is configured by the user (globally for all the devices in the group or in a per
|
|
197
|
+
device preprovisioning). Device measures can have four different behaviors:
|
|
178
198
|
|
|
179
199
|
- **`attributes`**: Are measures that are pushed from the device to the IoT agent. This measure changes will be sent
|
|
180
200
|
to the Context Broker as updateContext requests over the device entity. NGSI queries to the context broker will be
|
|
181
201
|
resolved in the Broker database. For each attribute, its `name` and `type` must be provided. Additional `metadata`
|
|
182
|
-
is optional.
|
|
202
|
+
is optional. They are called internally as _active attributes_.
|
|
183
203
|
|
|
184
204
|
- **`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.
|
|
205
|
+
attribute arrives to the Context Broker, it forwards the request to the IoT Agent (that behaves as NGSI Context
|
|
206
|
+
Provider for all the lazy attributes or commands). The IoT Agent will then ask the device for the information
|
|
207
|
+
needed, transform that information to a NGSI format and return it to the Context Broker. This operation will be
|
|
208
|
+
synchronous from the customer perspective: the Context Broker won't return a response until the device has returned
|
|
209
|
+
its response to the IoT Agent. For each attribute, its `name` and `type` must be provided. They are called
|
|
210
|
+
internally as _lazy attributes_.
|
|
190
211
|
|
|
191
212
|
- **`static`**: It is static attributes that are persisted in the Context Broker. They are not updated by the device,
|
|
192
213
|
but they can be modified by the user. They are useful to store information about the device that is not updated by
|
|
193
214
|
the device itself. For instance, a `location` static attribute is can be used to store the location of a fixed
|
|
194
215
|
device.
|
|
195
216
|
|
|
196
|
-
- **`commands`**: Commands are actions that can be invoked in the device. They are
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
217
|
+
- **`commands`**: Commands are actions that can be invoked in the device. They are entity attributes, but they are not
|
|
218
|
+
updated by the device, they are updated by the Context Broker. In this case, the interaction will begin by setting
|
|
219
|
+
an attribute in the device's entity, for which the IoT Agent will be regitered as Context Provider. The IoT Agent
|
|
220
|
+
will return an immediate response to the Context Broker, and will be held responsible of contacting the device to
|
|
221
|
+
perform the command itself using the device specific protocol. Special `status` and `info` attributes should be
|
|
222
|
+
update. For each command, its `name` and `type` must be provided. For further information, please refer to
|
|
223
|
+
[Command execution](#command-execution) section.
|
|
200
224
|
|
|
201
225
|
All of them have the same syntax, a list of objects with the following attributes:
|
|
202
226
|
|
|
@@ -237,6 +261,70 @@ Note that, when information coming from devices, this means measures, are not de
|
|
|
237
261
|
device, the IoT agent will store that information into the destination entity using the same attribute name than the
|
|
238
262
|
measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored.
|
|
239
263
|
|
|
264
|
+
## Device autoprovision and entity creation
|
|
265
|
+
|
|
266
|
+
For those agents that uses IoTA Node LIB version 3.4.0 or higher, you should consider that the entity is not created
|
|
267
|
+
automatically when a device is created. This means that all entities into the Context Broker are created when data
|
|
268
|
+
arrives from a device, no matter if the device is explicitly provisioned (via
|
|
269
|
+
[device provisioning API](#create-device-post-iotdevices)) or autoprovisioned.
|
|
270
|
+
|
|
271
|
+
If for any reason you need the entity at CB before the first measure of the corresponding device arrives to the
|
|
272
|
+
IOTAgent, you can create it in advance using the Context Broker
|
|
273
|
+
[NGSI v2 API](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md).
|
|
274
|
+
|
|
275
|
+
## Entity Name expression support
|
|
276
|
+
|
|
277
|
+
By default, the entity name used to persist the device measures in the Context Broker can be defined in the device
|
|
278
|
+
provisioning or, if the device is autoprovisioned, it is generated by the IoT Agent as a concatenation of the entity
|
|
279
|
+
type and the device ID. If you need to generate the entity name differently, it is possible to define an expression to
|
|
280
|
+
generate it, using the [Expression Language](#expression-language-support) through the `entityNameExp` field in the
|
|
281
|
+
group.
|
|
282
|
+
|
|
283
|
+
With this feature, the entity name can be generated dynamically based not only on the device ID and entity type, but
|
|
284
|
+
also on the measures reported by the device or any other context information. The `entityNameExp` field is only
|
|
285
|
+
available at the group level. **Important**: when using `entityNameExp`, the `entity_name` field in the device
|
|
286
|
+
provisioning is ignored. This means that the entity name used to store the device information in the Context Broker is
|
|
287
|
+
always generated by the `entityNameExp` expression. If you need to explicitly define the entity name for a particular
|
|
288
|
+
device, you can include a particular condition in the `entityNameExp` expression to handle that case (e.g.
|
|
289
|
+
`id == 'myDevice' ? 'myEntity' : entityType + ':' + id`).
|
|
290
|
+
|
|
291
|
+
The following example shows how to define an entity name expression:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"services": [
|
|
296
|
+
{
|
|
297
|
+
"resource": "/json",
|
|
298
|
+
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
299
|
+
"entity_type": "TemperatureSensor",
|
|
300
|
+
"entityNameExp": "id + '__' + sn",
|
|
301
|
+
"attributes": [
|
|
302
|
+
{
|
|
303
|
+
"object_id": "t",
|
|
304
|
+
"name": "temperature",
|
|
305
|
+
"type": "Number"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"object_id": "sn",
|
|
309
|
+
"name": "serialNumber",
|
|
310
|
+
"type": "Text"
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
As defined above, the `entityNameExp` is `id + '__' + sn` and it will generate the entity name by concatenating the
|
|
319
|
+
device ID and the serial number reported by the device. For example, for a given measure with `id` equal to `dev123` and
|
|
320
|
+
`sn` equal to `ABCDEF`, the resulting entity name will be `dev123__ABCDEF`.
|
|
321
|
+
|
|
322
|
+
Note that, when using `entityNameExp`, the `entity_name` of the device provisioning is set to the result of the
|
|
323
|
+
expression the first time the device is created. If the expression is modified later, the `entity_name` of the device
|
|
324
|
+
provisioning will not be updated, but the value used to persist the device measures in the Context Broker will be the
|
|
325
|
+
result of the new expression. This can lead to a situation where the `entity_name` of the device provisioning and the
|
|
326
|
+
entity name used in the Context Broker are different.
|
|
327
|
+
|
|
240
328
|
## Multientity support
|
|
241
329
|
|
|
242
330
|
The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities
|
|
@@ -320,45 +408,6 @@ e.g.:
|
|
|
320
408
|
}
|
|
321
409
|
```
|
|
322
410
|
|
|
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
411
|
### NGSI-LD data and metadata considerations
|
|
363
412
|
|
|
364
413
|
When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the
|
|
@@ -466,7 +515,8 @@ mappings of the provision. If `explicitAttrs` is provided both at device and con
|
|
|
466
515
|
precedence. Additionally `explicitAttrs` can be used to define which measures (identified by their attribute names, not
|
|
467
516
|
by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
|
|
468
517
|
|
|
469
|
-
Note that when `explicitAttrs` is an array or a JEXL expression resulting in to Array, if this array is empty then
|
|
518
|
+
Note that when `explicitAttrs` is an array or a JEXL expression resulting in to Array, if this array is empty then
|
|
519
|
+
`TimeInstant` is not propaged to CB.
|
|
470
520
|
|
|
471
521
|
The different possibilities are summarized below:
|
|
472
522
|
|
|
@@ -556,9 +606,11 @@ really useful when you need to adapt measure (for example, to change the units,
|
|
|
556
606
|
of expression in the IoT Agent are:
|
|
557
607
|
|
|
558
608
|
- [Measurement transformation](#measurement-transformation).
|
|
609
|
+
- [Metadata](#expression-support-in-metadata)
|
|
559
610
|
- Commands payload transformation (push and pull).
|
|
560
611
|
- Auto provisioned devices entity name. It is configured at config Group level by setting the `entityNameExp`
|
|
561
|
-
parameter. It defines an expression to generate the Entity Name for autoprovisioned devices.
|
|
612
|
+
parameter. It defines an expression to generate the Entity Name for autoprovisioned devices. More information in the
|
|
613
|
+
[Entity Name expression support](#entity-name-expression-support) section.
|
|
562
614
|
- Dynamic `endpoint` definition. Configured at device level, it defines where the device listen for push http
|
|
563
615
|
commands. It can be either a static value or an expression.
|
|
564
616
|
|
|
@@ -568,12 +620,19 @@ expression. In all cases the following data is available to all expressions:
|
|
|
568
620
|
- `id`: device ID
|
|
569
621
|
- `entity_name`: NGSI entity Name (principal)
|
|
570
622
|
- `type`: NGSI entity type (principal)
|
|
571
|
-
- `service`: device service
|
|
572
|
-
- `subservice`: device subservice
|
|
623
|
+
- `service`: device service (`Fiware-Service`)
|
|
624
|
+
- `subservice`: device subservice (`Fiware-ServicePath`)
|
|
573
625
|
- `staticAttributes`: static attributes defined in the device or config group
|
|
626
|
+
- `oldCtxt`: previous JEXL context (related to last processed measure)
|
|
574
627
|
|
|
575
628
|
Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions
|
|
576
|
-
(`expression`)
|
|
629
|
+
(`expression`) the following is available in the **context** used to evalute:
|
|
630
|
+
|
|
631
|
+
- measures, as `<AttributeName>`
|
|
632
|
+
- metadata (both for attribute measurement in the case of NGSI-v2 measurements and static attribute) are available in
|
|
633
|
+
the **context** under the following convention: `metadata.<AttributeName>.<MetadataName>` or
|
|
634
|
+
`metadata.<StaticAttributeName>.<MetadataName>` in a similar way of defined for
|
|
635
|
+
[Context Broker](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md#metadata-support)
|
|
577
636
|
|
|
578
637
|
### Examples of JEXL expressions
|
|
579
638
|
|
|
@@ -619,50 +678,52 @@ to incorporate new transformations from the IoT Agent configuration file in a fa
|
|
|
619
678
|
|
|
620
679
|
Current common transformation set:
|
|
621
680
|
|
|
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))))`
|
|
681
|
+
| JEXL Transformation | Equivalent JavaScript Function |
|
|
682
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
683
|
+
| jsonparse: (str) | `JSON.parse(str);` |
|
|
684
|
+
| jsonstringify: (obj) | `JSON.stringify(obj);` |
|
|
685
|
+
| indexOf: (val, char) | `String(val).indexOf(char);` |
|
|
686
|
+
| length: (val) | `String(val).length;` |
|
|
687
|
+
| trim: (val) | `String(val).trim();` |
|
|
688
|
+
| substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
|
|
689
|
+
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code> |
|
|
690
|
+
| lengtharray: (arr) | `arr.length;` |
|
|
691
|
+
| typeof: (val) | `typeof val;` |
|
|
692
|
+
| isarray: (arr) | `Array.isArray(arr);` |
|
|
693
|
+
| isnan: (val) | `isNaN(val);` |
|
|
694
|
+
| parseint: (val) | `parseInt(val);` |
|
|
695
|
+
| parsefloat: (val) | `parseFloat(val);` |
|
|
696
|
+
| toisodate: (val) | `new Date(val).toISOString();` |
|
|
697
|
+
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
|
|
698
|
+
| tostring: (val) | `val.toString();` |
|
|
699
|
+
| urlencode: (val) | `encodeURI(val);` |
|
|
700
|
+
| urldecode: (val) | `decodeURI(val);` |
|
|
701
|
+
| replacestr: (str, from, to) | `str.replace(from, to);` |
|
|
702
|
+
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
|
|
703
|
+
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
|
|
704
|
+
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
|
|
705
|
+
| split: (str, ch) | `str.split(ch);` |
|
|
706
|
+
| joinarrtostr: (arr, ch) | `arr.join(ch);` |
|
|
707
|
+
| concatarr: (arr, arr2) | `arr.concat(arr2);` |
|
|
708
|
+
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code> |
|
|
709
|
+
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
|
|
710
|
+
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
|
|
711
|
+
| slice: (arr, init, end) | `arr.slice(init,end);` |
|
|
712
|
+
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
|
|
713
|
+
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
|
|
714
|
+
| touppercase: (val) | `String(val).toUpperCase()` |
|
|
715
|
+
| tolowercase: (val) | `String(val).toLowerCase()` |
|
|
716
|
+
| round: (val) | `Math.round(val)` |
|
|
717
|
+
| floor: (val) | `Math.floor(val)` |
|
|
718
|
+
| ceil: (val) | `Math.ceil(val)` |
|
|
719
|
+
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)` |
|
|
720
|
+
| gettime: (d) | `new Date(d).getTime()` |
|
|
721
|
+
| toisostring: (d) | `new Date(d).toISOString()` |
|
|
722
|
+
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)` |
|
|
723
|
+
| now: () | `Date.now()` |
|
|
724
|
+
| hextostring: (val) | `new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))` |
|
|
725
|
+
| valuePicker: (val,pick) | <code>valuePicker: (val,pick) => Object.entries(val).filter(([_, v]) => v === pick).map(([k, _]) => k)</code> |
|
|
726
|
+
| valuePickerMulti: (val,pick) | <code>valuePickerMulti: (val,pick) => Object.entries(val).filter(([_, v]) => pick.includes(v)).map(([k, _]) => k)</code> |
|
|
666
727
|
|
|
667
728
|
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
668
729
|
test all the functions described above.
|
|
@@ -691,6 +752,50 @@ Another example using functions that return more than one value is the following
|
|
|
691
752
|
|
|
692
753
|
For a location value `"40.4165, -3.70256"`, the result of the previous expression will be `-3.70256`.
|
|
693
754
|
|
|
755
|
+
### Expression support in metadata
|
|
756
|
+
|
|
757
|
+
Metadata could also has `expression` like attributes in order to expand it:
|
|
758
|
+
|
|
759
|
+
e.g.:
|
|
760
|
+
|
|
761
|
+
```json
|
|
762
|
+
{
|
|
763
|
+
"entity_type": "Lamp",
|
|
764
|
+
"resource": "/iot/d",
|
|
765
|
+
"protocol": "PDI-IoTA-UltraLight",
|
|
766
|
+
"commands": [
|
|
767
|
+
{ "name": "on", "type": "command" },
|
|
768
|
+
{ "name": "off", "type": "command" }
|
|
769
|
+
],
|
|
770
|
+
"attributes": [
|
|
771
|
+
{ "object_id": "s", "name": "state", "type": "Text" },
|
|
772
|
+
{
|
|
773
|
+
"object_id": "l",
|
|
774
|
+
"name": "luminosity",
|
|
775
|
+
"type": "Integer",
|
|
776
|
+
"metadata": {
|
|
777
|
+
"unitCode": { "type": "Text", "value": "CAL" }
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
],
|
|
781
|
+
"static_attributes": [
|
|
782
|
+
{ "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
|
|
783
|
+
{
|
|
784
|
+
"name": "controlledProperty",
|
|
785
|
+
"type": "Text",
|
|
786
|
+
"value": ["light"],
|
|
787
|
+
"metadata": {
|
|
788
|
+
"includes": { "type": "Text", "value": ["state", "luminosity"], "expression": "level / 100" },
|
|
789
|
+
"alias": { "type": "Text", "value": "lamp" }
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
]
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Note that there is no order into metadata structure and there is no warranty about which metadata attribute expression
|
|
797
|
+
will be evaluated first.
|
|
798
|
+
|
|
694
799
|
## Measurement transformation
|
|
695
800
|
|
|
696
801
|
The IoTAgent Library provides support for measurement transformation using a
|
|
@@ -1017,13 +1122,34 @@ Will now generate the following NGSI v2 payload:
|
|
|
1017
1122
|
}
|
|
1018
1123
|
```
|
|
1019
1124
|
|
|
1020
|
-
## Timestamp
|
|
1125
|
+
## TimeInstant and Timestamp flag
|
|
1126
|
+
|
|
1127
|
+
As part of the device to entity mapping process, the IoT Agent creates and updates automatically a special timestamp
|
|
1128
|
+
attribute called `TimeInstant`. This timestamp is represented as two different properties of the mapped entity:
|
|
1129
|
+
|
|
1130
|
+
- An attribute `TimeInstant` is added to updated entities in the case of NGSI-v2, which captures as an ISO8601
|
|
1131
|
+
timestamp when the associated measurement was observed. With NGSI-LD, the Standard `observedAt` property is used
|
|
1132
|
+
instead
|
|
1021
1133
|
|
|
1022
|
-
|
|
1134
|
+
- With NGSI-v2, an attribute metadata named `TimeInstant` per active or lazy attribute mapped, which captures as an
|
|
1135
|
+
ISO8601 timestamp when the associated measurement (represented as attribute value) was observed. With NGSI-LD, the
|
|
1136
|
+
Standard `observedAt` property-of-a-property is used instead.
|
|
1023
1137
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1138
|
+
If timestamp is not explicily defined when sending the measures through the IoT Agent (for further details on how to
|
|
1139
|
+
attach timestamp information to the measures, please refer to the particular IoT Agent implementation documentation),
|
|
1140
|
+
the arrival time on the server when receiving the measurement will be used to generate a `TimeInstant` for both the
|
|
1141
|
+
entity attribute and the attribute metadata.
|
|
1142
|
+
|
|
1143
|
+
This functionality can be turned on and off globaly through the use of the `timestamp` configuration flag or
|
|
1144
|
+
`IOTA_TIMESTAMP` variable as well as `timestamp` flag in device or group provision (in this case, the device or group
|
|
1145
|
+
level flag takes precedence over the global one). The default value is `true`.
|
|
1146
|
+
|
|
1147
|
+
The `timestamp` configuration value used, according to the priority:
|
|
1148
|
+
|
|
1149
|
+
1. The one defined at device level
|
|
1150
|
+
2. The one defined at group level (if not defined at device level)
|
|
1151
|
+
3. The one defined at [IoTA configuration level](admin.md#timestamp) / `IOTA_TIMESTAMP` env var (if not defined at
|
|
1152
|
+
group level or device level)
|
|
1027
1153
|
|
|
1028
1154
|
Depending on the `timestamp` configuration and if the measure contains a value named `TimeInstant` with a correct value,
|
|
1029
1155
|
the IoTA behaviour is described in the following table:
|
|
@@ -1037,13 +1163,6 @@ the IoTA behaviour is described in the following table:
|
|
|
1037
1163
|
| Not defined | Yes | TimeInstant and metadata updated with measure value |
|
|
1038
1164
|
| Not defined | No | TimeInstant and metadata updated with server timestamp |
|
|
1039
1165
|
|
|
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
1166
|
Some additional considerations to take into account:
|
|
1048
1167
|
|
|
1049
1168
|
- If there is an attribute which maps a measure to `TimeInstant` attribute (after
|
|
@@ -1053,6 +1172,18 @@ Some additional considerations to take into account:
|
|
|
1053
1172
|
- If the resulting `TimeInstant` not follows [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) (either from a direct
|
|
1054
1173
|
measure of after a mapping, as described in the previous bullet) then it is refused (so a failover to server
|
|
1055
1174
|
timestamp will take place).
|
|
1175
|
+
- the timestamp of different attributes belonging to the same measurement record may not be equal.
|
|
1176
|
+
- the arrival time and the measurement timestamp will not be the same in the general case (when explicitly defining
|
|
1177
|
+
the timestamp in the measurement)
|
|
1178
|
+
- if `timezone` field is defined as part of the provisioning of the device or group, timestamp fields will be
|
|
1179
|
+
generated using it. For instance, if `timezone` is set to `America/Los_Angeles`, a possible timestamp could be
|
|
1180
|
+
`2025-08-05T00:35:01.468-07:00`. If `timezone` field is not defined, by default Zulu Time Zone (UTC +0) will be
|
|
1181
|
+
used. Following the previous example, timestamp could be `2015-08-05T07:35:01.468Z`.
|
|
1182
|
+
|
|
1183
|
+
E.g.: in the case of a device that can take measurements every hour of both temperature and humidity and sends the data
|
|
1184
|
+
once every day, at midnight, the `TimeInstant` reported for each measurement will be the hour when that measurement was
|
|
1185
|
+
observed (e.g. 4:00 PM), while all the measurements will have an arrival time around midnight. If no timestamps were
|
|
1186
|
+
reported with such measurements, the `TimeInstant` attribute would take those values around midnight.
|
|
1056
1187
|
|
|
1057
1188
|
## Multimeasure support
|
|
1058
1189
|
|
|
@@ -1175,6 +1306,215 @@ In this case a batch update (`POST /v2/op/update`) to CB will be generated with
|
|
|
1175
1306
|
}
|
|
1176
1307
|
```
|
|
1177
1308
|
|
|
1309
|
+
## Command execution
|
|
1310
|
+
|
|
1311
|
+
This section reviews the end-to-end process to trigger and receive commands into devices. The URL paths and messages
|
|
1312
|
+
format is based on the [IoT Agent JSON](https://github.com/telefonicaid/iotagent-json). It may differ in the case of
|
|
1313
|
+
using any other IoT Agent. In that case, please refer to the specific IoTA documentation.
|
|
1314
|
+
|
|
1315
|
+
### Triggering commands
|
|
1316
|
+
|
|
1317
|
+
This starts the process of sending data to devices. It starts by updating an attribute into the Context Broker defined
|
|
1318
|
+
as `command` in the [config group](#config-group-datamodel) or in the [device provision](#device-datamodel). Commands
|
|
1319
|
+
attributes are created using `command` as attribute type. Also, you can define the protocol you want the commands to be
|
|
1320
|
+
sent (HTTP/MQTT) with the `transport` parameter at the provisioning process.
|
|
1321
|
+
|
|
1322
|
+
For a given device provisioned with a `ping` command defined, any update on this attribute "ping" at the NGSI entity in
|
|
1323
|
+
the Context Broker will send a command to your device. For instance, to send the `ping` command with value
|
|
1324
|
+
`Ping request` you could use the following operation in the Context Broker API:
|
|
1325
|
+
|
|
1326
|
+
```
|
|
1327
|
+
PUT /v2/entities/<entity_id>/attrs/ping?type=<entity_type>
|
|
1328
|
+
|
|
1329
|
+
{
|
|
1330
|
+
"value": "Ping request",
|
|
1331
|
+
"type": "command"
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
It is important to note that parameter `type`, with the entity type must be included.
|
|
1337
|
+
|
|
1338
|
+
Context Broker API is quite flexible and allows to update an attribute in several ways. Please have a look to the
|
|
1339
|
+
[Orion API](<[http://telefonicaid.github.io/fiware-orion/api/v2/stable](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md)>)
|
|
1340
|
+
for details.
|
|
1341
|
+
|
|
1342
|
+
**Important note**: don't use operations in the NGSI API with creation semantics. Otherwise, the entity/attribute will
|
|
1343
|
+
be created locally to Context Broker and the command will not progress to the device (and you will need to delete the
|
|
1344
|
+
created entity/attribute if you want to make it to work again). Thus, the following operations _must not_ be used:
|
|
1345
|
+
|
|
1346
|
+
- `POST /v2/entities`
|
|
1347
|
+
- `POST /v2/entities/<id>/attrs`
|
|
1348
|
+
- `PUT /v2/entities/<id>/attrs`
|
|
1349
|
+
- `POST /v2/op/entites` with `actionType` `append`, `appendStrict` or `replace`
|
|
1350
|
+
|
|
1351
|
+
### Command reception
|
|
1352
|
+
|
|
1353
|
+
Once the command is triggered, it is send to the device. Depending on transport protocol, it is going to be sent to the
|
|
1354
|
+
device in a different way. After sending the command, the IoT Agent will have updated the value of `ping_status` to
|
|
1355
|
+
`PENDING` for entity into the Context Broker. Neither `ping_info` nor `ping` will be updated.
|
|
1356
|
+
|
|
1357
|
+
#### HTTP devices
|
|
1358
|
+
|
|
1359
|
+
**Push commands**
|
|
1360
|
+
|
|
1361
|
+
Push commands are those that are sent to the device once the IoT Agent receives the request from the Context Broker. In
|
|
1362
|
+
order to send push commands it is needed to set the `"endpoint": "http://[DEVICE_IP]:[PORT]/"` in the device or group
|
|
1363
|
+
provision. The device is supposed to be listening for commands at that URL in a synchronous way. Make sure the device
|
|
1364
|
+
endpoint is reachable by the IoT Agent. Push commands are only valid for HTTP devices. For MQTT devices it is not needed
|
|
1365
|
+
to set the `endpoint` parameter.
|
|
1366
|
+
|
|
1367
|
+
Considering using the IoTA-JSON Agent, and given the previous example, the device should receive a POST request to
|
|
1368
|
+
`http://[DEVICE_IP]:[PORT]` with the following payload:
|
|
1369
|
+
|
|
1370
|
+
```
|
|
1371
|
+
POST /
|
|
1372
|
+
Content-Type: application/json
|
|
1373
|
+
|
|
1374
|
+
{"ping":"Ping request"}
|
|
1375
|
+
```
|
|
1376
|
+
|
|
1377
|
+
**Poll commands**
|
|
1378
|
+
|
|
1379
|
+
Poll commands are those that are stored in the IoT Agent waiting to be retrieved by the devices. This kind of commands
|
|
1380
|
+
are typically used for devices that doesn't have a public IP or the IP cannot be reached because of power or netkork
|
|
1381
|
+
constrictions. The device connects to the IoT Agent periodically to retrieve commands. In order to configure the device
|
|
1382
|
+
as poll commands you just need to avoid the usage of `endpoint` parameter in the device provision.
|
|
1383
|
+
|
|
1384
|
+
Once the command request is issued to the IoT agent, the command is stored waiting to be retrieved by the device. In
|
|
1385
|
+
that moment, the status of the command is `"<command>_status": "PENDING"`.
|
|
1386
|
+
|
|
1387
|
+
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
|
|
1388
|
+
as `resource` in the provisioned group (`/iot/json` by default in IoTA-JSON if no `resource` is used) with the following
|
|
1389
|
+
parameters:
|
|
1390
|
+
|
|
1391
|
+
**FIXME [#1524](https://github.com/telefonicaid/iotagent-node-lib/issues/1524)**: `resource` different to the default
|
|
1392
|
+
one (`/iot/json` in the case of the [IoTA-JSON](https://github.com/telefonicaid/iotagent-json)) is not working at the
|
|
1393
|
+
present moment, but it will when this issue gets solved.
|
|
1394
|
+
|
|
1395
|
+
- `k`: API key of the device.
|
|
1396
|
+
- `i`: Device ID.
|
|
1397
|
+
- `getCmd`: This parameter is used to indicate the IoT Agent that the device is requesting a command. It is needed to
|
|
1398
|
+
set it to `1`
|
|
1399
|
+
|
|
1400
|
+
Taking the previous example, and considering the usage of the IoTA-JSON Agent, the device should make the following
|
|
1401
|
+
request, being the response to this request a JSON object with the command name as key and the command value as value:
|
|
1402
|
+
|
|
1403
|
+
**Request:**
|
|
1404
|
+
|
|
1405
|
+
```
|
|
1406
|
+
GET /iot/json?k=<apikey>&i=<deviceId>&getCmd=1
|
|
1407
|
+
Accept: application/json
|
|
1408
|
+
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
**Response:**:
|
|
1412
|
+
|
|
1413
|
+
```
|
|
1414
|
+
200 OK
|
|
1415
|
+
Content-type: application/json
|
|
1416
|
+
|
|
1417
|
+
{"ping":"Ping request"}
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
For IoT Agents different from IoTA-JSON it is exactly the same just changing in the request the resource by the
|
|
1421
|
+
corresponding resource employed by the agent (i.e., IoTA-UL uses `/iot/d` as default resource instead of `/iot/json`)
|
|
1422
|
+
and setting the correct `<apikey>` and `<deviceId>`. The response will be also different depending on the IoT Agent
|
|
1423
|
+
employed.
|
|
1424
|
+
|
|
1425
|
+
**FIXME [#1524](https://github.com/telefonicaid/iotagent-node-lib/issues/1524)**: `resource` different to the default
|
|
1426
|
+
one (`/iot/json` in the case of the [IoTA-JSON](https://github.com/telefonicaid/iotagent-json)) is not working at the
|
|
1427
|
+
present moment, but it will when this issue gets solved.
|
|
1428
|
+
|
|
1429
|
+
**Request**
|
|
1430
|
+
|
|
1431
|
+
```
|
|
1432
|
+
POST /iot/json?k=<apikey>&i=<deviceId>&getCmd=1
|
|
1433
|
+
Content-Type: application/json
|
|
1434
|
+
|
|
1435
|
+
{"t":25,"h":42,"l":"1299"}
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
**Response**
|
|
1439
|
+
|
|
1440
|
+
```
|
|
1441
|
+
200 OK
|
|
1442
|
+
Content-type: application/json
|
|
1443
|
+
|
|
1444
|
+
{"ping":"Ping request"}
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
This is also possible for IoTA-UL Agent changing in the request the resource, setting the correct `<apikey>`,
|
|
1448
|
+
`<deviceId>`, payload and headers.
|
|
1449
|
+
|
|
1450
|
+
Once the command is retrieved by the device the status is updated to `"<command>_status": "DELIVERED"`. Note that status
|
|
1451
|
+
`DELIVERED` only make sense in the case of poll commands. In the case of push command it cannot happen.
|
|
1452
|
+
|
|
1453
|
+
#### MQTT devices
|
|
1454
|
+
|
|
1455
|
+
For MQTT devices, it is not needed to declare an endpoint (i.e. if included in the provisioning request, it is not
|
|
1456
|
+
used). The device is supposed to be subscribed to the following MQTT topic where the IoT Agent will publish the command:
|
|
1457
|
+
|
|
1458
|
+
```
|
|
1459
|
+
/<apiKey>/<deviceId>/cmd
|
|
1460
|
+
```
|
|
1461
|
+
|
|
1462
|
+
In the case of using the IoTA-JSON Agent, the device should subscribe to the previous topic where it is going to receive
|
|
1463
|
+
a message like the following one when a command is triggered in the Context Broker like the previous step:
|
|
1464
|
+
|
|
1465
|
+
```json
|
|
1466
|
+
{ "ping": "Ping request" }
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
Please note that the device should subscribe to the broker using the disabled clean session mode (enabled using
|
|
1470
|
+
`--disable-clean-session` option CLI parameter in `mosquitto_sub`). This option means that all of the subscriptions for
|
|
1471
|
+
the device will be maintained after it disconnects, along with subsequent QoS 1 and QoS 2 commands that arrive. When the
|
|
1472
|
+
device reconnects, it will receive all of the queued commands.
|
|
1473
|
+
|
|
1474
|
+
### Command confirmation
|
|
1475
|
+
|
|
1476
|
+
Once the command is completely processed by the device, it should return the result of the command to the IoT Agent.
|
|
1477
|
+
This result will be progressed to the Context Broker where it will be stored in the `<command>_info` attribute. The
|
|
1478
|
+
status of the command will be stored in the `<command>_status` attribute (`OK` if everything goes right).
|
|
1479
|
+
|
|
1480
|
+
For the IoTA-JSON, the payload of the confirmation message must be a JSON object with name of the command as key and the
|
|
1481
|
+
result of the command as value. For other IoT Agents, the payload must follow the corresponding protocol. For a given
|
|
1482
|
+
`ping` command, with a command result `status_ok`, the response payload should be:
|
|
1483
|
+
|
|
1484
|
+
```JSON
|
|
1485
|
+
{"ping":"status_ok"}
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
Eventually, once the device makes the response request the IoTA would update the attributes `ping_status` to `OK` and
|
|
1489
|
+
`ping_info` to `status_ok` for the previous example.
|
|
1490
|
+
|
|
1491
|
+
#### HTTP
|
|
1492
|
+
|
|
1493
|
+
In order confirm the command execution, the device must make a POST request to the IoT Agent with the result of the
|
|
1494
|
+
command as payload, no matter if it is a push or a poll command. Following with the IoTAgent JSON case, the request must
|
|
1495
|
+
be made to the `/iot/json/commands`, this way:
|
|
1496
|
+
|
|
1497
|
+
```
|
|
1498
|
+
POST /iot/json/commands?k=<apikey>&i=<deviceId>
|
|
1499
|
+
Content-Type: application/json
|
|
1500
|
+
Accept: application/json
|
|
1501
|
+
|
|
1502
|
+
{"ping":"status_ok"}
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
#### MQTT
|
|
1506
|
+
|
|
1507
|
+
The device should publish the result of the command (`{"ping":"status_ok"}` in the previous example) to a topic
|
|
1508
|
+
following the next pattern:
|
|
1509
|
+
|
|
1510
|
+
```
|
|
1511
|
+
/<iotagent-protocol>/<apiKey>/<deviceId>/cmdexe
|
|
1512
|
+
```
|
|
1513
|
+
|
|
1514
|
+
The IoTA is subscribed to that topic, so it gets the result of the command. When this happens, the status is updated
|
|
1515
|
+
to`"<command>_status": "OK"`. Also the result of the command delivered by the device is stored in the `<command>_info`
|
|
1516
|
+
attribute.
|
|
1517
|
+
|
|
1178
1518
|
## Overriding global Context Broker host
|
|
1179
1519
|
|
|
1180
1520
|
**cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific types
|
|
@@ -1431,19 +1771,21 @@ Config group is represented by a JSON object with the following fields:
|
|
|
1431
1771
|
| `static_attributes` | ✓ | | | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
|
|
1432
1772
|
| `internal_attributes` | ✓ | | | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
|
|
1433
1773
|
| `explicitAttrs` | ✓ | string or bool | ✓ | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
|
|
1434
|
-
| `entityNameExp` | ✓ | string | | optional field to allow use expressions to define entity name, instead default name, based on the device ID and the entity type (i.e. `<device_id>:<entity_type>`)
|
|
1774
|
+
| `entityNameExp` | ✓ | string | | optional field to allow use expressions to define entity name, instead default name, based on the device ID and the entity type (i.e. `<device_id>:<entity_type>`). More information in [specific section](#entity-name-expression-entitynameexp) |
|
|
1435
1775
|
| `ngsiVersion` | ✓ | string | | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. Possible values are: `v2` or `ld`. The default is `v2`. When not running in mixed mode, this field is ignored. |
|
|
1436
1776
|
| `defaultEntityNameConjunction` | ✓ | string | | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. |
|
|
1437
1777
|
| `autoprovision` | ✓ | bool | ✓? | optional boolean: If `false`, autoprovisioned devices (i.e. devices that are not created with an explicit provision operation but when the first measure arrives) are not allowed in this group. Default (in the case of omitting the field) is `true`. |
|
|
1438
1778
|
| `payloadType` | ✓ | string | | optional string value used to switch between **IoTAgent**, **NGSI-v2** and **NGSI-LD** measure payloads types. Possible values are: `iotagent`, `ngsiv2` or `ngsild`. The default is `iotagent`. |
|
|
1439
1779
|
| `transport` | ✓ | `string` | | Transport protocol used by the group of devices to send updates, for the IoT Agents with multiple transport protocols. |
|
|
1440
1780
|
| `endpoint` | ✓ | `string` | | Endpoint where the group of device is going to receive commands, if any. |
|
|
1781
|
+
| `storeLastMeasure` | ✓ | `boolean` | | Store in device last measure received. See more info [in this section](admin.md#storelastmeasure). False by default |
|
|
1782
|
+
| `useCBflowControl` | ✓ | `boolean` | | Use Context Broker flow control. See more info [in this section](admin.md#useCBflowControl). False by default |
|
|
1441
1783
|
|
|
1442
1784
|
### Config group operations
|
|
1443
1785
|
|
|
1444
1786
|
The following actions are available under the config group endpoint:
|
|
1445
1787
|
|
|
1446
|
-
#### Retrieve config groups `GET /iot/
|
|
1788
|
+
#### Retrieve config groups `GET /iot/groups`
|
|
1447
1789
|
|
|
1448
1790
|
List all the config groups for the given `fiware-service` and `fiware-servicepath`. The config groups that match the
|
|
1449
1791
|
`fiware-servicepath` are returned in any other case.
|
|
@@ -1467,14 +1809,14 @@ Successful operations return `Content-Type` header with `application/json` value
|
|
|
1467
1809
|
|
|
1468
1810
|
_**Response payload**_
|
|
1469
1811
|
|
|
1470
|
-
A JSON object with a
|
|
1471
|
-
[config group datamodel](#
|
|
1812
|
+
A JSON object with a `groups` field that contains an array of groups that match the request. See the
|
|
1813
|
+
[config group datamodel](#config-group-datamodel) for more information.
|
|
1472
1814
|
|
|
1473
1815
|
Example:
|
|
1474
1816
|
|
|
1475
1817
|
```json
|
|
1476
1818
|
{
|
|
1477
|
-
"
|
|
1819
|
+
"groups": [
|
|
1478
1820
|
{
|
|
1479
1821
|
"resource": "/deviceTest",
|
|
1480
1822
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1511,7 +1853,7 @@ Example:
|
|
|
1511
1853
|
}
|
|
1512
1854
|
```
|
|
1513
1855
|
|
|
1514
|
-
#### Create config group `POST /iot/
|
|
1856
|
+
#### Create config group `POST /iot/groups`
|
|
1515
1857
|
|
|
1516
1858
|
Creates a set of config groups for the given service and service path. The service and subservice information will taken
|
|
1517
1859
|
from the headers, overwritting any preexisting values.
|
|
@@ -1525,14 +1867,14 @@ _**Request headers**_
|
|
|
1525
1867
|
|
|
1526
1868
|
_**Request payload**_
|
|
1527
1869
|
|
|
1528
|
-
A JSON object with a `
|
|
1529
|
-
[config group datamodel](#
|
|
1870
|
+
A JSON object with a `groups` field. The value is an array of config groups objects to create. See the
|
|
1871
|
+
[config group datamodel](#config-group-datamodel) for more information.
|
|
1530
1872
|
|
|
1531
1873
|
Example:
|
|
1532
1874
|
|
|
1533
1875
|
```json
|
|
1534
1876
|
{
|
|
1535
|
-
"
|
|
1877
|
+
"groups": [
|
|
1536
1878
|
{
|
|
1537
1879
|
"resource": "/deviceTest",
|
|
1538
1880
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
@@ -1566,7 +1908,7 @@ _**Response headers**_
|
|
|
1566
1908
|
|
|
1567
1909
|
Successful operations return `Content-Type` header with `application/json` value.
|
|
1568
1910
|
|
|
1569
|
-
#### Modify config group `PUT /iot/
|
|
1911
|
+
#### Modify config group `PUT /iot/groups`
|
|
1570
1912
|
|
|
1571
1913
|
Modifies the information of a config group, identified by the `resource` and `apikey` query parameters. Takes a service
|
|
1572
1914
|
group body as the payload. The body does not have to be complete: for incomplete bodies, just the attributes included in
|
|
@@ -1588,8 +1930,8 @@ _**Request headers**_
|
|
|
1588
1930
|
|
|
1589
1931
|
_**Request payload**_
|
|
1590
1932
|
|
|
1591
|
-
A JSON object with the config group information to be modified. See the
|
|
1592
|
-
|
|
1933
|
+
A JSON object with the config group information to be modified. See the [config group datamodel](config-group-datamodel)
|
|
1934
|
+
for more information.
|
|
1593
1935
|
|
|
1594
1936
|
Example:
|
|
1595
1937
|
|
|
@@ -1606,7 +1948,7 @@ _**Response code**_
|
|
|
1606
1948
|
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
1607
1949
|
- 500 SERVER ERROR if there was any error not contemplated above.:
|
|
1608
1950
|
|
|
1609
|
-
#### Remove config group `DELETE /iot/
|
|
1951
|
+
#### Remove config group `DELETE /iot/groups`
|
|
1610
1952
|
|
|
1611
1953
|
Removes a config group, identified by the `resource` and `apikey` query parameters.
|
|
1612
1954
|
|
|
@@ -1658,6 +2000,9 @@ the API resource fields and the same fields in the database model.
|
|
|
1658
2000
|
| `explicitAttrs` | ✓ | `boolean` | ✓ | Field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
|
|
1659
2001
|
| `ngsiVersion` | ✓ | `string` | | string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. The default is `v2`. When not running in mixed mode, this field is ignored. |
|
|
1660
2002
|
| `payloadType` | ✓ | `string` | | optional string value used to switch between **IoTAgent**, **NGSI-v2** and **NGSI-LD** measure payloads types. Possible values are: `iotagent`, `ngsiv2` or `ngsild`. The default is `iotagent`. |
|
|
2003
|
+
| `storeLastMeasure` | ✓ | `boolean` | | Store in device last measure received. Useful just for debugging purpose. See more info [in this section](admin.md#storelastmeasure). False by default. |
|
|
2004
|
+
| `lastMeasure` | ✓ | `object` | | last measure stored on device when `storeLastMeasure` is enabled. See more info [in this section](admin.md#storelastmeasure). This field can be cleared using `{}` in a device update request. In that case, `lastMeasure` is removed from device (until a next measure is received and `lastMesuare` gets created again). |
|
|
2005
|
+
| `useCBflowControl` | ✓ | `boolean` | | Use Context Broker flow control. See more info [in this section](admin.md#useCBflowControl). False by default. |
|
|
1661
2006
|
|
|
1662
2007
|
### Device operations
|
|
1663
2008
|
|
|
@@ -2026,6 +2371,57 @@ Example:
|
|
|
2026
2371
|
}
|
|
2027
2372
|
```
|
|
2028
2373
|
|
|
2374
|
+
### Metrics
|
|
2375
|
+
|
|
2376
|
+
The IoT Agent Library exposes a [openmetrics-compatible](https://github.com/OpenObservability/OpenMetrics) endpoint for
|
|
2377
|
+
telemetry collectors to gather application statistics.
|
|
2378
|
+
|
|
2379
|
+
#### Retrieve metrics `GET /metrics`
|
|
2380
|
+
|
|
2381
|
+
_**Response code**_
|
|
2382
|
+
|
|
2383
|
+
- `200` `OK` if successful.
|
|
2384
|
+
- `406` `Wrong Accept Header` If accept format is not supported.
|
|
2385
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
2386
|
+
|
|
2387
|
+
_**Response body**_
|
|
2388
|
+
|
|
2389
|
+
Returns the current value of the server stats,
|
|
2390
|
+
|
|
2391
|
+
- If `Accept` header contains `application/openmetrics-text; version=(1.0.0|0.0.1)`, the response has content-type
|
|
2392
|
+
`application/openmetrics-text; version=<the requested version>; charset=utf-8`
|
|
2393
|
+
- Else, If `Accept` header is missing or supports `text/plain` (explicitly or by `*/*`) , the response has
|
|
2394
|
+
content-type `text/plain; version=0.0.4; charset=utf-8` (legacy format for [prometheus](https://prometheus.io))
|
|
2395
|
+
- In any other case, returns an error message with `406` status.
|
|
2396
|
+
|
|
2397
|
+
For the kind of metrics exposed by the application, the actual payload itself is completely the same for both
|
|
2398
|
+
content-types, and follows the openmetrics specification, e.g:
|
|
2399
|
+
|
|
2400
|
+
```
|
|
2401
|
+
# HELP deviceCreationRequests global metric for deviceCreationRequests
|
|
2402
|
+
# TYPE deviceCreationRequests counter
|
|
2403
|
+
deviceCreationRequests 0
|
|
2404
|
+
# HELP deviceRemovalRequests global metric for deviceRemovalRequests
|
|
2405
|
+
# TYPE deviceRemovalRequests counter
|
|
2406
|
+
deviceRemovalRequests 0
|
|
2407
|
+
# HELP measureRequests global metric for measureRequests
|
|
2408
|
+
# TYPE measureRequests counter
|
|
2409
|
+
measureRequests 0
|
|
2410
|
+
# HELP raiseAlarm global metric for raiseAlarm
|
|
2411
|
+
# TYPE raiseAlarm counter
|
|
2412
|
+
raiseAlarm 0
|
|
2413
|
+
# HELP releaseAlarm global metric for releaseAlarm
|
|
2414
|
+
# TYPE releaseAlarm counter
|
|
2415
|
+
releaseAlarm 0
|
|
2416
|
+
# HELP updateEntityRequestsOk global metric for updateEntityRequestsOk
|
|
2417
|
+
# TYPE updateEntityRequestsOk counter
|
|
2418
|
+
updateEntityRequestsOk 2
|
|
2419
|
+
# HELP updateEntityRequestsError global metric for updateEntityRequestsError
|
|
2420
|
+
# TYPE updateEntityRequestsError counter
|
|
2421
|
+
updateEntityRequestsError 5
|
|
2422
|
+
# EOF
|
|
2423
|
+
```
|
|
2424
|
+
|
|
2029
2425
|
[1]:
|
|
2030
2426
|
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
2427
|
[2]:
|