diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c01e05e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/src/index.ts", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1707d03..4a22b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -792,11 +792,11 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "axios": { - "version": "0.25.0", - "resolved": "http://10.0.0.30:18081/repository/npm-group/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "0.26.1", + "resolved": "http://10.0.0.30:18081/repository/npm-group/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "requires": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.14.8" } }, "balanced-match": { @@ -2370,6 +2370,16 @@ "requires": { "axios": "^0.25.0", "form-data": "^4.0.0" + }, + "dependencies": { + "axios": { + "version": "0.25.0", + "resolved": "http://10.0.0.30:18081/repository/npm-group/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "requires": { + "follow-redirects": "^1.14.7" + } + } } }, "node-preload": { diff --git a/package.json b/package.json index c5edde6..d7d65bb 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "author": "Laur Ivan", "license": "GPL-3.0-or-later", "dependencies": { + "axios": "^0.26.1", "dotenv": "^16.0.0", + "form-data": "^4.0.0", "fs": "0.0.1-security", "knx-lib": "0.0.3", "node-appwrite": "^5.0.0", diff --git a/src/appwrite.ts b/src/appwrite.ts new file mode 100644 index 0000000..4ea0d5d --- /dev/null +++ b/src/appwrite.ts @@ -0,0 +1,8 @@ +import { Client, Database } from "node-appwrite"; + +const client: Client = new Client(); +client.setEndpoint(process.env.APPWRITE_ENDPOINT_URL) + .setProject(process.env.APPWRITE_PROJECT_ID) + .setKey(process.env.APPWRITE_API_KEY) + +export const database = new Database(client) \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 557e9c2..1ad6512 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,7 @@ const connection = new knx.Connection({ connected: function () { console.log('Connected!'); }, - event: function (evt: any, src: any, dest: any, value: any) { + event: function (evt: string, src: string, dest: string, value: Buffer) { console.log("%s **** KNX EVENT: %j, src: %j, dest: %j, value: %j", new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''), evt, src, dest, value); diff --git a/src/processTelegram.ts b/src/processTelegram.ts new file mode 100644 index 0000000..5c239ad --- /dev/null +++ b/src/processTelegram.ts @@ -0,0 +1,74 @@ +import { Client, Database, Query } from 'node-appwrite' +import { DataPointType } from 'dptlib/lib/DataPointType' +import { DPT } from 'dptlib/lib/definitions' +import { database } from './appwrite' + +// TODO: Get the map from a list of environment variables +const dptMap: { [index: string]: string } = { + 'DPT1.1': '61f46d9b81de70ac7ece', // percent + 'DPT5.1': '61f46d535343e1aa557f', // percent + 'DPT9.1': '61f46c7fc8869fa2c6a1' // temperature +} + +// TODO: add types +export async function getDPTforAddress(record: any) { + let list = await database.listDocuments(process.env.APPWRITE_KNX_ADDRESS_COLLECTION, [ + Query.equal('project_id', process.env.APPWRITE_PROJECT_ID), + Query.equal('address', record.dest_addr) + ]); + + // Throw an error if the address is not unique + // + if (list.documents.length != 1) { + throw 'The address is not unique!' + } + + record.dpt = DataPointType.TYPES[(list.documents[0] as any).dpt] + record.addressId = list.documents[0]['$id'] + return record +} + +// TODO: Add types +export async function upsert(entry: any) { + let dptId: string = entry.dpt.id + '.' + entry.dpt.subtypeid + let collectionId = (dptId in dptMap) ? dptMap[dptId] : undefined + + if (collectionId === undefined) { + console.log('Cannot process [%s] with value %j', dptId, [entry.src_addr, entry.apdu.dptData]) + return; + } + + // Upsert + let list_items = await database.listDocuments(collectionId, [ + Query.equal('dpt', dptId), + Query.equal('timestamp', entry.timestamp.getTime()) + ]) + + let data = { + address_id: entry.addressId, + dpt: dptId, + timestamp: entry.timestamp.getTime(), + value: entry.apdu.dptData + } + + if (list_items.total === 0) { + console.debug('insert') + await database.createDocument(collectionId, 'unique()', data); + } else if (list_items.total === 1) { + var doc = list_items.documents[0] as any + if ( + doc.address_id !== data.address_id && + doc.dpt !== data.dpt && + doc.timestamp !== data.timestamp && + doc.value !== data.value + ) { + console.log('update') + await database.updateDocument(collectionId, doc['$id'], data); + } else { + console.debug('Skip %s', doc['$id']) + } + + } else { + throw 'Multiple entries!' + } +} \ No newline at end of file diff --git a/src/types.d.ts b/src/types.d.ts index e69de29..0d53ac5 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -0,0 +1,3 @@ +declare module "node-appwrite" { + export type Response = any; +} \ No newline at end of file