iotagent-node-lib 4.7.0 → 4.9.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 (110) hide show
  1. package/.github/workflows/ci.yml +6 -3
  2. package/.nyc_output/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  3. package/.nyc_output/processinfo/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  4. package/.nyc_output/processinfo/index.json +1 -0
  5. package/CHANGES_NEXT_RELEASE +1 -0
  6. package/Changelog +793 -0
  7. package/doc/README.md +1 -0
  8. package/doc/admin.md +8 -4
  9. package/doc/api.md +8 -4
  10. package/doc/devel/northboundinteractions.md +122 -15
  11. package/doc/models/models.md +260 -0
  12. package/doc/requirements.txt +1 -1
  13. package/docker/Mosquitto/Dockerfile +1 -1
  14. package/lib/commonConfig.js +11 -2
  15. package/lib/fiware-iotagent-lib.js +15 -11
  16. package/lib/jexlTranformsMap.js +182 -35
  17. package/lib/model/Command.js +9 -0
  18. package/lib/model/Device.js +1 -0
  19. package/lib/services/commands/commandRegistryMongoDB.js +15 -1
  20. package/lib/services/commands/commandService.js +3 -3
  21. package/lib/services/common/domain.js +4 -3
  22. package/lib/services/devices/deviceRegistryMongoDB.js +1 -1
  23. package/lib/services/devices/deviceService.js +11 -2
  24. package/lib/services/devices/devices-NGSI-LD.js +1 -2
  25. package/lib/services/devices/devices-NGSI-v2.js +1 -2
  26. package/lib/services/ngsi/entities-NGSI-LD.js +69 -13
  27. package/lib/services/ngsi/ngsiService.js +3 -1
  28. package/lib/services/northBound/contextServer-NGSI-LD.js +5 -3
  29. package/lib/services/northBound/contextServer-NGSI-v2.js +12 -3
  30. package/lib/services/northBound/contextServer.js +2 -1
  31. package/lib/services/northBound/northboundServer.js +2 -1
  32. package/lib/services/northBound/restUtils.js +6 -2
  33. package/lib/templates/createDevice.json +4 -0
  34. package/lib/templates/updateDevice.json +4 -0
  35. package/package.json +1 -1
  36. package/test/functional/config-test.js +1 -1
  37. package/test/functional/testUtils.js +13 -2
  38. package/test/unit/expressions/jexlExpression-test.js +165 -1
  39. package/test/unit/ngsi-ld/examples/contextRequests/createDatetimeProvisionedDevice.json +1 -4
  40. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json +3 -12
  41. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json +3 -12
  42. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +6 -24
  43. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +1 -4
  44. package/test/unit/ngsi-ld/examples/contextRequests/updateContext1.json +1 -4
  45. package/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json +1 -4
  46. package/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json +1 -4
  47. package/test/unit/ngsi-ld/examples/contextRequests/updateContext5.json +1 -4
  48. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +1 -4
  49. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin7.json +1 -4
  50. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin8.json +4 -2
  51. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin9.json +1 -4
  52. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandError.json +3 -9
  53. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandExpired.json +13 -19
  54. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandFinish.json +13 -19
  55. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandStatus1.json +4 -9
  56. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json +1 -4
  57. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json +1 -4
  58. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +1 -4
  59. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin2.json +2 -8
  60. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +1 -4
  61. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4.json +2 -8
  62. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +3 -12
  63. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin6.json +3 -5
  64. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +1 -4
  65. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +1 -4
  66. package/test/unit/ngsi-ld/examples/contextRequests/updateContextJsonProperty.json +13 -0
  67. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListProperty.json +14 -0
  68. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListRelationship.json +14 -0
  69. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin1.json +2 -8
  70. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin2.json +2 -8
  71. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin3.json +3 -8
  72. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin4.json +1 -4
  73. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +2 -8
  74. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +2 -8
  75. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +2 -8
  76. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -4
  77. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -4
  78. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -8
  79. package/test/unit/ngsi-ld/examples/contextRequests/updateContextRelationship.json +11 -0
  80. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType1.json +51 -0
  81. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType2.json +65 -0
  82. package/test/unit/ngsi-ld/examples/contextRequests/updateContextVocabProperty.json +11 -0
  83. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionCommands1.json +2 -8
  84. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionDeviceStatic.json +1 -4
  85. package/test/unit/ngsi-ld/general/startup-test.js +3 -0
  86. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -1
  87. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +71 -1
  88. package/test/unit/ngsi-ld/ngsiService/attributeTypes-test.js +293 -0
  89. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +0 -10
  90. package/test/unit/ngsi-ld/ngsiService/value-types-test.js +221 -0
  91. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast1.json +0 -11
  92. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json +0 -14
  93. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast2.json +0 -11
  94. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast3.json +0 -11
  95. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast4.json +0 -11
  96. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast5.json +0 -7
  97. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast6.json +0 -17
  98. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast7.json +0 -19
  99. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json +0 -14
  100. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json +0 -14
  101. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +0 -109
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +0 -8
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +0 -8
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +0 -8
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +0 -8
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +0 -8
  107. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +0 -8
  108. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +0 -8
  109. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +0 -326
  110. /package/test/unit/ngsi-ld/examples/contextRequests/{updateContextLanguageProperties1.json → updateContextLanguageProperty.json} +0 -0
