diff --git a/package-lock.json b/package-lock.json index a8dffac838..c74d15161b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ }, "devDependencies": { "ajv": "^6.12.6", - "ajv-cli": "^5.0.0", + "ajv-cli": "^0.6.0", "prettier": "^2.6.2" } }, @@ -42,75 +42,29 @@ } }, "node_modules/ajv-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-5.0.0.tgz", - "integrity": "sha512-LY4m6dUv44HTyhV+u2z5uX4EhPYTM38Iv1jdgDJJJCyOOuqB8KtZEGjPZ2T+sh5ZIJrXUfgErYx/j3gLd3+PlQ==", + "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, "dependencies": { - "ajv": "^8.0.0", - "fast-json-patch": "^2.0.0", - "glob": "^7.1.0", - "js-yaml": "^3.14.0", - "json-schema-migrate": "^2.0.0", - "json5": "^2.1.3", + "ajv": "^3.8.0", + "glob": "^7.0.3", "minimist": "^1.2.0" }, "bin": { - "ajv": "dist/index.js" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } + "ajv": "index.js" } }, "node_modules/ajv-cli/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-cli/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/ajv-cli/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.8.10", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-3.8.10.tgz", + "integrity": "sha512-h74deHfbgeB8TuWq6UQxP4fwsCbo9T+pvWofl4pEdZzI6lMSSFAWZr3PsNXHjSFABCj49j6fgbzUTHWIrMdHMw==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, - "node_modules/ajv-cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -164,6 +118,16 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -199,43 +163,12 @@ "node": ">=6" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/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 }, - "node_modules/fast-json-patch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", - "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^2.0.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fast-json-patch/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, "node_modules/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", @@ -343,53 +276,31 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-schema-migrate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", - "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - } - }, - "node_modules/json-schema-migrate/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/json-schema-migrate/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/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 }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/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, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "jsonify": "^0.0.1" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/lodash.isequal": { @@ -509,21 +420,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -640,56 +536,25 @@ } }, "ajv-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-5.0.0.tgz", - "integrity": "sha512-LY4m6dUv44HTyhV+u2z5uX4EhPYTM38Iv1jdgDJJJCyOOuqB8KtZEGjPZ2T+sh5ZIJrXUfgErYx/j3gLd3+PlQ==", + "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": "^8.0.0", - "fast-json-patch": "^2.0.0", - "glob": "^7.1.0", - "js-yaml": "^3.14.0", - "json-schema-migrate": "^2.0.0", - "json5": "^2.1.3", + "ajv": "^3.8.0", + "glob": "^7.0.3", "minimist": "^1.2.0" }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "3.8.10", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-3.8.10.tgz", + "integrity": "sha512-h74deHfbgeB8TuWq6UQxP4fwsCbo9T+pvWofl4pEdZzI6lMSSFAWZr3PsNXHjSFABCj49j6fgbzUTHWIrMdHMw==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true } } }, @@ -737,6 +602,12 @@ "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", @@ -766,35 +637,12 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "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-patch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", - "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1" - }, - "dependencies": { - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "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", @@ -875,45 +723,25 @@ "argparse": "^2.0.1" } }, - "json-schema-migrate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", - "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, "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 }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "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": { @@ -1000,18 +828,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index e68a5ae44c..d3440cfb7c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "homepage": "https://github.com/TheThingsNetwork/lorawan-devices#readme", "devDependencies": { "ajv": "^6.12.6", - "ajv-cli": "^5.0.0", + "ajv-cli": "^0.6.0", "prettier": "^2.6.2" }, "dependencies": { 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/index.yaml b/vendor/index.yaml index 2f3e36edba..c43833dc36 100644 --- a/vendor/index.yaml +++ b/vendor/index.yaml @@ -1702,7 +1702,7 @@ vendors: - id: dingtek name: Dingtek - website: http://dingtek.com/ + website: https://www.dingtek.com/ logo: dingtek_logo.png - id: elster @@ -1966,3 +1966,12 @@ vendors: name: Agriseen website: http://www.agriseen.com/ logo: agriseen_logo.png + + - 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/ + 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 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/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/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"