diff --git a/lambda/updatesToLwM2M.ts b/lambda/updatesToLwM2M.ts index 7797fb5..c5afbf4 100644 --- a/lambda/updatesToLwM2M.ts +++ b/lambda/updatesToLwM2M.ts @@ -11,7 +11,7 @@ import { fromEnv } from '@nordicsemiconductor/from-env' import { transformShadowUpdateToLwM2M } from '../lwm2m/transformShadowUpdateToLwM2M.js' import { models, type LwM2MObject } from '@hello.nrfcloud.com/proto-lwm2m' import { ulid } from 'ulid' -import { updatesToShadow } from '../lwm2m/objectsToShadow.js' +import { objectsToShadow } from '../lwm2m/objectsToShadow.js' const { tableName } = fromEnv({ tableName: 'TABLE_NAME', @@ -77,7 +77,7 @@ const updateShadow = async ( shadowName: 'lwm2m', payload: JSON.stringify({ state: { - reported: updatesToShadow(objects), + reported: objectsToShadow(objects), }, }), }), diff --git a/lwm2m/objectsToShadow.spec.ts b/lwm2m/objectsToShadow.spec.ts new file mode 100644 index 0000000..6ba9740 --- /dev/null +++ b/lwm2m/objectsToShadow.spec.ts @@ -0,0 +1,70 @@ +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { objectsToShadow } from './objectsToShadow.js' + +void describe('objectsToShadow()', () => { + void it('should convert a list of LwM2M objects to a shadow document', () => + assert.deepEqual( + objectsToShadow([ + { + ObjectID: 14205, + Resources: { + '0': 27.69, + '1': 18.9, + '2': 97.271, + '99': new Date('2023-11-05T15:13:28.705Z'), + }, + }, + { + ObjectID: 14202, + Resources: { + '0': 99, + '1': 4.174, + '2': 0, + '3': 25.9, + '99': new Date('2023-11-05T15:13:49.276Z'), + }, + }, + { + ObjectID: 14203, + Resources: { + '0': 'LTE-M', + '1': 20, + '2': -93, + '3': 2305, + '4': 34237196, + '5': 24202, + '6': '100.81.95.75', + '11': 7, + '99': new Date('2023-11-05T15:13:28.795Z'), + }, + }, + ]), + { + '14205:1.0': { + '0': 27.69, + '1': 18.9, + '2': 97.271, + '99': 1699197208705, + }, + '14203:1.0': { + '0': 'LTE-M', + '1': 20, + '2': -93, + '3': 2305, + '4': 34237196, + '5': 24202, + '6': '100.81.95.75', + '11': 7, + '99': 1699197208795, + }, + '14202:1.0': { + '0': 99, + '1': 4.174, + '2': 0, + '3': 25.9, + '99': 1699197229276, + }, + }, + )) +}) diff --git a/lwm2m/objectsToShadow.ts b/lwm2m/objectsToShadow.ts index 1d79656..822eb2c 100644 --- a/lwm2m/objectsToShadow.ts +++ b/lwm2m/objectsToShadow.ts @@ -1,18 +1,12 @@ import type { LwM2MObject } from '@hello.nrfcloud.com/proto-lwm2m' -type LwM2MShadow = Record< +export type LwM2MShadow = Record< string, - Record< - string, - { - v: string | number | boolean - ts: number - } - > + Record > -export const updatesToShadow = (updates: Array): LwM2MShadow => - updates +export const objectsToShadow = (objects: Array): LwM2MShadow => + objects .sort((u1, u2) => { const d1 = Object.values(u1.Resources).find( (r) => r instanceof Date, @@ -23,22 +17,16 @@ export const updatesToShadow = (updates: Array): LwM2MShadow => return d1.getTime() > d2.getTime() ? 1 : -1 }) .reduce((shadow, update) => { - const ts = ( - Object.values(update.Resources).find((r) => r instanceof Date) as Date - ).getTime() const key = `${update.ObjectID}:${update.ObjectVersion ?? '1.0'}` return { ...shadow, [key]: { ...(shadow[key] ?? {}), ...Object.entries(update.Resources).reduce((resources, [k, v]) => { - if (v instanceof Date) return resources + if (v instanceof Date) return { ...resources, [k]: v.getTime() } return { ...resources, - [k]: { - v, - ts, - }, + [k]: v, } }, {}), }, diff --git a/lwm2m/shadowToObjects.spec.ts b/lwm2m/shadowToObjects.spec.ts new file mode 100644 index 0000000..d30a2c9 --- /dev/null +++ b/lwm2m/shadowToObjects.spec.ts @@ -0,0 +1,73 @@ +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { shadowToObjects } from './shadowToObjects.js' + +void describe('shadowToObjects()', () => { + void it('should convert a shadow to LwM2M objects', () => + assert.deepEqual( + shadowToObjects({ + '14205:1.0': { + '0': 27.63, + '1': 19.354, + '2': 97.465, + '99': 1699217636982, + }, + '14203:1.0': { + '0': 'LTE-M', + '1': 20, + '2': -90, + '3': 2305, + '4': 34237196, + '5': 24202, + '6': '100.81.95.75', + '11': 7, + '99': 1699217637072, + }, + '14202:1.0': { + '0': 99, + '1': 4.174, + '2': 0, + '3': 25.9, + '99': 1699217657553, + }, + }), + [ + { + ObjectID: 14205, + ObjectVersion: '1.0', + Resources: { + '0': 27.63, + '1': 19.354, + '2': 97.465, + '99': new Date(1699217636982), + }, + }, + { + ObjectID: 14203, + ObjectVersion: '1.0', + Resources: { + '0': 'LTE-M', + '1': 20, + '2': -90, + '3': 2305, + '4': 34237196, + '5': 24202, + '6': '100.81.95.75', + '11': 7, + '99': new Date(1699217637072), + }, + }, + { + ObjectID: 14202, + ObjectVersion: '1.0', + Resources: { + '0': 99, + '1': 4.174, + '2': 0, + '3': 25.9, + '99': new Date(1699217657553), + }, + }, + ], + )) +}) diff --git a/lwm2m/shadowToObjects.ts b/lwm2m/shadowToObjects.ts new file mode 100644 index 0000000..034046c --- /dev/null +++ b/lwm2m/shadowToObjects.ts @@ -0,0 +1,32 @@ +import { + timestampResources, + type LwM2MObject, +} from '@hello.nrfcloud.com/proto-lwm2m' +import type { LwM2MShadow } from './objectsToShadow' + +export const shadowToObjects = (shadow: LwM2MShadow): LwM2MObject[] => + Object.entries(shadow) + .map(([ObjectIdAndVersion, Resources]) => { + const [ObjectIDString, ObjectVersion] = ObjectIdAndVersion.split(':') as [ + string, + string, + ] + const ObjectID = parseInt(ObjectIDString, 10) + const tsResource = timestampResources[ObjectID] + if (tsResource === undefined) return null + return { + ObjectID, + ObjectVersion, + Resources: Object.entries(Resources).reduce( + (Resources, [k, v]) => ({ + ...Resources, + [k]: + typeof v === 'number' && parseInt(k, 10) === tsResource + ? new Date(v) + : v, + }), + {}, + ), + } + }) + .filter((o) => o !== null) as LwM2MObject[] diff --git a/lwm2m/updatesToShadow.spec.ts b/lwm2m/updatesToShadow.spec.ts deleted file mode 100644 index 26920da..0000000 --- a/lwm2m/updatesToShadow.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import assert from 'node:assert/strict' -import { describe, it } from 'node:test' -import { updatesToShadow } from './objectsToShadow.js' - -void describe('updatesToShadow()', () => { - void it('should convert a list update LwM2M objects to a shadow document', () => - assert.deepEqual( - updatesToShadow([ - { - ObjectID: 14205, - Resources: { - '0': 27.69, - '1': 18.9, - '2': 97.271, - '99': new Date('2023-11-05T15:13:28.705Z'), - }, - }, - { - ObjectID: 14202, - Resources: { - '0': 99, - '1': 4.174, - '2': 0, - '3': 25.9, - '99': new Date('2023-11-05T15:13:49.276Z'), - }, - }, - { - ObjectID: 14203, - Resources: { - '0': 'LTE-M', - '1': 20, - '2': -93, - '3': 2305, - '4': 34237196, - '5': 24202, - '6': '100.81.95.75', - '11': 7, - '99': new Date('2023-11-05T15:13:28.795Z'), - }, - }, - ]), - { - '14205:1.0': { - '0': { v: 27.69, ts: 1699197208705 }, - '1': { v: 18.9, ts: 1699197208705 }, - '2': { v: 97.271, ts: 1699197208705 }, - }, - '14203:1.0': { - '0': { v: 'LTE-M', ts: 1699197208795 }, - '1': { v: 20, ts: 1699197208795 }, - '2': { v: -93, ts: 1699197208795 }, - '3': { v: 2305, ts: 1699197208795 }, - '4': { v: 34237196, ts: 1699197208795 }, - '5': { v: 24202, ts: 1699197208795 }, - '6': { v: '100.81.95.75', ts: 1699197208795 }, - '11': { v: 7, ts: 1699197208795 }, - }, - '14202:1.0': { - '0': { v: 99, ts: 1699197229276 }, - '1': { v: 4.174, ts: 1699197229276 }, - '2': { v: 0, ts: 1699197229276 }, - '3': { v: 25.9, ts: 1699197229276 }, - }, - }, - )) -})