iotagent-node-lib 4.9.0 → 4.11.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 (49) hide show
  1. package/.github/workflows/ci.yml +6 -8
  2. package/CHANGES_NEXT_RELEASE +0 -1
  3. package/Changelog +14 -0
  4. package/config.js +2 -1
  5. package/doc/admin.md +17 -20
  6. package/doc/api.md +87 -50
  7. package/doc/deprecated.md +18 -0
  8. package/doc/devel/northboundinteractions.md +213 -113
  9. package/lib/commonConfig.js +12 -1
  10. package/lib/jexlTranformsMap.js +15 -0
  11. package/lib/model/Device.js +3 -1
  12. package/lib/model/Group.js +2 -1
  13. package/lib/model/dbConn.js +57 -58
  14. package/lib/services/common/iotManagerService.js +2 -1
  15. package/lib/services/devices/deviceRegistryMongoDB.js +5 -1
  16. package/lib/services/devices/deviceService.js +17 -1
  17. package/lib/services/devices/devices-NGSI-LD.js +3 -2
  18. package/lib/services/devices/devices-NGSI-mixed.js +16 -0
  19. package/lib/services/devices/devices-NGSI-v2.js +122 -8
  20. package/lib/services/devices/registrationUtils.js +97 -30
  21. package/lib/services/groups/groupRegistryMongoDB.js +2 -1
  22. package/lib/services/ngsi/subscription-NGSI-LD.js +2 -2
  23. package/lib/services/ngsi/subscription-NGSI-mixed.js +3 -3
  24. package/lib/services/ngsi/subscription-NGSI-v2.js +20 -6
  25. package/lib/services/ngsi/subscriptionService.js +2 -2
  26. package/lib/services/northBound/contextServer-NGSI-v2.js +4 -2
  27. package/lib/services/northBound/deviceProvisioningServer.js +6 -3
  28. package/lib/templates/updateDevice.json +12 -0
  29. package/lib/templates/updateDeviceLax.json +4 -0
  30. package/package.json +2 -2
  31. package/test/unit/expressions/jexlExpression-test.js +30 -0
  32. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -2
  33. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -1
  34. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  35. package/test/unit/ngsi-ld/general/https-support-test.js +1 -1
  36. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +6 -6
  37. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands.json +24 -0
  38. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands2.json +24 -0
  39. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands3.json +24 -0
  40. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands4.json +24 -0
  41. package/test/unit/ngsiv2/examples/contextRequests/updateEntity.json +5 -0
  42. package/test/unit/ngsiv2/examples/contextRequests/updateEntity2.json +5 -0
  43. package/test/unit/ngsiv2/examples/contextRequests/updateEntity2b.json +7 -0
  44. package/test/unit/ngsiv2/examples/contextRequests/updateEntity3.json +5 -0
  45. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest.json +4 -2
  46. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest2.json +4 -2
  47. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  48. package/test/unit/ngsiv2/general/https-support-test.js +1 -1
  49. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +245 -2
@@ -346,6 +346,9 @@ queries (and thus P2 and R2 payloads).
346
346
 
347
347
  ![General ](./img/scenario3.png 'Scenario 3: commands')
348
348
 
349
+ **FIXME:** this scenario describes the registration-based commanding mechanism, which is currently deprecated. It should
350
+ be reworked
351
+
349
352
  This scenario requires that the attributes that are going to be requested are marked as provided by the IoT Agent,
350
353
  through a registration process (NGSIv9). Examples of this registration process will be provided in the practical section
351
354
  of this document. It's worth mentioning that Orion Context Broker **will not** store locally any data about attributes
@@ -729,55 +732,62 @@ error, that error must follow the NGSI payloads described in the Scenario 1 erro
729
732
 
730
733
  ### Scenario 3: commands (happy path)
731
734
 
