iotagent-node-lib 3.0.0 → 3.2.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 +1 -0
- package/config.js +5 -5
- package/doc/api.md +1540 -298
- package/doc/deprecated.md +3 -1
- package/doc/development.md +120 -0
- package/doc/installationguide.md +3 -6
- package/docker-compose-dev.yml +1 -1
- package/lib/commonConfig.js +7 -10
- package/lib/fiware-iotagent-lib.js +0 -10
- package/lib/jexlTranformsMap.js +2 -1
- package/lib/plugins/bidirectionalData.js +8 -26
- package/lib/plugins/expressionPlugin.js +8 -40
- package/lib/plugins/jexlParser.js +28 -0
- package/lib/services/commands/commandService.js +1 -1
- package/lib/services/devices/deviceService.js +2 -1
- package/lib/services/ngsi/entities-NGSI-LD.js +15 -73
- package/lib/services/ngsi/entities-NGSI-v2.js +149 -124
- package/lib/services/northBound/deviceProvisioningServer.js +17 -14
- package/lib/templates/createDevice.json +5 -2
- package/lib/templates/createDeviceLax.json +7 -5
- package/lib/templates/updateDevice.json +5 -2
- package/lib/templates/updateDeviceLax.json +3 -5
- package/package.json +2 -2
- package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +1 -0
- package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
- package/test/unit/general/config-multi-core-test.js +2 -1
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -1
- package/test/unit/general/deviceService-test.js +2 -1
- package/test/unit/general/loglevel-api_test.js +2 -1
- package/test/unit/general/startup-test.js +2 -1
- package/test/unit/general/statistics-persistence_test.js +1 -0
- package/test/unit/general/statistics-service_test.js +1 -0
- package/test/unit/lazyAndCommands/commandRegistry_test.js +1 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +1 -0
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -0
- package/test/unit/mongodb/mongodb-registry-test.js +2 -1
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +2 -1
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
- package/test/unit/ngsi-ld/general/deviceService-test.js +2 -1
- package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
- package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsi-ld/general/startup-test.js +3 -2
- package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +5 -7
- package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +8 -6
- package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +18 -22
- package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +32 -34
- package/test/unit/ngsi-ld/ngsiService/staticAttributes-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +11 -15
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +10 -9
- package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +3 -2
- package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/device-registration_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +15 -12
- package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +1 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +13 -7
- package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +0 -8
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +6 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +20 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +32 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +7 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +7 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +7 -1
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +232 -8
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
- package/test/unit/ngsiv2/general/deviceService-test.js +2 -1
- package/test/unit/ngsiv2/general/https-support-test.js +2 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsiv2/general/startup-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +8 -6
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -3
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +3 -2
- package/test/unit/ngsiv2/ngsiService/autocast-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +2 -1
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +2 -1
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +8 -7
- package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +2 -1
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +151 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +87 -13
- package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -3
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +15 -12
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +1 -0
- package/test/unit/plugins/capture-configuration-inPlugins_test.js +2 -1
- package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
- package/doc/advanced-topics.md +0 -626
- package/doc/expressionLanguage.md +0 -762
- package/lib/plugins/expressionParser.js +0 -205
- package/test/unit/expressions/expression-test.js +0 -197
- package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -881
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -950
- package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -294
package/doc/api.md
CHANGED
|
@@ -1,116 +1,1206 @@
|
|
|
1
1
|
# IoT Agent API
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
<!-- TOC -->
|
|
4
|
+
|
|
5
|
+
- [Preface](#preface)
|
|
6
|
+
- [Topics](#topics)
|
|
7
|
+
- [Terminology](#terminology)
|
|
8
|
+
- [IoT Agent information model](#iot-agent-information-model)
|
|
9
|
+
- [Config groups](#config-groups)
|
|
10
|
+
- [Devices](#devices)
|
|
11
|
+
- [Entity attributes](#entity-attributes)
|
|
12
|
+
- [Multientity support)](#multientity-support)
|
|
13
|
+
- [Metadata support](#metadata-support)
|
|
14
|
+
- [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
|
|
15
|
+
- [Advice on Attribute definitions](#advice-on-attribute-definitions)
|
|
16
|
+
- [Reuse of attribute names](#reuse-of-attribute-names)
|
|
17
|
+
- [Reuse of attribute types](#reuse-of-attribute-types)
|
|
18
|
+
- [How to specify attribute Units of Measurement](#how-to-specify-attribute-units-of-measurement)
|
|
19
|
+
- [Measurement persistence options](#measurement-persistence-options)
|
|
20
|
+
- [Autoprovision configuration (autoprovision)](#autoprovision-configuration-autoprovision)
|
|
21
|
+
- [Explicitly defined attributes (explicitAttrs)](#explicitly-defined-attributes-explicitattrs)
|
|
22
|
+
- [Configuring operation to persist the data in Context Broker (appendMode)](#configuring-operation-to-persist-the-data-in-context-broker-appendmode)
|
|
23
|
+
- [Differences between `autoprovision`, `explicitAttrs` and `appendMode`](#differences-between-autoprovision-explicitattrs-and-appendmode)
|
|
24
|
+
- [Expression language support](#expression-language-support)
|
|
25
|
+
- [Examples of JEXL expressions](#examples-of-jexl-expressions)
|
|
26
|
+
- [Available functions](#available-functions)
|
|
27
|
+
- [Expressions with multiple transformations](#expressions-with-multiple-transformations)
|
|
28
|
+
- [Measurement transformation](#measurement-transformation)
|
|
29
|
+
- [Measurement transformation definition](#measurement-transformation-definition)
|
|
30
|
+
- [Measurement transformation execution](#measurement-transformation-execution)
|
|
31
|
+
- [Multientity measurement transformation support (`object_id`)](#multientity-measurement-transformation-support-object_id)
|
|
32
|
+
- [Timestamp Compression](#timestamp-compression)
|
|
33
|
+
- [Timestamp Processing](#timestamp-processing)
|
|
34
|
+
- [Bidirectionality plugin (bidirectional)](#bidirectionality-plugin-bidirectional)
|
|
35
|
+
- [Overriding global Context Broker host](#overriding-global-context-broker-host)
|
|
36
|
+
- [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
|
|
37
|
+
- [Secured access to the Context Broker](#secured-access-to-the-context-broker)
|
|
38
|
+
- [NGSI-LD support](#ngsi-ld-support)
|
|
39
|
+
- [NGSI-LD `GeoProperty` support](#ngsi-ld-geoproperty-support)
|
|
40
|
+
- [NGSI-LD Linked Data support](#ngsi-ld-linked-data-support)
|
|
41
|
+
- [NGSI-LD `datasetId` support](#ngsi-ld-datasetid-support)
|
|
42
|
+
- [API Routes](#api-routes)
|
|
43
|
+
- [Config group API](#config-group-api)
|
|
44
|
+
- [Config group datamodel](#config-group-datamodel)
|
|
45
|
+
- [Config group operations](#config-group-operations)
|
|
46
|
+
- [Retrieve config groups `GET /iot/services`](#retrieve-config-groups-get-iotservices)
|
|
47
|
+
- [Create config group `POST /iot/services`](#create-config-group-post-iotservices)
|
|
48
|
+
- [Modify config group `PUT /iot/services`](#modify-config-group-put-iotservices)
|
|
49
|
+
- [Remove config group `DELETE /iot/services`](#remove-config-group-delete-iotservices)
|
|
50
|
+
- [Device API](#device-api)
|
|
51
|
+
- [Device datamodel](#device-datamodel)
|
|
52
|
+
- [Device operations](#device-operations)
|
|
53
|
+
- [Retrieve devices /iot/devices `GET /iot/devices`](#retrieve-devices-iotdevices-get-iotdevices)
|
|
54
|
+
- [Create device `POST /iot/devices`](#create-device-post-iotdevices)
|
|
55
|
+
- [Get device details `GET /iot/devices/:deviceId`](#get-device-details-get-iotdevicesdeviceid)
|
|
56
|
+
- [Modify device `PUT /iot/devices/:deviceId`](#modify-device-put-iotdevicesdeviceid)
|
|
57
|
+
- [Remove device `DELETE /iot/devices/:deviceId`](#remove-device-delete-iotdevicesdeviceid)
|
|
58
|
+
- [Miscellaneous API](#miscellaneous-api)
|
|
59
|
+
- [Log operations](#log-operations)
|
|
60
|
+
- [Modify Loglevel `PUT /admin/log`](#modify-loglevel-put-adminlog)
|
|
61
|
+
- [Retrieve log level `PUT /admin/log`](#retrieve-log-level-put-adminlog)
|
|
62
|
+
- [About operations](#about-operations)
|
|
63
|
+
- [List IoTA Information `GET /iot/about`](#list-iota-information-get-iotabout)
|
|
64
|
+
|
|
65
|
+
<!-- /TOC -->
|
|
66
|
+
|
|
67
|
+
# Preface
|
|
68
|
+
|
|
69
|
+
The IoT Agent mission is to provide a common abstraction layer between the devices and the NGSI entities stored in
|
|
70
|
+
Context Broker. In order to achieve this, the IoT Agent sits between the Context Broker and a set of devices. It is in
|
|
71
|
+
charge of translating the information coming from the devices into NGSI requests and viceversa.
|
|
72
|
+
|
|
73
|
+
The **IoT Agent node library** is a Node.js module that can be used to implement IoT Agents. It provides a set of common
|
|
74
|
+
functionality that can be used to implement the different IoT Agents, offering a simple REST API which provides common
|
|
75
|
+
functionality to access, provision and decommission devices and config groups of devices. This document describes the
|
|
76
|
+
API provided by the IoT Agent node library.
|
|
77
|
+
|
|
78
|
+
# Topics
|
|
79
|
+
|
|
80
|
+
## Terminology
|
|
81
|
+
|
|
82
|
+
- **Devices**: A resource that match physical devices that are connected to the IoT Agent. Each device has a set of
|
|
83
|
+
attributes that can be read or written and a set of commands that can be invoked. The device is identified by a
|
|
84
|
+
`device_id` and points particular entity in the context broker.
|
|
85
|
+
- **Config Groups**: Also known as `provisioning groups` or `service groups`. A logical group of devices. Each Config
|
|
86
|
+
Group has a set of attributes that can be read or written. The config group is identified by a an `apikey`, used to
|
|
87
|
+
authenticate the requests coming from the devices.
|
|
88
|
+
- **Measurements**: A set of values that are sent by a device to the IoT Agent.
|
|
89
|
+
- **Service**: It is the `FIWARE-Service` that the device or config group belongs to.
|
|
90
|
+
- **Subservice**: It is the specific `FIWARE-ServicePath` that the device or config group belongs to.
|
|
91
|
+
- **provision**: The process of creating a new device. A device provisioned means that the device has been already
|
|
92
|
+
created in the IoT Agent. It can also refer to the group creation process.
|
|
93
|
+
- **autoprovision**: The process of creating a new device when a measure arrives to the IoT Agent and the device is
|
|
94
|
+
not provisioned yet. The attributes, entity type and other information is taken from the config group, and entity
|
|
95
|
+
name is generated according to the entity type and device ID or as a result of an expression if `entityNameExp` is
|
|
96
|
+
defined.
|
|
97
|
+
|
|
98
|
+
## IoT Agent information model
|
|
99
|
+
|
|
100
|
+
IoT Agents models 2 different kinds of resources: devices and config groups. Devices are the physical devices that send
|
|
101
|
+
measurements to the IoT Agent. Config groups are logical groups of devices that share the same configuration. A config
|
|
102
|
+
group contains zero or more devices.
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
erDiagram
|
|
106
|
+
"Config Group" ||--o{ Devices : contains
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Config groups
|
|
110
|
+
|
|
111
|
+
Config groups provides a template configuration for the all devices that belong to them. This allows to provision a set
|
|
112
|
+
of devices with a single operation. They are identified by an `apikey` and a `resource` and mapped to a particular
|
|
113
|
+
entity type.
|
|
114
|
+
|
|
115
|
+
Once a measure is received by the IoT Agent, the `apikey` and `resource` are used to identify the config group to which
|
|
116
|
+
the device belongs. The config group is used to map the measure to a particular entity type and to provide the
|
|
117
|
+
information needed to interact with the Context Broker.
|
|
118
|
+
|
|
119
|
+
If the device already exists in the Context Broker, the IoT Agent will update the entity with the new values. If the
|
|
120
|
+
device does not exist, the IoT Agent will create it with the information provided by the config group and eventually
|
|
121
|
+
will also create the entity in the Context Broker. This last operation is only possible if the IoT Agent is configured
|
|
122
|
+
to use autoprovisioning.
|
|
123
|
+
|
|
124
|
+
For every config group, the pair (resource, apikey) _must_ be unique (as it is used to identify which group to assign to
|
|
125
|
+
which device). Those operations of the API targeting specific resources will need the use of the `resource` and `apikey`
|
|
126
|
+
parameters to select the appropriate instance.
|
|
127
|
+
|
|
128
|
+
Config groups can be created with preconfigured sets of attributes, service information, security information and other
|
|
129
|
+
parameters. The specific parameters that can be configured for a given config group are described in the
|
|
130
|
+
[Config group datamodel](#config-group-datamodel) section.
|
|
131
|
+
|
|
132
|
+
### Devices
|
|
133
|
+
|
|
134
|
+
A device contains the information that connects a physical device to a particular entity in the Context Broker. Devices
|
|
135
|
+
are identified by a `device_id`, and they are associated to an existing config group based in `apiKey` matching or
|
|
136
|
+
`type` matching (in the case `apiKey` matching fails). For instance, let's consider a situation in which a config group
|
|
137
|
+
has been provisioned with `type=X`/`apiKey=111` and no other config group has been provisioned.
|
|
138
|
+
|
|
139
|
+
The IoT Agents offer a provisioning API where devices can be preregistered, so all the information about service and
|
|
140
|
+
subservice mapping, security information and attribute configuration can be specified in a per device way instead of
|
|
141
|
+
relaying on the config group configuration. The specific parameters that can be configured for a given device are
|
|
142
|
+
described in the [Device datamodel](#device-datamodel) section.
|
|
143
|
+
|
|
144
|
+
## Entity attributes
|
|
145
|
+
|
|
146
|
+
In the config group/device model there are four list of attributes with different purpose to configure how the
|
|
147
|
+
information coming from the device is mapped to the Context Broker attributes:
|
|
148
|
+
|
|
149
|
+
- **`attributes`**: Are measures that are pushed from the device to the IoT agent. This measure changes will be sent
|
|
150
|
+
to the Context Broker as updateContext requests over the device entity. NGSI queries to the context broker will be
|
|
151
|
+
resolved in the Broker database. For each attribute, its `name` and `type` must be provided. Additional `metadata`
|
|
152
|
+
is optional.
|
|
153
|
+
|
|
154
|
+
- **`lazy`**: Passive measures that are pulled from the device to the IoT agent. When a request for data from a lazy
|
|
155
|
+
attribute arrives to the Context Broker, it forwards the request to the Context Provider of that entity, in this
|
|
156
|
+
case the IoT Agent. The IoT Agent will then ask the device for the information needed, transform that information to
|
|
157
|
+
a NGSI format and return it to the Context Broker. This operation will be synchronous from the customer perspective:
|
|
158
|
+
the Context Broker won't return a response until the device has returned its response to the IoT Agent. For each
|
|
159
|
+
attribute, its `name` and `type` must be provided.
|
|
160
|
+
|
|
161
|
+
- **`static`**: It is static attributes that are persisted in the Context Broker. They are not updated by the device,
|
|
162
|
+
but they can be modified by the user. They are useful to store information about the device that is not updated by
|
|
163
|
+
the device itself. For instance, a `location` static attribute is can be used to store the location of a fixed
|
|
164
|
+
device.
|
|
165
|
+
|
|
166
|
+
- **`commands`**: Commands are actions that can be invoked in the device. They are similar to attributes, but they are
|
|
167
|
+
not updated by the device. They are updated by the Context Broker, and the IoT Agent will be in charge of
|
|
168
|
+
translating the updateContext request to the proper action in the device. Two additional attributes are created for
|
|
169
|
+
each command: `status` and `info`. For each command, its `name` and `type` must be provided.
|
|
170
|
+
|
|
171
|
+
All of them have the same syntax, a list of objects with the following attributes:
|
|
172
|
+
|
|
173
|
+
- **object_id** (optional): name of the attribute as coming from the device.
|
|
174
|
+
- **name** (mandatory): ID of the attribute in the target entity in the Context Broker.
|
|
175
|
+
- **type** (mandatory): name of the type of the attribute in the target entity.
|
|
176
|
+
- **metadata** (optional): additional static metadata for the attribute in the target entity. (e.g. `unitCode`)
|
|
177
|
+
|
|
178
|
+
Some transformation plugins also allow the use of the following optional fields:
|
|
179
|
+
|
|
180
|
+
- **expression**: indicates that the value of the target attribute will not be the plain value or the measurement, but
|
|
181
|
+
an expression based on a combination of the reported values. See the
|
|
182
|
+
[Expression Language definition](#expression-language-support) for details
|
|
183
|
+
- **skipValue**: indicates that if the result of applying `expression` to a measure is equal to the value of `skipValue` then
|
|
184
|
+
the attribute corresponding to the measure is not sent to CB. By default if `skipValue` is not defined then is
|
|
185
|
+
considered as `null` (i.e. if the result of apply `expression` results in `null` then corresponding attribute is not
|
|
186
|
+
sent to CB). It is only used if `expression` is provided (otherwise is ignored).
|
|
187
|
+
- **entity_name**: the presence of this attribute indicates that the value will not be stored in the original device
|
|
188
|
+
entity but in a new entity with an ID given by this attribute. The type of this additional entity can be configured
|
|
189
|
+
with the `entity_type` attribute. If no type is configured, the device entity type is used instead. Entity names can
|
|
190
|
+
be defined as expressions, using the [Expression Language definition](#expression-language-support).
|
|
191
|
+
- **entity_type**: configures the type of an alternative entity.
|
|
192
|
+
- **reverse**: add bidirectionality expressions to the attribute. See the **bidirectionality** transformation plugin
|
|
193
|
+
in the [Data Mapping Plugins section](development.md#bidirectionality-plugin-bidirectional) for details.
|
|
194
|
+
|
|
195
|
+
Additionally for commands (which are attributes of type `command`) the following fields are optional:
|
|
196
|
+
|
|
197
|
+
- **expression** indicates that the value of the target command will not be the plain value or the command, but an
|
|
198
|
+
expression based on a combination of the returned values. See the
|
|
199
|
+
[Expression Language definition](#expression-language-support) for details
|
|
200
|
+
- **payloadType**: indicates how command payload will be transformed before be sent to device. Please have a look to
|
|
201
|
+
particular IOTAs documentation for allowed values of this field in each case.
|
|
202
|
+
- **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of
|
|
203
|
+
transports)
|
|
204
|
+
|
|
205
|
+
## Multientity support
|
|
206
|
+
|
|
207
|
+
The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities
|
|
208
|
+
through the config group or device provision APIs.
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"devices": [
|
|
213
|
+
{
|
|
214
|
+
"protocol": "IoTA-UL",
|
|
215
|
+
"entity_name": "urn:ngsi-ld:Device:contador12",
|
|
216
|
+
"entity_type": "multientity",
|
|
217
|
+
"attributes": [
|
|
218
|
+
{
|
|
219
|
+
"object_id": "cont1",
|
|
220
|
+
"name": "vol",
|
|
221
|
+
"type": "Text",
|
|
222
|
+
"entity_name": "urn:ngsi-ld:Device:WaterMeterSoria01",
|
|
223
|
+
"entity_type": "WaterMeter"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"object_id": "cont2",
|
|
227
|
+
"name": "vol",
|
|
228
|
+
"type": "Text",
|
|
229
|
+
"entity_name": "urn:ngsi-ld:Device:WaterMeterSoria02",
|
|
230
|
+
"entity_type": "WaterMeter"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"object_id": "cont3",
|
|
234
|
+
"name": "vol",
|
|
235
|
+
"type": "Text",
|
|
236
|
+
"entity_name": "urn:ngsi-ld:Device:WaterMeterSoria03",
|
|
237
|
+
"entity_type": "WaterMeter"
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"device_id": "contador12"
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Metadata support
|
|
247
|
+
|
|
248
|
+
Both `attributes` and `static_attributes` may be supplied with metadata when creating a config group, so that the units
|
|
249
|
+
of measurement can be placed into the resultant entity.
|
|
250
|
+
|
|
251
|
+
e.g.:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"entity_type": "Lamp",
|
|
256
|
+
"resource": "/iot/d",
|
|
257
|
+
"protocol": "PDI-IoTA-UltraLight",
|
|
258
|
+
..etc
|
|
259
|
+
"commands": [
|
|
260
|
+
{"name": "on","type": "command"},
|
|
261
|
+
{"name": "off","type": "command"}
|
|
262
|
+
],
|
|
263
|
+
"attributes": [
|
|
264
|
+
{"object_id": "s", "name": "state", "type":"Text"},
|
|
265
|
+
{"object_id": "l", "name": "luminosity", "type":"Integer",
|
|
266
|
+
"metadata":{
|
|
267
|
+
"unitCode":{"type": "Text", "value" :"CAL"}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"static_attributes": [
|
|
272
|
+
{"name": "category", "type":"Text", "value": ["actuator","sensor"]},
|
|
273
|
+
{"name": "controlledProperty", "type": "Text", "value": ["light"],
|
|
274
|
+
"metadata":{
|
|
275
|
+
"includes":{"type": "Text", "value" :["state", "luminosity"]},
|
|
276
|
+
"alias":{"type": "Text", "value" :"lamp"}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### NGSI-LD data and metadata considerations
|
|
284
|
+
|
|
285
|
+
When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the
|
|
286
|
+
following:
|
|
287
|
+
|
|
288
|
+
- `Property`, `Relationship`, `GeoProperty`, `LanguageProperty`
|
|
289
|
+
- Native JSON types (e.g. `String`, `Boolean`, `Float` , `Integer` `Number`)
|
|
290
|
+
- Temporal Properties (e.g. `Datetime`, `Date` , `Time`)
|
|
291
|
+
- GeoJSON types (e.g `Point`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`)
|
|
292
|
+
|
|
293
|
+
Most NGSI-LD attributes are sent to the Context Broker as _properties_. If a GeoJSON type or native JSON type is
|
|
294
|
+
defined, the data will be converted to the appropriate type. Temporal properties should always be expressed in UTC,
|
|
295
|
+
using ISO 8601. This ISO 8601 conversion is applied automatically for the `observedAt` _property-of-a-property_ metadata
|
|
296
|
+
where present.
|
|
297
|
+
|
|
298
|
+
Data for any attribute defined as a _relationship_ must be a valid URN.
|
|
299
|
+
|
|
300
|
+
Note that when the `unitCode` metadata attribute is supplied in the provisioning data under NGSI-LD, the standard
|
|
301
|
+
`unitCode` _property-of-a-property_ `String` attribute is created.
|
|
302
|
+
|
|
303
|
+
Other unrecognised `type` attributes will be passed as NGSI-LD data using the following JSON-LD format:
|
|
304
|
+
|
|
305
|
+
```json
|
|
306
|
+
"<property_name>": {
|
|
307
|
+
"type" : "Property",
|
|
308
|
+
"value": {
|
|
309
|
+
"@type": "<property_type>",
|
|
310
|
+
"@value": { string or object}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
`null` values will be passed in the following format:
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
"<property_name>": {
|
|
319
|
+
"type" : "Property",
|
|
320
|
+
"value": {
|
|
321
|
+
"@type": "Intangible",
|
|
322
|
+
"@value": null
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Advice on Attribute definitions
|
|
328
|
+
|
|
329
|
+
### Reuse of attribute names
|
|
330
|
+
|
|
331
|
+
Check for the existence of the same Attribute on any of the other models and reuse it, if pertinent. Have a look at
|
|
332
|
+
schema.org trying to find a similar term with the same semantics. Try to find common used ontologies or existing
|
|
333
|
+
standards well accepted by the Community, or by goverments, agencies, etc. For instance, Open311 for civic issue
|
|
334
|
+
tracking or Datex II for transport systems.
|
|
335
|
+
|
|
336
|
+
### Reuse of attribute types
|
|
337
|
+
|
|
338
|
+
When possible reuse [schema.org](http://schema.org/) data types (`Text`, `Number`, `DateTime`, `StructuredValue`, etc.).
|
|
339
|
+
Remember that `null` is not allowed in NGSI-LD and therefore should be avoided as a value.
|
|
340
|
+
|
|
341
|
+
### How to specify attribute Units of Measurement
|
|
342
|
+
|
|
343
|
+
If your data use the default unit defined in the Data Model, you don't need to specify any. It is implied. Unless
|
|
344
|
+
explicitly stated otherwise, all FIWARE data models use the metric system of measurements by default. Regardless the
|
|
345
|
+
model specification include explicit reference to the scale adopted. If your data use a different unit, you will need to
|
|
346
|
+
use the `unitCode` metadata annotation in your data (and you will need to adopt the normalised representation). The code
|
|
347
|
+
used should be taken from those defined by
|
|
348
|
+
[UN/CEFACT](https://www.unece.org/fileadmin/DAM/cefact/recommendations/rec20/rec20_rev3_Annex3e.pdf). E.g.:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"object_id": "l",
|
|
353
|
+
"name": "length",
|
|
354
|
+
"type": "Integer",
|
|
355
|
+
"metadata": {
|
|
356
|
+
"unitCode": { "type": "Text", "value": "FOT" }
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Measurement persistence options
|
|
362
|
+
|
|
363
|
+
There are 3 different options to configure how the IoTAgent stores the measures received from the devices, depending on
|
|
364
|
+
the following parameters:
|
|
365
|
+
|
|
366
|
+
- `autoprovision`: If the device is not provisioned, the IoTAgent will create a new device and entity for it.
|
|
367
|
+
- `explicitAttrs`: If the measure element (object_id) is not defined in the mappings of the device or config group
|
|
368
|
+
provision, the measure is stored in the Context Broker by adding a new attribute to the entity with the same name of
|
|
369
|
+
the undefined measure element.
|
|
370
|
+
- `appendMode`: It configures the request to the Context Broker to update the entity every time a new measure arrives.
|
|
371
|
+
It have implications depending if the entity is already created or not in the Context Broker.
|
|
372
|
+
|
|
373
|
+
### Autoprovision configuration (autoprovision)
|
|
374
|
+
|
|
375
|
+
By default, when a measure arrives to the IoTAgent, if the `device_id` does not match with an existing one, then, the
|
|
376
|
+
IoTA creates a new device and a new entity according to the config group. Defining the field `autoprovision` to `false`
|
|
377
|
+
when provisioning the config group, the IoTA to reject the measure at the southbound, allowing only to persist the data
|
|
378
|
+
to devices that are already provisioned. It makes no sense to use this field in device provisioning since it is intended
|
|
379
|
+
to avoid provisioning devices (and for it to be effective, it would have to be provisional).
|
|
380
|
+
|
|
381
|
+
### Explicitly defined attributes (explicitAttrs)
|
|
382
|
+
|
|
383
|
+
If a given measure element (object_id) is not defined in the mappings of the device or config group, the measure is
|
|
384
|
+
stored in the Context Broker by adding a new attribute to the entity with the same name of the undefined measure
|
|
385
|
+
element. By adding the field `explicitAttrs` with `true` value to device or config group, the IoTAgent rejects the
|
|
386
|
+
measure elements that are not defined in the mappings of device or config group, persisting only the one defined in the
|
|
387
|
+
mappings of the provision. If `explicitAttrs` is provided both at device and config group level, the device level takes
|
|
388
|
+
precedence. Additionally `explicitAttrs` can be used to define which measures (identified by their attribute names, not
|
|
389
|
+
by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
|
|
390
|
+
|
|
391
|
+
The different possibilities are summarized below:
|
|
392
|
+
|
|
393
|
+
Case 1 (default):
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
"explicitAttrs": false
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
every measure will be propagated to NGSI interface.
|
|
400
|
+
|
|
401
|
+
Case 2:
|
|
402
|
+
|
|
403
|
+
```
|
|
404
|
+
"explicitAttrs": true
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
just measures defined in active, static (plus conditionally TimeInstant) will be propagated to NGSI interface.
|
|
408
|
+
|
|
409
|
+
Case 3:
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
"explicitAttrs": "['attr1','atrr2']"
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
just NGSI attributes defined in the array (identified by their attribute names, not by their object_id, plus
|
|
416
|
+
conditionally TimeInstant) will be propagated to NGSI interface (note that in this case the value of `explicitAttrs` is
|
|
417
|
+
not a JSON but a JEXL Array that looks likes a JSON).
|
|
418
|
+
|
|
419
|
+
Case 4:
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
"explicitAttrs": "['attr1','atrr2',{object_id:'active_id'}]"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
just NGSI attributes defined in the array (identified by their attribute names and/or by their object_id) will be
|
|
426
|
+
propagated to NGSI interface (note that in this case the value of `explicitAttrs` is not a JSON but a JEXL Array/Object
|
|
427
|
+
that looks likes a JSON). This is necessary when same attribute names are used within multiple entities.
|
|
428
|
+
|
|
429
|
+
Case 5:
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
"explicitAtttr": "<JEXL expression resulting in bool or array>"
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
depending on the JEXL expression evaluation:
|
|
436
|
+
|
|
437
|
+
- If it evaluates to `true` every measure will be propagated to NGSI interface (as in case 1)
|
|
438
|
+
- If it evaluates to `false` just measures defined in active, static (plus conditionally TimeInstant) will be
|
|
439
|
+
propagated to NGSI interface (as in case 2)
|
|
440
|
+
- If it evaluates to an array just measures defined in the array (identified by their attribute names, not by their
|
|
441
|
+
object_id) will be will be propagated to NGSI interface (as in case 3)
|
|
442
|
+
|
|
443
|
+
### Configuring operation to persist the data in Context Broker (appendMode)
|
|
444
|
+
|
|
445
|
+
This is a flag that can be enabled by activating the parameter `appendMode` in the configuration file or by using the
|
|
446
|
+
`IOTA_APPEND_MODE` environment variable (more info
|
|
447
|
+
[here](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/installationguide.md)). If this flag is
|
|
448
|
+
activated, the update requests to the Context Broker will be performed always with APPEND type, instead of the default
|
|
449
|
+
UPDATE. This have implications in the use of attributes with Context Providers, so this flag should be used with care.
|
|
450
|
+
|
|
451
|
+
### Differences between `autoprovision`, `explicitAttrs` and `appendMode`
|
|
452
|
+
|
|
453
|
+
Since those configuration parameters are quite similar, this section is intended to clarify the relation between them.
|
|
454
|
+
|
|
455
|
+
If `autoprovision` is set to `true` (default case), the agent will perform an initial request creating a new entity into
|
|
456
|
+
the Context Broker with **only** the static and active attributes provisioned in the config group, and also a new Device
|
|
457
|
+
in the agent, every time a measure arrives with a new `device_id`. Otherwise, this measure is ignored. This is something
|
|
458
|
+
related to the **southbound**.
|
|
459
|
+
|
|
460
|
+
What `explicitAttrs` does is to filter from the southbound the parameters that are not explicitly defined in the device
|
|
461
|
+
provision or config group. That also would avoid propagating the measures to the Context Broker.
|
|
462
|
+
|
|
463
|
+
The default way the agent updates the information into the Context Broker is by using an update request. If
|
|
464
|
+
`appendMode=true`, the IoTA will use an append request instead of an update one. This means it will store the attributes
|
|
465
|
+
even if they are not present in the entity. This seems the same functionality that the one provided by `autoprovision`,
|
|
466
|
+
but it is a different concept since the scope of this config is to setup how the IoT interacts with the context broker,
|
|
467
|
+
this is something related to the **northbound**.
|
|
468
|
+
|
|
469
|
+
Note that, even creating a config group with `autoprovision=true` and `explicitAttrs=true`, if you do not provision
|
|
470
|
+
previously the entity in the Context Broker (having all attributes to be updated), it would fail if `appendMode=false`.
|
|
471
|
+
For further information check the issue [#1301](https://github.com/telefonicaid/iotagent-node-lib/issues/1301).
|
|
472
|
+
|
|
473
|
+
## Expression language support
|
|
474
|
+
|
|
475
|
+
The IoTAgent Library provides an expression language for measurement transformation and other purposes. This expression
|
|
476
|
+
language is based on the [TomFrost/JEXL](https://github.com/TomFrost/Jexl) library. The common usage of this feature is
|
|
477
|
+
to adapt the information coming from the South Bound APIs to the information reported to the Context Broker. This is
|
|
478
|
+
really useful when you need to adapt measure (for example, to change the units, or to apply a formula to). All the usage
|
|
479
|
+
of expression in the IoT Agent are:
|
|
480
|
+
|
|
481
|
+
- [Measurement transformation](#measurement-transformation).
|
|
482
|
+
- Commands payload transformation (push and pull).
|
|
483
|
+
- Auto provisioned devices entity name. It is configured at config Group level by setting the `entityNameExp`
|
|
484
|
+
parameter. It defines an expression to generate the Entity Name for autoprovisioned devices.
|
|
485
|
+
- Dynamic `endpoint` definition. Configured at device level, it defines where the device listen for push http
|
|
486
|
+
commands. It can be either a static value or an expression.
|
|
487
|
+
|
|
488
|
+
The agent provides additional information, hereafter referred as **context**, in order to be used to evaluate the
|
|
489
|
+
expression. In all cases the following data is available to all expressions:
|
|
490
|
+
|
|
491
|
+
- `id`: device ID
|
|
492
|
+
- `entity_name`: NGSI entity Name (principal)
|
|
493
|
+
- `type`: NGSI entity type (principal)
|
|
494
|
+
- `service`: device service
|
|
495
|
+
- `subservice`: device subservice
|
|
496
|
+
- `staticAttributes`: static attributes defined in the device or config group
|
|
497
|
+
|
|
498
|
+
### Examples of JEXL expressions
|
|
499
|
+
|
|
500
|
+
The following table shows expressions and their expected outcomes taking into account the following measures at
|
|
501
|
+
southbound interface:
|
|
502
|
+
|
|
503
|
+
- `value` with value 6 (number)
|
|
504
|
+
- `ts` with value 1637245214901 (unix timestamp)
|
|
505
|
+
- `name` with value `"DevId629"` (string)
|
|
506
|
+
- `object` with value `{name: "John", surname: "Doe"}` (JSON object)
|
|
507
|
+
- `array` with value `[1, 3]` (JSON Array)
|
|
508
|
+
|
|
509
|
+
| Expression | Expected outcome | Format | Playground |
|
|
510
|
+
| :-------------------------------------------- | :---------------------------------------- | ------------------- | ------------- |
|
|
511
|
+
| `5 * value` | `30` | Integer | [Example][6] |
|
|
512
|
+
| `(6 + value) * 3` | `36` | Integer | [Example][7] |
|
|
513
|
+
| `value / 12 + 1` | `1.5` | Float | [Example][8] |
|
|
514
|
+
| `(5 + 2) * (value + 7)` | `91` | Integer | [Example][9] |
|
|
515
|
+
| `value * 5.2` | `31.2` | Float | [Example][10] |
|
|
516
|
+
| `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` | String | [Example][11] |
|
|
517
|
+
| `name + "value is " +value` | `"DevId629 value is 6"` | String | [Example][12] |
|
|
518
|
+
| `{coordinates: [value,value], type: 'Point'}` | `{"coordinates": [6,6], "type": "Point"}` | GeoJSON `Object` | [Example][13] |
|
|
519
|
+
| <code>ts|toisodate</code> | `2021-11-18T14:20:14.901Z` | ISO 8601 `DateTime` | [Example][14] |
|
|
520
|
+
|
|
521
|
+
Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
|
|
522
|
+
|
|
523
|
+
| Expression | Expected outcome | Playground |
|
|
524
|
+
| :-------------------------------------------------------- | :--------------- | ------------- |
|
|
525
|
+
| <code>" a "| trim</code> | `a` | [Example][15] |
|
|
526
|
+
| <code>name|length</code> | `8` | [Example][16] |
|
|
527
|
+
| <code>name|indexOf("e")</code> | `1` | [Example][17] |
|
|
528
|
+
| <code>name|substr(0,name|indexOf("e")+1)</code> | `"De"` | [Example][18] |
|
|
529
|
+
|
|
530
|
+
### Available functions
|
|
531
|
+
|
|
532
|
+
There are several predefined JEXL transformations available to be used at any JEXL expression. The definition of those
|
|
533
|
+
transformations and their JavaScript implementation can be found at jexlTransformsMap.js.
|
|
534
|
+
|
|
535
|
+
The library module also exports a method `iotAgentLib.dataPlugins.expressionTransformation.setJEXLTransforms(Map)` to be
|
|
536
|
+
used by specific IoT Agent implementations in order to incorporate extra transformations to this set. It is important to
|
|
537
|
+
remark that the lib `jexlTransformsMap` cannot be overwritten by the API additions. The idea behind this is to be able
|
|
538
|
+
to incorporate new transformations from the IoT Agent configuration file in a fast and tactical way.
|
|
539
|
+
|
|
540
|
+
Current common transformation set:
|
|
541
|
+
|
|
542
|
+
| JEXL Transformation | Equivalent JavaScript Function |
|
|
543
|
+
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
|
544
|
+
| jsonparse: (str) | `JSON.parse(str);` |
|
|
545
|
+
| jsonstringify: (obj) | `JSON.stringify(obj);` |
|
|
546
|
+
| indexOf: (val, char) | `String(val).indexOf(char);` |
|
|
547
|
+
| length: (val) | `String(val).length;` |
|
|
548
|
+
| trim: (val) | `String(val).trim();` |
|
|
549
|
+
| substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
|
|
550
|
+
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code> |
|
|
551
|
+
| lengtharray: (arr) | `arr.length;` |
|
|
552
|
+
| typeof: (val) | `typeof val;` |
|
|
553
|
+
| isarray: (arr) | `Array.isArray(arr);` |
|
|
554
|
+
| isnan: (val) | `isNaN(val);` |
|
|
555
|
+
| parseint: (val) | `parseInt(val);` |
|
|
556
|
+
| parsefloat: (val) | `parseFloat(val);` |
|
|
557
|
+
| toisodate: (val) | `new Date(val).toISOString();` |
|
|
558
|
+
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
|
|
559
|
+
| tostring: (val) | `val.toString();` |
|
|
560
|
+
| urlencode: (val) | `encodeURI(val);` |
|
|
561
|
+
| urldecode: (val) | `decodeURI(val);` |
|
|
562
|
+
| replacestr: (str, from, to) | `str.replace(from, to);` |
|
|
563
|
+
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
|
|
564
|
+
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
|
|
565
|
+
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
|
|
566
|
+
| split: (str, ch) | `str.split(ch);` |
|
|
567
|
+
| joinarrtostr: (arr, ch) | `arr.join(ch);` |
|
|
568
|
+
| concatarr: (arr, arr2) | `arr.concat(arr2);` |
|
|
569
|
+
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code> |
|
|
570
|
+
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
|
|
571
|
+
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
|
|
572
|
+
| slice: (arr, init, end) | `arr.slice(init,end);` |
|
|
573
|
+
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
|
|
574
|
+
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
|
|
575
|
+
| touppercase: (val) | `String(val).toUpperCase()` |
|
|
576
|
+
| tolowercase: (val) | `String(val).toLowerCase()` |
|
|
577
|
+
| round: (val) | `Math.round(val)` |
|
|
578
|
+
| floor: (val) | `Math.floor(val)` |
|
|
579
|
+
| ceil: (val) | `Math.ceil(val)` |
|
|
580
|
+
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)` |
|
|
581
|
+
| gettime: (d) | `new Date(d).getTime()` |
|
|
582
|
+
| toisostring: (d) | `new Date(d).toISOString()` |
|
|
583
|
+
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)` |
|
|
584
|
+
| now: () | `Date.now()` |
|
|
585
|
+
| hextostring: (val) | `new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))` |
|
|
586
|
+
|
|
587
|
+
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
588
|
+
test all the functions described above.
|
|
589
|
+
|
|
590
|
+
### Expressions with multiple transformations
|
|
591
|
+
|
|
592
|
+
When you need to apply multiples transformations to the same value, you can use the pipe character `|` to separate the
|
|
593
|
+
transformations. For example, if you want to apply the `trim` and `replacestr` transformations to a value, you can use
|
|
594
|
+
the following expression:
|
|
595
|
+
|
|
596
|
+
```json
|
|
597
|
+
{
|
|
598
|
+
"expression": "variable | trim | replacestr('hello','hi')"
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
If variable takes value `hello world`, the result of the previous expression will be `hi world`.
|
|
603
|
+
|
|
604
|
+
Another example using functions that return more than one value is the following:
|
|
605
|
+
|
|
606
|
+
```json
|
|
607
|
+
{
|
|
608
|
+
"expression": "location | split(', ')[1] | parsefloat()"
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
For a location value `"40.4165, -3.70256"`, the result of the previous expression will be `-3.70256`.
|
|
613
|
+
|
|
614
|
+
## Measurement transformation
|
|
615
|
+
|
|
616
|
+
The IoTAgent Library provides support for measurement transformation using a
|
|
617
|
+
[Expression Language](#expression-language-support) This feature can be used to adapt the information coming from the
|
|
618
|
+
South Bound APIs to the information reported to the Context Broker. This is really useful when you need to adapt measure
|
|
619
|
+
(for example, to change the units, or to apply a formula to).
|
|
620
|
+
|
|
621
|
+
### Measurement transformation definition
|
|
622
|
+
|
|
623
|
+
Measurement transformation can be defined for Active attributes, either in the Device provisioning or in the config
|
|
624
|
+
group provisioning. The following example shows a device provisioning payload with defined Measurement transformation:
|
|
625
|
+
|
|
626
|
+
```json
|
|
627
|
+
{
|
|
628
|
+
"devices": [
|
|
629
|
+
{
|
|
630
|
+
"device_id": "45",
|
|
631
|
+
"protocol": "GENERIC_PROTO",
|
|
632
|
+
"entity_name": "WasteContainer:WC45",
|
|
633
|
+
"entity_type": "WasteContainer",
|
|
634
|
+
"attributes": [
|
|
635
|
+
{
|
|
636
|
+
"name": "location",
|
|
637
|
+
"type": "geo:json",
|
|
638
|
+
"expression": "{coordinates: [longitude,latitude], type: 'Point'}"
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
"name": "fillingLevel",
|
|
642
|
+
"type": "Number",
|
|
643
|
+
"expression": "level / 100"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"name": "level",
|
|
647
|
+
"type": "Number"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"name": "latitude",
|
|
651
|
+
"type": "Number"
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
"name": "longitude",
|
|
655
|
+
"type": "Number"
|
|
656
|
+
}
|
|
657
|
+
]
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
[Interactive expression `{coordinates: [longitude,latitude], type: 'Point'}`][1]
|
|
664
|
+
|
|
665
|
+
[Interactive expression `level / 100`][2]
|
|
666
|
+
|
|
667
|
+
The value of the `expression` field defines the measure transformation. is a string that can contain any number of
|
|
668
|
+
expression patterns. In order to complete expression to be evaluated, all the expression patterns must be evaluable
|
|
669
|
+
(there must be a value in the measurement for all the variables of all the expression patterns).
|
|
670
|
+
|
|
671
|
+
Note that you need to include in the provision operation all the attributes required as inputs for the expressions. In
|
|
672
|
+
this example, they are `level`, `latitude` and `longitude`. Otherwise the device sending the measures will get
|
|
673
|
+
`{"name":"ATTRIBUTE_NOT_FOUND","message":"Some of the attributes does not exist"}` when it sends some of these and the
|
|
674
|
+
expression will not be calculated.
|
|
675
|
+
|
|
676
|
+
The exact same syntax works for Configuration and Device provisioning.
|
|
677
|
+
|
|
678
|
+
### Measurement transformation execution
|
|
679
|
+
|
|
680
|
+
Whenever a new measurement arrives to the IoT Agent for a device with declared expressions, all of the expressions for
|
|
681
|
+
the device will be checked for execution: for all the defined active attributes containing expressions, the IoT Agent
|
|
682
|
+
will check which ones contain expressions whose variables are present in the received measurement. For all of those
|
|
683
|
+
whose variables are covered, their expressions will be executed with the received values, and their values updated in
|
|
684
|
+
the Context Broker.
|
|
685
|
+
|
|
686
|
+
E.g.: if a device with the following provisioning information is created in the IoT Agent:
|
|
687
|
+
|
|
688
|
+
```json
|
|
689
|
+
{
|
|
690
|
+
"name":"location",
|
|
691
|
+
"type":"geo:point",
|
|
692
|
+
"expression": "longitude+', '+latitude"
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
"name":"fillingLevel",
|
|
696
|
+
"type":"Number",
|
|
697
|
+
"expression": "level / 100",
|
|
698
|
+
},
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
[Interactive expression `longitude+', '+latitude`][3]
|
|
702
|
+
|
|
703
|
+
[Interactive expression `level / 100`][4]
|
|
704
|
+
|
|
705
|
+
and a measurement with the following values arrive to the IoT Agent:
|
|
706
|
+
|
|
707
|
+
```text
|
|
708
|
+
latitude: 1.9
|
|
709
|
+
level: 85.3
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
The only expression rule that will be executed will be that of the `fillingLevel` attribute. It will produce the value
|
|
713
|
+
`0.853` that will be sent to the Context Broker.
|
|
714
|
+
|
|
715
|
+
Note that expressions are only applied if the attribute name (as received by the IoT Agent in the southbound interface)
|
|
716
|
+
matches the expression variable. Otherwise, the southbound value is used directly. Let's illustrate with the following
|
|
717
|
+
example:
|
|
718
|
+
|
|
719
|
+
```json
|
|
720
|
+
"consumption": {
|
|
721
|
+
"type": "String",
|
|
722
|
+
"value": "spaces | trim"
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
- Case 1: the following measure is received at the southbound interface:
|
|
727
|
+
|
|
728
|
+
```text
|
|
729
|
+
consumption: "0.44"
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
As `spaces` attribute is not included, then the expression is not applied and the `consumption` measure value is
|
|
733
|
+
directly used, so the following is sent to CB:
|
|
734
|
+
|
|
735
|
+
```json
|
|
736
|
+
"consumption": {
|
|
737
|
+
"type": "String",
|
|
738
|
+
"value": "0.44"
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
- Case 2: the following measure is received at the southbound interface:
|
|
743
|
+
|
|
744
|
+
```text
|
|
745
|
+
consumption: "0.44"
|
|
746
|
+
spaces: " foobar "
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
As `spaces` attribute is included, then the expression is evaluated, so overriding the 0.44 value and sending the
|
|
750
|
+
following to CB:
|
|
751
|
+
|
|
752
|
+
```json
|
|
753
|
+
"consumption": {
|
|
754
|
+
"type": "String",
|
|
755
|
+
"value": "foobar"
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
[Interactive expression `spaces | trim`][5]
|
|
760
|
+
|
|
761
|
+
### Multientity measurement transformation support (`object_id`)
|
|
762
|
+
|
|
763
|
+
To allow support for measurement transformation in combination with multi entity plugin, where the same attribute is
|
|
764
|
+
generated for different entities out of different incoming attribute values (i.e. `object_id`), we introduced support
|
|
765
|
+
for `object_id` in the expression context.
|
|
766
|
+
|
|
767
|
+
For example, the following device:
|
|
768
|
+
|
|
769
|
+
```json
|
|
770
|
+
"WeatherStation": {
|
|
771
|
+
"commands": [],
|
|
772
|
+
"type": "WeatherStation",
|
|
773
|
+
"lazy": [],
|
|
774
|
+
"active": [
|
|
775
|
+
{
|
|
776
|
+
"object_id": "v1",
|
|
777
|
+
"name": "vol",
|
|
778
|
+
"expression" : "v1*100",
|
|
779
|
+
"type": "Number",
|
|
780
|
+
"entity_name": "WeatherStation1"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
"object_id": "v2",
|
|
784
|
+
"name": "vol",
|
|
785
|
+
"expression" : "v2*100",
|
|
786
|
+
"type": "Number",
|
|
787
|
+
"entity_name": "WeatherStation2"
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
"object_id": "v",
|
|
791
|
+
"name": "vol",
|
|
792
|
+
"expression" : "v*100",
|
|
793
|
+
"type": "Number"
|
|
794
|
+
}
|
|
795
|
+
]
|
|
796
|
+
}
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
When receiving the measures `v`, `v1` and `v2` in a payload from a message received in the southbound (using as an
|
|
800
|
+
example a payload for the [IoT Agent JSON](https://github.com/telefonicaid/iotagent-json)):
|
|
801
|
+
|
|
802
|
+
```json
|
|
803
|
+
{
|
|
804
|
+
"v": 0
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
"v1": 1
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
"v2": 2
|
|
811
|
+
}
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
Will now generate the following NGSI v2 payload:
|
|
815
|
+
|
|
816
|
+
```json
|
|
817
|
+
{
|
|
818
|
+
"actionType": "append",
|
|
819
|
+
"entities": [
|
|
820
|
+
{
|
|
821
|
+
"id": "ws9",
|
|
822
|
+
"type": "WeatherStation",
|
|
823
|
+
"vol": {
|
|
824
|
+
"type": "Number",
|
|
825
|
+
"value": 0
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"vol": {
|
|
830
|
+
"type": "Number",
|
|
831
|
+
"value": 100
|
|
832
|
+
},
|
|
833
|
+
"type": "WeatherStation",
|
|
834
|
+
"id": "WeatherStation1"
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
"vol": {
|
|
838
|
+
"type": "Number",
|
|
839
|
+
"value": 200
|
|
840
|
+
},
|
|
841
|
+
"type": "WeatherStation",
|
|
842
|
+
"id": "WeatherStation2"
|
|
843
|
+
}
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
## Timestamp Compression
|
|
849
|
+
|
|
850
|
+
This functionality changes all the timestamp attributes found in the entity, and all the timestamp metadata found in any
|
|
851
|
+
attribute, from the basic complete calendar timestamp of the ISO8601 (e.g.: 20071103T131805) to the extended complete
|
|
852
|
+
calendar timestamp (e.g.: +002007-11-03T13:18). The middleware expects to receive the basic format in updates and return
|
|
853
|
+
it in queries (and viceversa, receive the extended one in queries and return it in updates).
|
|
854
|
+
|
|
855
|
+
## Timestamp Processing
|
|
856
|
+
|
|
857
|
+
The IOTA processes the entity attributes looking for a `TimeInstant` attribute. If one is found, for NGSI v2, the plugin
|
|
858
|
+
adds a `TimeInstant` attribute as metadata for every other attribute in the same request. With NGSI-LD, the Standard
|
|
859
|
+
`observedAt` property-of-a-property is used instead.
|
|
860
|
+
|
|
861
|
+
## Bidirectionality plugin (bidirectional)
|
|
862
|
+
|
|
863
|
+
This plugin allows the devices with composite values an expression to update the original values in the devices when the
|
|
864
|
+
composite expressions are updated in the Context Broker. This behavior is achieved through the use of subscriptions.
|
|
865
|
+
|
|
866
|
+
IoTAs using this plugins should also define a notification handler to handle incoming values. This handler will be
|
|
867
|
+
intercepted by the plugin, so the mapped values are included in the updated notification.
|
|
868
|
+
|
|
869
|
+
When a device is provisioned with bidirectional attributes, the IoTAgent subscribes to changes in that attribute. When a
|
|
870
|
+
change notification for that attribute arrives to the IoTA, it applies the transformation defined in the device
|
|
871
|
+
provisioning payload to the notification, and calls the underlying notification handler with the transformed entity
|
|
872
|
+
including the `value` along with any `metadata`, and in the case of an NGSI-LD bidirectional attribute a `datasetId` if
|
|
873
|
+
provided.
|
|
874
|
+
|
|
875
|
+
The following `attributes` section shows an example of the plugin configuration (using `IOTA_AUTOCAST=false` to avoid
|
|
876
|
+
translation from geo:point to geo:json)
|
|
877
|
+
|
|
878
|
+
```json
|
|
879
|
+
"attributes": [
|
|
880
|
+
{
|
|
881
|
+
"name":"location",
|
|
882
|
+
"type":"geo:point",
|
|
883
|
+
"expression": "latitude, longitude",
|
|
884
|
+
"reverse": [
|
|
885
|
+
{
|
|
886
|
+
"object_id":"longitude",
|
|
887
|
+
"type": "Number",
|
|
888
|
+
"expression": "location | split(', ')[0] | parsefloat()"
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
"object_id":"latitude",
|
|
892
|
+
"type": "Number",
|
|
893
|
+
"expression": "location | split(', ')[1] | parsefloat()"
|
|
894
|
+
}
|
|
895
|
+
]
|
|
896
|
+
}
|
|
897
|
+
],
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
For each attribute that would have bidirectionality, a new field `reverse` must be configured. This field will contain
|
|
901
|
+
an array of fields that will be created based on the notifications content. The expression notification can contain any
|
|
902
|
+
attribute of the same entity as the bidirectional attribute; declaring them in the expressions will add them to the
|
|
903
|
+
subscription payload.
|
|
904
|
+
|
|
905
|
+
For each attribute in the `reverse` array, an expression must be defined to calculate its value based on the
|
|
906
|
+
notification attributes. This value will be passed to the underlying protocol with the `object_id` name. Details about
|
|
907
|
+
how the value is then progressed to the device are protocol-specific.
|
|
908
|
+
|
|
909
|
+
## Overriding global Context Broker host
|
|
910
|
+
|
|
911
|
+
**cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific types
|
|
912
|
+
of devices.
|
|
913
|
+
|
|
914
|
+
## Multitenancy, FIWARE Service and FIWARE ServicePath
|
|
915
|
+
|
|
916
|
+
Every operation in the API require the `fiware-service` and `fiware-servicepath` to be defined; the operations are
|
|
917
|
+
performed in the scope of those headers. For the list case, the special wildcard servicepath can be specified, `/*`. In
|
|
918
|
+
this case, the operation applies to all the subservices of the service given by the `fiware-service` header.
|
|
919
|
+
|
|
920
|
+
## Secured access to the Context Broker
|
|
921
|
+
|
|
922
|
+
For access to instances of the Context Broker secured with a
|
|
923
|
+
[PEP Proxy](https://github.com/telefonicaid/fiware-orion-pep), an authentication mechanism based in Keystone Trust
|
|
924
|
+
tokens is provided. A trust token is a way of Keystone to allow an user delegates a role to another user for a
|
|
925
|
+
subservice. It is a long-term token that can be issued by any user to give another user permissions to impersonate him
|
|
926
|
+
with a given role in a given project (subservice). Such impersonation itself is in turn based on a short-term access
|
|
927
|
+
token.
|
|
928
|
+
|
|
929
|
+
For the authentication mechanisms to work, the `authentication` attribute in the configuration has to be fully
|
|
930
|
+
configured, and the `authentication.enabled` subattribute should have the value `true`.
|
|
931
|
+
|
|
932
|
+
When the administrator of a service is configuring a set of devices or device types in the IoT Agent to use a secured
|
|
933
|
+
Context Broker, he should follow this steps:
|
|
934
|
+
|
|
935
|
+
- First, a Trust Token ID should be requested to Keystone, using the service administrator credentials, the role ID
|
|
936
|
+
and the IoT Agent User ID. The Trust token can be retrieved using the following request (shown as a curl command):
|
|
937
|
+
|
|
938
|
+
```bash
|
|
939
|
+
curl http://${KEYSTONE_HOST}/v3/OS-TRUST/trusts \
|
|
940
|
+
-s \
|
|
941
|
+
-H "X-Auth-Token: $ADMIN_TOKEN" \
|
|
942
|
+
-H "Content-Type: application/json" \
|
|
943
|
+
-d '
|
|
944
|
+
{
|
|
945
|
+
"trust": {
|
|
946
|
+
"impersonation": false,
|
|
947
|
+
"project_id": "'$SUBSERVICE_ID'",
|
|
948
|
+
"roles": [
|
|
949
|
+
{"id": "'$ID_ROLE'"
|
|
950
|
+
}
|
|
951
|
+
],
|
|
952
|
+
"trustee_user_id": "'$ID_IOTAGENT_USER'",
|
|
953
|
+
"trustor_user_id": "'$ID_ADM1'"
|
|
954
|
+
}
|
|
955
|
+
}'
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
- Every device or type of devices configured to use a secured Context Broker must be provided with a Trust Token ID in
|
|
959
|
+
its configuration.
|
|
960
|
+
- Before any request is sent to a secured Context Broker, the IoT Agent uses the Trust Token ID to generate a
|
|
961
|
+
temporary access token, that is attached to the request (in the `X-Auth-token` header) (using Keystone API
|
|
962
|
+
https://developer.openstack.org/api-ref/identity/v3-ext/#consuming-a-trust).
|
|
963
|
+
|
|
964
|
+
Apart from the generation of the trust, the use of secured Context Brokers should be transparent to the user of the IoT
|
|
965
|
+
Agent.
|
|
966
|
+
|
|
967
|
+
Complete info on Keystone trust tokens could be found at:
|
|
968
|
+
|
|
969
|
+
- [Trusts concept](https://docs.openstack.org/keystone/stein/user/trusts)
|
|
970
|
+
- [Trusts API](https://docs.openstack.org/keystone/stein/api_curl_examples.html#post-v3-os-trust-trusts)
|
|
971
|
+
|
|
972
|
+
## NGSI-LD Support
|
|
973
|
+
|
|
974
|
+
### NGSI-LD `GeoProperty` support
|
|
975
|
+
|
|
976
|
+
For NGSI-LD only, the defined `type` of any GeoJSON attribute can be any set using any of the standard NGSI-v2 GeoJSON
|
|
977
|
+
types - (e.g. `geo:json`, `geo:point`). NGSI-LD formats such as `GeoProperty`, `Point` and `LineString` are also
|
|
978
|
+
accepted `type` values. If the latitude and longitude are received as separate measures,
|
|
979
|
+
[measurement transformation](#measurement-transformation) can be used to concatenate them into GeoJSON objects an array
|
|
980
|
+
of tuples or a string as shown
|
|
981
|
+
|
|
982
|
+
#### Encode as GeoJSON
|
|
983
|
+
|
|
984
|
+
```json
|
|
985
|
+
{
|
|
986
|
+
"entity_type": "GPS",
|
|
987
|
+
"resource": "/iot/d",
|
|
988
|
+
"protocol": "PDI-IoTA-JSON",
|
|
989
|
+
..etc
|
|
990
|
+
"attributes": [
|
|
991
|
+
{
|
|
992
|
+
"name": "location",
|
|
993
|
+
"type": "geo:json",
|
|
994
|
+
"expression": "{coordinates: [longitude,latitude], type: 'Point'}"
|
|
995
|
+
}
|
|
996
|
+
]
|
|
997
|
+
}
|
|
998
|
+
```
|
|
5
999
|
|
|
6
|
-
|
|
1000
|
+
JEXL can be used to create GeoJSON objects directly. For `attributes` and `static_attributes` which need to be formatted
|
|
1001
|
+
as GeoJSON values, three separate input formats are currently accepted. Provided the `type` is provisioned correctly,
|
|
1002
|
+
the `value` may be defined using any of the following formats:
|
|
7
1003
|
|
|
8
|
-
|
|
1004
|
+
- a comma delimited string
|
|
9
1005
|
|
|
10
1006
|
```json
|
|
11
1007
|
{
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
1008
|
+
"name": "location",
|
|
1009
|
+
"value": "23, 12.5"
|
|
1010
|
+
}
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
- an array of numbers
|
|
1014
|
+
|
|
1015
|
+
```json
|
|
1016
|
+
{
|
|
1017
|
+
"name": "location",
|
|
1018
|
+
"value": [23, 12.5]
|
|
1019
|
+
}
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
- an fully formatted GeoJSON object
|
|
1023
|
+
|
|
1024
|
+
```json
|
|
1025
|
+
{
|
|
1026
|
+
"name": "location",
|
|
1027
|
+
"value": {
|
|
1028
|
+
"type": "Point",
|
|
1029
|
+
"coordinates": [23, 12.5]
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
### NGSI-LD Linked Data support
|
|
1035
|
+
|
|
1036
|
+
`static_attributes` may be supplied with an additional `link` data element when provisioning an IoT Agent to ensure that
|
|
1037
|
+
active attributes from the provisioned IoT Device may be maintained in parallel with a linked data entity . Take for
|
|
1038
|
+
example a temperature gauge placed within a building. The **Device** data model literally represents the IoT device
|
|
1039
|
+
itself, but the `temperature` attribute also needs to be shared with the **Building** entity
|
|
1040
|
+
|
|
1041
|
+
A `link` between them can be provisioned as shown:
|
|
1042
|
+
|
|
1043
|
+
e.g.:
|
|
1044
|
+
|
|
1045
|
+
```json
|
|
1046
|
+
{
|
|
1047
|
+
"entity_type": "Device",
|
|
1048
|
+
"resource": "/iot/d",
|
|
1049
|
+
"protocol": "PDI-IoTA-UltraLight",
|
|
1050
|
+
..etc
|
|
1051
|
+
"attributes": [
|
|
1052
|
+
{"object_id": "l", "name": "temperature", "type":"Float",
|
|
1053
|
+
"metadata":{
|
|
1054
|
+
"unitCode":{"type": "Text", "value" :"CEL"}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
],
|
|
1058
|
+
"static_attributes": [
|
|
1059
|
+
{
|
|
1060
|
+
"name": "controlledAsset",
|
|
1061
|
+
"type": "Relationship",
|
|
1062
|
+
"value": "urn:ngsi-ld:Building:001",
|
|
1063
|
+
"link": {
|
|
1064
|
+
"attributes": ["temperature"],
|
|
1065
|
+
"name": "providedBy",
|
|
1066
|
+
"type": "Building"
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
]
|
|
1070
|
+
}
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
Whenever a `temperature` measure is received **Device** is updated, and entity `urn:ngsi-ld:Building:001` is also
|
|
1074
|
+
updated as shown:
|
|
1075
|
+
|
|
1076
|
+
```json
|
|
1077
|
+
"temperature": {
|
|
1078
|
+
"type": "Property",
|
|
1079
|
+
"value": 27.6,
|
|
1080
|
+
"unitCode": "CEL",
|
|
1081
|
+
"providedBy": {
|
|
1082
|
+
"type": "Relationship",
|
|
1083
|
+
"object": "urn:ngsi-ld:Device:thermometer1"
|
|
1084
|
+
}
|
|
16
1085
|
}
|
|
17
1086
|
```
|
|
18
1087
|
|
|
19
|
-
|
|
20
|
-
|
|
1088
|
+
#### NGSI-LD `datasetId` support
|
|
1089
|
+
|
|
1090
|
+
Limited support for parsing the NGSI-LD `datasetId` attribute is included within the library. A series of sequential
|
|
1091
|
+
commands for a single attribute can be sent as an NGSI-LD notification as follows:
|
|
1092
|
+
|
|
1093
|
+
```json
|
|
1094
|
+
{
|
|
1095
|
+
"id": "urn:ngsi-ld:Notification:5fd0fa684eb81930c97005f3",
|
|
1096
|
+
"type": "Notification",
|
|
1097
|
+
"subscriptionId": "urn:ngsi-ld:Subscription:5fd0f69b4eb81930c97005db",
|
|
1098
|
+
"notifiedAt": "2020-12-09T16:25:12.193Z",
|
|
1099
|
+
"data": [
|
|
1100
|
+
{
|
|
1101
|
+
"lampColor": [
|
|
1102
|
+
{
|
|
1103
|
+
"type": "Property",
|
|
1104
|
+
"value": { "color": "green", "duration": "55 secs" },
|
|
1105
|
+
"datasetId": "urn:ngsi-ld:Sequence:do-this"
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
"type": "Property",
|
|
1109
|
+
"value": { "color": "red", "duration": "10 secs" },
|
|
1110
|
+
"datasetId": "urn:ngsi-ld:Sequence:then-do-this"
|
|
1111
|
+
}
|
|
1112
|
+
]
|
|
1113
|
+
}
|
|
1114
|
+
]
|
|
1115
|
+
}
|
|
1116
|
+
```
|
|
21
1117
|
|
|
22
|
-
|
|
1118
|
+
This results in the following sequential array of attribute updates to be sent to the `NotificationHandler` of the IoT
|
|
1119
|
+
Agent itself:
|
|
23
1120
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1121
|
+
```json
|
|
1122
|
+
[
|
|
1123
|
+
{
|
|
1124
|
+
"name": "lampColor",
|
|
1125
|
+
"type": "Property",
|
|
1126
|
+
"datasetId": "urn:ngsi-ld:Sequence:do-this",
|
|
1127
|
+
"metadata": {},
|
|
1128
|
+
"value": { "color": "green", "duration": "55 secs" }
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
"name": "lampColor",
|
|
1132
|
+
"type": "Property",
|
|
1133
|
+
"datasetId": "urn:ngsi-ld:Sequence:then-do-this",
|
|
1134
|
+
"metadata": {},
|
|
1135
|
+
"value": { "color": "red", "duration": "10 secs" }
|
|
1136
|
+
}
|
|
1137
|
+
]
|
|
1138
|
+
```
|
|
28
1139
|
|
|
29
|
-
|
|
1140
|
+
A `datasetId` is also maintained for each new attribute defined in the `reverse` field.
|
|
30
1141
|
|
|
31
|
-
|
|
32
|
-
- Dynamic **Configuration API**: making use of the API URLs in the configuration URI, `/iot/services`. Please, note
|
|
33
|
-
that the configuration API manage servers under a URL that requires the `server.name` parameter to be set (the name
|
|
34
|
-
of the IoT Agent we are using). If no name is configured `default` is taken as the default one.
|
|
1142
|
+
# API Routes
|
|
35
1143
|
|
|
36
|
-
|
|
37
|
-
collection, they are described in the sections below.
|
|
1144
|
+
## Config group API
|
|
38
1145
|
|
|
39
|
-
|
|
40
|
-
`fiware-service` and `fiware-servicepath` to be defined; the operations are performed in the scope of those headers. For
|
|
41
|
-
the list case, the special wildcard servicepath can be specified, `/*`. In this case, the operation applies to all the
|
|
42
|
-
subservices of the service given by the `fiware-service` header.
|
|
1146
|
+
### Config group datamodel
|
|
43
1147
|
|
|
44
|
-
|
|
45
|
-
to which device). Those operations of the API targeting specific resources will need the use of the `resource` and
|
|
46
|
-
`apikey` parameters to select the appropriate instance.
|
|
1148
|
+
Config group is represented by a JSON object with the following fields:
|
|
47
1149
|
|
|
48
|
-
|
|
49
|
-
|
|
1150
|
+
| Field | Optional | Type | Expression | Definitiom |
|
|
1151
|
+
| ------------------------------ | -------- | -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1152
|
+
| `service` | | string | | `FIWARE-Service` header to be used |
|
|
1153
|
+
| `subservice` | | string | | Subservice of the devices of this type. |
|
|
1154
|
+
| `resource` | | string | | string representing the Southbound resource that will be used to assign a type to a device (e.g.: pathname in the southbound port). |
|
|
1155
|
+
| `apikey` | | string | | API Key string. |
|
|
1156
|
+
| `timestamp` | ✓ | bool | | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. |
|
|
1157
|
+
| `entity_type` | | string | | name of the Entity `type` to assign to the group. |
|
|
1158
|
+
| `trust` | ✓ | string | | trust token to use for secured access to the Context Broker for this type of devices (optional; only needed for secured scenarios). |
|
|
1159
|
+
| `cbHost` | ✓ | string | | Context Broker connection information. This options can be used to override the global ones for specific types of devices. |
|
|
1160
|
+
| `lazy` | ✓ | | | list of common lazy attributes of the device. For each attribute, its `name` and `type` must be provided. |
|
|
1161
|
+
| `commands` | ✓ | | | list of common commands attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
|
|
1162
|
+
| `attributes` | ✓ | | | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
|
|
1163
|
+
| `static_attributes` | ✓ | | | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
|
|
1164
|
+
| `internal_attributes` | ✓ | | | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
|
|
1165
|
+
| `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) |
|
|
1166
|
+
| `entityNameExp` | ✓ | string | | optional field to allow use expressions to define entity name, instead default `id` and `type` |
|
|
1167
|
+
| `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. |
|
|
1168
|
+
| `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. |
|
|
1169
|
+
| `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`. |
|
|
50
1170
|
|
|
51
|
-
###
|
|
1171
|
+
### Config group operations
|
|
52
1172
|
|
|
53
|
-
The
|
|
54
|
-
information, security information and other attributes. The `types` attribute of the configuration is a map, where the
|
|
55
|
-
key is the type name and the value is an object containing all the type information. Each type can has the following
|
|
56
|
-
information configured:
|
|
1173
|
+
The following actions are available under the config group endpoint:
|
|
57
1174
|
|
|
58
|
-
|
|
59
|
-
- **subservice**: subservice of the devices of this type.
|
|
60
|
-
- **attributes**: list of active attributes of the device. For each attribute, its `name` and `type` must be provided,
|
|
61
|
-
additional `metadata` is optional.
|
|
62
|
-
- **lazy**: list of lazy attributes of the device. For each attribute, its `name` and `type` must be provided.
|
|
63
|
-
- **commands**: list of commands attributes of the device. For each attribute, its `name` and `type` must be provided.
|
|
64
|
-
- **internalAttributes**: optional section with free format, to allow specific IoT Agents to store information along
|
|
65
|
-
with the devices in the Device Registry.
|
|
66
|
-
- **staticAttributes**: this array of attributes will be added to every entity of this type 'as is'. `name` and `type`
|
|
67
|
-
must be provided, additional `metadata` is optional.
|
|
68
|
-
- **trust**: trust token to use for secured access to the Context Broker for this type of devices (optional; only
|
|
69
|
-
needed for secured scenarios). Trust tokens may be called `access_tokens` by some Oauth2 providers.
|
|
70
|
-
- **cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific
|
|
71
|
-
types of devices.
|
|
1175
|
+
#### Retrieve config groups `GET /iot/services`
|
|
72
1176
|
|
|
73
|
-
|
|
1177
|
+
List all the config groups for the given `fiware-service` and `fiware-servicepath`. If the `fiware-servicepath` header
|
|
1178
|
+
has the wildcard expression, `/*`, all the config groups for that `fiware-service` are returned. The config groups that
|
|
1179
|
+
match the `fiware-servicepath` are returned in any other case.
|
|
74
1180
|
|
|
75
|
-
|
|
76
|
-
correspondence between the API resource fields and the same fields in the database model.
|
|
1181
|
+
_**Request headers**_
|
|
77
1182
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `resource` | `resource` | string representing the Southbound resource that will be used to assign a type to a device (e.g.: pathname in the southbound port). |
|
|
83
|
-
| `apikey` | `apikey` | API Key string. |
|
|
84
|
-
| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true |
|
|
85
|
-
| `entity_type` | `entity_type` | name of the Entity `type` to assign to the group. |
|
|
86
|
-
| `trust` | `trust` | trust token to use for secured access to the Context Broker for this type of devices (optional; only needed for secured scenarios). |
|
|
87
|
-
| `cbHost` | `cbHost` | Context Broker connection information. This options can be used to override the global ones for specific types of devices. |
|
|
88
|
-
| `lazy` | `lazy` | list of common lazy attributes of the device. For each attribute, its `name` and `type` must be provided. |
|
|
89
|
-
| `commands` | `commands` | list of common commands attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
|
|
90
|
-
| `attributes` | `attributes` | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
|
|
91
|
-
| `static_attributes` | `staticAttributes` | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
|
|
92
|
-
| `internal_attributes` | `internalAttributes` | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
|
|
93
|
-
| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, `legacy` is used as default value. |
|
|
94
|
-
| `explicitAttrs` | `explicitAttrs` | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](advanced-topics.md#explicitly-defined-attributes-explicitattrs) |
|
|
95
|
-
| `entityNameExp` | `entityNameExp` | optional field to allow use expressions to define entity name, instead default `id` and `type` |
|
|
96
|
-
| `ngsiVersion` | `ngsiVersion` | 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. |
|
|
97
|
-
| `defaultEntityNameConjunction` | `defaultEntityNameConjunction` | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. |
|
|
98
|
-
| `autoprovision` | `autoprovision` | 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`. |
|
|
1183
|
+
| Header | Optional | Description | Example |
|
|
1184
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1185
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1186
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
99
1187
|
|
|
100
|
-
|
|
1188
|
+
_**Response code**_
|
|
101
1189
|
|
|
102
|
-
|
|
1190
|
+
- 200 OK if successful, returning a config group body.
|
|
1191
|
+
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
1192
|
+
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
103
1193
|
|
|
104
|
-
|
|
1194
|
+
_**Response headers**_
|
|
105
1195
|
|
|
106
|
-
|
|
107
|
-
taken from the headers, overwritting any preexisting values.
|
|
1196
|
+
Successful operations return `Content-Type` header with `application/json` value.
|
|
108
1197
|
|
|
109
|
-
|
|
1198
|
+
_**Response payload**_
|
|
110
1199
|
|
|
111
|
-
|
|
1200
|
+
A JSON object with a services field that contains an array of services that match the request. See the
|
|
1201
|
+
[config group datamodel](#service-group-datamodel) for more information.
|
|
112
1202
|
|
|
113
|
-
|
|
1203
|
+
Example:
|
|
114
1204
|
|
|
115
1205
|
```json
|
|
116
1206
|
{
|
|
@@ -132,246 +1222,213 @@ E.g.:
|
|
|
132
1222
|
}
|
|
133
1223
|
],
|
|
134
1224
|
"lazy": [{ "name": "status", "type": "Boolean" }]
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"resource": "/deviceTest2",
|
|
1228
|
+
"apikey": "A21323ASDG12312ASDN21LWQEJPO2J123",
|
|
1229
|
+
"type": "Switch",
|
|
1230
|
+
"trust": "8970A9078A803H3BL98PINEQRW8342HBAMS",
|
|
1231
|
+
"cbHost": "http://orion:1026",
|
|
1232
|
+
"commands": [{ "name": "on", "type": "order" }],
|
|
1233
|
+
"attributes": [
|
|
1234
|
+
{
|
|
1235
|
+
"name": "swithc_status",
|
|
1236
|
+
"type": "boolean"
|
|
1237
|
+
}
|
|
1238
|
+
]
|
|
135
1239
|
}
|
|
136
1240
|
]
|
|
137
1241
|
}
|
|
138
1242
|
```
|
|
139
1243
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
- 200 OK if successful, with no payload.
|
|
143
|
-
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
144
|
-
- 400 WRONG_SYNTAX if the body doesn't comply with the schema.
|
|
145
|
-
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
146
|
-
|
|
147
|
-
##### GET /iot/services
|
|
1244
|
+
#### Create config group `POST /iot/services`
|
|
148
1245
|
|
|
149
|
-
|
|
150
|
-
|
|
1246
|
+
Creates a set of config groups for the given service and service path. The service and subservice information will taken
|
|
1247
|
+
from the headers, overwritting any preexisting values.
|
|
151
1248
|
|
|
152
|
-
|
|
1249
|
+
_**Request headers**_
|
|
153
1250
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
-
|
|
1251
|
+
| Header | Optional | Description | Example |
|
|
1252
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1253
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1254
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
157
1255
|
|
|
158
|
-
|
|
1256
|
+
_**Request payload**_
|
|
159
1257
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
existing attributes will be updated
|
|
1258
|
+
A JSON object with a `services` field. The value is an array of config groups objects to create. See the
|
|
1259
|
+
[config group datamodel](#service-group-datamodel) for more information.
|
|
163
1260
|
|
|
164
|
-
|
|
1261
|
+
Example:
|
|
165
1262
|
|
|
166
1263
|
```json
|
|
167
1264
|
{
|
|
168
|
-
"
|
|
169
|
-
|
|
1265
|
+
"services": [
|
|
1266
|
+
{
|
|
1267
|
+
"resource": "/deviceTest",
|
|
1268
|
+
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
1269
|
+
"type": "Light",
|
|
1270
|
+
"trust": "8970A9078A803H3BL98PINEQRW8342HBAMS",
|
|
1271
|
+
"cbHost": "http://orion:1026",
|
|
1272
|
+
"commands": [{ "name": "wheel1", "type": "Wheel" }],
|
|
1273
|
+
"attributes": [
|
|
1274
|
+
{
|
|
1275
|
+
"name": "luminescence",
|
|
1276
|
+
"type": "Integer",
|
|
1277
|
+
"metadata": {
|
|
1278
|
+
"unitCode": { "type": "Text", "value": "CAL" }
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
],
|
|
1282
|
+
"lazy": [{ "name": "status", "type": "Boolean" }]
|
|
1283
|
+
}
|
|
1284
|
+
]
|
|
170
1285
|
}
|
|
171
1286
|
```
|
|
172
1287
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- 200 OK if successful, returning the updated body.
|
|
176
|
-
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
177
|
-
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
1288
|
+
_**Response code**_
|
|
178
1289
|
|
|
179
|
-
|
|
1290
|
+
- `200` `OK` if successful, with no payload.
|
|
1291
|
+
- `400` `MISSING_HEADERS` if any of the mandatory headers is not present.
|
|
1292
|
+
- `400` `WRONG_SYNTAX` if the body doesn't comply with the schema.
|
|
1293
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
180
1294
|
|
|
181
|
-
|
|
1295
|
+
_**Response headers**_
|
|
182
1296
|
|
|
183
|
-
|
|
1297
|
+
Successful operations return `Content-Type` header with `application/json` value.
|
|
184
1298
|
|
|
185
|
-
|
|
186
|
-
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
187
|
-
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
1299
|
+
#### Modify config group `PUT /iot/services`
|
|
188
1300
|
|
|
189
|
-
|
|
1301
|
+
Modifies the information of a config group, identified by the `resource` and `apikey` query parameters. Takes a service
|
|
1302
|
+
group body as the payload. The body does not have to be complete: for incomplete bodies, just the attributes included in
|
|
1303
|
+
the JSON body will be updated. The rest of the attributes will remain unchanged.
|
|
190
1304
|
|
|
191
|
-
|
|
192
|
-
subservice mapping, security information and attribute configuration can be specified in a per device way instead of
|
|
193
|
-
relaying on the type configuration. The following section specifies the format of the device payload; this will be the
|
|
194
|
-
payload accepted by all the write operations and that will be returned by all the read operations. Take care of the
|
|
195
|
-
exception of the POST operation: in this case, the device objects must be specified as an array, as multiple devices can
|
|
196
|
-
be provided simultaneusly for the same service.
|
|
1305
|
+
_**Request query parameters**_
|
|
197
1306
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
outgoing requests).
|
|
1307
|
+
| Parameter | Mandatory | Description | Example |
|
|
1308
|
+
| --------- | --------- | -------------------------------------------- | ----------------------------------------- |
|
|
1309
|
+
| resource | ✓ | Resource of the config group to be modified. | `/device` |
|
|
1310
|
+
| apikey | ✓ | Apikey of the config group to be modified. | `801230BJKL23Y9090DSFL123HJK09H324HV8732` |
|
|
203
1311
|
|
|
204
|
-
|
|
205
|
-
`service_path` vs. `servicePath`).
|
|
1312
|
+
_**Request headers**_
|
|
206
1313
|
|
|
207
|
-
|
|
1314
|
+
| Header | Optional | Description | Example |
|
|
1315
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1316
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1317
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
208
1318
|
|
|
209
|
-
|
|
210
|
-
case `apiKey` matching fails). For instance, let's consider a situation in which a service group has been provisioned
|
|
211
|
-
with `type=X`/`apiKey=111` and no other service group has been provisioned.
|
|
1319
|
+
_**Request payload**_
|
|
212
1320
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
- IoT Agent receives a provisioning request for an explicit device of `type=Y`/`apiKey=111`. The matching `apiKey`
|
|
216
|
-
means the entity inherits from service group but type is overridden. Device entity has `type=Y` and `apiKey=111`
|
|
217
|
-
- IoT Agent receives a provisioning request for an explicit device of `type=X`/`apiKey=222`. The matching `type` means
|
|
218
|
-
the entity inherits from service group but `apiKey` is overridden. Device entity has `type=X` and `apiKey=222`.
|
|
219
|
-
- IoT Agent receives a provisioning request for an explicit device of `type=Y`/`apiKey=222`. No matching. Device
|
|
220
|
-
entity has `type=Y` and `apiKey=222` and no service group.
|
|
1321
|
+
A JSON object with the config group information to be modified. See the
|
|
1322
|
+
[config group datamodel](#service-group-datamodel) for more information.
|
|
221
1323
|
|
|
222
|
-
|
|
1324
|
+
Example:
|
|
223
1325
|
|
|
224
|
-
|
|
225
|
-
|
|
1326
|
+
```json
|
|
1327
|
+
{
|
|
1328
|
+
"trust": "8970A9078A803H3BL98PINEQRW8342HBAMS",
|
|
1329
|
+
"cbHost": "http://orion:1026"
|
|
1330
|
+
}
|
|
1331
|
+
```
|
|
226
1332
|
|
|
227
|
-
|
|
228
|
-
| --------------------- | -------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------- |
|
|
229
|
-
| `device_id` | `id` | Device ID that will be used to identify the device. | UO834IO |
|
|
230
|
-
| `service` | `service` | Name of the service the device belongs to (will be used in the fiware-service header). | smartGondor |
|
|
231
|
-
| `service_path` | `subservice` | Name of the subservice the device belongs to (used in the fiware-servicepath header). | /gardens |
|
|
232
|
-
| `entity_name` | `name` | Name of the entity representing the device in the Context Broker | ParkLamplight12 |
|
|
233
|
-
| `entity_type` | `type` | Type of the entity in the Context Broker | Lamplights |
|
|
234
|
-
| `timezone` | `timezone` | Time zone of the sensor if it has any | America/Santiago |
|
|
235
|
-
| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true |
|
|
236
|
-
| `apikey` | `apikey` | Optional Apikey key string to use instead of group apikey | 9n4hb1vpwbjozzmw9f0flf9c2 |
|
|
237
|
-
| `endpoint` | `endpoint` | Endpoint where the device is going to receive commands, if any. | http://theDeviceUrl:1234/commands |
|
|
238
|
-
| `protocol` | `protocol` | Name of the device protocol, for its use with an IoT Manager. | IoTA-UL |
|
|
239
|
-
| `transport` | `transport` | Name of the device transport protocol, for the IoT Agents with multiple transport protocols. | MQTT |
|
|
240
|
-
| `attributes` | `active` | List of active attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
|
|
241
|
-
| `lazy` | `lazy` | List of lazy attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
|
|
242
|
-
| `commands` | `commands` | List of commands of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
|
|
243
|
-
| `internal_attributes` | `internalAttributes` | List of internal attributes with free format for specific IoT Agent configuration | LWM2M mappings from object URIs to attributes |
|
|
244
|
-
| `static_attributes` | `staticAttributes` | List of static attributes to append to the entity. All the updateContext requests to the CB will have this set of attributes appended. | `[ { "name": "attr_name", "type": "Text" } ]` |
|
|
245
|
-
| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, legacy is used as default value. |
|
|
246
|
-
| `explicitAttrs` | `explicitAttrs` | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](advanced-topics.md#explicitly-defined-attributes-explicitattrs) | (see details in specific section) |
|
|
247
|
-
| `ngsiVersion` | `ngsiVersion` | optional 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. | `v2/ld` |
|
|
248
|
-
|
|
249
|
-
#### Attribute lists
|
|
250
|
-
|
|
251
|
-
In the group/device model there are three list of attributes that can be declared: attributes, lazy and commands. All of
|
|
252
|
-
them have the same syntax, an object containing the following attributes:
|
|
1333
|
+
_**Response code**_
|
|
253
1334
|
|
|
254
|
-
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
257
|
-
- **metadata** (optional): additional static metadata for the attribute in the target entity. (e.g. `unitCode`)
|
|
1335
|
+
- 200 OK if successful, returning the updated body.
|
|
1336
|
+
- 400 MISSING_HEADERS if any of the mandatory headers is not present.
|
|
1337
|
+
- 500 SERVER ERROR if there was any error not contemplated above.:
|
|
258
1338
|
|
|
259
|
-
|
|
1339
|
+
#### Remove config group `DELETE /iot/services`
|
|
260
1340
|
|
|
261
|
-
|
|
262
|
-
an expression based on a combination of the reported values. See the
|
|
263
|
-
[Expression Language definition](expressionLanguage.md) for details
|
|
264
|
-
- **entity_name**: the presence of this attribute indicates that the value will not be stored in the original device
|
|
265
|
-
entity but in a new entity with an ID given by this attribute. The type of this additional entity can be configured
|
|
266
|
-
with the `entity_type` attribute. If no type is configured, the device entity type is used instead. Entity names can
|
|
267
|
-
be defined as expressions, using the [Expression Language definition](expressionLanguage.md).
|
|
268
|
-
- **entity_type**: configures the type of an alternative entity.
|
|
269
|
-
- **reverse**: add bidirectionality expressions to the attribute. See the **bidirectionality** transformation plugin
|
|
270
|
-
in the [Data Mapping Plugins section](advanced-topics.md#bidirectionality-plugin-bidirectional) for details.
|
|
1341
|
+
Removes a config group, identified by the `resource` and `apikey` query parameters.
|
|
271
1342
|
|
|
272
|
-
|
|
1343
|
+
_**Request query parameters**_
|
|
273
1344
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
particular IOTAs documentation for allowed values of this field in each case.
|
|
279
|
-
- **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of
|
|
280
|
-
transports)
|
|
1345
|
+
| Parameter | Mandatory | Description | Example |
|
|
1346
|
+
| --------- | --------- | ------------------------------------------- | ----------------------------------------- |
|
|
1347
|
+
| resource | ✓ | Resource of the config group to be removed. | `/device` |
|
|
1348
|
+
| apikey | ✓ | Apikey of the config group to be removed. | `801230BJKL23Y9090DSFL123HJK09H324HV8732` |
|
|
281
1349
|
|
|
282
|
-
|
|
1350
|
+
_**Request headers**_
|
|
283
1351
|
|
|
284
|
-
|
|
1352
|
+
| Header | Optional | Description | Example |
|
|
1353
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1354
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1355
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
285
1356
|
|
|
286
|
-
|
|
1357
|
+
_**Response code**_
|
|
287
1358
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
tracking or Datex II for transport systems.
|
|
1359
|
+
- `200` `OK` if successful.
|
|
1360
|
+
- `400` `MISSING_HEADERS` if any of the mandatory headers is not present.
|
|
1361
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
292
1362
|
|
|
293
|
-
|
|
1363
|
+
## Device API
|
|
294
1364
|
|
|
295
|
-
|
|
296
|
-
Remember that `null` is not allowed in NGSI-LD and therefore should be avoided as a value.
|
|
1365
|
+
### Device datamodel
|
|
297
1366
|
|
|
298
|
-
|
|
1367
|
+
The table below shows the information held in the Device resource. The table also contains the correspondence between
|
|
1368
|
+
the API resource fields and the same fields in the database model.
|
|
299
1369
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
1370
|
+
| Field | Optional | Type | Expression | Definitiom |
|
|
1371
|
+
| --------------------- | -------- | --------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1372
|
+
| `device_id` | | `string` | | Device ID that will be used to identify the device. |
|
|
1373
|
+
| `service` | | `string` | | Name of the service the device belongs to (will be used in the fiware-service header). |
|
|
1374
|
+
| `service_path` | | `string` | | Name of the subservice the device belongs to (used in the fiware-servicepath header). |
|
|
1375
|
+
| `entity_name` | | `string` | | Name of the entity representing the device in the Context Broker |
|
|
1376
|
+
| `entity_type` | | `string` | | Type of the entity in the Context Broker |
|
|
1377
|
+
| `timezone` | ✓ | `string` | | Timezone of the device if that has any |
|
|
1378
|
+
| `timestamp` | ✓ | `string` | | Flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. |
|
|
1379
|
+
| `apikey` | ✓ | `string` | | Apikey key string to use instead of group apikey |
|
|
1380
|
+
| `endpoint` | ✓ | `string` | | Endpoint where the device is going to receive commands, if any. |
|
|
1381
|
+
| `protocol` | ✓ | `string` | | Pame of the device protocol, for its use with an IoT Manager |
|
|
1382
|
+
| `transport` | ✓ | `string` | | Transport protocol used by the device to send updates, for the IoT Agents with multiple transport protocols. |
|
|
1383
|
+
| `attributes` | ✓ | `array` | | List of attributes that will be stored in the Context Broker. |
|
|
1384
|
+
| `commands` | ✓ | `array` | | List of commands that will be stored in the Context Broker. |
|
|
1385
|
+
| `lazy` | ✓ | `array` | | List of lazy attributes that will be stored in the Context Broker. |
|
|
1386
|
+
| `static_attributes` | ✓ | `array` | | List of static attributes that will be stored in the Context Broker. |
|
|
1387
|
+
| `internal_attributes` | ✓ | `array` | | List of internal attributes with free format for specific IoT Agent configuration. |
|
|
1388
|
+
| `explicitAttrs` | ✓ | `boolean` | ✓ | Field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
|
|
1389
|
+
| `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. |
|
|
306
1390
|
|
|
307
|
-
|
|
308
|
-
{
|
|
309
|
-
"object_id": "l",
|
|
310
|
-
"name": "length",
|
|
311
|
-
"type": "Integer",
|
|
312
|
-
"metadata": {
|
|
313
|
-
"unitCode": { "type": "Text", "value": "FOT" }
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
```
|
|
1391
|
+
### Device operations
|
|
317
1392
|
|
|
318
|
-
####
|
|
1393
|
+
#### Retrieve devices /iot/devices `GET /iot/devices`
|
|
319
1394
|
|
|
320
|
-
|
|
1395
|
+
List all the devices registered in the IoT Agent device registry with all their information for a given `fiware-service`
|
|
1396
|
+
and `fiware-servicepath`.
|
|
321
1397
|
|
|
322
|
-
|
|
1398
|
+
_**Request query parameters**_
|
|
323
1399
|
|
|
324
|
-
|
|
1400
|
+
| Parameter | Mandatory | Description | Example |
|
|
1401
|
+
| --------- | --------- | --------------------------------------------------------- | ------- |
|
|
1402
|
+
| `limit` | | Maximum number of results to return in a single response. | `20` |
|
|
1403
|
+
| `offset` | | Number of results to skip from the original query. | `0` |
|
|
325
1404
|
|
|
326
|
-
|
|
327
|
-
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
1405
|
+
_**Request headers**_
|
|
328
1406
|
|
|
329
|
-
|
|
1407
|
+
| Header | Optional | Description | Example |
|
|
1408
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1409
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1410
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
330
1411
|
|
|
331
|
-
|
|
332
|
-
{
|
|
333
|
-
"devices": [
|
|
334
|
-
{
|
|
335
|
-
"device_id": "DevID1",
|
|
336
|
-
"entity_name": "urn:ngsi-ld:Device:TheDevice1",
|
|
337
|
-
"entity_type": "Device",
|
|
338
|
-
"attributes": [
|
|
339
|
-
{ "object_id": "t", "name": "temperature", "type": "Float" },
|
|
340
|
-
{ "object_id": "h", "name": "humidity", "type": "Float" }
|
|
341
|
-
],
|
|
342
|
-
"lazy": [
|
|
343
|
-
{
|
|
344
|
-
"object_id": "l",
|
|
345
|
-
"name": "percentage",
|
|
346
|
-
"type": "Integer",
|
|
347
|
-
"metadata": {
|
|
348
|
-
"unitCode": { "type": "Text", "value": "P1" }
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
],
|
|
352
|
-
"commands": [{ "object_id": "t", "name": "turn", "type": "Text" }],
|
|
353
|
-
"static_attributes": [{ "name": "serialID", "value": "02598347", "type": "Text" }]
|
|
354
|
-
}
|
|
355
|
-
]
|
|
356
|
-
}
|
|
357
|
-
```
|
|
1412
|
+
_**Response code**_
|
|
358
1413
|
|
|
359
|
-
|
|
1414
|
+
- `200` `OK` if successful.
|
|
1415
|
+
- `404` `NOT FOUND` if there are no devices for the given service and subservice.
|
|
1416
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
360
1417
|
|
|
361
|
-
|
|
1418
|
+
_**Response headers**_
|
|
362
1419
|
|
|
363
|
-
|
|
1420
|
+
Successful operations return `Content-Type` header with `application/json` value.
|
|
364
1421
|
|
|
365
|
-
|
|
366
|
-
- offset: if present, skip that number of devices from the original query.
|
|
1422
|
+
_**Response body**_
|
|
367
1423
|
|
|
368
|
-
|
|
1424
|
+
The response body contains a JSON object with the following fields:
|
|
369
1425
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
1426
|
+
| Field | Type | Description |
|
|
1427
|
+
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1428
|
+
| `count` | `integer` | Number of devices in the response. |
|
|
1429
|
+
| `devices` | `array` | List of devices in the response. Each device is represented by a JSON object. For more information about the device object, see the [Device datamodel](#device-datamodel) section. |
|
|
373
1430
|
|
|
374
|
-
Example
|
|
1431
|
+
Example:
|
|
375
1432
|
|
|
376
1433
|
```json
|
|
377
1434
|
{
|
|
@@ -416,17 +1473,80 @@ Example of return payload:
|
|
|
416
1473
|
}
|
|
417
1474
|
```
|
|
418
1475
|
|
|
419
|
-
#####
|
|
1476
|
+
##### Create device `POST /iot/devices`
|
|
420
1477
|
|
|
421
|
-
|
|
1478
|
+
Provision a new device in the IoT Agent's device registry for a given `fiware-service` and `fiware-servicepath`. Takes a
|
|
1479
|
+
Device in JSON format as the payload.
|
|
422
1480
|
|
|
423
|
-
|
|
1481
|
+
_**Request headers**_
|
|
424
1482
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
-
|
|
1483
|
+
| Header | Optional | Description | Example |
|
|
1484
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1485
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1486
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
1487
|
+
|
|
1488
|
+
_**Request body**_
|
|
1489
|
+
|
|
1490
|
+
The body of the a JSON object with field named `devices` containing a list of devices to be provisioned. Each device is
|
|
1491
|
+
represented by device JSON object. For more information, see the [Device datamodel](#device-datamodel) section.
|
|
1492
|
+
|
|
1493
|
+
Example:
|
|
1494
|
+
|
|
1495
|
+
```json
|
|
1496
|
+
{
|
|
1497
|
+
"devices": [
|
|
1498
|
+
{
|
|
1499
|
+
"device_id": "DevID1",
|
|
1500
|
+
"entity_name": "urn:ngsi-ld:Device:TheDevice1",
|
|
1501
|
+
"entity_type": "Device",
|
|
1502
|
+
"attributes": [
|
|
1503
|
+
{ "object_id": "t", "name": "temperature", "type": "Float" },
|
|
1504
|
+
{ "object_id": "h", "name": "humidity", "type": "Float" }
|
|
1505
|
+
]
|
|
1506
|
+
}
|
|
1507
|
+
]
|
|
1508
|
+
}
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1511
|
+
_**Response code**_
|
|
1512
|
+
|
|
1513
|
+
- `200` `OK` if successful.
|
|
1514
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
1515
|
+
|
|
1516
|
+
#### Get device details `GET /iot/devices/:deviceId`
|
|
1517
|
+
|
|
1518
|
+
Returns all the information about a device in the IoT Agent's device registry for a given `fiware-service` and
|
|
1519
|
+
`fiware-servicepath`.
|
|
1520
|
+
|
|
1521
|
+
_**Request URL parameters**_
|
|
1522
|
+
|
|
1523
|
+
| Parameter | Mandatory | Description | Example |
|
|
1524
|
+
| ---------- | --------- | ----------- | -------- |
|
|
1525
|
+
| `deviceId` | ✓ | Device ID. | `DevID1` |
|
|
1526
|
+
|
|
1527
|
+
_**Request headers**_
|
|
1528
|
+
|
|
1529
|
+
| Header | Optional | Description | Example |
|
|
1530
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1531
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1532
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
1533
|
+
|
|
1534
|
+
_**Response code**_
|
|
1535
|
+
|
|
1536
|
+
- `200` `OK` if successful.
|
|
1537
|
+
- `404` `NOT FOUND` if the device was not found in the database.
|
|
1538
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
1539
|
+
|
|
1540
|
+
_**Response headers**_
|
|
428
1541
|
|
|
429
|
-
|
|
1542
|
+
Successful operations return `Content-Type` header with `application/json` value.
|
|
1543
|
+
|
|
1544
|
+
_**Response body**_
|
|
1545
|
+
|
|
1546
|
+
The response body contains a device JSON object. For more information, see the [Device datamodel](#device-datamodel)
|
|
1547
|
+
section.
|
|
1548
|
+
|
|
1549
|
+
Example:
|
|
430
1550
|
|
|
431
1551
|
```json
|
|
432
1552
|
{
|
|
@@ -452,29 +1572,32 @@ Example of return payload:
|
|
|
452
1572
|
}
|
|
453
1573
|
```
|
|
454
1574
|
|
|
455
|
-
|
|
1575
|
+
#### Modify device `PUT /iot/devices/:deviceId`
|
|
456
1576
|
|
|
457
|
-
|
|
1577
|
+
Changes the stored values for the device with the provided Device payload. Neither the name, the type nor the ID of the
|
|
1578
|
+
device can be changed using this method (as they are used to link the already created entities in the Context Broker to
|
|
1579
|
+
the information in the device). `fiware-service` and `fiware-servicepath`, being taken from the headers, can't be
|
|
1580
|
+
changed also.
|
|
458
1581
|
|
|
459
|
-
|
|
1582
|
+
_**Request URL parameters**_
|
|
460
1583
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
1584
|
+
| Parameter | Mandatory | Description | Example |
|
|
1585
|
+
| ---------- | --------- | ----------------------- | -------- |
|
|
1586
|
+
| `deviceId` | ✓ | Device ID to be updated | `DevID1` |
|
|
464
1587
|
|
|
465
|
-
|
|
1588
|
+
_**Request headers**_
|
|
466
1589
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1590
|
+
| Header | Optional | Description | Example |
|
|
1591
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1592
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1593
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
470
1594
|
|
|
471
|
-
|
|
1595
|
+
_**Request body**_
|
|
472
1596
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
- 500 SERVER ERROR if there was any error not contemplated above.
|
|
1597
|
+
The request body contains a device JSON object the values of which will be updated in the device registry. For more
|
|
1598
|
+
information, see the [Device datamodel](#device-datamodel) section.
|
|
476
1599
|
|
|
477
|
-
|
|
1600
|
+
Example:
|
|
478
1601
|
|
|
479
1602
|
```json
|
|
480
1603
|
{
|
|
@@ -489,23 +1612,142 @@ Payload example:
|
|
|
489
1612
|
}
|
|
490
1613
|
```
|
|
491
1614
|
|
|
492
|
-
|
|
1615
|
+
_**Response code**_
|
|
1616
|
+
|
|
1617
|
+
- `200` `OK` if successful.
|
|
1618
|
+
- `404` `NOT FOUND` if the device was not found in the database.
|
|
1619
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
1620
|
+
|
|
1621
|
+
#### Remove device `DELETE /iot/devices/:deviceId`
|
|
1622
|
+
|
|
1623
|
+
Remove a device from the device registry. The device is identified by the Device ID as URL parameter.
|
|
1624
|
+
|
|
1625
|
+
_**Request URL parameters**_
|
|
1626
|
+
|
|
1627
|
+
| Parameter | Mandatory | Description | Example |
|
|
1628
|
+
| ---------- | --------- | ----------------------- | -------- |
|
|
1629
|
+
| `deviceId` | ✓ | Device ID to be deleted | `DevID1` |
|
|
1630
|
+
|
|
1631
|
+
_**Request headers**_
|
|
1632
|
+
|
|
1633
|
+
| Header | Optional | Description | Example |
|
|
1634
|
+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
|
1635
|
+
| `Fiware-Service` | ✓ | Tenant or service. See subsection [Multi tenancy](#multi-tenancy) for more information. | `acme` |
|
|
1636
|
+
| `Fiware-ServicePath` | ✓ | Service path or subservice. See subsection [Service Path](#service-path) for more information. | `/project` |
|
|
1637
|
+
|
|
1638
|
+
_**Response code**_
|
|
1639
|
+
|
|
1640
|
+
- `200` `OK` if successful.
|
|
1641
|
+
- `404` `NOT FOUND` if the device was not found in the database.
|
|
1642
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
493
1643
|
|
|
494
|
-
|
|
495
|
-
required in a `logger` object, shared between all of the modules. In order for the logging to be consistent across the
|
|
496
|
-
different modules of an IoTAgent (i.e.: the ones provided by the IoTA Library as well as those created for the
|
|
497
|
-
particular IoTAgent), the `logger` object is exported in the `logModule` property of the library. The agents should use
|
|
498
|
-
this module for logging.
|
|
1644
|
+
## Miscellaneous API
|
|
499
1645
|
|
|
500
|
-
|
|
501
|
-
realtime. This API has the following two actions:
|
|
1646
|
+
### Log operations
|
|
502
1647
|
|
|
503
|
-
|
|
1648
|
+
The IoT Agent Library makes use of the [Logops logging library](https://github.com/telefonicaid/logops). The IoT Agent
|
|
1649
|
+
Library provides a configuration API that lets the administrator change and manage the log level in realtime.
|
|
504
1650
|
|
|
505
|
-
|
|
506
|
-
(i.e.: one of the items in the array ['INFO', 'ERROR', 'FATAL', 'DEBUG', 'WARNING']), it will be automatically changed
|
|
507
|
-
for future logs.
|
|
1651
|
+
#### Modify Loglevel `PUT /admin/log`
|
|
508
1652
|
|
|
509
|
-
|
|
1653
|
+
This operation gets the new log level using the query parameter `level`. If the new level is a valid level, it will be
|
|
1654
|
+
automatically changed for future logs.
|
|
1655
|
+
|
|
1656
|
+
_**Request query parameters**_
|
|
1657
|
+
|
|
1658
|
+
| Parameter | Mandatory | Description | Example |
|
|
1659
|
+
| --------- | --------- | ------------------------------------------------------------------------------------------------------------------- | ------- |
|
|
1660
|
+
| `level` | ✓ | New log level. One of the following: `INFO`, `ERROR`, `FATAL`, `DEBUG`, `WARNING`. it will be automatically changed |
|
|
1661
|
+
|
|
1662
|
+
for future logs. | `DEBUG` |
|
|
1663
|
+
|
|
1664
|
+
_**Response code**_
|
|
1665
|
+
|
|
1666
|
+
- `200` `OK` if successful.
|
|
1667
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
1668
|
+
|
|
1669
|
+
#### Retrieve log level `PUT /admin/log`
|
|
1670
|
+
|
|
1671
|
+
_**Response code**_
|
|
1672
|
+
|
|
1673
|
+
- `200` `OK` if successful.
|
|
1674
|
+
- `500` `SERVER ERROR` if there was any error not contemplated above.
|
|
1675
|
+
|
|
1676
|
+
_**Response body**_
|
|
510
1677
|
|
|
511
1678
|
Returns the current log level, in a json payload with a single attribute `level`.
|
|
1679
|
+
|
|
1680
|
+
Example:
|
|
1681
|
+
|
|
1682
|
+
```json
|
|
1683
|
+
{
|
|
1684
|
+
"level": "DEBUG"
|
|
1685
|
+
}
|
|
1686
|
+
```
|
|
1687
|
+
|
|
1688
|
+
### About operations
|
|
1689
|
+
|
|
1690
|
+
#### List IoTA Information `GET /iot/about`
|
|
1691
|
+
|
|
1692
|
+
Returns a useful information about component version and deployment information. It can be used as a heartbeat operation
|
|
1693
|
+
to check the health of the IoT Agent if required.
|
|
1694
|
+
|
|
1695
|
+
_**Response payload**_
|
|
1696
|
+
|
|
1697
|
+
The response is a JSON object with the following parameters:
|
|
1698
|
+
|
|
1699
|
+
- `libVersion`: This field is the iotagent-node-lib version with which the IoT Agent has been developed.
|
|
1700
|
+
- `port`: port where the IoT Agent will be listening as a Context Provider.
|
|
1701
|
+
- `baseRoot`: base root to prefix all the paths where the IoT Agent will be listening as a Context Provider.
|
|
1702
|
+
- `version`: This field is the IoT Agent version. It will be read from the `iotaVersion` field of the config, if it
|
|
1703
|
+
exists.
|
|
1704
|
+
|
|
1705
|
+
Example:
|
|
1706
|
+
|
|
1707
|
+
```json
|
|
1708
|
+
{
|
|
1709
|
+
"libVersion": "2.7.0",
|
|
1710
|
+
"port": "4041",
|
|
1711
|
+
"baseRoot": "/",
|
|
1712
|
+
"version": "1.7.0"
|
|
1713
|
+
}
|
|
1714
|
+
```
|
|
1715
|
+
|
|
1716
|
+
[1]:
|
|
1717
|
+
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
|
|
1718
|
+
[2]:
|
|
1719
|
+
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=level%2F100&transforms=%7B%0A%7D
|
|
1720
|
+
[3]:
|
|
1721
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=longitude%2B%22%2C%20%22%2Blatitude&transforms=%7B%0A%7D
|
|
1722
|
+
[4]:
|
|
1723
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=level%2F100&transforms=%7B%0A%7D
|
|
1724
|
+
[5]:
|
|
1725
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22spaces%22%20%3A%20%22%20%20foobar%20%20%22%0A%7D&input=spaces%20%7C%20trim&transforms=%7B%0A%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%0A%7D
|
|
1726
|
+
[6]:
|
|
1727
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=5%20*%20value&transforms=%7B%0A%7D
|
|
1728
|
+
[7]:
|
|
1729
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(6%20%2B%20value)%20*%203&transforms=%7B%0A%7D
|
|
1730
|
+
[8]:
|
|
1731
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%2F%2012%20%2B%201&transforms=%7B%0A%7D
|
|
1732
|
+
[9]:
|
|
1733
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(5%20%2B%202)%20*%20(value%20%2B%207)&transforms=%7B%0A%7D
|
|
1734
|
+
[10]:
|
|
1735
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20*%205.2&transforms=%7B%0A%7D
|
|
1736
|
+
[11]:
|
|
1737
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22Pruebas%20%22%20%2B%20%22De%20Strings%22&transforms=%7B%0A%7D
|
|
1738
|
+
[12]:
|
|
1739
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%20%2B%20%22value%20is%20%22%20%2Bvalue&transforms=%7B%0A%7D
|
|
1740
|
+
[13]:
|
|
1741
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Bcoordinates%3A%20%5Bvalue%2Cvalue%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
|
|
1742
|
+
[14]:
|
|
1743
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=ts%7Ctoisodate&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
1744
|
+
[15]:
|
|
1745
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22%20a%20%22%7Ctrim&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
1746
|
+
[16]:
|
|
1747
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Clength&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
1748
|
+
[17]:
|
|
1749
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7CindexOf(%22e%22)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
1750
|
+
[18]:
|
|
1751
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Csubstr(0%2Cname%7CindexOf(%22e%22)%2B1)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
1752
|
+
[99]:
|
|
1753
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22text%22%20%3A%20%22%20%20foobar%7B%7D%20%20%22%0A%7D&input=text%20%7C%20replacestr(%22foo%22%2C%22FOO%22)%7Ctrim%7Curlencode&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|