diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11ddd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +# Keep environment variables out of version control +.env diff --git a/package-lock.json b/package-lock.json index 1cf8ce6..10d83eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,181 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "axios": "^1.6.7", + "@prisma/client": "^5.11.0", "express": "^4.18.3", - "node-cache": "^5.1.2" + "mqtt": "^5.3.6", + "protobufjs": "^7.2.6" + }, + "devDependencies": { + "prisma": "^5.10.2" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@prisma/client": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.11.0.tgz", + "integrity": "sha512-SWshvS5FDXvgJKM/a0y9nDC1rqd7KG0Q6ZVzd+U7ZXK5soe73DJxJJgbNBt2GNXOa+ysWB4suTpdK5zfFPhwiw==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.10.2.tgz", + "integrity": "sha512-bkBOmH9dpEBbMKFJj8V+Zp8IZHIBjy3fSyhLhxj4FmKGb/UBSt9doyfA6k1UeUREsMJft7xgPYBbHSOYBr8XCA==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.10.2.tgz", + "integrity": "sha512-HkSJvix6PW8YqEEt3zHfCYYJY69CXsNdhU+wna+4Y7EZ+AwzeupMnUThmvaDA7uqswiHkgm5/SZ6/4CStjaGmw==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.10.2", + "@prisma/engines-version": "5.10.0-34.5a9203d0590c951969e85a7d07215503f4672eb9", + "@prisma/fetch-engine": "5.10.2", + "@prisma/get-platform": "5.10.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.10.0-34.5a9203d0590c951969e85a7d07215503f4672eb9", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.10.0-34.5a9203d0590c951969e85a7d07215503f4672eb9.tgz", + "integrity": "sha512-uCy/++3Jx/O3ufM+qv2H1L4tOemTNqcP/gyEVOlZqTpBvYJUe0tWtW0y3o2Ueq04mll4aM5X3f6ugQftOSLdFQ==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.10.2.tgz", + "integrity": "sha512-dSmXcqSt6DpTmMaLQ9K8ZKzVAMH3qwGCmYEZr/uVnzVhxRJ1EbT/w2MMwIdBNq1zT69Rvh0h75WMIi0mrIw7Hg==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.10.2", + "@prisma/engines-version": "5.10.0-34.5a9203d0590c951969e85a7d07215503f4672eb9", + "@prisma/get-platform": "5.10.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.10.2.tgz", + "integrity": "sha512-nqXP6vHiY2PIsebBAuDeWiUYg8h8mfjBckHh6Jezuwej0QJNnjDiOq30uesmg+JXxGk99nqyG3B7wpcOODzXvg==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.10.2" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@types/node": { + "version": "20.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.26.tgz", + "integrity": "sha512-YwOMmyhNnAWijOBQweOJnQPl068Oqd4K3OFbTc6AHJwzweUwwWG3GIFY74OKks2PJUDkQPeddOQES9mLn1CTEQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.10.tgz", + "integrity": "sha512-AbUKBjcC8SHmImNi4yK2bbjogQlkFSg7shZCcicxPQapniOlajG8GCc39lvXzCWX4lLRRs7DM3VAeSlqmEVZUA==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" } }, "node_modules/accepts": { @@ -31,19 +203,34 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "node_modules/bl": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.12.tgz", + "integrity": "sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w==", "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" } }, "node_modules/body-parser": { @@ -69,6 +256,34 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -95,23 +310,36 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "delayed-stream": "~1.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 6" } }, "node_modules/content-disposition": { @@ -170,14 +398,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -240,6 +460,22 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/express": { "version": "4.18.3", "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", @@ -281,6 +517,18 @@ "node": ">= 0.10.0" } }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -298,38 +546,6 @@ "node": ">= 0.8" } }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -427,6 +643,11 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -453,6 +674,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -466,6 +706,28 @@ "node": ">= 0.10" } }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -517,6 +779,98 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mqtt": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.3.6.tgz", + "integrity": "sha512-3XeyCdHRFf3zZdUUBt/pqprKPtUABc8O4ZGPGs2QPO4sPNTnJels8U2UtBtMt09QCgpUmw8gLTLy2R7verR7kQ==", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.0.78", + "ws": "^8.14.2" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt-packet/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mqtt/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -530,17 +884,36 @@ "node": ">= 0.6" } }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", "dependencies": { - "clone": "2.x" + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/number-allocator/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" }, "engines": { - "node": ">= 8.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/number-allocator/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -573,6 +946,58 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/prisma": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.10.2.tgz", + "integrity": "sha512-hqb/JMz9/kymRE25pMWCxkdyhbnIWrq+h7S6WysJpdnCvhstbJSNP/S6mScEcqiB8Qv2F+0R3yG+osRaWqZacQ==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.10.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -585,11 +1010,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -626,6 +1046,36 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -730,6 +1180,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -738,6 +1196,14 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -746,6 +1212,11 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -758,6 +1229,16 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -766,6 +1247,11 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -781,6 +1267,57 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/worker-timers": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.4.tgz", + "integrity": "sha512-8PRtiPAyeYukrY+iOUL+0tq4Zn5qyCHrTqFTtHxcESfIxGyulxNwyzQkybrYBKhnMWmx0bku3wxRfE1hts5R6Q==", + "dependencies": { + "@babel/runtime": "^7.24.0", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.4", + "worker-timers-worker": "^7.0.67" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.4.tgz", + "integrity": "sha512-y3D+Yfj37lrItEMIlcfCm/IRueYtYKgpLlTG2wgTIZ9PSw0n/K4kweilgk3gTC4ahbQNVGT90lU+Rf7W4M5bsw==", + "dependencies": { + "@babel/runtime": "^7.24.0", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.67" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.67", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.67.tgz", + "integrity": "sha512-0ZP2+v2fyiiiGaCEdWxMRUk5YxGFwWdRGB12ZfQy13vw8/27Xd+MW3ua56qlcM30nzjpddXXzLuEpHhGW+Pz7w==", + "dependencies": { + "@babel/runtime": "^7.24.0", + "tslib": "^2.6.2" + } + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index af1c061..0a8e437 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,12 @@ "author": "", "license": "ISC", "dependencies": { - "axios": "^1.6.7", + "@prisma/client": "^5.11.0", "express": "^4.18.3", - "node-cache": "^5.1.2" + "mqtt": "^5.3.6", + "protobufjs": "^7.2.6" + }, + "devDependencies": { + "prisma": "^5.10.2" } } diff --git a/prisma/migrations/20240312135520_create_nodes_table/migration.sql b/prisma/migrations/20240312135520_create_nodes_table/migration.sql new file mode 100644 index 0000000..f4594b7 --- /dev/null +++ b/prisma/migrations/20240312135520_create_nodes_table/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE `nodes` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `node_id` BIGINT NOT NULL, + `long_name` VARCHAR(191) NOT NULL, + `short_name` VARCHAR(191) NOT NULL, + `hardware_model` INTEGER NOT NULL, + `is_licensed` BOOLEAN NOT NULL, + `role` INTEGER NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + UNIQUE INDEX `nodes_node_id_key`(`node_id`), + INDEX `nodes_created_at_idx`(`created_at`), + INDEX `nodes_updated_at_idx`(`updated_at`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/prisma/migrations/20240312140821_add_position_columns_to_nodes_table/migration.sql b/prisma/migrations/20240312140821_add_position_columns_to_nodes_table/migration.sql new file mode 100644 index 0000000..41aaebe --- /dev/null +++ b/prisma/migrations/20240312140821_add_position_columns_to_nodes_table/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE `nodes` ADD COLUMN `altitude` INTEGER NULL, + ADD COLUMN `latitude` INTEGER NULL, + ADD COLUMN `longitude` INTEGER NULL; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5a788a --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "mysql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..2e28f60 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,35 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model Node { + id BigInt @id @default(autoincrement()) + node_id BigInt @unique + long_name String + short_name String + hardware_model Int + is_licensed Boolean + role Int + + latitude Int? + longitude Int? + altitude Int? + + created_at DateTime @default(now()) + updated_at DateTime @default(now()) @updatedAt + + @@index(created_at) + @@index(updated_at) + @@map("nodes") +} diff --git a/src/index.js b/src/index.js index d0d7b18..a208ab2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,36 +1,28 @@ const path = require('path'); -const axios = require('axios'); const express = require('express'); -const NodeCache = require("node-cache"); -const cache = new NodeCache(); +const protobufjs = require("protobufjs"); + +// create prisma db client +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); + +// return big ints as string when using JSON.stringify +BigInt.prototype.toJSON = function() { + return this.toString(); +} + +// load protobufs +const root = new protobufjs.Root(); +root.resolvePath = (origin, target) => path.join(__dirname, "protos", target); +root.loadSync('meshtastic/mqtt.proto'); +const HardwareModel = root.lookupEnum("HardwareModel"); +const Role = root.lookupEnum("Config.DeviceConfig.Role"); const app = express(); // serve files inside the public folder from / app.use('/', express.static(path.join(__dirname, 'public'))); -async function getCached(key, ttl, callback) { - - // find data from cache - let data = cache.get(key); - - // update cache - if(!data){ - data = await callback(); - cache.set(key, data, ttl); - } - - // get expiration time in seconds - const cacheTtl = cache.getTtl(key); - const expiresIn = cacheTtl != null - ? Math.ceil((cacheTtl - Date.now()) / 1000) - : 0; - - // return data - return [ data, expiresIn ]; - -} - app.get('/', async (req, res) => { res.sendFile(path.join(__dirname, 'public/index.html')); }); @@ -59,15 +51,18 @@ app.get('/api', async (req, res) => { app.get('/api/v1/nodes', async (req, res) => { try { - // get nodes from cache, or retrieve from api - const [ nodes, expiresIn ] = await getCached('nodes', 10, async () => { - const response = await axios.get("https://meshmap.net/nodes2.json"); - return response.data; - }); + // get nodes from db + const nodes = await prisma.node.findMany(); res.json({ - expires_in: expiresIn, - nodes: nodes, + nodes: nodes.map((node) => { + return { + ...node, + node_id_hex: "!" + node.node_id.toString(16), + hardware_model_name: HardwareModel.valuesById[node.hardware_model] ?? "UNKNOWN", + role_name: Role.valuesById[node.role] ?? "UNKNOWN", + } + }), }); } catch(err) { diff --git a/src/mqtt.js b/src/mqtt.js new file mode 100644 index 0000000..7bfea32 --- /dev/null +++ b/src/mqtt.js @@ -0,0 +1,165 @@ +const crypto = require("crypto"); +const path = require("path"); +const mqtt = require("mqtt"); +const protobufjs = require("protobufjs"); + +// create prisma db client +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); + +// create mqtt client +const client = mqtt.connect("mqtt://mqtt.meshtastic.org", { + username: "meshdev", + password: "large4cats", +}); + +// load protobufs +const root = new protobufjs.Root(); +root.resolvePath = (origin, target) => path.join(__dirname, "protos", target); +root.loadSync('meshtastic/mqtt.proto'); +const Data = root.lookupType("Data"); +const ServiceEnvelope = root.lookupType("ServiceEnvelope"); +const Position = root.lookupType("Position"); +const User = root.lookupType("User"); + +function createNonce(packetId, fromNode) { + + // Expand packetId to 64 bits + const packetId64 = BigInt(packetId); + + // Initialize block counter (32-bit, starts at zero) + const blockCounter = 0; + + // Create a buffer for the nonce + const buf = Buffer.alloc(16); + + // Write packetId, fromNode, and block counter to the buffer + buf.writeBigUInt64LE(packetId64, 0); + buf.writeUInt32LE(fromNode, 8); + buf.writeUInt32LE(blockCounter, 12); + + return buf; + +} + +/** + * References: + * https://github.com/crypto-smoke/meshtastic-go/blob/develop/radio/aes.go#L42 + * https://github.com/pdxlocations/Meshtastic-MQTT-Connect/blob/main/meshtastic-mqtt-connect.py#L381 + */ +function decrypt(packet) { + try { + + // default encryption key + const key = Buffer.from("1PG7OiApB1nwvP+rz05pAQ==", "base64"); + + // create decryption iv/nonce for this packet + const nonceBuffer = createNonce(packet.id, packet.from); + + // create aes-128-ctr decipher + const decipher = crypto.createDecipheriv('aes-128-ctr', key, nonceBuffer); + + // decrypt encrypted packet + const decryptedBuffer = Buffer.concat([decipher.update(packet.encrypted), decipher.final()]); + + // parse as data message + return Data.decode(decryptedBuffer); + + } catch(e) { + return null; + } +} + +// subscribe to everything when connected +client.on("connect", () => { + client.subscribe("#"); +}); + +// handle message received +client.on("message", async (topic, message) => { + try { + + // decode service envelope + const envelope = ServiceEnvelope.decode(message); + + // attempt to decrypt encrypted packets + const isEncrypted = envelope.packet.encrypted?.length > 0; + if(isEncrypted){ + const decoded = decrypt(envelope.packet); + if(decoded){ + envelope.packet.decoded = decoded; + } + } + + const portnum = envelope.packet?.decoded?.portnum; + + if(portnum === 3) { + + const position = Position.decode(envelope.packet.decoded.payload); + + console.log("POSITION_APP", { + from: envelope.packet.from.toString(16), + position: position, + }); + + // update node position in db + if(position.latitudeI != null && position.longitudeI){ + try { + await prisma.node.updateMany({ + where: { + node_id: envelope.packet.from, + }, + data: { + latitude: position.latitudeI, + longitude: position.longitudeI, + altitude: position.altitude !== 0 ? position.altitude : null, + }, + }); + } catch (e) { + console.error(e); + } + } + + } + + if(portnum === 4) { + + const user = User.decode(envelope.packet.decoded.payload); + + console.log("NODEINFO_APP", { + from: envelope.packet.from.toString(16), + user: user, + }); + + // create or update node in db + try { + await prisma.node.upsert({ + where: { + node_id: envelope.packet.from, + }, + create: { + node_id: envelope.packet.from, + long_name: user.longName, + short_name: user.shortName, + hardware_model: user.hwModel, + is_licensed: user.isLicensed === true, + role: user.role, + }, + update: { + long_name: user.longName, + short_name: user.shortName, + hardware_model: user.hwModel, + is_licensed: user.isLicensed === true, + role: user.role, + }, + }); + } catch (e) { + console.error(e); + } + + } + + } catch(e) { + // ignore errors + } +}); diff --git a/src/protos/meshtastic/admin.options b/src/protos/meshtastic/admin.options new file mode 100644 index 0000000..1d582e2 --- /dev/null +++ b/src/protos/meshtastic/admin.options @@ -0,0 +1,12 @@ +*AdminMessage.payload_variant anonymous_oneof:true + +*AdminMessage.set_canned_message_module_messages max_size:201 +*AdminMessage.get_canned_message_module_messages_response max_size:201 +*AdminMessage.delete_file_request max_size:201 + +*AdminMessage.set_ringtone_message max_size:231 +*AdminMessage.get_ringtone_response max_size:231 + +*HamParameters.call_sign max_size:8 +*HamParameters.short_name max_size:6 +*NodeRemoteHardwarePinsResponse.node_remote_hardware_pins max_count:16 \ No newline at end of file diff --git a/src/protos/meshtastic/admin.proto b/src/protos/meshtastic/admin.proto new file mode 100644 index 0000000..01295f4 --- /dev/null +++ b/src/protos/meshtastic/admin.proto @@ -0,0 +1,364 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/channel.proto"; +import "meshtastic/config.proto"; +import "meshtastic/connection_status.proto"; +import "meshtastic/deviceonly.proto"; +import "meshtastic/mesh.proto"; +import "meshtastic/module_config.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "AdminProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This message is handled by the Admin module and is responsible for all settings/channel read/write operations. + * This message is used to do settings operations to both remote AND local nodes. + * (Prior to 1.2 these operations were done via special ToRadio operations) + */ +message AdminMessage { + /* + * TODO: REPLACE + */ + enum ConfigType { + /* + * TODO: REPLACE + */ + DEVICE_CONFIG = 0; + + /* + * TODO: REPLACE + */ + POSITION_CONFIG = 1; + + /* + * TODO: REPLACE + */ + POWER_CONFIG = 2; + + /* + * TODO: REPLACE + */ + NETWORK_CONFIG = 3; + + /* + * TODO: REPLACE + */ + DISPLAY_CONFIG = 4; + + /* + * TODO: REPLACE + */ + LORA_CONFIG = 5; + + /* + * TODO: REPLACE + */ + BLUETOOTH_CONFIG = 6; + } + + /* + * TODO: REPLACE + */ + enum ModuleConfigType { + /* + * TODO: REPLACE + */ + MQTT_CONFIG = 0; + + /* + * TODO: REPLACE + */ + SERIAL_CONFIG = 1; + + /* + * TODO: REPLACE + */ + EXTNOTIF_CONFIG = 2; + + /* + * TODO: REPLACE + */ + STOREFORWARD_CONFIG = 3; + + /* + * TODO: REPLACE + */ + RANGETEST_CONFIG = 4; + + /* + * TODO: REPLACE + */ + TELEMETRY_CONFIG = 5; + + /* + * TODO: REPLACE + */ + CANNEDMSG_CONFIG = 6; + + /* + * TODO: REPLACE + */ + AUDIO_CONFIG = 7; + + /* + * TODO: REPLACE + */ + REMOTEHARDWARE_CONFIG = 8; + + /* + * TODO: REPLACE + */ + NEIGHBORINFO_CONFIG = 9; + + /* + * TODO: REPLACE + */ + AMBIENTLIGHTING_CONFIG = 10; + + /* + * TODO: REPLACE + */ + DETECTIONSENSOR_CONFIG = 11; + + /* + * TODO: REPLACE + */ + PAXCOUNTER_CONFIG = 12; + } + + /* + * TODO: REPLACE + */ + oneof payload_variant { + /* + * Send the specified channel in the response to this message + * NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) + */ + uint32 get_channel_request = 1; + + /* + * TODO: REPLACE + */ + Channel get_channel_response = 2; + + /* + * Send the current owner data in the response to this message. + */ + bool get_owner_request = 3; + + /* + * TODO: REPLACE + */ + User get_owner_response = 4; + + /* + * Ask for the following config data to be sent + */ + ConfigType get_config_request = 5; + + /* + * Send the current Config in the response to this message. + */ + Config get_config_response = 6; + + /* + * Ask for the following config data to be sent + */ + ModuleConfigType get_module_config_request = 7; + + /* + * Send the current Config in the response to this message. + */ + ModuleConfig get_module_config_response = 8; + + /* + * Get the Canned Message Module messages in the response to this message. + */ + bool get_canned_message_module_messages_request = 10; + + /* + * Get the Canned Message Module messages in the response to this message. + */ + string get_canned_message_module_messages_response = 11; + + /* + * Request the node to send device metadata (firmware, protobuf version, etc) + */ + bool get_device_metadata_request = 12; + + /* + * Device metadata response + */ + DeviceMetadata get_device_metadata_response = 13; + + /* + * Get the Ringtone in the response to this message. + */ + bool get_ringtone_request = 14; + + /* + * Get the Ringtone in the response to this message. + */ + string get_ringtone_response = 15; + + /* + * Request the node to send it's connection status + */ + bool get_device_connection_status_request = 16; + + /* + * Device connection status response + */ + DeviceConnectionStatus get_device_connection_status_response = 17; + + /* + * Setup a node for licensed amateur (ham) radio operation + */ + HamParameters set_ham_mode = 18; + + /* + * Get the mesh's nodes with their available gpio pins for RemoteHardware module use + */ + bool get_node_remote_hardware_pins_request = 19; + + /* + * Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use + */ + NodeRemoteHardwarePinsResponse get_node_remote_hardware_pins_response = 20; + + /* + * Enter (UF2) DFU mode + * Only implemented on NRF52 currently + */ + bool enter_dfu_mode_request = 21; + + /* + * Delete the file by the specified path from the device + */ + string delete_file_request = 22; + + /* + * Set the owner for this node + */ + User set_owner = 32; + + /* + * Set channels (using the new API). + * A special channel is the "primary channel". + * The other records are secondary channels. + * Note: only one channel can be marked as primary. + * If the client sets a particular channel to be primary, the previous channel will be set to SECONDARY automatically. + */ + Channel set_channel = 33; + + /* + * Set the current Config + */ + Config set_config = 34; + + /* + * Set the current Config + */ + ModuleConfig set_module_config = 35; + + /* + * Set the Canned Message Module messages text. + */ + string set_canned_message_module_messages = 36; + + /* + * Set the ringtone for ExternalNotification. + */ + string set_ringtone_message = 37; + + /* + * Remove the node by the specified node-num from the NodeDB on the device + */ + uint32 remove_by_nodenum = 38; + + /* + * Begins an edit transaction for config, module config, owner, and channel settings changes + * This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) + */ + bool begin_edit_settings = 64; + + /* + * Commits an open transaction for any edits made to config, module config, owner, and channel settings + */ + bool commit_edit_settings = 65; + + /* + * Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) + * Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. + */ + int32 reboot_ota_seconds = 95; + + /* + * This message is only supported for the simulator Portduino build. + * If received the simulator will exit successfully. + */ + bool exit_simulator = 96; + + /* + * Tell the node to reboot in this many seconds (or <0 to cancel reboot) + */ + int32 reboot_seconds = 97; + + /* + * Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) + */ + int32 shutdown_seconds = 98; + + /* + * Tell the node to factory reset, all device settings will be returned to factory defaults. + */ + int32 factory_reset = 99; + + /* + * Tell the node to reset the nodedb. + */ + int32 nodedb_reset = 100; + } +} + +/* + * Parameters for setting up Meshtastic for ameteur radio usage + */ +message HamParameters { + /* + * Amateur radio call sign, eg. KD2ABC + */ + string call_sign = 1; + + /* + * Transmit power in dBm at the LoRA transceiver, not including any amplification + */ + int32 tx_power = 2; + + /* + * The selected frequency of LoRA operation + * Please respect your local laws, regulations, and band plans. + * Ensure your radio is capable of operating of the selected frequency before setting this. + */ + float frequency = 3; + + /* + * Optional short name of user + */ + string short_name = 4; +} + +/* + * Response envelope for node_remote_hardware_pins + */ +message NodeRemoteHardwarePinsResponse { + /* + * Nodes and their respective remote hardware GPIO pins + */ + repeated NodeRemoteHardwarePin node_remote_hardware_pins = 1; +} diff --git a/src/protos/meshtastic/apponly.options b/src/protos/meshtastic/apponly.options new file mode 100644 index 0000000..28244de --- /dev/null +++ b/src/protos/meshtastic/apponly.options @@ -0,0 +1 @@ +*ChannelSet.settings max_count:8 diff --git a/src/protos/meshtastic/apponly.proto b/src/protos/meshtastic/apponly.proto new file mode 100644 index 0000000..100833f --- /dev/null +++ b/src/protos/meshtastic/apponly.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/channel.proto"; +import "meshtastic/config.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "AppOnlyProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This is the most compact possible representation for a set of channels. + * It includes only one PRIMARY channel (which must be first) and + * any SECONDARY channels. + * No DISABLED channels are included. + * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL + */ +message ChannelSet { + /* + * Channel list with settings + */ + repeated ChannelSettings settings = 1; + + /* + * LoRa config + */ + Config.LoRaConfig lora_config = 2; +} diff --git a/src/protos/meshtastic/atak.options b/src/protos/meshtastic/atak.options new file mode 100644 index 0000000..c341400 --- /dev/null +++ b/src/protos/meshtastic/atak.options @@ -0,0 +1,6 @@ +*Contact.callsign max_size:120 +*Contact.device_callsign max_size:120 +*Status.battery int_size:8 +*PLI.course int_size:16 +*GeoChat.message max_size:200 +*GeoChat.to max_size:120 \ No newline at end of file diff --git a/src/protos/meshtastic/atak.proto b/src/protos/meshtastic/atak.proto new file mode 100644 index 0000000..c44479f --- /dev/null +++ b/src/protos/meshtastic/atak.proto @@ -0,0 +1,251 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ATAKProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; +/* + * Packets for the official ATAK Plugin + */ +message TAKPacket +{ + /* + * Are the payloads strings compressed for LoRA transport? + */ + bool is_compressed = 1; + /* + * The contact / callsign for ATAK user + */ + Contact contact = 2; + /* + * The group for ATAK user + */ + Group group = 3; + /* + * The status of the ATAK EUD + */ + Status status = 4; + /* + * The payload of the packet + */ + oneof payload_variant { + /* + * TAK position report + */ + PLI pli = 5; + /* + * ATAK GeoChat message + */ + GeoChat chat = 6; + } +} + +/* + * ATAK GeoChat message + */ +message GeoChat { + /* + * The text message + */ + string message = 1; + + /* + * Uid recipient of the message + */ + optional string to = 2; +} + +/* + * ATAK Group + * <__group role='Team Member' name='Cyan'/> + */ +message Group { + /* + * Role of the group member + */ + MemberRole role = 1; + /* + * Team (color) + * Default Cyan + */ + Team team = 2; +} + +enum Team { + /* + * Unspecifed + */ + Unspecifed_Color = 0; + /* + * White + */ + White = 1; + /* + * Yellow + */ + Yellow = 2; + /* + * Orange + */ + Orange = 3; + /* + * Magenta + */ + Magenta = 4; + /* + * Red + */ + Red = 5; + /* + * Maroon + */ + Maroon = 6; + /* + * Purple + */ + Purple = 7; + /* + * Dark Blue + */ + Dark_Blue = 8; + /* + * Blue + */ + Blue = 9; + /* + * Cyan + */ + Cyan = 10; + /* + * Teal + */ + Teal = 11; + /* + * Green + */ + Green = 12; + /* + * Dark Green + */ + Dark_Green = 13; + /* + * Brown + */ + Brown = 14; +} + +/* + * Role of the group member + */ +enum MemberRole { + /* + * Unspecifed + */ + Unspecifed = 0; + /* + * Team Member + */ + TeamMember = 1; + /* + * Team Lead + */ + TeamLead = 2; + /* + * Headquarters + */ + HQ = 3; + /* + * Airsoft enthusiast + */ + Sniper = 4; + /* + * Medic + */ + Medic = 5; + /* + * ForwardObserver + */ + ForwardObserver = 6; + /* + * Radio Telephone Operator + */ + RTO = 7; + /* + * Doggo + */ + K9 = 8; +} + +/* + * ATAK EUD Status + * + */ +message Status { + /* + * Battery level + */ + uint32 battery = 1; +} +/* + * ATAK Contact + * + */ +message Contact { + /* + * Callsign + */ + string callsign = 1; + + /* + * Device callsign + */ + string device_callsign = 2; + /* + * IP address of endpoint in integer form (0.0.0.0 default) + */ + // fixed32 enpoint_address = 3; + /* + * Port of endpoint (4242 default) + */ + // uint32 endpoint_port = 4; + /* + * Phone represented as integer + * Terrible practice, but we really need the wire savings + */ + // uint32 phone = 4; +} + +/* + * Position Location Information from ATAK + */ +message PLI { + /* + * The new preferred location encoding, multiply by 1e-7 to get degrees + * in floating point + */ + sfixed32 latitude_i = 1; + + /* + * The new preferred location encoding, multiply by 1e-7 to get degrees + * in floating point + */ + sfixed32 longitude_i = 2; + + /* + * Altitude (ATAK prefers HAE) + */ + int32 altitude = 3; + + /* + * Speed + */ + uint32 speed = 4; + + /* + * Course in degrees + */ + uint32 course = 5; +} diff --git a/src/protos/meshtastic/cannedmessages.options b/src/protos/meshtastic/cannedmessages.options new file mode 100644 index 0000000..c1d537b --- /dev/null +++ b/src/protos/meshtastic/cannedmessages.options @@ -0,0 +1 @@ +*CannedMessageModuleConfig.messages max_size:201 diff --git a/src/protos/meshtastic/cannedmessages.proto b/src/protos/meshtastic/cannedmessages.proto new file mode 100644 index 0000000..baa5134 --- /dev/null +++ b/src/protos/meshtastic/cannedmessages.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "CannedMessageConfigProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * Canned message module configuration. + */ +message CannedMessageModuleConfig { + /* + * Predefined messages for canned message module separated by '|' characters. + */ + string messages = 1; +} diff --git a/src/protos/meshtastic/channel.options b/src/protos/meshtastic/channel.options new file mode 100644 index 0000000..d0bdcbc --- /dev/null +++ b/src/protos/meshtastic/channel.options @@ -0,0 +1,5 @@ +*Channel.index int_size:8 + +# 256 bit or 128 bit psk key +*ChannelSettings.psk max_size:32 +*ChannelSettings.name max_size:12 diff --git a/src/protos/meshtastic/channel.proto b/src/protos/meshtastic/channel.proto new file mode 100644 index 0000000..2b10aaf --- /dev/null +++ b/src/protos/meshtastic/channel.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ChannelProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This information can be encoded as a QRcode/url so that other users can configure + * their radio to join the same channel. + * A note about how channel names are shown to users: channelname-X + * poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). + * Where X is a letter from A-Z (base 26) representing a hash of the PSK for this + * channel - so that if the user changes anything about the channel (which does + * force a new PSK) this letter will also change. Thus preventing user confusion if + * two friends try to type in a channel name of "BobsChan" and then can't talk + * because their PSKs will be different. + * The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" + * This also allows the option of someday if people have the PSK off (zero), the + * users COULD type in a channel name and be able to talk. + * FIXME: Add description of multi-channel support and how primary vs secondary channels are used. + * FIXME: explain how apps use channels for security. + * explain how remote settings and remote gpio are managed as an example + */ +message ChannelSettings { + /* + * Deprecated in favor of LoraConfig.channel_num + */ + uint32 channel_num = 1 [deprecated = true]; + + /* + * A simple pre-shared key for now for crypto. + * Must be either 0 bytes (no crypto), 16 bytes (AES128), or 32 bytes (AES256). + * A special shorthand is used for 1 byte long psks. + * These psks should be treated as only minimally secure, + * because they are listed in this source code. + * Those bytes are mapped using the following scheme: + * `0` = No crypto + * `1` = The special "default" channel key: {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01} + * `2` through 10 = The default channel key, except with 1 through 9 added to the last byte. + * Shown to user as simple1 through 10 + */ + bytes psk = 2; + + /* + * A SHORT name that will be packed into the URL. + * Less than 12 bytes. + * Something for end users to call the channel + * If this is the empty string it is assumed that this channel + * is the special (minimally secure) "Default"channel. + * In user interfaces it should be rendered as a local language translation of "X". + * For channel_num hashing empty string will be treated as "X". + * Where "X" is selected based on the English words listed above for ModemPreset + */ + string name = 3; + + /* + * Used to construct a globally unique channel ID. + * The full globally unique ID will be: "name.id" where ID is shown as base36. + * Assuming that the number of meshtastic users is below 20K (true for a long time) + * the chance of this 64 bit random number colliding with anyone else is super low. + * And the penalty for collision is low as well, it just means that anyone trying to decrypt channel messages might need to + * try multiple candidate channels. + * Any time a non wire compatible change is made to a channel, this field should be regenerated. + * There are a small number of 'special' globally known (and fairly) insecure standard channels. + * Those channels do not have a numeric id included in the settings, but instead it is pulled from + * a table of well known IDs. + * (see Well Known Channels FIXME) + */ + fixed32 id = 4; + + /* + * If true, messages on the mesh will be sent to the *public* internet by any gateway ndoe + */ + bool uplink_enabled = 5; + + /* + * If true, messages seen on the internet will be forwarded to the local mesh. + */ + bool downlink_enabled = 6; + + /* + * Per-channel module settings. + */ + ModuleSettings module_settings = 7; +} + +/* + * This message is specifically for modules to store per-channel configuration data. + */ +message ModuleSettings { +/* + * Bits of precision for the location sent in position packets. + */ + uint32 position_precision = 1; +} + +/* + * A pair of a channel number, mode and the (sharable) settings for that channel + */ +message Channel { + /* + * How this channel is being used (or not). + * Note: this field is an enum to give us options for the future. + * In particular, someday we might make a 'SCANNING' option. + * SCANNING channels could have different frequencies and the radio would + * occasionally check that freq to see if anything is being transmitted. + * For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow + * cross band routing as needed. + * If a device has only a single radio (the common case) only one channel can be PRIMARY at a time + * (but any number of SECONDARY channels can't be sent received on that common frequency) + */ + enum Role { + /* + * This channel is not in use right now + */ + DISABLED = 0; + + /* + * This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY + */ + PRIMARY = 1; + + /* + * Secondary channels are only used for encryption/decryption/authentication purposes. + * Their radio settings (freq etc) are ignored, only psk is used. + */ + SECONDARY = 2; + } + + /* + * The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1) + * (Someday - not currently implemented) An index of -1 could be used to mean "set by name", + * in which case the target node will find and set the channel by settings.name. + */ + int32 index = 1; + + /* + * The new settings, or NULL to disable that channel + */ + ChannelSettings settings = 2; + + /* + * TODO: REPLACE + */ + Role role = 3; +} diff --git a/src/protos/meshtastic/clientonly.options b/src/protos/meshtastic/clientonly.options new file mode 100644 index 0000000..bc98b39 --- /dev/null +++ b/src/protos/meshtastic/clientonly.options @@ -0,0 +1,2 @@ +*DeviceProfile.long_name max_size:40 +*DeviceProfile.short_name max_size:5 \ No newline at end of file diff --git a/src/protos/meshtastic/clientonly.proto b/src/protos/meshtastic/clientonly.proto new file mode 100644 index 0000000..1d58b11 --- /dev/null +++ b/src/protos/meshtastic/clientonly.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/localonly.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ClientOnlyProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This abstraction is used to contain any configuration for provisioning a node on any client. + * It is useful for importing and exporting configurations. + */ +message DeviceProfile { + /* + * Long name for the node + */ + optional string long_name = 1; + + /* + * Short name of the node + */ + optional string short_name = 2; + + /* + * The url of the channels from our node + */ + optional string channel_url = 3; + + /* + * The Config of the node + */ + optional LocalConfig config = 4; + + /* + * The ModuleConfig of the node + */ + optional LocalModuleConfig module_config = 5; +} + +/* + * A heartbeat message is sent by a node to indicate that it is still alive. + * This is currently only needed to keep serial connections alive. + */ +message Heartbeat { + +} diff --git a/src/protos/meshtastic/config.options b/src/protos/meshtastic/config.options new file mode 100644 index 0000000..4490f08 --- /dev/null +++ b/src/protos/meshtastic/config.options @@ -0,0 +1,14 @@ +*NetworkConfig.wifi_ssid max_size:33 +*NetworkConfig.wifi_psk max_size:65 +*NetworkConfig.ntp_server max_size:33 +*NetworkConfig.rsyslog_server max_size:33 + +# Max of three ignored nodes for our testing +*LoRaConfig.ignore_incoming max_count:3 + +*LoRaConfig.tx_power int_size:8 +*LoRaConfig.bandwidth int_size:16 +*LoRaConfig.coding_rate int_size:8 +*LoRaConfig.channel_num int_size:16 + +*PowerConfig.device_battery_ina_address int_size:8 diff --git a/src/protos/meshtastic/config.proto b/src/protos/meshtastic/config.proto new file mode 100644 index 0000000..7ebbe45 --- /dev/null +++ b/src/protos/meshtastic/config.proto @@ -0,0 +1,955 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ConfigProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +message Config { + /* + * Configuration + */ + message DeviceConfig { + /* + * Defines the device's role on the Mesh network + */ + enum Role { + /* + * Description: App connected or stand alone messaging device. + * Technical Details: Default Role + */ + CLIENT = 0; + /* + * Description: Device that does not forward packets from other devices. + */ + CLIENT_MUTE = 1; + + /* + * Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. + * Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. + * The wifi radio and the oled screen will be put to sleep. + * This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. + */ + ROUTER = 2; + + /* + * Description: Combination of both ROUTER and CLIENT. Not for mobile devices. + */ + ROUTER_CLIENT = 3; + + /* + * Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. + * Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry + * or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. + */ + REPEATER = 4; + + /* + * Description: Broadcasts GPS position packets as priority. + * Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. + * When used in conjunction with power.is_power_saving = true, nodes will wake up, + * send position, and then sleep for position.position_broadcast_secs seconds. + */ + TRACKER = 5; + + /* + * Description: Broadcasts telemetry packets as priority. + * Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. + * When used in conjunction with power.is_power_saving = true, nodes will wake up, + * send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. + */ + SENSOR = 6; + + /* + * Description: Optimized for ATAK system communication and reduces routine broadcasts. + * Technical Details: Used for nodes dedicated for connection to an ATAK EUD. + * Turns off many of the routine broadcasts to favor CoT packet stream + * from the Meshtastic ATAK plugin -> IMeshService -> Node + */ + TAK = 7; + + /* + * Description: Device that only broadcasts as needed for stealth or power savings. + * Technical Details: Used for nodes that "only speak when spoken to" + * Turns all of the routine broadcasts but allows for ad-hoc communication + * Still rebroadcasts, but with local only rebroadcast mode (known meshes only) + * Can be used for clandestine operation or to dramatically reduce airtime / power consumption + */ + CLIENT_HIDDEN = 8; + + /* + * Description: Broadcasts location as message to default channel regularly for to assist with device recovery. + * Technical Details: Used to automatically send a text message to the mesh + * with the current position of the device on a frequent interval: + * "I'm lost! Position: lat / long" + */ + LOST_AND_FOUND = 9; + + /* + * Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. + * Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream + * and automatic TAK PLI (position location information) broadcasts. + * Uses position module configuration to determine TAK PLI broadcast interval. + */ + TAK_TRACKER = 10; + } + + /* + * Defines the device's behavior for how messages are rebroadcast + */ + enum RebroadcastMode { + /* + * Default behavior. + * Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. + */ + ALL = 0; + + /* + * Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. + * Only available in Repeater role. Setting this on any other roles will result in ALL behavior. + */ + ALL_SKIP_DECODING = 1; + + /* + * Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. + * Only rebroadcasts message on the nodes local primary / secondary channels. + */ + LOCAL_ONLY = 2; + + /* + * Ignores observed messages from foreign meshes like LOCAL_ONLY, + * but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) + */ + KNOWN_ONLY = 3; + } + + /* + * Sets the role of node + */ + Role role = 1; + + /* + * Disabling this will disable the SerialConsole by not initilizing the StreamAPI + */ + bool serial_enabled = 2; + + /* + * By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + * Set this to true to leave the debug log outputting even when API is active. + */ + bool debug_log_enabled = 3; + + /* + * For boards without a hard wired button, this is the pin number that will be used + * Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. + */ + uint32 button_gpio = 4; + + /* + * For boards without a PWM buzzer, this is the pin number that will be used + * Defaults to PIN_BUZZER if defined. + */ + uint32 buzzer_gpio = 5; + + /* + * Sets the role of node + */ + RebroadcastMode rebroadcast_mode = 6; + + /* + * Send our nodeinfo this often + * Defaults to 900 Seconds (15 minutes) + */ + uint32 node_info_broadcast_secs = 7; + + /* + * Treat double tap interrupt on supported accelerometers as a button press if set to true + */ + bool double_tap_as_button_press = 8; + + /* + * If true, device is considered to be "managed" by a mesh administrator + * Clients should then limit available configuration and administrative options inside the user interface + */ + bool is_managed = 9; + + /* + * Disables the triple-press of user button to enable or disable GPS + */ + bool disable_triple_click = 10; + } + + /* + * Position Config + */ + message PositionConfig { + /* + * Bit field of boolean configuration options, indicating which optional + * fields to include when assembling POSITION messages. + * Longitude, latitude, altitude, speed, heading, and DOP + * are always included (also time if GPS-synced) + * NOTE: the more fields are included, the larger the message will be - + * leading to longer airtime and a higher risk of packet loss + */ + enum PositionFlags { + /* + * Required for compilation + */ + UNSET = 0x0000; + + /* + * Include an altitude value (if available) + */ + ALTITUDE = 0x0001; + + /* + * Altitude value is MSL + */ + ALTITUDE_MSL = 0x0002; + + /* + * Include geoidal separation + */ + GEOIDAL_SEPARATION = 0x0004; + + /* + * Include the DOP value ; PDOP used by default, see below + */ + DOP = 0x0008; + + /* + * If POS_DOP set, send separate HDOP / VDOP values instead of PDOP + */ + HVDOP = 0x0010; + + /* + * Include number of "satellites in view" + */ + SATINVIEW = 0x0020; + + /* + * Include a sequence number incremented per packet + */ + SEQ_NO = 0x0040; + + /* + * Include positional timestamp (from GPS solution) + */ + TIMESTAMP = 0x0080; + + /* + * Include positional heading + * Intended for use with vehicle not walking speeds + * walking speeds are likely to be error prone like the compass + */ + HEADING = 0x0100; + + /* + * Include positional speed + * Intended for use with vehicle not walking speeds + * walking speeds are likely to be error prone like the compass + */ + SPEED = 0x0200; + } + + enum GpsMode { + /* + * GPS is present but disabled + */ + DISABLED = 0; + + /* + * GPS is present and enabled + */ + ENABLED = 1; + + /* + * GPS is not present on the device + */ + NOT_PRESENT = 2; + } + + /* + * We should send our position this often (but only if it has changed significantly) + * Defaults to 15 minutes + */ + uint32 position_broadcast_secs = 1; + + /* + * Adaptive position braoadcast, which is now the default. + */ + bool position_broadcast_smart_enabled = 2; + + /* + * If set, this node is at a fixed position. + * We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node. + * The lat/lon/alt can be set by an internal GPS or with the help of the app. + */ + bool fixed_position = 3; + + /* + * Is GPS enabled for this node? + */ + bool gps_enabled = 4[deprecated = true]; + + /* + * How often should we try to get GPS position (in seconds) + * or zero for the default of once every 30 seconds + * or a very large value (maxint) to update only once at boot. + */ + uint32 gps_update_interval = 5; + + /* + * Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time + */ + uint32 gps_attempt_time = 6 [deprecated = true]; + + /* + * Bit field of boolean configuration options for POSITION messages + * (bitwise OR of PositionFlags) + */ + uint32 position_flags = 7; + + /* + * (Re)define GPS_RX_PIN for your board. + */ + uint32 rx_gpio = 8; + + /* + * (Re)define GPS_TX_PIN for your board. + */ + uint32 tx_gpio = 9; + + /* + * The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled + */ + uint32 broadcast_smart_minimum_distance = 10; + + /* + * The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled + */ + uint32 broadcast_smart_minimum_interval_secs = 11; + + /* + * (Re)define PIN_GPS_EN for your board. + */ + uint32 gps_en_gpio = 12; + + /* + * Set where GPS is enabled, disabled, or not present + */ + GpsMode gps_mode = 13; + } + + /* + * Power Config\ + * See [Power Config](/docs/settings/config/power) for additional power config details. + */ + message PowerConfig { + /* + * If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in + * we should try to minimize power consumption as much as possible. + * YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case). + * Advanced Option + */ + bool is_power_saving = 1; + + /* + * If non-zero, the device will fully power off this many seconds after external power is removed. + */ + uint32 on_battery_shutdown_after_secs = 2; + + /* + * Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) + * Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. + * Should be set to floating point value between 2 and 4 + * Fixes issues on Heltec v2 + */ + float adc_multiplier_override = 3; + + /* + * Wait Bluetooth Seconds + * The number of seconds for to wait before turning off BLE in No Bluetooth states + * 0 for default of 1 minute + */ + uint32 wait_bluetooth_secs = 4; + + /* + * Super Deep Sleep Seconds + * While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep + * for this value (default 1 year) or a button press + * 0 for default of one year + */ + uint32 sds_secs = 6; + + /* + * Light Sleep Seconds + * In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on + * ESP32 Only + * 0 for default of 300 + */ + uint32 ls_secs = 7; + + /* + * Minimum Wake Seconds + * While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value + * 0 for default of 10 seconds + */ + uint32 min_wake_secs = 8; + + /* + * I2C address of INA_2XX to use for reading device battery voltage + */ + uint32 device_battery_ina_address = 9; + } + + /* + * Network Config + */ + message NetworkConfig { + enum AddressMode { + /* + * obtain ip address via DHCP + */ + DHCP = 0; + + /* + * use static ip address + */ + STATIC = 1; + } + + message IpV4Config { + /* + * Static IP address + */ + fixed32 ip = 1; + + /* + * Static gateway address + */ + fixed32 gateway = 2; + + /* + * Static subnet mask + */ + fixed32 subnet = 3; + + /* + * Static DNS server address + */ + fixed32 dns = 4; + } + + /* + * Enable WiFi (disables Bluetooth) + */ + bool wifi_enabled = 1; + + /* + * If set, this node will try to join the specified wifi network and + * acquire an address via DHCP + */ + string wifi_ssid = 3; + + /* + * If set, will be use to authenticate to the named wifi + */ + string wifi_psk = 4; + + /* + * NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org` + */ + string ntp_server = 5; + + /* + * Enable Ethernet + */ + bool eth_enabled = 6; + + /* + * acquire an address via DHCP or assign static + */ + AddressMode address_mode = 7; + + /* + * struct to keep static address + */ + IpV4Config ipv4_config = 8; + + /* + * rsyslog Server and Port + */ + string rsyslog_server = 9; + } + + /* + * Display Config + */ + message DisplayConfig { + /* + * How the GPS coordinates are displayed on the OLED screen. + */ + enum GpsCoordinateFormat { + /* + * GPS coordinates are displayed in the normal decimal degrees format: + * DD.DDDDDD DDD.DDDDDD + */ + DEC = 0; + + /* + * GPS coordinates are displayed in the degrees minutes seconds format: + * DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant + */ + DMS = 1; + + /* + * Universal Transverse Mercator format: + * ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing + */ + UTM = 2; + + /* + * Military Grid Reference System format: + * ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, + * E is easting, N is northing + */ + MGRS = 3; + + /* + * Open Location Code (aka Plus Codes). + */ + OLC = 4; + + /* + * Ordnance Survey Grid Reference (the National Grid System of the UK). + * Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, + * E is the easting, N is the northing + */ + OSGR = 5; + } + + /* + * Unit display preference + */ + enum DisplayUnits { + /* + * Metric (Default) + */ + METRIC = 0; + + /* + * Imperial + */ + IMPERIAL = 1; + } + + /* + * Override OLED outo detect with this if it fails. + */ + enum OledType { + /* + * Default / Auto + */ + OLED_AUTO = 0; + + /* + * Default / Auto + */ + OLED_SSD1306 = 1; + + /* + * Default / Auto + */ + OLED_SH1106 = 2; + + /* + * Can not be auto detected but set by proto. Used for 128x128 screens + */ + OLED_SH1107 = 3; + } + + /* + * Number of seconds the screen stays on after pressing the user button or receiving a message + * 0 for default of one minute MAXUINT for always on + */ + uint32 screen_on_secs = 1; + + /* + * How the GPS coordinates are formatted on the OLED screen. + */ + GpsCoordinateFormat gps_format = 2; + + /* + * Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. + * Potentially useful for devices without user buttons. + */ + uint32 auto_screen_carousel_secs = 3; + + /* + * If this is set, the displayed compass will always point north. if unset, the old behaviour + * (top of display is heading direction) is used. + */ + bool compass_north_top = 4; + + /* + * Flip screen vertically, for cases that mount the screen upside down + */ + bool flip_screen = 5; + + /* + * Perferred display units + */ + DisplayUnits units = 6; + + /* + * Override auto-detect in screen + */ + OledType oled = 7; + + enum DisplayMode { + /* + * Default. The old style for the 128x64 OLED screen + */ + DEFAULT = 0; + + /* + * Rearrange display elements to cater for bicolor OLED displays + */ + TWOCOLOR = 1; + + /* + * Same as TwoColor, but with inverted top bar. Not so good for Epaper displays + */ + INVERTED = 2; + + /* + * TFT Full Color Displays (not implemented yet) + */ + COLOR = 3; + } + /* + * Display Mode + */ + DisplayMode displaymode = 8; + + /* + * Print first line in pseudo-bold? FALSE is original style, TRUE is bold + */ + bool heading_bold = 9; + + /* + * Should we wake the screen up on accelerometer detected motion or tap + */ + bool wake_on_tap_or_motion = 10; + } + + /* + * Lora Config + */ + message LoRaConfig { + enum RegionCode { + /* + * Region is not set + */ + UNSET = 0; + + /* + * United States + */ + US = 1; + + /* + * European Union 433mhz + */ + EU_433 = 2; + + /* + * European Union 868mhz + */ + EU_868 = 3; + + /* + * China + */ + CN = 4; + + /* + * Japan + */ + JP = 5; + + /* + * Australia / New Zealand + */ + ANZ = 6; + + /* + * Korea + */ + KR = 7; + + /* + * Taiwan + */ + TW = 8; + + /* + * Russia + */ + RU = 9; + + /* + * India + */ + IN = 10; + + /* + * New Zealand 865mhz + */ + NZ_865 = 11; + + /* + * Thailand + */ + TH = 12; + + /* + * WLAN Band + */ + LORA_24 = 13; + + /* + * Ukraine 433mhz + */ + UA_433 = 14; + + /* + * Ukraine 868mhz + */ + UA_868 = 15; + + /* + * Malaysia 433mhz + */ + MY_433 = 16; + + /* + * Malaysia 919mhz + */ + MY_919 = 17; + + /* + * Singapore 923mhz + */ + SG_923 = 18; + } + + /* + * Standard predefined channel settings + * Note: these mappings must match ModemPreset Choice in the device code. + */ + enum ModemPreset { + /* + * Long Range - Fast + */ + LONG_FAST = 0; + + /* + * Long Range - Slow + */ + LONG_SLOW = 1; + + /* + * Very Long Range - Slow + */ + VERY_LONG_SLOW = 2; + + /* + * Medium Range - Slow + */ + MEDIUM_SLOW = 3; + + /* + * Medium Range - Fast + */ + MEDIUM_FAST = 4; + + /* + * Short Range - Slow + */ + SHORT_SLOW = 5; + + /* + * Short Range - Fast + */ + SHORT_FAST = 6; + + /* + * Long Range - Moderately Fast + */ + LONG_MODERATE = 7; + } + + /* + * When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate` + * will be taked from their respective manually defined fields + */ + bool use_preset = 1; + + /* + * Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH. + * As a heuristic: If bandwidth is specified, do not use modem_config. + * Because protobufs take ZERO space when the value is zero this works out nicely. + * This value is replaced by bandwidth/spread_factor/coding_rate. + * If you'd like to experiment with other options add them to MeshRadio.cpp in the device code. + */ + ModemPreset modem_preset = 2; + + /* + * Bandwidth in MHz + * Certain bandwidth numbers are 'special' and will be converted to the + * appropriate floating point value: 31 -> 31.25MHz + */ + uint32 bandwidth = 3; + + /* + * A number from 7 to 12. + * Indicates number of chirps per symbol as 1< 7 results in the default + */ + uint32 hop_limit = 8; + + /* + * Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests. + * Defaults to false + */ + bool tx_enabled = 9; + + /* + * If zero, then use default max legal continuous power (ie. something that won't + * burn out the radio hardware) + * In most cases you should use zero here. + * Units are in dBm. + */ + int32 tx_power = 10; + + /* + * This controls the actual hardware frequency the radio transmits on. + * Most users should never need to be exposed to this field/concept. + * A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region). + * If ZERO then the rule is "use the old channel name hash based + * algorithm to derive the channel number") + * If using the hash algorithm the channel number will be: hash(channel_name) % + * NUM_CHANNELS (Where num channels depends on the regulatory region). + */ + uint32 channel_num = 11; + + /* + * If true, duty cycle limits will be exceeded and thus you're possibly not following + * the local regulations if you're not a HAM. + * Has no effect if the duty cycle of the used region is 100%. + */ + bool override_duty_cycle = 12; + + /* + * If true, sets RX boosted gain mode on SX126X based radios + */ + bool sx126x_rx_boosted_gain = 13; + + /* + * This parameter is for advanced users and licensed HAM radio operators. + * Ignore Channel Calculation and use this frequency instead. The frequency_offset + * will still be applied. This will allow you to use out-of-band frequencies. + * Please respect your local laws and regulations. If you are a HAM, make sure you + * enable HAM mode and turn off encryption. + */ + float override_frequency = 14; + + /* + * For testing it is useful sometimes to force a node to never listen to + * particular other nodes (simulating radio out of range). All nodenums listed + * in ignore_incoming will have packets they send dropped on receive (by router.cpp) + */ + repeated uint32 ignore_incoming = 103; + + /* + * If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. + */ + bool ignore_mqtt = 104; + } + + message BluetoothConfig { + enum PairingMode { + /* + * Device generates a random PIN that will be shown on the screen of the device for pairing + */ + RANDOM_PIN = 0; + + /* + * Device requires a specified fixed PIN for pairing + */ + FIXED_PIN = 1; + + /* + * Device requires no PIN for pairing + */ + NO_PIN = 2; + } + + /* + * Enable Bluetooth on the device + */ + bool enabled = 1; + + /* + * Determines the pairing strategy for the device + */ + PairingMode mode = 2; + + /* + * Specified PIN for PairingMode.FixedPin + */ + uint32 fixed_pin = 3; + } + + /* + * Payload Variant + */ + oneof payload_variant { + DeviceConfig device = 1; + PositionConfig position = 2; + PowerConfig power = 3; + NetworkConfig network = 4; + DisplayConfig display = 5; + LoRaConfig lora = 6; + BluetoothConfig bluetooth = 7; + } +} diff --git a/src/protos/meshtastic/connection_status.options b/src/protos/meshtastic/connection_status.options new file mode 100644 index 0000000..d4901dd --- /dev/null +++ b/src/protos/meshtastic/connection_status.options @@ -0,0 +1 @@ +*WifiConnectionStatus.ssid max_size:33 diff --git a/src/protos/meshtastic/connection_status.proto b/src/protos/meshtastic/connection_status.proto new file mode 100644 index 0000000..7551596 --- /dev/null +++ b/src/protos/meshtastic/connection_status.proto @@ -0,0 +1,120 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ConnStatusProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +message DeviceConnectionStatus { + /* + * WiFi Status + */ + optional WifiConnectionStatus wifi = 1; + /* + * WiFi Status + */ + optional EthernetConnectionStatus ethernet = 2; + + /* + * Bluetooth Status + */ + optional BluetoothConnectionStatus bluetooth = 3; + + /* + * Serial Status + */ + optional SerialConnectionStatus serial = 4; +} + +/* + * WiFi connection status + */ +message WifiConnectionStatus { + /* + * Connection status + */ + NetworkConnectionStatus status = 1; + + /* + * WiFi access point SSID + */ + string ssid = 2; + + /* + * RSSI of wireless connection + */ + int32 rssi = 3; +} + +/* + * Ethernet connection status + */ +message EthernetConnectionStatus { + /* + * Connection status + */ + NetworkConnectionStatus status = 1; +} + +/* + * Ethernet or WiFi connection status + */ +message NetworkConnectionStatus { + /* + * IP address of device + */ + fixed32 ip_address = 1; + + /* + * Whether the device has an active connection or not + */ + bool is_connected = 2; + + /* + * Whether the device has an active connection to an MQTT broker or not + */ + bool is_mqtt_connected = 3; + + /* + * Whether the device is actively remote syslogging or not + */ + bool is_syslog_connected = 4; +} + +/* + * Bluetooth connection status + */ +message BluetoothConnectionStatus { + /* + * The pairing PIN for bluetooth + */ + uint32 pin = 1; + + /* + * RSSI of bluetooth connection + */ + int32 rssi = 2; + + /* + * Whether the device has an active connection or not + */ + bool is_connected = 3; +} + +/* + * Serial connection status + */ +message SerialConnectionStatus { + /* + * Serial baud rate + */ + uint32 baud = 1; + + /* + * Whether the device has an active connection or not + */ + bool is_connected = 2; +} diff --git a/src/protos/meshtastic/deviceonly.options b/src/protos/meshtastic/deviceonly.options new file mode 100644 index 0000000..d870ace --- /dev/null +++ b/src/protos/meshtastic/deviceonly.options @@ -0,0 +1,19 @@ +# options for nanopb +# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options + +# FIXME pick a higher number someday? or do dynamic alloc in nanopb? +*DeviceState.node_db_lite max_count:100 + +# FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in RAM +*DeviceState.receive_queue max_count:1 + +*ChannelFile.channels max_count:8 + +*OEMStore.oem_text max_size:40 +*OEMStore.oem_icon_bits max_size:2048 +*OEMStore.oem_aes_key max_size:32 + +*DeviceState.node_remote_hardware_pins max_count:12 + +*NodeInfoLite.channel int_size:8 +*NodeInfoLite.hops_away int_size:8 \ No newline at end of file diff --git a/src/protos/meshtastic/deviceonly.proto b/src/protos/meshtastic/deviceonly.proto new file mode 100644 index 0000000..169df1c --- /dev/null +++ b/src/protos/meshtastic/deviceonly.proto @@ -0,0 +1,262 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/channel.proto"; +import "meshtastic/localonly.proto"; +import "meshtastic/mesh.proto"; +import "meshtastic/telemetry.proto"; +import "meshtastic/module_config.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "DeviceOnly"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This message is never sent over the wire, but it is used for serializing DB + * state to flash in the device code + * FIXME, since we write this each time we enter deep sleep (and have infinite + * flash) it would be better to use some sort of append only data structure for + * the receive queue and use the preferences store for the other stuff + */ +message DeviceState { + /* + * Read only settings/info about this node + */ + MyNodeInfo my_node = 2; + + /* + * My owner info + */ + User owner = 3; + + /* + * Received packets saved for delivery to the phone + */ + repeated MeshPacket receive_queue = 5; + + /* + * A version integer used to invalidate old save files when we make + * incompatible changes This integer is set at build time and is private to + * NodeDB.cpp in the device code. + */ + uint32 version = 8; + + /* + * We keep the last received text message (only) stored in the device flash, + * so we can show it on the screen. + * Might be null + */ + MeshPacket rx_text_message = 7; + + /* + * Used only during development. + * Indicates developer is testing and changes should never be saved to flash. + */ + bool no_save = 9; + + /* + * Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. + */ + bool did_gps_reset = 11; + + /* + * We keep the last received waypoint stored in the device flash, + * so we can show it on the screen. + * Might be null + */ + MeshPacket rx_waypoint = 12; + + /* + * The mesh's nodes with their available gpio pins for RemoteHardware module + */ + repeated NodeRemoteHardwarePin node_remote_hardware_pins = 13; + + /* + * New lite version of NodeDB to decrease memory footprint + */ + repeated NodeInfoLite node_db_lite = 14; +} + +message NodeInfoLite { + /* + * The node number + */ + uint32 num = 1; + + /* + * The user info for this node + */ + User user = 2; + + /* + * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + * Position.time now indicates the last time we received a POSITION from that node. + */ + PositionLite position = 3; + + /* + * Returns the Signal-to-noise ratio (SNR) of the last received message, + * as measured by the receiver. Return SNR of the last received message in dB + */ + float snr = 4; + + /* + * Set to indicate the last time we received a packet from this node + */ + fixed32 last_heard = 5; + /* + * The latest device metrics for the node. + */ + DeviceMetrics device_metrics = 6; + + /* + * local channel index we heard that node on. Only populated if its not the default channel. + */ + uint32 channel = 7; + + /* + * True if we witnessed the node over MQTT instead of LoRA transport + */ + bool via_mqtt = 8; + + /* + * Number of hops away from us this node is (0 if adjacent) + */ + uint32 hops_away = 9; +} + +/* + * Position with static location information only for NodeDBLite + */ +message PositionLite { + /* + * The new preferred location encoding, multiply by 1e-7 to get degrees + * in floating point + */ + sfixed32 latitude_i = 1; + + /* + * TODO: REPLACE + */ + sfixed32 longitude_i = 2; + + /* + * In meters above MSL (but see issue #359) + */ + int32 altitude = 3; + + /* + * This is usually not sent over the mesh (to save space), but it is sent + * from the phone so that the local device can set its RTC If it is sent over + * the mesh (because there are devices on the mesh without GPS), it will only + * be sent by devices which has a hardware GPS clock. + * seconds since 1970 + */ + fixed32 time = 4; + + /* + * TODO: REPLACE + */ + Position.LocSource location_source = 5; +} + +/* + * The on-disk saved channels + */ +message ChannelFile { + /* + * The channels our node knows about + */ + repeated Channel channels = 1; + + /* + * A version integer used to invalidate old save files when we make + * incompatible changes This integer is set at build time and is private to + * NodeDB.cpp in the device code. + */ + uint32 version = 2; +} + +/* + * TODO: REPLACE + */ +enum ScreenFonts { + /* + * TODO: REPLACE + */ + FONT_SMALL = 0; + + /* + * TODO: REPLACE + */ + FONT_MEDIUM = 1; + + /* + * TODO: REPLACE + */ + FONT_LARGE = 2; +} + +/* + * This can be used for customizing the firmware distribution. If populated, + * show a secondary bootup screen with custom logo and text for 2.5 seconds. + */ +message OEMStore { + /* + * The Logo width in Px + */ + uint32 oem_icon_width = 1; + + /* + * The Logo height in Px + */ + uint32 oem_icon_height = 2; + + /* + * The Logo in XBM bytechar format + */ + bytes oem_icon_bits = 3; + + /* + * Use this font for the OEM text. + */ + ScreenFonts oem_font = 4; + + /* + * Use this font for the OEM text. + */ + string oem_text = 5; + + /* + * The default device encryption key, 16 or 32 byte + */ + bytes oem_aes_key = 6; + + /* + * A Preset LocalConfig to apply during factory reset + */ + LocalConfig oem_local_config = 7; + + /* + * A Preset LocalModuleConfig to apply during factory reset + */ + LocalModuleConfig oem_local_module_config = 8; +} + +/* + * RemoteHardwarePins associated with a node + */ +message NodeRemoteHardwarePin { + /* + * The node_num exposing the available gpio pin + */ + uint32 node_num = 1; + + /* + * The the available gpio pin for usage with RemoteHardware module + */ + RemoteHardwarePin pin = 2; +} \ No newline at end of file diff --git a/src/protos/meshtastic/localonly.proto b/src/protos/meshtastic/localonly.proto new file mode 100644 index 0000000..9297dff --- /dev/null +++ b/src/protos/meshtastic/localonly.proto @@ -0,0 +1,135 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/config.proto"; +import "meshtastic/module_config.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "LocalOnlyProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * Protobuf structures common to apponly.proto and deviceonly.proto + * This is never sent over the wire, only for local use + */ + +message LocalConfig { + /* + * The part of the config that is specific to the Device + */ + Config.DeviceConfig device = 1; + + /* + * The part of the config that is specific to the GPS Position + */ + Config.PositionConfig position = 2; + + /* + * The part of the config that is specific to the Power settings + */ + Config.PowerConfig power = 3; + + /* + * The part of the config that is specific to the Wifi Settings + */ + Config.NetworkConfig network = 4; + + /* + * The part of the config that is specific to the Display + */ + Config.DisplayConfig display = 5; + + /* + * The part of the config that is specific to the Lora Radio + */ + Config.LoRaConfig lora = 6; + + /* + * The part of the config that is specific to the Bluetooth settings + */ + Config.BluetoothConfig bluetooth = 7; + + /* + * A version integer used to invalidate old save files when we make + * incompatible changes This integer is set at build time and is private to + * NodeDB.cpp in the device code. + */ + uint32 version = 8; +} + +message LocalModuleConfig { + /* + * The part of the config that is specific to the MQTT module + */ + ModuleConfig.MQTTConfig mqtt = 1; + + /* + * The part of the config that is specific to the Serial module + */ + ModuleConfig.SerialConfig serial = 2; + + /* + * The part of the config that is specific to the ExternalNotification module + */ + ModuleConfig.ExternalNotificationConfig external_notification = 3; + + /* + * The part of the config that is specific to the Store & Forward module + */ + ModuleConfig.StoreForwardConfig store_forward = 4; + + /* + * The part of the config that is specific to the RangeTest module + */ + ModuleConfig.RangeTestConfig range_test = 5; + + /* + * The part of the config that is specific to the Telemetry module + */ + ModuleConfig.TelemetryConfig telemetry = 6; + + /* + * The part of the config that is specific to the Canned Message module + */ + ModuleConfig.CannedMessageConfig canned_message = 7; + + /* + * The part of the config that is specific to the Audio module + */ + ModuleConfig.AudioConfig audio = 9; + + /* + * The part of the config that is specific to the Remote Hardware module + */ + ModuleConfig.RemoteHardwareConfig remote_hardware = 10; + + /* + * The part of the config that is specific to the Neighbor Info module + */ + ModuleConfig.NeighborInfoConfig neighbor_info = 11; + + /* + * The part of the config that is specific to the Ambient Lighting module + */ + ModuleConfig.AmbientLightingConfig ambient_lighting = 12; + + /* + * The part of the config that is specific to the Detection Sensor module + */ + ModuleConfig.DetectionSensorConfig detection_sensor = 13; + + /* + * Paxcounter Config + */ + ModuleConfig.PaxcounterConfig paxcounter = 14; + + /* + * A version integer used to invalidate old save files when we make + * incompatible changes This integer is set at build time and is private to + * NodeDB.cpp in the device code. + */ + uint32 version = 8; +} \ No newline at end of file diff --git a/src/protos/meshtastic/mesh.options b/src/protos/meshtastic/mesh.options new file mode 100644 index 0000000..aedfe99 --- /dev/null +++ b/src/protos/meshtastic/mesh.options @@ -0,0 +1,61 @@ +# options for nanopb +# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options + +*macaddr max_size:6 fixed_length:true # macaddrs +*id max_size:16 # node id strings + +*User.long_name max_size:40 +*User.short_name max_size:5 + +*RouteDiscovery.route max_count:8 + +# note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is +# outside of this envelope +*Data.payload max_size:237 + +*NodeInfo.channel int_size:8 +*NodeInfo.hops_away int_size:8 + +# Big enough for 1.2.28.568032c-d +*MyNodeInfo.firmware_version max_size:18 + +*MyNodeInfo.air_period_tx max_count:8 +*MyNodeInfo.air_period_rx max_count:8 + +# Note: the actual limit (because of header bytes) on the size of encrypted payloads is 251 bytes, but I use 256 +# here because we might need to fill with zeros for padding to encryption block size (16 bytes per block) +*MeshPacket.encrypted max_size:256 +*MeshPacket.payload_variant anonymous_oneof:true +*MeshPacket.hop_limit int_size:8 +*MeshPacket.hop_start int_size:8 +*MeshPacket.channel int_size:8 + +*QueueStatus.res int_size:8 +*QueueStatus.free int_size:8 +*QueueStatus.maxlen int_size:8 + +*ToRadio.payload_variant anonymous_oneof:true + +*FromRadio.payload_variant anonymous_oneof:true + +*Routing.variant anonymous_oneof:true + +*LogRecord.message max_size:64 +*LogRecord.source max_size:8 + +# MyMessage.name max_size:40 +# or fixed_length or fixed_count, or max_count + +#This value may want to be a few bytes smaller to compensate for the parent fields. +*Compressed.data max_size:237 + +*Waypoint.name max_size:30 +*Waypoint.description max_size:100 + +*NeighborInfo.neighbors max_count:10 + +*DeviceMetadata.firmware_version max_size:18 + +*MqttClientProxyMessage.topic max_size:60 +*MqttClientProxyMessage.data max_size:435 +*MqttClientProxyMessage.text max_size:435 diff --git a/src/protos/meshtastic/mesh.proto b/src/protos/meshtastic/mesh.proto new file mode 100644 index 0000000..76aa01b --- /dev/null +++ b/src/protos/meshtastic/mesh.proto @@ -0,0 +1,1564 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/channel.proto"; +import "meshtastic/config.proto"; +import "meshtastic/module_config.proto"; +import "meshtastic/portnums.proto"; +import "meshtastic/telemetry.proto"; +import "meshtastic/xmodem.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "MeshProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * a gps position + */ +message Position { + /* + * The new preferred location encoding, multiply by 1e-7 to get degrees + * in floating point + */ + sfixed32 latitude_i = 1; + + /* + * TODO: REPLACE + */ + sfixed32 longitude_i = 2; + + /* + * In meters above MSL (but see issue #359) + */ + int32 altitude = 3; + + /* + * This is usually not sent over the mesh (to save space), but it is sent + * from the phone so that the local device can set its time if it is sent over + * the mesh (because there are devices on the mesh without GPS or RTC). + * seconds since 1970 + */ + fixed32 time = 4; + + /* + * How the location was acquired: manual, onboard GPS, external (EUD) GPS + */ + enum LocSource { + /* + * TODO: REPLACE + */ + LOC_UNSET = 0; + + /* + * TODO: REPLACE + */ + LOC_MANUAL = 1; + + /* + * TODO: REPLACE + */ + LOC_INTERNAL = 2; + + /* + * TODO: REPLACE + */ + LOC_EXTERNAL = 3; + } + + /* + * TODO: REPLACE + */ + LocSource location_source = 5; + + /* + * How the altitude was acquired: manual, GPS int/ext, etc + * Default: same as location_source if present + */ + enum AltSource { + /* + * TODO: REPLACE + */ + ALT_UNSET = 0; + + /* + * TODO: REPLACE + */ + ALT_MANUAL = 1; + + /* + * TODO: REPLACE + */ + ALT_INTERNAL = 2; + + /* + * TODO: REPLACE + */ + ALT_EXTERNAL = 3; + + /* + * TODO: REPLACE + */ + ALT_BAROMETRIC = 4; + } + + /* + * TODO: REPLACE + */ + AltSource altitude_source = 6; + + /* + * Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds + */ + fixed32 timestamp = 7; + + /* + * Pos. timestamp milliseconds adjustment (rarely available or required) + */ + int32 timestamp_millis_adjust = 8; + + /* + * HAE altitude in meters - can be used instead of MSL altitude + */ + sint32 altitude_hae = 9; + + /* + * Geoidal separation in meters + */ + sint32 altitude_geoidal_separation = 10; + + /* + * Horizontal, Vertical and Position Dilution of Precision, in 1/100 units + * - PDOP is sufficient for most cases + * - for higher precision scenarios, HDOP and VDOP can be used instead, + * in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2)) + * TODO: REMOVE/INTEGRATE + */ + uint32 PDOP = 11; + + /* + * TODO: REPLACE + */ + uint32 HDOP = 12; + + /* + * TODO: REPLACE + */ + uint32 VDOP = 13; + + /* + * GPS accuracy (a hardware specific constant) in mm + * multiplied with DOP to calculate positional accuracy + * Default: "'bout three meters-ish" :) + */ + uint32 gps_accuracy = 14; + + /* + * Ground speed in m/s and True North TRACK in 1/100 degrees + * Clarification of terms: + * - "track" is the direction of motion (measured in horizontal plane) + * - "heading" is where the fuselage points (measured in horizontal plane) + * - "yaw" indicates a relative rotation about the vertical axis + * TODO: REMOVE/INTEGRATE + */ + uint32 ground_speed = 15; + + /* + * TODO: REPLACE + */ + uint32 ground_track = 16; + + /* + * GPS fix quality (from NMEA GxGGA statement or similar) + */ + uint32 fix_quality = 17; + + /* + * GPS fix type 2D/3D (from NMEA GxGSA statement) + */ + uint32 fix_type = 18; + + /* + * GPS "Satellites in View" number + */ + uint32 sats_in_view = 19; + + /* + * Sensor ID - in case multiple positioning sensors are being used + */ + uint32 sensor_id = 20; + + /* + * Estimated/expected time (in seconds) until next update: + * - if we update at fixed intervals of X seconds, use X + * - if we update at dynamic intervals (based on relative movement etc), + * but "AT LEAST every Y seconds", use Y + */ + uint32 next_update = 21; + + /* + * A sequence number, incremented with each Position message to help + * detect lost updates if needed + */ + uint32 seq_number = 22; + + /* + * Indicates the bits of precision set by the sending node + */ + uint32 precision_bits = 23; +} + +/* + * Note: these enum names must EXACTLY match the string used in the device + * bin/build-all.sh script. + * Because they will be used to find firmware filenames in the android app for OTA updates. + * To match the old style filenames, _ is converted to -, p is converted to . + */ +enum HardwareModel { + /* + * TODO: REPLACE + */ + UNSET = 0; + + /* + * TODO: REPLACE + */ + TLORA_V2 = 1; + + /* + * TODO: REPLACE + */ + TLORA_V1 = 2; + + /* + * TODO: REPLACE + */ + TLORA_V2_1_1P6 = 3; + + /* + * TODO: REPLACE + */ + TBEAM = 4; + + /* + * The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 + * (see HELTEC_V2 for the new version). + */ + HELTEC_V2_0 = 5; + + /* + * TODO: REPLACE + */ + TBEAM_V0P7 = 6; + + /* + * TODO: REPLACE + */ + T_ECHO = 7; + + /* + * TODO: REPLACE + */ + TLORA_V1_1P3 = 8; + + /* + * TODO: REPLACE + */ + RAK4631 = 9; + + /* + * The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. + * Sadly they did not update anything on the silkscreen to identify this board + */ + HELTEC_V2_1 = 10; + + /* + * Ancient heltec WiFi_Lora_32 board + */ + HELTEC_V1 = 11; + + /* + * New T-BEAM with ESP32-S3 CPU + */ + LILYGO_TBEAM_S3_CORE = 12; + + /* + * RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ + */ + RAK11200 = 13; + + /* + * B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano + */ + NANO_G1 = 14; + + /* + * TODO: REPLACE + */ + TLORA_V2_1_1P8 = 15; + + /* + * TODO: REPLACE + */ + TLORA_T3_S3 = 16; + + /* + * B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer + */ + NANO_G1_EXPLORER = 17; + + /* + * B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra + */ + NANO_G2_ULTRA = 18; + + /* + * LoRAType device: https://loratype.org/ + */ + LORA_TYPE = 19; + + /* + * B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station + */ + STATION_G1 = 25; + + /* + * RAK11310 (RP2040 + SX1262) + */ + RAK11310 = 26; + + /* + * Makerfabs SenseLoRA Receiver (RP2040 + RFM96) + */ + SENSELORA_RP2040 = 27; + + /* + * Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) + */ + SENSELORA_S3 = 28; + + /* + * Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone + */ + CANARYONE = 29; + + /* + * Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm + */ + RP2040_LORA = 30; + + /* + * B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 + */ + STATION_G2 = 31; + + /* + * --------------------------------------------------------------------------- + * Less common/prototype boards listed here (needs one more byte over the air) + * --------------------------------------------------------------------------- + */ + LORA_RELAY_V1 = 32; + + /* + * TODO: REPLACE + */ + NRF52840DK = 33; + + /* + * TODO: REPLACE + */ + PPR = 34; + + /* + * TODO: REPLACE + */ + GENIEBLOCKS = 35; + + /* + * TODO: REPLACE + */ + NRF52_UNKNOWN = 36; + + /* + * TODO: REPLACE + */ + PORTDUINO = 37; + + /* + * The simulator built into the android app + */ + ANDROID_SIM = 38; + + /* + * Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics + */ + DIY_V1 = 39; + + /* + * nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ + */ + NRF52840_PCA10059 = 40; + + /* + * Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 + */ + DR_DEV = 41; + + /* + * M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ + */ + M5STACK = 42; + + /* + * New Heltec LoRA32 with ESP32-S3 CPU + */ + HELTEC_V3 = 43; + + /* + * New Heltec Wireless Stick Lite with ESP32-S3 CPU + */ + HELTEC_WSL_V3 = 44; + + /* + * New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU + */ + BETAFPV_2400_TX = 45; + + /* + * BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU + */ + BETAFPV_900_NANO_TX = 46; + + /* + * Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module + */ + RPI_PICO = 47; + + /* + * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT + * Newer V1.1, version is written on the PCB near the display. + */ + HELTEC_WIRELESS_TRACKER = 48; + + /* + * Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display + */ + HELTEC_WIRELESS_PAPER = 49; + + /* + * LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display + */ + T_DECK = 50; + + /* + * LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display + */ + T_WATCH_S3 = 51; + + /* + * Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display + */ + PICOMPUTER_S3 = 52; + + /* + * Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa + */ + HELTEC_HT62 = 53; + + /* + * EBYTE SPI LoRa module and ESP32-S3 + */ + EBYTE_ESP32_S3 = 54; + + /* + * Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink + */ + ESP32_S3_PICO = 55; + + /* + * CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom + * Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible + * with one cut and one jumper Meshtastic works + */ + CHATTER_2 = 56; + + /* + * Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display + * Older "V1.0" Variant, has no "version sticker" + * E-Ink model is DEPG0213BNS800 + * Tab on the screen protector is RED + * Flex connector marking is FPC-7528B + */ + HELTEC_WIRELESS_PAPER_V1_0 = 57; + + /* + * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT + * Older "V1.0" Variant + */ + HELTEC_WIRELESS_TRACKER_V1_0 = 58; + + /* + * ------------------------------------------------------------------------------------------------------------------------------------------ + * Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. + * ------------------------------------------------------------------------------------------------------------------------------------------ + */ + PRIVATE_HW = 255; +} + +/* + * Broadcast when a newly powered mesh node wants to find a node num it can use + * Sent from the phone over bluetooth to set the user id for the owner of this node. + * Also sent from nodes to each other when a new node signs on (so all clients can have this info) + * The algorithm is as follows: + * when a node starts up, it broadcasts their user and the normal flow is for all + * other nodes to reply with their User as well (so the new node can build its nodedb) + * If a node ever receives a User (not just the first broadcast) message where + * the sender node number equals our node number, that indicates a collision has + * occurred and the following steps should happen: + * If the receiving node (that was already in the mesh)'s macaddr is LOWER than the + * new User who just tried to sign in: it gets to keep its nodenum. + * We send a broadcast message of OUR User (we use a broadcast so that the other node can + * receive our message, considering we have the same id - it also serves to let + * observers correct their nodedb) - this case is rare so it should be okay. + * If any node receives a User where the macaddr is GTE than their local macaddr, + * they have been vetoed and should pick a new random nodenum (filtering against + * whatever it knows about the nodedb) and rebroadcast their User. + * A few nodenums are reserved and will never be requested: + * 0xff - broadcast + * 0 through 3 - for future use + */ +message User { + /* + * A globally unique ID string for this user. + * In the case of Signal that would mean +16504442323, for the default macaddr derived id it would be !<8 hexidecimal bytes>. + * Note: app developers are encouraged to also use the following standard + * node IDs "^all" (for broadcast), "^local" (for the locally connected node) + */ + string id = 1; + + /* + * A full name for this user, i.e. "Kevin Hester" + */ + string long_name = 2; + + /* + * A VERY short name, ideally two characters. + * Suitable for a tiny OLED screen + */ + string short_name = 3; + + /* + * Deprecated in Meshtastic 2.1.x + * This is the addr of the radio. + * Not populated by the phone, but added by the esp32 when broadcasting + */ + bytes macaddr = 4 [deprecated = true]; + + /* + * TBEAM, HELTEC, etc... + * Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + * Apps will still need the string here for older builds + * (so OTA update can find the right image), but if the enum is available it will be used instead. + */ + HardwareModel hw_model = 5; + + /* + * In some regions Ham radio operators have different bandwidth limitations than others. + * If this user is a licensed operator, set this flag. + * Also, "long_name" should be their licence number. + */ + bool is_licensed = 6; + + /* + * Indicates that the user's role in the mesh + */ + Config.DeviceConfig.Role role = 7; +} + +/* + * A message used in our Dynamic Source Routing protocol (RFC 4728 based) + */ +message RouteDiscovery { + /* + * The list of nodenums this packet has visited so far + */ + repeated fixed32 route = 1; +} + +/* + * A Routing control Data packet handled by the routing module + */ +message Routing { + /* + * A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide + * details on the type of failure). + */ + enum Error { + /* + * This message is not a failure + */ + NONE = 0; + + /* + * Our node doesn't have a route to the requested destination anymore. + */ + NO_ROUTE = 1; + + /* + * We received a nak while trying to forward on your behalf + */ + GOT_NAK = 2; + + /* + * TODO: REPLACE + */ + TIMEOUT = 3; + + /* + * No suitable interface could be found for delivering this packet + */ + NO_INTERFACE = 4; + + /* + * We reached the max retransmission count (typically for naive flood routing) + */ + MAX_RETRANSMIT = 5; + + /* + * No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) + */ + NO_CHANNEL = 6; + + /* + * The packet was too big for sending (exceeds interface MTU after encoding) + */ + TOO_LARGE = 7; + + /* + * The request had want_response set, the request reached the destination node, but no service on that node wants to send a response + * (possibly due to bad channel permissions) + */ + NO_RESPONSE = 8; + + /* + * Cannot send currently because duty cycle regulations will be violated. + */ + DUTY_CYCLE_LIMIT = 9; + + /* + * The application layer service on the remote node received your request, but considered your request somehow invalid + */ + BAD_REQUEST = 32; + + /* + * The application layer service on the remote node received your request, but considered your request not authorized + * (i.e you did not send the request on the required bound channel) + */ + NOT_AUTHORIZED = 33; + } + + oneof variant { + /* + * A route request going from the requester + */ + RouteDiscovery route_request = 1; + + /* + * A route reply + */ + RouteDiscovery route_reply = 2; + + /* + * A failure in delivering a message (usually used for routing control messages, but might be provided + * in addition to ack.fail_id to provide details on the type of failure). + */ + Error error_reason = 3; + } +} + +/* + * (Formerly called SubPacket) + * The payload portion fo a packet, this is the actual bytes that are sent + * inside a radio packet (because from/to are broken out by the comms library) + */ +message Data { + /* + * Formerly named typ and of type Type + */ + PortNum portnum = 1; + + /* + * TODO: REPLACE + */ + bytes payload = 2; + + /* + * Not normally used, but for testing a sender can request that recipient + * responds in kind (i.e. if it received a position, it should unicast back it's position). + * Note: that if you set this on a broadcast you will receive many replies. + */ + bool want_response = 3; + + /* + * The address of the destination node. + * This field is is filled in by the mesh radio device software, application + * layer software should never need it. + * RouteDiscovery messages _must_ populate this. + * Other message types might need to if they are doing multihop routing. + */ + fixed32 dest = 4; + + /* + * The address of the original sender for this message. + * This field should _only_ be populated for reliable multihop packets (to keep + * packets small). + */ + fixed32 source = 5; + + /* + * Only used in routing or response messages. + * Indicates the original message ID that this message is reporting failure on. (formerly called original_id) + */ + fixed32 request_id = 6; + + /* + * If set, this message is intened to be a reply to a previously sent message with the defined id. + */ + fixed32 reply_id = 7; + + /* + * Defaults to false. If true, then what is in the payload should be treated as an emoji like giving + * a message a heart or poop emoji. + */ + fixed32 emoji = 8; +} + +/* + * Waypoint message, used to share arbitrary locations across the mesh + */ +message Waypoint { + /* + * Id of the waypoint + */ + uint32 id = 1; + + /* + * latitude_i + */ + sfixed32 latitude_i = 2; + + /* + * longitude_i + */ + sfixed32 longitude_i = 3; + + /* + * Time the waypoint is to expire (epoch) + */ + uint32 expire = 4; + + /* + * If greater than zero, treat the value as a nodenum only allowing them to update the waypoint. + * If zero, the waypoint is open to be edited by any member of the mesh. + */ + uint32 locked_to = 5; + + /* + * Name of the waypoint - max 30 chars + */ + string name = 6; + + /* + * Description of the waypoint - max 100 chars + */ + string description = 7; + + /* + * Designator icon for the waypoint in the form of a unicode emoji + */ + fixed32 icon = 8; +} + +/* + * This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server + */ + message MqttClientProxyMessage { + /* + * The MQTT topic this message will be sent /received on + */ + string topic = 1; + + /* + * The actual service envelope payload or text for mqtt pub / sub + */ + oneof payload_variant { + /* + * Bytes + */ + bytes data = 2; + + /* + * Text + */ + string text = 3; + } + + /* + * Whether the message should be retained (or not) + */ + bool retained = 4; +} + +/* + * A packet envelope sent/received over the mesh + * only payload_variant is sent in the payload portion of the LORA packet. + * The other fields are either not sent at all, or sent in the special 16 byte LORA header. + */ +message MeshPacket { + /* + * The priority of this message for sending. + * Higher priorities are sent first (when managing the transmit queue). + * This field is never sent over the air, it is only used internally inside of a local device node. + * API clients (either on the local node or connected directly to the node) + * can set this parameter if necessary. + * (values must be <= 127 to keep protobuf field to one byte in size. + * Detailed background on this field: + * I noticed a funny side effect of lora being so slow: Usually when making + * a protocol there isn’t much need to use message priority to change the order + * of transmission (because interfaces are fairly fast). + * But for lora where packets can take a few seconds each, it is very important + * to make sure that critical packets are sent ASAP. + * In the case of meshtastic that means we want to send protocol acks as soon as possible + * (to prevent unneeded retransmissions), we want routing messages to be sent next, + * then messages marked as reliable and finally 'background' packets like periodic position updates. + * So I bit the bullet and implemented a new (internal - not sent over the air) + * field in MeshPacket called 'priority'. + * And the transmission queue in the router object is now a priority queue. + */ + enum Priority { + /* + * Treated as Priority.DEFAULT + */ + UNSET = 0; + + /* + * TODO: REPLACE + */ + MIN = 1; + + /* + * Background position updates are sent with very low priority - + * if the link is super congested they might not go out at all + */ + BACKGROUND = 10; + + /* + * This priority is used for most messages that don't have a priority set + */ + DEFAULT = 64; + + /* + * If priority is unset but the message is marked as want_ack, + * assume it is important and use a slightly higher priority + */ + RELIABLE = 70; + + /* + * Ack/naks are sent with very high priority to ensure that retransmission + * stops as soon as possible + */ + ACK = 120; + + /* + * TODO: REPLACE + */ + MAX = 127; + } + + /* + * Identify if this is a delayed packet + */ + enum Delayed { + /* + * If unset, the message is being sent in real time. + */ + NO_DELAY = 0; + + /* + * The message is delayed and was originally a broadcast + */ + DELAYED_BROADCAST = 1; + + /* + * The message is delayed and was originally a direct message + */ + DELAYED_DIRECT = 2; + } + + /* + * The sending node number. + * Note: Our crypto implementation uses this field as well. + * See [crypto](/docs/overview/encryption) for details. + */ + fixed32 from = 1; + + /* + * The (immediate) destination for this packet + */ + fixed32 to = 2; + + /* + * (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on. + * If unset, packet was on the primary channel. + * A particular node might know only a subset of channels in use on the mesh. + * Therefore channel_index is inherently a local concept and meaningless to send between nodes. + * Very briefly, while sending and receiving deep inside the device Router code, this field instead + * contains the 'channel hash' instead of the index. + * This 'trick' is only used while the payload_variant is an 'encrypted'. + */ + uint32 channel = 3; + + /* + * Internally to the mesh radios we will route SubPackets encrypted per [this](docs/developers/firmware/encryption). + * However, when a particular node has the correct + * key to decode a particular packet, it will decode the payload into a SubPacket protobuf structure. + * Software outside of the device nodes will never encounter a packet where + * "decoded" is not populated (i.e. any encryption/decryption happens before reaching the applications) + * The numeric IDs for these fields were selected to keep backwards compatibility with old applications. + */ + + oneof payload_variant { + /* + * TODO: REPLACE + */ + Data decoded = 4; + + /* + * TODO: REPLACE + */ + bytes encrypted = 5; + } + + /* + * A unique ID for this packet. + * Always 0 for no-ack packets or non broadcast packets (and therefore take zero bytes of space). + * Otherwise a unique ID for this packet, useful for flooding algorithms. + * ID only needs to be unique on a _per sender_ basis, and it only + * needs to be unique for a few minutes (long enough to last for the length of + * any ACK or the completion of a mesh broadcast flood). + * Note: Our crypto implementation uses this id as well. + * See [crypto](/docs/overview/encryption) for details. + */ + fixed32 id = 6; + + /* + * The time this message was received by the esp32 (secs since 1970). + * Note: this field is _never_ sent on the radio link itself (to save space) Times + * are typically not sent over the mesh, but they will be added to any Packet + * (chain of SubPacket) sent to the phone (so the phone can know exact time of reception) + */ + fixed32 rx_time = 7; + + /* + * *Never* sent over the radio links. + * Set during reception to indicate the SNR of this packet. + * Used to collect statistics on current link quality. + */ + float rx_snr = 8; + + /* + * If unset treated as zero (no forwarding, send to adjacent nodes only) + * if 1, allow hopping through one node, etc... + * For our usecase real world topologies probably have a max of about 3. + * This field is normally placed into a few of bits in the header. + */ + uint32 hop_limit = 9; + + /* + * This packet is being sent as a reliable message, we would prefer it to arrive at the destination. + * We would like to receive a ack packet in response. + * Broadcasts messages treat this flag specially: Since acks for broadcasts would + * rapidly flood the channel, the normal ack behavior is suppressed. + * Instead, the original sender listens to see if at least one node is rebroadcasting this packet (because naive flooding algorithm). + * If it hears that the odds (given typical LoRa topologies) the odds are very high that every node should eventually receive the message. + * So FloodingRouter.cpp generates an implicit ack which is delivered to the original sender. + * If after some time we don't hear anyone rebroadcast our packet, we will timeout and retransmit, using the regular resend logic. + * Note: This flag is normally sent in a flag bit in the header when sent over the wire + */ + bool want_ack = 10; + + /* + * The priority of this message for sending. + * See MeshPacket.Priority description for more details. + */ + Priority priority = 11; + + /* + * rssi of received packet. Only sent to phone for dispay purposes. + */ + int32 rx_rssi = 12; + + /* + * Describe if this message is delayed + */ + Delayed delayed = 13 [deprecated = true]; + + /* + * Describes whether this packet passed via MQTT somewhere along the path it currently took. + */ + bool via_mqtt = 14; + + /* + * Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. + * When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. + */ + uint32 hop_start = 15; +} + +/* + * Shared constants between device and phone + */ +enum Constants { + /* + * First enum must be zero, and we are just using this enum to + * pass int constants between two very different environments + */ + ZERO = 0; + + /* + * From mesh.options + * note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is + * outside of this envelope + */ + DATA_PAYLOAD_LEN = 237; +} + +/* + * The bluetooth to device link: + * Old BTLE protocol docs from TODO, merge in above and make real docs... + * use protocol buffers, and NanoPB + * messages from device to phone: + * POSITION_UPDATE (..., time) + * TEXT_RECEIVED(from, text, time) + * OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) + * messages from phone to device: + * SET_MYID(id, human readable long, human readable short) (send down the unique ID + * string used for this node, a human readable string shown for that id, and a very + * short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) + * (for signal messages or other applications) SEND_TEXT(dest, text) Get all + * nodes() (returns list of nodes, with full info, last time seen, loc, battery + * level etc) SET_CONFIG (switches device to a new set of radio params and + * preshared key, drops all existing nodes, force our node to rejoin this new group) + * Full information about a node on the mesh + */ +message NodeInfo { + /* + * The node number + */ + uint32 num = 1; + + /* + * The user info for this node + */ + User user = 2; + + /* + * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + * Position.time now indicates the last time we received a POSITION from that node. + */ + Position position = 3; + + /* + * Returns the Signal-to-noise ratio (SNR) of the last received message, + * as measured by the receiver. Return SNR of the last received message in dB + */ + float snr = 4; + + /* + * TODO: REMOVE/INTEGRATE + * Returns the last measured frequency error. + * The LoRa receiver estimates the frequency offset between the receiver + * center frequency and that of the received LoRa signal. This function + * returns the estimates offset (in Hz) of the last received message. + * Caution: this measurement is not absolute, but is measured relative to the + * local receiver's oscillator. Apparent errors may be due to the + * transmitter, the receiver or both. \return The estimated center frequency + * offset in Hz of the last received message. + * int32 frequency_error = 6; + * enum RouteState { + * Invalid = 0; + * Discovering = 1; + * Valid = 2; + * } + * Not needed? + * RouteState route = 4; + */ + + /* + * TODO: REMOVE/INTEGRATE + * Not currently used (till full DSR deployment?) Our current preferred node node for routing - might be the same as num if + * we are adjacent Or zero if we don't yet know a route to this node. + * fixed32 next_hop = 5; + */ + + /* + * Set to indicate the last time we received a packet from this node + */ + fixed32 last_heard = 5; + /* + * The latest device metrics for the node. + */ + DeviceMetrics device_metrics = 6; + + /* + * local channel index we heard that node on. Only populated if its not the default channel. + */ + uint32 channel = 7; + + /* + * True if we witnessed the node over MQTT instead of LoRA transport + */ + bool via_mqtt = 8; + + /* + * Number of hops away from us this node is (0 if adjacent) + */ + uint32 hops_away = 9; +} + +/* + * Error codes for critical errors + * The device might report these fault codes on the screen. + * If you encounter a fault code, please post on the meshtastic.discourse.group + * and we'll try to help. + */ +enum CriticalErrorCode { + /* + * TODO: REPLACE + */ + NONE = 0; + + /* + * A software bug was detected while trying to send lora + */ + TX_WATCHDOG = 1; + + /* + * A software bug was detected on entry to sleep + */ + SLEEP_ENTER_WAIT = 2; + + /* + * No Lora radio hardware could be found + */ + NO_RADIO = 3; + + /* + * Not normally used + */ + UNSPECIFIED = 4; + + /* + * We failed while configuring a UBlox GPS + */ + UBLOX_UNIT_FAILED = 5; + + /* + * This board was expected to have a power management chip and it is missing or broken + */ + NO_AXP192 = 6; + + /* + * The channel tried to set a radio setting which is not supported by this chipset, + * radio comms settings are now undefined. + */ + INVALID_RADIO_SETTING = 7; + + /* + * Radio transmit hardware failure. We sent data to the radio chip, but it didn't + * reply with an interrupt. + */ + TRANSMIT_FAILED = 8; + + /* + * We detected that the main CPU voltage dropped below the minimum acceptable value + */ + BROWNOUT = 9; + + /* Selftest of SX1262 radio chip failed */ + SX1262_FAILURE = 10; + + /* + * A (likely software but possibly hardware) failure was detected while trying to send packets. + * If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug + */ + RADIO_SPI_BUG = 11; +} + +/* + * Unique local debugging info for this node + * Note: we don't include position or the user info, because that will come in the + * Sent to the phone in response to WantNodes. + */ +message MyNodeInfo { + /* + * Tells the phone what our node number is, default starting value is + * lowbyte of macaddr, but it will be fixed if that is already in use + */ + uint32 my_node_num = 1; + + /* + * The total number of reboots this node has ever encountered + * (well - since the last time we discarded preferences) + */ + uint32 reboot_count = 8; + + /* + * The minimum app version that can talk to this device. + * Phone/PC apps should compare this to their build number and if too low tell the user they must update their app + */ + uint32 min_app_version = 11; +} + +/* + * Debug output from the device. + * To minimize the size of records inside the device code, if a time/source/level is not set + * on the message it is assumed to be a continuation of the previously sent message. + * This allows the device code to use fixed maxlen 64 byte strings for messages, + * and then extend as needed by emitting multiple records. + */ +message LogRecord { + /* + * Log levels, chosen to match python logging conventions. + */ + enum Level { + /* + * Log levels, chosen to match python logging conventions. + */ + UNSET = 0; + + /* + * Log levels, chosen to match python logging conventions. + */ + CRITICAL = 50; + + /* + * Log levels, chosen to match python logging conventions. + */ + ERROR = 40; + + /* + * Log levels, chosen to match python logging conventions. + */ + WARNING = 30; + + /* + * Log levels, chosen to match python logging conventions. + */ + INFO = 20; + + /* + * Log levels, chosen to match python logging conventions. + */ + DEBUG = 10; + + /* + * Log levels, chosen to match python logging conventions. + */ + TRACE = 5; + } + + /* + * Log levels, chosen to match python logging conventions. + */ + string message = 1; + + /* + * Seconds since 1970 - or 0 for unknown/unset + */ + fixed32 time = 2; + + /* + * Usually based on thread name - if known + */ + string source = 3; + + /* + * Not yet set + */ + Level level = 4; +} + +message QueueStatus { + /* Last attempt to queue status, ErrorCode */ + int32 res = 1; + + /* Free entries in the outgoing queue */ + uint32 free = 2; + + /* Maximum entries in the outgoing queue */ + uint32 maxlen = 3; + + /* What was mesh packet id that generated this response? */ + uint32 mesh_packet_id = 4; +} + +/* + * Packets from the radio to the phone will appear on the fromRadio characteristic. + * It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? + * It will sit in that descriptor until consumed by the phone, + * at which point the next item in the FIFO will be populated. + */ +message FromRadio { + /* + * The packet id, used to allow the phone to request missing read packets from the FIFO, + * see our bluetooth docs + */ + uint32 id = 1; + + /* + * Log levels, chosen to match python logging conventions. + */ + oneof payload_variant { + /* + * Log levels, chosen to match python logging conventions. + */ + MeshPacket packet = 2; + + /* + * Tells the phone what our node number is, can be -1 if we've not yet joined a mesh. + * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + */ + MyNodeInfo my_info = 3; + + /* + * One packet is sent for each node in the on radio DB + * starts over with the first node in our DB + */ + NodeInfo node_info = 4; + + /* + * Include a part of the config (was: RadioConfig radio) + */ + Config config = 5; + + /* + * Set to send debug console output over our protobuf stream + */ + LogRecord log_record = 6; + + /* + * Sent as true once the device has finished sending all of the responses to want_config + * recipient should check if this ID matches our original request nonce, if + * not, it means your config responses haven't started yet. + * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + */ + uint32 config_complete_id = 7; + + /* + * Sent to tell clients the radio has just rebooted. + * Set to true if present. + * Not used on all transports, currently just used for the serial console. + * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + */ + bool rebooted = 8; + + /* + * Include module config + */ + ModuleConfig moduleConfig = 9; + + /* + * One packet is sent for each channel + */ + Channel channel = 10; + + /* + * Queue status info + */ + QueueStatus queueStatus = 11; + + /* + * File Transfer Chunk + */ + XModem xmodemPacket = 12; + + /* + * Device metadata message + */ + DeviceMetadata metadata = 13; + + /* + * MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) + */ + MqttClientProxyMessage mqttClientProxyMessage = 14; + } +} + +/* + * Packets/commands to the radio will be written (reliably) to the toRadio characteristic. + * Once the write completes the phone can assume it is handled. + */ +message ToRadio { + /* + * Log levels, chosen to match python logging conventions. + */ + oneof payload_variant { + /* + * Send this packet on the mesh + */ + MeshPacket packet = 1; + + /* + * Phone wants radio to send full node db to the phone, This is + * typically the first packet sent to the radio when the phone gets a + * bluetooth connection. The radio will respond by sending back a + * MyNodeInfo, a owner, a radio config and a series of + * FromRadio.node_infos, and config_complete + * the integer you write into this field will be reported back in the + * config_complete_id response this allows clients to never be confused by + * a stale old partially sent config. + */ + uint32 want_config_id = 3; + + /* + * Tell API server we are disconnecting now. + * This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. + * (Sending this message is optional for clients) + */ + bool disconnect = 4; + + /* + * File Transfer Chunk + */ + + XModem xmodemPacket = 5; + + /* + * MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) + */ + MqttClientProxyMessage mqttClientProxyMessage = 6; + } +} + +/* + * Compressed message payload + */ +message Compressed { + /* + * PortNum to determine the how to handle the compressed payload. + */ + PortNum portnum = 1; + + /* + * Compressed data. + */ + bytes data = 2; +} + +/* + * Full info on edges for a single node + */ +message NeighborInfo { + /* + * The node ID of the node sending info on its neighbors + */ + uint32 node_id = 1; + /* + * Field to pass neighbor info for the next sending cycle + */ + uint32 last_sent_by_id = 2; + + /* + * Broadcast interval of the represented node (in seconds) + */ + uint32 node_broadcast_interval_secs = 3; + /* + * The list of out edges from this node + */ + repeated Neighbor neighbors = 4; +} + +/* + * A single edge in the mesh + */ +message Neighbor { + /* + * Node ID of neighbor + */ + uint32 node_id = 1; + + /* + * SNR of last heard message + */ + float snr = 2; + + /* + * Reception time (in secs since 1970) of last message that was last sent by this ID. + * Note: this is for local storage only and will not be sent out over the mesh. + */ + fixed32 last_rx_time = 3; + + /* + * Broadcast interval of this neighbor (in seconds). + * Note: this is for local storage only and will not be sent out over the mesh. + */ + uint32 node_broadcast_interval_secs = 4; +} + +/* + * Device metadata response + */ +message DeviceMetadata { + /* + * Device firmware version string + */ + string firmware_version = 1; + + /* + * Device state version + */ + uint32 device_state_version = 2; + + /* + * Indicates whether the device can shutdown CPU natively or via power management chip + */ + bool canShutdown = 3; + + /* + * Indicates that the device has native wifi capability + */ + bool hasWifi = 4; + + /* + * Indicates that the device has native bluetooth capability + */ + bool hasBluetooth = 5; + + /* + * Indicates that the device has an ethernet peripheral + */ + bool hasEthernet = 6; + + /* + * Indicates that the device's role in the mesh + */ + Config.DeviceConfig.Role role = 7; + + /* + * Indicates the device's current enabled position flags + */ + uint32 position_flags = 8; + + /* + * Device hardware model + */ + HardwareModel hw_model = 9; + + /* + * Has Remote Hardware enabled + */ + bool hasRemoteHardware = 10; +} \ No newline at end of file diff --git a/src/protos/meshtastic/module_config.options b/src/protos/meshtastic/module_config.options new file mode 100644 index 0000000..fdc46d5 --- /dev/null +++ b/src/protos/meshtastic/module_config.options @@ -0,0 +1,28 @@ +*CannedMessageConfig.allow_input_source max_size:16 + +*MQTTConfig.address max_size:64 +*MQTTConfig.username max_size:64 +*MQTTConfig.password max_size:64 +*MQTTConfig.root max_size:16 + +*AudioConfig.ptt_pin int_size:8 +*AudioConfig.i2s_ws int_size:8 +*AudioConfig.i2s_sd int_size:8 +*AudioConfig.i2s_din int_size:8 +*AudioConfig.i2s_sck int_size:8 + +*ExternalNotificationConfig.output_vibra int_size:8 +*ExternalNotificationConfig.output_buzzer int_size:8 +*ExternalNotificationConfig.nag_timeout int_size:16 + +*RemoteHardwareConfig.available_pins max_count:4 +*RemoteHardwarePin.name max_size:15 +*RemoteHardwarePin.gpio_pin int_size:8 + +*AmbientLightingConfig.current int_size:8 +*AmbientLightingConfig.red int_size:8 +*AmbientLightingConfig.green int_size:8 +*AmbientLightingConfig.blue int_size:8 + +*DetectionSensorConfig.monitor_pin int_size:8 +*DetectionSensorConfig.name max_size:20 \ No newline at end of file diff --git a/src/protos/meshtastic/module_config.proto b/src/protos/meshtastic/module_config.proto new file mode 100644 index 0000000..f2c2805 --- /dev/null +++ b/src/protos/meshtastic/module_config.proto @@ -0,0 +1,801 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "ModuleConfigProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * Module Config + */ +message ModuleConfig { + /* + * MQTT Client Config + */ + message MQTTConfig { + /* + * If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as + * is_uplink_enabled or is_downlink_enabled. + */ + bool enabled = 1; + + /* + * The server to use for our MQTT global message gateway feature. + * If not set, the default server will be used + */ + string address = 2; + + /* + * MQTT username to use (most useful for a custom MQTT server). + * If using a custom server, this will be honoured even if empty. + * If using the default server, this will only be honoured if set, otherwise the device will use the default username + */ + string username = 3; + + /* + * MQTT password to use (most useful for a custom MQTT server). + * If using a custom server, this will be honoured even if empty. + * If using the default server, this will only be honoured if set, otherwise the device will use the default password + */ + string password = 4; + + /* + * Whether to send encrypted or decrypted packets to MQTT. + * This parameter is only honoured if you also set server + * (the default official mqtt.meshtastic.org server can handle encrypted packets) + * Decrypted packets may be useful for external systems that want to consume meshtastic packets + */ + bool encryption_enabled = 5; + + /* + * Whether to send / consume json packets on MQTT + */ + bool json_enabled = 6; + + /* + * If true, we attempt to establish a secure connection using TLS + */ + bool tls_enabled = 7; + + /* + * The root topic to use for MQTT messages. Default is "msh". + * This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs + */ + string root = 8; + + /* + * If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection + */ + bool proxy_to_client_enabled = 9; + + /* + * If true, we will periodically report unencrypted information about our node to a map via MQTT + */ + bool map_reporting_enabled = 10; + + /* + * Settings for reporting information about our node to a map via MQTT + */ + MapReportSettings map_report_settings = 11; + } + + /* + * Settings for reporting unencrypted information about our node to a map via MQTT + */ + message MapReportSettings { + /* + * How often we should report our info to the map (in seconds) + */ + uint32 publish_interval_secs = 1; + + /* + * Bits of precision for the location sent (default of 32 is full precision). + */ + uint32 position_precision = 2; + } + + /* + * RemoteHardwareModule Config + */ + message RemoteHardwareConfig { + /* + * Whether the Module is enabled + */ + bool enabled = 1; + + /* + * Whether the Module allows consumers to read / write to pins not defined in available_pins + */ + bool allow_undefined_pin_access = 2; + + /* + * Exposes the available pins to the mesh for reading and writing + */ + repeated RemoteHardwarePin available_pins = 3; + } + + /* + * NeighborInfoModule Config + */ + message NeighborInfoConfig { + /* + * Whether the Module is enabled + */ + bool enabled = 1; + + /* + * Interval in seconds of how often we should try to send our + * Neighbor Info to the mesh + */ + uint32 update_interval = 2; + } + + /* + * Detection Sensor Module Config + */ + message DetectionSensorConfig { + /* + * Whether the Module is enabled + */ + bool enabled = 1; + + /* + * Interval in seconds of how often we can send a message to the mesh when a state change is detected + */ + uint32 minimum_broadcast_secs = 2; + + /* + * Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes + * When set to 0, only state changes will be broadcasted + * Works as a sort of status heartbeat for peace of mind + */ + uint32 state_broadcast_secs = 3; + /* + * Send ASCII bell with alert message + * Useful for triggering ext. notification on bell + */ + bool send_bell = 4; + + /* + * Friendly name used to format message sent to mesh + * Example: A name "Motion" would result in a message "Motion detected" + * Maximum length of 20 characters + */ + string name = 5; + + /* + * GPIO pin to monitor for state changes + */ + uint32 monitor_pin = 6; + + /* + * Whether or not the GPIO pin state detection is triggered on HIGH (1) + * Otherwise LOW (0) + */ + bool detection_triggered_high = 7; + + /* + * Whether or not use INPUT_PULLUP mode for GPIO pin + * Only applicable if the board uses pull-up resistors on the pin + */ + bool use_pullup = 8; + } + + /* + * Audio Config for codec2 voice + */ + message AudioConfig { + /* + * Baudrate for codec2 voice + */ + enum Audio_Baud { + CODEC2_DEFAULT = 0; + CODEC2_3200 = 1; + CODEC2_2400 = 2; + CODEC2_1600 = 3; + CODEC2_1400 = 4; + CODEC2_1300 = 5; + CODEC2_1200 = 6; + CODEC2_700 = 7; + CODEC2_700B = 8; + } + + /* + * Whether Audio is enabled + */ + bool codec2_enabled = 1; + + /* + * PTT Pin + */ + uint32 ptt_pin = 2; + + /* + * The audio sample rate to use for codec2 + */ + Audio_Baud bitrate = 3; + + /* + * I2S Word Select + */ + uint32 i2s_ws = 4; + + /* + * I2S Data IN + */ + uint32 i2s_sd = 5; + + /* + * I2S Data OUT + */ + uint32 i2s_din = 6; + + /* + * I2S Clock + */ + uint32 i2s_sck = 7; + } + + /* + * Config for the Paxcounter Module + */ + message PaxcounterConfig { + /* + * Enable the Paxcounter Module + */ + bool enabled = 1; + + /* + * Interval in seconds of how often we should try to send our + * metrics to the mesh + */ + + uint32 paxcounter_update_interval = 2; + } + + /* + * Serial Config + */ + message SerialConfig { + /* + * TODO: REPLACE + */ + enum Serial_Baud { + BAUD_DEFAULT = 0; + BAUD_110 = 1; + BAUD_300 = 2; + BAUD_600 = 3; + BAUD_1200 = 4; + BAUD_2400 = 5; + BAUD_4800 = 6; + BAUD_9600 = 7; + BAUD_19200 = 8; + BAUD_38400 = 9; + BAUD_57600 = 10; + BAUD_115200 = 11; + BAUD_230400 = 12; + BAUD_460800 = 13; + BAUD_576000 = 14; + BAUD_921600 = 15; + } + + /* + * TODO: REPLACE + */ + enum Serial_Mode { + DEFAULT = 0; + SIMPLE = 1; + PROTO = 2; + TEXTMSG = 3; + NMEA = 4; + // NMEA messages specifically tailored for CalTopo + CALTOPO = 5; + } + + /* + * Preferences for the SerialModule + */ + bool enabled = 1; + + /* + * TODO: REPLACE + */ + bool echo = 2; + + /* + * RX pin (should match Arduino gpio pin number) + */ + uint32 rxd = 3; + + /* + * TX pin (should match Arduino gpio pin number) + */ + uint32 txd = 4; + + /* + * Serial baud rate + */ + Serial_Baud baud = 5; + + /* + * TODO: REPLACE + */ + uint32 timeout = 6; + + /* + * Mode for serial module operation + */ + Serial_Mode mode = 7; + + /* + * Overrides the platform's defacto Serial port instance to use with Serial module config settings + * This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes + * Existing logging over the Serial Console will still be present + */ + bool override_console_serial_port = 8; + } + + /* + * External Notifications Config + */ + message ExternalNotificationConfig { + /* + * Enable the ExternalNotificationModule + */ + bool enabled = 1; + + /* + * When using in On/Off mode, keep the output on for this many + * milliseconds. Default 1000ms (1 second). + */ + uint32 output_ms = 2; + + /* + * Define the output pin GPIO setting Defaults to + * EXT_NOTIFY_OUT if set for the board. + * In standalone devices this pin should drive the LED to match the UI. + */ + uint32 output = 3; + + /* + * Optional: Define a secondary output pin for a vibra motor + * This is used in standalone devices to match the UI. + */ + uint32 output_vibra = 8; + + /* + * Optional: Define a tertiary output pin for an active buzzer + * This is used in standalone devices to to match the UI. + */ + uint32 output_buzzer = 9; + + /* + * IF this is true, the 'output' Pin will be pulled active high, false + * means active low. + */ + bool active = 4; + + /* + * True: Alert when a text message arrives (output) + */ + bool alert_message = 5; + + /* + * True: Alert when a text message arrives (output_vibra) + */ + bool alert_message_vibra = 10; + + /* + * True: Alert when a text message arrives (output_buzzer) + */ + bool alert_message_buzzer = 11; + + /* + * True: Alert when the bell character is received (output) + */ + bool alert_bell = 6; + + /* + * True: Alert when the bell character is received (output_vibra) + */ + bool alert_bell_vibra = 12; + + /* + * True: Alert when the bell character is received (output_buzzer) + */ + bool alert_bell_buzzer = 13; + + /* + * use a PWM output instead of a simple on/off output. This will ignore + * the 'output', 'output_ms' and 'active' settings and use the + * device.buzzer_gpio instead. + */ + bool use_pwm = 7; + + /* + * The notification will toggle with 'output_ms' for this time of seconds. + * Default is 0 which means don't repeat at all. 60 would mean blink + * and/or beep for 60 seconds + */ + uint32 nag_timeout = 14; + + /* + * When true, enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer + * T-Watch S3 and T-Deck for example have this capability + */ + bool use_i2s_as_buzzer = 15; + } + + /* + * Store and Forward Module Config + */ + message StoreForwardConfig { + /* + * Enable the Store and Forward Module + */ + bool enabled = 1; + + /* + * TODO: REPLACE + */ + bool heartbeat = 2; + + /* + * TODO: REPLACE + */ + uint32 records = 3; + + /* + * TODO: REPLACE + */ + uint32 history_return_max = 4; + + /* + * TODO: REPLACE + */ + uint32 history_return_window = 5; + } + + /* + * Preferences for the RangeTestModule + */ + message RangeTestConfig { + /* + * Enable the Range Test Module + */ + bool enabled = 1; + + /* + * Send out range test messages from this node + */ + uint32 sender = 2; + + /* + * Bool value indicating that this node should save a RangeTest.csv file. + * ESP32 Only + */ + bool save = 3; + } + + /* + * Configuration for both device and environment metrics + */ + message TelemetryConfig { + /* + * Interval in seconds of how often we should try to send our + * device metrics to the mesh + */ + uint32 device_update_interval = 1; + + /* + * Interval in seconds of how often we should try to send our + * environment measurements to the mesh + */ + + uint32 environment_update_interval = 2; + + /* + * Preferences for the Telemetry Module (Environment) + * Enable/Disable the telemetry measurement module measurement collection + */ + bool environment_measurement_enabled = 3; + + /* + * Enable/Disable the telemetry measurement module on-device display + */ + bool environment_screen_enabled = 4; + + /* + * We'll always read the sensor in Celsius, but sometimes we might want to + * display the results in Fahrenheit as a "user preference". + */ + bool environment_display_fahrenheit = 5; + + /* + * Enable/Disable the air quality metrics + */ + bool air_quality_enabled = 6; + + /* + * Interval in seconds of how often we should try to send our + * air quality metrics to the mesh + */ + uint32 air_quality_interval = 7; + + /* + * Interval in seconds of how often we should try to send our + * air quality metrics to the mesh + */ + bool power_measurement_enabled = 8; + + /* + * Interval in seconds of how often we should try to send our + * air quality metrics to the mesh + */ + uint32 power_update_interval = 9; + + /* + * Interval in seconds of how often we should try to send our + * air quality metrics to the mesh + */ + bool power_screen_enabled = 10; + + + + + + + + + + } + + /* + * TODO: REPLACE + */ + message CannedMessageConfig { + /* + * TODO: REPLACE + */ + enum InputEventChar { + /* + * TODO: REPLACE + */ + NONE = 0; + + /* + * TODO: REPLACE + */ + UP = 17; + + /* + * TODO: REPLACE + */ + DOWN = 18; + + /* + * TODO: REPLACE + */ + LEFT = 19; + + /* + * TODO: REPLACE + */ + RIGHT = 20; + + /* + * '\n' + */ + SELECT = 10; + + /* + * TODO: REPLACE + */ + BACK = 27; + + /* + * TODO: REPLACE + */ + CANCEL = 24; + } + + /* + * Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. + */ + bool rotary1_enabled = 1; + + /* + * GPIO pin for rotary encoder A port. + */ + uint32 inputbroker_pin_a = 2; + + /* + * GPIO pin for rotary encoder B port. + */ + uint32 inputbroker_pin_b = 3; + + /* + * GPIO pin for rotary encoder Press port. + */ + uint32 inputbroker_pin_press = 4; + + /* + * Generate input event on CW of this kind. + */ + InputEventChar inputbroker_event_cw = 5; + + /* + * Generate input event on CCW of this kind. + */ + InputEventChar inputbroker_event_ccw = 6; + + /* + * Generate input event on Press of this kind. + */ + InputEventChar inputbroker_event_press = 7; + + /* + * Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker. + */ + bool updown1_enabled = 8; + + /* + * Enable/disable CannedMessageModule. + */ + bool enabled = 9; + + /* + * Input event origin accepted by the canned message module. + * Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" + */ + string allow_input_source = 10; + + /* + * CannedMessageModule also sends a bell character with the messages. + * ExternalNotificationModule can benefit from this feature. + */ + bool send_bell = 11; + } + + /* + Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. + Initially created for the RAK14001 RGB LED module. + */ + message AmbientLightingConfig { + + /* + * Sets LED to on or off. + */ + bool led_state = 1; + + /* + * Sets the current for the LED output. Default is 10. + */ + uint32 current = 2; + + /* + * Sets the red LED level. Values are 0-255. + */ + uint32 red = 3; + + /* + * Sets the green LED level. Values are 0-255. + */ + uint32 green = 4; + + /* + * Sets the blue LED level. Values are 0-255. + */ + uint32 blue = 5; + } + + /* + * TODO: REPLACE + */ + oneof payload_variant { + /* + * TODO: REPLACE + */ + MQTTConfig mqtt = 1; + + /* + * TODO: REPLACE + */ + SerialConfig serial = 2; + + /* + * TODO: REPLACE + */ + ExternalNotificationConfig external_notification = 3; + + /* + * TODO: REPLACE + */ + StoreForwardConfig store_forward = 4; + + /* + * TODO: REPLACE + */ + RangeTestConfig range_test = 5; + + /* + * TODO: REPLACE + */ + TelemetryConfig telemetry = 6; + + /* + * TODO: REPLACE + */ + CannedMessageConfig canned_message = 7; + + /* + * TODO: REPLACE + */ + AudioConfig audio = 8; + + /* + * TODO: REPLACE + */ + RemoteHardwareConfig remote_hardware = 9; + + /* + * TODO: REPLACE + */ + NeighborInfoConfig neighbor_info = 10; + + /* + * TODO: REPLACE + */ + AmbientLightingConfig ambient_lighting = 11; + + /* + * TODO: REPLACE + */ + DetectionSensorConfig detection_sensor = 12; + + /* + * TODO: REPLACE + */ + PaxcounterConfig paxcounter = 13; + } +} + +/* + * A GPIO pin definition for remote hardware module + */ +message RemoteHardwarePin { + /* + * GPIO Pin number (must match Arduino) + */ + uint32 gpio_pin = 1; + + /* + * Name for the GPIO pin (i.e. Front gate, mailbox, etc) + */ + string name = 2; + + /* + * Type of GPIO access available to consumers on the mesh + */ + RemoteHardwarePinType type = 3; +} + +enum RemoteHardwarePinType { + /* + * Unset/unused + */ + UNKNOWN = 0; + + /* + * GPIO pin can be read (if it is high / low) + */ + DIGITAL_READ = 1; + + /* + * GPIO pin can be written to (high / low) + */ + DIGITAL_WRITE = 2; +} \ No newline at end of file diff --git a/src/protos/meshtastic/mqtt.options b/src/protos/meshtastic/mqtt.options new file mode 100644 index 0000000..591e898 --- /dev/null +++ b/src/protos/meshtastic/mqtt.options @@ -0,0 +1,8 @@ +*ServiceEnvelope.packet type:FT_POINTER +*ServiceEnvelope.channel_id type:FT_POINTER +*ServiceEnvelope.gateway_id type:FT_POINTER + +*MapReport.long_name max_size:40 +*MapReport.short_name max_size:5 +*MapReport.firmware_version max_size:18 +*MapReport.num_online_local_nodes int_size:16 \ No newline at end of file diff --git a/src/protos/meshtastic/mqtt.proto b/src/protos/meshtastic/mqtt.proto new file mode 100644 index 0000000..17ebf0e --- /dev/null +++ b/src/protos/meshtastic/mqtt.proto @@ -0,0 +1,106 @@ +syntax = "proto3"; + +package meshtastic; + +import "meshtastic/mesh.proto"; +import "meshtastic/config.proto"; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "MQTTProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * This message wraps a MeshPacket with extra metadata about the sender and how it arrived. + */ +message ServiceEnvelope { + /* + * The (probably encrypted) packet + */ + MeshPacket packet = 1; + + /* + * The global channel ID it was sent on + */ + string channel_id = 2; + + /* + * The sending gateway node ID. Can we use this to authenticate/prevent fake + * nodeid impersonation for senders? - i.e. use gateway/mesh id (which is authenticated) + local node id as + * the globally trusted nodenum + */ + string gateway_id = 3; +} + +/* + * Information about a node intended to be reported unencrypted to a map using MQTT. + */ +message MapReport { + /* + * A full name for this user, i.e. "Kevin Hester" + */ + string long_name = 1; + + /* + * A VERY short name, ideally two characters. + * Suitable for a tiny OLED screen + */ + string short_name = 2; + + /* + * Role of the node that applies specific settings for a particular use-case + */ + Config.DeviceConfig.Role role = 3; + + /* + * Hardware model of the node, i.e. T-Beam, Heltec V3, etc... + */ + HardwareModel hw_model = 4; + + /* + * Device firmware version string + */ + string firmware_version = 5; + + /* + * The region code for the radio (US, CN, EU433, etc...) + */ + Config.LoRaConfig.RegionCode region = 6; + + /* + * Modem preset used by the radio (LongFast, MediumSlow, etc...) + */ + Config.LoRaConfig.ModemPreset modem_preset = 7; + + /* + * Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...) + * and it uses the default frequency slot given the region and modem preset. + */ + bool has_default_channel = 8; + + /* + * Latitude: multiply by 1e-7 to get degrees in floating point + */ + sfixed32 latitude_i = 9; + + /* + * Longitude: multiply by 1e-7 to get degrees in floating point + */ + sfixed32 longitude_i = 10; + + /* + * Altitude in meters above MSL + */ + int32 altitude = 11; + + /* + * Indicates the bits of precision for latitude and longitude set by the sending node + */ + uint32 position_precision = 12; + + /* + * Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) + */ + uint32 num_online_local_nodes = 13; +} \ No newline at end of file diff --git a/src/protos/meshtastic/paxcount.proto b/src/protos/meshtastic/paxcount.proto new file mode 100644 index 0000000..47b2639 --- /dev/null +++ b/src/protos/meshtastic/paxcount.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "PaxcountProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * TODO: REPLACE + */ +message Paxcount { + /* + * seen Wifi devices + */ + uint32 wifi = 1; + + /* + * Seen BLE devices + */ + uint32 ble = 2; + + /* + * Uptime in seconds + */ + uint32 uptime = 3; +} diff --git a/src/protos/meshtastic/portnums.proto b/src/protos/meshtastic/portnums.proto new file mode 100644 index 0000000..b02651a --- /dev/null +++ b/src/protos/meshtastic/portnums.proto @@ -0,0 +1,216 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "Portnums"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a + * unique 'portnum' for their application. + * If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this + * master table. + * PortNums should be assigned in the following range: + * 0-63 Core Meshtastic use, do not use for third party apps + * 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application + * 256-511 Use one of these portnums for your private applications that you don't want to register publically + * All other values are reserved. + * Note: This was formerly a Type enum named 'typ' with the same id # + * We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. + * This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. + */ +enum PortNum { + /* + * Deprecated: do not use in new code (formerly called OPAQUE) + * A message sent from a device outside of the mesh, in a form the mesh does not understand + * NOTE: This must be 0, because it is documented in IMeshService.aidl to be so + * ENCODING: binary undefined + */ + UNKNOWN_APP = 0; + + /* + * A simple UTF-8 text message, which even the little micros in the mesh + * can understand and show on their screen eventually in some circumstances + * even signal might send messages in this form (see below) + * ENCODING: UTF-8 Plaintext (?) + */ + TEXT_MESSAGE_APP = 1; + + /* + * Reserved for built-in GPIO/example app. + * See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number + * ENCODING: Protobuf + */ + REMOTE_HARDWARE_APP = 2; + + /* + * The built-in position messaging app. + * Payload is a Position message. + * ENCODING: Protobuf + */ + POSITION_APP = 3; + + /* + * The built-in user info app. + * Payload is a User message. + * ENCODING: Protobuf + */ + NODEINFO_APP = 4; + + /* + * Protocol control packets for mesh protocol use. + * Payload is a Routing message. + * ENCODING: Protobuf + */ + ROUTING_APP = 5; + + /* + * Admin control packets. + * Payload is a AdminMessage message. + * ENCODING: Protobuf + */ + ADMIN_APP = 6; + + /* + * Compressed TEXT_MESSAGE payloads. + * ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression + * NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed + * payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress + * any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. + */ + TEXT_MESSAGE_COMPRESSED_APP = 7; + + /* + * Waypoint payloads. + * Payload is a Waypoint message. + * ENCODING: Protobuf + */ + WAYPOINT_APP = 8; + + /* + * Audio Payloads. + * Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now + * ENCODING: codec2 audio frames + * NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. + * This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. + */ + AUDIO_APP = 9; + + /* + * Same as Text Message but originating from Detection Sensor Module. + * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 + */ + DETECTION_SENSOR_APP = 10; + + /* + * Provides a 'ping' service that replies to any packet it receives. + * Also serves as a small example module. + * ENCODING: ASCII Plaintext + */ + REPLY_APP = 32; + + /* + * Used for the python IP tunnel feature + * ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. + */ + IP_TUNNEL_APP = 33; + + /* + * Paxcounter lib included in the firmware + * ENCODING: protobuf + */ + PAXCOUNTER_APP = 34; + + /* + * Provides a hardware serial interface to send and receive from the Meshtastic network. + * Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic + * network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. + * Maximum packet size of 240 bytes. + * Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. + * ENCODING: binary undefined + */ + SERIAL_APP = 64; + + /* + * STORE_FORWARD_APP (Work in Progress) + * Maintained by Jm Casler (MC Hamster) : jm@casler.org + * ENCODING: Protobuf + */ + STORE_FORWARD_APP = 65; + + /* + * Optional port for messages for the range test module. + * ENCODING: ASCII Plaintext + * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 + */ + RANGE_TEST_APP = 66; + + /* + * Provides a format to send and receive telemetry data from the Meshtastic network. + * Maintained by Charles Crossan (crossan007) : crossan007@gmail.com + * ENCODING: Protobuf + */ + TELEMETRY_APP = 67; + + /* + * Experimental tools for estimating node position without a GPS + * Maintained by Github user a-f-G-U-C (a Meshtastic contributor) + * Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS + * ENCODING: arrays of int64 fields + */ + ZPS_APP = 68; + + /* + * Used to let multiple instances of Linux native applications communicate + * as if they did using their LoRa chip. + * Maintained by GitHub user GUVWAF. + * Project files at https://github.com/GUVWAF/Meshtasticator + * ENCODING: Protobuf (?) + */ + SIMULATOR_APP = 69; + + /* + * Provides a traceroute functionality to show the route a packet towards + * a certain destination would take on the mesh. + * ENCODING: Protobuf + */ + TRACEROUTE_APP = 70; + + /* + * Aggregates edge info for the network by sending out a list of each node's neighbors + * ENCODING: Protobuf + */ + NEIGHBORINFO_APP = 71; + + /* + * ATAK Plugin + * Portnum for payloads from the official Meshtastic ATAK plugin + */ + ATAK_PLUGIN = 72; + + /* + * Provides unencrypted information about a node for consumption by a map via MQTT + */ + MAP_REPORT_APP = 73; + + /* + * Private applications should use portnums >= 256. + * To simplify initial development and testing you can use "PRIVATE_APP" + * in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) + */ + PRIVATE_APP = 256; + + /* + * ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder + * ENCODING: libcotshrink + */ + ATAK_FORWARDER = 257; + + /* + * Currently we limit port nums to no higher than this value + */ + MAX = 511; +} \ No newline at end of file diff --git a/src/protos/meshtastic/remote_hardware.proto b/src/protos/meshtastic/remote_hardware.proto new file mode 100644 index 0000000..ba4a693 --- /dev/null +++ b/src/protos/meshtastic/remote_hardware.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "RemoteHardware"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * An example app to show off the module system. This message is used for + * REMOTE_HARDWARE_APP PortNums. + * Also provides easy remote access to any GPIO. + * In the future other remote hardware operations can be added based on user interest + * (i.e. serial output, spi/i2c input/output). + * FIXME - currently this feature is turned on by default which is dangerous + * because no security yet (beyond the channel mechanism). + * It should be off by default and then protected based on some TBD mechanism + * (a special channel once multichannel support is included?) + */ +message HardwareMessage { + /* + * TODO: REPLACE + */ + enum Type { + /* + * Unset/unused + */ + UNSET = 0; + + /* + * Set gpio gpios based on gpio_mask/gpio_value + */ + WRITE_GPIOS = 1; + + /* + * We are now interested in watching the gpio_mask gpios. + * If the selected gpios change, please broadcast GPIOS_CHANGED. + * Will implicitly change the gpios requested to be INPUT gpios. + */ + WATCH_GPIOS = 2; + + /* + * The gpios listed in gpio_mask have changed, the new values are listed in gpio_value + */ + GPIOS_CHANGED = 3; + + /* + * Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated + */ + READ_GPIOS = 4; + + /* + * A reply to READ_GPIOS. gpio_mask and gpio_value will be populated + */ + READ_GPIOS_REPLY = 5; + } + + /* + * What type of HardwareMessage is this? + */ + Type type = 1; + + /* + * What gpios are we changing. Not used for all MessageTypes, see MessageType for details + */ + uint64 gpio_mask = 2; + + /* + * For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios. + * Not used for all MessageTypes, see MessageType for details + */ + uint64 gpio_value = 3; +} diff --git a/src/protos/meshtastic/rtttl.options b/src/protos/meshtastic/rtttl.options new file mode 100644 index 0000000..1ae0c2f --- /dev/null +++ b/src/protos/meshtastic/rtttl.options @@ -0,0 +1 @@ +*RTTTLConfig.ringtone max_size:230 diff --git a/src/protos/meshtastic/rtttl.proto b/src/protos/meshtastic/rtttl.proto new file mode 100644 index 0000000..11c8b92 --- /dev/null +++ b/src/protos/meshtastic/rtttl.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "RTTTLConfigProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * Canned message module configuration. + */ +message RTTTLConfig { + /* + * Ringtone for PWM Buzzer in RTTTL Format. + */ + string ringtone = 1; +} diff --git a/src/protos/meshtastic/storeforward.options b/src/protos/meshtastic/storeforward.options new file mode 100644 index 0000000..8580aab --- /dev/null +++ b/src/protos/meshtastic/storeforward.options @@ -0,0 +1 @@ +*StoreAndForward.text max_size:237 \ No newline at end of file diff --git a/src/protos/meshtastic/storeforward.proto b/src/protos/meshtastic/storeforward.proto new file mode 100644 index 0000000..ef7de2c --- /dev/null +++ b/src/protos/meshtastic/storeforward.proto @@ -0,0 +1,218 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "StoreAndForwardProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * TODO: REPLACE + */ +message StoreAndForward { + /* + * 001 - 063 = From Router + * 064 - 127 = From Client + */ + enum RequestResponse { + /* + * Unset/unused + */ + UNSET = 0; + + /* + * Router is an in error state. + */ + ROUTER_ERROR = 1; + + /* + * Router heartbeat + */ + ROUTER_HEARTBEAT = 2; + + /* + * Router has requested the client respond. This can work as a + * "are you there" message. + */ + ROUTER_PING = 3; + + /* + * The response to a "Ping" + */ + ROUTER_PONG = 4; + + /* + * Router is currently busy. Please try again later. + */ + ROUTER_BUSY = 5; + + /* + * Router is responding to a request for history. + */ + ROUTER_HISTORY = 6; + + /* + * Router is responding to a request for stats. + */ + ROUTER_STATS = 7; + + /* + * Router sends a text message from its history that was a direct message. + */ + ROUTER_TEXT_DIRECT = 8; + + /* + * Router sends a text message from its history that was a broadcast. + */ + ROUTER_TEXT_BROADCAST = 9; + + /* + * Client is an in error state. + */ + CLIENT_ERROR = 64; + + /* + * Client has requested a replay from the router. + */ + CLIENT_HISTORY = 65; + + /* + * Client has requested stats from the router. + */ + CLIENT_STATS = 66; + + /* + * Client has requested the router respond. This can work as a + * "are you there" message. + */ + CLIENT_PING = 67; + + /* + * The response to a "Ping" + */ + CLIENT_PONG = 68; + + /* + * Client has requested that the router abort processing the client's request + */ + CLIENT_ABORT = 106; + } + + /* + * TODO: REPLACE + */ + message Statistics { + /* + * Number of messages we have ever seen + */ + uint32 messages_total = 1; + + /* + * Number of messages we have currently saved our history. + */ + uint32 messages_saved = 2; + + /* + * Maximum number of messages we will save + */ + uint32 messages_max = 3; + + /* + * Router uptime in seconds + */ + uint32 up_time = 4; + + /* + * Number of times any client sent a request to the S&F. + */ + uint32 requests = 5; + + /* + * Number of times the history was requested. + */ + uint32 requests_history = 6; + + /* + * Is the heartbeat enabled on the server? + */ + bool heartbeat = 7; + + /* + * Maximum number of messages the server will return. + */ + uint32 return_max = 8; + + /* + * Maximum history window in minutes the server will return messages from. + */ + uint32 return_window = 9; + } + + /* + * TODO: REPLACE + */ + message History { + /* + * Number of that will be sent to the client + */ + uint32 history_messages = 1; + + /* + * The window of messages that was used to filter the history client requested + */ + uint32 window = 2; + + /* + * Index in the packet history of the last message sent in a previous request to the server. + * Will be sent to the client before sending the history and can be set in a subsequent request to avoid getting packets the server already sent to the client. + */ + uint32 last_request = 3; + } + + /* + * TODO: REPLACE + */ + message Heartbeat { + /* + * Period in seconds that the heartbeat is sent out that will be sent to the client + */ + uint32 period = 1; + + /* + * If set, this is not the primary Store & Forward router on the mesh + */ + uint32 secondary = 2; + } + + /* + * TODO: REPLACE + */ + RequestResponse rr = 1; + + /* + * TODO: REPLACE + */ + oneof variant { + /* + * TODO: REPLACE + */ + Statistics stats = 2; + + /* + * TODO: REPLACE + */ + History history = 3; + + /* + * TODO: REPLACE + */ + Heartbeat heartbeat = 4; + + /* + * Text from history message. + */ + bytes text = 5; + } +} \ No newline at end of file diff --git a/src/protos/meshtastic/telemetry.options b/src/protos/meshtastic/telemetry.options new file mode 100644 index 0000000..6c80df9 --- /dev/null +++ b/src/protos/meshtastic/telemetry.options @@ -0,0 +1,4 @@ +# options for nanopb +# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options + + diff --git a/src/protos/meshtastic/telemetry.proto b/src/protos/meshtastic/telemetry.proto new file mode 100644 index 0000000..22b234a --- /dev/null +++ b/src/protos/meshtastic/telemetry.proto @@ -0,0 +1,287 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "TelemetryProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +/* + * Key native device metrics such as battery level + */ +message DeviceMetrics { + /* + * 0-100 (>100 means powered) + */ + uint32 battery_level = 1; + + /* + * Voltage measured + */ + float voltage = 2; + + /* + * Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). + */ + float channel_utilization = 3; + + /* + * Percent of airtime for transmission used within the last hour. + */ + float air_util_tx = 4; +} + +/* + * Weather station or other environmental metrics + */ +message EnvironmentMetrics { + /* + * Temperature measured + */ + float temperature = 1; + + /* + * Relative humidity percent measured + */ + float relative_humidity = 2; + + /* + * Barometric pressure in hPA measured + */ + float barometric_pressure = 3; + + /* + * Gas resistance in MOhm measured + */ + float gas_resistance = 4; + + /* + * Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) + */ + float voltage = 5; + + /* + * Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) + */ + float current = 6; +} + +/* + * Power Metrics (voltage / current / etc) + */ +message PowerMetrics { + /* + * Voltage (Ch1) + */ + float ch1_voltage = 1; + + /* + * Current (Ch1) + */ + float ch1_current = 2; + + /* + * Voltage (Ch2) + */ + float ch2_voltage = 3; + + /* + * Current (Ch2) + */ + float ch2_current = 4; + + /* + * Voltage (Ch3) + */ + float ch3_voltage = 5; + + /* + * Current (Ch3) + */ + float ch3_current = 6; +} + +/* + * Air quality metrics + */ +message AirQualityMetrics { + /* + * Concentration Units Standard PM1.0 + */ + uint32 pm10_standard = 1; + + /* + * Concentration Units Standard PM2.5 + */ + uint32 pm25_standard = 2; + + /* + * Concentration Units Standard PM10.0 + */ + uint32 pm100_standard = 3; + + /* + * Concentration Units Environmental PM1.0 + */ + uint32 pm10_environmental = 4; + + /* + * Concentration Units Environmental PM2.5 + */ + uint32 pm25_environmental = 5; + + /* + * Concentration Units Environmental PM10.0 + */ + uint32 pm100_environmental = 6; + + /* + * 0.3um Particle Count + */ + uint32 particles_03um = 7; + + /* + * 0.5um Particle Count + */ + uint32 particles_05um = 8; + + /* + * 1.0um Particle Count + */ + uint32 particles_10um = 9; + + /* + * 2.5um Particle Count + */ + uint32 particles_25um = 10; + + /* + * 5.0um Particle Count + */ + uint32 particles_50um = 11; + + /* + * 10.0um Particle Count + */ + uint32 particles_100um = 12; +} + +/* + * Types of Measurements the telemetry module is equipped to handle + */ +message Telemetry { + /* + * Seconds since 1970 - or 0 for unknown/unset + */ + fixed32 time = 1; + + oneof variant { + /* + * Key native device metrics such as battery level + */ + DeviceMetrics device_metrics = 2; + + /* + * Weather station or other environmental metrics + */ + EnvironmentMetrics environment_metrics = 3; + + /* + * Air quality metrics + */ + AirQualityMetrics air_quality_metrics = 4; + + /* + * Power Metrics + */ + PowerMetrics power_metrics = 5; + } +} + +/* + * Supported I2C Sensors for telemetry in Meshtastic + */ +enum TelemetrySensorType { + /* + * No external telemetry sensor explicitly set + */ + SENSOR_UNSET = 0; + + /* + * High accuracy temperature, pressure, humidity + */ + BME280 = 1; + + /* + * High accuracy temperature, pressure, humidity, and air resistance + */ + BME680 = 2; + + /* + * Very high accuracy temperature + */ + MCP9808 = 3; + + /* + * Moderate accuracy current and voltage + */ + INA260 = 4; + + /* + * Moderate accuracy current and voltage + */ + INA219 = 5; + + /* + * High accuracy temperature and pressure + */ + BMP280 = 6; + + /* + * High accuracy temperature and humidity + */ + SHTC3 = 7; + + /* + * High accuracy pressure + */ + LPS22 = 8; + + /* + * 3-Axis magnetic sensor + */ + QMC6310 = 9; + + /* + * 6-Axis inertial measurement sensor + */ + QMI8658 = 10; + + /* + * 3-Axis magnetic sensor + */ + QMC5883L = 11; + + /* + * High accuracy temperature and humidity + */ + SHT31 = 12; + + /* + * PM2.5 air quality sensor + */ + PMSA003I = 13; + + /* + * INA3221 3 Channel Voltage / Current Sensor + */ + INA3221 = 14; + + /* + * BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) + */ + BMP085 = 15; + +} diff --git a/src/protos/meshtastic/xmodem.options b/src/protos/meshtastic/xmodem.options new file mode 100644 index 0000000..3af6125 --- /dev/null +++ b/src/protos/meshtastic/xmodem.options @@ -0,0 +1,6 @@ +# options for nanopb +# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options + +*XModem.buffer max_size:128 +*XModem.seq int_size:16 +*XModem.crc16 int_size:16 diff --git a/src/protos/meshtastic/xmodem.proto b/src/protos/meshtastic/xmodem.proto new file mode 100644 index 0000000..732780a --- /dev/null +++ b/src/protos/meshtastic/xmodem.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package meshtastic; + +option csharp_namespace = "Meshtastic.Protobufs"; +option go_package = "github.com/meshtastic/go/generated"; +option java_outer_classname = "XmodemProtos"; +option java_package = "com.geeksville.mesh"; +option swift_prefix = ""; + +message XModem { + enum Control { + NUL = 0; + SOH = 1; + STX = 2; + EOT = 4; + ACK = 6; + NAK = 21; + CAN = 24; + CTRLZ = 26; + } + + Control control = 1; + uint32 seq = 2; + uint32 crc16 = 3; + bytes buffer = 4; +} diff --git a/src/public/index.html b/src/public/index.html index 68de9aa..3f75bfd 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -13,9 +13,6 @@ - - - @@ -313,18 +310,6 @@ return false; } - if(lat == 0 && lng == 0){ - return false; - } - - if(lat < -90 || lat > 90){ - return false; - } - - if(lng < -180 || lng > 180){ - return false; - } - return true; } @@ -488,6 +473,11 @@ // add nodes for(var node of updatedNodes){ + // skip nodes without position + if(!node.latitude || !node.longitude){ + continue; + } + // fix lat long node.latitude = node.latitude / 10000000; node.longitude = node.longitude / 10000000; @@ -504,11 +494,11 @@ var icon = iconOnline; // todo status - var tooltip = `${node.longName}` + - `
ID: ${node.id}` + - `
Role: ${node.role}` + - `
Short Name: ${node.shortName}` + - `
Hardware: ${node.hwModel}`; + var tooltip = `${node.long_name}` + + `
ID: ${node.node_id_hex}` + + `
Role: ${node.role_name}` + + `
Short Name: ${node.short_name}` + + `
Hardware: ${node.hardware_model_name}`; // create node marker var marker = L.marker([node.latitude, node.longitude], { @@ -554,7 +544,7 @@ // update nodes var json = await response.json(); - onNodesUpdated(Object.values(json.nodes)); + onNodesUpdated(json.nodes); // hide loading setLoading(false);