732
- #### Context Provider Registration
735
+ The interactions depend on the command mode (`cmdMode`):
736
+
737
+ - `legacy`
738
+ - `notification`
739
+ - `advancedNotification`
740
+
741
+ The way of setting up Context Broker to IotAgent communication and the interaction between Context Broker and IoTAgent
742
+ when the command is executed depends on the mode (thus specific subsections about it are provided next for the three
743
+ modes). However, the way in which the command result is provided is the same to all modes (so it is described in a
744
+ [common section](#result-reporting)).
733
745
 
734
- Scenario 3 relies on the Context Provider mechanism of the Context Broker. For this scenario to work, the IoTAgent must
746
+ #### `legacy` mode
747
+
748
+ ##### Set up Context Broker to IotAgent comunication mechanism
749
+
750
+ This mode relies on the Context Provider mechanism of the Context Broker. For this scenario to work, the IoTAgent must
735
751
  register its commands for each device, with a request like the following:
736
752
 
737
753
  ```bash
738
754
  curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Fiware-service: workshop" \
739
755
  -H "fiware-servicepath: /iota2ngsi " -H "x-auth-token: <token>" -d '{
740
- "contextRegistrations": [
741
- {
742
- "entities": [
743
- {
744
- "type": "device",
745
- "isPattern": "false",
746
- "id": "Dev0001"
747
- }
748
- ],
749
- "attributes": [
750
- {
751
- "name": "switch",
752
- "type": "command",
753
- "isDomain": "false"
754
- }
755
- ],
756
- "providingApplication": "http://<target-host>:1026/v1"
757
- }
758
- ],
759
- "duration": "P1M"
756
+ "dataProvided": {
757
+ "entities": [
758
+ {
759
+ "id": "Dev0001",
760
+ "type": "Device"
761
+ }
762
+ ],
763
+ "attrs": [ "switch" ]
764
+ },
765
+ "provider": {
766
+ "http": {
767
+ "url": "<value of the IOTA_PROVIDER_URL>"
768
+ }
769
+ }
760
770
  }' "https://<platform-ip>:1026/v2/registrations"
761
771
  ```
762
772
 
763
- If everything has gone OK, the Context Broker will return the following payload:
773
+ If everything has gone OK, the Context Broker will return 201 Created with the ID of the registration in the `Location`
774
+ header:
764
775
 
765
- ```json
766
- {
767
- "duration": "P1M",
768
- "registrationId": "41adf79dc5a0bba830a6f3824"
769
- }
776
+ ```
777
+ HTTP/1.1 201 Created
778
+ Date: ...
779
+ Fiware-Correlator: ...
780
+ Location: /v2/registrations/41adf79dc5a0bba830a6f3824
781
+ Content-Length: 0
770
782
  ```
771
783
 
772
784
  This ID can be used to update the registration in the future.
773
785
 
774
786
  The registration of the commands is performed once in the lifetime of the Device.
775
787
 
776
- #### Command Execution
788
+ ##### Command Execution
777
789
 
778
- ##### Based in update (classic way)
779
-
780
- Scenario 3 begins with the request for a command from the User to the Context Broker (P1):
790
+ Execution begins with the request for a command from the User to the Context Broker (P1):
781
791
 
782
792
  ```bash
783
793
  curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Fiware-Service: workshop" \
@@ -785,7 +795,6 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
785
795
  "entities": [
786
796
  {
787
797
  "type": "device",
788
- "isPattern": "false",
789
798
  "id": "Dev0001",
790
799
  "switch": {
791
800
  "type": "command",
@@ -793,7 +802,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
793
802
  }
794
803
  }
795
804
  ],
796
- "updateAction": "append"
805
+ "updateAction": "update"
797
806
  } ' "https://<platform-ip>:1026/v2/op/update"
798
807
  ```
799
808
 
@@ -823,38 +832,160 @@ Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
823
832
  }
824
833
  }
825
834
  ],
826
- "updateAction" : "append"
835
+ "updateAction" : "update"
827
836
  }
828
837
  ```
829
838
 
830
- The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with a 204 OK (without
831
- payload).
839
+ The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with a 204 No Content
840
+ (without payload).
832
841
 
833
842
  This response just indicates that the IoT Agent has received the command successfully, and gives no information about
834
843
  the requested information or command execution.
835
844
 
836
- The Context Broker, forwards the same response to the user, thus replying the original request (200 OK):
845
+ The Context Broker, forwards the same response to the user, thus replying the original request with 204 No Content.
837
846
 
838
- ```json
839
- [
840
- {
841
- "type": "device",
842
- "id": "Dev0001",
843
- "switch": {
844
- "type": "command",
845
- "value": ""
847
+ At this point, the command has been issued to the IoTAgent and the User doesn't still know what the result of its
848
+ request will be.
849
+
850
+ #### `notification` mode
851
+
852
+ ##### Set up Context Broker to IoTAgent comunication mechanism
853
+
854
+ This mode relies on the notification mechanism of the Context Broker. For this scenario to work, the IoTAgent must
855
+ subscribe its commands for each device, with a request like the following:
856
+
857
+ ```bash
858
+ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Fiware-service: workshop" \
859
+ -H "fiware-servicepath: /iota2ngsi " -H "x-auth-token: <token>" -d '{
860
+ "subject": {
861
+ "entities": [
862
+ {
863
+ "type": "device",
864
+ "id": "Dev0001",
865
+ }
866
+ ],
867
+ "condition": {
868
+ "attrs": [ "switch" ]
846
869
  }
870
+ },
871
+ "notification": {
872
+ "http": {
873
+ "url": "<value of the IOTA_PROVIDER_URL>/notify"
874
+ },
875
+ "attrsFormat": "normalized",
876
+ "attrs": [ "switch" ]
847
877
  }
848
- ]
878
+ }' "https://<platform-ip>:1026/v2/subscriptions"
849
879
  ```
850
880
 
881
+ If everything has gone OK, the Context Broker will return 201 Created with the ID of the subscription in the `Location`
882
+ header:
883
+
884
+ ```
885
+ HTTP/1.1 201 Created
886
+ Date: ...
887
+ Fiware-Correlator: ...
888
+ Location: /v2/subscription/60b0cedd497e8b681d40b58e
889
+ Content-Length: 0
890
+ ```
891
+
892
+ This ID can be used to update the subscription in the future.
893
+
894
+ The subscription of the commands is performed once in the lifetime of the Device.
895
+
896
+ ##### Command Execution
897
+
898
+ As in the legacy mode, execution begins with the request for a command from the User to the Context Broker (P1):
899
+
900
+ ```bash
901
+ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Fiware-Service: workshop" \
902
+ -H "Fiware-ServicePath: /iota2ngsi " -H "X-Auth-Token: <token>" -d '{
903
+ "entities": [
904
+ {
905
+ "type": "device",
906
+ "id": "Dev0001",
907
+ "switch": {
908
+ "type": "command",
909
+ "value": "54, 12"
910
+ }
911
+ }
912
+ ],
913
+ "updateAction": "update"
914
+ } ' "https://<platform-ip>:1026/v2/op/update"
915
+ ```
916
+
917
+ The Context Broker receives this update and detects that it triggers the subscription, so it notifies to the IoT Agent,
918
+ as follows:
919
+
920
+ ```bash
921
+ POST /notify HTTP/1.1
922
+ Host: <target-host>:<northbound_port>
923
+ Fiware-service: workshop
924
+ Fiware-ServicePath: /iota2ngsi
925
+ Accept: application/json
926
+ Content-length: ...
927
+ Content-type: application/json; charset=utf-8
928
+ Ngsiv2-Attrsformat: normalized
929
+ Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
930
+
931
+ {
932
+ "subscriptionId": "60b0cedd497e8b681d40b58e",
933
+ "data": [{
934
+ "id": "Dev0001",
935
+ "type": "device",
936
+ "switch": {
937
+ "type": "command",
938
+ "value": "54, 12",
939
+ "metadata": {}
940
+ }
941
+ }]
942
+ }
943
+ ```
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
+
948
+ This response just indicates that the IoT Agent has received the command successfully, and gives no information about
949
+ the requested information or command execution.
950
+
851
951
  At this point, the command has been issued to the IoTAgent and the User doesn't still know what the result of its
852
952
  request will be.
853
953
 
854
- ##### Based in notification (new way)
954
+ #### `advancedNotification` mode
955
+
956
+ ##### Set up ContextBroker to IOTA comunication mechanism
855
957
 
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:
958
+ The communication mechanism will be based on subscriptions, although a different one than the one used in `notification`
959
+ mode. Note this mode has not been implemented yet, so following should be taken as a draft:
960
+
961
+ ```bash
962
+ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Fiware-Service: workshop" \
963
+ -H "Fiware-ServicePath: /iota2ngsi " -H "X-Auth-Token: <token>" -d '{
964
+ "subject": {
965
+ "entities": [
966
+ {
967
+ "idPattern": ".*",
968
+ "type": "switchExecution"
969
+ }
970
+ ],
971
+ "condition": {
972
+ "expression": {
973
+ "q": "status:FORWARDED;targetEntityType:StreetLight"
974
+ }
975
+ }
976
+ },
977
+ "notification": {
978
+ "http": {
979
+ "url": "<value of the IOTA_PROVIDER_URL>/notify"
980
+ }
981
+ }
982
+ }' "https://<platform-ip>:1026/v2/subscriptions"
983
+ ```
984
+
985
+ ##### Command Execution
986
+
987
+ Command execution involves to create a _command execution entity_ (details are yet to be implemented), which in sequence
988
+ triggers a notification to IoTAgent as this one (draft)
858
989
 
859
990
  ```bash