package/doc/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - [Administration manual](admin.md)
8
8
  - [Deprecated features](deprecated.md)
9
9
  - [Roadmap](roadmap.md)
10
+ - [Data models](models/models.md)
10
11
 
11
12
  ## Development documentation
12
13
 
package/doc/admin.md CHANGED
@@ -145,7 +145,8 @@ used by default. E.g.:
145
145
  When connected to an **NGSI-LD** context broker, an IoT Agent is able to indicate whether it is willing to accept `null`
146
146
  values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these values to
147
147
  `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT Agent does not
148
- support nulls or multi-attribute requests if they are encountered.
148
+ support nulls or multi-attribute requests if they are encountered. It is also possible to pass on attribute datatypes
149
+ using `@type` or `valueType` if desired.
149
150
 
150
151
  ```javascript
151
152
  {
@@ -153,7 +154,8 @@ support nulls or multi-attribute requests if they are encountered.
153
154
  port: 4041,
154
155
  ldSupport : {
155
156
  null: true,
156
- datasetId: true
157
+ datasetId: true,
158
+ datatype: 'valueType'
157
159
  }
158
160
  }
159
161
  ```
@@ -446,8 +448,9 @@ For example in a device document stored in MongoDB will be extended with a subdo
446
448
 
447
449
  #### `useCBflowControl`
448
450
 
449
- If this flag is activated, when iotAgent invokes Context Broker will use [flowControl option](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/admin/perf_tuning.md#updates-flow-control-mechanism). This flag is overwritten by
450
- `useCBflowControl` flag in group or device. This flag is disabled by default.
451
+ If this flag is activated, when iotAgent invokes Context Broker will use
452
+ [flowControl option](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/admin/perf_tuning.md#updates-flow-control-mechanism).
453
+ This flag is overwritten by `useCBflowControl` flag in group or device. This flag is disabled by default.
451
454
 
452
455
  ### Configuration using environment variables
453
456
 
@@ -465,6 +468,7 @@ overrides.
465
468
  | IOTA_CB_NGSI_VERSION | `contextBroker.ngsiVersion` |
466
469
  | IOTA_NORTH_HOST | `server.host` |
467
470
  | IOTA_NORTH_PORT | `server.port` |
471
+ | IOTA_LD_SUPPORT_DATA_TYPE | `server.ldSupport.datatype` |
468
472
  | IOTA_LD_SUPPORT_NULL | `server.ldSupport.null` |
469
473
  | IOTA_LD_SUPPORT_DATASET_ID | `server.ldSupport.datasetId` |
470
474
  | IOTA_PROVIDER_URL | `providerUrl` |
package/doc/api.md CHANGED
@@ -165,7 +165,8 @@ parameters defined at device level in database, the parameters are inherit from
165
165
 
166
166
  ### Uniqueness of groups and devices
167
167
 
168
- Group uniqueness is defined by the combination of: resource and apikey. This is so because given a measure (identified by an apikey) there is no way to identify group related if apikey is not unique for all services and subservices.
168
+ Group uniqueness is defined by the combination of: resource and apikey. This is so because given a measure (identified
169
+ by an apikey) there is no way to identify group related if apikey is not unique for all services and subservices.
169
170
 
170
171
  Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
171
172
  with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
@@ -256,6 +257,9 @@ Additionally for commands (which are attributes of type `command`) the following
256
257
  particular IOTAs documentation for allowed values of this field in each case.
257
258
  - **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of
258
259
  transports)
260
+ - **headers**: extra customer headers used when send command by HTTP transport (ignored in other kinds of
261
+ transports)
262
+ Check full detail of these fields in [comand-transformations](https://github.com/telefonicaid/iotagent-json/blob/master/docs/usermanual.md#commands-transformations)
259
263
 
260
264
  Note that, when information coming from devices, this means measures, are not defined neither in the group, nor in the
261
265
  device, the IoT agent will store that information into the destination entity using the same attribute name than the
@@ -579,8 +583,8 @@ Case 5:
579
583
 
580
584
  depending on the JEXL expression evaluation:
581
585
 
582
- - If it evaluates to `true` every measure will be propagated to NGSI interface (as in case 1)
583
- - If it evaluates to `false` just measures defined in active, static (plus conditionally TimeInstant) will be
586
+ - If it evaluates to `false` every measure will be propagated to NGSI interface (as in case 1)
587
+ - If it evaluates to `true` just measures defined in active, static (plus conditionally TimeInstant) will be
584
588
  propagated to NGSI interface (as in case 2)
585
589
  - If it evaluates to an array just measures defined in the array (identified by their attribute names, not by their
586
590
  object_id) will be will be propagated to NGSI interface (as in case 3)
@@ -2459,4 +2463,4 @@ updateEntityRequestsError 5
2459
2463
  [18]:
2460
2464
  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
2461
2465
  [99]:
2462
- 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
2466
+ 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=%20%7B%0A%20%20%20%20jsonparse%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation(JSON.parse)(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20jsonstringify%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation(JSON.stringify)(val)%3B%0A%20%20%20%20%7D%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%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v))(arr)%3B%0A%20%20%20%20%7D%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%20Array.isArray%2C%0A%20%20%20%20isnan%3A%20isNaN%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeParseNumber%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20result%20%3D%20fn(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(result)%20%3F%20null%20%3A%20result%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeParseNumber((val)%20%3D%3E%20parseInt(val%2C%2010))(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeParseNumber%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20result%20%3D%20fn(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(result)%20%3F%20null%20%3A%20result%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeParseNumber(parseFloat)(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeDateOperation%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20date%20%3D%20new%20Date(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(date.getTime())%20%3F%20null%20%3A%20fn(date)%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeDateOperation((date)%20%3D%3E%20date.toISOString())(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20timeoffset%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeDateOperation%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20date%20%3D%20new%20Date(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(date.getTime())%20%3F%20null%20%3A%20fn(date)%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeDateOperation((date)%20%3D%3E%20date.getTimezoneOffset())(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((val)%20%3D%3E%20val.toString())(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20urlencode%3A%20encodeURI%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation(decodeURI)(val)%3B%0A%20%20%20%20%7D%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%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to))(str%2C%20reg%2C%20to)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg%2C%20'g')%2C%20to))(str%2C%20reg%2C%20to)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20joinarrtostr%3A%20(arr%2C%20ch)%20%3D%3E%20arr.join(ch)%2C%0A%20%20%20%20concatarr%3A%20(arr%2C%20arr2)%20%3D%3E%20arr.concat(arr2)%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%5Bvalues.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20!%3D%3D%20null%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20i%20%3A%20null)%2C%20null)%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)%2C%0A%20%20%20%20addset%3A%20(arr%2C%20x)%20%3D%3E%20Array.from(new%20Set(arr).add(x))%2C%0A%20%20%20%20removeset%3A%20(arr%2C%20x)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20s%20%3D%20new%20Set(arr)%3B%0A%20%20%20%20%20%20%20%20s.delete(x)%3B%0A%20%20%20%20%20%20%20%20return%20Array.from(s)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20touppercase%3A%20(val)%20%3D%3E%20String(val).toUpperCase()%2C%0A%20%20%20%20tolowercase%3A%20(val)%20%3D%3E%20String(val).toLowerCase()%2C%0A%20%20%20%20floor%3A%20Math.floor%2C%0A%20%20%20%20ceil%3A%20Math.ceil%2C%0A%20%20%20%20round%3A%20Math.round%2C%0A%20%20%20%20tofixed%3A%20(val%2C%20decimals)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20num%20%3D%20Number.parseFloat(val)%3B%0A%20%20%20%20%20%20%20%20const%20dec%20%3D%20Number.parseInt(decimals)%3B%0A%20%20%20%20%20%20%20%20return%20isNaN(num)%20%7C%7C%20isNaN(dec)%20%3F%20null%20%3A%20num.toFixed(dec)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20gettime%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeDateOperation%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20date%20%3D%20new%20Date(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(date.getTime())%20%3F%20null%20%3A%20fn(date)%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeDateOperation((date)%20%3D%3E%20date.getTime())(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20toisostring%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeDateOperation%20%3D%20(fn)%20%3D%3E%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20date%20%3D%20new%20Date(val)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20isNaN(date.getTime())%20%3F%20null%20%3A%20fn(date)%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeDateOperation((date)%20%3D%3E%20date.toISOString())(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20localestring%3A%20(d%2C%20timezone%2C%20options)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((d%2C%20timezone%2C%20options)%20%3D%3E%20new%20Date(d).toLocaleString(timezone%2C%20options))(d%2C%20timezone%2C%20options)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20now%3A%20Date.now%2C%0A%20%20%20%20hextostring%3A%20(val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20safeOperation%20%3D%20(fn)%20%3D%3E%20(...args)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20fn(...args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20return%20safeOperation((val)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(typeof%20val%20!%3D%3D%20'string'%20%7C%7C%20!%2F%5E%5B0-9a-fA-F%5D%2B%24%2F.test(val)%20%7C%7C%20val.length%20%25%202%20!%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20new%20TextDecoder().decode(new%20Uint8Array(val.match(%2F.%7B1%2C2%7D%2Fg).map((byte)%20%3D%3E%20parseInt(byte%2C%2016))))%3B%0A%20%20%20%20%20%20%20%20%7D)(val)%3B%0A%20%20%20%20%7D%2C%0A%20%20%20%20valuePicker%3A%20(val%2C%20pick)%20%3D%3E%0A%20%20%20%20%20%20%20%20Object.entries(val)%0A%20%20%20%20%20%20%20%20%20%20%20%20.filter((%5B%2C%20v%5D)%20%3D%3E%20v%20%3D%3D%3D%20pick)%0A%20%20%20%20%20%20%20%20%20%20%20%20.map((%5Bk%5D)%20%3D%3E%20k)%2C%0A%20%20%20%20valuePickerMulti%3A%20(val%2C%20pick)%20%3D%3E%0A%20%20%20%20%20%20%20%20Object.entries(val)%0A%20%20%20%20%20%20%20%20%20%20%20%20.filter((%5B%2C%20v%5D)%20%3D%3E%20pick.includes(v))%0A%20%20%20%20%20%20%20%20%20%20%20%20.map((%5Bk%5D)%20%3D%3E%20k)%0A%7D
@@ -775,6 +775,8 @@ The registration of the commands is performed once in the lifetime of the Device
775
775
 
776
776
  #### Command Execution
777
777
 
778
+ ##### Based in update (classic way)
779
+
778
780
  Scenario 3 begins with the request for a command from the User to the Context Broker (P1):
779
781
 
780
782
  ```bash
