iotagent-node-lib 3.1.0 → 3.3.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.
Files changed (163) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +16 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +55 -0
  4. package/.github/advanced-issue-labeler.yml +30 -0
  5. package/.github/workflows/issue-labeler.yml +43 -0
  6. package/CHANGES_NEXT_RELEASE +0 -2
  7. package/config.js +5 -5
  8. package/doc/api.md +1483 -298
  9. package/doc/deprecated.md +7 -1
  10. package/doc/development.md +120 -0
  11. package/doc/howto.md +58 -62
  12. package/doc/installationguide.md +3 -11
  13. package/doc/requirements.txt +1 -1
  14. package/docker/Mosquitto/Dockerfile +1 -1
  15. package/lib/commonConfig.js +7 -10
  16. package/lib/fiware-iotagent-lib.js +0 -10
  17. package/lib/jexlTranformsMap.js +2 -1
  18. package/lib/model/Device.js +0 -1
  19. package/lib/model/Group.js +0 -1
  20. package/lib/model/dbConn.js +1 -7
  21. package/lib/plugins/bidirectionalData.js +8 -26
  22. package/lib/plugins/expressionPlugin.js +8 -40
  23. package/lib/plugins/jexlParser.js +28 -0
  24. package/lib/services/commands/commandService.js +1 -1
  25. package/lib/services/common/iotManagerService.js +0 -1
  26. package/lib/services/devices/deviceRegistryMongoDB.js +10 -10
  27. package/lib/services/devices/deviceService.js +16 -20
  28. package/lib/services/devices/devices-NGSI-v2.js +2 -5
  29. package/lib/services/ngsi/entities-NGSI-LD.js +16 -60
  30. package/lib/services/ngsi/entities-NGSI-v2.js +179 -119
  31. package/lib/services/northBound/deviceProvisioningServer.js +17 -17
  32. package/lib/templates/createDevice.json +5 -6
  33. package/lib/templates/createDeviceLax.json +6 -8
  34. package/lib/templates/deviceGroup.json +1 -5
  35. package/lib/templates/updateDevice.json +9 -2
  36. package/lib/templates/updateDeviceLax.json +14 -5
  37. package/package.json +1 -1
  38. package/scripts/legacy_expression_tool/README.md +262 -0
  39. package/scripts/legacy_expression_tool/legacy_expression_tool.py +423 -0
  40. package/scripts/legacy_expression_tool/requirements.txt +3 -0
  41. package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
  42. package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
  43. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +3 -13
  44. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
  45. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +31 -30
  46. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +8 -8
  47. package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
  48. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +1 -1
  49. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +33 -37
  50. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
  51. package/test/unit/ngsiv2/examples/contextRequests/updateContext.json +2 -0
  52. package/test/unit/ngsiv2/examples/contextRequests/updateContext1.json +3 -1
  53. package/test/unit/ngsiv2/examples/contextRequests/updateContext3WithStatic.json +2 -0
  54. package/test/unit/ngsiv2/examples/contextRequests/updateContext4.json +4 -1
  55. package/test/unit/ngsiv2/examples/contextRequests/updateContext5.json +12 -0
  56. package/test/unit/ngsiv2/examples/contextRequests/updateContext6.json +10 -0
  57. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin1.json +2 -0
  58. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin2.json +3 -1
  59. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin3.json +3 -1
  60. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin4.json +3 -1
  61. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin5.json +3 -1
  62. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin6.json +3 -1
  63. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin7.json +3 -1
  64. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin8.json +3 -1
  65. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin9.json +3 -1
  66. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +2 -0
  67. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +2 -0
  68. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +3 -1
  69. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +3 -1
  70. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +3 -1
  71. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +3 -1
  72. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +3 -1
  73. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandError.json +3 -1
  74. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandExpired.json +3 -1
  75. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandFinish.json +3 -1
  76. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus.json +2 -0
  77. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +2 -0
  78. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp1.json +3 -1
  79. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp2.json +3 -1
  80. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json +2 -0
  81. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json +2 -0
  82. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin12.json +2 -0
  83. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin13.json +3 -1
  84. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json +2 -0
  85. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29.json +2 -0
  86. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json +2 -0
  87. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +2 -0
  88. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +2 -8
  89. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +2 -0
  90. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +2 -0
  91. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +2 -0
  92. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +22 -0
  93. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  94. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin4.json +2 -0
  95. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
  96. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +33 -0
  97. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json +2 -0
  98. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json +2 -0
  99. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json +2 -0
  100. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json +2 -0
  101. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json +2 -0
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionSkip.json +12 -0
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +1 -1
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin1.json +1 -1
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10.json +1 -1
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +1 -1
  107. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin12.json +1 -1
  108. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin13.json +1 -1
  109. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin14.json +1 -1
  110. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +1 -1
  111. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +1 -1
  112. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +1 -1
  113. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin2.json +1 -1
  114. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
  115. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin3.json +1 -1
  116. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +1 -1
  117. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +1 -1
  118. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +1 -1
  119. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -1
  120. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +1 -1
  121. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin9.json +1 -1
  122. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +1 -1
  123. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -1
  124. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
  125. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -0
  126. package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +2 -0
  127. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributes.json +2 -0
  128. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributesMetadata.json +3 -1
  129. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestamp.json +3 -1
  130. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalse.json +12 -0
  131. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalseTimeInstant.json +12 -0
  132. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +2 -0
  133. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +2 -0
  134. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampTimezone.json +3 -1
  135. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +355 -75
  136. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +18 -51
  137. package/test/unit/ngsiv2/general/https-support-test.js +1 -5
  138. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +4 -10
  139. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +9 -26
  140. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +143 -57
  141. package/test/unit/ngsiv2/ngsiService/autocast-test.js +14 -21
  142. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +3 -5
  143. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +1 -10
  144. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +20 -30
  145. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +6 -69
  146. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +4 -6
  147. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +150 -0
  148. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +87 -16
  149. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -3
  150. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +2 -3
  151. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +15 -55
  152. package/test/unit/ngsiv2/provisioning/device-registration_test.js +1 -7
  153. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +2 -9
  154. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +0 -11
  155. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +0 -7
  156. package/test/unit/plugins/capture-provision-inPlugins_test.js +0 -6
  157. package/doc/advanced-topics.md +0 -626
  158. package/doc/expressionLanguage.md +0 -762
  159. package/lib/plugins/expressionParser.js +0 -205
  160. package/test/unit/expressions/expression-test.js +0 -197
  161. package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -882
  162. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -951
  163. package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -296