860
991
  POST /notify HTTP/1.1
@@ -864,6 +995,7 @@ Fiware-ServicePath: /iota2ngsi
864
995
  Accept: application/json
865
996
  Content-length: 290
866
997
  Content-type: application/json; charset=utf-8
998
+ Ngsiv2-Attrsformat: normalized
867
999
  Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
868
1000
 
869
1001
  {
@@ -945,8 +1077,37 @@ In this case relevant fields are just `targetEntityId`, `targetEntityType`, `cmd
945
1077
  The IoT Agent detects the selected attribute is a command, and replies to the Context Broker with a 204 OK (without
946
1078
  payload).
947
1079
 
1080
+ This response just indicates that the IoT Agent has received the command successfully, and gives no information about
1081
+ the requested information or command execution.
1082
+
1083
+ At this point, the command has been issued to the IoTAgent and the User doesn't still know what the result of its
1084
+ request will be.
1085
+
1086
+ As mentioned before, advanced notification mode has not been yet implemented. The following considerations are gap in
1087
+ the current implementation at IoT Agent side that should be addressed:
1088
+
1089
+ - Fields others than `targetEntityId`, `targetEntityType`, `cmd` and `params` (i.e. `execTs`, `status`, `info`,
1090
+ `onDelivered`, `onOk`, `onError`, `onInfo`, `cmdExecution` and `dataExpiration`), are not actually used. By the
1091
+ moment they are stored in the commands model (commands collection) but nothing is done with them appart from
1092
+ storing.
1093
+ - The "Result reporting" should not be a "hardwired" behaviour updating the entity associated to the device, but using
1094
+ the corresponding `on*` attribute in the notificaiton (e.g. `onOk` in the case of success). That attribute would
1095
+ typically be a [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) object like this
1096
+ `"onOk": { "href": "/v2/entities/123456abcdefg/attrs/status?type=switchExecution", "method": "PUT" }`. Moreover, the
1097
+ entity to be updated in that HATEOAS would be the transient entity corresponding to command execuion, not the entity
1098
+ associated to the device.
1099
+
1100
+ ```
1101
+ PUT /v2/entities/123456abcdefg/attrs/status?type=switchExecution
1102
+ content-type: text/plain
1103
+
1104
+ OK
1105
+ ```
1106
+
948
1107
  #### Result reporting
949
1108
 
1109
+ No matter the command mode (legacy or notification based), the result reporting is the same in all cases.
1110
+
950
1111
  Once the IoT Agent has executed the command or retrieved the information from the device, it reports the results to the
951
1112
  Context Broker, with an updateContext (P1):
952
1113
 
@@ -956,7 +1117,6 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
956
1117
  "entities": [
957
1118
  {
958
1119
  "type": "device",
959
- "isPattern": "false",
960
1120
  "id": "Dev0001",
961
1121
  "switch_info": {
962
1122
  "type": "commandResult",
@@ -975,24 +1135,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
975
1135
  This update does not modify the original command attribute, but two auxiliary attributes, that are not provided by the
976
1136
  IoT Agent (usually, those attributes has the same name as the command, with an added suffix).
977
1137
 
978
- The Context Broker replies to the IoT Agent with a R1 payload (200 OK):
979
-
980
- ```json
981
- [
982
- {
983
- "type": "device",
984
- "id": "Dev0001",
985
- "switch_info": {
986
- "type": "commandResult",
987
- "value": ""
988
- },
989
- "switch_status": {
990
- "type": "commandStatus",
991
- "value": ""
992
- }
993
- }
994
- ]
995
- ```
1138
+ The Context Broker replies to the IoT Agent with 204 No Content response (no payload).
996
1139
 
997
1140
  This operation stores the retrieved values locally in the Context Broker, so it can be retrieved with standard NGSI
998
1141
  mechanisms.
@@ -1007,7 +1150,6 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
1007
1150
  -H "Fiware-ServicePath: /iota2ngsi " -H "X-Auth-Token: <token>" -d '{
1008
1151
  "entities": [
1009
1152
  {
1010
- "isPattern": "false",
1011
1153
  "id": "Dev0001",
1012
1154
  "type": "device"
1013
1155
  }
@@ -1038,30 +1180,6 @@ The Context Broker replies with all the desired data, in R2 format (200 OK):
1038
1180
  ]
1039
1181
  ```
1040
1182
 
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
-
1065
1183
  ### Scenario 3: commands (error)
1066
1184
 
1067
1185
  In Scenario 3, errors can happen asynchronously, out of the main interactions. When the IoTAgent detects an error
@@ -1074,7 +1192,6 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
1074
1192
  "entities": [
1075
1193
  {
1076
1194
  "type": "device",
1077
- "isPattern": "false",
1078
1195
  "id": "Dev0001",
1079
1196
  "switch_info":{
1080
1197
  "type": "commandResult",
@@ -1090,23 +1207,6 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
1090
1207
  } ' "https://<platform-ip>:1026/v2/op/update"
1091
1208
  ```
1092
1209
 
1093
- In this case, the Context Broker reply with the following response (200 OK):
1094
-
1095
- ```json
1096
- [
1097
- {
1098
- "type": "device",
1099
- "id": "Dev0001",
1100
- "switch_info": {
1101
- "type": "commandResult",
1102
- "value": ""
1103
- },
1104
- "switch_status": {
1105
- "type": "commandStatus",
1106
- "value": ""
1107
- }
1108
- }
1109
- ]
1110
- ```
1210
+ In this case, the Context Broker reply with the a 204 No Content response (no payload).
1111
1211
 
1112
1212
  The User will acknowledge the error the next time he queries the Context Broker for information about the command.
@@ -160,7 +160,8 @@ function processEnvironmentVariables() {
160
160
  'IOTA_LD_SUPPORT_DATA_TYPE',
161
161
  'IOTA_EXPRESS_LIMIT',
162
162
  'IOTA_USE_CB_FLOW_CONTROL',
163
- 'IOTA_STORE_LAST_MEASURE'
163
+ 'IOTA_STORE_LAST_MEASURE',
164
+ 'IOTA_CMD_MODE'
164
165
  ];
165
166
  const iotamVariables = [
166
167
  'IOTA_IOTAM_URL',
@@ -172,6 +173,9 @@ function processEnvironmentVariables() {
172
173
  'IOTA_IOTAM_AGENTPATH'
173
174
  ];
174
175
  const mongoVariables = [
176
+ 'IOTA_MONGO_URI',
177
+ // FIXME: the following IOTA_MONGO_ env vars are deprecated and will
178
+ // be eventually removed (use IOTA_MONGO_URI)
175
179
  'IOTA_MONGO_HOST',
176
180
  'IOTA_MONGO_PORT',
177
181
  'IOTA_MONGO_DB',
@@ -408,6 +412,10 @@ function processEnvironmentVariables() {
408
412
  config.mongodb = {};
409
413
  }
410
414
 
415
+ if (process.env.IOTA_MONGO_URI) {
416
+ config.mongodb.uri = process.env.IOTA_MONGO_URI;
417
+ }
418
+
411
419
  if (process.env.IOTA_MONGO_HOST) {
412
420
  config.mongodb.host = process.env.IOTA_MONGO_HOST;
413
421
  }
@@ -500,6 +508,9 @@ function processEnvironmentVariables() {
500
508
  } else {
501
509
  config.storeLastMeasure = config.storeLastMeasure === true;
502
510
  }
511
+ if (process.env.IOTA_CMD_MODE) {
512
+ config.cmdMode = process.env.IOTA_CMD_MODE;
513
+ }
503
514
  }
504
515
 
505
516
  function setConfig(newConfig) {
@@ -204,6 +204,21 @@ const map = {
204
204
  options
205
205
  );
206
206
  },
207
+ localestringnumber: (n, locale, options) => {
208
+ const safeOperation =
209
+ (fn) =>
210
+ (...args) => {
211
+ try {
212
+ return fn(...args);
213
+ } catch (e) {
214
+ return null;
215
+ }
216
+ };
217
+ return safeOperation((n, locale, options) => {
218
+ if (typeof n !== 'number' || isNaN(n)) return null;
219
+ return n.toLocaleString(locale, options);
220
+ })(n, locale, options);
221
+ },
207
222
  now: Date.now,
208
223
  hextostring: (val) => {
209
224
  const safeOperation =
@@ -57,7 +57,9 @@ const Device = new Schema({
57
57
  useCBflowControl: Boolean,
58
58
  storeLastMeasure: Boolean,
59
59
  lastMeasure: Object,
60
- oldCtxt: Object
60
+ oldCtxt: Object,
61
+ cmdMode: String,
62
+ subscriptionId: String
61
63
  });
62
64
 
63
65
  function load() {
@@ -67,7 +67,8 @@ const Group = new Schema({
67
67
  entityNameExp: String,
68
68
  payloadType: String,
69
69
  useCBflowControl: Boolean,
70
- storeLastMeasure: Boolean
70
+ storeLastMeasure: Boolean,
71
+ cmdMode: String
71
72
  });
72
73
 
73
74
  function load() {