@@ -825,21 +827,8 @@ Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
825
827
  }
826
828
  ```
827
829
 
828
- The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with the following payload
829
- (200 OK):
830
-
831
- ```json
832
- [
833
- {
834
- "type": "device",
835
- "id": "Dev0001",
836
- "switch": {
837
- "type": "command",
838
- "value": ""
839
- }
840
- }
841
- ]
842
- ```
830
+ The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with a 204 OK (without
831
+ payload).
843
832
 
844
833
  This response just indicates that the IoT Agent has received the command successfully, and gives no information about
845
834
  the requested information or command execution.
@@ -862,6 +851,100 @@ The Context Broker, forwards the same response to the user, thus replying the or
862
851
  At this point, the command has been issued to the IoTAgent and the User doesn't still know what the result of its
863
852
  request will be.
864
853
 
854
+ ##### Based in notification (new way)
855
+
856
+ A new way to ContextBroker provides a command to a IoTAgent is through notifications. In this case CB notify the command
857
+ to the IotAgent with a request like the following:
858
+
859
+ ```bash
860
+ POST /notify HTTP/1.1
861
+ Host: <target-host>:<northbound_port>
862
+ fiware-service: workshop
863
+ Fiware-ServicePath: /iota2ngsi
864
+ Accept: application/json
865
+ Content-length: 290
866
+ Content-type: application/json; charset=utf-8
867
+ Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
868
+
869
+ {
870
+ "subscriptionId": "60b0cedd497e8b681d40b58e",
871
+ "data": [{
872
+ "id": "123456abcdefg",
873
+ "type": "switchOnOffExecution",
874
+ "targetEntityId": {
875
+ "type": "Text",
876
+ "value": "Dev0001",
877
+ "metadata": {}
878
+ },
879
+ "targetEntityType": {
880
+ "type": "Text",
881
+ "value": "device",
882
+ "metadata": {}
883
+ },
884
+ "execTs": {
885
+ "type": "DateTime",
886
+ "value": "2020-05-27T00:00:00.000Z",
887
+ "metadata": {}
888
+ },
889
+ "cmd": {
890
+ "type": "Text",
891
+ "value": "switch",
892
+ "metadata": {}
893
+ },
894
+ "params": {
895
+ "type": "Text",
896
+ "value": "54, 12",
897
+ "metadata": {}
898
+ },
899
+ "status": {
900
+ "type": "Text",
901
+ "value": "FORWARDED",
902
+ "metadata": {}
903
+ },
904
+ "info": {
905
+ "type": "Text",
906
+ "value": null,
907
+ "metadata": {}
908
+ },
909
+ "onDelivered": {
910
+ "type": "Request",
911
+ "value": {
912
+ }
913
+ },
914
+ "onOk": {
915
+ "type": "Request",
916
+ "value": {
917
+ }
918
+ },
919
+ "onError": {
920
+ "type": "Request",
921
+ "value": {
922
+ }
923
+ },
924
+ "onInfo": {
925
+ "type": "Request",
926
+ "value": {
927
+ }
928
+ },
929
+ "cmdExecution": {
930
+ "type": "value",
931
+ "value": true,
932
+ "metadata": {}
933
+ },
934
+ "dateExpiration": {
935
+ "type": "DateTime",
936
+ "value": "2020-05-27T20:00:00.000Z",
937
+ "metadata": {}
938
+ }
939
+ }]
940
+ }
941
+ ```
942
+
943
+ In this case relevant fields are just `targetEntityId`, `targetEntityType`, `cmd` and `params`.
944
+
945
+ The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with a 204 OK (without
946
+ payload).
947
+
865
948
  #### Result reporting
866
949
 
867
950
  Once the IoT Agent has executed the command or retrieved the information from the device, it reports the results to the
@@ -955,6 +1038,30 @@ The Context Broker replies with all the desired data, in R2 format (200 OK):
955
1038
  ]
956
1039
  ```
