@hello.nrfcloud.com/proto-map 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +122 -0
- package/dist/generator/addDocBlock.js +5 -0
- package/dist/generator/generateLwM2MDefinitions.js +82 -0
- package/dist/generator/generateLwM2MDefinitions.spec.js +91 -0
- package/dist/generator/generateLwm2mTimestampResources.js +69 -0
- package/dist/generator/generateModels.js +142 -0
- package/dist/generator/generateType.js +90 -0
- package/dist/generator/generateValidator.js +132 -0
- package/dist/generator/generateValidators.js +63 -0
- package/dist/generator/isDir.js +163 -0
- package/dist/generator/isDir.spec.js +212 -0
- package/dist/generator/lwm2m.js +106 -0
- package/dist/generator/models.js +306 -0
- package/dist/generator/printNode.js +8 -0
- package/dist/generator/tokenizeName.js +5 -0
- package/dist/generator/tokenizeName.spec.js +98 -0
- package/dist/generator/types.js +140 -0
- package/dist/lwm2m/LWM2MObjectDefinition.js +88 -0
- package/dist/lwm2m/LWM2MObjectInfo.js +9 -0
- package/dist/lwm2m/LwM2MObject.d.js +1 -0
- package/dist/lwm2m/LwM2MObjectID.js +57 -0
- package/dist/lwm2m/ParsedLwM2MObjectDefinition.js +1 -0
- package/dist/lwm2m/check-lwm2m-rules.js +480 -0
- package/dist/lwm2m/definitions.js +596 -0
- package/dist/lwm2m/fromXML2JSON.js +194 -0
- package/dist/lwm2m/instanceTs.js +9 -0
- package/dist/lwm2m/instanceTs.spec.js +16 -0
- package/dist/lwm2m/isRegisteredLwM2MObject.js +3 -0
- package/dist/lwm2m/isRegisteredLwM2MObject.spec.js +45 -0
- package/dist/lwm2m/object/14201.d.js +5 -0
- package/dist/lwm2m/object/14202.d.js +5 -0
- package/dist/lwm2m/object/14203.d.js +5 -0
- package/dist/lwm2m/object/14204.d.js +5 -0
- package/dist/lwm2m/object/14205.d.js +5 -0
- package/dist/lwm2m/object/14210.d.js +5 -0
- package/dist/lwm2m/object/14220.d.js +5 -0
- package/dist/lwm2m/object/14230.d.js +5 -0
- package/dist/lwm2m/object/validate14201.js +18 -0
- package/dist/lwm2m/object/validate14202.js +17 -0
- package/dist/lwm2m/object/validate14203.js +19 -0
- package/dist/lwm2m/object/validate14204.js +17 -0
- package/dist/lwm2m/object/validate14205.js +15 -0
- package/dist/lwm2m/object/validate14210.js +13 -0
- package/dist/lwm2m/object/validate14220.js +12 -0
- package/dist/lwm2m/object/validate14230.js +13 -0
- package/dist/lwm2m/objects.js +8 -0
- package/dist/lwm2m/parseRangeEnumeration.js +20 -0
- package/dist/lwm2m/parseRangeEnumeration.spec.js +27 -0
- package/dist/lwm2m/resourceType.js +12 -0
- package/dist/lwm2m/timestampResources.js +12 -0
- package/dist/lwm2m/unwrapNestedArray.js +114 -0
- package/dist/lwm2m/unwrapNestedArray.spec.js +374 -0
- package/dist/lwm2m/validate.js +14 -0
- package/dist/lwm2m/validation.js +146 -0
- package/dist/lwm2m/validators.js +20 -0
- package/dist/markdown/getCodeBlock.js +74 -0
- package/dist/markdown/getFrontMatter.js +15 -0
- package/dist/markdown/parseREADME.js +19 -0
- package/dist/models/asset_tracker_v2+AWS/examples/examples.spec.js +489 -0
- package/dist/models/check-model-rules.js +137 -0
- package/dist/models/models.js +137 -0
- package/dist/models/types.js +13 -0
- package/dist/senml/SenMLSchema.js +79 -0
- package/dist/senml/SenMLSchema.spec.js +23 -0
- package/dist/senml/hasValue.js +8 -0
- package/dist/senml/hasValue.spec.js +103 -0
- package/dist/senml/lwm2mToSenML.js +137 -0
- package/dist/senml/lwm2mToSenML.spec.js +104 -0
- package/dist/senml/parseResourceId.js +58 -0
- package/dist/senml/parseResourceId.spec.js +13 -0
- package/dist/senml/senMLtoLwM2M.js +126 -0
- package/dist/senml/senMLtoLwM2M.spec.js +226 -0
- package/dist/senml/validateSenML.js +6 -0
- package/dist/senml/validateSenML.spec.js +31 -0
- package/export.js +13 -0
- package/index.d.ts +14 -0
- package/lwm2m/14201.xml +94 -0
- package/lwm2m/14202.xml +84 -0
- package/lwm2m/14203.xml +104 -0
- package/lwm2m/14204.xml +84 -0
- package/lwm2m/14205.xml +64 -0
- package/lwm2m/14210.xml +44 -0
- package/lwm2m/14220.xml +34 -0
- package/lwm2m/14230.xml +44 -0
- package/lwm2m/LWM2M-v1_1.xsd +168 -0
- package/lwm2m/LWM2MObjectDefinition.ts +84 -0
- package/lwm2m/LWM2MObjectInfo.ts +42 -0
- package/lwm2m/LwM2MObject.d.ts +19 -0
- package/lwm2m/LwM2MObjectID.ts +73 -0
- package/lwm2m/ParsedLwM2MObjectDefinition.ts +28 -0
- package/lwm2m/check-lwm2m-rules.ts +160 -0
- package/lwm2m/definitions.ts +278 -0
- package/lwm2m/format.sh +3 -0
- package/lwm2m/fromXML2JSON.ts +44 -0
- package/lwm2m/instanceTs.spec.ts +19 -0
- package/lwm2m/instanceTs.ts +10 -0
- package/lwm2m/isRegisteredLwM2MObject.spec.ts +48 -0
- package/lwm2m/isRegisteredLwM2MObject.ts +4 -0
- package/lwm2m/object/14201.d.ts +73 -0
- package/lwm2m/object/14202.d.ts +59 -0
- package/lwm2m/object/14203.d.ts +67 -0
- package/lwm2m/object/14204.d.ts +55 -0
- package/lwm2m/object/14205.d.ts +43 -0
- package/lwm2m/object/14210.d.ts +31 -0
- package/lwm2m/object/14220.d.ts +25 -0
- package/lwm2m/object/14230.d.ts +31 -0
- package/lwm2m/object/validate14201.ts +10 -0
- package/lwm2m/object/validate14202.ts +10 -0
- package/lwm2m/object/validate14203.ts +10 -0
- package/lwm2m/object/validate14204.ts +10 -0
- package/lwm2m/object/validate14205.ts +10 -0
- package/lwm2m/object/validate14210.ts +10 -0
- package/lwm2m/object/validate14220.ts +10 -0
- package/lwm2m/object/validate14230.ts +10 -0
- package/lwm2m/objects.ts +16 -0
- package/lwm2m/parseRangeEnumeration.spec.ts +34 -0
- package/lwm2m/parseRangeEnumeration.ts +29 -0
- package/lwm2m/resourceType.ts +11 -0
- package/lwm2m/timestampResources.ts +4 -0
- package/lwm2m/unwrapNestedArray.spec.ts +241 -0
- package/lwm2m/unwrapNestedArray.ts +27 -0
- package/lwm2m/validate.ts +30 -0
- package/lwm2m/validation.ts +120 -0
- package/lwm2m/validators.ts +21 -0
- package/models/PCA20035+solar/README.md +10 -0
- package/models/PCA20035+solar/transforms/airQuality.md +48 -0
- package/models/PCA20035+solar/transforms/battery.md +46 -0
- package/models/PCA20035+solar/transforms/button.md +45 -0
- package/models/PCA20035+solar/transforms/deviceInfo.md +72 -0
- package/models/PCA20035+solar/transforms/gain.md +45 -0
- package/models/PCA20035+solar/transforms/geolocationFromGroundfix.md +67 -0
- package/models/PCA20035+solar/transforms/geolocationFromMessage.md +80 -0
- package/models/PCA20035+solar/transforms/humidity.md +43 -0
- package/models/PCA20035+solar/transforms/networkInfo.md +84 -0
- package/models/PCA20035+solar/transforms/pressure.md +43 -0
- package/models/PCA20035+solar/transforms/temperature.md +43 -0
- package/models/README.md +10 -0
- package/models/asset_tracker_v2+AWS/README.md +6 -0
- package/models/asset_tracker_v2+AWS/examples/examples.spec.ts +229 -0
- package/models/asset_tracker_v2+AWS/examples/shadow/example-1.json +24 -0
- package/models/asset_tracker_v2+AWS/examples/shadow/example-2.json +30 -0
- package/models/asset_tracker_v2+AWS/examples/shadow/example-3.json +37 -0
- package/models/asset_tracker_v2+AWS/examples/shadow/example-4.json +48 -0
- package/models/asset_tracker_v2+AWS/examples/shadow/example-5.json +43 -0
- package/models/asset_tracker_v2+AWS/transforms/GNSS.md +66 -0
- package/models/asset_tracker_v2+AWS/transforms/battery-voltage.md +50 -0
- package/models/asset_tracker_v2+AWS/transforms/device-info.md +61 -0
- package/models/asset_tracker_v2+AWS/transforms/env.md +69 -0
- package/models/asset_tracker_v2+AWS/transforms/fuel-gauge.md +62 -0
- package/models/asset_tracker_v2+AWS/transforms/roam.md +100 -0
- package/models/asset_tracker_v2+AWS/transforms/solar.md +58 -0
- package/models/check-model-rules.ts +125 -0
- package/models/kartverket-vasstandsdata/README.md +13 -0
- package/models/models.ts +36 -0
- package/models/types.ts +17 -0
- package/package.json +111 -0
- package/senml/SenMLSchema.spec.ts +21 -0
- package/senml/SenMLSchema.ts +74 -0
- package/senml/hasValue.spec.ts +19 -0
- package/senml/hasValue.ts +12 -0
- package/senml/lwm2mToSenML.spec.ts +74 -0
- package/senml/lwm2mToSenML.ts +62 -0
- package/senml/parseResourceId.spec.ts +13 -0
- package/senml/parseResourceId.ts +23 -0
- package/senml/senMLtoLwM2M.spec.ts +181 -0
- package/senml/senMLtoLwM2M.ts +121 -0
- package/senml/validateSenML.spec.ts +16 -0
- package/senml/validateSenML.ts +8 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { unwrapNestedArray } from './unwrapNestedArray.js'
|
|
4
|
+
|
|
5
|
+
void describe('unwrapNestedArray()', () => {
|
|
6
|
+
void it('should unwrap an array', () =>
|
|
7
|
+
assert.deepEqual(
|
|
8
|
+
unwrapNestedArray({
|
|
9
|
+
LWM2M: {
|
|
10
|
+
$: {
|
|
11
|
+
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
|
12
|
+
'xsi:noNamespaceSchemaLocation':
|
|
13
|
+
'http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd',
|
|
14
|
+
},
|
|
15
|
+
Object: [
|
|
16
|
+
{
|
|
17
|
+
$: { ObjectType: 'MODefinition' },
|
|
18
|
+
Name: ['Location'],
|
|
19
|
+
Description1: [
|
|
20
|
+
'This LwM2M Object provides a range of location telemetry related information which can be queried by the LwM2M Server.',
|
|
21
|
+
],
|
|
22
|
+
ObjectID: ['6'],
|
|
23
|
+
ObjectURN: ['urn:oma:lwm2m:oma:6'],
|
|
24
|
+
LWM2MVersion: ['1.0'],
|
|
25
|
+
ObjectVersion: ['1.0'],
|
|
26
|
+
MultipleInstances: ['Single'],
|
|
27
|
+
Mandatory: ['Optional'],
|
|
28
|
+
Resources: [
|
|
29
|
+
{
|
|
30
|
+
Item: [
|
|
31
|
+
{
|
|
32
|
+
$: { ID: '0' },
|
|
33
|
+
Name: ['Latitude'],
|
|
34
|
+
Operations: ['R'],
|
|
35
|
+
MultipleInstances: ['Single'],
|
|
36
|
+
Mandatory: ['Mandatory'],
|
|
37
|
+
Type: ['Float'],
|
|
38
|
+
RangeEnumeration: [''],
|
|
39
|
+
Units: ['lat'],
|
|
40
|
+
Description: [
|
|
41
|
+
'The decimal notation of latitude, e.g. -43.5723 [World Geodetic System 1984].',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
$: { ID: '1' },
|
|
46
|
+
Name: ['Longitude'],
|
|
47
|
+
Operations: ['R'],
|
|
48
|
+
MultipleInstances: ['Single'],
|
|
49
|
+
Mandatory: ['Mandatory'],
|
|
50
|
+
Type: ['Float'],
|
|
51
|
+
RangeEnumeration: [''],
|
|
52
|
+
Units: ['lon'],
|
|
53
|
+
Description: [
|
|
54
|
+
'The decimal notation of longitude, e.g. 153.21760 [World Geodetic System 1984].',
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
$: { ID: '2' },
|
|
59
|
+
Name: ['Altitude'],
|
|
60
|
+
Operations: ['R'],
|
|
61
|
+
MultipleInstances: ['Single'],
|
|
62
|
+
Mandatory: ['Optional'],
|
|
63
|
+
Type: ['Float'],
|
|
64
|
+
RangeEnumeration: [''],
|
|
65
|
+
Units: ['m'],
|
|
66
|
+
Description: [
|
|
67
|
+
'The decimal notation of altitude in meters above sea level.',
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
$: { ID: '3' },
|
|
72
|
+
Name: ['Radius'],
|
|
73
|
+
Operations: ['R'],
|
|
74
|
+
MultipleInstances: ['Single'],
|
|
75
|
+
Mandatory: ['Optional'],
|
|
76
|
+
Type: ['Float'],
|
|
77
|
+
RangeEnumeration: [''],
|
|
78
|
+
Units: ['m'],
|
|
79
|
+
Description: [
|
|
80
|
+
'The value in this resource indicates the radius of a circular area in meters. The circular area is used to describe uncertainty about a point for coordinates in a two-dimensional coordinate reference systems (CRS). The center point of a circular area is specified by using the Latitude and the Longitude Resources.',
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
$: { ID: '4' },
|
|
85
|
+
Name: ['Velocity'],
|
|
86
|
+
Operations: ['R'],
|
|
87
|
+
MultipleInstances: ['Single'],
|
|
88
|
+
Mandatory: ['Optional'],
|
|
89
|
+
Type: ['Opaque'],
|
|
90
|
+
RangeEnumeration: [''],
|
|
91
|
+
Units: [''],
|
|
92
|
+
Description: [
|
|
93
|
+
'The velocity of the LwM2M Client, as defined in [3GPP-TS_23.032].',
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
$: { ID: '5' },
|
|
98
|
+
Name: ['Timestamp'],
|
|
99
|
+
Operations: ['R'],
|
|
100
|
+
MultipleInstances: ['Single'],
|
|
101
|
+
Mandatory: ['Mandatory'],
|
|
102
|
+
Type: ['Time'],
|
|
103
|
+
RangeEnumeration: [''],
|
|
104
|
+
Units: [''],
|
|
105
|
+
Description: [
|
|
106
|
+
'The timestamp of when the location measurement was performed.',
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
$: { ID: '6' },
|
|
111
|
+
Name: ['Speed'],
|
|
112
|
+
Operations: ['R'],
|
|
113
|
+
MultipleInstances: ['Single'],
|
|
114
|
+
Mandatory: ['Optional'],
|
|
115
|
+
Type: ['Float'],
|
|
116
|
+
RangeEnumeration: [''],
|
|
117
|
+
Units: ['m/s'],
|
|
118
|
+
Description: [
|
|
119
|
+
'Speed is the time rate of change in position of a LwM2M Client without regard for direction: the scalar component of velocity.',
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
Description2: [''],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
{
|
|
131
|
+
LWM2M: {
|
|
132
|
+
$: {
|
|
133
|
+
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
|
134
|
+
'xsi:noNamespaceSchemaLocation':
|
|
135
|
+
'http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd',
|
|
136
|
+
},
|
|
137
|
+
Object: {
|
|
138
|
+
$: { ObjectType: 'MODefinition' },
|
|
139
|
+
Name: 'Location',
|
|
140
|
+
Description1:
|
|
141
|
+
'This LwM2M Object provides a range of location telemetry related information which can be queried by the LwM2M Server.',
|
|
142
|
+
ObjectID: '6',
|
|
143
|
+
ObjectURN: 'urn:oma:lwm2m:oma:6',
|
|
144
|
+
LWM2MVersion: '1.0',
|
|
145
|
+
ObjectVersion: '1.0',
|
|
146
|
+
MultipleInstances: 'Single',
|
|
147
|
+
Mandatory: 'Optional',
|
|
148
|
+
Resources: {
|
|
149
|
+
Item: [
|
|
150
|
+
{
|
|
151
|
+
$: { ID: '0' },
|
|
152
|
+
Name: 'Latitude',
|
|
153
|
+
Operations: 'R',
|
|
154
|
+
MultipleInstances: 'Single',
|
|
155
|
+
Mandatory: 'Mandatory',
|
|
156
|
+
Type: 'Float',
|
|
157
|
+
RangeEnumeration: '',
|
|
158
|
+
Units: 'lat',
|
|
159
|
+
Description:
|
|
160
|
+
'The decimal notation of latitude, e.g. -43.5723 [World Geodetic System 1984].',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
$: { ID: '1' },
|
|
164
|
+
Name: 'Longitude',
|
|
165
|
+
Operations: 'R',
|
|
166
|
+
MultipleInstances: 'Single',
|
|
167
|
+
Mandatory: 'Mandatory',
|
|
168
|
+
Type: 'Float',
|
|
169
|
+
RangeEnumeration: '',
|
|
170
|
+
Units: 'lon',
|
|
171
|
+
Description:
|
|
172
|
+
'The decimal notation of longitude, e.g. 153.21760 [World Geodetic System 1984].',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
$: { ID: '2' },
|
|
176
|
+
Name: 'Altitude',
|
|
177
|
+
Operations: 'R',
|
|
178
|
+
MultipleInstances: 'Single',
|
|
179
|
+
Mandatory: 'Optional',
|
|
180
|
+
Type: 'Float',
|
|
181
|
+
RangeEnumeration: '',
|
|
182
|
+
Units: 'm',
|
|
183
|
+
Description:
|
|
184
|
+
'The decimal notation of altitude in meters above sea level.',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
$: { ID: '3' },
|
|
188
|
+
Name: 'Radius',
|
|
189
|
+
Operations: 'R',
|
|
190
|
+
MultipleInstances: 'Single',
|
|
191
|
+
Mandatory: 'Optional',
|
|
192
|
+
Type: 'Float',
|
|
193
|
+
RangeEnumeration: '',
|
|
194
|
+
Units: 'm',
|
|
195
|
+
Description:
|
|
196
|
+
'The value in this resource indicates the radius of a circular area in meters. The circular area is used to describe uncertainty about a point for coordinates in a two-dimensional coordinate reference systems (CRS). The center point of a circular area is specified by using the Latitude and the Longitude Resources.',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
$: { ID: '4' },
|
|
200
|
+
Name: 'Velocity',
|
|
201
|
+
Operations: 'R',
|
|
202
|
+
MultipleInstances: 'Single',
|
|
203
|
+
Mandatory: 'Optional',
|
|
204
|
+
Type: 'Opaque',
|
|
205
|
+
RangeEnumeration: '',
|
|
206
|
+
Units: '',
|
|
207
|
+
Description:
|
|
208
|
+
'The velocity of the LwM2M Client, as defined in [3GPP-TS_23.032].',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
$: { ID: '5' },
|
|
212
|
+
Name: 'Timestamp',
|
|
213
|
+
Operations: 'R',
|
|
214
|
+
MultipleInstances: 'Single',
|
|
215
|
+
Mandatory: 'Mandatory',
|
|
216
|
+
Type: 'Time',
|
|
217
|
+
RangeEnumeration: '',
|
|
218
|
+
Units: '',
|
|
219
|
+
Description:
|
|
220
|
+
'The timestamp of when the location measurement was performed.',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
$: { ID: '6' },
|
|
224
|
+
Name: 'Speed',
|
|
225
|
+
Operations: 'R',
|
|
226
|
+
MultipleInstances: 'Single',
|
|
227
|
+
Mandatory: 'Optional',
|
|
228
|
+
Type: 'Float',
|
|
229
|
+
RangeEnumeration: '',
|
|
230
|
+
Units: 'm/s',
|
|
231
|
+
Description:
|
|
232
|
+
'Speed is the time rate of change in position of a LwM2M Client without regard for direction: the scalar component of velocity.',
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
Description2: '',
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
))
|
|
241
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A recursive function that walks through the given object and for each property checks if its value is an array and only has one element. If that is the case it takes the first element and uses it instead.
|
|
3
|
+
*/
|
|
4
|
+
export const unwrapNestedArray = (
|
|
5
|
+
object: Record<string, unknown>,
|
|
6
|
+
): Record<string, unknown> => {
|
|
7
|
+
if (typeof object === 'string') return object
|
|
8
|
+
return Object.entries(object).reduce((result, [k, v]) => {
|
|
9
|
+
if (typeof v === 'string') return { ...result, [k]: v }
|
|
10
|
+
if (Array.isArray(v)) {
|
|
11
|
+
if (v.length === 1) {
|
|
12
|
+
return {
|
|
13
|
+
...result,
|
|
14
|
+
[k]: unwrapNestedArray(v[0]),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
...result,
|
|
19
|
+
[k]: v.map(unwrapNestedArray),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...result,
|
|
24
|
+
[k]: unwrapNestedArray(v as Record<string, unknown>),
|
|
25
|
+
}
|
|
26
|
+
}, {})
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LwM2MObject } from './LwM2MObject.js'
|
|
2
|
+
import { isLwM2MObject } from './validation.js'
|
|
3
|
+
import { LwM2MObjectID } from './LwM2MObjectID.js'
|
|
4
|
+
import type { LwM2MResourceValue } from '../senml/senMLtoLwM2M.js'
|
|
5
|
+
|
|
6
|
+
export const validate =
|
|
7
|
+
(
|
|
8
|
+
validators: Map<
|
|
9
|
+
LwM2MObjectID,
|
|
10
|
+
(o: unknown, onError?: (error: Error) => void) => boolean
|
|
11
|
+
>,
|
|
12
|
+
) =>
|
|
13
|
+
(
|
|
14
|
+
o: unknown,
|
|
15
|
+
onError?: (error: Error) => void,
|
|
16
|
+
): o is LwM2MObject<{
|
|
17
|
+
ObjectID: LwM2MObjectID
|
|
18
|
+
ObjectVersion: string
|
|
19
|
+
Resources: Record<number, LwM2MResourceValue | undefined>
|
|
20
|
+
}> => {
|
|
21
|
+
if (!isLwM2MObject(o, onError)) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
const validator = validators.get(o.ObjectID)
|
|
25
|
+
if (validator === undefined) {
|
|
26
|
+
onError?.(new Error(`No validator defined for ObjectID: ${o.ObjectID}!`))
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
return validator(o, onError)
|
|
30
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { LwM2MResourceValue } from '../senml/senMLtoLwM2M.js'
|
|
2
|
+
import { LwM2MObjectIDs, type LwM2MObjectID } from './LwM2MObjectID.js'
|
|
3
|
+
|
|
4
|
+
export const isLwM2MObject = (
|
|
5
|
+
object: unknown,
|
|
6
|
+
onError?: (error: Error) => void,
|
|
7
|
+
): object is {
|
|
8
|
+
ObjectID: LwM2MObjectID
|
|
9
|
+
/**
|
|
10
|
+
* The Object Version of an Object is composed of 2 digits separated by a dot '.'
|
|
11
|
+
*
|
|
12
|
+
* @see https://www.openmobilealliance.org/release/LightweightM2M/V1_1_1-20190617-A/OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A.pdf Section 7.2.2
|
|
13
|
+
*
|
|
14
|
+
* @default 1.0
|
|
15
|
+
*/
|
|
16
|
+
ObjectVersion?: string
|
|
17
|
+
Resources: Record<number, LwM2MResourceValue>
|
|
18
|
+
} => {
|
|
19
|
+
const error = (message: string) => {
|
|
20
|
+
onError?.(new Error(message))
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
// Must be an object
|
|
24
|
+
if (typeof object !== 'object' || object === null)
|
|
25
|
+
return error(`Not an object`)
|
|
26
|
+
// Must have valid ObjectID
|
|
27
|
+
if (
|
|
28
|
+
!('ObjectID' in object) ||
|
|
29
|
+
typeof object.ObjectID !== 'number' ||
|
|
30
|
+
object.ObjectID < 14200 ||
|
|
31
|
+
object.ObjectID > 15000 ||
|
|
32
|
+
LwM2MObjectIDs.includes(object.ObjectID) === false
|
|
33
|
+
)
|
|
34
|
+
return error(
|
|
35
|
+
`Not an valid Object ID: ${JSON.stringify((object as any).ObjectID)}`,
|
|
36
|
+
)
|
|
37
|
+
// ObjectVersion must be valid
|
|
38
|
+
if ('ObjectVersion' in object) {
|
|
39
|
+
if (
|
|
40
|
+
typeof object.ObjectVersion !== 'string' ||
|
|
41
|
+
!/^\d+\.\d+$/.test(object.ObjectVersion)
|
|
42
|
+
)
|
|
43
|
+
return error(`Invalid ObjectVersion`)
|
|
44
|
+
}
|
|
45
|
+
// Must have valid resources
|
|
46
|
+
if (
|
|
47
|
+
!('Resources' in object) ||
|
|
48
|
+
typeof object.Resources !== 'object' ||
|
|
49
|
+
object.Resources === null
|
|
50
|
+
)
|
|
51
|
+
return error(`Resources must be an object`)
|
|
52
|
+
// All keys must be numbers
|
|
53
|
+
if (
|
|
54
|
+
(Object.keys(object.Resources).find((k) => /[^\d]/.test(k))?.length ?? 0) >
|
|
55
|
+
0
|
|
56
|
+
)
|
|
57
|
+
return error(`All resource IDs must be a number`)
|
|
58
|
+
// All values must be number, string, boolean, or Date
|
|
59
|
+
for (const v of Object.values(object.Resources)) {
|
|
60
|
+
if (typeof v === 'string') continue
|
|
61
|
+
if (typeof v === 'boolean') continue
|
|
62
|
+
if (typeof v === 'number') continue
|
|
63
|
+
if (typeof v === 'object' && v instanceof Date) continue
|
|
64
|
+
return error(`Invalid value type ${typeof v}`)
|
|
65
|
+
}
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const validate =
|
|
70
|
+
(
|
|
71
|
+
ObjectID: LwM2MObjectID,
|
|
72
|
+
ObjectVersion: string,
|
|
73
|
+
Resources: Record<number, (r: unknown) => boolean>,
|
|
74
|
+
) =>
|
|
75
|
+
(o: unknown, onError?: (error: Error) => void): boolean => {
|
|
76
|
+
const error = (message: string) => {
|
|
77
|
+
onError?.(new Error(message))
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
if (!isLwM2MObject(o, onError)) return false
|
|
81
|
+
if (o.ObjectID !== ObjectID)
|
|
82
|
+
return error(
|
|
83
|
+
`Given Object ID ${o.ObjectID} does not match expected ${ObjectID}`,
|
|
84
|
+
)
|
|
85
|
+
if ((o.ObjectVersion ?? '1.0') !== ObjectVersion)
|
|
86
|
+
return error(
|
|
87
|
+
`Given Object version ${o.ObjectVersion} does not match expected ${ObjectVersion}`,
|
|
88
|
+
)
|
|
89
|
+
return Object.entries(Resources).reduce(
|
|
90
|
+
(allValid, [ResourceID, validator]) => {
|
|
91
|
+
if (!allValid) return false
|
|
92
|
+
if (validator(o.Resources[parseInt(ResourceID, 10)]) === false) {
|
|
93
|
+
return error(`Resource ${ResourceID} is invalid.`)
|
|
94
|
+
}
|
|
95
|
+
return allValid
|
|
96
|
+
},
|
|
97
|
+
true,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const NumberResource = (r: unknown): r is number => typeof r === 'number'
|
|
102
|
+
export const StringResource = (r: unknown): r is string => typeof r === 'string'
|
|
103
|
+
export const DateResource = (r: unknown): r is Date =>
|
|
104
|
+
typeof r === 'object' && r instanceof Date
|
|
105
|
+
export const BooleanResource = (r: unknown): r is boolean =>
|
|
106
|
+
typeof r === 'boolean'
|
|
107
|
+
|
|
108
|
+
export const OptionalResource =
|
|
109
|
+
(
|
|
110
|
+
validator:
|
|
111
|
+
| typeof NumberResource
|
|
112
|
+
| typeof StringResource
|
|
113
|
+
| typeof DateResource
|
|
114
|
+
| typeof BooleanResource,
|
|
115
|
+
) =>
|
|
116
|
+
(r: unknown): boolean =>
|
|
117
|
+
r === undefined ? true : validator(r)
|
|
118
|
+
|
|
119
|
+
export const isLwM2MObjectID = (o: unknown): o is LwM2MObjectID =>
|
|
120
|
+
typeof o === 'number' && LwM2MObjectIDs.includes(o)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LwM2MObjectID } from "./LwM2MObjectID.js";
|
|
2
|
+
import { validate14201 } from "./object/validate14201.js";
|
|
3
|
+
import { validate14202 } from "./object/validate14202.js";
|
|
4
|
+
import { validate14203 } from "./object/validate14203.js";
|
|
5
|
+
import { validate14204 } from "./object/validate14204.js";
|
|
6
|
+
import { validate14205 } from "./object/validate14205.js";
|
|
7
|
+
import { validate14210 } from "./object/validate14210.js";
|
|
8
|
+
import { validate14220 } from "./object/validate14220.js";
|
|
9
|
+
import { validate14230 } from "./object/validate14230.js";
|
|
10
|
+
/**
|
|
11
|
+
* Contains the validators for all registered LwM2M objects.
|
|
12
|
+
*/
|
|
13
|
+
export const validators = new Map<LwM2MObjectID, (o: unknown) => boolean>();
|
|
14
|
+
validators.set(LwM2MObjectID.Geolocation_14201, validate14201)
|
|
15
|
+
validators.set(LwM2MObjectID.BatteryAndPower_14202, validate14202)
|
|
16
|
+
validators.set(LwM2MObjectID.ConnectionInformation_14203, validate14203)
|
|
17
|
+
validators.set(LwM2MObjectID.DeviceInformation_14204, validate14204)
|
|
18
|
+
validators.set(LwM2MObjectID.Environment_14205, validate14205)
|
|
19
|
+
validators.set(LwM2MObjectID.SolarCharge_14210, validate14210)
|
|
20
|
+
validators.set(LwM2MObjectID.ButtonPress_14220, validate14220)
|
|
21
|
+
validators.set(LwM2MObjectID.SeaWaterLevel_14230, validate14230)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Thingy:91 with Solar Shield
|
|
2
|
+
|
|
3
|
+
The Nordic Thingy:91 Solar Shield is a plug-and-play prototyping platform.
|
|
4
|
+
Powerfoyle solar cell is mounted onto the Thingy to quickly get started
|
|
5
|
+
exploring the endless possibilities with solar powered IoT applications and to
|
|
6
|
+
develop products with eternal life or even battery-free products.
|
|
7
|
+
|
|
8
|
+
The Thingy:91 runs the
|
|
9
|
+
[`asset_tracker_v2`](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/applications/asset_tracker_v2/README.html)
|
|
10
|
+
application and sends messages to nRF Cloud using MQTT.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: messages
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Environment sensor: air quality
|
|
6
|
+
|
|
7
|
+
The Bosch BME680 sensor calculates an Air Quality Index.
|
|
8
|
+
|
|
9
|
+
See
|
|
10
|
+
<https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme680-ds001.pdf>
|
|
11
|
+
|
|
12
|
+
## Match Expression
|
|
13
|
+
|
|
14
|
+
```jsonata
|
|
15
|
+
appId = 'AIR_QUAL'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Transform Expression
|
|
19
|
+
|
|
20
|
+
```jsonata
|
|
21
|
+
[
|
|
22
|
+
{"bn": "/14205/0/", "n": "10", "v": $number(data), "bt": ts }
|
|
23
|
+
]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Input Example
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"appId": "AIR_QUAL",
|
|
31
|
+
"messageType": "DATA",
|
|
32
|
+
"ts": 1681985384511,
|
|
33
|
+
"data": "177"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Result Example
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
"bn": "/14205/0/",
|
|
43
|
+
"n": "10",
|
|
44
|
+
"v": 177,
|
|
45
|
+
"bt": 1681985384511
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: messages
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Battery state of charge
|
|
6
|
+
|
|
7
|
+
This is the legacy battery state of charge using the modem's battery
|
|
8
|
+
information.
|
|
9
|
+
|
|
10
|
+
## Match Expression
|
|
11
|
+
|
|
12
|
+
```jsonata
|
|
13
|
+
appId = 'BATTERY'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Transform Expression
|
|
17
|
+
|
|
18
|
+
```jsonata
|
|
19
|
+
[
|
|
20
|
+
{"bn": "/14202/0/", "n": "0", "v": $number(data), "bt": ts }
|
|
21
|
+
]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Input Example
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"appId": "BATTERY",
|
|
29
|
+
"messageType": "DATA",
|
|
30
|
+
"ts": 1687448260542,
|
|
31
|
+
"data": "94"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Result Example
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
"bn": "/14202/0/",
|
|
41
|
+
"n": "0",
|
|
42
|
+
"v": 94,
|
|
43
|
+
"bt": 1687448260542
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: messages
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Button Press
|
|
6
|
+
|
|
7
|
+
Describes a button press.
|
|
8
|
+
|
|
9
|
+
## Match Expression
|
|
10
|
+
|
|
11
|
+
```jsonata
|
|
12
|
+
appId = 'BUTTON'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Transform Expression
|
|
16
|
+
|
|
17
|
+
```jsonata
|
|
18
|
+
[
|
|
19
|
+
{"bn": "/14220/0/", "n": "0", "v": $number(data), "bt": ts }
|
|
20
|
+
]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Input Example
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"data": "1",
|
|
28
|
+
"appId": "BUTTON",
|
|
29
|
+
"messageType": "DATA",
|
|
30
|
+
"ts": 1676366857236
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Result Example
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
"bn": "/14220/0/",
|
|
40
|
+
"n": "0",
|
|
41
|
+
"v": 1,
|
|
42
|
+
"bt": 1676366857236
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: messages
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Device info
|
|
6
|
+
|
|
7
|
+
## Match Expression
|
|
8
|
+
|
|
9
|
+
```jsonata
|
|
10
|
+
appId = 'DEVICE' and $exists(data.deviceInfo)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Transform Expression
|
|
14
|
+
|
|
15
|
+
```jsonata
|
|
16
|
+
[
|
|
17
|
+
{"bn": "/14204/0/", "n": "0", "vs": data.deviceInfo.imei, "bt": ts },
|
|
18
|
+
{"n": "1", "vs": data.deviceInfo.iccid },
|
|
19
|
+
{"n": "2", "vs": data.deviceInfo.modemFirmware },
|
|
20
|
+
{"n": "3", "vs": data.deviceInfo.appVersion },
|
|
21
|
+
{"n": "4", "vs": data.deviceInfo.board },
|
|
22
|
+
{"n": "5", "vs": data.deviceInfo.bat }
|
|
23
|
+
]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Input Example
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"appId": "DEVICE",
|
|
31
|
+
"messageType": "DATA",
|
|
32
|
+
"ts": 1676369307189,
|
|
33
|
+
"data": {
|
|
34
|
+
"deviceInfo": {
|
|
35
|
+
"imei": "350457794611739",
|
|
36
|
+
"iccid": "8931080620054223678",
|
|
37
|
+
"modemFirmware": "mfw_nrf9160_1.3.3",
|
|
38
|
+
"board": "thingy91_nrf9160",
|
|
39
|
+
"appVersion": "0.0.0-development"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Result Example
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
"bn": "/14204/0/",
|
|
51
|
+
"n": "0",
|
|
52
|
+
"vs": "350457794611739",
|
|
53
|
+
"bt": 1676369307189
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"n": "1",
|
|
57
|
+
"vs": "8931080620054223678"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"n": "2",
|
|
61
|
+
"vs": "mfw_nrf9160_1.3.3"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"n": "3",
|
|
65
|
+
"vs": "0.0.0-development"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"n": "4",
|
|
69
|
+
"vs": "thingy91_nrf9160"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
```
|