@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.
Files changed (169) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +122 -0
  3. package/dist/generator/addDocBlock.js +5 -0
  4. package/dist/generator/generateLwM2MDefinitions.js +82 -0
  5. package/dist/generator/generateLwM2MDefinitions.spec.js +91 -0
  6. package/dist/generator/generateLwm2mTimestampResources.js +69 -0
  7. package/dist/generator/generateModels.js +142 -0
  8. package/dist/generator/generateType.js +90 -0
  9. package/dist/generator/generateValidator.js +132 -0
  10. package/dist/generator/generateValidators.js +63 -0
  11. package/dist/generator/isDir.js +163 -0
  12. package/dist/generator/isDir.spec.js +212 -0
  13. package/dist/generator/lwm2m.js +106 -0
  14. package/dist/generator/models.js +306 -0
  15. package/dist/generator/printNode.js +8 -0
  16. package/dist/generator/tokenizeName.js +5 -0
  17. package/dist/generator/tokenizeName.spec.js +98 -0
  18. package/dist/generator/types.js +140 -0
  19. package/dist/lwm2m/LWM2MObjectDefinition.js +88 -0
  20. package/dist/lwm2m/LWM2MObjectInfo.js +9 -0
  21. package/dist/lwm2m/LwM2MObject.d.js +1 -0
  22. package/dist/lwm2m/LwM2MObjectID.js +57 -0
  23. package/dist/lwm2m/ParsedLwM2MObjectDefinition.js +1 -0
  24. package/dist/lwm2m/check-lwm2m-rules.js +480 -0
  25. package/dist/lwm2m/definitions.js +596 -0
  26. package/dist/lwm2m/fromXML2JSON.js +194 -0
  27. package/dist/lwm2m/instanceTs.js +9 -0
  28. package/dist/lwm2m/instanceTs.spec.js +16 -0
  29. package/dist/lwm2m/isRegisteredLwM2MObject.js +3 -0
  30. package/dist/lwm2m/isRegisteredLwM2MObject.spec.js +45 -0
  31. package/dist/lwm2m/object/14201.d.js +5 -0
  32. package/dist/lwm2m/object/14202.d.js +5 -0
  33. package/dist/lwm2m/object/14203.d.js +5 -0
  34. package/dist/lwm2m/object/14204.d.js +5 -0
  35. package/dist/lwm2m/object/14205.d.js +5 -0
  36. package/dist/lwm2m/object/14210.d.js +5 -0
  37. package/dist/lwm2m/object/14220.d.js +5 -0
  38. package/dist/lwm2m/object/14230.d.js +5 -0
  39. package/dist/lwm2m/object/validate14201.js +18 -0
  40. package/dist/lwm2m/object/validate14202.js +17 -0
  41. package/dist/lwm2m/object/validate14203.js +19 -0
  42. package/dist/lwm2m/object/validate14204.js +17 -0
  43. package/dist/lwm2m/object/validate14205.js +15 -0
  44. package/dist/lwm2m/object/validate14210.js +13 -0
  45. package/dist/lwm2m/object/validate14220.js +12 -0
  46. package/dist/lwm2m/object/validate14230.js +13 -0
  47. package/dist/lwm2m/objects.js +8 -0
  48. package/dist/lwm2m/parseRangeEnumeration.js +20 -0
  49. package/dist/lwm2m/parseRangeEnumeration.spec.js +27 -0
  50. package/dist/lwm2m/resourceType.js +12 -0
  51. package/dist/lwm2m/timestampResources.js +12 -0
  52. package/dist/lwm2m/unwrapNestedArray.js +114 -0
  53. package/dist/lwm2m/unwrapNestedArray.spec.js +374 -0
  54. package/dist/lwm2m/validate.js +14 -0
  55. package/dist/lwm2m/validation.js +146 -0
  56. package/dist/lwm2m/validators.js +20 -0
  57. package/dist/markdown/getCodeBlock.js +74 -0
  58. package/dist/markdown/getFrontMatter.js +15 -0
  59. package/dist/markdown/parseREADME.js +19 -0
  60. package/dist/models/asset_tracker_v2+AWS/examples/examples.spec.js +489 -0
  61. package/dist/models/check-model-rules.js +137 -0
  62. package/dist/models/models.js +137 -0
  63. package/dist/models/types.js +13 -0
  64. package/dist/senml/SenMLSchema.js +79 -0
  65. package/dist/senml/SenMLSchema.spec.js +23 -0
  66. package/dist/senml/hasValue.js +8 -0
  67. package/dist/senml/hasValue.spec.js +103 -0
  68. package/dist/senml/lwm2mToSenML.js +137 -0
  69. package/dist/senml/lwm2mToSenML.spec.js +104 -0
  70. package/dist/senml/parseResourceId.js +58 -0
  71. package/dist/senml/parseResourceId.spec.js +13 -0
  72. package/dist/senml/senMLtoLwM2M.js +126 -0
  73. package/dist/senml/senMLtoLwM2M.spec.js +226 -0
  74. package/dist/senml/validateSenML.js +6 -0
  75. package/dist/senml/validateSenML.spec.js +31 -0
  76. package/export.js +13 -0
  77. package/index.d.ts +14 -0
  78. package/lwm2m/14201.xml +94 -0
  79. package/lwm2m/14202.xml +84 -0
  80. package/lwm2m/14203.xml +104 -0
  81. package/lwm2m/14204.xml +84 -0
  82. package/lwm2m/14205.xml +64 -0
  83. package/lwm2m/14210.xml +44 -0
  84. package/lwm2m/14220.xml +34 -0
  85. package/lwm2m/14230.xml +44 -0
  86. package/lwm2m/LWM2M-v1_1.xsd +168 -0
  87. package/lwm2m/LWM2MObjectDefinition.ts +84 -0
  88. package/lwm2m/LWM2MObjectInfo.ts +42 -0
  89. package/lwm2m/LwM2MObject.d.ts +19 -0
  90. package/lwm2m/LwM2MObjectID.ts +73 -0
  91. package/lwm2m/ParsedLwM2MObjectDefinition.ts +28 -0
  92. package/lwm2m/check-lwm2m-rules.ts +160 -0
  93. package/lwm2m/definitions.ts +278 -0
  94. package/lwm2m/format.sh +3 -0
  95. package/lwm2m/fromXML2JSON.ts +44 -0
  96. package/lwm2m/instanceTs.spec.ts +19 -0
  97. package/lwm2m/instanceTs.ts +10 -0
  98. package/lwm2m/isRegisteredLwM2MObject.spec.ts +48 -0
  99. package/lwm2m/isRegisteredLwM2MObject.ts +4 -0
  100. package/lwm2m/object/14201.d.ts +73 -0
  101. package/lwm2m/object/14202.d.ts +59 -0
  102. package/lwm2m/object/14203.d.ts +67 -0
  103. package/lwm2m/object/14204.d.ts +55 -0
  104. package/lwm2m/object/14205.d.ts +43 -0
  105. package/lwm2m/object/14210.d.ts +31 -0
  106. package/lwm2m/object/14220.d.ts +25 -0
  107. package/lwm2m/object/14230.d.ts +31 -0
  108. package/lwm2m/object/validate14201.ts +10 -0
  109. package/lwm2m/object/validate14202.ts +10 -0
  110. package/lwm2m/object/validate14203.ts +10 -0
  111. package/lwm2m/object/validate14204.ts +10 -0
  112. package/lwm2m/object/validate14205.ts +10 -0
  113. package/lwm2m/object/validate14210.ts +10 -0
  114. package/lwm2m/object/validate14220.ts +10 -0
  115. package/lwm2m/object/validate14230.ts +10 -0
  116. package/lwm2m/objects.ts +16 -0
  117. package/lwm2m/parseRangeEnumeration.spec.ts +34 -0
  118. package/lwm2m/parseRangeEnumeration.ts +29 -0
  119. package/lwm2m/resourceType.ts +11 -0
  120. package/lwm2m/timestampResources.ts +4 -0
  121. package/lwm2m/unwrapNestedArray.spec.ts +241 -0
  122. package/lwm2m/unwrapNestedArray.ts +27 -0
  123. package/lwm2m/validate.ts +30 -0
  124. package/lwm2m/validation.ts +120 -0
  125. package/lwm2m/validators.ts +21 -0
  126. package/models/PCA20035+solar/README.md +10 -0
  127. package/models/PCA20035+solar/transforms/airQuality.md +48 -0
  128. package/models/PCA20035+solar/transforms/battery.md +46 -0
  129. package/models/PCA20035+solar/transforms/button.md +45 -0
  130. package/models/PCA20035+solar/transforms/deviceInfo.md +72 -0
  131. package/models/PCA20035+solar/transforms/gain.md +45 -0
  132. package/models/PCA20035+solar/transforms/geolocationFromGroundfix.md +67 -0
  133. package/models/PCA20035+solar/transforms/geolocationFromMessage.md +80 -0
  134. package/models/PCA20035+solar/transforms/humidity.md +43 -0
  135. package/models/PCA20035+solar/transforms/networkInfo.md +84 -0
  136. package/models/PCA20035+solar/transforms/pressure.md +43 -0
  137. package/models/PCA20035+solar/transforms/temperature.md +43 -0
  138. package/models/README.md +10 -0
  139. package/models/asset_tracker_v2+AWS/README.md +6 -0
  140. package/models/asset_tracker_v2+AWS/examples/examples.spec.ts +229 -0
  141. package/models/asset_tracker_v2+AWS/examples/shadow/example-1.json +24 -0
  142. package/models/asset_tracker_v2+AWS/examples/shadow/example-2.json +30 -0
  143. package/models/asset_tracker_v2+AWS/examples/shadow/example-3.json +37 -0
  144. package/models/asset_tracker_v2+AWS/examples/shadow/example-4.json +48 -0
  145. package/models/asset_tracker_v2+AWS/examples/shadow/example-5.json +43 -0
  146. package/models/asset_tracker_v2+AWS/transforms/GNSS.md +66 -0
  147. package/models/asset_tracker_v2+AWS/transforms/battery-voltage.md +50 -0
  148. package/models/asset_tracker_v2+AWS/transforms/device-info.md +61 -0
  149. package/models/asset_tracker_v2+AWS/transforms/env.md +69 -0
  150. package/models/asset_tracker_v2+AWS/transforms/fuel-gauge.md +62 -0
  151. package/models/asset_tracker_v2+AWS/transforms/roam.md +100 -0
  152. package/models/asset_tracker_v2+AWS/transforms/solar.md +58 -0
  153. package/models/check-model-rules.ts +125 -0
  154. package/models/kartverket-vasstandsdata/README.md +13 -0
  155. package/models/models.ts +36 -0
  156. package/models/types.ts +17 -0
  157. package/package.json +111 -0
  158. package/senml/SenMLSchema.spec.ts +21 -0
  159. package/senml/SenMLSchema.ts +74 -0
  160. package/senml/hasValue.spec.ts +19 -0
  161. package/senml/hasValue.ts +12 -0
  162. package/senml/lwm2mToSenML.spec.ts +74 -0
  163. package/senml/lwm2mToSenML.ts +62 -0
  164. package/senml/parseResourceId.spec.ts +13 -0
  165. package/senml/parseResourceId.ts +23 -0
  166. package/senml/senMLtoLwM2M.spec.ts +181 -0
  167. package/senml/senMLtoLwM2M.ts +121 -0
  168. package/senml/validateSenML.spec.ts +16 -0
  169. 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
+ ```