957
1040
 
1041
+ #### Differences regarding the new commands mode
1042
+
1043
+ A new commands flow has been defined (involving also modifications at ContextBroker). As part of that design, commands
1044
+ are not sent to IOTAs using NGSIv2 notifications, but the current implementation has some differences regarding the
1045
+ desired behaviour, which are described next:
1046
+
1047
+ - Fields others than `targetEntityId`, `targetEntityType`, `cmd` and `params` (i.e. `execTs`, `status`, `info`,
1048
+ `onDelivered`, `onOk`, `onError`, `onInfo`, `cmdExecution` and `dataExpiration`), are not actually used. By the
1049
+ moment they are stored in the commands model (commands collection) but nothing is done with them appart from
1050
+ storing.
1051
+ - The "Result reporting" should not be a "hardwired" behaviour updating the entity associated to the device, but using
1052
+ the corresponding `on*` attribute in the notificaiton (e.g. `onOk` in the case of success). That attribute would
1053
+ typically be a [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) object like this
1054
+ `"onOk": { "href": "/v2/entities/123456abcdefg/attrs/status?type=switchExecution", "method": "PUT" }`. Moreover, the
1055
+ entity to be updated in that HATEOAS would be the transient entity corresponding to command execuion, not the entity
1056
+ associated to the device.
1057
+
1058
+ ```
1059
+ PUT /v2/entities/123456abcdefg/attrs/status?type=switchExecution
1060
+ content-type: text/plain
1061
+
1062
+ OK
1063
+ ```
1064
+
958
1065
  ### Scenario 3: commands (error)
