diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b44d4f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +config.json +devices.json +node_modules/ \ No newline at end of file diff --git a/decons-api.json b/decons-api.json deleted file mode 100644 index bd4a900..0000000 --- a/decons-api.json +++ /dev/null @@ -1,429 +0,0 @@ -{ - "1": { - "config": { - "configured": true, - "on": true, - "sunriseoffset": 30, - "sunsetoffset": -30 - }, - "etag": "3cb5d1b086eae04ee0b0de75db5aae56", - "manufacturername": "Philips", - "modelid": "PHDL00", - "name": "Daylight", - "state": { - "dark": true, - "daylight": false, - "lastupdated": "2024-12-16T23:25:15.812", - "status": 230, - "sunrise": "2024-12-16T12:22:55", - "sunset": "2024-12-16T21:49:01" - }, - "swversion": "1.0", - "type": "Daylight", - "uniqueid": "00:21:2e:ff:ff:0e:53:ba-01" - }, - "10": { - "config": { - "battery": 0, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "ba0d365520b4b5ee5a4b271c8951dbae", - "lastannounced": null, - "lastseen": "2024-12-14T02:12Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Gantry Sensor", - "state": { - "humidity": 4341, - "lastupdated": "2024-10-15T01:39:20.620" - }, - "swversion": "0.0.0_0005", - "type": "ZHAHumidity", - "uniqueid": "00:15:8d:00:8b:11:04:bf-01-0405" - }, - "11": { - "config": { - "battery": 0, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "71c01da7a56608800e3b9491fea7cd35", - "lastannounced": null, - "lastseen": "2024-12-14T02:12Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Gantry Sensor", - "state": { - "lastupdated": "2024-10-15T01:39:20.636", - "pressure": 1006 - }, - "swversion": "0.0.0_0005", - "type": "ZHAPressure", - "uniqueid": "00:15:8d:00:8b:11:04:bf-01-0403" - }, - "12": { - "config": { - "battery": 100, - "offset": 0, - "on": true, - "reachable": true - }, - "ep": 1, - "etag": "4a70d4e90337e257df42213e69fa0b9e", - "lastannounced": null, - "lastseen": "2024-12-16T23:38Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "lumi.weather", - "state": { - "lastupdated": "2024-12-16T23:38:01.353", - "temperature": 2161 - }, - "swversion": "0.0.0_0005", - "type": "ZHATemperature", - "uniqueid": "00:15:8d:00:8b:11:52:80-01-0402" - }, - "13": { - "config": { - "battery": 100, - "offset": 0, - "on": true, - "reachable": true - }, - "ep": 1, - "etag": "8adbf9da29ad11e1d7e20e42d25a7545", - "lastannounced": null, - "lastseen": "2024-12-16T23:38Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Humidity 13", - "state": { - "humidity": 3925, - "lastupdated": "2024-12-16T23:38:01.370" - }, - "swversion": "0.0.0_0005", - "type": "ZHAHumidity", - "uniqueid": "00:15:8d:00:8b:11:52:80-01-0405" - }, - "14": { - "config": { - "battery": 100, - "offset": 0, - "on": true, - "reachable": true - }, - "ep": 1, - "etag": "7718ada5c54f6203146dce76b0c9843c", - "lastannounced": null, - "lastseen": "2024-12-16T23:38Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Pressure 14", - "state": { - "lastupdated": "2024-12-16T23:38:01.385", - "pressure": 1016 - }, - "swversion": "0.0.0_0005", - "type": "ZHAPressure", - "uniqueid": "00:15:8d:00:8b:11:52:80-01-0403" - }, - "15": { - "config": { - "alert": "none", - "battery": 90, - "on": true, - "reachable": true - }, - "ep": 1, - "etag": "86c358ee17d41f2d2041220ecf71aaaf", - "lastannounced": null, - "lastseen": "2024-12-16T23:16Z", - "manufacturername": "IKEA of Sweden", - "mode": 1, - "modelid": "Remote Control N2", - "name": "Remote Control N2", - "productid": "E2002", - "state": { - "buttonevent": 2002, - "lastupdated": "2024-12-14T16:51:00.643" - }, - "swversion": "2.4.5", - "type": "ZHASwitch", - "uniqueid": "60:b6:47:ff:fe:da:c5:9a-01-1000" - }, - "16": { - "config": { - "alert": "none", - "battery": 86, - "delay": 20, - "group": "20000", - "on": true, - "reachable": true - }, - "ep": 2, - "etag": "c9ca0728b16f0e6213df3399027aa8d2", - "lastannounced": null, - "lastseen": "2024-12-16T23:46Z", - "manufacturername": "IKEA of Sweden", - "modelid": "VALLHORN Wireless Motion Sensor", - "name": "VALLHORN Wireless Motion Sensor", - "productid": "E2134", - "productname": "Vallhorn presence sensor", - "state": { - "lastupdated": "2024-12-16T23:47:21.195", - "presence": false - }, - "swversion": "1.0.64", - "type": "ZHAPresence", - "uniqueid": "ec:f6:4c:ff:fe:45:14:74-02-0406" - }, - "17": { - "config": { - "alert": "none", - "battery": 86, - "on": true, - "reachable": true, - "tholddark": 12000, - "tholdoffset": 7000 - }, - "ep": 2, - "etag": "24ec1371b2d96fa1a69d9daee8ab1cf5", - "lastannounced": null, - "lastseen": "2024-12-16T23:46Z", - "manufacturername": "IKEA of Sweden", - "modelid": "VALLHORN Wireless Motion Sensor", - "name": "LightLevel 17", - "productname": "Vallhorn light sensor", - "state": { - "dark": false, - "daylight": false, - "lastupdated": "2024-12-16T23:46:55.871", - "lightlevel": 13979, - "lux": 25 - }, - "swversion": "1.0.64", - "type": "ZHALightLevel", - "uniqueid": "ec:f6:4c:ff:fe:45:14:74-02-0400" - }, - "18": { - "config": { - "duration": 60, - "on": true, - "reachable": true - }, - "etag": "276c412778404a53b07990dda6684b05", - "manufacturername": "Phoscon", - "modelid": "PHOSCON_VPIR", - "name": "VPIR PIR-Motion Detected", - "state": { - "lastupdated": "2024-12-16T23:37:24.313", - "presence": false - }, - "swversion": "1.0", - "type": "CLIPPresence", - "uniqueid": "vpir-1730675210681" - }, - "19": { - "config": { - "on": true, - "reachable": true - }, - "etag": "c8a5ad0393286a78c723abcd101ef5c6", - "manufacturername": "Phoscon", - "modelid": "PHOSCON_FSM_STATE", - "name": "FSM_STATE PIR-Motion Detected", - "state": { - "lastupdated": "2024-12-16T23:39:25.313", - "status": 0 - }, - "swversion": "1.0", - "type": "CLIPGenericStatus", - "uniqueid": "fsm-state-1730675210681" - }, - "2": { - "config": { - "mode": "sunset", - "offset": 0, - "on": true, - "reachable": true - }, - "etag": "725c26278a25172da0e800e337f01a00", - "manufacturername": "Phoscon", - "modelid": "DLOFFSET1211", - "name": "Offset Ref P-SS", - "state": { - "lastupdated": "2024-12-16T05:10:05.812", - "localtime": "2024-12-16T16:49:01" - }, - "swversion": "1.0", - "type": "CLIPDaylightOffset", - "uniqueid": "AB:30:F8:4B-C7E4-2E28-8694-5F:5F:73:81:03:39" - }, - "20": { - "config": { - "on": true, - "reachable": true - }, - "etag": "c9ca0728b16f0e6213df3399027aa8d2", - "manufacturername": "Phoscon", - "modelid": "Motion Detected_CLIPGenericFlag_CLIPPresence", - "name": "VSen_CLIPPresence_Motion Detected", - "state": { - "flag": false, - "lastupdated": "2024-12-16T23:47:21.195" - }, - "swversion": "1.0", - "type": "CLIPGenericFlag", - "uniqueid": "vSen-1730675238128" - }, - "3": { - "config": { - "mode": "sunrise", - "offset": 0, - "on": true, - "reachable": true - }, - "etag": "484cd31e2377a36774cf21a1bcbd0a79", - "manufacturername": "Phoscon", - "modelid": "DLOFFSET2100", - "name": "Offset Ref P-SR", - "state": { - "lastupdated": "2024-12-16T05:10:35.813", - "localtime": "2024-12-16T07:22:55" - }, - "swversion": "1.0", - "type": "CLIPDaylightOffset", - "uniqueid": "D6:32:DB:82-0CB4-7262-84C9-61:D3:57:F3:FE:64" - }, - "4": { - "config": { - "mode": "sunset", - "offset": -392, - "on": true, - "reachable": true - }, - "etag": "d7d3cd7bc958d8df033541fdb42e6d5b", - "manufacturername": "Phoscon", - "modelid": "DLOFFSET2112", - "name": "Offset Ref P10002", - "state": { - "lastupdated": "2024-12-16T05:10:15.813", - "localtime": "2024-12-16T10:17:01" - }, - "swversion": "1.0", - "type": "CLIPDaylightOffset", - "uniqueid": "3B:FD:76:20-EC05-1E9C-BBDC-E8:E1:48:86:19:F3" - }, - "5": { - "config": { - "mode": "sunset", - "offset": 167, - "on": true, - "reachable": true - }, - "etag": "2051531b7bd6e5761f154102b954d15d", - "manufacturername": "Phoscon", - "modelid": "DLOFFSET1002", - "name": "Offset Ref P200", - "state": { - "lastupdated": "2024-12-16T05:10:45.812", - "localtime": "2024-12-16T19:36:01" - }, - "swversion": "1.0", - "type": "CLIPDaylightOffset", - "uniqueid": "8E:3E:86:90-78E7-E4BD-BE13-CE:E9:F1:92:DC:33" - }, - "6": { - "config": { - "battery": 85, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "d9651d3bc6eb16709fb30473b907d7fa", - "lastannounced": "2024-12-14T02:37:27Z", - "lastseen": "2024-12-14T03:18Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Server Bathroom", - "state": { - "lastupdated": "2024-12-14T03:18:41.381", - "temperature": 1417 - }, - "swversion": "0.0.0_0005", - "type": "ZHATemperature", - "uniqueid": "00:15:8d:00:8b:11:04:f3-01-0402" - }, - "7": { - "config": { - "battery": 85, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "d9651d3bc6eb16709fb30473b907d7fa", - "lastannounced": "2024-12-14T02:37:27Z", - "lastseen": "2024-12-14T03:18Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Server Bathroom", - "state": { - "humidity": 3555, - "lastupdated": "2024-12-14T03:18:41.397" - }, - "swversion": "0.0.0_0005", - "type": "ZHAHumidity", - "uniqueid": "00:15:8d:00:8b:11:04:f3-01-0405" - }, - "8": { - "config": { - "battery": 85, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "d9651d3bc6eb16709fb30473b907d7fa", - "lastannounced": "2024-12-14T02:37:27Z", - "lastseen": "2024-12-14T03:18Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Server Bathroom", - "state": { - "lastupdated": "2024-12-14T03:18:41.413", - "pressure": 1034 - }, - "swversion": "0.0.0_0005", - "type": "ZHAPressure", - "uniqueid": "00:15:8d:00:8b:11:04:f3-01-0403" - }, - "9": { - "config": { - "battery": 0, - "offset": 0, - "on": true, - "reachable": false - }, - "ep": 1, - "etag": "ba0d365520b4b5ee5a4b271c8951dbae", - "lastannounced": null, - "lastseen": "2024-12-14T02:12Z", - "manufacturername": "LUMI", - "modelid": "lumi.weather", - "name": "Gantry Sensor", - "state": { - "lastupdated": "2024-10-15T01:39:20.604", - "temperature": 2356 - }, - "swversion": "0.0.0_0005", - "type": "ZHATemperature", - "uniqueid": "00:15:8d:00:8b:11:04:bf-01-0402" - } -} diff --git a/example.config.json b/example.config.json index cd6fa90..2ac5916 100644 --- a/example.config.json +++ b/example.config.json @@ -8,7 +8,8 @@ "influx": { "url": "http://influx.hacdc.org", "apiKey": "", - "bucket": "" + "bucket": "", + "orgId": "" } }, "devices": [ @@ -17,7 +18,7 @@ "uniqueId": "", "valueKey": "", "valueModifier": "1", - "unit": "" + "influxKey": "" } ] } diff --git a/getDevices.js b/getDevices.js new file mode 100644 index 0000000..e38c0e1 --- /dev/null +++ b/getDevices.js @@ -0,0 +1,9 @@ +const axios = require('axios') +const fs = require('fs') +let config = JSON.parse(fs.readFileSync('config.json', 'utf8')) + +axios.get(config.servers.deconz.url + '/api/' + config.servers.deconz.apiKey + '/sensors') +.then(response => { + // console.log(response.data) + fs.writeFileSync('devices.json', JSON.stringify(response.data, null, 2)) +}) \ No newline at end of file diff --git a/getKey.js b/getKey.js new file mode 100644 index 0000000..65e4eb5 --- /dev/null +++ b/getKey.js @@ -0,0 +1,37 @@ +import axios from 'axios' +import fs from 'fs' +import meow from 'meow' +let config = JSON.parse(fs.readFileSync('config.json', 'utf8')) + +const cli = meow(` + Usage + $ node getKey.js + + Options + --force, -f Overwrite existing config.json file +`, { + importMeta: import.meta, + flags: { + force: { + type: 'boolean', + shortFlag: 'f' + } + } +}) + +if (config.servers.deconz.apiKey.length > 0 && !cli.flags.force) { + console.log("API Key already exists in config.json. Use --force to overwrite.") + process.exit(1) +} + +axios.post(config.servers.deconz.url + '/api', { + devicetype: config.servers.deconz.clientName +'l', +}) +.then(response => { + console.log("Got API Key, saving to config.json") + config.servers.deconz.apiKey = response.data[0].success.username + fs.writeFileSync('config.json', JSON.stringify(config, null, 2)) +}).catch(error => { + console.log("Did you enable API linking in Phoscon?") + console.log(error.status) +}) \ No newline at end of file diff --git a/index.js b/index.js index 0864347..0bebcf8 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,68 @@ +import axios from 'axios' +import fs from 'fs' +let config = JSON.parse(fs.readFileSync('config.json', 'utf8')) +// name influxKey=data[valueKey],influxKey=data[valueKey],... -const config = JSON.parse(fs.readFileSync('config.json', 'utf8')); +axios.get(config.servers.deconz.url + '/api/' + config.servers.deconz.apiKey + '/sensors') +.then(response => { + let sensors = response.data + let data = [] + let influxPayload = "" + let devices = {} + // Filter only the data we find relevant + for (let sensor in sensors) { // sensor: "10" + try { + if (sensors[sensor].config.reachable) { + data.push({ + uniqueId: sensors[sensor].uniqueid, + data: sensors[sensor].state + }) + } + } catch (error) { + console.error(error) + } + } + // Assemble data from specified sensors + [...config.devices].forEach(device => { + try{ + if (!devices[device.name]) { + devices[device.name] = [] + } + let relevantData = data.filter(sensor => sensor.uniqueId.includes(device.uniqueId)) + let value = relevantData[0].data[device.valueKey]/device.valueModifier + if (device.valueModifier != 1){ + value = Math.round(relevantData[0].data[device.valueKey]/device.valueModifier).toFixed(2) + } + devices[device.name].push(`${device.influxKey}=${value}`) + } catch (error) { + console.error(error) + } + }) + + // Assemble influx payload + for (let device in devices) { + influxPayload += `${device} ${devices[device].join(",")}\n` + } + influxPayload = influxPayload.slice(0, -1) + + // Send data to influx + axios.post(`${config.servers.influx.url}/api/v2/write?orgID=${config.servers.influx.orgId}&bucket=${config.servers.influx.bucket}`, + influxPayload, + { + headers: { + Authorization: `Token ${config.servers.influx.apiKey}`, + 'Content-Type': 'text/plain' + } + } + ).then(response => { + console.log(response.data) + }) + .catch(error => { + console.log(error.response.data) + }) +}) +.catch(error => { + console.error(error) +}) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..eeb0c9a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,128 @@ +{ + "name": "dec0ntamination", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dec0ntamination", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "axios": "^1.7.9", + "meow": "^13.2.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index 492e5df..1697722 100644 --- a/package.json +++ b/package.json @@ -11,5 +11,10 @@ "url": "https://git.corrupt.link/liz/dec0ntamination" }, "author": "Liz Cray", - "license": "MIT" + "license": "MIT", + "type": "module", + "dependencies": { + "axios": "^1.7.9", + "meow": "^13.2.0" + } }