@@ -1,626 +0,0 @@
1
- ## Advanced Topics
2
-
3
- - [Secured access to the Context Broker](#secured-access-to-the-context-broker)
4
- - [NGSI-LD `GeoProperty` support](#ngsi-ld-geoproperty-support)
5
- - [Metadata support](#metadata-support)
6
- - [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
7
- - [NGSI-LD Linked Data support](#ngsi-ld-linked-data-support)
8
- - [Autoprovision configuration (autoprovision)](#autoprovision-configuration-autoprovision)
9
- - [Explicitly defined attributes (explicitAttrs)](#explicitly-defined-attributes-explicitattrs)
10
- - [Configuring operation to persist the data in Context Broker (appendMode)](#configuring-operation-to-persist-the-data-in-context-broker-appendmode)
11
- - [Differences between `autoprovision`, `explicitAttrs` and `appendMode`](#differences-between-autoprovision-explicitattrs-and-appendmode)
12
- - [Data mapping plugins](#data-mapping-plugins)
13
- - [Development](#development)
14
- - [Provided plugins](#provided-plugins)
15
- - [Timestamp Compression plugin (compressTimestamp)](#timestamp-compression-plugin-compresstimestamp)
16
- - [Attribute Alias plugin (attributeAlias)](#attribute-alias-plugin-attributealias)
17
- - [Event plugin (addEvents)](#event-plugin-addevents)
18
- - [Timestamp Processing Plugin (timestampProcess)](#timestamp-processing-plugin-timestampprocess)
19
- - [Expression Translation plugin (expressionTransformation)](#expression-translation-plugin-expressiontransformation)
20
- - [Multientity plugin (multiEntity)](#multientity-plugin-multientity)
21
- - [Bidirectionality plugin (bidirectional)](#bidirectionality-plugin-bidirectional)
22
- - [Autoprovision configuration (autoprovision)](#autoprovision-configuration-autoprovision)
23
- - [Explicitly defined attributes (explicitAttrs)](#explicitly-defined-attributes-explicitattrs)
24
-
25
- ### Secured access to the Context Broker
26
-
27
- For access to instances of the Context Broker secured with a
28
- [PEP Proxy](https://github.com/telefonicaid/fiware-orion-pep), an authentication mechanism based in Keystone Trust
29
- tokens is provided. A trust token is a way of Keystone to allow an user delegates a role to another user for a
30
- subservice. It is a long-term token that can be issued by any user to give another user permissions to impersonate him
31
- with a given role in a given project (subservice). Such impersonation itself is in turn based on a short-term access
32
- token.
33
-
34
- For the authentication mechanisms to work, the `authentication` attribute in the configuration has to be fully
35
- configured, and the `authentication.enabled` subattribute should have the value `true`.
36
-
37
- When the administrator of a service is configuring a set of devices or device types in the IoT Agent to use a secured
38
- Context Broker, he should follow this steps:
39
-
40
- - First, a Trust Token ID should be requested to Keystone, using the service administrator credentials, the role ID
41
- and the IoT Agent User ID. The Trust token can be retrieved using the following request (shown as a curl command):
42
-
43
- ```bash
44
- curl http://${KEYSTONE_HOST}/v3/OS-TRUST/trusts \
45
- -s \
46
- -H "X-Auth-Token: $ADMIN_TOKEN" \
47
- -H "Content-Type: application/json" \
48
- -d '
49
- {
50
- "trust": {
51
- "impersonation": false,
52
- "project_id": "'$SUBSERVICE_ID'",
53
- "roles": [
54
- {"id": "'$ID_ROLE'"
55
- }
56
- ],
57
- "trustee_user_id": "'$ID_IOTAGENT_USER'",
58
- "trustor_user_id": "'$ID_ADM1'"
59
- }
60
- }'
61
- ```
62
-
63
- - Every device or type of devices configured to use a secured Context Broker must be provided with a Trust Token ID in
64
- its configuration.
65
- - Before any request is sent to a secured Context Broker, the IoT Agent uses the Trust Token ID to generate a
66
- temporary access token, that is attached to the request (in the `X-Auth-token` header) (using Keystone API
67
- https://developer.openstack.org/api-ref/identity/v3-ext/#consuming-a-trust).
68
-
69
- Apart from the generation of the trust, the use of secured Context Brokers should be transparent to the user of the IoT
70
- Agent.
71
-
72
- Complete info on Keystone trust tokens could be found at:
73
-
74
- - [Trusts concept](https://docs.openstack.org/keystone/stein/user/trusts)
75
- - [Trusts API](https://docs.openstack.org/keystone/stein/api_curl_examples.html#post-v3-os-trust-trusts)
76
-
77
- ### NGSI-LD `GeoProperty` support
78
-
79
- For NGSI-LD only, the defined `type` of any GeoJSON attribute can be any set using any of the standard NGSI-v2 GeoJSON
80
- types - (e.g. `geo:json`, `geo:point`). NGSI-LD formats such as `GeoProperty`, `Point` and `LineString` are also
81
- accepted `type` values. If the latitude and longitude are received as separate measures, the JEXL or legacy
82
- [expression language](expressionLanguage.md) can be used to concatenate them into GeoJSON objects an array of tuples or
83
- a string as shown
84
-
85
- #### Legacy - encode as String
86
-
87
- ```json
88
- {
89
- "entity_type": "GPS",
90
- "resource": "/iot/d",
91
- "protocol": "PDI-IoTA-JSON",
92
- ..etc
93
- "attributes": [
94
- {
95
- "name": "location",
96
- "type": "geo:json",
97
- "expression": "${@lng}, ${@lat}"
98
- }
99
- ]
100
- }
101
- ```
102
-
103
- #### JEXL - encode as GeoJSON
104
-
105
- ```json
106
- {
107
- "entity_type": "GPS",
108
- "resource": "/iot/d",
109
- "protocol": "PDI-IoTA-JSON",
110
- "expressionLanguage": "jexl",
111
- ..etc
112
- "attributes": [
113
- {
114
- "name": "location",
115
- "type": "geo:json",
116
- "expression": "{coordinates: [longitude,latitude], type: 'Point'}"
117
- }
118
- ]
119
- }
120
- ```
121
-
122
- JEXL can be used to create GeoJSON objects directly. The Legacy expression language does not support GeoJSON. However,
123
- there is a workaround specifically for NGSI-LD Entities which always require `location` to be encoded as GeoJSON. For
124
- `attributes` and `static_attributes` which need to be formatted as GeoJSON values, three separate input formats are
125
- currently accepted. Provided the `type` is provisioned correctly, the `value` may be defined using any of the following
126
- formats:
127
-
128
- - a comma delimited string
129
-
130
- ```json
131
- {
132
- "name": "location",
133
- "value": "23, 12.5"
134
- }
135
- ```
136
-
137
- - an array of numbers
138
-
139
- ```json
140
- {
141
- "name": "location",
142
- "value": [23, 12.5]
143
- }
144
- ```
145
-
146
- - an fully formatted GeoJSON object
147
-
148
- ```json
149
- {
150
- "name": "location",
151
- "value": {
152
- "type": "Point",
153
- "coordinates": [23, 12.5]
154
- }
155
- }
156
- ```
157
-
158
- ### Metadata support
159
-
160
- Both `attributes` and `static_attributes` may be supplied with metadata when provisioning an IoT Agent, so that the
161
- units of measurement can be placed into the resultant entity.
162
-
163
- e.g.:
164
-
165
- ```json
166
- {
167
- "entity_type": "Lamp",
168
- "resource": "/iot/d",
169
- "protocol": "PDI-IoTA-UltraLight",
170
- ..etc
171
- "commands": [
172
- {"name": "on","type": "command"},
173
- {"name": "off","type": "command"}
174
- ],
175
- "attributes": [
176
- {"object_id": "s", "name": "state", "type":"Text"},
177
- {"object_id": "l", "name": "luminosity", "type":"Integer",
178
- "metadata":{
179
- "unitCode":{"type": "Text", "value" :"CAL"}
180
- }
181
- }
182
- ],
183
- "static_attributes": [
184
- {"name": "category", "type":"Text", "value": ["actuator","sensor"]},
185
- {"name": "controlledProperty", "type": "Text", "value": ["light"],
186
- "metadata":{
187
- "includes":{"type": "Text", "value" :["state", "luminosity"]},
188
- "alias":{"type": "Text", "value" :"lamp"}
189
- }
190
- },
191
- ]
192
- }
193
- ```
194
-
195
- #### NGSI-LD data and metadata considerations
196
-
197
- When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the
198
- following:
199
-
200
- - `Property`, `Relationship`, `GeoProperty`, `LanguageProperty`
201
- - Native JSON types (e.g. `String`, `Boolean`, `Float` , `Integer` `Number`)
202
- - Temporal Properties (e.g. `Datetime`, `Date` , `Time`)
203
- - GeoJSON types (e.g `Point`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`)
204
-
205
- Most NGSI-LD attributes are sent to the Context Broker as _properties_. If a GeoJSON type or native JSON type is
206
- defined, the data will be converted to the appropriate type. Temporal properties should always be expressed in UTC,
207
- using ISO 8601. This ISO 8601 conversion is applied automatically for the `observedAt` _property-of-a-property_ metadata
208
- where present.
209
-
210
- Data for any attribute defined as a _relationship_ must be a valid URN.
211
-
212
- Note that when the `unitCode` metadata attribute is supplied in the provisioning data under NGSI-LD, the standard
213
- `unitCode` _property-of-a-property_ `String` attribute is created.
214
-
215
- Other unrecognised `type` attributes will be passed as NGSI-LD data using the following JSON-LD format:
216
-
217
- ```json
218
- "<property_name>": {
219
- "type" : "Property",
220
- "value": {
221
- "@type": "<property_type>",
222
- "@value": { string or object}
223
- }
224
- }
225
- ```
226
-
227
- `null` values will be passed in the following format:
228
-
229
- ```json
230
- "<property_name>": {
231
- "type" : "Property",
232
- "value": {
233
- "@type": "Intangible",
234
- "@value": null
235
- }
236
- }
237
- ```
238
-
239
- ### NGSI-LD Linked Data support
240
-
241
- `static_attributes` may be supplied with an additional `link` data element when provisioning an IoT Agent to ensure that
242
- active attributes from the provisioned IoT Device may be maintained in parallel with a linked data entity . Take for
243
- example a temperature gauge placed within a building. The **Device** data model literally represents the IoT device
244
- itself, but the `temperature` attribute also needs to be shared with the **Building** entity
245
-
246
- A `link` between them can be provisioned as shown:
247
-
248
- e.g.:
249
-
250
- ```json
251
- {
252
- "entity_type": "Device",
253
- "resource": "/iot/d",
254
- "protocol": "PDI-IoTA-UltraLight",
255
- ..etc
256
- "attributes": [
257
- {"object_id": "l", "name": "temperature", "type":"Float",
258
- "metadata":{
259
- "unitCode":{"type": "Text", "value" :"CEL"}
260
- }
261
- }
262
- ],
263
- "static_attributes": [
264
- {
265
- "name": "controlledAsset",
266
- "type": "Relationship",
267
- "value": "urn:ngsi-ld:Building:001",
268
- "link": {
269
- "attributes": ["temperature"],
270
- "name": "providedBy",
271
- "type": "Building"
272
- }
273
- }
274
- ]
275
- }
276
- ```
277
-
278
- Whenever a `temperature` measure is received **Device** is updated, and entity `urn:ngsi-ld:Building:001` is also
279
- updated as shown:
280
-
281
- ```json
282
- "temperature": {
283
- "type": "Property",
284
- "value": 27.6,
285
- "unitCode": "CEL",
286
- "providedBy": {
287
- "type": "Relationship",
288
- "object": "urn:ngsi-ld:Device:thermometer1"
289
- }
290
- }
291
- ```
292
-
293
- ### Autoprovision configuration (autoprovision)
294
-
295
- By default, when a measure arrives to the IoTAgent, if the `device_id` does not match with an existing one, then, the
296
- IoTA creates a new device and a new entity according to the group config. Defining the field `autoprovision` to `false`
297
- when provisioning the device group, the IoTA to reject the measure at the southbound, allowing only to persist the data
298
- to devices that are already provisioned. It makes no sense to use this field in device provisioning since it is intended
299
- to avoid provisioning devices (and for it to be effective, it would have to be provisional).
300
-
301
- ### Explicitly defined attributes (explicitAttrs)
302
-
303
- If a given measure element (object_id) is not defined in the mappings of the device or group provision, the measure is
304
- stored in the Context Broker by adding a new attribute to the entity with the same name of the undefined measure
305
- element. By adding the field `explicitAttrs` with `true` value to device or group provision, the IoTAgent rejects the
306
- measure elements that are not defined in the mappings of device or group provision, persisting only the one defined in
307
- the mappings of the provision. If `explicitAttrs` is provided both at device and group level, the device level takes
308
- precedence. Additionally `explicitAttrs` can be used to define which meassures (identified by their attribute names, not
309
- by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
310
-
311
- The different possibilities are summarized below:
312
-
313
- Case 1 (default):
314
-
315
- ```
316
- "explicitAttrs": false
317
- ```
318
-
319
- every measure will be propagated to NGSI interface.
320
-
321
- Case 2:
322
-
323
- ```
324
- "explicitAttrs": true
325
- ```
326
-
327
- just measures defined in active, static (plus conditionally TimeInstant) will be propagated to NGSI interface.
328
-
329
- Case 3:
330
-
331
- ```
332
- "explicitAttrs": "['attr1','atrr2']"
333
- ```
334
-
335
- just NGSI attributes defined in the array (identified by their attribute names, not by their object_id, plus
336
- conditionally TimeInstant) will be propagated to NGSI interface (note that in this case the value of `explicitAttrs` is
337
- not a JSON but a JEXL Array that looks likes a JSON).
338
-
339
- Case 4:
340
-
341
- ```
342
- "explicitAttrs": "['attr1','atrr2',{object_id:'active_id'}]"
343
- ```
344
-
345
- just NGSI attributes defined in the array (identified by their attribute names and/or by their object_id) will be
346
- propagated to NGSI interface (note that in this case the value of `explicitAttrs` is not a JSON but a JEXL Array/Object
347
- that looks likes a JSON). This is necessary when same attribute names are used within multiple entities.
348
-
349
- Case 5:
350
-
351
- ```
352
- "explicitAtttr": "<JEXL expression resulting in bool or array>"
353
- ```
354
-
355
- depending on the JEXL expression evaluation:
356
-
357
- - If it evaluates to `true` every measure will be propagated to NGSI interface (as in case 1)
358
- - If it evaluates to `false` just measures defined in active, static (plus conditionally TimeInstant) will be
359
- propagated to NGSI interface (as in case 2)
360
- - If it evaluates to an array just measures defined in the array (identified by their attribute names, not by their
361
- object_id) will be will be propagated to NGSI interface (as in case 3)
362
-
363
- ### Configuring operation to persist the data in Context Broker (appendMode)
364
-
365
- This is a flag that can be enabled by activating the parameter `appendMode` in the configuration file or by using the
366
- `IOTA_APPEND_MODE` environment variable (more info
367
- [here](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/installationguide.md)). If this flag is
368
- activated, the update requests to the Context Broker will be performed always with APPEND type, instead of the default
369
- UPDATE. This have implications in the use of attributes with Context Providers, so this flag should be used with care.
370
-
371
- ### Differences between `autoprovision`, `explicitAttrs` and `appendMode`
372
-
373
- Since those configuration parameters are quite similar, this section is intended to clarify the relation between them.
374
-
375
- If `autoprovision` is set to `true` (default case), the agent will perform an initial request creating a new entity into
376
- the Context Broker with **only** the static and active attributes provisioned in the config group, and also a new
377
- Device in the agent, every time a measure arrives with a new `device_id`. Otherwise, this measure is ignored. This is
378
- something related to the **southbound**.
379
-
380
- What `explicitAttrs` does is to filter from the southbound the parameters that are not explicitly defined in the device
381
- provision or config group. That also would avoid propagating the measures to the Context Broker.
382
-
383
- The default way the agent updates the information into the Context Broker is by using an update request. If
384
- `appendMode=true`, the IoTA will use an append request instead of an update one. This means it will store
385
- the attributes even if they are not present in the entity. This seems the same functionality that the one provided by
386
- `autoprovision`, but it is a different concept since the scope of this config is to setup how the IoT interacts with the
387
- context broker, this is something related to the **northbound**.
388
-
389
- Note that, even creating a group with `autoprovision=true` and `explicitAttrs=true`, if you do not provision previously
390
- the entity in the Context Broker (having all attributes to be updated), it would fail if `appendMode=false`. For further
391
- information check the issue [#1301](https://github.com/telefonicaid/iotagent-node-lib/issues/1301).
392
-
393
- ### Data mapping plugins
394
-
395
- The IoT Agent Library provides a plugin mechanism in order to facilitate reusing code that makes small transformations
396
- on incoming data (both from the device and from the context consumers). This mechanism is based in the use of
397
- middlewares, i.e.: small pieces of code that receive and return an `entity`, making as many changes as they need, but
398
- taking care of returning a valid entity, that can be used as the input for other middlewares; this way, all those pieces
399
- of code can be chained together in order to make all the needed transformations in the target entity.
400
-
401
- There are two kinds of middlewares: updateContext middlewares and queryContext middlewares. The updateContext
402
- middlewares are applied before the information is sent to the Context Broker, modifiying the entity before it is sent to
403
- Orion. The queryContext middlewares are applied on the received data, whenever the IoT Agent queries the Context Broker
404
- for information. I.e.: both middlewares will be automatically applied whenever the `update()` or `query()` functions are
405
- called in the library.
406
-
407
- All the middlewares have the opportunity to break the chain of middleware applications by calling the `callback()` with
408
- an error object (the usual convention). If any of the updateContext middlewares raise an error, no request will be sent
409
- to the Context Broker. On the other hand, the queryContext request is always performed, but the call to the `query()`
410
- function will end up in an error if any of the queryContext middlewares report an error.
411
-
412
- #### Development
413
-
414
- All the middlewares have the same signature:
415
-
416
- ```javascript
417
- function middlewareName(entity, typeInformation, callback) {}
418
- ```
419
-
420
- The arguments for any middleware are the NGSI data over which it can operate:
421
-
422
- - An updateContext payload in the case of an updateContext middleware and a queryContext payload otherwise;
423
- - a typeInformation object containing all the information about the device stored during registration.
424
- - and the customary `callback` parameter, with the usual meaning. It's really important for the library user to call
425
- this callback, as failing to do so may hang the IoT Agent completely. The callback must be called with the an
426
- optional error in the first argument and the same arguments received (potentially modified) as the following.
427
-
428
- In order to manage the middlewares to the system, the following functions can be used:
429
-
430
- - `addUpdateMiddleware`: adds an updateContext middleware to the stack of middlewares. All the middlewares will be
431
- applied to every call to the `update()` function. The final payload of the updateContext request will be the result
432
- of applying all this middlewares in the order they have been defined.
433
-
434
- - `addQueryMiddleware`: adds a queryContext middleware to the stack of middlewares. All the middlewares will be
435
- applied to every call to the `query()` function.
436
-
437
- - `resetMiddlewares`: remove all the middlewares from the system.
438
-
439
- Usually, the full list of middlewares an IoT Agent will use would be added in the IoTAgent start sequence, so they
440
- should not change a lot during the IoT lifetime.
441
-
442
- #### Provided plugins
443
-
444
- The library provides some plugins out of the box, in the `dataPlugins` collection. In order to load any of them, just
445
- use the `addQueryMiddleware` and `addUpdateMiddleware` functions with the selected plugin, as in the example:
446
-
447
- ```javascript
448
- var iotaLib = require('iotagent-node-lib');
449
-
450
- iotaLib.addUpdateMiddleware(iotaLib.dataPlugins.compressTimestamp.update);
451
- iotaLib.addQueryMiddleware(iotaLib.dataPlugins.compressTimestamp.query);
452
- ```
453
-
454
- ##### Timestamp Compression plugin (compressTimestamp)
455
-
456
- This plugins change all the timestamp attributes found in the entity, and all the timestamp metadata found in any
457
- attribute, from the basic complete calendar timestamp of the ISO8601 (e.g.: 20071103T131805) to the extended complete
458
- calendar timestamp (e.g.: +002007-11-03T13:18). The middleware expects to receive the basic format in updates and return
459
- it in queries (and viceversa, receive the extended one in queries and return it in updates).
460
-
461
- ##### Attribute Alias plugin (attributeAlias)
462
-
463
- In the Device provision, an ID can be specified for each attribute, along with its name. The ID can be used then as the
464
- left part of a mapping from attribute names in the south bound to attribute names in the North Bound. If the ID and name
465
- attributes are used in this way, this plugin makes the translation from one to the other automatically.
466
-
467
- ##### Event plugin (addEvents)
468
-
469
- This plugin allows for the creation of Event attributes, i.e.: attributes whose value will be the timestamp of its
470
- inclusion in the system, regardless of the value they carried in the Southbound API. If this plugin is active, all the
471
- events in the IoT Agent with the configured type name will be marked as events. The event name can be configured in the
472
- `config.eventType` attribute.
473
-
474
- ##### Timestamp Processing Plugin (timestampProcess)
475
-
476
- This plugin processes the entity attributes looking for a `TimeInstant` attribute. If one is found, for NGSI v2, the
477
- plugin adds a `TimeInstant` attribute as metadata for every other attribute in the same request. With NGSI-LD, the
478
- Standard `observedAt` property-of-a-property is used instead.
479
-
480
- ##### Expression Translation plugin (expressionTransformation)
481
-
482
- This plugin allows the devices and configurations that have defined expressions to generate their values from those
483
- expressions and the reported measure information.
484
-
485
- For further information on how the expressions work, refer to the
486
- [Expression Language Reference](expressionLanguage.md).
487
-
488
- ##### Multientity plugin (multiEntity)
489
-
490
- Allows the devices provisioned in the IoTAgent to map their attributes to more than one entity, declaring the target
491
- entity through the Configuration or Device provisioning APIs.
492
-
493
- ```json
494
- {
495
- "devices": [
496
- {
497
- "protocol": "IoTA-UL",
498
- "entity_name": "urn:ngsi-ld:Device:contador12",
499
- "entity_type": "multientity",
500
- "attributes": [
501
- {
502
- "object_id": "cont1",
503
- "name": "vol",
504
- "type": "Text",
505
- "entity_name": "urn:ngsi-ld:Device:WaterMeterSoria01",
506
- "entity_type": "WaterMeter"
507
- },
508
- {
509
- "object_id": "cont2",
510
- "name": "vol",
511
- "type": "Text",
512
- "entity_name": "urn:ngsi-ld:Device:WaterMeterSoria02",
513
- "entity_type": "WaterMeter"
514
- },
515
- {
516
- "object_id": "cont3",
517
- "name": "vol",
518
- "type": "Text",
519
- "entity_name": "urn:ngsi-ld:Device:WaterMeterSoria03",
520
- "entity_type": "WaterMeter"
521
- }
522
- ],
523
- "device_id": "contador12"
524
- }
525
- ]
526
- }
527
- ```
528
-
529
- ##### Bidirectionality plugin (bidirectional)
530
-
531
- This plugin allows the devices with composite values an expression to update the original values in the devices when the
532
- composite expressions are updated in the Context Broker. This behavior is achieved through the use of subscriptions.
533
-
534
- IoTAs using this plugins should also define a notification handler to handle incoming values. This handler will be
535
- intercepted by the plugin, so the mapped values are included in the updated notification.
536
-
537
- When a device is provisioned with bidirectional attributes, the IoTAgent subscribes to changes in that attribute. When a
538
- change notification for that attribute arrives to the IoTA, it applies the transformation defined in the device
539
- provisioning payload to the notification, and calls the underlying notification handler with the transformed entity including the `value` along with any `metadata`, and in the case of an NGSI-LD bidirectional attribute a `datasetId` if provided.
540
-
541
- The following `attributes` section shows an example of the plugin configuration (using `IOTA_AUTOCAST=false` to avoid
542
- translation from geo:point to geo:json)
543
-
544
- ```json
545
- "attributes": [
546
- {
547
- "name":"location",
548
- "type":"geo:point",
549
- "expression": "${@latitude}, ${@longitude}",
550
- "reverse": [
551
- {
552
- "object_id":"longitude",
553
- "type": "Text",
554
- "expression": "${trim(substr(@location, indexOf(@location, \",\") + 1, length(@location)))}"
555
- },
556
- {
557
- "object_id":"latitude",
558
- "type": "Text",
559
- "expression": "${trim(substr(@location, 0, indexOf(@location, \",\")))}"
560
- }
561
- ]
562
- }
563
- ],
564
- ```
565
-
566
- For each attribute that would have bidirectionality, a new field `reverse` must be configured. This field will contain
567
- an array of fields that will be created based on the notifications content. The expression notification can contain any
568
- attribute of the same entity as the bidirectional attribute; declaring them in the expressions will add them to the
569
- subscription payload.
570
-
571
- For each attribute in the `reverse` array, an expression must be defined to calculate its value based on the
572
- notification attributes. This value will be passed to the underlying protocol with the `object_id` name. Details about
573
- how the value is then progressed to the device are protocol-specific.
574
-
575
- ##### NGSI-LD `datasetId` support
576
-
577
- Limited support for parsing the NGSI-LD `datasetId` attribute is included within the library. A series of sequential commands for a single attribute can be sent as an NGSI-LD notification as follows:
578
-
579
-
580
- ```json
581
- {
582
- "id": "urn:ngsi-ld:Notification:5fd0fa684eb81930c97005f3",
583
- "type": "Notification",
584
- "subscriptionId": "urn:ngsi-ld:Subscription:5fd0f69b4eb81930c97005db",
585
- "notifiedAt": "2020-12-09T16:25:12.193Z",
586
- "data": [
587
- {
588
- "lampColor": [
589
- {
590
- "type": "Property",
591
- "value": { "color": "green", "duration": "55 secs"},
592
- "datasetId": "urn:ngsi-ld:Sequence:do-this"
593
- },
594
- {
595
- "type": "Property",
596
- "value": {"color": "red", "duration": "10 secs"},
597
- "datasetId": "urn:ngsi-ld:Sequence:then-do-this"
598
- }
599
- ]
600
- }
601
- ]
602
- }
603
- ```
604
-
605
- This results in the following sequential array of attribute updates to be sent to the `NotificationHandler` of the IoT Agent itself:
606
-
607
- ```json
608
- [
609
- {
610
- "name": "lampColor",
611
- "type": "Property",
612
- "datasetId": "urn:ngsi-ld:Sequence:do-this",
613
- "metadata": {},
614
- "value": { "color": "green", "duration": "55 secs"}
615
- },
616
- {
617
- "name": "lampColor",
618
- "type": "Property",
619
- "datasetId": "urn:ngsi-ld:Sequence:then-do-this",
620
- "metadata": {},
621
- "value": {"color": "red", "duration": "10 secs"}
622
- }
623
- ]
624
- ```
625
-
626
- A `datasetId` is also maintained for each new attribute defined in the `reverse` field.