959
1066
 
960
1067
  In Scenario 3, errors can happen asynchronously, out of the main interactions. When the IoTAgent detects an error
@@ -0,0 +1,260 @@
1
+ # MongoDB Datamodel
2
+
3
+ This file displays IoT Agent datamodel stored in the MongoDB database.
4
+
5
+ ## Collections
6
+
7
+ ### Devices
8
+
9
+ The collection 'devices' sotres information about the iotagent devices
10
+
11
+ Fields:
12
+
13
+ - **\_ID** _ObjectId_: unique object ID used by mongoDB
14
+ - **id** _string_: id of device
15
+ - **type** _string_: entity type used
16
+ - **name** _string_: entity name used
17
+ - **lazy** _array_: array of lazy attributes of device
18
+ - **active** _array_: array of active attributes of device
19
+ - **commands** _array_: array of commands of device
20
+ - **apikey** _string_: apikey of device
21
+ - **endpoint** _string_: endpoint used by push commands when http
22
+ - **resource** _string_: iotagent resource
23
+ - **protocol** _string_: device protocol (JSON)
24
+ - **transport** _string_: device transport (http, mqtt, amqp)
25
+ - **staticAttributes** _array_: array of static attributes of device
26
+ - **subscriptions** _array_: subscriptions of device
27
+ - **service** _string_: service which the device belongs to
28
+ - **subservice** _string_: subservice which the rule belongs to.
29
+ - **polling** _boolean_: if device uses polling for commands
30
+ - **timezone** _string_: timezone of device
31
+ - **timestamp** _boolean_ timestamp of device
32
+ - **registrationId** _string_: registrationId of device
33
+ - **internalId** _string_: internalId of device
34
+ - **creationDate** _date_: creationDate of device
35
+ - **internalAttributes** _object_: internalAttributes of device
36
+ - **autoprovision** _boolean_: if device support autoprovision
37
+ - **explicitAttrs** _enum_:
38
+ - **ngsiVersion** _string_: ngsi version used by device
39
+ - **payloadType** _string_: payloadType used by device
40
+ - **useCBflowControl** _boolean_: if CBFlow will be used by device when updates in CB
41
+ - **storeLastMeasure** _boolean_: if device store last measure received
42
+ - **lastMeasure** _object_: last measure received by device
43
+ - **oldCtxt** _object_: jexl Ctxt used at last measure
44
+
45
+ Example:
46
+
47
+ ```json
48
+ {
49
+ "_id": {
50
+ "$oid": "680b4b338d0e60f98718a8b2"
51
+ },
52
+ "lazy": [],
53
+ "commands": [
54
+ {
55
+ "name": "reset",
56
+ "type": "command",
57
+ "value": "",
58
+ "expression": "{ set: brand + '_' + reset }",
59
+ "object_id": "reset"
60
+ },
61
+ {
62
+ "name": "cmd1",
63
+ "type": "command",
64
+ "value": "",
65
+ "expression": "brand",
66
+ "object_id": "cmd1"
67
+ },
68
+ {
69
+ "name": "cmd2",
70
+ "type": "command",
71
+ "value": "",
72
+ "expression": "reset",
73
+ "object_id": "cmd2"
74
+ }
75
+ ],
76
+ "staticAttributes": [],
77
+ "creationDate": {
78
+ "$date": {
79
+ "$numberLong": "1745570611491"
80
+ }
81
+ },
82
+ "id": "disp",
83
+ "type": "thing",
84
+ "name": "thing:disp",
85
+ "service": "smartcity",
86
+ "subservice": "/",
87
+ "registrationId": "680b4b33956cc1ed0205840a",
88
+ "apikey": "APIKEY",
89
+ "protocol": "IoTA-JSON",
90
+ "transport": "HTTP",
91
+ "polling": false,
92
+ "active": [],
93
+ "oldCtxt": {
94
+ "level": "33",
95
+ "brand": "o1",
96
+ "id": "disp",
97
+ "type": "thing",
98
+ "service": "smartcity",
99
+ "subservice": "/",
100
+ "entity_name": "thing:disp",
101
+ "TimeInstant": "2025-04-25T08:43:31.496Z"
102
+ },
103
+ "subscriptions": []
104
+ }
105
+ ```
106
+
107
+ ### Groups
108
+
109
+ The collection groups' stores information about the iotagent groups of devices
110
+
111
+ Fields:
112
+
113
+ - **\_ID** _ObjectId_: unique object ID used by mongoDB
114
+ - **url** _string_: url used by group of devices
115
+ - **resource** _string_: iotagent resource
116
+ - **apikey** _string_: apikey used by group of devices
117
+ - **endpoint** _string_: endpoint used by push commands when http
118
+ - **transport** _string_: group transport (http, mqtt, amqp)
119
+ - **type** _string_: entity type used
120
+ - **service** _string_: service which the group of device belongs to
121
+ - **subservice** _string_: subservice which the group of devices belongs to
122
+ - **description** _string_: description of group of devices
123
+ - **trust** _string_: keystone trust id used when devices of this group request to CB
124
+ - **cbHost** _string_: CB endpoint used by devices of this group
125
+ - **timezone** _string_: timezone used by group of devices
126
+ - **timestamp** _boolean_: timestamp of group
127
+ - **commands** _array_: array of commands of device group
128
+ - **staticAttributes** _array_: array of static attributes of device group
129
+ - **lazy** _array_: array of lazy attributes of device group
130
+ - **attributes** _array_: array of active attributes of device group
131
+ - **internalAttributes** _array_: array of internal attributes used by devices of group
132
+ - **autoprovision** _boolean_: if devices of group supports autoprovision
133
+ - **explicitAttrs** _enum_: explicit attributes configuration used by devices of group
134
+ - **defaultEntityNameConjunction** _string_:
135
+ - **ngsiVersion** _string_: ngsi version used by devices of group
136
+ - **entityNameExp** _string_: entity name expression used by devics of group
137
+ - **payloadType** _string_: payloadType used by devices of group
138
+ - **useCBflowControl** _boolean_: payloadType used by device group
139
+ - **storeLastMeasure** _boolean_: if devices of group store last measure received
140
+
141
+ Example:
142
+
143
+ ```json
144
+ {
145
+ "_id": {
146
+ "$oid": "67a1e6447ae8b4ba4478f019"
147
+ },
148
+ "commands": [
149
+ {
150
+ "name": "reset",
151
+ "type": "command",
152
+ "value": "",
153
+ "expression": "{ set: brand + '_' + reset }"
154
+ },
155
+ {
156
+ "name": "cmd1",
157
+ "type": "command",
158
+ "value": "",
159
+ "expression": "brand"
160
+ },
161
+ {
162
+ "name": "cmd2",
163
+ "type": "command",
164
+ "value": "",
165
+ "expression": "reset"
166
+ }
167
+ ],
168
+ "staticAttributes": [
169
+ {
170
+ "name": "brand",
171
+ "type": "Text",
172
+ "value": "o1",
173
+ "metadata": {}
174
+ }
175
+ ],
176
+ "attributes": [],
177
+ "resource": "/iot/json",
178
+ "apikey": "APIKEY",
179
+ "type": "thing",
180
+ "service": "smartcity",
181
+ "subservice": "/",
182
+ "description": "miJSON",
183
+ "timestamp": true,
184
+ "internalAttributes": [],
185
+ "lazy": [],
186
+ "transport": "HTTP",
187
+ "endpoint": "'https://eoykcmmm.m.pipedream.net' + '/' + service + '/' + subservice + '/' + id + '/' + type"
188
+ }
189
+ ```
190
+
191
+ ### Commands
192
+
193
+ The collection 'commands' stores information about the commands
194
+
195
+ Fields:
196
+
197
+ - **\_ID** _ObjectId_: unique object ID used by mongoDB
198
+ - **deviceId** _string_: device ID of the device
199
+ - **type** _string_: type of the command
200
+ - **name** _string_: name of the command
201
+ - **value** _object_: value of the command
202
+ - **service** _string_: service which the device command belongs to
203
+ - **subservice** _string_: subservice which the device command belongs to
204
+ - **execTs** _date_: related with new commands functionality (stored but not yet in use)
205
+ - **status** _string_: related with new commands functionality (stored but not yet in use)
206
+ - **info** _string_: related with new commands functionality (stored but not yet in use)
207
+ - **onDelivered** _Object_: related with new commands functionality (stored but not yet in use)
208
+ - **onOk**: _Object_: related with new commands functionality (stored but not yet in use)
209
+ - **onError** _Object_: related with new commands functionality (stored but not yet in use)
210
+ - **onInfo** _Object_: related with new commands functionality (stored but not yet in use)
211
+ - **cmdExecution** \_Boolean\_\_: related with new commands functionality (stored but not yet in use)
212
+ - **dateExpiration**: { type: Date }: related with new commands functionality (stored but not yet in use)
213
+ - **creationDate** _date_: creation date of command
214
+
215
+ Example:
216
+
217
+ ```json
218
+ {
219
+ "_id": {
220
+ "$oid": "680b4b538d0e60f98718a8eb"
221
+ },
222
+ "creationDate": {
223
+ "$date": {
224
+ "$numberLong": "1745570643252"
225
+ }
226
+ },
227
+ "name": "cmd1",
228
+ "type": "command",
229
+ "value": "on",
230
+ "deviceId": "disp3",
231
+ "service": "smartcity",
232
+ "subservice": "/"
233
+ }
234
+ ```
235
+
236
+ ## Indexes
237
+
238
+ ### Devices
239
+
240
+ An index guarantees that every device is identified by the tuple (service, subservice, apikey, id)
241
+
242
+ The index is created/ensured when iotagent starts, but it can be created from a mongoDB shell with
243
+
244
+ ```javascript
245
+ db.devices.ensureIndex({ service: 1, subservice: 1, apikey: 1, id: 1 }, { unique: true });
246
+ ```
247
+
248
+ ### Groups
249
+
250
+ An index guarantees that every group is identified by the tuple (apikey, resource)
251
+
252
+ The index is created/ensured when iotagent starts, but it can be created from a mongoDB shell with
253
+
254
+ ```javascript
255
+ db.groups.ensureIndex({ apikey: 1, resource: 1 }, { unique: true });
256
+ ```
257
+
258
+ ### Commands
259
+
260
+ None index is defined
@@ -1,4 +1,4 @@
1
1
  mkdocs==1.2.4
