diff --git a/package-lock.json b/package-lock.json index e470053a74..e5737e7671 100644 --- a/package-lock.json +++ b/package-lock.json @@ -524,5 +524,389 @@ "node": ">=10" } } + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-cli": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-0.6.0.tgz", + "integrity": "sha512-85CnFX7Nft+UvmAi1fHNnqrtOiSL8KW/o4hS096AVT1P+45SAe5YO242pn9+rqC9F1UKyeFioCG52jKXTOI2FA==", + "dev": true, + "requires": { + "ajv": "^3.8.0", + "glob": "^7.0.3", + "minimist": "^1.2.0" + }, + "dependencies": { + "ajv": { + "version": "3.8.10", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-3.8.10.tgz", + "integrity": "sha512-h74deHfbgeB8TuWq6UQxP4fwsCbo9T+pvWofl4pEdZzI6lMSSFAWZr3PsNXHjSFABCj49j6fgbzUTHWIrMdHMw==", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "image-size": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.9.7.tgz", + "integrity": "sha512-KRVgLNZkr00YGN0qn9MlIrmlxbRhsCcEb1Byq3WKGnIV4M48iD185cprRtaoK4t5iC+ym2Q5qlArxZ/V1yzDgA==", + "requires": { + "queue": "6.0.2" + } + }, + "image-type": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/image-type/-/image-type-4.1.0.tgz", + "integrity": "sha512-CFJMJ8QK8lJvRlTCEgarL4ro6hfDQKif2HjSvYCdQZESaIPV4v9imrf7BQHK+sQeTeNeMpWciR9hyC/g8ybXEg==", + "requires": { + "file-type": "^10.10.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "dev": true, + "requires": { + "jsonify": "^0.0.1" + } + }, + "jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } + }, + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } } } diff --git a/vendor/dingtek/dc413-codec.yaml b/vendor/dingtek/dc413-codec.yaml new file mode 100644 index 0000000000..eec92f1f82 --- /dev/null +++ b/vendor/dingtek/dc413-codec.yaml @@ -0,0 +1,113 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dc413.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x02, 0x11, 0x06, 0xA4, 0x00, 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + level: 1700 + alarmLevel: false + alarmFall: false + alarmBattery: false + angle: 0 + temperature: 22 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x19, 0x03, 0x02, 0x06, 0x3C, 0x1E, 0x4B, 0x14, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81] + output: + data: + firmware: '3.2' + uploadInterval: 6 + detectInterval: 60 + levelThreshold: 30 + fallThreshold: 20 + fallEnable: true + workMode: 1 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dc413.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change periodic detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change full alarm threshold to 35cm + input: + data: + levelThreshold: 35 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + fPort: 3 + - description: change motion alarm threshold to 45° + input: + data: + fallThreshold: 45 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x32, 0x44, 0x38, 0x31] + fPort: 3 + - description: enable tilt detection + input: + data: + fallEnable: true + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dc413.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change full alarm threshold to 35cm + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + output: + data: + levelThreshold: 35 + - description: change motion alarm threshold to 45° + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x32, 0x44, 0x38, 0x31] + output: + data: + fallThreshold: 45 + - description: enable motion detection + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31] + output: + data: + fallEnable: true diff --git a/vendor/dingtek/dc413.js b/vendor/dingtek/dc413.js new file mode 100644 index 0000000000..14fc7cf71e --- /dev/null +++ b/vendor/dingtek/dc413.js @@ -0,0 +1,223 @@ +var units = [' ℃', ' hours', ' minutes', ' mm', ' °', ' cm']; +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + + switch (input.bytes.length) { + case 17: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmFall: Boolean(input.bytes[12] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + angle: input.bytes[9] & (0x0f === 0x00) ? input.bytes[10] : 0 - input.bytes[10], + temperature: input.bytes[8], + frameCounter: (input.bytes[13] << 8) + input.bytes[14], + }, + }; + case 25: + var data_type = input.bytes[3]; + if (data_type === 0x03) { + return { + // Decoded parameter + data: { + firmware: input.bytes[5] + '.' + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + levelThreshold: input.bytes[9], + fallThreshold: input.bytes[11], + fallEnable: Boolean(input.bytes[12]), + workMode: input.bytes[14], + }, + }; + } + default: + return { + errors: ['wrong length'], + }; + } +} + +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var periodic_interval = input.data.uploadInterval; + var periodic_interval_high = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var periodic_interval_low = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (periodic_interval > 168 || periodic_interval < 1) { + return { + errors: ['periodic upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, periodic_interval_high, periodic_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detection_interval = input.data.detectInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['periodic detection interval range 1-60 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var full_alarm_threshold = input.data.levelThreshold; + var full_alarm_threshold_high = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var full_alarm_threshold_low = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (full_alarm_threshold > 255 || full_alarm_threshold < 15) { + return { + errors: ['full alarm threshold range 15-255 cm.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, full_alarm_threshold_high, full_alarm_threshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.fallThreshold != null && !isNaN(input.data.fallThreshold)) { + var tilt_alarm_threshold = input.data.fallThreshold; + var tilt_alarm_threshold_high = tilt_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var tilt_alarm_threshold_low = tilt_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (tilt_alarm_threshold > 90 || tilt_alarm_threshold < 15) { + return { + errors: ['tilt alarm threshold range 15-90 °.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, tilt_alarm_threshold_high, tilt_alarm_threshold_low, 0x38, 0x31], + }; + } + } + if (input.data.fallEnable != null && input.data.fallEnable === !!input.data.fallEnable) { + var tilt_enable = input.data.fallEnable; + if (tilt_enable === true) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x39, 0x38, 0x31], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 4: + return { + data: { + fallThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x09: + return { + data: { + fallEnable: false, + }, + }; + case 0x0a: + return { + data: { + fallEnable: true, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} diff --git a/vendor/dingtek/dc413.png b/vendor/dingtek/dc413.png new file mode 100644 index 0000000000..d2a5322ec9 Binary files /dev/null and b/vendor/dingtek/dc413.png differ diff --git a/vendor/dingtek/dc413.yaml b/vendor/dingtek/dc413.yaml new file mode 100644 index 0000000000..e295af4dae --- /dev/null +++ b/vendor/dingtek/dc413.yaml @@ -0,0 +1,155 @@ +name: dc413 +description: The CNDingtek dc413 is a smart manhole sensor that integrates a liquid/solid level sensor and a movement sensor. Suitable for measuring water and sewage levels in wells and manholes. The data is sent to a LoRaWAN network, then the application server for further processing. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.2' + numeric: 12 + - version: '1.3' + numeric: 13 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.2' + - '1.3' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc413-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc413-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc413-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dc413-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dc413-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dc413-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc413-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + - motion + #- full_alarm + #- fire_alarm + #- battery_alarm + #- angle + #- temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 115 + height: 40 + +# Weight in grams (optional) +weight: 150 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP68 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/manhole-sensor-dc413 +#dataSheetURL: http://www.dingtek.com/documents/19/dc413_Smart_Manhole_LoRaWAN_Datasheet_V1.2.pdf + +# Photos +photos: + main: dc413.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/bqGKnRtoLwo + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dc500-codec.yaml b/vendor/dingtek/dc500-codec.yaml new file mode 100644 index 0000000000..d8c5d4362e --- /dev/null +++ b/vendor/dingtek/dc500-codec.yaml @@ -0,0 +1,98 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dc500.js + # Examples (optional) + examples: + - description: event trigger upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x02, 0x01, 0x15, 0x01, 0x01, 0x01, 0x00, 0x01, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + peopleCounter: 257 + alarmCounter: false + alarmBattery: true + volt: 3.67 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x02, 0x03, 0x0C, 0x01, 0x03, 0x04, 0x01, 0xF4, 0x1E, 0x81] + output: + data: + firmware: '1.3' + uploadInterval: 4 + peopleCounterThreshold: 500 + batteryThreshold: 30 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dc500.js + examples: + - description: change periodic upload interval to 10 hours + input: + data: + uploadInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + + - description: change people counter threshold to 500 + input: + data: + peopleCounterThreshold: 500 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x46, 0x34, 0x38, 0x31] + fPort: 3 + + - description: change battery threshold to 30% + input: + data: + batteryThreshold: 30 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x45, 0x38, 0x31] + fPort: 3 + + - description: zero people counter + input: + data: + zeroPeopleCounter: 1 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dc500.js + examples: + - description: change periodic upload interval to 10 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + output: + data: + uploadInterval: 10 + - description: change people counter threshold to 500 + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x46, 0x34, 0x38, 0x31] + output: + data: + peopleCounterThreshold: 500 + - description: change battery threshold to 30% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x45, 0x38, 0x31] + output: + data: + batteryThreshold: 30 + + - description: zero people counter + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31] + output: + data: + zeroPeopleCounter: 1 diff --git a/vendor/dingtek/dc500.js b/vendor/dingtek/dc500.js new file mode 100644 index 0000000000..80403454a8 --- /dev/null +++ b/vendor/dingtek/dc500.js @@ -0,0 +1,195 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 21: + return { + // Decoded data + data: { + peopleCounter: ((input.bytes[5] << 8) + input.bytes[6]), + alarmCounter: Boolean(input.bytes[7] & 0xF0), + alarmBattery: Boolean(input.bytes[7] & 0x0F), + volt: ((input.bytes[9] << 8) + input.bytes[10]) / 100, + frameCounter: (input.bytes[17] << 8) + input.bytes[18], + }, + }; + case 12: + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + batteryThreshold: input.bytes[10], + peopleCounterThreshold: (input.bytes[8] << 8) + input.bytes[9], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 168 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.peopleCounterThreshold != null && !isNaN(input.data.peopleCounterThreshold)) { + var peopleCounterThreshold = input.data.peopleCounterThreshold; + var peopleCounter_1st = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[0].charCodeAt(0); + var peopleCounter_2nd = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[1].charCodeAt(0); + var peopleCounter_3rd = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[2].charCodeAt(0); + var peopleCounter_4th = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[3].charCodeAt(0); + if (peopleCounterThreshold > 65535 || peopleCounterThreshold < 1) { + return { + errors: ['people counter range 1-65535.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, peopleCounter_1st, peopleCounter_2nd, peopleCounter_3rd, peopleCounter_4th, 0x38, 0x31], + }; + } + } + + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.zeroPeopleCounter != null && !isNaN(input.data.zeroPeopleCounter)) { + var zeroPeopleCounter = input.data.zeroPeopleCounter; + if (zeroPeopleCounter != 1) { + return { + errors: ['zero people counter: 1.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + if (input_length == 16) + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]) + String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16); + else + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 2: + return { + data: { + peopleCounterThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + + case 9: + switch (value) { + case 0x0F: + return { + data: { + zeroPeopleCounter: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/dc500.png b/vendor/dingtek/dc500.png new file mode 100644 index 0000000000..263a03eeba Binary files /dev/null and b/vendor/dingtek/dc500.png differ diff --git a/vendor/dingtek/dc500.yaml b/vendor/dingtek/dc500.yaml new file mode 100644 index 0000000000..251a6c1d54 --- /dev/null +++ b/vendor/dingtek/dc500.yaml @@ -0,0 +1,159 @@ +name: dc500 +description: The CNDingtek dc500 is a pir people counter, which calculate the people number. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.4' + numeric: 14 + - version: '1.5' + numeric: 15 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.4' + - '1.5' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc500-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc500-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc500-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dc500-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dc500-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dc500-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc500-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - pulse count + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + #- temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 80 + width: 78 + height: 40 + +# Weight in grams (optional) +weight: 240 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/people-counting-sensor-dc500 +#dataSheetURL: http://www.dingtek.com/documents/23/dc500_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: dc500.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/qzEZIBocfFg + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dc510-codec.yaml b/vendor/dingtek/dc510-codec.yaml new file mode 100644 index 0000000000..3ebd5cb43a --- /dev/null +++ b/vendor/dingtek/dc510-codec.yaml @@ -0,0 +1,99 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dc510.js + # Examples (optional) + examples: + - description: event trigger upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x15, 0x01, 0x12, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x10, 0x00, 0x01, 0xA4, 0x00, 0x01, 0x81] + output: + data: + peopleCounter: 5 + alarmCounter: true + alarmBattery: false + temperature: 20 + volt: 4.2 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [80, 0x00, 0x15, 0x03, 0x0D, 0x01, 0x03, 0x04, 0x01, 0xF4, 0x14, 0x01, 0x81] + output: + data: + firmware: '1.3' + uploadInterval: 4 + peopleCounterThreshold: 500 + batteryThreshold: 20 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dc510.js + examples: + - description: change periodic upload interval to 10 hours + input: + data: + uploadInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + + - description: change people counter threshold to 500 + input: + data: + peopleCounterThreshold: 500 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x46, 0x34, 0x38, 0x31] + fPort: 3 + + - description: change battery threshold to 30% + input: + data: + batteryThreshold: 30 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x45, 0x38, 0x31] + fPort: 3 + + - description: zero people counter + input: + data: + zeroPeopleCounter: 1 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dc510.js + examples: + - description: change periodic upload interval to 10 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + output: + data: + uploadInterval: 10 + - description: change people counter threshold to 500 + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x46, 0x34, 0x38, 0x31] + output: + data: + peopleCounterThreshold: 500 + - description: change battery threshold to 30% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x45, 0x38, 0x31] + output: + data: + batteryThreshold: 30 + + - description: zero people counter + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31] + output: + data: + zeroPeopleCounter: 1 diff --git a/vendor/dingtek/dc510.js b/vendor/dingtek/dc510.js new file mode 100644 index 0000000000..dbd6814a97 --- /dev/null +++ b/vendor/dingtek/dc510.js @@ -0,0 +1,238 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 18: + return { + // Decoded data + data: { + peopleCounter: ((input.bytes[5] << 8) + input.bytes[6]), + alarmCounter: Boolean(input.bytes[11] & 0xF0), + alarmBattery: Boolean(input.bytes[12] & 0x0F), + temperature: input.bytes[8], + volt: ((input.bytes[13] << 8) + input.bytes[14]) / 100, + frameCounter: (input.bytes[15] << 8) + input.bytes[16], + }, + }; + case 13: + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + batteryThreshold: input.bytes[10], + peopleCounterThreshold: (input.bytes[8] << 8) + input.bytes[9], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 168 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.peopleCounterThreshold != null && !isNaN(input.data.peopleCounterThreshold)) { + var peopleCounterThreshold = input.data.peopleCounterThreshold; + var peopleCounter_1st = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[0].charCodeAt(0); + var peopleCounter_2nd = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[1].charCodeAt(0); + var peopleCounter_3rd = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[2].charCodeAt(0); + var peopleCounter_4th = peopleCounterThreshold.toString(16).padStart(4, '0').toUpperCase()[3].charCodeAt(0); + if (peopleCounterThreshold > 65535 || peopleCounterThreshold < 1) { + return { + errors: ['people counter range 1-65535.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, peopleCounter_1st, peopleCounter_2nd, peopleCounter_3rd, peopleCounter_4th, 0x38, 0x31], + }; + } + } + + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.zeroPeopleCounter != null && !isNaN(input.data.zeroPeopleCounter)) { + var zeroPeopleCounter = input.data.zeroPeopleCounter; + if (zeroPeopleCounter != 1) { + return { + errors: ['zero people counter: 1.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x46, 0x38, 0x31], + }; + } + } + if (input.data.factoryReset != null && !isNaN(input.data.factoryReset)) { + var factoryReset = input.data.factoryReset; + if (factoryReset != 1) { + return { + errors: ['factory reset: 1.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x44, 0x38, 0x31], + }; + } + } + if (input.data.reset != null && !isNaN(input.data.reset)) { + var reset = input.data.reset; + if (reset != 1) { + return { + errors: ['reset: 1.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x32, 0x38, 0x31], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + if (input_length == 16) + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]) + String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16); + else + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 2: + return { + data: { + peopleCounterThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + + case 9: + switch (value) { + case 0x02: + return { + data: { + reset: 1, + }, + }; + case 0x0D: + return { + data: { + factoryReset: 1, + }, + }; + case 0x0F: + return { + data: { + zeroPeopleCounter: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/dc510.png b/vendor/dingtek/dc510.png new file mode 100644 index 0000000000..6a630ab632 Binary files /dev/null and b/vendor/dingtek/dc510.png differ diff --git a/vendor/dingtek/dc510.yaml b/vendor/dingtek/dc510.yaml new file mode 100644 index 0000000000..65c905d3e4 --- /dev/null +++ b/vendor/dingtek/dc510.yaml @@ -0,0 +1,159 @@ +name: dc510 +description: The CNDingtek dc510 is a pir people counter for indoor using, which calculate the people number. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc510-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc510-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc510-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dc510-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dc510-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dc510-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc510-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - pulse count + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + #- temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 110 + width: 109 + height: 25 + +# Weight in grams (optional) +weight: 80 + +# Battery information (optional) +battery: + replaceable: false + type: 1000mAh + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/people-counter-dc510 +#dataSheetURL: http://www.dingtek.com/documents/23/dc510_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: dc510.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/hyZHpHczhfY + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dc600-codec.yaml b/vendor/dingtek/dc600-codec.yaml new file mode 100644 index 0000000000..4ff5b7d2a9 --- /dev/null +++ b/vendor/dingtek/dc600-codec.yaml @@ -0,0 +1,70 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dc600.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x01, 0x11, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x81] + output: + data: + monitorStatus: true + alarmChannel1: false + alarmChannel2: false + alarmChannel3: false + alarmChannel4: false + alarmBattery: false + temperature: 25 + frameCounter: 0 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x11, 0x01, 0x04, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x81] + output: + data: + firmware: '1.4' + uploadInterval: 24 + batteryThreshold: 20 + monitorStatus: true + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dc600.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: set battery alarm threshold to 20% + input: + data: + batteryThreshold: 20 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dc600.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + + - description: set battery alarm threshold to 20% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + output: + data: + batteryThreshold: 20 diff --git a/vendor/dingtek/dc600.js b/vendor/dingtek/dc600.js new file mode 100644 index 0000000000..3e62b923f8 --- /dev/null +++ b/vendor/dingtek/dc600.js @@ -0,0 +1,190 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 17: + if (input.bytes[3] != 0x03) { + return { + // Decoded data + data: { + monitorStatus: !Boolean(input.bytes[11] & 0x01), + alarmChannel1: !Boolean(input.bytes[12] & 0x10), + alarmChannel2: !Boolean(input.bytes[12] & 0x20), + alarmChannel3: !Boolean(input.bytes[12] & 0x40), + alarmChannel4: !Boolean(input.bytes[12] & 0x80), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + temperature: input.bytes[8], + frameCounter: (input.bytes[13] << 8) + input.bytes[14], + }, + }; + } else { + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + batteryThreshold: input.bytes[11], + monitorStatus: !Boolean(input.bytes[12] & 0x01), + }, + }; + } + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 168 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['Battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.monitorStatus != null && !isNaN(input.data.monitorStatus)) { + var monitorStatus = input.data.monitorStatus; + if (monitorStatus == 0) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31], + }; + } else if (monitorStatus == 1){ + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x39, 0x38, 0x31], + }; + }else{ + return { + errors: ['Monitor status range 0-1.'], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x09: + return { + data: { + monitorStatus: 1, + }, + }; + case 0x0A: + return { + data: { + monitorStatus: 1, + }, + }; + case 0x0D: + return { + data: { + reset: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/dc600.png b/vendor/dingtek/dc600.png new file mode 100644 index 0000000000..92eee7cb19 Binary files /dev/null and b/vendor/dingtek/dc600.png differ diff --git a/vendor/dingtek/dc600.yaml b/vendor/dingtek/dc600.yaml new file mode 100644 index 0000000000..c53b29f62d --- /dev/null +++ b/vendor/dingtek/dc600.yaml @@ -0,0 +1,158 @@ +name: dc600 +description: The CNDingtek dc600 is water leakage sensor with up to 4 channels. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc600-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc600-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dc600-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dc600-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dc600-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dc600-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dc600-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - water + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 80 + width: 78 + height: 30 + +# Weight in grams (optional) +weight: 240 + +# Battery information (optional) +battery: + replaceable: true + type: 8000mAh + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP66 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/water-leak-sensor-dc600 +#dataSheetURL: http://www.dingtek.com/documents/23/dc600_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: dc600.png + #other: + +# Youtube or Vimeo Video (optional) +#videos: +#main: https://youtu.be/T8q0kb5tMXY +#other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df200-codec.yaml b/vendor/dingtek/df200-codec.yaml new file mode 100644 index 0000000000..1e7edef3b0 --- /dev/null +++ b/vendor/dingtek/df200-codec.yaml @@ -0,0 +1,98 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df200.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x01, 0x0F, 0x02, 0x4C, 0x00, 0x19, 0x4B, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + level: 75 + alarmLevel: false + alarmBattery: false + temperature: 25 + volt: 5.88 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x0D, 0x01, 0x02, 0x18, 0x0A, 0x14, 0x19, 0x00, 0x81] + output: + data: + firmware: '1.2' + uploadInterval: 24 + detectInterval: 10 + batteryThreshold: 20 + levelThreshold: 25 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df200.js + examples: + - description: change periodic upload interval to 10 hours + input: + data: + uploadInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change level alarm threshold to 50% + input: + data: + levelThreshold: 50 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x33, 0x32, 0x38, 0x31] + fPort: 3 + + - description: change battery threshold to 20% + input: + data: + batteryThreshold: 20 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df200.js + examples: + - description: change periodic upload interval to 10 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + output: + data: + uploadInterval: 10 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change level alarm threshold to 50% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x33, 0x32, 0x38, 0x31] + output: + data: + levelThreshold: 50 + + - description: change battery threshold to 20% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + output: + data: + batteryThreshold: 20 diff --git a/vendor/dingtek/df200.js b/vendor/dingtek/df200.js new file mode 100644 index 0000000000..c46be6ba12 --- /dev/null +++ b/vendor/dingtek/df200.js @@ -0,0 +1,205 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 15: + return { + // Decoded data + data: { + level: (input.bytes[9]), + alarmLevel: Boolean(input.bytes[10] & 0x0f), + alarmBattery: Boolean(input.bytes[7] & 0x0f), + temperature: input.bytes[8], + volt: ((input.bytes[5] << 8) + input.bytes[6]) / 100, + frameCounter: (input.bytes[11] << 8) + input.bytes[12], + }, + }; + case 13: + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + batteryThreshold: input.bytes[9], + levelThreshold: input.bytes[10], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 255 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-255 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detectInterval = input.data.detectInterval; + var detectInterval_high = detectInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detectInterval_low = detectInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detectInterval > 255 || detectInterval < 1) { + return { + errors: ['detection interval range 1-255 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detectInterval_high, detectInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var levelThreshold = input.data.levelThreshold; + var levelThreshold_high = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var levelThreshold_low = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (levelThreshold!=25 && levelThreshold !=50 && levelThreshold !=75 && levelThreshold !=100) { + return { + errors: ['level alarm threshold range 25/50/75/100.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, levelThreshold_high, levelThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold>99 || batteryThreshold<5) { + return { + errors: ['battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35,batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + + case 9: + switch (value) { + case 0x00: + return { + data: { + calibration: 0, + }, + }; + case 0x01: + return { + data: { + calibration: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/df200.png b/vendor/dingtek/df200.png new file mode 100644 index 0000000000..62e6fddf34 Binary files /dev/null and b/vendor/dingtek/df200.png differ diff --git a/vendor/dingtek/df200.yaml b/vendor/dingtek/df200.yaml new file mode 100644 index 0000000000..74225a8467 --- /dev/null +++ b/vendor/dingtek/df200.yaml @@ -0,0 +1,159 @@ +name: df200 +description: The CNDingtek df200 is soap dispenser level sensor, which detect the liquid level in the dispenser. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df200-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df200-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df200-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df200-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df200-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df200-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df200-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 158 + width: 116 + height: 286 + +# Weight in grams (optional) +weight: 750 + +# Battery information (optional) +battery: + replaceable: true + type: D size + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/dispenser-level-sensor-df200 +#dataSheetURL: http://www.dingtek.com/documents/23/df200_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df200.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/TSreE_67FVE + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df400-codec.yaml b/vendor/dingtek/df400-codec.yaml new file mode 100644 index 0000000000..205321b48f --- /dev/null +++ b/vendor/dingtek/df400-codec.yaml @@ -0,0 +1,99 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df400.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x01, 0x11, 0x02, 0x4C, 0x00, 0x19, 0x00, 0x46, 0x4B, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + level: 70 + percent: 75 + alarmLevel: false + alarmBattery: false + temperature: 25 + volt: 5.88 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x0D, 0x00, 0x02, 0x18, 0x1E, 0x14, 0x0A, 0x00, 0x81] + output: + data: + firmware: '0.2' + uploadInterval: 24 + detectInterval: 30 + batteryThreshold: 20 + levelThreshold: 10 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df400.js + examples: + - description: change periodic upload interval to 10 hours + input: + data: + uploadInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change level alarm threshold to 50% + input: + data: + levelThreshold: 50 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x33, 0x32, 0x38, 0x31] + fPort: 3 + + - description: change battery threshold to 20% + input: + data: + batteryThreshold: 20 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df400.js + examples: + - description: change periodic upload interval to 10 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + output: + data: + uploadInterval: 10 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change level alarm threshold to 50% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x33, 0x32, 0x38, 0x31] + output: + data: + levelThreshold: 50 + + - description: change battery threshold to 20% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, 0x31, 0x34, 0x38, 0x31] + output: + data: + batteryThreshold: 20 diff --git a/vendor/dingtek/df400.js b/vendor/dingtek/df400.js new file mode 100644 index 0000000000..70f2e55594 --- /dev/null +++ b/vendor/dingtek/df400.js @@ -0,0 +1,194 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 17: + return { + // Decoded data + data: { + level: (input.bytes[9] << 8) + input.bytes[10], + percent: (input.bytes[11]), + alarmLevel: Boolean(input.bytes[12] & 0x0f), + alarmBattery: Boolean(input.bytes[7] & 0x0f), + temperature: input.bytes[8], + volt: ((input.bytes[5] << 8) + input.bytes[6]) / 100, + frameCounter: (input.bytes[13] << 8) + input.bytes[14], + }, + }; + case 13: + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + batteryThreshold: input.bytes[9], + levelThreshold: input.bytes[10], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 255 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-255 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detectInterval = input.data.detectInterval; + var detectInterval_high = detectInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detectInterval_low = detectInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detectInterval > 255 || detectInterval < 1) { + return { + errors: ['detection interval range 1-255 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detectInterval_high, detectInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var levelThreshold = input.data.levelThreshold; + var levelThreshold_high = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var levelThreshold_low = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (levelThreshold > 99 || levelThreshold < 1) { + return { + errors: ['level alarm threshold range 1-99,unit %.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, levelThreshold_high, levelThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + + case 9: + switch (value) { + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/df400.png b/vendor/dingtek/df400.png new file mode 100644 index 0000000000..4491cea133 Binary files /dev/null and b/vendor/dingtek/df400.png differ diff --git a/vendor/dingtek/df400.yaml b/vendor/dingtek/df400.yaml new file mode 100644 index 0000000000..e276b344df --- /dev/null +++ b/vendor/dingtek/df400.yaml @@ -0,0 +1,159 @@ +name: df400 +description: The CNDingtek df400 is tissue dispenser level sensor, which detect the paper in the dispenser. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df400-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df400-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df400-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df400-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df400-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df400-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df400-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 120 + width: 64 + height: 18 + +# Weight in grams (optional) +weight: 40 + +# Battery information (optional) +battery: + replaceable: true + type: ER14500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/toilet-paper-level-sensor-df400 +#dataSheetURL: http://www.dingtek.com/documents/23/df400_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df400.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/0v9kJNyk2PY + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df555-codec.yaml b/vendor/dingtek/df555-codec.yaml new file mode 100644 index 0000000000..d12bee2c0a --- /dev/null +++ b/vendor/dingtek/df555-codec.yaml @@ -0,0 +1,94 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df555.js + # Examples (optional) + examples: + - description: heartbeat upload without gps + input: + fPort: 3 + bytes: [0x80, 0, 1, 1, 0x11, 7, 0xD0, 0, 0x19, 0, 0, 0, 0, 0, 1, 0, 0x81] + output: + data: + level: 2000 + alarmLevel: false + alarmBattery: false + temperature: 25 + frameCounter: 1 + + - description: heartbeat upload with gps + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x01, 0x19, 0x07, 0xD0, 0x01, 0xCD, 0x03, 0xE9, 0x42, 0xEF, 0x27, 0x20, 0x42, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + level: 2000 + longitude: '116.507430' + latitude: '40.038999' + alarmLevel: false + alarmBattery: false + temperature: 25 + frameCounter: 1 + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x11, 0x01, 0x04, 0x18, 0x01, 0x1E, 0x4B, 0x1E, 0x00, 0x02, 0x00, 0x00, 0x81] + output: + data: + firmware: '1.4' + uploadInterval: 24 + detectInterval: 1 + levelThreshold: 30 + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df555.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change level alarm threshold to 35cm + input: + data: + levelThreshold: 35 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df555.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change level alarm threshold to 35cm + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + output: + data: + levelThreshold: 35 diff --git a/vendor/dingtek/df555.js b/vendor/dingtek/df555.js new file mode 100644 index 0000000000..6c8b81d2b9 --- /dev/null +++ b/vendor/dingtek/df555.js @@ -0,0 +1,291 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 17: + if (input.bytes[3] != 0x03) { + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + temperature: input.bytes[8], + frameCounter: (input.bytes[13] << 8) + input.bytes[14], + }, + }; + } else { + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + levelThreshold: input.bytes[9], + workMode: input.bytes[14], + }, + }; + } + + case 25: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[19] >> 4), + alarmBattery: Boolean(input.bytes[20] & 0x0f), + longitude: hex2float((input.bytes[11] << 24) + (input.bytes[10] << 16) + (input.bytes[9] << 8) + input.bytes[8]).toFixed(6), + latitude: hex2float((input.bytes[15] << 24) + (input.bytes[14] << 16) + (input.bytes[13] << 8) + input.bytes[12]).toFixed(6), + temperature: input.bytes[16], + frameCounter: (input.bytes[21] << 8) + input.bytes[22], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 255 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-255 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detectInterval = input.data.detectInterval; + var detectInterval_high = detectInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detectInterval_low = detectInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detectInterval > 255 || detectInterval < 1) { + return { + errors: ['detection interval range 1-255 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detectInterval_high, detectInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var levelThreshold = input.data.levelThreshold; + var levelThreshold_high = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var levelThreshold_low = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (levelThreshold > 255 || levelThreshold < 15) { + return { + errors: ['Level alarm threshold range 15-255.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, levelThreshold_high, levelThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['Battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.gpsEnable != null && !isNaN(input.data.gpsEnable)) { + var gpsEnable = input.data.gpsEnable; + if (gpsEnable == true || gpsEnable == 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x31, 0x38, 0x31], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x30, 0x38, 0x31], + }; + } + } + if (input.data.workMode != null && !isNaN(input.data.workMode)) { + var workMode = input.data.workMode; + if (workMode === 0) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x35, 0x38, 0x31], + }; + } else if (workMode === 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x36, 0x38, 0x31], + }; + } else if (workMode === 2) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x45, 0x38, 0x31], + }; + }else{ + return { + errors: ['Work mode range 0-2.'], + } + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x00: + return { + data: { + gpsEnable: 0, + }, + }; + case 0x01: + return { + data: { + gpsEnable: 1, + }, + }; + case 0x02: + return { + data: { + reset: 1, + }, + }; + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + case 0x0E: + return { + data: { + workMode: 2, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} + diff --git a/vendor/dingtek/df555.png b/vendor/dingtek/df555.png new file mode 100644 index 0000000000..7015492fd5 Binary files /dev/null and b/vendor/dingtek/df555.png differ diff --git a/vendor/dingtek/df555.yaml b/vendor/dingtek/df555.yaml new file mode 100644 index 0000000000..0925ba9145 --- /dev/null +++ b/vendor/dingtek/df555.yaml @@ -0,0 +1,158 @@ +name: df555 +description: The CNDingtek df555 is an ultrasonic level for kinds tank, which is installed on top of the tank. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df555-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df555-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df555-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df555-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df555-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df555-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df555-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + - gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 95 + height: 123 + +# Weight in grams (optional) +weight: 500 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP67 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/top-installation-level-sensor-df555 +#dataSheetURL: http://www.dingtek.com/documents/23/df555_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df555.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/CEzacjJCQm4 + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df556-codec.yaml b/vendor/dingtek/df556-codec.yaml new file mode 100644 index 0000000000..b1ce958143 --- /dev/null +++ b/vendor/dingtek/df556-codec.yaml @@ -0,0 +1,114 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df556.js + # Examples (optional) + examples: + - description: heartbeat upload without gps + input: + fPort: 3 + bytes: [0x80, 0x00, 0x16, 0x02, 0x12, 0x02, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x01, 0x81] + output: + data: + level: 613 + gpsEnabled: false + alarmLevel: false + alarmBattery: false + volt: 360 + frameCounter: 1 + + - description: heartbeat upload with gps + input: + fPort: 3 + bytes: [0x80, 0x00, 0x16, 0x02, 0x1A, 0x02, 0x65, 0x01, 0xCD, 0x03, 0xE9, 0x42, 0xEF, 0x27, 0x20, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x01, 0x81] + output: + data: + level: 613 + gpsEnabled: true + longitude: '116.507430' + latitude: '40.038999' + alarmLevel: false + alarmBattery: false + volt: 360 + frameCounter: 1 + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x16, 0x03, 0x11, 0x04, 0x05, 0x00, 0x3C, 0x0A, 0x1E, 0x4B, 0x03, 0xE8, 0x14, 0x00, 0x81] + output: + data: + firmware: '4.5' + uploadInterval: 60 + detectInterval: 10 + levelThreshold: 30 + density: 1000 + batteryThreshold: 20 + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df556.js + examples: + - description: change periodic upload interval to 60 minutes + input: + data: + uploadInterval: 60 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x30, 0x33, 0x43, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change level alarm threshold to 35cm + input: + data: + levelThreshold: 35 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + fPort: 3 + + - description: change liquid density to 1000mg/cm3 + input: + data: + density: 1000 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x41, 0x30, 0x33, 0x45, 0x38, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df556.js + examples: + - description: change periodic upload interval to 60 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x30, 0x33, 0x43, 0x38, 0x31] + output: + data: + uploadInterval: 60 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change level alarm threshold to 35cm + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + output: + data: + levelThreshold: 35 + + - description: change liquid density to 1000mg/cm3 + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x41, 0x30, 0x33, 0x45, 0x38, 0x38, 0x31] + output: + data: + density: 1000 diff --git a/vendor/dingtek/df556.js b/vendor/dingtek/df556.js new file mode 100644 index 0000000000..52764c4247 --- /dev/null +++ b/vendor/dingtek/df556.js @@ -0,0 +1,335 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 18: + if (input.bytes[3] != 0x03) { + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + gpsEnabled: Boolean(input.bytes[7]), + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + volt: (input.bytes[13] << 8) + input.bytes[14], + frameCounter: (input.bytes[15] << 8) + input.bytes[16], + }, + }; + } else { + return { + errors: ['wrong length'], + }; + } + case 17: + if (input.bytes[3] == 0x03) { + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: (input.bytes[7] << 8) + input.bytes[8], + detectInterval: input.bytes[9], + levelThreshold: input.bytes[10], + density: (input.bytes[12] << 8) + input.bytes[13], + batteryThreshold: input.bytes[14], + workMode: input.bytes[15], + }, + }; + } else { + return { + errors: ['wrong length'], + }; + } + case 26: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + gpsEnabled: Boolean(input.bytes[7]), + alarmLevel: Boolean(input.bytes[19] >> 4), + alarmBattery: Boolean(input.bytes[20] & 0x0f), + longitude: hex2float((input.bytes[11] << 24) + (input.bytes[10] << 16) + (input.bytes[9] << 8) + input.bytes[8]).toFixed(6), + latitude: hex2float((input.bytes[15] << 24) + (input.bytes[14] << 16) + (input.bytes[13] << 8) + input.bytes[12]).toFixed(6), + volt: (input.bytes[21] << 8) + input.bytes[22], + frameCounter: (input.bytes[23] << 8) + input.bytes[24], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadIntervalStr= uploadInterval.toString(16).padStart(4, '0').toUpperCase(); + var uploadInterval3rd = uploadIntervalStr[0].charCodeAt(0); + var uploadInterval2nd = uploadIntervalStr[1].charCodeAt(0); + var uploadInterval1st = uploadIntervalStr[2].charCodeAt(0); + var uploadInterval0zr = uploadIntervalStr[3].charCodeAt(0); + if (uploadInterval > 65535 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-65535 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval3rd, uploadInterval2nd,uploadInterval1st,uploadInterval0zr, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detection_interval = input.data.detectInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['cyclic detection interval range 1-60 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.density != null && !isNaN(input.data.density)) { + var density = input.data.density; + var densityStr= density.toString(16).padStart(4, '0').toUpperCase(); + var density3rd = densityStr[0].charCodeAt(0); + var density2nd = densityStr[1].charCodeAt(0); + var density1st = densityStr[2].charCodeAt(0); + var density0zr = densityStr[3].charCodeAt(0); + if (density > 65535 || density < 1) { + return { + errors: ['upload interval range 1-65535 mg/cm3 .'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x41, density3rd, density2nd,density1st,density0zr, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var levelThreshold = input.data.levelThreshold; + var levelThreshold_high = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var levelThreshold_low = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (levelThreshold > 255 || levelThreshold < 1) { + return { + errors: ['Level alarm threshold range 1-255.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, levelThreshold_high, levelThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['Battery alarm threshold range 5-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.gpsEnable != null && !isNaN(input.data.gpsEnable)) { + var gpsEnable = input.data.gpsEnable; + if (gpsEnable == true || gpsEnable == 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x31, 0x38, 0x31], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x30, 0x38, 0x31], + }; + } + } + if (input.data.workMode != null && !isNaN(input.data.workMode)) { + var workMode = input.data.workMode; + if (workMode === 0) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x35, 0x38, 0x31], + }; + } else if (workMode === 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x36, 0x38, 0x31], + }; + } else if (workMode === 2) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x45, 0x38, 0x31], + }; + } else { + return { + errors: ['Work mode range 0-2.'], + } + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]) + String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16), + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 10: + return { + data: { + density: parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]) + String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16), + }, + }; + case 9: + switch (value) { + case 0x00: + return { + data: { + gpsEnable: 0, + }, + }; + case 0x01: + return { + data: { + gpsEnable: 1, + }, + }; + case 0x02: + return { + data: { + reset: 1, + }, + }; + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + + case 0x0E: + return { + data: { + workMode: 2, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/df556.png b/vendor/dingtek/df556.png new file mode 100644 index 0000000000..62e6fddf34 Binary files /dev/null and b/vendor/dingtek/df556.png differ diff --git a/vendor/dingtek/df556.yaml b/vendor/dingtek/df556.yaml new file mode 100644 index 0000000000..ef511463ac --- /dev/null +++ b/vendor/dingtek/df556.yaml @@ -0,0 +1,158 @@ +name: df556 +description: The CNDingtek df556 is a pressure type level for kinds tank, which is installed on top of the tank. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df556-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df556-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df556-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df556-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df556-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df556-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df556-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + - gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 95 + height: 123 + +# Weight in grams (optional) +weight: 820 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP67 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/pressure-liquid-level-sens-df556 +#dataSheetURL: http://www.dingtek.com/documents/23/df556_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df556.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/Q_t8VMRS5nk + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df701-codec.yaml b/vendor/dingtek/df701-codec.yaml new file mode 100644 index 0000000000..14ccb4f675 --- /dev/null +++ b/vendor/dingtek/df701-codec.yaml @@ -0,0 +1,81 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df701.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x02, 0x12, 0x27, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x01, 0x81] + output: + data: + level: 9999 + alarmLevel: false + alarmBattery: false + volt: 3.60 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x0C, 0x01, 0x02, 0x0A, 0x0A, 0x14, 0x00, 0x81] + output: + data: + firmware: '1.2' + uploadInterval: 10 + detectInterval: 10 + levelThreshold: 20 + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df701.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change full alarm threshold to 35cm + input: + data: + levelThreshold: 35 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df701.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change full alarm threshold to 35cm + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + output: + data: + levelThreshold: 35 diff --git a/vendor/dingtek/df701.js b/vendor/dingtek/df701.js new file mode 100644 index 0000000000..ccd69ae82c --- /dev/null +++ b/vendor/dingtek/df701.js @@ -0,0 +1,241 @@ +var units = [' ℃', ' hours', ' minutes', ' mm', ' °', ' cm']; +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + + switch (input.bytes.length) { + case 18: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + volt: ((input.bytes[13] << 8) + input.bytes[14]) / 100, + frameCounter: (input.bytes[15] << 8) + input.bytes[16], + }, + }; + + case 12: + var data_type = input.bytes[3]; + if (data_type === 0x03) { + return { + // Decoded parameter + data: { + firmware: input.bytes[5] + '.' + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + levelThreshold: input.bytes[9], + workMode: input.bytes[10], + }, + }; + } + default: + return { + errors: ['wrong length'], + }; + } +} + +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var periodic_interval = input.data.uploadInterval; + var periodic_interval_high = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var periodic_interval_low = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (periodic_interval > 168 || periodic_interval < 1) { + return { + errors: ['periodic upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, periodic_interval_high, periodic_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detection_interval = input.data.detectInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['cyclic detection interval range 1-60 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var full_alarm_threshold = input.data.levelThreshold; + var full_alarm_threshold_high = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var full_alarm_threshold_low = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (full_alarm_threshold > 255 || full_alarm_threshold < 15) { + return { + errors: ['full alarm threshold range 15-255 cm.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, full_alarm_threshold_high, full_alarm_threshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.workMode != null) { + var workMode = input.data.workMode; + switch (workMode) { + case 0: + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x30, 0x38, 0x31], + }; + break; + case 1: + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x31, 0x38, 0x31], + }; + break; + case 2: + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x45, 0x38, 0x31], + }; + break; + default: + break; + } + } + + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x03: + return { + data: { + activeMode: ABP, + }, + }; + case 0x04: + return { + data: { + activeMode: OTAA, + }, + }; + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + case 0x0e: + return { + data: { + workMode: 2, + }, + }; + case 0x09: + return { + data: { + fallEnable: false, + }, + }; + case 0x0a: + return { + data: { + fallEnable: true, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} diff --git a/vendor/dingtek/df701.png b/vendor/dingtek/df701.png new file mode 100644 index 0000000000..c445359b5e Binary files /dev/null and b/vendor/dingtek/df701.png differ diff --git a/vendor/dingtek/df701.yaml b/vendor/dingtek/df701.yaml new file mode 100644 index 0000000000..8e2c5045c4 --- /dev/null +++ b/vendor/dingtek/df701.yaml @@ -0,0 +1,157 @@ +name: df701 +description: The CNDingtek df701 is a laser level sensor for indoor waste bins. The measured data is reported to a LoRaWAN® network that notifies garbage collecting authorities to collect and empty the bins. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df701-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df701-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df701-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df701-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df701-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df701-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df701-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + #- gps + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + #- temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + length: 67 + width: 59 + height: 23 + +# Weight in grams (optional) +weight: 70 + +# Battery information (optional) +battery: + replaceable: true + type: ER18505 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/waste-bin-sensor-df701 +#dataSheetURL: http://www.dingtek.com/documents/23/df701_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df701.png + #other: + +# Youtube or Vimeo Video (optional) +#videos: +#main: https://youtu.be/86XVM0thJMY + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/df703-codec.yaml b/vendor/dingtek/df703-codec.yaml new file mode 100644 index 0000000000..f09f47f966 --- /dev/null +++ b/vendor/dingtek/df703-codec.yaml @@ -0,0 +1,145 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: df703.js + # Examples (optional) + examples: + - description: heartbeat upload without gps + input: + fPort: 3 + bytes: [0x80, 0, 1, 1, 0x11, 7, 0xD0, 0, 0x19, 0, 0, 0, 0, 0, 1, 0, 0x81] + output: + data: + level: 2000 + alarmLevel: false + alarmFire: false + alarmFall: false + alarmBattery: false + angle: 0 + temperature: 25 + frameCounter: 1 + + - description: heartbeat upload with gps + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x01, 0x19, 0x07, 0xD0, 0x01, 0xCD, 0x03, 0xE9, 0x42, 0xEF, 0x27, 0x20, 0x42, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81] + output: + data: + level: 2000 + longitude: '116.507430' + latitude: '40.038999' + alarmLevel: false + alarmFire: false + alarmFall: false + alarmBattery: false + angle: 0 + temperature: 25 + frameCounter: 1 + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x19, 0x01, 0x04, 0x18, 0x01, 0x1E, 0x4B, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81] + output: + data: + firmware: '1.4' + uploadInterval: 24 + detectInterval: 1 + levelThreshold: 30 + fireThreshold: 75 + fallThreshold: 30 + fallEnable: false + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: df703.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change full alarm threshold to 35cm + input: + data: + levelThreshold: 35 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + fPort: 3 + - description: change fire alarm threshold to 70℃ + input: + data: + fireThreshold: 70 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, 0x34, 0x36, 0x38, 0x31] + fPort: 3 + - description: change tilt alarm threshold to 45° + input: + data: + fallThreshold: 45 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x32, 0x44, 0x38, 0x31] + fPort: 3 + - description: enable fall detection + input: + data: + fallEnable: true + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: df703.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change full alarm threshold to 35cm + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x32, 0x33, 0x38, 0x31] + output: + data: + levelThreshold: 35 + - description: change fire alarm threshold to 70℃ + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, 0x34, 0x36, 0x38, 0x31] + output: + data: + fireThreshold: 70 + - description: change tilt alarm threshold to 45° + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x32, 0x44, 0x38, 0x31] + output: + data: + fallThreshold: 45 + - description: enable tilt detection + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31] + output: + data: + fallEnable: true diff --git a/vendor/dingtek/df703.js b/vendor/dingtek/df703.js new file mode 100644 index 0000000000..4b4847efe2 --- /dev/null +++ b/vendor/dingtek/df703.js @@ -0,0 +1,309 @@ +var units = [' ℃', ' hours', ' minutes', ' mm', ' °', ' cm']; +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + + switch (input.bytes.length) { + case 17: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmFire: Boolean(input.bytes[11] & 0x0f), + alarmFall: Boolean(input.bytes[12] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + angle: (input.bytes[9] & (0x0f === 0x00) ? input.bytes[10] : 0 - input.bytes[10]), + temperature: input.bytes[8], + frameCounter: (input.bytes[13] << 8) + input.bytes[14], + }, + }; + + case 25: + var data_type = input.bytes[3]; + if (data_type === 0x03) { + return { + // Decoded parameter + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + levelThreshold: input.bytes[9], + fireThreshold: input.bytes[10], + fallThreshold: input.bytes[11], + fallEnable: Boolean(input.bytes[12]), + workMode:input.bytes[14], + }, + }; + } else { + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + longitude: hex2float((input.bytes[11] << 24) + (input.bytes[10] << 16) + (input.bytes[9] << 8) + input.bytes[8]).toFixed(6), + latitude: hex2float((input.bytes[15] << 24) + (input.bytes[14] << 16) + (input.bytes[13] << 8) + input.bytes[12]).toFixed(6), + alarmLevel: Boolean(input.bytes[19] >> 4), + alarmFire: Boolean(input.bytes[19] & 0x0f), + alarmFall: Boolean(input.bytes[20] >> 4), + alarmBattery: Boolean(input.bytes[20] & 0x0f), + angle: (input.bytes[17] & (0x0f === 0x00) ? input.bytes[18] : 0 - input.bytes[18]), + temperature: input.bytes[16], + frameCounter: (input.bytes[21] << 8) + input.bytes[22], + }, + }; + } + default: + return { + errors: ['wrong length'], + }; + } +} + +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 168 || uploadInterval < 1) { + return { + errors: ['periodic upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detection_interval = input.data.detectInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['cyclic detection interval range 1-60 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var full_alarm_threshold = input.data.levelThreshold; + var full_alarm_threshold_high = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var full_alarm_threshold_low = full_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (full_alarm_threshold > 255 || full_alarm_threshold < 15) { + return { + errors: ['full alarm threshold range 15-255 cm.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, full_alarm_threshold_high, full_alarm_threshold_low, 0x38, 0x31], + }; + } + } + if (input.data.fireThreshold != null && !isNaN(input.data.fireThreshold)) { + var fireThreshold = input.data.fireThreshold; + var fireThreshold_high = fireThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var fireThreshold_low = fireThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (fireThreshold > 255 || fireThreshold < 1) { + return { + errors: ['fire alarm threshold range 1-255 cellus degree.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, fireThreshold_high, fireThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.fallThreshold != null && !isNaN(input.data.fallThreshold)) { + var tilt_alarm_threshold = input.data.fallThreshold; + var tilt_alarm_threshold_high = tilt_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var tilt_alarm_threshold_low = tilt_alarm_threshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (tilt_alarm_threshold > 90 || tilt_alarm_threshold < 15) { + return { + errors: ['fall alarm threshold range 15-90 °.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, tilt_alarm_threshold_high, tilt_alarm_threshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.fallEnable != null && input.data.fallEnable === !!input.data.fallEnable) { + var tilt_enable = input.data.fallEnable; + if (tilt_enable === true) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x41, 0x38, 0x31], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x39, 0x38, 0x31], + }; + } + } + + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 3: + return { + data: { + fireThreshold: value, + }, + }; + case 4: + return { + data: { + fallThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x00: + return { + data: { + gpsEnable: false, + }, + }; + case 0x01: + return { + data: { + gpsEnable: true, + }, + }; + case 0x03: + return { + data: { + activeMode: ABP, + }, + }; + case 0x04: + return { + data: { + activeMode: OTAA, + }, + }; + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + case 0x0E: + return { + data: { + workMode: 2, + }, + }; + case 0x09: + return { + data: { + fallEnable: false, + }, + }; + case 0x0a: + return { + data: { + fallEnable: true, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} diff --git a/vendor/dingtek/df703.png b/vendor/dingtek/df703.png new file mode 100644 index 0000000000..338da0cb47 Binary files /dev/null and b/vendor/dingtek/df703.png differ diff --git a/vendor/dingtek/df703.yaml b/vendor/dingtek/df703.yaml new file mode 100644 index 0000000000..6e2da7b977 --- /dev/null +++ b/vendor/dingtek/df703.yaml @@ -0,0 +1,157 @@ +name: df703 +description: The CNDingtek df703 is an ultrasonic level waste bin sensor that consists of a level sensor, fire sensor, and accelerometer. The measured data is reported to a LoRaWAN® network that notifies garbage collecting authorities to collect and empty the bins. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df703-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df703-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: df703-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: df703-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: df703-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: df703-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: df703-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + - gps + - motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 115 + height: 40 + +# Weight in grams (optional) +weight: 150 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP68 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/waste-bin-sensor-df703 +#dataSheetURL: http://www.dingtek.com/documents/23/df703_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: df703.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/eJ1fTiSvOSw + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dingtek-profile-470.yaml b/vendor/dingtek/dingtek-profile-470.yaml index 7977c4b208..ea3470a072 100644 --- a/vendor/dingtek/dingtek-profile-470.yaml +++ b/vendor/dingtek/dingtek-profile-470.yaml @@ -1,5 +1,5 @@ -macVersion: 1.0.2 -regionalParametersVersion: RP001-1.0.2 +macVersion: 1.0.3 +regionalParametersVersion: RP001-1.0.3-RevA supportsJoin: true maxEIRP: 20 supports32bitFCnt: true diff --git a/vendor/dingtek/dingtek-profile-865.yaml b/vendor/dingtek/dingtek-profile-865.yaml index aec7ad6d23..5cb3e039bb 100644 --- a/vendor/dingtek/dingtek-profile-865.yaml +++ b/vendor/dingtek/dingtek-profile-865.yaml @@ -1,5 +1,5 @@ -macVersion: 1.0.2 -regionalParametersVersion: RP001-1.0.2 +macVersion: 1.0.3 +regionalParametersVersion: RP001-1.0.3-RevA supportsJoin: true maxEIRP: 30 supports32bitFCnt: true diff --git a/vendor/dingtek/dingtek-profile-915.yaml b/vendor/dingtek/dingtek-profile-915.yaml index aec7ad6d23..5cb3e039bb 100644 --- a/vendor/dingtek/dingtek-profile-915.yaml +++ b/vendor/dingtek/dingtek-profile-915.yaml @@ -1,5 +1,5 @@ -macVersion: 1.0.2 -regionalParametersVersion: RP001-1.0.2 +macVersion: 1.0.3 +regionalParametersVersion: RP001-1.0.3-RevA supportsJoin: true maxEIRP: 30 supports32bitFCnt: true diff --git a/vendor/dingtek/dingtek-profile-923.yaml b/vendor/dingtek/dingtek-profile-923.yaml index 310e161f67..31c0dee4e5 100644 --- a/vendor/dingtek/dingtek-profile-923.yaml +++ b/vendor/dingtek/dingtek-profile-923.yaml @@ -1,5 +1,5 @@ -macVersion: 1.0.2 -regionalParametersVersion: RP001-1.0.2 +macVersion: 1.0.3 +regionalParametersVersion: RP001-1.0.3-RevA supportsJoin: true maxEIRP: 16 supports32bitFCnt: true diff --git a/vendor/dingtek/do200-codec.yaml b/vendor/dingtek/do200-codec.yaml new file mode 100644 index 0000000000..80d209db80 --- /dev/null +++ b/vendor/dingtek/do200-codec.yaml @@ -0,0 +1,85 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: do200.js + # Examples (optional) + examples: + - description: telemetry upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x02, 0x01, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6F, 0x00, 0x79, 0xFF, 0xB0, 0xFF, 0xF5, 0x00, 0x00, 0x00, 0x81] + output: + data: + level: 0 + volt: 3.67 + alarmPark: false + alarmLevel: false + alarmMagnet: false + alarmBattery: false + xMagnet: 121 + yMagnet: -80 + zMagnet: -11 + Frame_Counter: 0 + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x02, 0x03, 0x11, 0x00, 0x0B, 0x18, 0x02, 0x0A, 0x05, 0xA0, 0x00, 0x3C, 0x00, 0x00, 0x81] + output: + data: + firmware: '0.11' + uploadInterval: 24 + ultraDetectInterval: 2 + magDetectInterval: 10 + magThreshold: 60 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: do200.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change geomagnetic detection interval to 10 seconds + input: + data: + magDetectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x34, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change geomagnetic threshold to 60mGs + input: + data: + magThreshold: 60 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x32, 0x33, 0x43, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: do200.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change geomagnetic detection interval to 10 seconds + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x34, 0x30, 0x41, 0x38, 0x31] + output: + data: + magDetectInterval: 10 + - description: change geomagnetic threshold to 60mGs + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x32, 0x33, 0x43, 0x38, 0x31] + output: + data: + magThreshold: 60 diff --git a/vendor/dingtek/do200.js b/vendor/dingtek/do200.js new file mode 100644 index 0000000000..058e30cfd7 --- /dev/null +++ b/vendor/dingtek/do200.js @@ -0,0 +1,215 @@ +var units = [' ℃', ' hours', ' minutes', ' mm', ' °', ' cm']; +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + + switch (input.bytes.length) { + case 21: + var mag_x = (input.bytes[11] << 8) + input.bytes[12]; + var mag_y = (input.bytes[13] << 8) + input.bytes[14]; + var mag_z = (input.bytes[15] << 8) + input.bytes[16]; + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + volt: ((input.bytes[9] << 8) + input.bytes[10]) / 100, + alarmPark: Boolean(input.bytes[7] >> 4), + alarmLevel: Boolean(input.bytes[7] & 0x0f), + alarmMagnet: Boolean(input.bytes[8] >> 4), + alarmBattery: Boolean(input.bytes[8] & 0x0f), + xMagnet: mag_x > 32767 ? mag_x - 65536 : mag_x, + yMagnet: mag_y > 32767 ? mag_y - 65536 : mag_y, + zMagnet: mag_z > 32767 ? mag_z - 65536 : mag_z, + Frame_Counter: (input.bytes[17] << 8) + input.bytes[18], + }, + }; + + case 17: + var data_type = input.bytes[3]; + if (data_type === 0x03) { + return { + // Decoded parameter + data: { + firmware: input.bytes[5] + '.' + input.bytes[6], + uploadInterval: input.bytes[7], + ultraDetectInterval: input.bytes[8], + magDetectInterval: input.bytes[9], + magThreshold: (input.bytes[12] << 8) + input.bytes[13], + }, + }; + } + default: + return { + errors: ['wrong length'], + }; + } +} + +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var periodic_interval = input.data.uploadInterval; + var periodic_interval_high = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var periodic_interval_low = periodic_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (periodic_interval > 168 || periodic_interval < 1) { + return { + errors: ['periodic upload interval range 1-168 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x31, periodic_interval_high, periodic_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.ultraDetectionInterval != null && !isNaN(input.data.ultraDetectionInterval)) { + var detection_interval = input.data.ultraDetectionInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['ultra detection interval range 1-60 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x33, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + if (input.data.magDetectInterval != null && !isNaN(input.data.magDetectInterval)) { + var detection_interval = input.data.magDetectInterval; + var detection_interval_high = detection_interval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detection_interval_low = detection_interval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detection_interval > 60 || detection_interval < 1) { + return { + errors: ['mag detection interval range 1-60 seconds.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x34, detection_interval_high, detection_interval_low, 0x38, 0x31], + }; + } + } + + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 5) { + return { + errors: ['battery alarm threshold range 5-99 %.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.magThreshold != null && !isNaN(input.data.magThreshold)) { + var magThreshold = input.data.magThreshold; + var magThreshold_high = magThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var magThreshold_low = magThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (magThreshold > 255 || magThreshold < 1) { + return { + errors: ['Geomagnet threshold range 1-255 mGs.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x38, 0x38, 0x38, 0x38, 0x30, 0x32, magThreshold_high, magThreshold_low, 0x38, 0x31], + }; + } + } + + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x38 || + input.bytes[5] != 0x38 || + input.bytes[6] != 0x38 || + input.bytes[7] != 0x38 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 2: + return { + data: { + magThreshold: value, + }, + }; + case 3: + return { + data: { + ultraDetectInterval: value, + }, + }; + case 4: + return { + data: { + magDetectInterval: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + default: + return { + errors: ['invalid parameter key.'], + }; + } +} diff --git a/vendor/dingtek/do200.png b/vendor/dingtek/do200.png new file mode 100644 index 0000000000..f59963a3a2 Binary files /dev/null and b/vendor/dingtek/do200.png differ diff --git a/vendor/dingtek/do200.yaml b/vendor/dingtek/do200.yaml new file mode 100644 index 0000000000..627dcf9b64 --- /dev/null +++ b/vendor/dingtek/do200.yaml @@ -0,0 +1,157 @@ +name: do200 +description: The CNDingtek do200 is parking occupation sensor with combination of ultrasonic and magnetic detection , fire sensor, and accelerometer. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: do200-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: do200-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: do200-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: do200-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: do200-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: do200-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: do200-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - level + - magnetometer + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + #- temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 115 + height: 50 + +# Weight in grams (optional) +weight: 150 + +# Battery information (optional) +battery: + replaceable: true + type: ER26500 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP68 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://dingtek.com/parking-occupation-sensor-do200 +#dataSheetURL: http://www.dingtek.com/documents/23/do200_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: do200.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/wfHHbZIhZMs + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dt311-codec.yaml b/vendor/dingtek/dt311-codec.yaml new file mode 100644 index 0000000000..648158eda9 --- /dev/null +++ b/vendor/dingtek/dt311-codec.yaml @@ -0,0 +1,137 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dt311.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x02, 0x12, 0x01, 0xA4, 0x00, 0x00, 0x26, 0x69, 0x57, 0x62, 0x10, 0x10, 0x00, 0x01, 0x81] + output: + data: + temperature: '26.69' + humidity: '57.62' + alarmHighTemperature: true + alarmLowTemperature: false + alarmHighHumidity: true + alarmLowHumidity: false + alarmBattery: false + volt: 4.20 + frameCounter: 1 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x01, 0x03, 0x11, 0x01, 0x02, 0x18, 0x0A, 0x00, 0x64, 0x01, 0x0A, 0x64, 0x00, 0x00, 0x14, 0x81] + output: + data: + firmware: '1.2' + uploadInterval: 24 + detectInterval: 10 + highTemperatureThreshold: 100 + lowTemperatureThreshold: -10 + highHumidityThreshold: 100 + lowHumidityThreshold: 0 + batteryThreshold: 20 + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dt311.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: change high temperature alarm threshold to 50℃ + input: + data: + highTemperatureThreshold: 50 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x30, 0x33, 0x32, 0x38, 0x31] + fPort: 3 + + - description: change low temperature alarm threshold to -10℃ + input: + data: + lowTemperatureThreshold: -10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + + - description: change high humidity alarm threshold to 70% + input: + data: + highHumidityThreshold: 70 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x34, 0x36, 0x38, 0x31] + fPort: 3 + + - description: change low humidity alarm threshold to 20% + input: + data: + lowHumidityThreshold: 20 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x45, 0x31, 0x34, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dt311.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: change high temperature alarm threshold to 50℃ + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x30, 0x33, 0x32, 0x38, 0x31] + output: + data: + highTemperatureThreshold: 50 + + - description: change low temperature alarm threshold to -10℃ + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, 0x30, 0x31, 0x30, 0x41, 0x38, 0x31] + output: + data: + lowTemperatureThreshold: -10 + + - description: change high humidity alarm threshold to 70% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, 0x34, 0x36, 0x38, 0x31] + output: + data: + highHumidityThreshold: 70 + + - description: change low humidity alarm threshold to 10% + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x45, 0x31, 0x34, 0x38, 0x31] + output: + data: + lowHumidityThreshold: 20 diff --git a/vendor/dingtek/dt311.js b/vendor/dingtek/dt311.js new file mode 100644 index 0000000000..5824915f08 --- /dev/null +++ b/vendor/dingtek/dt311.js @@ -0,0 +1,315 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes[3]) { + case 1: + case 2: + var temp_sign = input.bytes[8]; + var temp_abs = (input.bytes[9] >> 4) * 10 + (input.bytes[9] & 0x0F) + (input.bytes[10] >> 4) / 10 + (input.bytes[10] & 0x0F) / 100; + var humi_abs = (input.bytes[11] >> 4) * 10 + (input.bytes[11] & 0x0F) + (input.bytes[12] >> 4) / 10 + (input.bytes[12] & 0x0F) / 100; + var temperature_temp=(temp_sign == 0 )? temp_abs : (0 - temp_abs); + return { + // Decoded data + data: { + temperature: temperature_temp.toFixed(2), + humidity: humi_abs.toFixed(2), + alarmHighTemperature: (input.bytes[13] & 0x10) ? true : false, + alarmLowTemperature: (input.bytes[13] & 0x01) ? true : false, + alarmHighHumidity: (input.bytes[14] & 0x10) ? true : false, + alarmLowHumidity: (input.bytes[14] & 0x01) ? true : false, + alarmBattery: (input.bytes[7] & 0x01) ? true : false, + volt: ((input.bytes[5] << 8) + input.bytes[6]) / 100, + frameCounter: (input.bytes[15] << 8) + input.bytes[16], + }, + }; + case 3: + var high_temp_abs = input.bytes[10]; + var low_temp_abs = input.bytes[12]; + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." + input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + highTemperatureThreshold: input.bytes[9] ? 256 - high_temp_abs : high_temp_abs, + lowTemperatureThreshold: input.bytes[11] ? 0 - low_temp_abs : low_temp_abs, + highHumidityThreshold: input.bytes[13], + lowHumidityThreshold: input.bytes[14], + batteryThreshold: input.bytes[16], + workMode: input.bytes[15], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 255 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-255 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detectInterval = input.data.detectInterval; + var detectInterval_high = detectInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detectInterval_low = detectInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detectInterval > 255 || detectInterval < 1) { + return { + errors: ['detection interval range 1-255 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detectInterval_high, detectInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.highTemperatureThreshold != null && !isNaN(input.data.highTemperatureThreshold)) { + var highTemperatureThreshold = input.data.highTemperatureThreshold<0?0-input.data.highTemperatureThreshold:input.data.highTemperatureThreshold; + var highTemperatureSign=input.data.highTemperatureThreshold<0?0x31:0x30; + var highTemperatureThreshold_high = highTemperatureThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var highTemperatureThreshold_low = highTemperatureThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (input.data.highTemperatureThreshold > 85 || input.data.highTemperatureThreshold < -30) { + return { + errors: ['High temperature alarm threshold range -30~+85.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30,highTemperatureSign,highTemperatureThreshold_high, highTemperatureThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.lowTemperatureThreshold != null && !isNaN(input.data.lowTemperatureThreshold)) { + var lowTemperatureThreshold = input.data.lowTemperatureThreshold<0?0-input.data.lowTemperatureThreshold:input.data.lowTemperatureThreshold; + var lowTemperatureSign=input.data.lowTemperatureThreshold<0?0x31:0x30; + var lowTemperatureThreshold_high = lowTemperatureThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var lowTemperatureThreshold_low = lowTemperatureThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (input.data.lowTemperatureThreshold > 85 || input.data.lowTemperatureThreshold < -30) { + return { + errors: ['Low temperature alarm threshold range -30~+85.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x33, 0x30,lowTemperatureSign,lowTemperatureThreshold_high, lowTemperatureThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.highHumidityThreshold != null && !isNaN(input.data.highHumidityThreshold)) { + var highHumidityThreshold = input.data.highHumidityThreshold<0?0-input.data.highHumidityThreshold:input.data.highHumidityThreshold; + var highHumidityThreshold_high = highHumidityThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var highHumidityThreshold_low = highHumidityThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (input.data.highHumidityThreshold > 100 || input.data.highHumidityThreshold < 0) { + return { + errors: ['High humidity alarm threshold range 0~100.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x34, highHumidityThreshold_high, highHumidityThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.lowHumidityThreshold != null && !isNaN(input.data.lowHumidityThreshold)) { + var lowHumidityThreshold = input.data.lowHumidityThreshold<0?0-input.data.lowHumidityThreshold:input.data.lowHumidityThreshold; + var lowHumidityThreshold_high = lowHumidityThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var lowHumidityThreshold_low = lowHumidityThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (input.data.lowHumidityThreshold > 100 || input.data.lowHumidityThreshold < 0) { + return { + errors: ['High humidity alarm threshold range 0~100.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x45, lowHumidityThreshold_high, lowHumidityThreshold_low, 0x38, 0x31], + }; + } + } + if (input.data.batteryThreshold != null && !isNaN(input.data.batteryThreshold)) { + var batteryThreshold = input.data.batteryThreshold; + var batteryThreshold_high = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var batteryThreshold_low = batteryThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (batteryThreshold > 99 || batteryThreshold < 1) { + return { + errors: ['Battery alarm threshold range 1-99.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x35, batteryThreshold_high, batteryThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.workMode != null && !isNaN(input.data.workMode)) { + var workMode = input.data.workMode; + if (workMode === 0) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x35, 0x38, 0x31], + }; + } else if (workMode === 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x36, 0x38, 0x31], + }; + } else { + return { + errors: ['Work mode range 0-1.'], + } + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + var temp_abs = parseInt(String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16); + return { + data: { + highTemperatureThreshold: value > 0 ? 0 - temp_abs : temp_abs, + }, + }; + case 3: + var temp_abs = parseInt(String.fromCharCode(input.bytes[12]) + String.fromCharCode(input.bytes[13]), 16); + return { + data: { + lowTemperatureThreshold: value > 0 ? 0 - temp_abs : temp_abs, + }, + }; + case 4: + return { + data: { + highHumidityThreshold: value, + }, + }; + case 5: + return { + data: { + batteryThreshold: value, + }, + }; + case 14: + return { + data: { + lowHumidityThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/dt311.png b/vendor/dingtek/dt311.png new file mode 100644 index 0000000000..e03c67db11 Binary files /dev/null and b/vendor/dingtek/dt311.png differ diff --git a/vendor/dingtek/dt311.yaml b/vendor/dingtek/dt311.yaml new file mode 100644 index 0000000000..4c303b656d --- /dev/null +++ b/vendor/dingtek/dt311.yaml @@ -0,0 +1,158 @@ +name: dt311 +description: The CNDingtek dt311 is temperature and humidity sensor for indoor using. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dt311-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dt311-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dt311-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dt311-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dt311-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dt311-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dt311-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - humidity + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + - temperature + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 80 + height: 38 + +# Weight in grams (optional) +weight: 100 + +# Battery information (optional) +battery: + replaceable: true + type: 1000mAh + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP67 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/temperature-humitidy-sensor-dt311 +#dataSheetURL: http://www.dingtek.com/documents/23/dt311_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: dt311.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/Y1FqgKDrRsc + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/dt320-codec.yaml b/vendor/dingtek/dt320-codec.yaml new file mode 100644 index 0000000000..2386baff28 --- /dev/null +++ b/vendor/dingtek/dt320-codec.yaml @@ -0,0 +1,82 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: dt320.js + # Examples (optional) + examples: + - description: heartbeat upload + input: + fPort: 3 + bytes: [0x80, 0x00, 0x20, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x74, 0x00, 0x03, 0x81] + output: + data: + level: 0 + alarmLevel: false + alarmBattery: true + temperature: 0 + volt: 3.72 + frameCounter: 3 + + - description: parameter packet + input: + fPort: 3 + bytes: [0x80, 0x00, 0x20, 0x03, 0x0C, 0x01, 0x02, 0x04, 0x1E, 0x01, 0x00, 0x81] + output: + data: + firmware: '1.2' + uploadInterval: 4 + detectInterval: 30 + levelThreshold: 1 + workMode: 0 + +# Downlink encoder encodes JSON object into a binary data downlink (optional) +downlinkEncoder: + fileName: dt320.js + examples: + - description: change periodic upload interval to 4 hours + input: + data: + uploadInterval: 4 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + fPort: 3 + - description: change detection interval to 10 minutes + input: + data: + detectInterval: 10 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + fPort: 3 + - description: set air alarm threshold to 1 + input: + data: + levelThreshold: 1 + output: + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x38, 0x31] + fPort: 3 + +# Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +downlinkDecoder: + fileName: dt320.js + examples: + - description: change periodic upload interval to 4 hours + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, 0x30, 0x34, 0x38, 0x31] + output: + data: + uploadInterval: 4 + - description: change detection interval to 10 minutes + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, 0x30, 0x41, 0x38, 0x31] + output: + data: + detectInterval: 10 + - description: set air alarm threshold to 1 + input: + fPort: 3 + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, 0x30, 0x31, 0x38, 0x31] + output: + data: + levelThreshold: 1 diff --git a/vendor/dingtek/dt320.js b/vendor/dingtek/dt320.js new file mode 100644 index 0000000000..6353910713 --- /dev/null +++ b/vendor/dingtek/dt320.js @@ -0,0 +1,204 @@ +//IEEE754 hex to float convert +function hex2float(num) { + var sign = num & 0x80000000 ? -1 : 1; + var exponent = ((num >> 23) & 0xff) - 127; + var mantissa = 1 + (num & 0x7fffff) / 0x7fffff; + return sign * mantissa * Math.pow(2, exponent); +} + +function decodeUplink(input) { + if (input.fPort != 3) { + return { + errors: ['unknown FPort'], + }; + } + switch (input.bytes.length) { + case 18: + return { + // Decoded data + data: { + level: (input.bytes[5] << 8) + input.bytes[6], + alarmLevel: Boolean(input.bytes[11] >> 4), + alarmBattery: Boolean(input.bytes[12] & 0x0f), + temperature: input.bytes[8], + volt: ((input.bytes[13] << 8) + input.bytes[14]) / 100, + frameCounter: (input.bytes[15] << 8) + input.bytes[16], + }, + }; + case 12: + return { + // Decoded data + data: { + firmware: input.bytes[5] + "." +input.bytes[6], + uploadInterval: input.bytes[7], + detectInterval: input.bytes[8], + levelThreshold: input.bytes[9], + workMode: input.bytes[10], + }, + }; + default: + return { + errors: ['wrong length'], + }; + } +} + +// Encode downlink function. +// +// Input is an object with the following fields: +// - data = Object representing the payload that must be encoded. +// - variables = Object containing the configured device variables. +// +// Output must be an object with the following fields: +// - bytes = Byte array containing the downlink payload. +function encodeDownlink(input) { + if (input.data.uploadInterval != null && !isNaN(input.data.uploadInterval)) { + var uploadInterval = input.data.uploadInterval; + var uploadInterval_high = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var uploadInterval_low = uploadInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (uploadInterval > 255 || uploadInterval < 1) { + return { + errors: ['upload interval range 1-255 hours.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x31, uploadInterval_high, uploadInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.detectInterval != null && !isNaN(input.data.detectInterval)) { + var detectInterval = input.data.detectInterval; + var detectInterval_high = detectInterval.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var detectInterval_low = detectInterval.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (detectInterval > 255 || detectInterval < 1) { + return { + errors: ['detection interval range 1-255 minutes.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x38, detectInterval_high, detectInterval_low, 0x38, 0x31], + }; + } + } + if (input.data.levelThreshold != null && !isNaN(input.data.levelThreshold)) { + var levelThreshold = input.data.levelThreshold; + var levelThreshold_high = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[0].charCodeAt(0); + var levelThreshold_low = levelThreshold.toString(16).padStart(2, '0').toUpperCase()[1].charCodeAt(0); + if (levelThreshold > 3 || levelThreshold < 1) { + return { + errors: ['Air quality alarm threshold range 1-3.'], + }; + } else { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x32, levelThreshold_high, levelThreshold_low, 0x38, 0x31], + }; + } + } + + if (input.data.workMode != null && !isNaN(input.data.workMode)) { + var workMode = input.data.workMode; + if (workMode === 0) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x35, 0x38, 0x31], + }; + } else if(workMode === 1) { + return { + // LoRaWAN FPort used for the downlink message + fPort: 3, + // Encoded bytes + bytes: [0x38, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x30, 0x39, 0x30, 0x36, 0x38, 0x31], + }; + }else{ + return { + errors: ['Work mode range 0-1.'], + } + } + } + return { + errors: ['invalid downlink parameter.'], + }; +} + +function decodeDownlink(input) { + var input_length = input.bytes.length; + if (input.fPort != 3) { + return { + errors: ['invalid FPort.'], + }; + } + + if ( + input_length < 12 || + input.bytes[0] != 0x38 || + input.bytes[1] != 0x30 || + input.bytes[2] != 0x30 || + input.bytes[3] != 0x32 || + input.bytes[4] != 0x39 || + input.bytes[5] != 0x39 || + input.bytes[6] != 0x39 || + input.bytes[7] != 0x39 || + input.bytes[input_length - 2] != 0x38 || + input.bytes[input_length - 1] != 0x31 + ) { + return { + errors: ['invalid format.'], + }; + } + var option = parseInt(String.fromCharCode(input.bytes[8]) + String.fromCharCode(input.bytes[9]), 16); + var value = parseInt(String.fromCharCode(input.bytes[10]) + String.fromCharCode(input.bytes[11]), 16); + switch (option) { + case 1: + return { + data: { + uploadInterval: value, + }, + }; + case 8: + return { + data: { + detectInterval: value, + }, + }; + case 2: + return { + data: { + levelThreshold: value, + }, + }; + case 9: + switch (value) { + case 0x05: + return { + data: { + workMode: 0, + }, + }; + case 0x06: + return { + data: { + workMode: 1, + }, + }; + default: + return { + errors: ['invalid parameter value.'], + }; + } + default: + return { + errors: ['invalid parameter key.'], + }; + } +} \ No newline at end of file diff --git a/vendor/dingtek/dt320.png b/vendor/dingtek/dt320.png new file mode 100644 index 0000000000..e03c67db11 Binary files /dev/null and b/vendor/dingtek/dt320.png differ diff --git a/vendor/dingtek/dt320.yaml b/vendor/dingtek/dt320.yaml new file mode 100644 index 0000000000..661f3018e3 --- /dev/null +++ b/vendor/dingtek/dt320.yaml @@ -0,0 +1,157 @@ +name: dt320 +description: The CNDingtek dt320 is air quality sensor monitoring the level of methanal, ammonia, ethanol and cigarette. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 10 + - version: '1.1' + numeric: 11 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + - '1.1' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dt320-codec + US902-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dt320-codec + AU915-928: + id: dingtek-profile-915 + lorawanCertified: false + codec: dt320-codec + AS923: + id: dingtek-profile-923 + lorawanCertified: false + codec: dt320-codec + IN865-867: + id: dingtek-profile-865 + lorawanCertified: false + codec: dt320-codec + CN470-510: + id: dingtek-profile-470 + lorawanCertified: false + codec: dt320-codec + RU864-870: + id: dingtek-profile-868 + lorawanCertified: false + codec: dt320-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - iaq + #- gps + #- motion + #- full_alarm + #- fire_alarm + #- tilt_alarm + #- battery_alarm + #- angle + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + diameter: 80 + height: 38 + +# Weight in grams (optional) +weight: 100 + +# Battery information (optional) +battery: + replaceable: true + type: 1000mAh + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP67 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - serial + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://www.dingtek.com/air-quality-sensor-dt320 +#dataSheetURL: http://www.dingtek.com/documents/23/dt320_Waste_Bin_Detector_LoRaWAN_V1.8.pdf + +# Photos +photos: + main: dt320.png + #other: + +# Youtube or Vimeo Video (optional) +videos: + main: https://youtu.be/T8q0kb5tMXY + #other: https://youtu.be/EWrX-4gCLJ4 + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 1.9.2 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 1.6.1 diff --git a/vendor/dingtek/index.yaml b/vendor/dingtek/index.yaml index d0f95c473b..4b191761bc 100644 --- a/vendor/dingtek/index.yaml +++ b/vendor/dingtek/index.yaml @@ -1,4 +1,19 @@ endDevices: + - df701 - df702 - - dc410 + - df703 + - df200 + - df400 + #- df520 - df530 + - df555 + - df556 + - dc410 + - dc413 + - dc500 + - dc510 + - dc600 + - do200 + - dt311 + - dt320 + #- dt322 diff --git a/vendor/dragino/ldds75.js b/vendor/dragino/ldds75.js index f5d90c2f0a..aa9cfcfb17 100644 --- a/vendor/dragino/ldds75.js +++ b/vendor/dragino/ldds75.js @@ -7,7 +7,7 @@ function decodeUplink(input) { var interrupt = input.bytes[len-1]; switch (input.fPort) { case 2: - if(len==5) + if(len==8) { data.value=input.bytes[2]<<8 | input.bytes[3]; data.distance=(value);//distance,units:mm @@ -25,4 +25,4 @@ default: errors: ["unknown FPort"] } } -} \ No newline at end of file +} diff --git a/vendor/elv/elv-bm-trx1.js b/vendor/elv/elv-bm-trx1.js index 2ad2af1bf2..3b7d464533 100644 --- a/vendor/elv/elv-bm-trx1.js +++ b/vendor/elv/elv-bm-trx1.js @@ -1,7 +1,7 @@ /* * ELV modular system Payload-Parser * - * Version: V1.6.0 + * Version: V1.7.0 * * */ @@ -559,11 +559,202 @@ function Decoder(bytes, port) { } break; } - //case 0x??: // Further Data Type + case 0x10: // Absolute angle + { + // Get the 8 bit value + index++; // Set index to data value + Temp_Value = bytes[index]; + switch ( bytes[index] ) + { + case 0xff: + { + decoded.Absolut_Angle = "Unknown"; + } + break; + default: + { + Temp_Value *= 2.5; // Multiply the value with the angle resolution of 2.5 ° + decoded.Absolut_Angle = String(Temp_Value.toFixed(1)); + } + break; + } + break; + } + case 0x11: // Speed + { + // Get the 16 bit value + index++; // Set index to high byte data value + Temp_Value = (bytes[index] * 256); + index++; // Set index to low byte data value + Temp_Value += bytes[index]; + + // Check the wind detection bit + if( Temp_Value & 0x0800 ) + { + decoded.Wind_Detection = "1"; + } + else + { + decoded.Wind_Detection = "0"; + } + + // Get the unsigned 11 bit speed value + Temp_Value &= 0x07ff; + switch ( Temp_Value ) + { + case 0x7ff: + { + decoded.Wind_Speed = "Unknown"; + } + break; + case 0x7fe: + { + decoded.Wind_Speed = "Overflow"; + } + break; + default: + { + Temp_Value *= 0.1; // Multiply the value with the speed resolution of 0.1 km/h + decoded.Wind_Speed = String(Temp_Value.toFixed(1)); + } + break; + } + break; + } + case 0x12: // Wind + { + // Get the 16 bit value + index++; // Set index to high byte data value + Temp_Value = (bytes[index] * 256); + index++; // Set index to low byte data value + Temp_Value += bytes[index]; + + // Check the variation range + switch ( ((Temp_Value & 0xf000) / 4096) ) + { + case 0xf: + { + decoded.Variation_Angle = "Unknown"; + } + break; + case 0xe: + { + decoded.Variation_Angle = "Overflow"; + } + break; + default: + { + decoded.Variation_Angle = String(((11.25 * (Temp_Value & 0xf000) / 4096)).toFixed(2)); + } + break; + + } + // Check the wind detection bit + if( Temp_Value & 0x0800 ) + { + decoded.Wind_Detection = "1"; + } + else + { + decoded.Wind_Detection = "0"; + } - // break; + // Get the unsigned 11 bit speed value + Temp_Value &= 0x07ff; + switch ( Temp_Value ) + { + case 0x7ff: + { + decoded.Wind_Speed = "Unknown"; + } + break; + case 0x7fe: + { + decoded.Wind_Speed = "Overflow"; + } + break; + default: + { + Temp_Value *= 0.1; // Multiply the value with the speed resolution of 0.1 km/h + decoded.Wind_Speed = String(Temp_Value.toFixed(1)); + } + break; + } + // Get the 8 bit value + index++; // Set index to data value + Temp_Value = bytes[index]; + switch ( bytes[index] ) + { + case 0xff: + { + decoded.Absolut_Angle = "Unknown"; + } + break; + default: + { + Temp_Value *= 2.5; // Multiply the value with the angle resolution of 2.5 ° + decoded.Absolut_Angle = String(Temp_Value.toFixed(1)); + } + break; + } + break; + } + case 0x13: // Rainfall + { + // Get the 16 bit value + index++; // Set index to high byte data value + Temp_Value = (bytes[index] * 256); + index++; // Set index to low byte data value + Temp_Value += bytes[index]; + + // Check the rain detection bit + if( Temp_Value & 0x8000 ) + { + decoded.Rain_Detection = "1"; + } + else + { + decoded.Rain_Detection = "0"; + } + + // Check the rain counter overflow bit + if( Temp_Value & 0x4000 ) + { + decoded.Rain_Counter_Overflow = "1"; + } + else + { + decoded.Rain_Counter_Overflow = "0"; + } + + // Get the unsigned 14 bit rain amount value + Temp_Value &= 0x3fff; + switch ( Temp_Value ) + { + case 0x3fff: + { + decoded.Rain_Amount = "Unknown"; + } + break; + default: + { + Temp_Value *= 0.1; // Multiply the value with the rainfall resolution of 0.1 l/m² + decoded.Rain_Amount = String(Temp_Value.toFixed(1)); + } + break; + } + break; + } + // case 0x??: // Further Data Type + // { + // . + // . + // . + // break; + // } default: // There is something wrong with the data type value + { // Removing all added properties from the "decoded" object with a deep clean // https://stackoverflow.com/questions/19316857/removing-all-properties-from-a-object/19316873#19316873 // Object.keys(decoded).forEach(function(key){ delete decoded[key]; }); @@ -574,6 +765,7 @@ function Decoder(bytes, port) { // Add error code propertiy to the "decoded" object decoded.parser_error = "Data Type Failure --> Please update your payload parser"; break; + } } } while ((++index < bytes.length) && ('parser_error' in decoded === false)); } @@ -588,3 +780,4 @@ function Decoder(bytes, port) { return decoded; } + diff --git a/vendor/elv/elv-lw-mob.yaml b/vendor/elv/elv-lw-mob.yaml index e555e996a8..5c251b43bb 100644 --- a/vendor/elv/elv-lw-mob.yaml +++ b/vendor/elv/elv-lw-mob.yaml @@ -1,5 +1,5 @@ -name: ELV LoRaWAN® Bewegungssensor/1-Tastenfernbedienung ELV-LW-MOB -description: Compact LoRaWAN® battery radio remote control with acceleration and tilt detection from ELV +name: ELV Motion Button for LoRaWAN® +description: Compact LoRaWAN® battery powered radio remote control with acceleration and tilt detection from ELV # Hardware versions (optional, use when you have revisions) hardwareVersions: diff --git a/vendor/elv/elv-lw-omo-backview.png b/vendor/elv/elv-lw-omo-backview.png new file mode 100644 index 0000000000..1e33e86679 Binary files /dev/null and b/vendor/elv/elv-lw-omo-backview.png differ diff --git a/vendor/elv/elv-lw-omo-codec.yaml b/vendor/elv/elv-lw-omo-codec.yaml new file mode 100644 index 0000000000..59b44ab8cb --- /dev/null +++ b/vendor/elv/elv-lw-omo-codec.yaml @@ -0,0 +1,35 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: elv-lw-omo.js + # Examples (optional) + examples: + - description: device status information + input: + fPort: 10 + bytes: [0x96, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10] + output: + data: + Supply_Voltage: 1500 + frame_type: 'Device_State' + TX_Reason: 'Button Pressed' + Accelerated: false + Tilt_Area_0: false + Tilt_Area_1: false + Tilt_Area_2: false + Angle: 0 + Activation_count: 16 + - description: device acceleration + input: + fPort: 10 + bytes: [0x96, 0x02, 0x05, 0x01, 0x2D] + output: + data: + Supply_Voltage: 1500 + frame_type: 'Acceleration_Data' + TX_Reason: 'Acceleration' + Accelerated: true + Tilt_Area_0: false + Tilt_Area_1: false + Tilt_Area_2: false + Angle: 45 diff --git a/vendor/elv/elv-lw-omo-components.png b/vendor/elv/elv-lw-omo-components.png new file mode 100644 index 0000000000..04c6e785ab Binary files /dev/null and b/vendor/elv/elv-lw-omo-components.png differ diff --git a/vendor/elv/elv-lw-omo-frontview.png b/vendor/elv/elv-lw-omo-frontview.png new file mode 100644 index 0000000000..c504175ff6 Binary files /dev/null and b/vendor/elv/elv-lw-omo-frontview.png differ diff --git a/vendor/elv/elv-lw-omo-housing.png b/vendor/elv/elv-lw-omo-housing.png new file mode 100644 index 0000000000..adf41922d1 Binary files /dev/null and b/vendor/elv/elv-lw-omo-housing.png differ diff --git a/vendor/elv/elv-lw-omo.js b/vendor/elv/elv-lw-omo.js new file mode 100644 index 0000000000..41b9c6980a --- /dev/null +++ b/vendor/elv/elv-lw-omo.js @@ -0,0 +1,125 @@ +/* +* ELV-LW-OMO Payload Parser +* +* Version: V1.0.1 +* +* */ + +function decodeUplink(input) { + var data = input.bytes; + var valid = true; + + if (typeof Decoder === "function") { + data = Decoder(data, input.fPort); + } + + if (typeof Converter === "function") { + data = Converter(data, input.fPort); + } + + if (typeof Validator === "function") { + valid = Validator(data, input.fPort); + } + + if (valid) { + return { + data: data + }; + } else { + return { + data: {}, + errors: ["Invalid data received"] + }; + } +} + +var tx_reason = ["Undefined","Button Pressed", "Heartbeat", "Settings", "Joined", "Acceleration", "Tilt", "Ongoing Acceleration", "Inactivity", "Error"]; +var frame_type = ["Device_Info", "Device_State", "Acceleration_Data", "Button_Pressed", "Config_Data"]; +var device_modes = ["Acceleration", "Tilt"]; +/* + * @brief Receives the bytes transmitted from a device of the ELV-LW-OMO + * @param bytes: Array with the data stream + * @param port: Used TTN/TTS data port + * @return Decoded data from a device of the ELV-LW-OMO + * */ +function Decoder(bytes, port) { + var decoded = {}; // Container with the decoded output + var Temp_Value = 0; // Variable for temporarily calculated values + + if (port === 10) { // The default port for app data + // Minimum 5 Bytes for Header + + // Collecting header data + + decoded.Supply_Voltage = bytes[0] * 10; + decoded.frame_type = frame_type[(bytes[1])]; //Frametype encodes what Kind of Payload is being sent + decoded.TX_Reason = tx_reason[(bytes[2])]; + //Write every reason for sending correspondig to bit n + switch (decoded.frame_type) + { + case "Device_Info": + var bl_version_major = bytes[3]; + var bl_version_minor = bytes[4]; + var bl_version_patch = bytes[5]; + + decoded.Bootloader_Version = `${bl_version_major}.${bl_version_minor}.${bl_version_patch}`; //Build version-String from 3 previous values + + var fw_version_major = bytes[6]; + var fw_version_minor = bytes[7]; + var fw_version_patch = bytes[8]; + + decoded.Firmware_Version = `${fw_version_major}.${fw_version_minor}.${fw_version_patch}`;//Build version-String from 3 previous values + + decoded.hw_revision = bytes[9] << 8 | bytes[10]; // //HW version is encoded as 16-Bit int + break; + case "Device_State": + decoded.Accelerated = !!(bytes[3] & 0x1); + decoded.Tilt_Area_0 = !!(bytes[3] & 0x10); + decoded.Tilt_Area_1 = !!(bytes[3] & 0x20); + decoded.Tilt_Area_2 = !!(bytes[3] & 0x40); + + decoded.Angle = bytes[4]; + + decoded.Activation_count = (bytes[5] << 8 | bytes[6]); + break; + case "Acceleration_Data": + decoded.Accelerated = !!(bytes[3] & 0x1); + decoded.Tilt_Area_0 = !!(bytes[3] & 0x10); + decoded.Tilt_Area_1 = !!(bytes[3] & 0x20); + decoded.Tilt_Area_2 = !!(bytes[3] & 0x40); + + decoded.Angle = bytes[4]; + break; + case "Button_Pressed": + decoded.Button_Count = bytes[3]; + break; + case "Config_Data": + decoded.device_mode = ""; + for(let i = 0; i < 8; i++) + { + if((bytes[3] >> i) & 1) + { + decoded.device_mode += device_modes[i]; + } + } + + decoded.sensor_threshold = bytes[4]; + + decoded.range = bytes[5]; + decoded.alpha = bytes[6]; + + decoded.beta = bytes[7]; + + decoded.hysteresis = bytes[8]; + + decoded.senc_cycle_minutes = bytes[9] * 6; + break; + } + + } + else { + decoded.parser_error = "Wrong Port Number"; + } + + return decoded; +} \ No newline at end of file diff --git a/vendor/elv/elv-lw-omo.yaml b/vendor/elv/elv-lw-omo.yaml new file mode 100644 index 0000000000..3a40c70a0d --- /dev/null +++ b/vendor/elv/elv-lw-omo.yaml @@ -0,0 +1,118 @@ +name: ELV Outdoor Motion Sensor for LoRaWAN® +description: The ELV-LW-OMO is a device to track acceleration, vibration or realtive position changes of an outdoor asset. Fields of application are e.g. the monitoring of garbage cans or the detection of activity at a bird house + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0.3' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # If vendorID is empty, the current vendor ID is used. In this example, the vendorID is the current vendor ID, + # which is verbose. + #vendorID: elv + # Identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: trx1-profile + lorawanCertified: true + codec: elv-lw-omo-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - accelerometer + - button + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 18 + length: 17 + height: 81 + +# Weight in grams (optional) +weight: 43 + +# Battery information (optional) +battery: + replaceable: true + type: LR03 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -10 + max: 55 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +keyProgramming: + - firmware + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: none + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +firmwareProgramming: + - serial + +# Product and data sheet URLs (optional) +productURL: https://de.elv.com/ + +# Commercial information +resellerURLs: + - name: 'ELVshop' + region: + - European Union + url: https://de.elv.com/elv-lorawan-erschuetterungssensor-outdoor-elv-lw-omo-158753 +msrp: + EUR: 39.95 + +# Photos +photos: + main: elv-lw-omo-housing.png + other: + - elv-lw-omo-frontview.png + - elv-lw-omo-backview.png + - elv-lw-omo-components.png diff --git a/vendor/elv/index.yaml b/vendor/elv/index.yaml index 9782e8fb99..120553dfb9 100644 --- a/vendor/elv/index.yaml +++ b/vendor/elv/index.yaml @@ -6,3 +6,4 @@ endDevices: - elv-lw-esi - elv-lw-gps1 - elv-lw-mob + - elv-lw-omo diff --git a/vendor/enginko/decodel-level.js b/vendor/enginko/decodel-level.js index 4b4672ed9c..94c21429a6 100644 --- a/vendor/enginko/decodel-level.js +++ b/vendor/enginko/decodel-level.js @@ -10,6 +10,10 @@ function decodeUplink(payload) { content = parseTimeSync(payload.trim()); break; + case '0A': + content = parseIO(payload.trim()); + break; + case '14': content = parseLevel(payload.trim()); break; @@ -61,34 +65,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseLevel(payload) { @@ -476,3 +480,93 @@ function parseDateByte(payload) { date = new Date(year, month - 1, day, hour, minute, second, 0).toLocaleString(); return date; } + +function parseIO(payload) { + var uplinkId = payload.substring(0, 2); + + if (uplinkId.toUpperCase() === '0A') { + var date = { + variable: 'date', + value: parseDate(payload.substring(2, 10)), + }; + var firstByte = []; + var secondByte = []; + var thirdByte = []; + var fourthByte = []; + var k = 0; + + for (var i = 0; i < 3; i++) { + firstByte[i] = parseInt(payload.substring(k + 10, k + 10 + 2), 16); + secondByte[i] = parseInt(payload.substring(k + 10 + 2, k + 10 + 4), 16); + thirdByte[i] = parseInt(payload.substring(k + 10 + 4, k + 10 + 6), 16); + fourthByte[i] = parseInt(payload.substring(k + 10 + 6, k + 10 + 8), 16); + k = k + 8; + } + + var inputStatus8_1 = { + variable: 'inputStatus8_1', + value: parseFloat(firstByte[0].toString(2)), + }; + var inputStatus9_16 = { + variable: 'inputStatus9_16', + value: parseFloat(secondByte[0].toString(2)), + }; + var inputStatus17_24 = { + variable: 'inputStatus17_24', + value: parseFloat(thirdByte[0].toString(2)), + }; + var inputStatus25_32 = { + variable: 'inputStatus25_32', + value: parseFloat(fourthByte[0].toString(2)), + }; + var outputStatus8_1 = { + variable: 'outputStatus8_1', + value: parseFloat(firstByte[1].toString(2)), + }; + var outputStatus9_16 = { + variable: 'outputStatus9_16', + value: parseFloat(secondByte[1].toString(2)), + }; + var outputStatus17_24 = { + variable: 'outputStatus17_24', + value: parseFloat(thirdByte[1].toString(2)), + }; + var outputStatus25_32 = { + variable: 'outputStatus25_32', + value: parseFloat(fourthByte[1].toString(2)), + }; + var inputTrigger8_1 = { + variable: 'inputTrigger8_1', + value: parseFloat(firstByte[2].toString(2)), + }; + var inputTrigger9_16 = { + variable: 'inputTrigger9_16', + value: parseFloat(secondByte[2].toString(2)), + }; + var inputTrigger17_24 = { + variable: 'inputTrigger17_24', + value: parseFloat(thirdByte[2].toString(2)), + }; + var inputTrigger25_32 = { + variable: 'inputTrigger25_32', + value: parseFloat(fourthByte[2].toString(2)), + }; + return [ + date, + inputStatus8_1, + inputStatus9_16, + inputStatus17_24, + inputStatus25_32, + outputStatus8_1, + outputStatus9_16, + outputStatus17_24, + outputStatus25_32, + inputTrigger8_1, + inputTrigger9_16, + inputTrigger17_24, + inputTrigger25_32, + ]; + } else { + return null; + } +} diff --git a/vendor/enginko/decoder-digital.js b/vendor/enginko/decoder-digital.js index d4a1aa8e2d..586d85ec21 100644 --- a/vendor/enginko/decoder-digital.js +++ b/vendor/enginko/decoder-digital.js @@ -101,34 +101,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; - - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; + + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseDigitalData(payload) { diff --git a/vendor/enginko/decoder-environmental.js b/vendor/enginko/decoder-environmental.js index 5f4464865d..268e5b5bca 100644 --- a/vendor/enginko/decoder-environmental.js +++ b/vendor/enginko/decoder-environmental.js @@ -102,34 +102,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseTER(payload) { @@ -196,7 +196,7 @@ function parseTERMeasurement(payload, number) { unit: 'hPa', }; } - + return [date, temperature, humidity, pressure]; } diff --git a/vendor/enginko/decoder-level.js b/vendor/enginko/decoder-level.js index 54f3b840fe..086819381b 100644 --- a/vendor/enginko/decoder-level.js +++ b/vendor/enginko/decoder-level.js @@ -107,34 +107,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseDate(payload) { @@ -528,3 +528,93 @@ function getHumidity(lo) { var humidity = (((((0 & 0xff) << 8) | (lo & 0xff)) << 16) >> 16) / 2; return Number(humidity).toFixed(2); } + +function parseIO(payload) { + var uplinkId = payload.substring(0, 2); + + if (uplinkId.toUpperCase() === '0A') { + var date = { + variable: 'date', + value: parseDate(payload.substring(2, 10)), + }; + var firstByte = []; + var secondByte = []; + var thirdByte = []; + var fourthByte = []; + var k = 0; + + for (var i = 0; i < 3; i++) { + firstByte[i] = parseInt(payload.substring(k + 10, k + 10 + 2), 16); + secondByte[i] = parseInt(payload.substring(k + 10 + 2, k + 10 + 4), 16); + thirdByte[i] = parseInt(payload.substring(k + 10 + 4, k + 10 + 6), 16); + fourthByte[i] = parseInt(payload.substring(k + 10 + 6, k + 10 + 8), 16); + k = k + 8; + } + + var inputStatus8_1 = { + variable: 'inputStatus8_1', + value: parseFloat(firstByte[0].toString(2)), + }; + var inputStatus9_16 = { + variable: 'inputStatus9_16', + value: parseFloat(secondByte[0].toString(2)), + }; + var inputStatus17_24 = { + variable: 'inputStatus17_24', + value: parseFloat(thirdByte[0].toString(2)), + }; + var inputStatus25_32 = { + variable: 'inputStatus25_32', + value: parseFloat(fourthByte[0].toString(2)), + }; + var outputStatus8_1 = { + variable: 'outputStatus8_1', + value: parseFloat(firstByte[1].toString(2)), + }; + var outputStatus9_16 = { + variable: 'outputStatus9_16', + value: parseFloat(secondByte[1].toString(2)), + }; + var outputStatus17_24 = { + variable: 'outputStatus17_24', + value: parseFloat(thirdByte[1].toString(2)), + }; + var outputStatus25_32 = { + variable: 'outputStatus25_32', + value: parseFloat(fourthByte[1].toString(2)), + }; + var inputTrigger8_1 = { + variable: 'inputTrigger8_1', + value: parseFloat(firstByte[2].toString(2)), + }; + var inputTrigger9_16 = { + variable: 'inputTrigger9_16', + value: parseFloat(secondByte[2].toString(2)), + }; + var inputTrigger17_24 = { + variable: 'inputTrigger17_24', + value: parseFloat(thirdByte[2].toString(2)), + }; + var inputTrigger25_32 = { + variable: 'inputTrigger25_32', + value: parseFloat(fourthByte[2].toString(2)), + }; + return [ + date, + inputStatus8_1, + inputStatus9_16, + inputStatus17_24, + inputStatus25_32, + outputStatus8_1, + outputStatus9_16, + outputStatus17_24, + outputStatus25_32, + inputTrigger8_1, + inputTrigger9_16, + inputTrigger17_24, + inputTrigger25_32, + ]; + } else { + return null; + } +} diff --git a/vendor/enginko/decoder-power.js b/vendor/enginko/decoder-power.js index adb7d4898c..0a307d8cbf 100644 --- a/vendor/enginko/decoder-power.js +++ b/vendor/enginko/decoder-power.js @@ -102,34 +102,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; - - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; + + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseDigitalData(payload) { diff --git a/vendor/enginko/decoder-weather.js b/vendor/enginko/decoder-weather.js index 5f4464865d..268e5b5bca 100644 --- a/vendor/enginko/decoder-weather.js +++ b/vendor/enginko/decoder-weather.js @@ -102,34 +102,34 @@ function TTNto(content) { } function parseTimeSync(payload) { - const uplinkId = payload.substring(0, 2); - if (uplinkId.toUpperCase() === '01') { - const syncID = { - variable: 'syncID', - value: payload.substring(2, 10) - }; - const syncVersion = { - variable: 'syncVersion', - value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) - }; - const applicationType = { - variable: 'applicationType', - value: payload.substring(16, 20) - }; - const rfu = { - variable: 'rfu', - value: payload.substring(20) - }; + const uplinkId = payload.substring(0, 2); + if (uplinkId.toUpperCase() === '01') { + const syncID = { + variable: 'syncID', + value: payload.substring(2, 10) + }; + const syncVersion = { + variable: 'syncVersion', + value: payload.substring(10, 12) + "." + payload.substring(12, 14) + "." + payload.substring(14, 16) + }; + const applicationType = { + variable: 'applicationType', + value: payload.substring(16, 20) + }; + const rfu = { + variable: 'rfu', + value: payload.substring(20) + }; - return [ - syncID, - syncVersion, - applicationType, - rfu - ]; - } else { - return null; - } + return [ + syncID, + syncVersion, + applicationType, + rfu + ]; + } else { + return null; + } } function parseTER(payload) { @@ -196,7 +196,7 @@ function parseTERMeasurement(payload, number) { unit: 'hPa', }; } - + return [date, temperature, humidity, pressure]; } diff --git a/vendor/index.yaml b/vendor/index.yaml index 6cc57d926a..4d4c60d5d0 100644 --- a/vendor/index.yaml +++ b/vendor/index.yaml @@ -1705,7 +1705,7 @@ vendors: - id: dingtek name: Dingtek - website: http://dingtek.com/ + website: https://www.dingtek.com/ logo: dingtek_logo.png - id: elster @@ -1964,3 +1964,17 @@ vendors: name: dnt Innovation GmbH website: https://www.dnt.de/ logo: dnt-logo.svg + + - id: koidra + name: Koidra Inc. + description: Koidra is an intelligent automation company, dedicated to modernizing the automation of manufacturing facilities, with the high-tech greenhouse industry being the core market. Our tiered products deliver a comprehensive solution that enhances industrial efficiency. + website: https://www.koidra.ai/ + logo: koidra-logo.svg + social: + linkedin: https://www.linkedin.com/company/koidra/ + + - id: open-boat-projects + name: Open Boat Projects + description: Open Boat Projects is a platform on which water sports enthusiasts present their self-constructed DIY projects around boating. The focus is on the idea of open source and open hardware as well as the exchange of ideas. + website: https://open-boat-projects.org + logo: obp_logo.png diff --git a/vendor/koidra/index.yaml b/vendor/koidra/index.yaml new file mode 100644 index 0000000000..a7b4593d05 --- /dev/null +++ b/vendor/koidra/index.yaml @@ -0,0 +1,8 @@ +# This example contains just one end device: windsensor. It is referenced here in the index. + +endDevices: + # Unique identifier of the end device (lowercase, alphanumeric with dashes, max 36 characters) + - sdi-12-dra # look in sdi-12-dra.yaml for the end device definition + # - rs-485-ln # look in windsensor.yaml for the end device definition + # - rs-485-bl # look in windsensor.yaml for the end device definition + - sdi-12-tek # look in sdi-12-tek.yaml for the end device definition diff --git a/vendor/koidra/koidra-logo.svg b/vendor/koidra/koidra-logo.svg new file mode 100644 index 0000000000..2f13ae769f --- /dev/null +++ b/vendor/koidra/koidra-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vendor/koidra/sdi-12-dra-codec.yaml b/vendor/koidra/sdi-12-dra-codec.yaml new file mode 100644 index 0000000000..0b8efa9bb1 --- /dev/null +++ b/vendor/koidra/sdi-12-dra-codec.yaml @@ -0,0 +1,65 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sdi-12-dra.js + # Examples (optional) + # examples: + # - description: 32 knots from the North + # input: + # fPort: 1 + # bytes: [0, 32] + # output: + # data: + # direction: 'N' + # speed: 32 + # # Normalized output, uses the normalizeUplink function (optional) + # normalizedOutput: + # data: + # - wind: + # speed: 16.4608 + # direction: 0 + # - description: 42 knots from the East + # input: + # fPort: 1 + # bytes: [1, 42] + # output: + # data: + # direction: 'E' + # speed: 42 + # - description: Unknown FPort + # input: + # fPort: 42 + # bytes: [1, 42] + # output: + # errors: + # - unknown FPort +# # Downlink encoder encodes JSON object into a binary data downlink (optional) +# downlinkEncoder: +# fileName: windsensor.js +# examples: +# - description: Turn green +# input: +# data: +# led: green +# output: +# bytes: [1] +# fPort: 2 +# - description: Invalid color +# input: +# data: +# led: blue +# output: +# errors: +# - invalid LED color + +# # Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +# downlinkDecoder: +# fileName: windsensor.js +# examples: +# - description: Turn green +# input: +# fPort: 2 +# bytes: [1] +# output: +# data: +# led: green diff --git a/vendor/koidra/sdi-12-dra-package.jpg b/vendor/koidra/sdi-12-dra-package.jpg new file mode 100644 index 0000000000..dcc5d2233a Binary files /dev/null and b/vendor/koidra/sdi-12-dra-package.jpg differ diff --git a/vendor/koidra/sdi-12-dra-profile.yaml b/vendor/koidra/sdi-12-dra-profile.yaml new file mode 100644 index 0000000000..711231155b --- /dev/null +++ b/vendor/koidra/sdi-12-dra-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: '1.0.3' +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: 'RP001-1.0.3-RevA' + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 16 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/koidra/sdi-12-dra.jpg b/vendor/koidra/sdi-12-dra.jpg new file mode 100644 index 0000000000..1c2c2f37e2 Binary files /dev/null and b/vendor/koidra/sdi-12-dra.jpg differ diff --git a/vendor/koidra/sdi-12-dra.js b/vendor/koidra/sdi-12-dra.js new file mode 100644 index 0000000000..14f279a999 --- /dev/null +++ b/vendor/koidra/sdi-12-dra.js @@ -0,0 +1,123 @@ +// Only for AT+DATAUP=0 & AT+ALLDATAMOD=0 +function Decoder(bytes, port) { + if(port==5) + { + var freq_band; + var sub_band; + var sensor; + + if(bytes[0]==0x17) + sensor= "SDI12-LB"; + + var firm_ver= (bytes[1]&0x0f)+'.'+(bytes[2]>>4&0x0f)+'.'+(bytes[2]&0x0f); + + if(bytes[3]==0x01) + freq_band="EU868"; + else if(bytes[3]==0x02) + freq_band="US915"; + else if(bytes[3]==0x03) + freq_band="IN865"; + else if(bytes[3]==0x04) + freq_band="AU915"; + else if(bytes[3]==0x05) + freq_band="KZ865"; + else if(bytes[3]==0x06) + freq_band="RU864"; + else if(bytes[3]==0x07) + freq_band="AS923"; + else if(bytes[3]==0x08) + freq_band="AS923_1"; + else if(bytes[3]==0x09) + freq_band="AS923_2"; + else if(bytes[3]==0x0A) + freq_band="AS923_3"; + else if(bytes[3]==0x0F) + freq_band="AS923_4"; + else if(bytes[3]==0x0B) + freq_band="CN470"; + else if(bytes[3]==0x0C) + freq_band="EU433"; + else if(bytes[3]==0x0D) + freq_band="KR920"; + else if(bytes[3]==0x0E) + freq_band="MA869"; + + if(bytes[4]==0xff) + sub_band="NULL"; + else + sub_band=bytes[4]; + + var bat= (bytes[5]<<8 | bytes[6])/1000; + + return { + SENSOR_MODEL:sensor, + FIRMWARE_VERSION:firm_ver, + FREQUENCY_BAND:freq_band, + SUB_BAND:sub_band, + BAT:bat, + } + } + else if(port==100) + { + var datas_sum={}; + for(var j=0;j Suppose_Len) { + // ñ\u00170+2574.78+22.2+637JZL\r\n + garbage = 1; + tempWc = parseFloat(data_sum_infos[1]); + decode.wc_Root = 6.771*Math.pow(10, -10) * Math.pow(tempWc, 3) - 5.105*Math.pow(10, -6) * Math.pow(tempWc, 2) + 1.302*Math.pow(10, -2) * tempWc - 10.848; + } else { + decode.wc_Root = null; + } + + if (garbage) { + decode.T_Root = !isNaN(parseFloat(data_sum_infos[2])) ? parseFloat(data_sum_infos[2]) : null; + const numberRegex = /\d+/g; + const numberMatches = data_sum_infos[3].match(numberRegex); + tempEc = numberMatches % 100; + decode.ec_Root = numberMatches ? (parseInt(tempEc) / 1000) : null; + } else { + decode.T_Root = !isNaN(parseFloat(data_sum_infos[1])) ? parseFloat(data_sum_infos[1]) : null; + const numberRegex = /\d+/g; + const numberMatches = data_sum_infos[2].match(numberRegex); + decode.ec_Root = numberMatches ? (parseInt(numberMatches.join("")) / 1000) : null; + } + + return decode; + } + } \ No newline at end of file diff --git a/vendor/koidra/sdi-12-dra.yaml b/vendor/koidra/sdi-12-dra.yaml new file mode 100644 index 0000000000..3e06457e51 --- /dev/null +++ b/vendor/koidra/sdi-12-dra.yaml @@ -0,0 +1,113 @@ +name: SDI-12-DRA +description: The Koidra SDI-12 is an SDI-12 to LoRaWAN® converter. It allows the SDI-12 devices to use LoRaWAN wireless protocol which simplifies the IoT installation and reduces the installation/maintaining cost. + +# Hardware versions (optional, use when you have revisions) +# hardwareVersions: +# - version: '1.0' +# numeric: 1 +# - version: '1.0-rev-A' +# numeric: 2 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + # hardwareVersions: + # - '1.2' + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + # Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + US902-928: + id: us915-sdi-12-dra-profile + codec: sdi-12-dra-codec + KR920-923: + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + AU915-928: + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + RU864-870: + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + IN865-867: + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + AS923: + id: sdi-12-dra-profile + codec: sdi-12-dra-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, snr, solar radiation, +# sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, vibration, voltage, +# water potential, water, weight, wifi ssid, wind direction, wind speed. +#sensors: +# - wind direction +# - wind speed + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 100 + length: 160 + height: 60 + +# Weight in grams (optional) +weight: 910 + +# Battery information (optional) +battery: + replaceable: true +# type: AA + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -30 + max: 85 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 0.97 + +# IP rating (optional) +ipCode: IP67 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key security (optional) +# Valid values are: none, read protected and secure element. +# keySecurity: none +keyProgramming: + - serial + - firmware + +# Product and data sheet URLs (optional) +# productURL: http://www.dragino.com/products/lora-lorawan-end-node/item/154-rs485-ln.html +# dataSheetURL: http://www.dragino.com/products/lora-lorawan-end-node/item/154-rs485-ln.html +sellerURLs: + - name: 'Koidra' + region: + - Vietnam + url: https://www.koidra.ai/ + +# Photos +photos: + main: sdi-12-dra.jpg + other: + - sdi-12-dra-package.jpg diff --git a/vendor/koidra/sdi-12-tek-codec.yaml b/vendor/koidra/sdi-12-tek-codec.yaml new file mode 100644 index 0000000000..a47b5a3746 --- /dev/null +++ b/vendor/koidra/sdi-12-tek-codec.yaml @@ -0,0 +1,65 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sdi-12-tek.js + # Examples (optional) + # examples: + # - description: 32 knots from the North + # input: + # fPort: 1 + # bytes: [0, 32] + # output: + # data: + # direction: 'N' + # speed: 32 + # # Normalized output, uses the normalizeUplink function (optional) + # normalizedOutput: + # data: + # - wind: + # speed: 16.4608 + # direction: 0 + # - description: 42 knots from the East + # input: + # fPort: 1 + # bytes: [1, 42] + # output: + # data: + # direction: 'E' + # speed: 42 + # - description: Unknown FPort + # input: + # fPort: 42 + # bytes: [1, 42] + # output: + # errors: + # - unknown FPort +# # Downlink encoder encodes JSON object into a binary data downlink (optional) +# downlinkEncoder: +# fileName: windsensor.js +# examples: +# - description: Turn green +# input: +# data: +# led: green +# output: +# bytes: [1] +# fPort: 2 +# - description: Invalid color +# input: +# data: +# led: blue +# output: +# errors: +# - invalid LED color + +# # Downlink decoder decodes the encoded downlink message (optional, must be symmetric with downlinkEncoder) +# downlinkDecoder: +# fileName: windsensor.js +# examples: +# - description: Turn green +# input: +# fPort: 2 +# bytes: [1] +# output: +# data: +# led: green diff --git a/vendor/koidra/sdi-12-tek-package.jpg b/vendor/koidra/sdi-12-tek-package.jpg new file mode 100644 index 0000000000..f4a1c4072e Binary files /dev/null and b/vendor/koidra/sdi-12-tek-package.jpg differ diff --git a/vendor/koidra/sdi-12-tek-profile.yaml b/vendor/koidra/sdi-12-tek-profile.yaml new file mode 100644 index 0000000000..711231155b --- /dev/null +++ b/vendor/koidra/sdi-12-tek-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: '1.0.3' +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: 'RP001-1.0.3-RevA' + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 16 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/koidra/sdi-12-tek.jpg b/vendor/koidra/sdi-12-tek.jpg new file mode 100644 index 0000000000..bac74d7765 Binary files /dev/null and b/vendor/koidra/sdi-12-tek.jpg differ diff --git a/vendor/koidra/sdi-12-tek.js b/vendor/koidra/sdi-12-tek.js new file mode 100644 index 0000000000..25aaf2ac64 --- /dev/null +++ b/vendor/koidra/sdi-12-tek.js @@ -0,0 +1,79 @@ +// Only for AT+DATAUP=0 & AT+ALLDATAMOD=0 +// Tekbox format only +function Decoder(bytes, port) { + const Report_Len = 41; + const Bat_Len = 25; + const Measure_Len = 38; + + if(port==5) + { + var freq_band; + var sub_band; + var sensor; + + } + else if(port==100) + { + var datas_sum={}; + for(var j=0;j value == '1'; var calculateTemperature = function (rawData){return (rawData - 400) / 10}; var calculateHumidity = function(rawData){return (rawData * 100) / 256}; - + var decbin = function (number) { + if (number < 0) { + number = 0xFFFFFFFF + number + 1 + } + number = number.toString(2); + return "00000000".substr(number.length) + number; + } function handleKeepalive(bytes, data){ var tempHex = '0' + bytes[1].toString(16) + bytes[2].toString(16); var tempDec = parseInt(tempHex, 16); var temperatureValue = calculateTemperature(tempDec); var humidityValue = calculateHumidity(bytes[3]); - var batteryHex = '0' + bytes[4].toString(16) + bytes[5].toString(16); - var batteryVoltageCalculated = parseInt(batteryHex, 16)/1000; + var temperature = temperatureValue; var humidity = humidityValue; - var batteryVoltage = batteryVoltageCalculated; + var batteryVoltage = parseInt(`${decbin(bytes[4])}${decbin(bytes[5])}`, 2)/1000; var targetTemperature = bytes[6]; var powerSourceStatus = bytes[7]; var lux = parseInt('0' + bytes[8].toString(16) + bytes[9].toString(16), 16); @@ -91,6 +95,12 @@ function decodeUplink(input) { data.targetTemperature = parseInt(commands[i + 1], 16) ; } break; + case '30': + { + command_len = 1; + data.manualTargetTemperatureUpdate = parseInt(commands[i + 1], 16) ; + } + break; case '32': { command_len = 1; @@ -121,7 +131,7 @@ function decodeUplink(input) { data = handleKeepalive(bytes, data); }else{ data = handleResponse(bytes,data); - bytes = bytes.slice(-8); + bytes = bytes.slice(-11); data = handleKeepalive(bytes, data); } return {data: data}; diff --git a/vendor/mutelcor/mtc-aq01.yaml b/vendor/mutelcor/mtc-aq01.yaml index b5cb018c64..1bc541ec46 100644 --- a/vendor/mutelcor/mtc-aq01.yaml +++ b/vendor/mutelcor/mtc-aq01.yaml @@ -1,5 +1,5 @@ name: MUTELCOR LoRa Air Quality Sensor (IP30, IP67) -description: The Mutelcor LoRa Air Quality Sensor is a LoRaWAN® Device that measures wirelessly Temperature, Humidity and Pressure in Regfrigerators, Deep Freezers, including indoor applications like Corporate Buildings & Healthcare centers (ICUs Operation Theatre & Medical Labs) and Triggers alarms on exceeded thresholds. +description: The Mutelcor LoRa Air Quality Sensor is a LoRaWAN® Device that measures wirelessly Temperature and Humidity in Regfrigerators, Deep Freezers, including indoor applications like Corporate Buildings & Healthcare centers (ICUs Operation Theatre & Medical Labs) and Triggers alarms on exceeded thresholds. # Hardware versions (optional, use when you have revisions) hardwareVersions: @@ -63,7 +63,6 @@ sensors: - button - humidity - temperature - - pressure - digital input # Dimensions in mm (optional) @@ -96,8 +95,8 @@ ipCode: IP30 keySecurity: none # Product and data sheet URLs (optional) -productURL: https://mutelcor.com/air-quality-sensor -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_LoRa_Air_Quality_Sensor.pdf +productURL: https://mutelcor.com/lora-air-quality-sensor/ +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_LoRa_Air_Quality_Sensor.pdf # Photos photos: diff --git a/vendor/mutelcor/mtc-au01.yaml b/vendor/mutelcor/mtc-au01.yaml index b33eca4976..96d14f03d7 100644 --- a/vendor/mutelcor/mtc-au01.yaml +++ b/vendor/mutelcor/mtc-au01.yaml @@ -89,7 +89,7 @@ keySecurity: none # Product and data sheet URLs (optional) productURL: https://mutelcor.com/lora-alarm-unit/ -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_LoRa_Alarm_Unit.pdf +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_LoRa_Alarm_Unit.pdf # Photos photos: diff --git a/vendor/mutelcor/mtc-cf01.yaml b/vendor/mutelcor/mtc-cf01.yaml index 1e1ff6e8db..874d355c2f 100644 --- a/vendor/mutelcor/mtc-cf01.yaml +++ b/vendor/mutelcor/mtc-cf01.yaml @@ -94,8 +94,8 @@ ipCode: IP67 keySecurity: none # Product and data sheet URLs (optional) -productURL: https://mutelcor.com/iot/iot-solutions/lora-customer-feedback/ -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_LoRa_Customer_Feedback.pdf +productURL: https://mutelcor.com/lora-customer-feedback/ +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_LoRa_Customer_Feedback.pdf # Photos photos: diff --git a/vendor/mutelcor/mtc-co2-01.yaml b/vendor/mutelcor/mtc-co2-01.yaml index b64a66e933..ec73586982 100644 --- a/vendor/mutelcor/mtc-co2-01.yaml +++ b/vendor/mutelcor/mtc-co2-01.yaml @@ -91,8 +91,8 @@ ipCode: IP30 keySecurity: none # Product and data sheet URLs (optional) -productURL: https://mutelcor.com/lora-co2-digitalmelder/ -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_Smart_CO2_LoRa_Sensor.pdf +productURL: https://mutelcor.com/smart-co2-lora-sensor/ +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_Smart_CO2_LoRa_Sensor.pdf # Photos photos: diff --git a/vendor/mutelcor/mtc-mh01.yaml b/vendor/mutelcor/mtc-mh01.yaml index 4f5479afa1..cb0bb2a38f 100644 --- a/vendor/mutelcor/mtc-mh01.yaml +++ b/vendor/mutelcor/mtc-mh01.yaml @@ -93,8 +93,8 @@ ipCode: IP67 keySecurity: none # Product and data sheet URLs (optional) -productURL: https://mutelcor.com/iot/iot-solutions/lora-manhole-sensor/ -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_LoRa_Manhole_Sensor.pdf +productURL: https://mutelcor.com/lora-manhole-sensor/ +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_LoRa_Manhole_Sensor.pdf # Photos photos: diff --git a/vendor/mutelcor/mtc-pb01.yaml b/vendor/mutelcor/mtc-pb01.yaml index b4b2fc4754..5b9b776c2c 100644 --- a/vendor/mutelcor/mtc-pb01.yaml +++ b/vendor/mutelcor/mtc-pb01.yaml @@ -94,8 +94,8 @@ ipCode: IP67 keySecurity: none # Product and data sheet URLs (optional) -productURL: https://mutelcor.com/iot/iot-solutions/iot-reflection-panic-button-security-solution/ -dataSheetURL: https://mutelcor.com/wp-content/uploads/2022/11/MUTELCOR_Datasheet_LoRa_Panic_Button_with_confirmation.pdf +productURL: https://mutelcor.com/lora-panic-button-with-confirmation/ +dataSheetURL: https://mutelcor.com/wp-content/uploads/2023/07/MUTELCOR_Datasheet_LoRa_Panic_Button_with_confirmation.pdf # Photos photos: diff --git a/vendor/nexelec/feel-codec.yaml b/vendor/nexelec/feel-codec.yaml new file mode 100644 index 0000000000..3535c0ce28 --- /dev/null +++ b/vendor/nexelec/feel-codec.yaml @@ -0,0 +1,10 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sign-codec.js + +downlinkEncoder: + fileName: sign-codec.js + +downlinkDecoder: + fileName: sign-codec.js diff --git a/vendor/nexelec/feel-profile.yaml b/vendor/nexelec/feel-profile.yaml new file mode 100644 index 0000000000..edcc14b18a --- /dev/null +++ b/vendor/nexelec/feel-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0686 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: 1.0.2 +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: RP001-1.0.2-RevB + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 14 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/nexelec/feel.yaml b/vendor/nexelec/feel.yaml new file mode 100644 index 0000000000..b9dcab76f2 --- /dev/null +++ b/vendor/nexelec/feel.yaml @@ -0,0 +1,131 @@ +name: FEEL +description: FEEL is a 2-in-1 LoRaWAN-connected, battery- or USB-powered room sensor that can be rapidly configurable via NFC. Depending on the model, the sensor can measure temperature and humidity. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: 'V001' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: 'V01.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - 'V001' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # If vendorID is empty, the current vendor ID is used. In this example, the vendorID is the current vendor ID, + # which is verbose. + #vendorID: example + # Identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: feel-profile + lorawanCertified: false + codec: sign-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - temperature + - humidity + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +additionalRadios: + - nfc + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 87 + length: 87 + height: 23 + +# Weight in grams (optional) +weight: 110 + +# Battery information (optional) +battery: + replaceable: true + type: AA + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -30 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP30 + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Product and data sheet URLs (optional) +productURL: https://www.nexelec.fr/ +dataSheetURL: https://support.nexelec.fr/en/support/solutions/articles/80001085095-datasheet/ + +# Commercial information +resellerURLs: + - name: 'Nexelec' + region: + - European Union + url: https://www.nexelec.fr/ + +msrp: + EUR: 59 + USD: 65 + +# Photos +photos: + main: sign.png + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + - body: ETSI + norm: EN + standard: 62479:2010 + + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 2.2.0 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 2.1.0 + - body: ETSI + norm: EN + standard: 300 220-2 + version: 3.2.1 diff --git a/vendor/nexelec/index.yaml b/vendor/nexelec/index.yaml index a2f5f5c1a3..8c80e162f0 100644 --- a/vendor/nexelec/index.yaml +++ b/vendor/nexelec/index.yaml @@ -1,5 +1,7 @@ -# This example contains just one end device: windsensor. It is referenced here in the index. - endDevices: # Unique identifier of the end device (lowercase, alphanumeric with dashes, max 36 characters) - - carbon # look in windsensor.yaml for the end device definition + - carbon # CO2, temp, hum sensor + - sign # CO2, COV, temp, hum, lum, pir, sound sensor + - move # CO2, temp, hum, lum, pir sensor + - rise # CO2, temp, hum sensor + - feel # temp, hum sensor diff --git a/vendor/nexelec/move-codec.yaml b/vendor/nexelec/move-codec.yaml new file mode 100644 index 0000000000..3535c0ce28 --- /dev/null +++ b/vendor/nexelec/move-codec.yaml @@ -0,0 +1,10 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sign-codec.js + +downlinkEncoder: + fileName: sign-codec.js + +downlinkDecoder: + fileName: sign-codec.js diff --git a/vendor/nexelec/move-profile.yaml b/vendor/nexelec/move-profile.yaml new file mode 100644 index 0000000000..edcc14b18a --- /dev/null +++ b/vendor/nexelec/move-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0686 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: 1.0.2 +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: RP001-1.0.2-RevB + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 14 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/nexelec/move.yaml b/vendor/nexelec/move.yaml new file mode 100644 index 0000000000..de38934fe4 --- /dev/null +++ b/vendor/nexelec/move.yaml @@ -0,0 +1,134 @@ +name: MOVE +description: MOVE is a 5-in-1 LoRaWAN-connected, battery- or USB-powered room sensor that can be rapidly configurable via NFC. Depending on the model, the sensor can measure temperature, humidity, brightness, presence and noise. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: 'V001' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: 'V01.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - 'V001' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # If vendorID is empty, the current vendor ID is used. In this example, the vendorID is the current vendor ID, + # which is verbose. + #vendorID: example + # Identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: move-profile + lorawanCertified: false + codec: sign-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - temperature + - humidity + - motion + - light + - sound + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +additionalRadios: + - nfc + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 87 + length: 87 + height: 23 + +# Weight in grams (optional) +weight: 110 + +# Battery information (optional) +battery: + replaceable: true + type: AA + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -30 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP30 + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Product and data sheet URLs (optional) +productURL: https://www.nexelec.fr/ +dataSheetURL: https://support.nexelec.fr/en/support/solutions/articles/80001085094-datasheet/ + +# Commercial information +resellerURLs: + - name: 'Nexelec' + region: + - European Union + url: https://www.nexelec.fr/ + +msrp: + EUR: 79 + USD: 87 + +# Photos +photos: + main: sign.png + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + - body: ETSI + norm: EN + standard: 62479:2010 + + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 2.2.0 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 2.1.0 + - body: ETSI + norm: EN + standard: 300 220-2 + version: 3.2.1 diff --git a/vendor/nexelec/rise-codec.yaml b/vendor/nexelec/rise-codec.yaml new file mode 100644 index 0000000000..3535c0ce28 --- /dev/null +++ b/vendor/nexelec/rise-codec.yaml @@ -0,0 +1,10 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sign-codec.js + +downlinkEncoder: + fileName: sign-codec.js + +downlinkDecoder: + fileName: sign-codec.js diff --git a/vendor/nexelec/rise-profile.yaml b/vendor/nexelec/rise-profile.yaml new file mode 100644 index 0000000000..edcc14b18a --- /dev/null +++ b/vendor/nexelec/rise-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0686 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: 1.0.2 +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: RP001-1.0.2-RevB + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 14 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/nexelec/rise.yaml b/vendor/nexelec/rise.yaml new file mode 100644 index 0000000000..2ba67773e8 --- /dev/null +++ b/vendor/nexelec/rise.yaml @@ -0,0 +1,132 @@ +name: RISE +description: RISE is a 3-in-1 LoRaWAN-connected, battery- or USB-powered room sensor that can be rapidly configurable via NFC. Depending on the model, the sensor can measure co2, temperature and humidity. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: 'V001' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: 'V01.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - 'V001' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # If vendorID is empty, the current vendor ID is used. In this example, the vendorID is the current vendor ID, + # which is verbose. + #vendorID: example + # Identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: rise-profile + lorawanCertified: false + codec: sign-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - co2 + - temperature + - humidity + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +additionalRadios: + - nfc + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 87 + length: 87 + height: 23 + +# Weight in grams (optional) +weight: 110 + +# Battery information (optional) +battery: + replaceable: true + type: AA + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -30 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP30 + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Product and data sheet URLs (optional) +productURL: https://www.nexelec.fr/ +dataSheetURL: https://support.nexelec.fr/en/support/solutions/articles/80001085097-datasheet/ + +# Commercial information +resellerURLs: + - name: 'Nexelec' + region: + - European Union + url: https://www.nexelec.fr/ + +msrp: + EUR: 165 + USD: 181 + +# Photos +photos: + main: sign.png + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + - body: ETSI + norm: EN + standard: 62479:2010 + + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 2.2.0 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 2.1.0 + - body: ETSI + norm: EN + standard: 300 220-2 + version: 3.2.1 diff --git a/vendor/nexelec/sign-codec.js b/vendor/nexelec/sign-codec.js new file mode 100644 index 0000000000..755defe5e8 --- /dev/null +++ b/vendor/nexelec/sign-codec.js @@ -0,0 +1,551 @@ +function decodeUplink(input) +{ + var output = 0; + var data; + + stringHex = bytesString(input.bytes); + //console.log(stringHex) + var octetTypeProduit = parseInt(stringHex.substring(0,2),16); + var octetTypeMessage = parseInt(stringHex.substring(2,4),16); + //console.log(octetTypeMessage) + + function bytesString(input){ + var bufferString=''; + var decToString=''; + + for(var i=0; i=1023){return "Error"} + else{return {"value":((octetTemperatureValue/10)-30), "unit":"°C" }} + + } + + function humidity(octetHumidityValue) + { + if(octetHumidityValue>=1023){return "Error"} + else{return {"value":(octetHumidityValue/10), "unit" :"%RH"}} + } + + function co2(octetCO2Value) + { + if(octetCO2Value>=16383){return "Error"} + else{return {"value":octetCO2Value, "unit":"ppm"}}; + } + + function covt(octetCOVTValue) + { + if(octetCOVTValue>=16383){return "Error"} + else{return {"value":octetCOVTValue,"unit":"ug/m3"}}; + } + + function luminosity(octetLuminosityValue) + { + if(octetLuminosityValue>=1023){return "Error"} + else{return {"value":(octetLuminosityValue*5),"unit":"lux"}}; + } + + function buttonPress(octetButtonValue) + { + if(octetButtonValue===0){return "Button not pressed"} + else if(octetButtonValue===1){return "Button pressed"}; + } + + function averageNoise(octetAverageNoise) + { + if(octetAverageNoise===127){return "Error"} + else{return {"value":octetAverageNoise,"unit":"dB"}}; + } + + function peakNoise(octetPeakNoise) + { + if(octetPeakNoise===127){return "Error"} + else{return {"value":octetPeakNoise,"unit":"dB"}}; + } + + function occupancyRate(octetOccupancyRate) + { + if(octetOccupancyRate===127){return "Error"} + else{return {"value":octetOccupancyRate,"unit":"%"}}; + } + + function iaqGlobalArgument(octetiaqGlobal) + { + const message_name =["Excellent","Reserved","Fair","Reserved","Bad","Reserved","Reserved","Error"] + return message_name[octetiaqGlobal] + } + + function iaqSourceArgument(octetiaqSource) + { + const message_name =["None","Reserved","Reserved","Reserved","Reserved","CO2","VOC","Reserved","Reserved","Reserved","Reserved","Reserved","Reserved","Reserved","Reserved","Error"] + return message_name[octetiaqSource] + } + + /////////////////////////////////////////////// + // Product Status Message Function + /////////////////////////////////////////////// + + function hwRevision(octetHWRevision) + { + switch(octetHWRevision){ + case 0: + return 0 + break; + + case 1: + return 1 + break; + } + } + + function swRevision(octetSWRevision) + { + switch(octetSWRevision){ + case 0: + return 0 + break; + + case 1: + return 1 + break; + } + } + + function powerSource(octetPowerSource) + { + const message_name =["Battery","External 5V","Reserved","Reserved"] + return message_name[octetPowerSource] + } + + function batteryVoltage(octetbatteryVoltage) + { + if(octetbatteryVoltage===1023){return "Error"} + else if(octetbatteryVoltage === 1022){return "External power supply"} + else{return {"value":(octetbatteryVoltage*5), "unit":"mV" }} + } + + function batterieLevelArgument(octetBatteryLevel) + { + const message_name =["High","Medium","Low","Critical","External power supply"] + return message_name[octetBatteryLevel] + } + + function productHwStatusArgument(octetProductHwStatus) + { + const message_name =["Hardware working correctly","Hardware fault detected"] + return message_name[octetProductHwStatus] + } + + function sensorStatusArgument(octetSensorStatus) + { + const message_name =["Sensor Ok","Sensor default","Sensor not present","Sensor disabled","End-of-life sensor"] + return message_name[octetSensorStatus] + } + + function sdStatusArgument(octetSDStatus) + { + const message_name =["SD card OK","Fault: Cannot mount drive","Card missing","Functionality disabled","SD card at end of life"] + return message_name[octetSDStatus] + } + + function productActivationTimeCounter(octetTimeCounter) + { + if(octetTimeCounter===1023 ){return "Error"} + else{return {"value":octetTimeCounter,"unit":"month"}}; + } + + ///////////////////////////////////////////////////// + //Product Configuration Message Function + //////////////////////////////////////////////////// + + function reconfigurationSource(octetReconfigurationSource) + { + const message_name =["NFC","Downlink","Product Start up","Network","GPS","Local"] + return message_name[octetReconfigurationSource] + } + + function reconfigurationState(octetReconfigurationState) + { + const message_name =["Total Succes","Partial Succes","Total fail","Reserved"] + return message_name[octetReconfigurationState] + } + + function period(octetPeriod) + { + return {"value":octetPeriod, "unit":"minutes"}; + } + + function sensorActivation(octetSensorActivate) + { + if(octetSensorActivate===0){return "Sensor desactivated"} + else if(octetSensorActivate===1){return "Sensor activated" }; + } + + function sdActivation(octetSDActivate) + { + if(octetSDActivate===0){return "SD desactivated"} + else if(octetSDActivate===1){return "SD activated"}; + } + + function calibrationActivation(octetCalibrationActivate) + { + if(octetCalibrationActivate===0){return "Automatic calibration desactivated"} + else if(octetCalibrationActivate===1){return "Automatic calibration activated"}; + } + + function co2Threshold(octetCO2Threshold) + { + return {"value":octetCO2Threshold, "unit":"ppm"}; + } + + function active(octetActive) + { + if(octetActive===0){return "Non-active"} + else if(octetActive===1){return "Active "}; + } + + function notificationByLEDandBuzzer(octetNotification) + { + if(octetNotification===0){return "CO2"} + else if(octetNotification===1){return "IziAir"}; + } + + function loraRegion(octetLoRaRegion) + { + const message_name =["EU868","US915","AS923","AU915","KR920","IN865","RU864"] + return message_name[octetLoRaRegion] + } + + function deltaCO2(octetDeltaCO2) + { + if(octetDeltaCO2===255){return "Desactivated"} + else{return {"value":(octetDeltaCO2*0.25),"unit":"ppm"}} + } + + function deltaTemp(octetDeltaTemp) + { + if(octetDeltaTemp===127){return "Desactivated"} + else{return {"value":(octetDeltaTemp*0.1),"unit":"ppm"}} + } + + function co2Threshold(octetCO2Threshold) + { + return {"value":octetCO2Threshold, "unit":"ppm"}; + } + + function transmissionPeriodHistorical(octetPeriodHistorical) + { + if(octetPeriodHistorical===255){return "Erreur"} + else{return {"value":(octetPeriodHistorical*0.1),"unit":"ppm"}} + } + + function pendingJoin(octetPending) + { + if(octetPending===0){return "No join request"} + else if(octetPending===1){return "Programmed join request"}; + } + + function nfcStatus(octetNfcStatus) + { + if(octetNfcStatus===0){return "No join request"} + else if(octetNfcStatus===1){return "Programmed join request"}; + } + + ////////////////////////////////////////////////////////////////////// + ////// Hex to binary + //////////////////////////////////////////////////////////////////// + + function hexToBinary(stringHex) + { + var string_bin="" + var string_bin_elements=""; + for(i=0;i> 6) & 0x3FF; + var data_humidity = (parseInt(stringHex.substring(6,9),16)) & 0x3FF; + var data_co2 = (parseInt(stringHex.substring(9,13),16)>>2) & 0x3FFF; + var data_covt = (parseInt(stringHex.substring(12,16),16)) & 0x3FFF; + var data_luminosity = (parseInt(stringHex.substring(16,19),16) >> 2) & 0x3FF; + var data_button_press = (parseInt(stringHex.substring(18,20),16)>>5) & 0x01; + var data_avg_noise = (parseInt(stringHex.substring(18,21),16)>>2) & 0x7F; + var data_peak_noise = (parseInt(stringHex.substring(20,23),16) >> 3) & 0x7F; + var data_occupancy_rate = (parseInt(stringHex.substring(22,24),16)) & 0x7F; + var data_izi_air_global = (parseInt(stringHex.substring(24,26),16) >> 5) & 0x07; + var data_izi_air_src = (parseInt(stringHex.substring(24,26),16) >> 1) & 0x0F; + var data_izi_air_co2 = (parseInt(stringHex.substring(25,27),16) >> 2) & 0x07; + var data_izi_air_cov = (parseInt(stringHex.substring(26,28),16) >> 3) & 0x07; + + data = { "typeOfProduct": typeOfProduct(octetTypeProduit), + "typeOfMessage": typeOfMessage(octetTypeMessage), + "temperature": temperature(data_temperature), + "humidity": humidity(data_humidity) , + "co2": co2(data_co2), + "covt": covt(data_covt), + "luminosity": luminosity(data_luminosity), + "buttonPress": buttonPress(data_button_press) , + "averageNoise": averageNoise(data_avg_noise) , + "peakNoise": peakNoise(data_peak_noise), + "occupancyRate": occupancyRate(data_occupancy_rate), + "iziairGlobal": iaqGlobalArgument(data_izi_air_global) , + "iziairSrc": iaqSourceArgument(data_izi_air_src), + "iziairCo2": iaqGlobalArgument(data_izi_air_co2), + "iziairCov": iaqGlobalArgument(data_izi_air_cov), + }; + return data; + } + + function historicalCO2DataOutput(stringHex) + { + var mesure = []; + var debugmesure = []; + var i = 0; + var offset_octet = 0; + + var data_nombre_mesures = (parseInt(stringHex.substring(4,6),16)>>2)&0x3F; + var data_time_between_measurement_sec = 60 * ((parseInt(stringHex.substring(4,8),16)>>2)&0xFF); + var data_repetition = (parseInt(stringHex.substring(7,9),16))&0x3F; + var binary=hexToBinary(stringHex) + + + for(i=0;i>2)&0x3F; + var data_time_between_measurement_sec = 60 * ((parseInt(stringHex.substring(4,8),16)>>2)&0xFF); + var data_repetition = (parseInt(stringHex.substring(7,9),16))&0x3F; + var binary=hexToBinary(stringHex) + + + + + for(i=0;i>1) & 0x07; + var data_hw_status_temperature = (parseInt(stringHex.substring(12,14),16)>>5) & 0x07; + à corriger + */ + function productStatusDataOutput(stringHex) + { + var data_hw_version = (parseInt(stringHex.substring(4,6),16)) & 0xFF; + var data_sw_version = (parseInt(stringHex.substring(6,8),16)) & 0xFF; + var data_power_source = (parseInt(stringHex.substring(8,10),16)>>6) & 0x07; + var data_battery_voltage = (parseInt(stringHex.substring(8,11),16)) & 0x3FF; + var data_battery_level = (parseInt(stringHex.substring(10,12),16) >> 1 ) & 0x07; + var data_hw_status = (parseInt(stringHex.substring(10,12),16)) & 0x01; + var data_hw_status_temperature = (parseInt(stringHex.substring(11,13),16)>>1) & 0x07; + var data_hw_status_co2 = (parseInt(stringHex.substring(12,14),16)>>2) & 0x07; + var data_hw_status_covt = (parseInt(stringHex.substring(13,15),16)>>3) & 0x07; + var data_hw_status_pir = (parseInt(stringHex.substring(13,15),16)) & 0x07; + var data_hw_status_microphone = (parseInt(stringHex.substring(14,16),16)>>1) & 0x07; + var data_hw_status_luminosity = (parseInt(stringHex.substring(15,17),16)>>3) & 0x07; + var data_hw_status_SD= (parseInt(stringHex.substring(16,18),16)>>3) & 0x07; + var data_activation_time = parseInt(stringHex.substring(16,19),16) & 0x3FF; + var data_co2_last_manual_calibration_time = parseInt(stringHex.substring(19,21),16); + + data = { "typeOfProduct": typeOfProduct(octetTypeProduit), + "typeOfMessage": typeOfMessage(octetTypeMessage), + "hardwareVersion":data_hw_version, + "softwareVersion":data_sw_version, + "powerSource":powerSource(data_power_source), + "batteryVoltage":batteryVoltage(data_battery_voltage), + "batteryLevel":batterieLevelArgument(data_battery_level), + "hardwareStatus":productHwStatusArgument(data_hw_status), + "temperatureSensorStatus":sensorStatusArgument(data_hw_status_temperature), + "co2SensorStatus":sensorStatusArgument(data_hw_status_co2), + "covtSensorStatus":sensorStatusArgument(data_hw_status_covt), + "pirSensorStatus":sensorStatusArgument(data_hw_status_pir), + "microphoneStatus":sensorStatusArgument(data_hw_status_microphone), + "luminositySensorStatus":sensorStatusArgument(data_hw_status_luminosity), + "sdStatus":sdStatusArgument(data_hw_status_SD), + "cumulativeProductActivationTime":productActivationTimeCounter(data_activation_time), + "timeSinceLastManualCalibration": {"value":data_co2_last_manual_calibration_time,"unit":"days"} + }; + return data; + } + + function productConfigurationDataOutput(stringHex) + { + var data_config_source = (parseInt(stringHex.substring(4,5),16)>>1) & 0x07; + var data_config_status = (parseInt(stringHex.substring(4,6),16)>>3) & 0x03; + var data_periode_mesure = (parseInt(stringHex.substring(5,7),16)>>2) & 0x1F; + var data_sensor_on_off_temperature = (parseInt(stringHex.substring(6,7),16)>>1) & 0x01; + var data_sensor_on_off_co2 = (parseInt(stringHex.substring(6,7),16)) & 0x01; + var data_sensor_on_off_cov = (parseInt(stringHex.substring(7,8),16)>>3) & 0x01; + var data_sensor_on_off_pir = (parseInt(stringHex.substring(7,8),16)>>2) & 0x01; + var data_sensor_on_off_microphone = (parseInt(stringHex.substring(7,8),16)>>1) & 0x01; + var data_sensor_on_off_luminosity = (parseInt(stringHex.substring(7,8),16)) & 0x01; + var data_sd_storage_on_off = (parseInt(stringHex.substring(8,9),16)>>3) & 0x01; + var data_co2_auto_calibration = (parseInt(stringHex.substring(8,9),16)>>2) & 0x01; + var data_co2_medium_level = (parseInt(stringHex.substring(8,11),16)) & 0x3FF; + var data_co2_high_level = (parseInt(stringHex.substring(11,14),16)>>2) & 0x3FF; + var data_led_on_off = (parseInt(stringHex.substring(13,14),16) >> 1) & 0x01; + var data_led_orange_on_off = (parseInt(stringHex.substring(13,14),16)) & 0x01; + var data_buzzer_on_off = (parseInt(stringHex.substring(14,15),16) >> 3) & 0x01; + var data_buzzer_confirm_on_off = (parseInt(stringHex.substring(14,15),16) >> 2) & 0x01; + var data_led_source = parseInt(stringHex.substring(14,15), 16) & 0x03; + var data_button_on_off = (parseInt(stringHex.substring(15,16),16) >> 3) & 0x01; + var data_lora_region = (parseInt(stringHex.substring(15,17), 16)>>3) & 0x0F; + var data_periodic_tx_on_off = (parseInt(stringHex.substring(16,17),16) >> 2) & 0x01; + var data_periodic_tx_period = (parseInt(stringHex.substring(16,18),16)) & 0x01; + var data_periodic_delta_co2 = (parseInt(stringHex.substring(18,20),16)) & 0xFF; + var data_periodic_delta_temp = (parseInt(stringHex.substring(20,22),16)>>1) & 0x7F; + var data_datalog_co2_on_off = (parseInt(stringHex.substring(21,22),16)) & 0x01; + var data_datalog_temperature_on_off = (parseInt(stringHex.substring(22,23),16)>>3) & 0x01; + var data_datalog_new_measure = (parseInt(stringHex.substring(22,24),16)>>1) & 0x3F; + var data_datalog_repetition = (parseInt(stringHex.substring(23,25),16)) & 0x1F; + var data_datalog_tx_period = (parseInt(stringHex.substring(25,27),16)) & 0xFF; + var data_pending_join = (parseInt(stringHex.substring(26,27),16)) & 0x01; + var data_nfc_status = (parseInt(stringHex.substring(27,28),16)>>2) & 0x03; + var data_date_produit_annee = (parseInt(stringHex.substring(27,29),16)>>2) & 0x3F; + var data_date_produit_mois = (parseInt(stringHex.substring(29,31),16)) & 0x0F; + var data_date_produit_jour = (parseInt(stringHex.substring(30,32),16)>>2) & 0x1F; + var data_date_produit_heure = (parseInt(stringHex.substring(31,33),16)>>1) & 0x1F; + var data_date_produit_minute = (parseInt(stringHex.substring(32,35),16)>>3) & 0x3F; + + + data = { "typeOfProduct": typeOfProduct(octetTypeProduit), + "typeOfMessage": typeOfMessage(octetTypeMessage), + "configurationSource": reconfigurationSource(data_config_source), + "configurationStatus": reconfigurationState(data_config_status), + "periodBetween2measures": period(data_periode_mesure), + "temperatureSensorActivation":sensorActivation(data_sensor_on_off_temperature), + "co2SensorActivation":sensorActivation(data_sensor_on_off_co2), + "covSensorActivation":sensorActivation(data_sensor_on_off_cov), + "pirSensorActivation":sensorActivation(data_sensor_on_off_pir), + "microphoneSensorActivation":sensorActivation(data_sensor_on_off_microphone), + "luminositySensorActivation":sensorActivation(data_sensor_on_off_luminosity), + "localStorageActivation":sdActivation(data_sd_storage_on_off), + "automaticCalibrationActivation": calibrationActivation(data_co2_auto_calibration), + "mediumCO2Threshold":co2Threshold(data_co2_medium_level), + "highCO2Threshold":co2Threshold(data_co2_high_level), + "ledActivation":active(data_led_on_off), + "orangeLEDActivation":active(data_led_orange_on_off), + "buzzerActivation":active(data_buzzer_on_off), + "buzzerConfirmationActivation":active(data_buzzer_confirm_on_off), + "ledSourceActivation":notificationByLEDandBuzzer(data_led_source), + "buttonNotification":active(data_button_on_off), + "loraRegion":loraRegion(data_lora_region), + "periodicalDataActivation":active(data_periodic_tx_on_off), + "dataTransmissionPeriod":period(data_periodic_tx_period), + "deltaCO2":deltaCO2(data_periodic_delta_co2), + "deltaTemperature": deltaTemp(data_periodic_delta_temp), + "historicalCO2DataActivation":active(data_datalog_co2_on_off), + "historicalTemperatureDataActivation":active(data_datalog_temperature_on_off), + "numberOfRecordsInADatalogMessage":data_datalog_new_measure, + "numberOfMessagesFor1Record":data_datalog_repetition, + "transmissionPeriodOfHistoricalMessage":transmissionPeriodHistorical(data_datalog_tx_period), + "networkJoinStatus": pendingJoin(data_pending_join), + "nfcStatus": nfcStatus(data_nfc_status), + "productDate:Year": {"value":data_date_produit_annee,"unit":"year"}, + "productDate:Month": {"value":data_date_produit_mois,"unit":"month"}, + "productDate:Day": {"value":data_date_produit_jour,"unit":"day"}, + "productDate:Hours": {"value":data_date_produit_heure,"unit":"hours"}, + "productDate:Minutes": {"value":data_date_produit_minute,"unit":"minutes"} + + }; + + return data; + } + data=dataOutput(octetTypeMessage); +return {data}; + +} // end of decoder + + diff --git a/vendor/nexelec/sign-codec.yaml b/vendor/nexelec/sign-codec.yaml new file mode 100644 index 0000000000..3535c0ce28 --- /dev/null +++ b/vendor/nexelec/sign-codec.yaml @@ -0,0 +1,10 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sign-codec.js + +downlinkEncoder: + fileName: sign-codec.js + +downlinkDecoder: + fileName: sign-codec.js diff --git a/vendor/nexelec/sign-profile.yaml b/vendor/nexelec/sign-profile.yaml new file mode 100644 index 0000000000..edcc14b18a --- /dev/null +++ b/vendor/nexelec/sign-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +vendorProfileID: 0686 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: 1.0.2 +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: RP001-1.0.2-RevB + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 14 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/nexelec/sign.png b/vendor/nexelec/sign.png new file mode 100644 index 0000000000..de62f72766 Binary files /dev/null and b/vendor/nexelec/sign.png differ diff --git a/vendor/nexelec/sign.yaml b/vendor/nexelec/sign.yaml new file mode 100644 index 0000000000..8b13162550 --- /dev/null +++ b/vendor/nexelec/sign.yaml @@ -0,0 +1,136 @@ +name: SIGN +description: SIGN is a 7-in-1 LoRaWAN-connected, battery- or USB-powered room sensor that can be rapidly configurable via NFC. Depending on the model, the sensor can measure temperature, humidity, CO2, VOCs, brightness, presence and noise. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: 'V001' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: 'V01.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - 'V001' + + # Firmware features (optional) + # Valid values are: remote rejoin (trigger a join from the application layer), transmission interval (configure how + # often he device sends a message). + features: + - remote rejoin + - transmission interval + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # If vendorID is empty, the current vendor ID is used. In this example, the vendorID is the current vendor ID, + # which is verbose. + #vendorID: example + # Identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: sign-profile + lorawanCertified: false + codec: sign-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - co2 + - temperature + - humidity + - tvoc + - motion + - light + - sound + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +additionalRadios: + - nfc + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 87 + length: 87 + height: 23 + +# Weight in grams (optional) +weight: 110 + +# Battery information (optional) +battery: + replaceable: true + type: AA + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -30 + max: 70 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP30 + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: read protected + +# Product and data sheet URLs (optional) +productURL: https://www.nexelec.fr/ +dataSheetURL: https://support.nexelec.fr/en/support/solutions/articles/80001085086-datasheet/ + +# Commercial information +resellerURLs: + - name: 'Nexelec' + region: + - European Union + url: https://www.nexelec.fr/ + +msrp: + EUR: 209 + USD: 229 + +# Photos +photos: + main: sign.png + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + - body: ETSI + norm: EN + standard: 62479:2010 + + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 2.2.0 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 2.1.0 + - body: ETSI + norm: EN + standard: 300 220-2 + version: 3.2.1 diff --git a/vendor/nke-watteco/modbus-sensor.js b/vendor/nke-watteco/modbus-sensor.js index 04a8748b9d..1c84b9233a 100644 --- a/vendor/nke-watteco/modbus-sensor.js +++ b/vendor/nke-watteco/modbus-sensor.js @@ -976,7 +976,7 @@ function Decoder(bytes, port) { // initMultiModbusValue() stdData.value.multimodbus_payload = "" - bytes.slice(index+1).forEach((value, i) => bytes[i] = value.toString( 16 ).toUpperCase().length === 1 ? "0" + value.toString( 16 ).toUpperCase() : value.toString( 16 ).toUpperCase()) + bytes.slice(index+4).forEach((value, i) => bytes[i] = value.toString( 16 ).toUpperCase().length === 1 ? "0" + value.toString( 16 ).toUpperCase() : value.toString( 16 ).toUpperCase()) stdData.value.multimodbus_payload = bytes.join('') } tab.push(stdData); @@ -1098,4 +1098,4 @@ function decodeUplink(input) { errors: [] }; return decodedInput.data.data -} \ No newline at end of file +} diff --git a/vendor/nwave/ncc405-codec.yaml b/vendor/nwave/ncc405-codec.yaml index 5587b1149f..87ef9f9f1d 100644 --- a/vendor/nwave/ncc405-codec.yaml +++ b/vendor/nwave/ncc405-codec.yaml @@ -17,7 +17,7 @@ uplinkDecoder: output: data: type: heartbeat - hw_health_status: 21 + hw_health_status: 40 battery_voltage: 2.908 battery_voltage_mean_24h: 2.888 diff --git a/vendor/nwave/ncc405.js b/vendor/nwave/ncc405.js index 2056e6b41b..a43d90f83f 100644 --- a/vendor/nwave/ncc405.js +++ b/vendor/nwave/ncc405.js @@ -8,7 +8,7 @@ function decodeUplink(input) { case 2: // Heartbeat data.type = "heartbeat"; - data.hw_health_status = input.bytes[0] >> 3; + data.hw_health_status = input.bytes[0] & 0x7F; var batteryVoltageMv = 2500 + input.bytes[1] * 4; data.battery_voltage = batteryVoltageMv / 1000; diff --git a/vendor/nwave/ncc405fm.yaml b/vendor/nwave/ncc405fm.yaml index 8b8bdd9668..ec0f42463a 100644 --- a/vendor/nwave/ncc405fm.yaml +++ b/vendor/nwave/ncc405fm.yaml @@ -29,6 +29,8 @@ sensors: - proximity dimensions: diameter: 156 + width: 156 + length: 156 height: 20 weight: 405 battery: diff --git a/vendor/nwave/nps310sm.yaml b/vendor/nwave/nps310sm.yaml index 9c8ce72ab0..645460466d 100644 --- a/vendor/nwave/nps310sm.yaml +++ b/vendor/nwave/nps310sm.yaml @@ -29,6 +29,8 @@ sensors: - proximity dimensions: diameter: 190 + width: 190 + length: 190 height: 20 weight: 460 battery: diff --git a/vendor/nwave/nps405-codec.yaml b/vendor/nwave/nps405-codec.yaml index a7eb52d889..da9787eefc 100644 --- a/vendor/nwave/nps405-codec.yaml +++ b/vendor/nwave/nps405-codec.yaml @@ -4,7 +4,7 @@ uplinkDecoder: - description: Parking Status input: fPort: 1 - bytes: [0xCA] + bytes: [0x95] output: data: type: parking_status @@ -16,7 +16,7 @@ uplinkDecoder: - description: User Registration input: fPort: 10 - bytes: [0xE8, 0x1F, 0x34, 0x56, 0x78] + bytes: [0xD1, 0x1F, 0x34, 0x56, 0x78] output: data: type: user_registration diff --git a/vendor/nwave/nps405.js b/vendor/nwave/nps405.js index 08c976b0c8..72ff35a2f1 100644 --- a/vendor/nwave/nps405.js +++ b/vendor/nwave/nps405.js @@ -28,8 +28,8 @@ function decodeUplink(input) { switch (input.fPort) { case 1: // Parking status data.type = "parking_status"; - data.occupied = (bytes[0] >> 7 & 0x1) === 0x1; - previousState = calculatePreviousState(bytes[0] & 0x7F); + data.occupied = (bytes[0] & 0x1) === 0x1; + previousState = calculatePreviousState((bytes[0] >> 1) & 0x7F); data.previous_state_duration = previousState.previous_state_duration; data.previous_state_duration_error = previousState.previous_state_duration_error; data.previous_state_duration_overflow = previousState.previous_state_duration_overflow; @@ -44,9 +44,11 @@ function decodeUplink(input) { } data.type = "heartbeat"; - data.occupied = (bytes[0] >> 7 & 0x1) === 0x1; + data.occupied = (bytes[0] & 0x1) === 0x1; data.hw_health_status = bytes[0] & 0x7F; - data.temperature = (bytes[2] << 24 >> 24) / 2 + 10; + + var temp_byte = (bytes[2] > 127) ? bytes[2] - 256 : bytes[2] + data.temperature = temp_byte / 2 + 10; var batteryVoltageMv = 2500 + bytes[1] * 4; data.battery_voltage = batteryVoltageMv / 1000; @@ -96,9 +98,9 @@ function decodeUplink(input) { case 10: // SDI tag registration data.type = "user_registration"; - if ((bytes[0] >> 7 & 0x1) === 0x1) { + if ((bytes[0] & 0x1) === 0x1) { data.occupied = true; - previousState = calculatePreviousState(bytes[0] & 0x7F); + previousState = calculatePreviousState((bytes[0] >> 1) & 0x7F); data.previous_state_duration = previousState.previous_state_duration; data.previous_state_duration_error = previousState.previous_state_duration_error; data.previous_state_duration_overflow = previousState.previous_state_duration_overflow; diff --git a/vendor/nwave/nps405sm.yaml b/vendor/nwave/nps405sm.yaml index 0b5decbd47..ecf7d58e3b 100644 --- a/vendor/nwave/nps405sm.yaml +++ b/vendor/nwave/nps405sm.yaml @@ -29,6 +29,8 @@ sensors: - proximity dimensions: diameter: 205 + width: 205 + length: 205 height: 20 weight: 412 battery: diff --git a/vendor/open-boat-projects/index.yaml b/vendor/open-boat-projects/index.yaml new file mode 100644 index 0000000000..595e94a196 --- /dev/null +++ b/vendor/open-boat-projects/index.yaml @@ -0,0 +1,5 @@ +# This example contains just one end device: windsensor. It is referenced here in the index. + +endDevices: + # Unique identifier of the end device (lowercase, alphanumeric with dashes, max 36 characters) + - loraboatmonitor # look in loraboatmonitor.yaml for the end device definition diff --git a/vendor/open-boat-projects/lora_boat_monitor_1.jpg b/vendor/open-boat-projects/lora_boat_monitor_1.jpg new file mode 100644 index 0000000000..b89229e0ed Binary files /dev/null and b/vendor/open-boat-projects/lora_boat_monitor_1.jpg differ diff --git a/vendor/open-boat-projects/lora_boat_monitor_2.jpg b/vendor/open-boat-projects/lora_boat_monitor_2.jpg new file mode 100644 index 0000000000..0dfacef8e5 Binary files /dev/null and b/vendor/open-boat-projects/lora_boat_monitor_2.jpg differ diff --git a/vendor/open-boat-projects/loraboatmonitor-codec.yaml b/vendor/open-boat-projects/loraboatmonitor-codec.yaml new file mode 100644 index 0000000000..70d8da0466 --- /dev/null +++ b/vendor/open-boat-projects/loraboatmonitor-codec.yaml @@ -0,0 +1,4 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: loraboatmonitor.js diff --git a/vendor/open-boat-projects/loraboatmonitor-profile.yaml b/vendor/open-boat-projects/loraboatmonitor-profile.yaml new file mode 100644 index 0000000000..d9bf8b53ec --- /dev/null +++ b/vendor/open-boat-projects/loraboatmonitor-profile.yaml @@ -0,0 +1,52 @@ +# Vendor profile ID, can be freely issued by the vendor +# This vendor profile ID is also used on the QR code for LoRaWAN devices, see +# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf +#vendorProfileID: 0 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: '1.0' +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: TS001-1.0 + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: false +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +rx1Delay: 5 +# RX1 data rate offset +rx1DataRateOffset: 0 +# RX2 data rate index +rx2DataRateIndex: 0 +# RX2 frequency (MHz) +rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 14 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/open-boat-projects/loraboatmonitor.js b/vendor/open-boat-projects/loraboatmonitor.js new file mode 100644 index 0000000000..96bea68d23 --- /dev/null +++ b/vendor/open-boat-projects/loraboatmonitor.js @@ -0,0 +1,83 @@ +/* +Payload smaple: 0A00541FE027770B9517D6043F1AA702751AFF133D0F0000000001 + +Decode result: + +{ + "alarm1": 1, + "altitude": 1, + "counter": 10, + "device": 123456789, + "dewpoint": 10.4, + "hdop": 1.1, + "humidity": 29.35, + "latitude": 51.193901, + "level1": 0, + "level2": 0, + "longitude": 6.796773, + "position": { + "context": { + "lat": 51.193901, + "lng": 6.796773 + }, + "value": 0 + }, + "pressure": 1020.8, + "relay": 0, + "tempbattery": 17.2, + "temperature": 20.1, + "voltage": 12.38 +} + +*/ + +function decodeUplink(input) { + var data = {}; + var events = { + 1: "setup", + 2: "interval", + 3: "motion", + 4: "button" + }; +// data.event = events[input.fPort]; + + var voffset = 0; // Voltage offset + var toffset = 0; // Temperature offset for BME280 + var poffset = 0; // pressure offset for altitude + + data.device = 123456789; + data.counter = ((input.bytes[1] << 8) | input.bytes[0]); + var temperature = (((input.bytes[3] << 8) | input.bytes[2]) / 100) - 50 + toffset; + data.temperature = Math.round(temperature * 10) / 10; + data.pressure = ((input.bytes[5] << 8) | input.bytes[4]) / 10 + poffset; + data.humidity = ((input.bytes[7] << 8) | input.bytes[6]) / 100; + var dewpoint = (((input.bytes[9] << 8) | input.bytes[8]) / 100) - 50; + data.dewpoint = Math.round(dewpoint * 10) / 10; + var voltage = ((input.bytes[11] << 8) | input.bytes[10]) / 1000 + voffset; + data.voltage = Math.round(voltage * 1000) / 1000; + var tempbattery = (((input.bytes[13] << 8) | input.bytes[12]) / 100) - 50; + data.tempbattery = Math.round(tempbattery * 10) / 10; + var longitude = ((input.bytes[15] << 8) | input.bytes[14]) / 100 + ((input.bytes[17] << 8) | input.bytes[16]) / 1000000; + data.longitude = longitude; + var latitude = ((input.bytes[19] << 8) | input.bytes[18]) / 100 + ((input.bytes[21] << 8) | input.bytes[20]) / 1000000; + data.latitude = latitude; + data.altitude = 1; + data.hdop = 1.1; + data.position = {"value": 0, context:{"lat": latitude, "lng": longitude}}; + data.level1 = ((input.bytes[23] << 8) | input.bytes[22]) / 100; + data.level2 = ((input.bytes[25] << 8) | input.bytes[24]) / 100; + data.alarm1 = input.bytes[26] & 0x01; + data.relay = (input.bytes[26] & 0x10) / 0x10; + + var warnings = []; + if (data.voltage < 10) { + warnings.push("Battery undervoltage"); + } + if (data.voltage > 14.7) { + warnings.push("Battery overload"); + } + return { + data: data, + warnings: warnings + }; +} diff --git a/vendor/open-boat-projects/loraboatmonitor.yaml b/vendor/open-boat-projects/loraboatmonitor.yaml new file mode 100644 index 0000000000..a11b230d84 --- /dev/null +++ b/vendor/open-boat-projects/loraboatmonitor.yaml @@ -0,0 +1,79 @@ +name: LoRa Boat Monitor +description: The LoRa boat monitor is used to monitor the boat when you are away. Various measured values such as battery voltage, GPS position, humidity, temperature, air pressure, filling levels, bilge and door contact are continuously forwarded to the LoRaWAN and recorded at freely adjustable time intervals via the LoRa radio technology. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.06' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + # Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: loraboatmonitor-profile + codec: loraboatmonitor-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, snr, solar radiation, +# sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, vibration, voltage, +# water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - digital input + - gps + - temperature + - pressure + - humidity + - level + - voltage + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 90 + length: 115 + height: 55 + +# Weight in grams (optional) +weight: 275 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 60 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP65 + +# Product and data sheet URLs (optional) +productURL: https://open-boat-projects.org/en/lora-bootsmonitor +resellerURLs: + - name: 'Open Boat Projects' + region: + - European Union + url: https://open-boat-projects.org/en/kontakt + +# Photos +photos: + main: lora_boat_monitor_1.jpg + other: + - lora_boat_monitor_2.jpg diff --git a/vendor/open-boat-projects/obp_logo.png b/vendor/open-boat-projects/obp_logo.png new file mode 100644 index 0000000000..cc78f8d163 Binary files /dev/null and b/vendor/open-boat-projects/obp_logo.png differ diff --git a/vendor/radio-bridge/RBS306-US10M.jpg b/vendor/radio-bridge/RBS306-US10M.jpg index 08e6a230a4..a91739eb3a 100644 Binary files a/vendor/radio-bridge/RBS306-US10M.jpg and b/vendor/radio-bridge/RBS306-US10M.jpg differ diff --git a/vendor/radio-bridge/rbs101-con.yaml b/vendor/radio-bridge/rbs101-con.yaml index 956f6225b8..afaa18096d 100644 --- a/vendor/radio-bridge/rbs101-con.yaml +++ b/vendor/radio-bridge/rbs101-con.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs101-con-profile lorawanCertified: true codec: rbs101-con-codec +# Photos +photos: + main: rbs101-con.png diff --git a/vendor/radio-bridge/rbs301-abm.yaml b/vendor/radio-bridge/rbs301-abm.yaml index 70c4a87dd0..06b86f1427 100644 --- a/vendor/radio-bridge/rbs301-abm.yaml +++ b/vendor/radio-bridge/rbs301-abm.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-abm-profile lorawanCertified: true codec: rbs301-abm-codec +# Photos +photos: + main: rbs301-abm.png diff --git a/vendor/radio-bridge/rbs301-dws.yaml b/vendor/radio-bridge/rbs301-dws.yaml index cf67fd9b7b..354bd3cb7f 100644 --- a/vendor/radio-bridge/rbs301-dws.yaml +++ b/vendor/radio-bridge/rbs301-dws.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-dws-profile lorawanCertified: true codec: rbs301-dws-codec +# Photos +photos: + main: rbs301-dws.png diff --git a/vendor/radio-bridge/rbs301-temp-ext.yaml b/vendor/radio-bridge/rbs301-temp-ext.yaml index e899793a0e..4c0f3dd01e 100644 --- a/vendor/radio-bridge/rbs301-temp-ext.yaml +++ b/vendor/radio-bridge/rbs301-temp-ext.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-temp-ext-profile lorawanCertified: true codec: rbs301-temp-ext-codec +# Photos +photos: + main: rbs301-temp-ext.png diff --git a/vendor/radio-bridge/rbs301-temp-int.yaml b/vendor/radio-bridge/rbs301-temp-int.yaml index 7e0511c54b..685aeca2c1 100644 --- a/vendor/radio-bridge/rbs301-temp-int.yaml +++ b/vendor/radio-bridge/rbs301-temp-int.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-temp-int-profile lorawanCertified: true codec: rbs301-temp-int-codec +# Photos +photos: + main: rbs301-temp-int.png diff --git a/vendor/radio-bridge/rbs301-tilt.yaml b/vendor/radio-bridge/rbs301-tilt.yaml index 6d6b9028cc..40b029d4e9 100644 --- a/vendor/radio-bridge/rbs301-tilt.yaml +++ b/vendor/radio-bridge/rbs301-tilt.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-tilt-profile lorawanCertified: true codec: rbs301-tilt-codec +# Photos +photos: + main: rbs301-tilt.png diff --git a/vendor/radio-bridge/rbs301-wat.yaml b/vendor/radio-bridge/rbs301-wat.yaml index 0e9a883d44..4726a628b0 100644 --- a/vendor/radio-bridge/rbs301-wat.yaml +++ b/vendor/radio-bridge/rbs301-wat.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-wat-profile lorawanCertified: true codec: rbs301-wat-codec +# Photos +photos: + main: rbs301-wat.png diff --git a/vendor/radio-bridge/rbs301-wr1m.yaml b/vendor/radio-bridge/rbs301-wr1m.yaml index 7b82199b53..103faeca88 100644 --- a/vendor/radio-bridge/rbs301-wr1m.yaml +++ b/vendor/radio-bridge/rbs301-wr1m.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs301-wr1m-profile lorawanCertified: true codec: rbs301-wr1m-codec +# Photos +photos: + main: rbs301-wr1m.png diff --git a/vendor/radio-bridge/rbs305-ath.yaml b/vendor/radio-bridge/rbs305-ath.yaml index 9ef7450b98..2d36c21dc1 100644 --- a/vendor/radio-bridge/rbs305-ath.yaml +++ b/vendor/radio-bridge/rbs305-ath.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs305-ath-profile lorawanCertified: true codec: rbs305-ath-codec +# Photos +photos: + main: rbs305-ath.png diff --git a/vendor/radio-bridge/rbs306-420ma.yaml b/vendor/radio-bridge/rbs306-420ma.yaml index 502fb20060..f7f02c7f24 100644 --- a/vendor/radio-bridge/rbs306-420ma.yaml +++ b/vendor/radio-bridge/rbs306-420ma.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-420ma-profile lorawanCertified: true codec: rbs306-420ma-codec +# Photos +photos: + main: rbs306-420ma.png diff --git a/vendor/radio-bridge/rbs306-abm.yaml b/vendor/radio-bridge/rbs306-abm.yaml index ef7890ab87..8efbe96920 100644 --- a/vendor/radio-bridge/rbs306-abm.yaml +++ b/vendor/radio-bridge/rbs306-abm.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-abm-profile lorawanCertified: true codec: rbs306-abm-codec +# Photos +photos: + main: rbs306-abm.png diff --git a/vendor/radio-bridge/rbs306-ath-ext.yaml b/vendor/radio-bridge/rbs306-ath-ext.yaml index 822eac8b87..321301fd80 100644 --- a/vendor/radio-bridge/rbs306-ath-ext.yaml +++ b/vendor/radio-bridge/rbs306-ath-ext.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-ath-ext-profile lorawanCertified: true codec: rbs306-ath-ext-codec +# Photos +photos: + main: rbs306-ath-ext.png diff --git a/vendor/radio-bridge/rbs306-con.yaml b/vendor/radio-bridge/rbs306-con.yaml index 2170fdf7a2..e88aa03ff9 100644 --- a/vendor/radio-bridge/rbs306-con.yaml +++ b/vendor/radio-bridge/rbs306-con.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-con-profile lorawanCertified: true codec: rbs306-con-codec +# Photos +photos: + main: rbs306-con.png diff --git a/vendor/radio-bridge/rbs306-mbhr.yaml b/vendor/radio-bridge/rbs306-mbhr.yaml index b774ae8dfb..7fb6599b25 100644 --- a/vendor/radio-bridge/rbs306-mbhr.yaml +++ b/vendor/radio-bridge/rbs306-mbhr.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-mbhr-profile lorawanCertified: true codec: rbs306-mbhr-codec +# Photos +photos: + main: rbs306-mbhr.jpg diff --git a/vendor/radio-bridge/rbs306-temp-ext.yaml b/vendor/radio-bridge/rbs306-temp-ext.yaml index d660b1db2b..b382b24d96 100644 --- a/vendor/radio-bridge/rbs306-temp-ext.yaml +++ b/vendor/radio-bridge/rbs306-temp-ext.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-temp-ext-profile lorawanCertified: true codec: rbs306-temp-ext-codec +# Photos +photos: + main: rbs306-temp-ext.png diff --git a/vendor/radio-bridge/rbs306-temp-tc.yaml b/vendor/radio-bridge/rbs306-temp-tc.yaml index df08ff78bb..501c540279 100644 --- a/vendor/radio-bridge/rbs306-temp-tc.yaml +++ b/vendor/radio-bridge/rbs306-temp-tc.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-temp-tc-profile lorawanCertified: true codec: rbs306-temp-tc-codec +# Photos +photos: + main: rbs306-temp-tc.png diff --git a/vendor/radio-bridge/rbs306-tilt-hp.yaml b/vendor/radio-bridge/rbs306-tilt-hp.yaml index 09a7d9a0f9..702d09d502 100644 --- a/vendor/radio-bridge/rbs306-tilt-hp.yaml +++ b/vendor/radio-bridge/rbs306-tilt-hp.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-tilt-hp-profile lorawanCertified: true codec: rbs306-tilt-hp-codec +# Photos +photos: + main: rbs306-tilt-hp.png diff --git a/vendor/radio-bridge/rbs306-us10m.jpg b/vendor/radio-bridge/rbs306-us10m.jpg index 08e6a230a4..a91739eb3a 100644 Binary files a/vendor/radio-bridge/rbs306-us10m.jpg and b/vendor/radio-bridge/rbs306-us10m.jpg differ diff --git a/vendor/radio-bridge/rbs306-us10m.yaml b/vendor/radio-bridge/rbs306-us10m.yaml index 27259d2abb..ca886e4294 100644 --- a/vendor/radio-bridge/rbs306-us10m.yaml +++ b/vendor/radio-bridge/rbs306-us10m.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-us10m-profile lorawanCertified: true codec: rbs306-us10m-codec +# Photos +photos: + main: rbs306-us10m.jpg diff --git a/vendor/radio-bridge/rbs306-vm30.yaml b/vendor/radio-bridge/rbs306-vm30.yaml index 67db9995ce..58118ab80e 100644 --- a/vendor/radio-bridge/rbs306-vm30.yaml +++ b/vendor/radio-bridge/rbs306-vm30.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-vm30-profile lorawanCertified: true codec: rbs306-vm30-codec +# Photos +photos: + main: rbs306-vm30.jpg diff --git a/vendor/radio-bridge/rbs306-vshb.yaml b/vendor/radio-bridge/rbs306-vshb.yaml index 7af783506e..46cbd18d98 100644 --- a/vendor/radio-bridge/rbs306-vshb.yaml +++ b/vendor/radio-bridge/rbs306-vshb.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-vshb-profile lorawanCertified: true codec: rbs306-vshb-codec +# Photos +photos: + main: rbs306-vshb.jpg diff --git a/vendor/radio-bridge/rbs306-wr1m.yaml b/vendor/radio-bridge/rbs306-wr1m.yaml index 2df273cfdb..f74f81d5ce 100644 --- a/vendor/radio-bridge/rbs306-wr1m.yaml +++ b/vendor/radio-bridge/rbs306-wr1m.yaml @@ -19,3 +19,6 @@ firmwareVersions: id: rbs306-wr1m-profile lorawanCertified: true codec: rbs306-wr1m-codec +# Photos +photos: + main: rbs306-wr1m.png diff --git a/vendor/sensecap/index.yaml b/vendor/sensecap/index.yaml index f1cb66179d..cae981c0f7 100644 --- a/vendor/sensecap/index.yaml +++ b/vendor/sensecap/index.yaml @@ -15,3 +15,4 @@ endDevices: - sensecaps2105-soll-moisture-temp-ec - sensecaps2100-data-logger - sensecaps2120-8-in-1 + - sensecapt1000-tracker-ab diff --git a/vendor/sensecap/sensecapt1000-tracker-ab-codec.yaml b/vendor/sensecap/sensecapt1000-tracker-ab-codec.yaml new file mode 100644 index 0000000000..7fa5b9156f --- /dev/null +++ b/vendor/sensecap/sensecapt1000-tracker-ab-codec.yaml @@ -0,0 +1,5 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: sensecapt1000-tracker-ab-decoder.js + # Examples (optional) diff --git a/vendor/sensecap/sensecapt1000-tracker-ab-decoder.js b/vendor/sensecap/sensecapt1000-tracker-ab-decoder.js new file mode 100644 index 0000000000..e31df3a1cf --- /dev/null +++ b/vendor/sensecap/sensecapt1000-tracker-ab-decoder.js @@ -0,0 +1,511 @@ +function decodeUplink (input) { + var bytes = input['bytes'] + var bytesString = bytes2HexString(bytes).toLocaleUpperCase() + var decoded = { + valid: true, + err: 0, + payload: bytesString, + messages: [] + } + let measurement = messageAnalyzed(bytesString) + decoded.messages = measurement + return { data: decoded } +} + +function messageAnalyzed (messageValue) { + try { + let frames = unpack(messageValue) + let measurementResultArray = [] + for (let i = 0; i < frames.length; i++) { + let item = frames[i] + let dataId = item.dataId + let dataValue = item.dataValue + let measurementArray = deserialize(dataId, dataValue) + measurementResultArray.push(measurementArray) + } + return measurementResultArray + } catch (e) { + return e.toString() + } +} + +function unpack (messageValue) { + let frameArray = [] + + for (let i = 0; i < messageValue.length; i++) { + let remainMessage = messageValue + let dataId = remainMessage.substring(0, 2).toUpperCase() + let dataValue + let dataObj = {} + let packageLen + switch (dataId) { + case '01': + packageLen = 94 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '02': + packageLen = 32 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '03': + packageLen = 64 + if (remainMessage.length < packageLen) { + return frameArray + } + break + case '04': + packageLen = 20 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '05': + packageLen = 10 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '06': + packageLen = 44 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '07': + packageLen = 84 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '08': + packageLen = 70 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '09': + packageLen = 36 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '0A': + packageLen = 76 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '0B': + packageLen = 62 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '0C': + packageLen = 2 + if (remainMessage.length < packageLen) { + return frameArray + } + break + case '0D': + packageLen = 10 + if (remainMessage.length < packageLen) { + return frameArray + } + dataValue = remainMessage.substring(2, packageLen) + messageValue = remainMessage.substring(packageLen) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + default: + return frameArray + } + if (dataValue.length < 2) { + break + } + frameArray.push(dataObj) + } + return frameArray +} + +function deserialize (dataId, dataValue) { + let measurementArray = [] + let eventList = [] + let collectTime = 0 + switch (dataId) { + case '01': + measurementArray = getUpShortInfo(dataValue) + break + case '02': + measurementArray = getUpShortInfo(dataValue) + break + case '03': + break + case '04': + measurementArray = [ + {measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(dataValue.substring(0, 2))}, + {measurementId: '3942', type: 'Heartbeat Interval', measurementValue: getOneWeekInterval(dataValue.substring(4, 8))}, + {measurementId: '3943', type: 'Periodic Interval', measurementValue: getOneWeekInterval(dataValue.substring(8, 12))}, + {measurementId: '3944', type: 'Event Interval', measurementValue: getOneWeekInterval(dataValue.substring(12, 16))}, + {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(16, 18))} + ] + break; + case '05': + measurementArray = [ + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(0, 2))}, + {measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(dataValue.substring(2, 4))}, + {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(6, 8))} + ] + break + case '06': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '4197', type: 'Longitude', measurementValue: getSensorValue(dataValue.substring(16, 24), 1000000)}, + {measurementId: '4198', type: 'Latitude', measurementValue: getSensorValue(dataValue.substring(24, 32), 1000000)}, + {measurementId: '4097', type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, + {measurementId: '4199', type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(40, 42))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '07': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '5001', type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, + {measurementId: '4097', type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, + {measurementId: '4199', type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(80, 82))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '08': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '5002', type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, + {measurementId: '4097', type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, + {measurementId: '4199', type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(66, 68))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '09': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '4197', type: 'Longitude', measurementValue: getSensorValue(dataValue.substring(16, 24), 1000000)}, + {measurementId: '4198', type: 'Latitude', measurementValue: getSensorValue(dataValue.substring(24, 32), 1000000)}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(32, 34))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '0A': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '5001', type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(72, 74))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '0B': + eventList = this.getEventStatus(dataValue.substring(0, 6)) + collectTime = this.getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray = [ + {measurementId: '4200', type: 'SOS Event', measurementValue: eventList[6]}, + {measurementId: '5002', type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, + {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(58, 60))}, + {type: 'Timestamp', measurementValue: collectTime} + ] + break + case '0D': + let errorCode = this.getInt(dataValue) + let error = '' + switch (errorCode) { + case 0: + error = 'THE GNSS SCAN TIME OUT' + break + case 1: + error = 'THE WI-FI SCAN TIME OUT' + break + case 2: + error = 'THE WI-FI+GNSS SCAN TIME OUT' + break + case 3: + error = 'THE GNSS+WI-FI SCAN TIME OUT' + break + case 4: + error = 'THE BEACON SCAN TIME OUT' + break + case 5: + error = 'THE BEACON+WI-FI SCAN TIME OUT' + break + case 6: + error = 'THE BEACON+GNSS SCAN TIME OUT' + break + case 7: + error = 'THE BEACON+WI-FI+GNSS SCAN TIME OUT' + break + case 8: + error = 'FAILED TO OBTAIN THE UTC TIMESTAMP' + break + } + measurementArray.push({errorCode, error}) + } + return measurementArray +} + +function getUpShortInfo (messageValue) { + return [ + { + measurementId: '3000', type: 'Battery', measurementValue: getBattery(messageValue.substring(0, 2)) + }, { + measurementId: '3502', type: 'Firmware Version', measurementValue: getSoftVersion(messageValue.substring(2, 6)) + }, { + measurementId: '3001', type: 'Hardware Version', measurementValue: getHardVersion(messageValue.substring(6, 10)) + }, { + measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(messageValue.substring(10, 12)) + }, { + measurementId: '3942', type: 'Heartbeat Interval', measurementValue: getOneWeekInterval(messageValue.substring(14, 18)) + }, { + measurementId: '3943', type: 'Periodic Interval', measurementValue: getOneWeekInterval(messageValue.substring(18, 22)) + }, { + measurementId: '3944', type: 'Event Interval', measurementValue: getOneWeekInterval(messageValue.substring(22, 26)) + }, { + measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(messageValue.substring(28, 30)) + } + ] +} +function getBattery (batteryStr) { + return loraWANV2DataFormat(batteryStr) +} +function getSoftVersion (softVersion) { + return `${loraWANV2DataFormat(softVersion.substring(0, 2))}.${loraWANV2DataFormat(softVersion.substring(2, 4))}` +} +function getHardVersion (hardVersion) { + return `${loraWANV2DataFormat(hardVersion.substring(0, 2))}.${loraWANV2DataFormat(hardVersion.substring(2, 4))}` +} + +function getOneWeekInterval (str) { + return loraWANV2DataFormat(str) * 60 +} +function getSensorValue (str, dig) { + if (str === '8000') { + return null + } else { + return loraWANV2DataFormat(str, dig) + } +} + +function bytes2HexString (arrBytes) { + var str = '' + for (var i = 0; i < arrBytes.length; i++) { + var tmp + var num = arrBytes[i] + if (num < 0) { + tmp = (255 + num + 1).toString(16) + } else { + tmp = num.toString(16) + } + if (tmp.length === 1) { + tmp = '0' + tmp + } + str += tmp + } + return str +} +function loraWANV2DataFormat (str, divisor = 1) { + let strReverse = bigEndianTransform(str) + let str2 = toBinary(strReverse) + if (str2.substring(0, 1) === '1') { + let arr = str2.split('') + let reverseArr = arr.map((item) => { + if (parseInt(item) === 1) { + return 0 + } else { + return 1 + } + }) + str2 = parseInt(reverseArr.join(''), 2) + 1 + return '-' + str2 / divisor + } + return parseInt(str2, 2) / divisor +} + +function bigEndianTransform (data) { + let dataArray = [] + for (let i = 0; i < data.length; i += 2) { + dataArray.push(data.substring(i, i + 2)) + } + return dataArray +} + +function toBinary (arr) { + let binaryData = arr.map((item) => { + let data = parseInt(item, 16) + .toString(2) + let dataLength = data.length + if (data.length !== 8) { + for (let i = 0; i < 8 - dataLength; i++) { + data = `0` + data + } + } + return data + }) + return binaryData.toString().replace(/,/g, '') +} + +function getSOSMode (str) { + return loraWANV2DataFormat(str) +} + +function getMacAndRssiObj (pair) { + let pairs = [] + if (pair.length % 14 === 0) { + for (let i = 0; i < pair.length; i += 14) { + let mac = getMacAddress(pair.substring(i, i + 12)) + if (mac) { + let rssi = getInt8RSSI(pair.substring(i + 12, i + 14)) + pairs.push({mac: mac, rssi: rssi}) + } else { + continue + } + } + } + return pairs +} + +function getMacAddress (str) { + if (str.toLowerCase() === 'ffffffffffff') { + return null + } + let macArr = [] + for (let i = 1; i < str.length; i++) { + if (i % 2 === 1) { + macArr.push(str.substring(i - 1, i + 1)) + } + } + let mac = '' + for (let i = 0; i < macArr.length; i++) { + mac = mac + macArr[i] + if (i < macArr.length - 1) { + mac = mac + ':' + } + } + return mac +} + +function getInt8RSSI (str) { + return this.loraWANV2DataFormat(str) +} + +function getInt (str) { + return parseInt(str) +} + +/** + * 1.MOVING_STARTING + * 2.MOVING_END + * 3.DEVICE_STATIC + * 4.SHOCK_EVENT + * 5.TEMP_EVENT + * 6.LIGHTING_EVENT + * 7.SOS_EVENT + * 8.CUSTOMER_EVENT + * */ +function getEventStatus (str) { + let bitStr = this.getByteArray(str) + let event = [] + for (let i = bitStr.length; i >= 0; i--) { + if (i === 0) { + event[i] = bitStr.substring(0) + } else { + event[i] = bitStr.substring(i - 1, i) + } + } + return event.reverse() +} + +function getByteArray (str) { + let bytes = [] + for (let i = 0; i < str.length; i += 2) { + bytes.push(str.substring(i, i + 2)) + } + return toBinary(bytes) +} + +function getWorkingMode (workingMode) { + return getInt(workingMode) +} + +function getUTCTimestamp(str){ + return parseInt(this.loraWANV2PositiveDataFormat(str)) * 1000 +} + +function loraWANV2PositiveDataFormat (str, divisor = 1) { + let strReverse = this.bigEndianTransform(str) + let str2 = this.toBinary(strReverse) + return parseInt(str2, 2) / divisor +} \ No newline at end of file diff --git a/vendor/sensecap/sensecapt1000-tracker-ab.yaml b/vendor/sensecap/sensecapt1000-tracker-ab.yaml new file mode 100644 index 0000000000..ae72e3577b --- /dev/null +++ b/vendor/sensecap/sensecapt1000-tracker-ab.yaml @@ -0,0 +1,136 @@ +name: SenseCAP T1000 Tracker A/B +description: SenseCAP T1000 A/B is a credit card-sized tracker, which is designed for asset and personal tracking. By utilizing GNSS/Wi-Fi/Bluetooth, it covers both outdoor and indoor positioning scenarios with high accuracy. + +# Hardware versions (optional, use when you have revisions) +hardwareVersions: + - version: '1.0' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Corresponding hardware versions (optional) + hardwareVersions: + - '1.0' + + # LoRaWAN Device Profiles per region + # Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, + # RU864-870 + profiles: + EU863-870: + # Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters) + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + US902-928: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + AU915-928: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + KR920-923: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + IN865-867: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + AS923: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + RU864-870: + id: sensecap-profile + lorawanCertified: true + codec: sensecapt1000-tracker-ab-codec + +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - temperature + - light + #- longitude + #- latitude + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 6.5 + length: 55 + height: 85 + +# Weight in grams (optional) +weight: 32 + +# Battery information (optional) +battery: + replaceable: true + type: Rechargeable lithium battery, 700mAh + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: -20 + max: 60 + # Relative humidity (fraction of 1) + relativeHumidity: + min: 0 + max: 1 + +# IP rating (optional) +ipCode: IP65 + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +keyProvisioning: + - custom + - join server + +# Key security (optional) +# Valid values are: none, read protected and secure element. +keySecurity: none + +# Product and data sheet URLs (optional) +productURL: https://files.seeedstudio.com/products/SenseCAP/SenseCAP_Tracker/SenseCAP_Tracker_T1000-AB_User_Guide.pdf +dataSheetURL: https://files.seeedstudio.com/products/SenseCAP/SenseCAP_Tracker/SenseCAP_Tracker_T1000_Datasheet.pdf + +# Commercial information +resellerURLs: + - name: 'Seeed' + region: + - Other + url: https://www.seeedstudio.com/sensecap-s2120-lorawan-8-in-1-weather-sensor-p-5436.html + +# Photos +photos: + main: sensecapt1000.png + +video: https://youtu.be/jj6XN8IcchI + +# Regulatory compliances (optional) +compliances: + safety: + - body: IEC + norm: EN + standard: 62368-1 + radioEquipment: + - body: ETSI + norm: EN + standard: 301 489-1 + version: 2.2.0 + - body: ETSI + norm: EN + standard: 301 489-3 + version: 2.1.0 diff --git a/vendor/sensecap/sensecapt1000.png b/vendor/sensecap/sensecapt1000.png new file mode 100644 index 0000000000..0a0af2f01a Binary files /dev/null and b/vendor/sensecap/sensecapt1000.png differ diff --git a/vendor/the-things-industries/generic-node-sensor-edition-codec.js b/vendor/the-things-industries/generic-node-sensor-edition-codec.js index 2ab501369a..c5925fe3c6 100644 --- a/vendor/the-things-industries/generic-node-sensor-edition-codec.js +++ b/vendor/the-things-industries/generic-node-sensor-edition-codec.js @@ -3,6 +3,7 @@ function decodeUplink(input) { data.batt_volt = input.bytes[0] / 10; data.temperature = ((input.bytes[1] << 8) + input.bytes[2] - 500) / 10; data.humidity = ((input.bytes[3] << 8) + input.bytes[4]) / 10; + data.button = input.bytes[5]; return { data: data, diff --git a/website/yarn.lock b/website/yarn.lock index de5bdc141a..c46ece09c3 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -3764,10 +3764,12 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^7.4.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.0.tgz#649aaeb294a56297b5cbc5d70f198dcc5ebe5747" - integrity sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -5159,9 +5161,9 @@ selfsigned@^1.10.8: node-forge "^0.10.0" "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@7.0.0: version "7.0.0" @@ -5169,16 +5171,16 @@ semver@7.0.0: integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.4: - version "7.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" - integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: - lru-cache "^7.4.0" + lru-cache "^6.0.0" send@0.17.2: version "0.17.2" @@ -6212,6 +6214,11 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"