2
2
  Pygments==2.15.0
3
3
  Markdown==3.3.4
4
- jinja2==3.1.5
4
+ jinja2==3.1.6
@@ -1,4 +1,4 @@
1
- ARG IMAGE_TAG=12.6-slim
1
+ ARG IMAGE_TAG=12.10-slim
2
2
  FROM debian:${IMAGE_TAG}
3
3
 
4
4
  ARG CLEAN_DEV_TOOLS
@@ -157,6 +157,7 @@ function processEnvironmentVariables() {
157
157
  'IOTA_FALLBACK_PATH',
158
158
  'IOTA_LD_SUPPORT_NULL',
159
159
  'IOTA_LD_SUPPORT_DATASET_ID',
160
+ 'IOTA_LD_SUPPORT_DATA_TYPE',
160
161
  'IOTA_EXPRESS_LIMIT',
161
162
  'IOTA_USE_CB_FLOW_CONTROL',
162
163
  'IOTA_STORE_LAST_MEASURE'
@@ -264,7 +265,12 @@ function processEnvironmentVariables() {
264
265
  config.server.port = process.env.IOTA_NORTH_PORT;
265
266
  }
266
267
 
267
- config.server.ldSupport = config.server.ldSupport || { null: true, datasetId: true, merge: false };
268
+ config.server.ldSupport = config.server.ldSupport || {
269
+ null: true,
270
+ datasetId: true,
271
+ merge: false,
272
+ dataType: 'none'
273
+ };
268
274
 
269
275
  if (process.env.IOTA_LD_SUPPORT_NULL) {
270
276
  config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
@@ -275,6 +281,9 @@ function processEnvironmentVariables() {
275
281
  if (process.env.IOTA_LD_SUPPORT_MERGE) {
276
282
  config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
277
283
  }
284
+ if (process.env.IOTA_LD_SUPPORT_DATA_TYPE) {
285
+ config.server.ldSupport.dataType = process.env.IOTA_LD_SUPPORT_DATA_TYPE;
286
+ }
278
287
 
279
288
  if (process.env.IOTA_PROVIDER_URL) {
280
289
  config.providerUrl = process.env.IOTA_PROVIDER_URL;
@@ -511,7 +520,7 @@ function getConfig() {
511
520
  function getConfigForTypeInformation() {
512
521
  // Just return relevant configuration flags
513
522
  // avoid to include server, authentication, mongodb, orion and iotamanger info
514
- let conf = {
523
+ const conf = {
515
524
  timestamp: config.timestamp,
516
525
  defaultResource: config.defaultResource,
517
526
  explicitAttrs: config.explicitAttrs,
@@ -47,17 +47,20 @@ const context = {
47
47
 
48
48
  /* eslint-disable-next-line no-unused-vars */
49
49
  function activateStatLogs(newConfig, callback) {
50
- async.series([
51
- apply(statsRegistry.globalLoad, {
52
- deviceCreationRequests: 0,
53
- deviceRemovalRequests: 0,
54
- measureRequests: 0,
55
- raiseAlarm: 0,
56
- releaseAlarm: 0,
57
- updateEntityRequestsOk: 0,
58
- updateEntityRequestsError: 0
59
- })
60
- ], callback);
50
+ async.series(
51
+ [
52
+ apply(statsRegistry.globalLoad, {
53
+ deviceCreationRequests: 0,
54
+ deviceRemovalRequests: 0,
55
+ measureRequests: 0,
56
+ raiseAlarm: 0,
57
+ releaseAlarm: 0,
58
+ updateEntityRequestsOk: 0,
59
+ updateEntityRequestsError: 0
60
+ })
61
+ ],
62
+ callback
63
+ );
61
64
  }
62
65
 
63
66
  /**
@@ -314,6 +317,7 @@ exports.setDataUpdateHandler = contextServer.setUpdateHandler;
314
317
  exports.setCommandHandler = contextServer.setCommandHandler;
315
318
  exports.setMergePatchHandler = contextServer.setMergePatchHandler;
316
319
  exports.setDataQueryHandler = contextServer.setQueryHandler;
320
+ exports.executeUpdateSideEffects = contextServer.executeUpdateSideEffects;
317
321
  exports.setConfigurationHandler = contextServer.setConfigurationHandler;
318
322
  exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
319
323
  exports.setProvisioningHandler = contextServer.setProvisioningHandler;