spec: update test fixtures (#517)

* spec: update test fixtures

* app: address review comments
This commit is contained in:
l5y authored and GitHub committed 2025-11-26 17:36:53 +01:00
1 parent f8d78b5489
commit db61a7d626
8 files changed
+78656 -8150

No files matched your search

+282
View File
@@ -0,0 +1,282 @@
[
{
"id":"5fe4ee3640b6361f9ee9b012c68c5413248370d51b27866886e644a7f1b2ca4f",
"domain":"map.meshdresden.eu",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdy3TNMSF0y+F5hwxD5D\n+qboueS1kY5DL6cjEai/7pS12Og5rQAgbGEBjmBJh5cdW02y2hr2gs8XcWzFGcRz\nMYwaFK7fTJtpTVbJ7ZXK2FR8a5DBIT0/c6ldTL5fDxJTyLUVqdC/yz1gn9VgO+jf\nN6HYZI2d6jsA5a9+qbc2E+w1mZ7rbZ7eY573vxVYmfAQP4d8fFuTgpVcDEbNNBjX\nR2W4S5k5Hs9WTL5TU8l45TbGzfB2HSpqahzs9XQ/5clF/fn5fm76dv0aSyoqbKq2\nl91V8zlWc2HBpC0cCj98MY0RLa8fdfXAtn/MOHCbTcCxwLSxJxRfZ5mdcP1+Dy8t\nwQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Mesh Dresden",
"version":"0.5.6",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":51.0,
"longitude":13.61,
"lastUpdateTime":1764035531,
"isPrivate":false,
"signature":"bpIaRq+X7YNON7ziQErU6JbGAxZ/UlbIdpvvct/7jajnVa7+rn4r4fCP/Bk+7r1DaHGBzpvi4F7K8sILFEDL5b8ZONFSgXOG0Y8r/UOu5kFt+ZffTqvERk+GZHFI9XLSPAW97CpD4rnoZq7xNiohTtCNTGg3O057i5J7S+aKrt+E3aGsOHK52GDsfW2odnLuMtGTkST/lbPEdjPSHAgiRn+jmnn1legpcQZtfrjFjf+n0kODRUKLtAcUJMl4V+SR/okqbhJgNBupaxxxTUguCTgxlRc/IHt4Ty8JGaUBqS9HKaItlUBEC47rPIvLKhI3JrUW1elqxeYC2uYOoQNtFg=="
},
{
"id":"e07c6fc04aec13295f57a47fedfdab949099ad983a1ef690cad2c504c0902d70",
"domain":"map.meshrheinland.de",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2FmZ+ZqX6BpGvhYpBUz6\n8H+bAI7B1rX9D8UoYfu7oBtql1D85/UzO2GOJBpcXf9m84FZAjvyC7ZPTBzfL4Tj\nMe2thB1UsbKEm5iBg1A/NcMFbtaBLUXduUxNATDYANB3WwkpWKQiLsZ5cJgfuFWq\nb3RlRGc//FnEoGjmu/YdhnhG8eAu1G8FJAYlz27rPM98+TN3NCgwNL6Ye2hPWH6B\naUK3Sz+6/n60+wqlqo/1sC5TZE6qDFInnYglHF5jZMjq4yFtTOsxo00JNzmsihx/\nxBFVLiIxwtNi2AHtMZmQaJ6T6kca3/Bzkqti6+zMMntdBaZMTMgO8ThRAUSCHyMM\nmQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Mesh Rheinland",
"version":"0.5.6",
"channel":"#ShortSlow",
"frequency":"868MHz",
"latitude":50.71288282,
"longitude":7.05035462,
"lastUpdateTime":1764044758,
"isPrivate":false,
"signature":"Tw251mm5M+K6gSoAjOaeqRwSoytHt1BbqGVrT3vTWE3o0OrjuIO53/Qy67DJ/KOSW/IfNqAzxw2GADgng4fjRNkPo07czPNCxK3tX8CnDhXL90dgMUyRTrNldUUENwDT7+ghdjfv6p5+FWqPdkBBeecpQXz3xk20eW77sIyVjqcJlSTssoBzkNPijjCHOOYp7dhoanaNFSo/Cwn3wPEBJuV/mRTOCmeEESbI9L5qMF6pq75Co+jQwP9hDMIvSqSSZYhqho6woNnJ8YPZbCoCqs45QbbPDLTx6svxYQdfMVWm3dS/rss78KRqnrUrQTvZJr2rt8Xw4JJ7NIWPjjkfhA=="
},
{
"id":"346f27a20aacd92d4fbbc2aa77b879a676a2d7f82d25141cdaa6959505ce0e1f",
"domain":"map.meshtastic-kiel.de",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz8PED8Xh4OeyNLmT9zI6\ndH1XmsbfYis73ZClBYd58sPZKlx28YOodD2KHPMrm7lY/xaH/t4sefveGO5vX78X\nbp3jDPVXvbMWAlK7gQjb/M4MW4IoAo8qMZn8lzbnXmYHEPv/AFvLP2f9s1lSXtpY\nMMa3KcaDbQiLnJEVpCERnMIcWzySi5gvir7K00y/mCgtz6/C3jMhFO0Tk+TDhkc7\nT6KChDTucJy7G58l0fW5lhF8aHVLb05UAx7HuQf0ZvvrGGrS0GO6+T77KLKp8/yd\njRaxE8x9SotpfHfHb2izkQycVWV4I5cgbwzhCKN2HZARiCy4UKhhuuXV/OYqjlCF\nNQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Meshtastic Kiel",
"version":"0.5.5",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":54.32074,
"longitude":10.14314,
"lastUpdateTime":1764105493,
"isPrivate":false,
"signature":"hD1ykqFIwbg/lItms3p8R+unyF5JPXAt8zyJSgrl+HQOFacQh+aGBI+hMCsTIh3gV3DgMfCb8LM6ny4CaDkndqVKTrx33j/dQoM9LQaIf1DOVDjbVwdtn37Ld1qqZXhg09PmVPhqg32uFvTSFs3yv6y72T9i8ozBr5w5AHdBtV3lUdpYD/GSLwxRFGg5rO+FK5CztvpgDZLiB498WUIlNbDkxhUJYUUYd0bVpXbywaJ+xmXFauzcq2DU+OHaOdmuWPzqGC+db+uerrK1R5f7ag6zUbF21mgZ4iQWnsWelD2WyDePt8FCDrqEOycV2pPlZ3Op6apFF9JvDDkQOBM9fA=="
},
{
"id":"aeaf8abf2b4d44498c2cda6e2ebe4d2c83c00208163c6b981aed508d498fa366",
"domain":"map.meshtastic-krd.ru",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaQQV295xIRsiSe+UuJO\n9tq7yV+oUoTNYnvjJsCxvgSKovVJIIZN/Lmcx3P+Dse+lY4WaGKLP9eiIcTXeNjT\nhLJ5rHmVubVGZsVS6l/wInJXvRULKgdTYbZN3f7wlgf+mew9NHYE+tkmR2da7I6O\nbXb6rRdoj67ukGkFfCpfijuzY9sya/Z9LQqfezMnHOmAoHxvhTndWqwINoKcCaY8\nqYdwSR+YnDE0gbwx2zUg2o5hmCdAh2EqsF4EfiOXdcnYoVmQkCOnB9dunkWOzVtD\nrIcwTLxSQEVV46Wb6qhqdQqEeDaQfuEXfFNIi6Ho5mnu7Lf89PeCvtERmuio7u71\nIQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Krasnodar MeshMap",
"version":"v0.5.3",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":45.04363,
"longitude":39.00739,
"lastUpdateTime":1764056911,
"isPrivate":false,
"signature":"m/TZjdZm32dg4xjwcyC0VcCqlwUCoxfk54JKGhBduY/+4Pkc1MFCcnzsJXCsTp8sswXM2t/qaulqtEuAercTBJxfJq4wzDn9R6L+IWjcEOqBjPF3OHUAM94kduuVDb3myUaPRN/6p+Xjj1S77wytOeAQ0EhtmUEAzZntkMk/jA+2AokW5mHLvbIufRIVQjjvUfsNFJvL0OVWeeGiFIcUL9ZN4ZwCnMatFGSNah0RIpFcxge4ENofFMuF46Suem5hshMGTtpHVMm2iSbANhaccPrfD1767YvdaFAziS6ID8j0oP59fN1cvovgwOxBri+806QQoOONwdkt/Nv1QrOHYw=="
},
{
"id":"82292441e3188b54be1b2c1962a20aad7b1e5ce9e61ce65bbf6b30b6f6d37904",
"domain":"map.meshtastic.spb.ru",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2JqB5p+yIKbwItNc13ri\n8rW+Z7d1Qrofjt160pIMmALmsT4O6SrdRrQwFmoZEL7pP1zwyrpyV+V2pjHK33NJ\nD+Eh89+bCPskILJfNyfxcHPVb/xPVBaEpFjAOJJ4VYKFAE7G3pTb+TluB37wHDre\ncY1ewV4/2sasTxmzWQoelpz0a3MJ/oGdCBKi8OwYAthxLzk9WVZmAmDWliEezpzr\nE0chri13RoegOKGkl5340yHTJh8m46UpaWanRgjGye9sU3ml7MIvcckjsm+zokrv\n0qyF615EM8bAHGsn5jAre666St4eO1kuiJcNK+Z5lDVTQknSH9wwWCX9wWQ1YhIF\nxQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"SPB Meshtastic Map from RE50",
"version":"v0.5.4",
"channel":"#LongFast",
"frequency":"869.075MHz",
"latitude":59.951298,
"longitude":30.315806,
"lastUpdateTime":1764092137,
"isPrivate":false,
"signature":"KfgBgjqc6PS6ps4VDuDnX7l+qwfMhPc5zv36VWLQ77hgnkwLuB/97OL+AfV/d94imMOYqWyGu1MMrbkeavAhoMBkpyjs1DB+ABk9Pn/GXcu4kVILL1speJ2DnNLiv1piycJmdAjOBV9X9Nnw5V4oXjOSXfL7tO3MEjaubRlzu9Jex7cXCYrO+pCTwQMZZCuUgJKVg1B2FWgxWeR1uU/ToB9tFRi2WlaR8AIlEq4wiHq/wpJyPDlYwvgS7TiJ9oRyxC+zsULC+B0TQ1dXPHViDOrmaeAsYWbPhtJh4lfZht+pxWxqlG9jguDySHAU/EoqEK/hOZedZGXtt2TXvUsdkA=="
},
{
"id":"92dd330d801bb817afa8f5ba9c78152e24221bd83c30d864cae39d5429ebf110",
"domain":"mesh.fux.is",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2bGkm/Ab//iSHmP05ZSy\n0A7D+OZ8htqG647giTNjAh6KESPQsV5qFIAm/0Aqa/vKyWYfMYMBlRu0fVcat74o\n5JqEJJTSYcipfOYzzgQqIIUswwNniLzwG5OLsIhYpnVs13gLJ6GRJJhklWFjAZiV\nxNbZKDFF1NWJoxGYVuo+TcKQ2j7TxzHTsE0rtx85WPexGZwAAL7eicquapwdAtyj\nHLrBXdUtC/TqOKhh5Qa8v5KAAs/pM8+mWglQMUigpQdB4D4rh3kecGFXwXn/WUb5\nxUGgz7Oa7HpyJUQbkzCPmfovS4G9+tCMn8sWFxvE/pQyMK4O5PyDnxkIwEfBkgNP\n0QIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"[DE] Rhein-Neckar Mesh",
"version":"0.5.6",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":49.487457,
"longitude":8.46604,
"lastUpdateTime":1764084339,
"isPrivate":false,
"signature":"ijW8TwvPT2JLyGVUZ/SvaQNBuPoULroRO0wKkLVCfCXoUHfv9lMXs+s+OTTvSdBsnM1Rqpt+oh8CoS7rF7HzOKBahywOOeihuK8p1uBDMNlTm3c5k0+vZHHpPdGGL2QvaN5LYfZ+k96F+mxnJSOTm2RZv+iS15bc/i1I0TjM5lE+MWOgOwkiykqKmU0GGWc4kmAiTT3k6dhmRwWtpDOzPxXCvwb8iwAf4/ObzTfRLp9fYSCkDkLxcoCTmSo8PDLq+H1IGP+5jQPeUvCKKJcIvYznT4psWxF8I8G85zT1ME6rqC8bU+oRoM1mFhsxPUDS83JQtGhzqt6hk0sLtcVt4A=="
},
{
"id":"811ef6a4184c7e1562196b378071fef4119d6d4c1bdf8ef79089f1de5516885b",
"domain":"mesh.hotellink.ru",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArxjRS/TJkWhkClhW5wKJ\nMwtWzkIo/qRs6tsHrK3eOE7EAAHEaN2EemlK3A1rYvphsQtkSN7nbAWrfeJcUP9i\nycWiEYcwSQTcjZ7tKynHnxc1IWkLFDUEEJSmXXyKSzf7/eq/C9426zPXn4aCx/6b\njkhslKivgBGht6a7XBomVoY3Q4ekCucIzLY2534U9CyB5OhMuE9JNiwQyvfyPe5v\nimvII07hVvE0PHWkWai4E9l8ktFIKQZzvPP2SQqEmpjHIo9h1QaRujjKW6JhenMk\nZb9CBGqcmMs9J4vjWVsAROmCsI5L3yv21bBWKSQn0E6elGnwf7qnVWD59KagY5cP\nJwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Saint-Petersburg MeshMap",
"version":"v0.5.3",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":59.934423,
"longitude":30.311212,
"lastUpdateTime":1764027925,
"isPrivate":false,
"signature":"ig+tk6xcjdmktrG1P3TNXwLC0Fxlky2nqNlEMGd49wfjDU6DmklR3u+OBbQR3tjF6YlcL9VxMEODtrfKsDQv7ajZANwBLdU7Re8VyAoLAWTztCFAUvlOUcKWV/Jt69aCPRHIlqIy4uQNjparDyptwo7SDQcAd56bpc1LyTlJn65OyfbIpC7MQqn+Xnttm7z4jo3EZEya2Vo+WljBw47lNjT73FdT0Lg+7XK6cB0S1voLKt50/apJtMht+L+2sM9rEcfokzdnbXqYZLK1QBL/p1IXJvN7CGGLLs57+8K8ZBzhxqMjRO4AxDVLD8Sq3MxS3MNBOofrnuP52aw7T2Ee+A=="
},
{
"id":"a8d7ed2ec68c872f33b55a99291cad0bb0bb6731248c9e8fe6becaf3dbaf58eb",
"domain":"mesh.kynai.net",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr6QvP8PMgNzUOTTlFI8+\n1JKZuL8EZy6Uju7jxXl+3/0yT5J7bNXzsSW3JGzbsrOlkrr3BudbX8ahHGGKo5pL\n78Y4wQZ+086q1ckYzN5fEjLm2EeXQ76QFFNIAFE3D1MmhvrOXMxddNx6c0bptenz\neppCm1kuft+oL+OEJJYZ+dgkzsxSeagEwgECR376emmKeB4MxnO2h0Nf2djVSVnZ\nj2LdOdxgHWhh4z2ZTOTMXUALiolMFQUHHqDunJBItkgGcF97rZVdhM4XuFxSlpBj\nHMYrkrlHXqLu4/BXLUr4d47UCz4ctDGQCRblgmz0MsyeYqKQliJmsrv3AXiKXAjZ\nKQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Meshtastic Madrid",
"version":"0.5.6",
"channel":"#MediumFast",
"frequency":"868MHz",
"latitude":40.41831,
"longitude":-3.70275,
"lastUpdateTime":1764080459,
"isPrivate":false,
"signature":"hdCvQU62U10FvjBGkR9zeJToRKwS2dWr4C1TyU/7ShyswbKOVSInQmcCFDLhcOS29XcAfT+4phvuR7XacX3CXpDMsVz9wUC6QT3ru7UhgKYr6GqBGUxQsiEHXqaN6YVLaFElQKzhicfLGU735Qen4vPRLMBSKygpRbqs/A31h4n6YrFqgP7BgKQKCmlpPXohLQifqVv/YwLicWBrHkM12g9zYq12O8Y3qH2z21Cp1Q37tA0CIW1YsSCvlChfAddJ6rFISFrgvVDbDzGI1kE4MjYLNULYn5mP9rmpR3kCXb7etlXr3F8xVYXCvZEOm/QfAkReapqy0THo2O1f/d9qaQ=="
},
{
"id":"128a612a38a7b086bc41f43a3cb26158495a2f74c05b7fff0c8b81732bd54c5e",
"domain":"mesh.lora.ms",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4+eYZjp6QiSCeIIBFOLA\nfjxNqg/IqdUIrizOXS/UBfNWMBsnWhamBwm6l1FQ8ZiD1EKmEPdKlpDyNorAUHov\n3jsUgYsB63aZ+cjHID3lHLHAxOmWF+Fdeej09MmJNq4jyudE3fgjTdf+1C6HXGHF\nMXqk4F6md37GJ0pGVonNzLR5GA7CRZS4+tuHlwEUR8LbCxPIadza64PdAFtJvuPB\nda+h2JnuSeRErxWei4jqaYYqTwfwBAd+iEoUSLmvxmD8UHYUn9UEo94Q4TnWIopY\nJh+5ajKNgRNYM2hhw1ZSG9immfjvc3DGk30D3pERGlb7UJuk+bFKs1IwsmdK5HVC\nqQIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"lora.ms",
"version":"v0.5.5-rc2+10-b84764c",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":51.962,
"longitude":7.625,
"lastUpdateTime":1764096892,
"isPrivate":false,
"signature":"jRkvR6ZMRaTj/VhtbHEs/DU6Plt7tK+gtHtR2uJuXXdC2xP9VCAzB5MLEn1dcw6QV1Y7twVF2a0e9uASQWWYt/kyHghZatq6GinV5uOBzv1+jm4iqmFYyMZkTwqhFjGNmgBDOO3+jbuFtQri2chf1GIlCKQqnV7mi6XBuFdUSLlqs0sdi24sTT4Uys2VYuF5FM9D7LQ/aHFlzF2DMIHK86ScUgEhtdZRC5zxTHzg4+YZgmRBF6F5bH0EYnOdaGrMXmJX6wM6WmXrMnKPbxiXxFsEbtASpXDwHn4kkHnI+mygPl8tAhska0/JWnzK0sG1jn0b7dhDomyBxIQr1In2uw=="
},
{
"id":"b2ce736f0390c5da08b71d055b2e6d05355b74831c57dc07b22e79b4ceb02bef",
"domain":"mesh.qrp.ro",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8CiUkygd8IdNLZ1tqtgC\nE763F8KvBNBdklruZb0wRWtDz6gEB1ndQ1T59udpDICTwy4SqUZWbMinaZsClGa3\n6p+PY2jMC3y+WF0hsuPQ/RwI1OYG7SGoU21X5hpkrB83+2pPXcFWuo+OtKXAzEty\nSr9FLECCA5pZHJS+DlYHYeWXd45YU6UH/JBT82E6qRSwiOdVlr8c6uyBRIIA1vdv\ns6hmAASeWHXbftT6rTvB7Ss01q38fni4YSB5Iv9F0Qyki1bS26yg8Ik7ieb/ncgo\niBGwbi1j0ur4TTF9nnqvYQaqrTWl6RG5iZsHw/jl9G/Aujpq+ssWillVXS1r8fXM\nuwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Meshtastic Romania",
"version":"0.5.6",
"channel":"#radioamator - #LongFast",
"frequency":"433MHz - 868MHz",
"latitude":45.603112,
"longitude":24.735904,
"lastUpdateTime":1764106976,
"isPrivate":false,
"signature":"DggUf7J2h4WRl/oPtvYfKPVN8kiyMmJJejqsDpMCM87tXOmV0LCyYLFR7SvqZslcZhUfgB6x/5hxlb2TwPYq0L/CsY3DObB++GdIWmNcC4Zvb/JxegmHkrjsA8EnyUXyfELpKjvz9XOyxfug2N/A4jvqszrivS3VMdWDelC76/eD/czLDLBh/wm5RJMKkAckh+5HSH6aSlK30iPGK2g8XOyOy6d3ghRYwjQxHyDGGNhAyFPbReVHWLkDu4bVHLVo/2J7HPPzyB5g172/yYH1XoBc3y1rwm5RYpN4W1HnWLyn96+FaQqlJq7jMHIVxHIdDHxej78qUoa1j8u3QoRmkQ=="
},
{
"id":"181853cbca021d8ffc9c27b8f21d692ad01eb43dad9ae2b05ed8a9bc87cc917f",
"domain":"mesh.r1cbw.ru",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuEg7/LEz7HUZ2K5eP5En\n1/sD9cElq8GeliHVeYqknHDYxKi5E/hrmYin/xQA8IETPucIneLV+sihPN4A2cdf\nSGnQtLGZxiLHRkWZYYyQAyRcSKbhi87XZOqQK+qC/dd44087ruH21bTVsgxtpWBo\nCMU99JD3N3c95m2bb6/VLSizK9SKsZLKREv+7gRymkSHspOKnQaauPj1Ke0yPo5S\n+klquRApKf1scrDMiWy3SA8LOahcWyZR/82QPLQkXrV/5QVaSRe0KrRLyb92mCA4\nedMCDQjRCd2i0HonGMf5ikpEURL2cB7YtNAs0CleFQTqs4hFbcMa905PZiKgDoHg\n7wIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Yanino-1 Meshtastic Network",
"version":"v0.5.3",
"channel":"#LongFast",
"frequency":"869.075MHz",
"latitude":59.9523328,
"longitude":30.5922047,
"lastUpdateTime":1763579931,
"isPrivate":false,
"signature":"PxYezt08/r/ZNoMc1yjfgRJ6EgH1gK/ERWOofy4PMrmmoRbNmZs33MCxNDDaS2UsgMdCzpe9tqKPhRlQZUJ8QyZz7IV9t3vVnOS46QNPsBNJi76XWnG+rAYTu0bUIJTOmIp06OmiEot1ocCca/BE9N/NjA+07bqrpDXz3iDz5d2M065BHcc+r2SqmtBzMPNPDLe46yqf8lPv1vFrD+Mdd0fbJVMBuq2gHXNjT5QNTFV9Ztt36W5WGMgVN/wNKn/r4JisUlkA/uMHPjqAkFlgOUZra0Bpy5Cl6Q9DW1paHqxrfDFznPQO6fXacf1hpV5LhleUUlGN59n0KlyrFP0egg=="
},
{
"id":"fa22823dbd2628002d236954347e45402401aac30770e2d6c4f822360abc448e",
"domain":"mspm.invalid-domain.org",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqrmDDlpov0uXu94hLFNV\n8ByWrsa2GbQ9BiV1tEJodKch+/PIO0HWG7jslJJO+E2UIQ+LrrCPMbgb928SwHuQ\njuTvUjouXKJkB2v06qqRQ77hRGQIqzYEuAztsh3VWinw4ux8pGMegbCAjJQzBTy6\n8GGHpdP5pvIu6l4Jn+G0vkBr5jkwQinx6HnvYqJS+9lK1Hz3OE1tYjePhiz1V09k\nKw1Zqpr0KbWkS0Ywjc9MWH6m370NRh1qzF+N6gbrnQkGoc+vIi3kHZcjzjbNHnsb\neYZuWFBeuLD5TiPwPdm44LckVUriWdERoT2oOwIzwxxuapblDx9TcWqmYR+MVU8c\nCwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"MSPM",
"version":"0.5.5",
"channel":"#MediumFast",
"frequency":"913.125MHZ",
"latitude":44.97269652411355,
"longitude":-93.2602843583228,
"lastUpdateTime":1764094130,
"isPrivate":false,
"signature":"L6aopRCFYF8IcY2c05XoFVUhLbX6ZMo+WYAvUh3sjWead0JyTAHXIzCK4OukZtTkmBHqMM4tieSMJW6RJpibtbsIP/ByDsCOwAGqSj6Oi9kiESImX/QeC8ZLjix5Cmby84Gl92npbRlE8/UlL8+na8FVhlMksyYsb5hTqJRZbsdFOVemYq2j0qNH3T3lei4IgPncr9pAskXON4jyjXxmAohNh+tWn8bgaON22Exa94e4JST3/X7sNRRaAWosAGaL4PrctAmZs6Hs8I/bf1jaVqauHejd4PotD1n+t02lK+5oKSySaz2XbbCrv0X+evCxqpv42k01wD4xGnOY0cuM/w=="
},
{
"id":"f1788f830865962f41c9839dccc1197568e114f090a16c60ce94b631cae4fb7a",
"domain":"potato.ringo.ws",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmXrfI+5JkvYic3EBvz1\nyyCF9Ebj8N5PAQDIymfSpM1cir4LIZMCU9Aeh0tbkQChlXwiu3R4Qt2d23hVyH5j\ndPn6/rxF4ILJ9evkOB64Jr7ZDxBpgGm/JMUnEBGaq9+lEDW6jDbccqnyJsK1Ak4/\nHEamxyoPEOu/pUlzhavItT732+FW380DNkv2HwnygtPL1qXtKPU0EV4xRq7fZenC\ni0FKBPKJG+NezZBn3W6NI1KteuZArTVvkkT8g96Mp0Lc6nSZ+VEEBTwxHX6pR731\nJpbdU+6GjyWJ44MeVlbTcxuWdKBg5ltNb0TZoOh9z314rZ0Ic3tnMeZA2m9Fwybb\nYwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Ekb mesh",
"version":"0.5.5",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":56.8519,
"longitude":60.6122,
"lastUpdateTime":1764084665,
"isPrivate":false,
"signature":"hSjAbYYy3tJZTnZQALAUaxWJvqdFYU/rTeVNbct2q9Ti4SYrB2+rIGoFZWbgZqvmzmRdbEcSTMK1xsmFS5TFCxCPiDT7VopMnSMY/LWMXJbmTfCiuBJfESVawfrDXzVheFR3oFLYTn3IklDSCH7jpOMBpkFDuzM1AgHjic56upN7GYDoAhwBIUSPomOohhLeQABbMBcKvNm5QuweyKRY8G7PKQGrjERvOPi7CEI97WPgqzq+ycOEKa/o0MbpRygJZn9T9Iq5cbaoGudv8geF7zbjoZd1x4XOHkuboyHkJTppoysir3goBI6U6S55JQ26H+xuBNq8JG/Yf4GR+Fi6tg=="
},
{
"id":"55a9ccee75de853e28fe3e1438a09ba8e4629fb5788832036d9cb1aeda1b1b36",
"domain":"potato.theldti.com",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2LAPqNDTZhnUK1tNjHmB\nrGk8jPGU2Zq75PMc33mMDZaZTwFsPjCMAT/0+XDpgjCwUVhDqe8ZagD3AKb66Mvk\nRTS3uJIQZWQHZS4gfp2phIG/k5RJJB/IPV340Q78lQ27E8qeWC9nLWHVdFCmowrS\nCWfeGZSzQnEdS4C2JmJsft9VeggEIGO4DuAIDLmdilwV6kNx/xEeH12tzoY0joBv\nzslYiW7x5pBaZZSn9R44oBZBBOSO4Z3KyXoiS5ykcnS1JIjJTau+C/a5XoSYityR\nn+aN88YPytrjg8p72k9twQ6CZ7WzYB24fmXNrNrKf7RYOHJTEpdtOjOHX9KaYw2l\nBwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Meshtastic Alt IL Community - The Israeli Mesh",
"version":"0.5.6",
"channel":"#LongFast",
"frequency":"919.375",
"latitude":32.0805216,
"longitude":34.7807083,
"lastUpdateTime":1764090682,
"isPrivate":false,
"signature":"pIz0XeYHgsviwFZIMnXL3AiO4A0VUV+4kevP9V7QV1jJl/dfODF10m7zCuhpPLK7EdHpZAhVsquPbb03sWuLJy7x58nms14UGkc3dB3jJ6Elft7E1D3Q+tPzxRU3Z8i74K7PJ9BLCboKhvKgsFdiRl+mJcdqb3Osf8m5sMenJjR/xF44xq4kvtl2oug6OU2cjSMNyM/OoLFK/Ag/YGzq17nrM5DgairsTmAb+Lp/IKUaWkRvh+Tmt6VbZqghurDidHZht8hhUs3Db86+dlARj0T9jaw3qQQDQourxv8nDdfLWNZLD4GipGOsSeQOHHunIl719NFtDTPN9eeWdchdXg=="
},
{
"id":"87553a5e1f7b7c883b19b97764c72399bd359fb26e09fbd32a9e4febd0b8301d",
"domain":"potato.tnmesh.org",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh5CvMnfCGHOhVm+64bFy\nB/djkDO68XHvFM/gXCHda+lVZtunDdjncYtZXqIuPZ4JGPYxmqrMf1IyI5LKZ9fU\nfKKxC++vgnrs7508k79AHpIaJgAs/dpY1A/GEmKguSZ7FxTvPzcbPGnOH1tlJHyP\nK9McEiSpDfJ+DL6ezknZs7LI61tIK5omao1wRkHxbR6wCHedhPRAVqxsbRCHF6Zc\n+JLj/UFSXLdmY4RRL1ei7ykJXFdon5dfySr6WrWxfoMf4EMYsbze9PSiK8UjVsBH\nerD58e6awo8NEq558PQjDONGYmrk4mIxlCgaNN8OpglYEPkbsMy+wT68qkg71aMf\nTwIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Tennessee Meshtastic Network",
"version":"v0.5.5",
"channel":"#LongFast",
"frequency":"915MHz",
"latitude":36.1627,
"longitude":-86.7816,
"lastUpdateTime":1764093408,
"isPrivate":false,
"signature":"gqIqpKyJ66fDYVlarosCGHFKMs58pH0rA8xfisOUVaSoEoFxJDyE+ybKLQn7fcb4pc+V3zJRMpypOHbkKLOH5tNOgFK7mEY+1CUKuJ3fk6HGnnwxuU8aNzOw8cpUy8S+Bq02wBgLQq5J8u8X1B1Futj47oU5+FOfHJnADjWKpynJcQ3FcBq2/dvgp6xUl29aEKKI8qpu0lUC8TfErogzsw5svllZ2TZ3vCUJn2lJ6+/soiHTAntfg9zCVz5u7U5bxmkcFKAIGYRg3KfIgQtufWpYjUq5a5Z64SALCvfhKccAbPqcDhT+jK1ULC8B29zp1BxeutUxQjFjpXmznHOgPQ=="
},
{
"id":"5a3cc5815d09687c742ff27210f66133c8a89a26bbae662a7cc8b3e9074168ed",
"domain":"potato.varna.radio",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1maZcARwozutUrboPnLF\neAL6ibW7oV2qxvx3vCy9uM37OkHap2b7Hx0P1mrf1bYGIK5qvP6hUiCr5Soe19Ta\nhQUawHTxJgqQCzYS9OC4QUm24LMp4q7ZT5cfmdvDBMjSoAQ9yhyrTZc0TS8LSd+H\nrgXJntc9/JM/aQO+5cZqlDhbNnnYKJj0mRSDYmkE5ali4YkEFn2AiYtsAAYLfzgc\nC3zzPQpSJ3i7XIigRCucBEL5wQgtszaVm4nO5GudKXJwIKq45Cz9etSZYenD2/P7\ne+TSyS0EPgZ3oORSYEFDpo6p19DOaMfR9OfZFbZJ9H1eq+PHmd9aNJzYXVxXbVKK\n4wIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Varna Mesh",
"version":"v0.5.5",
"channel":"#LongFast",
"frequency":"868MHz",
"latitude":43.222428,
"longitude":27.912604,
"lastUpdateTime":1764103542,
"isPrivate":false,
"signature":"aj1pIihCQcmGKERtM1tPcIQzAhLNRN7HrhDwhcMCLeutAN27omna2pY3WI7Mx0QtQugsSesXXhDQ/yjgmeXyVZl0+OiZOv5SvMzVYvUKnUSt2j6ueqJMsX26mXH2TFlKAWiZxi3SMBj6tMmIqcPBsEzXPfNcfo7jdA5NSJ2okP8WUELEV3wNtIGFkWOK1hWGexnprR8kIokioMCd/+AvPgSDFxW9egrao44NRQnvHEHjCw47b68gGyuqlARjIVNDKXrPhQHWfDeFTC4OWgib+5TSVfjixlplp4Pl7FppsjPbkubTzUR+SgbNuXzeYovHoTnAsWGBPU2TtKFxbsIP+A=="
},
{
"id":"7dd9af5d2ac893efcea473866b4dbd98a5ef0e89fb5807a0c345ff3c9902e273",
"domain":"potatomesh.jmrp.io",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzeBnJ/jPYQW+jq+UOxQO\ni2rF9Qhn2qKlsrLbemvfyb9q7tbb4ZD6pQed9OpqdHFBrycnf0iVoaPS0Tw9FNXb\n4wqUPQItjRqPQNTLmNpZlHeJCE1/ORcyRQiWJo0p7ZhkSRKWdCwqLYM9NoJn36Mh\npaNOjnJvadLUhwI87lzP+rJYxSCHDKHaBBCz8tB5brMUWnbpUH0WHUJhjv8X6jf6\n56j5Af9WhdGaX+YjQCMY9Loh1+3VH/ayesojoRR3nBHvWnQY7cbRzhsw5NX6gQRL\nVY39EX4QYOvJS1EU1EHQPkcl/gqtIJVuvAEDVrQ1uC2KKqdEVy/2qx83VMP8tunv\n3QIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"[ES] Valencia, Rafelbunyol",
"version":"0.5.6",
"channel":"#LongFast/#MediumFast",
"frequency":"868MHz",
"latitude":39.585449,
"longitude":-0.336276,
"lastUpdateTime":1764002445,
"isPrivate":false,
"signature":"EcMXp1UoFwFQOPrPDPeuTq48VlC05p66eR4L7JwOXGAWFCo7791pl6Zdry0JeI1tImkS4Vz0NNikRDfqUAKsh64eykPq2FNQPb+uRnD8JZ9xiB24xBY9lbUxfXc0PgMSSOuM1MYPGwpSDwguDCuLc4t0eCk3Wc7x8uPClpadxasVwFDTwD2rkwxwa7LQOWD5f4TzYpEZH77Fb0QpGYZKwBrt11bIX4IfnKo2p6fSFbfrGgtS9zAM8SUzzXO+L4zYd79IxYVvl79ulLxc1mPL3H0DH5pemoJrGj8iRlPHs76dFglZJpWe9o0MX1mN50GzVZcFJQsldnGZJHjD/z3VVA=="
},
{
"id":"50ede1bea9fc64698742bf00ceeff89e7d7c09d1592ca193b07aad1e4f4c91dd",
"domain":"potatomesh.net",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvA6yuxpgT5wygE4tmsai\nciFZLzu+bMKjpHEbI/mq1ri7Y0dntFd4Z51OeZoIQVcNGjq5GmCnTroLlH+PKxR5\nBB4Ipxq1hgsjtVT7jhQGat9DFWmu/8hKt0LL7s2pQJEAOalJvmdjoWhvSK57jf8o\nPmjh5UQnj/HI/k2zwHi1pZFJuFbZrUX7dAJTEH7im8jHKbh0Fm6x5Wj017MnCAeb\nc5+b8oQQXQKB+JGv57KNfQotwKmuuAqAj5WcCt08bKNgCcEsxEltZSADslIpL6pi\na8I8ZRIGJg9VpwwzFh8ifWbKXiml9wa4ZOVTSPzUlzaX181fhgaiFvV/nKPJeh6q\n4wIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"BerlinMesh",
"version":"v0.5.6-rc2",
"channel":"#MediumFast",
"frequency":"868MHz",
"latitude":52.50289,
"longitude":13.40419,
"lastUpdateTime":1764108301,
"isPrivate":false,
"signature":"NfkeowuQdprrXYf91WvExaSzzqUVKVHmC+KbKsl7FZsrADundS41B5GuSu9xTz03CNhE4PrdB9jGoSIhFcAAkY6fO8N5q0GZNXRMtlcWcMeyvz4zrb0ntsDtTD3ieUbDEFYwaHT4aIoEz1mbjwU3TrTQhsEx0/JSqF8z02gZTTZHdZKYPWbBcPp5y4/kA3O0gbA28rcNnC41fkvxUEiyH/nY9EM9JUdG/j587t3gWen/lOa2gSnfePIwIdUlRk590snoQnX6bIwvd0dW5ZUjr9XN1OTN9R27XNyQkxig45yLe6DKcmvawnIfqqWvv77ucNg9V/GeeEMuoFBhGp+fqQ=="
},
{
"id":"aa37ff9b4a0d66230b14bcea980b6cc95f929326baa4bb7138b761c2f6d00c64",
"domain":"potatomesh.urspringer.de",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiTl7UTSs7P49IfBoS/Ns\nipAYusPImhmhlg44kRVjhoTomPzn8VFN4np9ekpP8J0kBZ24hnV0n+eC/6RFhwyd\nlR09IvxIdjDcdxU+UTa46K2ta/kkga32k5iaG1Z2OaXDm14KgglcvbB/V7sqIRHJ\nXcYlfOIqXbhfnZpBRF08/zLw1YmktjUWCwr2ZloHPkfWHNjC/PlnNo50g0p0fKKq\nQkF9uLhriSCRO4K3J4SasSRCEk6pvDFWeA15Xprca6cMGHavIpCuVuzwvaduYm2o\nmd8ScB7y61lSRjxyusCC1ir6GonRu7HZPyIEj4FsxVTPK/Vd3A/SPHWDrKyspgei\n4wIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"Mesh Hessen (Maintal)",
"version":"0.5.5",
"channel":"#ShortSlow",
"frequency":"868MHz",
"latitude":50.1306942,
"longitude":8.8447884,
"lastUpdateTime":1763637532,
"isPrivate":false,
"signature":"QqScuL4EmClZ+thrA4BFyJyj2Fa+t5no2Q3PAYHpgY0l0AllYbmW0vc9ifFLbqdHwmFnoyQtD4BnoofZGl1poasoEVRF3twr0IEJtgb9pdOvmISwAOSr2WN/sLlguFxdBo/xrDzfbKiZrt8GQXN3U+eM6Kaay4apoIJcpjSC1uCkEcACnTWNvLcgKGWoBQWWB7R45ZIzNkkr9Lxn+gm7myqSrz9DMkY8p6KW2qB/TgKzEppLdy2eHxXEZGONrWohnHwJvhzXNDj7+M+wy/EScPaQEPy6Ij8OliymkhJOaLWSz7D026vFgCCq9XE/AlRbB3a8gRy0s2s+7SZNtjMg6Q=="
},
{
"id":"d7b1f776aec81cab87bf2e7c90a3143882d60d9c42fd64f4b0fdc413553ac85a",
"domain":"tovvel.nl",
"pubkey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsnR6kxgyFjKZJPmxiSWU\nISwPpz4e5ldbQ8bX5NGww75zdbUuCtgkFKcx5jczuAmcv2KPJWooLqu4E7n+mQvV\nWDl//Zio6oV/XE3Ac3Mc7xFk+51VrDq1PNTJdT1zLtrmsd9RgHBFWnpi3zes7pUe\nXdFocla2Ny2ByVozKWic0kgT3GZqGpbjeFp5mKXamKbXx4Sq/wZXcYg4ecr0Wz1a\nNToUucURL5jb/acQ5PF9WQn1RxUy/DmUaMcisPJmPNzdvGd+FAjR6PuTXTsGd7ul\nxlDqtv62RTWk4qJnp/sW2BNvcKjSjP8Ta4cFBwZW8EIrQ1MrEaLCcgnBPX5tsijY\nywIDAQAB\n-----END PUBLIC KEY-----\n",
"name":"GrunnMesh",
"version":"0.5.5",
"channel":"#LongFast",
"frequency":"868 MHz",
"latitude":53.2192,
"longitude":6.5683,
"lastUpdateTime":1764085936,
"isPrivate":false,
"signature":"LJFGPso4bkSaMcLxJeiJOaVDVjCpXfzT/IhP/0ZmalyYloPS6dlQhgxyOfN4q7NM56fY1Gpl7nwYsPn7AHUaBZWY/rw86+FqvzagbuB29VOh6S+/VPNNcixUuF0nrlyMYKPt6dUknSbcBKLtEOKpu6U+b9C/7TcAb+lyAIMTxCev3x+z5n7eW0ZXiQ6XMRVVUZVfackY0SJrh+QXQfQ2xHgOfAbllQ2mjCUFAAvWemEOWwmZOjTHvvNe3xP8zXUaJlHc69xOwtvGpXWhA++OUAs30CKVdiqnyDyf8XBkr4lVHltAoK1rIeTdtXWwLqQD/LjT0iHGm8MmpRSKNu0UFA=="
}
]
+15082 -3904
View File
File diff suppressed because it is too large. Load diff
+766 -14
View File
@@ -1,20 +1,772 @@
[
{
"node_id": "!7c5b0920",
"rx_time": 1758884186,
"node_broadcast_interval_secs": 1800,
"last_sent_by": "!9e99f8c0",
"neighbors": [
{ "node_id": "!2b22accc", "snr": -6.5, "rx_time": 1758884106 },
{ "node_id": "!43ba26d0", "snr": -5.0, "rx_time": 1758884120 },
{ "node_id": "!69ba6f71", "snr": -13.0, "rx_time": 1758884135 },
{ "node_id": "!fa848384", "snr": -14.75, "rx_time": 1758884150 },
{ "node_id": "!da6a35b4", "snr": -6.5, "rx_time": 1758884165 }
]
"node_id":"!7c5b0920",
"neighbor_id":"!18de8f57",
"snr":-11.0,
"rx_time":1764108148,
"rx_iso":"2025-11-25T22:02:28Z"
},
{
"node_id": "!cafebabe",
"rx_time": 1758883200,
"neighbors": []
"node_id":"!7c5b0920",
"neighbor_id":"!da6556d4",
"snr":-3.5,
"rx_time":1764108148,
"rx_iso":"2025-11-25T22:02:28Z"
},
{
"node_id":"!7c5b0920",
"neighbor_id":"!9a0728b1",
"snr":-10.5,
"rx_time":1764108148,
"rx_iso":"2025-11-25T22:02:28Z"
},
{
"node_id":"!7c5b0920",
"neighbor_id":"!43b6e530",
"snr":-10.5,
"rx_time":1764108148,
"rx_iso":"2025-11-25T22:02:28Z"
},
{
"node_id":"!7c5b0920",
"neighbor_id":"!da5ec1d8",
"snr":-11.0,
"rx_time":1764108148,
"rx_iso":"2025-11-25T22:02:28Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!33602324",
"snr":-8.25,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!43561304",
"snr":-1.5,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!44968f61",
"snr":6.25,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!7c5b0920",
"snr":6.75,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!d198a280",
"snr":-8.75,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!da5ec1d8",
"snr":9.75,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!28978f94",
"snr":2.0,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!b03c97a4",
"snr":10.75,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!b2a78754",
"snr":-0.5,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!1d60dd3c",
"neighbor_id":"!b03b3b38",
"snr":-9.0,
"rx_time":1764108111,
"rx_iso":"2025-11-25T22:01:51Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!9a0728b1",
"snr":10.25,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!da627d58",
"snr":6.0,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!43b6e530",
"snr":3.75,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!18de8f57",
"snr":-5.0,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!433e31b4",
"snr":-2.5,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!da6556d4",
"snr":-11.75,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!433da0b8",
"snr":-13.0,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!7c5b0920",
"snr":9.75,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!da5ec1d8",
"snr":-13.0,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!b03d7b74",
"neighbor_id":"!fa6baed0",
"snr":-9.5,
"rx_time":1764107957,
"rx_iso":"2025-11-25T21:59:17Z"
},
{
"node_id":"!6c290ea7",
"neighbor_id":"!9eb0b457",
"snr":-6.0,
"rx_time":1764107033,
"rx_iso":"2025-11-25T21:43:53Z"
},
{
"node_id":"!6c290ea7",
"neighbor_id":"!74b11cbc",
"snr":-7.5,
"rx_time":1764107033,
"rx_iso":"2025-11-25T21:43:53Z"
},
{
"node_id":"!6c290ea7",
"neighbor_id":"!12ea9d7e",
"snr":-1.75,
"rx_time":1764107033,
"rx_iso":"2025-11-25T21:43:53Z"
},
{
"node_id":"!6c290ea7",
"neighbor_id":"!194a7351",
"snr":8.25,
"rx_time":1764107033,
"rx_iso":"2025-11-25T21:43:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!b03cc22c",
"snr":-14.75,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!b03d7b74",
"snr":-3.5,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!858f81ad",
"snr":11.25,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!da63e474",
"snr":-8.0,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!4eb3bc8d",
"snr":-5.0,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!45cbfc40",
"snr":-10.25,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!db528df8",
"snr":-6.5,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!9a0728b1",
"snr":7.5,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!bcf10936",
"snr":-3.0,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!43b6e530",
"neighbor_id":"!43b7c694",
"snr":-13.25,
"rx_time":1764106673,
"rx_iso":"2025-11-25T21:37:53Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!43b6e530",
"snr":5.5,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!b03d7b74",
"snr":3.5,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!6c73bf84",
"snr":-12.0,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!fa6baed0",
"snr":-7.25,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!da6556d4",
"snr":-11.5,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!433ad740",
"snr":10.0,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!6d022fe7",
"snr":-8.0,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!7a6bf888",
"snr":-14.5,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!18de8f57",
"snr":-9.75,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!9a0728b1",
"neighbor_id":"!da5ec1d8",
"snr":-7.75,
"rx_time":1764104539,
"rx_iso":"2025-11-25T21:02:19Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!e38969e1",
"snr":-11.5,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!1d60dd3c",
"snr":-14.75,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!b2a79150",
"snr":-5.25,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!b03ceb58",
"snr":-13.25,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!6d022fe7",
"snr":8.5,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!0c63e027",
"neighbor_id":"!b03d1244",
"snr":-11.75,
"rx_time":1764104506,
"rx_iso":"2025-11-25T21:01:46Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!d42e18e8",
"snr":10.5,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!da5ec1d8",
"snr":-13.25,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!433ef3e8",
"snr":-9.5,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!6d022fe7",
"snr":-3.25,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!5bcdae60",
"snr":-2.5,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!18de8f57",
"snr":10.5,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!9a0728b1",
"snr":6.0,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!335c9e9c",
"snr":1.0,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!d1edc388",
"snr":-14.25,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!fa6baed0",
"neighbor_id":"!43b6e530",
"snr":1.25,
"rx_time":1764104227,
"rx_iso":"2025-11-25T20:57:07Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!fa6baed0",
"snr":10.25,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!b29f9e2c",
"snr":-10.0,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!ea8ee2e0",
"snr":-11.5,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!da627d58",
"snr":-0.25,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!43b6e530",
"snr":-11.0,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!9a0728b1",
"snr":8.75,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!b03d7b74",
"snr":-2.5,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!699ab082",
"snr":-14.25,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!d42e18e8",
"snr":10.75,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!18de8f57",
"neighbor_id":"!7c5b0920",
"snr":10.75,
"rx_time":1764102441,
"rx_iso":"2025-11-25T20:27:21Z"
},
{
"node_id":"!d42e18e8",
"neighbor_id":"!7c5b0920",
"snr":-10.25,
"rx_time":1764102333,
"rx_iso":"2025-11-25T20:25:33Z"
},
{
"node_id":"!d42e18e8",
"neighbor_id":"!fa6baed0",
"snr":10.75,
"rx_time":1764102333,
"rx_iso":"2025-11-25T20:25:33Z"
},
{
"node_id":"!d42e18e8",
"neighbor_id":"!18de8f57",
"snr":10.75,
"rx_time":1764102333,
"rx_iso":"2025-11-25T20:25:33Z"
},
{
"node_id":"!dd7fdb95",
"neighbor_id":"!97bae011",
"snr":-12.25,
"rx_time":1764099533,
"rx_iso":"2025-11-25T19:38:53Z"
},
{
"node_id":"!dd7fdb95",
"neighbor_id":"!fe794c32",
"snr":-5.25,
"rx_time":1764099533,
"rx_iso":"2025-11-25T19:38:53Z"
},
{
"node_id":"!dd7fdb95",
"neighbor_id":"!9e95cf60",
"snr":11.25,
"rx_time":1764099533,
"rx_iso":"2025-11-25T19:38:53Z"
},
{
"node_id":"!dd7fdb95",
"neighbor_id":"!44968f61",
"snr":11.0,
"rx_time":1764099533,
"rx_iso":"2025-11-25T19:38:53Z"
},
{
"node_id":"!db528df8",
"neighbor_id":"!43b6e530",
"snr":-12.75,
"rx_time":1764095095,
"rx_iso":"2025-11-25T18:24:55Z"
},
{
"node_id":"!db528df8",
"neighbor_id":"!db52829c",
"snr":10.25,
"rx_time":1764095095,
"rx_iso":"2025-11-25T18:24:55Z"
},
{
"node_id":"!12ea9d7e",
"neighbor_id":"!74b11cbc",
"snr":-10.75,
"rx_time":1764095008,
"rx_iso":"2025-11-25T18:23:28Z"
},
{
"node_id":"!12ea9d7e",
"neighbor_id":"!194a7351",
"snr":10.5,
"rx_time":1764095008,
"rx_iso":"2025-11-25T18:23:28Z"
},
{
"node_id":"!12ea9d7e",
"neighbor_id":"!6c290ea7",
"snr":-2.25,
"rx_time":1764095008,
"rx_iso":"2025-11-25T18:23:28Z"
},
{
"node_id":"!b03d10e0",
"neighbor_id":"!b03d7b74",
"snr":-12.0,
"rx_time":1764005122,
"rx_iso":"2025-11-24T17:25:22Z"
},
{
"node_id":"!b03d10e0",
"neighbor_id":"!433e31b4",
"snr":-12.5,
"rx_time":1764005122,
"rx_iso":"2025-11-24T17:25:22Z"
},
{
"node_id":"!b03d10e0",
"neighbor_id":"!9a0728b1",
"snr":-8.5,
"rx_time":1764005122,
"rx_iso":"2025-11-24T17:25:22Z"
},
{
"node_id":"!b03d10e0",
"neighbor_id":"!43b6e530",
"snr":-11.5,
"rx_time":1764005122,
"rx_iso":"2025-11-24T17:25:22Z"
},
{
"node_id":"!b03d10e0",
"neighbor_id":"!7c5b0920",
"snr":5.25,
"rx_time":1764005122,
"rx_iso":"2025-11-24T17:25:22Z"
},
{
"node_id":"!da547468",
"neighbor_id":"!43b6e530",
"snr":-13.75,
"rx_time":1763947406,
"rx_iso":"2025-11-24T01:23:26Z"
},
{
"node_id":"!da547468",
"neighbor_id":"!7c5b0920",
"snr":-5.75,
"rx_time":1763947406,
"rx_iso":"2025-11-24T01:23:26Z"
},
{
"node_id":"!da547468",
"neighbor_id":"!9a0728b1",
"snr":-10.5,
"rx_time":1763947406,
"rx_iso":"2025-11-24T01:23:26Z"
},
{
"node_id":"!e38969e1",
"neighbor_id":"!849a8ba4",
"snr":-12.25,
"rx_time":1763627064,
"rx_iso":"2025-11-20T08:24:24Z"
},
{
"node_id":"!e38969e1",
"neighbor_id":"!433ba500",
"snr":-7.75,
"rx_time":1763627064,
"rx_iso":"2025-11-20T08:24:24Z"
},
{
"node_id":"!e38969e1",
"neighbor_id":"!6d022fe7",
"snr":-14.5,
"rx_time":1763627064,
"rx_iso":"2025-11-20T08:24:24Z"
},
{
"node_id":"!e38969e1",
"neighbor_id":"!6aac99f2",
"snr":-5.25,
"rx_time":1763627064,
"rx_iso":"2025-11-20T08:24:24Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!bcf10936",
"snr":-13.25,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!f2867a08",
"snr":-15.25,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!fa6baed0",
"snr":-1.0,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!9a0728b1",
"snr":0.25,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!ea24e0b4",
"snr":11.0,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!7c5b0920",
"snr":9.25,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!45edbd08",
"neighbor_id":"!43b6e530",
"snr":6.5,
"rx_time":1763601426,
"rx_iso":"2025-11-20T01:17:06Z"
},
{
"node_id":"!da621dbc",
"neighbor_id":"!43b6e530",
"snr":-13.75,
"rx_time":1763548274,
"rx_iso":"2025-11-19T10:31:14Z"
},
{
"node_id":"!da621dbc",
"neighbor_id":"!18de8f57",
"snr":6.75,
"rx_time":1763548274,
"rx_iso":"2025-11-19T10:31:14Z"
},
{
"node_id":"!da621dbc",
"neighbor_id":"!7c5b0920",
"snr":-10.0,
"rx_time":1763548274,
"rx_iso":"2025-11-19T10:31:14Z"
},
{
"node_id":"!da621dbc",
"neighbor_id":"!fa6baed0",
"snr":5.75,
"rx_time":1763548274,
"rx_iso":"2025-11-19T10:31:14Z"
}
]
+17786 -4009
View File
File diff suppressed because it is too large. Load diff
+23717
View File
File diff suppressed because it is too large. Load diff
+20775 -125
View File
File diff suppressed because it is too large. Load diff
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env bash
# Copyright © 2025-26 l5yth & contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
sqlite3 ../data/mesh.db ".backup './mesh.db'"
curl http://127.0.0.1:41447/api/nodes |jq > ./nodes.json
curl http://127.0.0.1:41447/api/messages |jq > ./messages.json
+248 -78
View File
@@ -238,6 +238,41 @@ RSpec.describe "Potato Mesh Sinatra app" do
end
end
# Retrieve a telemetry metric from nested or top-level payload keys.
#
# @param entry [Hash] telemetry payload.
# @param keys [Array<String>] possible metric names, using snake_case.
# @return [Object, nil] discovered metric value or nil when absent.
def telemetry_metric(entry, *keys)
sources = [
entry,
entry["device_metrics"],
entry["deviceMetrics"],
entry["environment_metrics"],
entry["environmentMetrics"],
].compact
candidates = keys.flat_map { |key| [key, camelize_key(key)] }.compact.uniq
sources.each do |source|
candidates.each do |candidate|
return source[candidate] if source.key?(candidate)
end
end
nil
end
# Convert a snake_case key to camelCase for fixture lookups.
#
# @param key [String] snake_case key.
# @return [String, nil] camelCase variant or nil when not applicable.
def camelize_key(key)
return nil unless key.is_a?(String) && key.include?("_")
parts = key.split("_")
[parts.first, *parts[1..].map(&:capitalize)].join
end
# Import all nodes defined in the fixture file via the HTTP API.
#
# @return [void]
@@ -262,6 +297,53 @@ RSpec.describe "Potato Mesh Sinatra app" do
end
end
# Import a subset of position packets from the fixture file.
#
# @param limit [Integer] number of fixture entries to submit.
# @return [void]
def import_positions_fixture(limit: positions_fixture.size)
positions_fixture.first(limit).each do |position|
post "/api/positions", position.to_json, auth_headers
expect(last_response).to be_ok
expect(JSON.parse(last_response.body)).to eq("status" => "ok")
end
end
# Import a subset of instance records from the fixture file.
#
# @param limit [Integer] number of fixture entries to submit.
# @return [void]
def import_instances_fixture(limit: instances_fixture.size)
insert_sql = <<~SQL
INSERT INTO instances (
id, domain, pubkey, name, version, channel, frequency,
latitude, longitude, last_update_time, is_private, signature
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
SQL
with_db do |db|
instances_fixture.first(limit).each do |instance|
db.execute(
insert_sql,
[
instance["id"],
instance["domain"],
instance["pubkey"],
instance["name"],
instance["version"],
instance["channel"],
instance["frequency"],
instance["latitude"],
instance["longitude"],
instance["lastUpdateTime"],
instance["isPrivate"] ? 1 : 0,
instance["signature"],
],
)
end
end
end
let(:api_token) { "spec-token" }
let(:auth_headers) do
{
@@ -272,6 +354,8 @@ RSpec.describe "Potato Mesh Sinatra app" do
let(:nodes_fixture) { JSON.parse(File.read(fixture_path("nodes.json"))) }
let(:messages_fixture) { JSON.parse(File.read(fixture_path("messages.json"))) }
let(:telemetry_fixture) { JSON.parse(File.read(fixture_path("telemetry.json"))) }
let(:positions_fixture) { JSON.parse(File.read(fixture_path("positions.json"))) }
let(:instances_fixture) { JSON.parse(File.read(fixture_path("instances.json"))) }
let(:trace_fixture) do
[
{
@@ -2181,6 +2265,25 @@ RSpec.describe "Potato Mesh Sinatra app" do
expect(self_entry["signature"]).not_to be_nil
end
it "exposes validated instance records from fixture data" do
clear_database
sample_size = 3
import_instances_fixture(limit: sample_size)
get "/api/instances"
expect(last_response).to be_ok
payload = JSON.parse(last_response.body)
non_self_entries = payload.reject { |entry| entry["id"] == SELF_INSTANCE_ID }
expect(non_self_entries.size).to eq(sample_size)
sample = instances_fixture.first
record = non_self_entries.find { |entry| entry["id"] == sample["id"] }
expect(record).not_to be_nil
expect(record["domain"]).to eq(sample["domain"])
expect(record["name"]).to eq(sample["name"])
end
it "includes previously stored remote registrations" do
remote_attributes = {
id: "remote-instance-1",
@@ -2947,6 +3050,30 @@ RSpec.describe "Potato Mesh Sinatra app" do
end
end
it "stores position fixture samples with detailed metadata" do
clear_database
sample_count = 5
import_positions_fixture(limit: sample_count)
with_db(readonly: true) do |db|
db.results_as_hash = true
rows = db.execute(
"SELECT id, node_id, latitude, longitude, altitude, location_source FROM positions ORDER BY id",
)
expect(rows.size).to eq(sample_count)
positions_fixture.first(sample_count).each do |expected|
row = rows.find { |entry| entry["id"] == expected["id"] }
expect(row).not_to be_nil
expect(row["node_id"]).to eq(expected["node_id"])
expect_same_value(row["latitude"], expected["latitude"])
expect_same_value(row["longitude"], expected["longitude"])
expect_same_value(row["altitude"], expected["altitude"])
expect(row["location_source"]).to eq(expected["location_source"])
end
end
end
it "returns 400 when the payload is not valid JSON" do
post "/api/positions", "{", auth_headers
@@ -3100,10 +3227,10 @@ RSpec.describe "Potato Mesh Sinatra app" do
expect(first["node_id"]).to eq(payload[0]["node_id"])
expect(first["rx_time"]).to eq(payload[0]["rx_time"])
expect_same_value(first["battery_level"], payload[0]["battery_level"])
expect_same_value(first["voltage"], payload[0].dig("device_metrics", "voltage"))
expect_same_value(first["channel_utilization"], payload[0].dig("device_metrics", "channelUtilization"))
expect_same_value(first["air_util_tx"], payload[0].dig("device_metrics", "airUtilTx"))
expect(first["uptime_seconds"]).to eq(payload[0].dig("device_metrics", "uptimeSeconds"))
expect_same_value(first["voltage"], telemetry_metric(payload[0], "voltage"))
expect_same_value(first["channel_utilization"], telemetry_metric(payload[0], "channel_utilization"))
expect_same_value(first["air_util_tx"], telemetry_metric(payload[0], "air_util_tx"))
expect(first["uptime_seconds"]).to eq(telemetry_metric(payload[0], "uptime_seconds"))
expect_same_value(first["current"], payload[0]["current"])
expect_same_value(first["gas_resistance"], payload[0]["gas_resistance"])
expect_same_value(first["iaq"], payload[0]["iaq"])
@@ -3124,37 +3251,37 @@ RSpec.describe "Potato Mesh Sinatra app" do
expect_same_value(first["soil_temperature"], payload[0]["soil_temperature"])
environment_row = rows.find { |row| row["id"] == payload[1]["id"] }
expect(environment_row["temperature"]).to be_within(1e-6).of(payload[1].dig("environment_metrics", "temperature"))
expect(environment_row["relative_humidity"]).to be_within(1e-6).of(payload[1].dig("environment_metrics", "relativeHumidity"))
expect(environment_row["barometric_pressure"]).to be_within(1e-6).of(payload[1].dig("environment_metrics", "barometricPressure"))
expect_same_value(environment_row["gas_resistance"], payload[1].dig("environment_metrics", "gasResistance"))
expect_same_value(environment_row["iaq"], payload[1].dig("environment_metrics", "iaq"))
expect_same_value(environment_row["distance"], payload[1].dig("environment_metrics", "distance"))
expect_same_value(environment_row["lux"], payload[1].dig("environment_metrics", "lux"))
expect_same_value(environment_row["white_lux"], payload[1].dig("environment_metrics", "whiteLux"))
expect_same_value(environment_row["ir_lux"], payload[1].dig("environment_metrics", "irLux"))
expect_same_value(environment_row["uv_lux"], payload[1].dig("environment_metrics", "uvLux"))
expect_same_value(environment_row["wind_direction"], payload[1].dig("environment_metrics", "windDirection"))
expect_same_value(environment_row["wind_speed"], payload[1].dig("environment_metrics", "windSpeed"))
expect_same_value(environment_row["wind_gust"], payload[1].dig("environment_metrics", "windGust"))
expect_same_value(environment_row["wind_lull"], payload[1].dig("environment_metrics", "windLull"))
expect_same_value(environment_row["weight"], payload[1].dig("environment_metrics", "weight"))
expect_same_value(environment_row["radiation"], payload[1].dig("environment_metrics", "radiation"))
expect_same_value(environment_row["rainfall_1h"], payload[1].dig("environment_metrics", "rainfall1h"))
expect_same_value(environment_row["rainfall_24h"], payload[1].dig("environment_metrics", "rainfall24h"))
expect_same_value(environment_row["soil_moisture"], payload[1].dig("environment_metrics", "soilMoisture"))
expect_same_value(environment_row["soil_temperature"], payload[1].dig("environment_metrics", "soilTemperature"))
expect_same_value(environment_row["temperature"], telemetry_metric(payload[1], "temperature"))
expect_same_value(environment_row["relative_humidity"], telemetry_metric(payload[1], "relative_humidity"))
expect_same_value(environment_row["barometric_pressure"], telemetry_metric(payload[1], "barometric_pressure"))
expect_same_value(environment_row["gas_resistance"], telemetry_metric(payload[1], "gas_resistance"))
expect_same_value(environment_row["iaq"], telemetry_metric(payload[1], "iaq"))
expect_same_value(environment_row["distance"], telemetry_metric(payload[1], "distance"))
expect_same_value(environment_row["lux"], telemetry_metric(payload[1], "lux"))
expect_same_value(environment_row["white_lux"], telemetry_metric(payload[1], "white_lux"))
expect_same_value(environment_row["ir_lux"], telemetry_metric(payload[1], "ir_lux"))
expect_same_value(environment_row["uv_lux"], telemetry_metric(payload[1], "uv_lux"))
expect_same_value(environment_row["wind_direction"], telemetry_metric(payload[1], "wind_direction"))
expect_same_value(environment_row["wind_speed"], telemetry_metric(payload[1], "wind_speed"))
expect_same_value(environment_row["wind_gust"], telemetry_metric(payload[1], "wind_gust"))
expect_same_value(environment_row["wind_lull"], telemetry_metric(payload[1], "wind_lull"))
expect_same_value(environment_row["weight"], telemetry_metric(payload[1], "weight"))
expect_same_value(environment_row["radiation"], telemetry_metric(payload[1], "radiation"))
expect_same_value(environment_row["rainfall_1h"], telemetry_metric(payload[1], "rainfall_1h", "rainfall1h"))
expect_same_value(environment_row["rainfall_24h"], telemetry_metric(payload[1], "rainfall_24h", "rainfall24h"))
expect_same_value(environment_row["soil_moisture"], telemetry_metric(payload[1], "soil_moisture"))
expect_same_value(environment_row["soil_temperature"], telemetry_metric(payload[1], "soil_temperature"))
third_row = rows.find { |row| row["id"] == payload[2]["id"] }
expect_same_value(third_row["current"], payload[2].dig("device_metrics", "current"))
expect_same_value(third_row["distance"], payload[2].dig("environment_metrics", "distance"))
expect_same_value(third_row["lux"], payload[2].dig("environment_metrics", "lux"))
expect_same_value(third_row["wind_direction"], payload[2].dig("environment_metrics", "windDirection"))
expect_same_value(third_row["wind_speed"], payload[2].dig("environment_metrics", "windSpeed"))
expect_same_value(third_row["weight"], payload[2].dig("environment_metrics", "weight"))
expect_same_value(third_row["rainfall_24h"], payload[2].dig("environment_metrics", "rainfall24h"))
expect_same_value(third_row["soil_moisture"], payload[2].dig("environment_metrics", "soilMoisture"))
expect_same_value(third_row["soil_temperature"], payload[2].dig("environment_metrics", "soilTemperature"))
expect_same_value(third_row["current"], telemetry_metric(payload[2], "current"))
expect_same_value(third_row["distance"], telemetry_metric(payload[2], "distance"))
expect_same_value(third_row["lux"], telemetry_metric(payload[2], "lux"))
expect_same_value(third_row["wind_direction"], telemetry_metric(payload[2], "wind_direction"))
expect_same_value(third_row["wind_speed"], telemetry_metric(payload[2], "wind_speed"))
expect_same_value(third_row["weight"], telemetry_metric(payload[2], "weight"))
expect_same_value(third_row["rainfall_24h"], telemetry_metric(payload[2], "rainfall_24h", "rainfall24h"))
expect_same_value(third_row["soil_moisture"], telemetry_metric(payload[2], "soil_moisture"))
expect_same_value(third_row["soil_temperature"], telemetry_metric(payload[2], "soil_temperature"))
end
with_db(readonly: true) do |db|
@@ -3164,11 +3291,29 @@ RSpec.describe "Potato Mesh Sinatra app" do
"SELECT battery_level, voltage, channel_utilization, air_util_tx, uptime_seconds, last_heard, first_heard FROM nodes WHERE node_id = ?",
[payload[0]["node_id"]],
)
expect_same_value(metrics_node["battery_level"], payload[0]["battery_level"])
expect_same_value(metrics_node["voltage"], payload[0]["device_metrics"]["voltage"])
expect_same_value(metrics_node["channel_utilization"], payload[0]["device_metrics"]["channelUtilization"])
expect_same_value(metrics_node["air_util_tx"], payload[0]["device_metrics"]["airUtilTx"])
expect(metrics_node["uptime_seconds"]).to eq(payload[0]["device_metrics"]["uptimeSeconds"])
expect_same_value(metrics_node["battery_level"], telemetry_metric(payload[0], "battery_level"))
expect_same_value(metrics_node["voltage"], telemetry_metric(payload[0], "voltage"))
telemetry_for_node = payload.select { |entry| entry["node_id"] == payload[0]["node_id"] }
util_values = telemetry_for_node.filter_map { |entry| telemetry_metric(entry, "channel_utilization") }
air_values = telemetry_for_node.filter_map { |entry| telemetry_metric(entry, "air_util_tx") }
if util_values.any?
expect(metrics_node["channel_utilization"]).to be_between(util_values.min, util_values.max)
else
expect(metrics_node["channel_utilization"]).to be_nil
end
if air_values.any?
expect(metrics_node["air_util_tx"]).to be_between(air_values.min, air_values.max)
else
expect(metrics_node["air_util_tx"]).to be_nil
end
uptime_values = telemetry_for_node.filter_map { |entry| telemetry_metric(entry, "uptime_seconds") }
if uptime_values.any?
expect(metrics_node["uptime_seconds"]).to be_between(uptime_values.min, uptime_values.max)
else
expect(metrics_node["uptime_seconds"]).to be_nil
end
expect(metrics_node["last_heard"]).to eq(payload[0]["rx_time"])
expect(metrics_node["first_heard"]).to eq(payload[0]["rx_time"])
@@ -3176,17 +3321,35 @@ RSpec.describe "Potato Mesh Sinatra app" do
"SELECT last_heard, battery_level, voltage FROM nodes WHERE node_id = ?",
[payload[1]["node_id"]],
)
expect(env_node["last_heard"]).to eq(payload[1]["rx_time"])
expect(env_node["battery_level"]).to be_nil
expect(env_node["voltage"]).to be_nil
env_rx_times = payload.select { |entry| entry["node_id"] == payload[1]["node_id"] }.filter_map do |entry|
entry["rx_time"]
end
latest_rx = env_rx_times.max
expect(env_node["last_heard"]).to eq(latest_rx)
expect_same_value(env_node["battery_level"], telemetry_metric(payload[1], "battery_level"))
expect_same_value(env_node["voltage"], telemetry_metric(payload[1], "voltage"))
local_node = db.get_first_row(
"SELECT battery_level, uptime_seconds, last_heard FROM nodes WHERE node_id = ?",
[payload[2]["node_id"]],
)
expect_same_value(local_node["battery_level"], payload[2]["device_metrics"]["battery_level"])
expect(local_node["uptime_seconds"]).to eq(payload[2]["device_metrics"]["uptime_seconds"])
expect(local_node["last_heard"]).to eq(payload[2]["rx_time"])
expect_same_value(local_node["battery_level"], telemetry_metric(payload[2], "battery_level"))
local_uptime_values = payload.select { |entry| entry["node_id"] == payload[2]["node_id"] }.filter_map do |entry|
telemetry_metric(entry, "uptime_seconds")
end
if local_uptime_values.any?
expect(local_node["uptime_seconds"]).to be_between(local_uptime_values.min, local_uptime_values.max)
else
expect(local_node["uptime_seconds"]).to be_nil
end
local_rx_times = payload.select { |entry| entry["node_id"] == payload[2]["node_id"] }.filter_map do |entry|
entry["rx_time"]
end
if local_rx_times.any?
expect(local_node["last_heard"]).to eq(local_rx_times.max)
else
expect(local_node["last_heard"]).to be_nil
end
end
end
@@ -3650,7 +3813,7 @@ RSpec.describe "Potato Mesh Sinatra app" do
it "returns the stored nodes with derived timestamps" do
import_nodes_fixture
get "/api/nodes"
get "/api/nodes?limit=#{nodes_fixture.size}"
expect(last_response).to be_ok
actual = JSON.parse(last_response.body)
@@ -3782,11 +3945,18 @@ RSpec.describe "Potato Mesh Sinatra app" do
import_nodes_fixture
import_messages_fixture
get "/api/messages?encrypted=1"
filtered_messages = messages_fixture.select do |message|
%w[text encrypted emoji reply_id].any? do |key|
value = message[key]
value.is_a?(String) ? !value.strip.empty? : !value.nil?
end
end
get "/api/messages?encrypted=1&limit=#{messages_fixture.size}"
expect(last_response).to be_ok
actual = JSON.parse(last_response.body)
expect(actual.size).to eq(messages_fixture.size)
expect(actual.size).to eq(filtered_messages.size)
actual_by_id = actual.each_with_object({}) do |row, acc|
acc[row["id"]] = row
@@ -3800,7 +3970,7 @@ RSpec.describe "Potato Mesh Sinatra app" do
end
end
messages_fixture.each do |message|
filtered_messages.each do |message|
expected = message.reject { |key, _| key == "node" }
actual_row = actual_by_id.fetch(message["id"])
@@ -4231,40 +4401,40 @@ RSpec.describe "Potato Mesh Sinatra app" do
expect(first_entry["telemetry_time"]).to eq(latest["telemetry_time"])
expect(first_entry["telemetry_time_iso"]).to eq(Time.at(latest["telemetry_time"]).utc.iso8601)
expect(first_entry).not_to have_key("device_metrics")
expect_same_value(first_entry["battery_level"], latest.dig("device_metrics", "battery_level") || latest.dig("device_metrics", "batteryLevel"))
expect_same_value(first_entry["current"], latest.dig("device_metrics", "current"))
expect_same_value(first_entry["distance"], latest.dig("environment_metrics", "distance"))
expect_same_value(first_entry["lux"], latest.dig("environment_metrics", "lux"))
expect_same_value(first_entry["wind_direction"], latest.dig("environment_metrics", "windDirection"))
expect_same_value(first_entry["wind_speed"], latest.dig("environment_metrics", "windSpeed"))
expect_same_value(first_entry["weight"], latest.dig("environment_metrics", "weight"))
expect_same_value(first_entry["rainfall_24h"], latest.dig("environment_metrics", "rainfall24h"))
expect_same_value(first_entry["soil_moisture"], latest.dig("environment_metrics", "soilMoisture"))
expect_same_value(first_entry["soil_temperature"], latest.dig("environment_metrics", "soilTemperature"))
expect_same_value(first_entry["battery_level"], telemetry_metric(latest, "battery_level"))
expect_same_value(first_entry["current"], telemetry_metric(latest, "current"))
expect_same_value(first_entry["distance"], telemetry_metric(latest, "distance"))
expect_same_value(first_entry["lux"], telemetry_metric(latest, "lux"))
expect_same_value(first_entry["wind_direction"], telemetry_metric(latest, "wind_direction"))
expect_same_value(first_entry["wind_speed"], telemetry_metric(latest, "wind_speed"))
expect_same_value(first_entry["weight"], telemetry_metric(latest, "weight"))
expect_same_value(first_entry["rainfall_24h"], telemetry_metric(latest, "rainfall_24h", "rainfall24h"))
expect_same_value(first_entry["soil_moisture"], telemetry_metric(latest, "soil_moisture"))
expect_same_value(first_entry["soil_temperature"], telemetry_metric(latest, "soil_temperature"))
second_entry = data.last
expect(second_entry["id"]).to eq(second_latest["id"])
expect(second_entry).not_to have_key("environment_metrics")
expect(second_entry["temperature"]).to be_within(1e-6).of(second_latest["environment_metrics"]["temperature"])
expect(second_entry["relative_humidity"]).to be_within(1e-6).of(second_latest["environment_metrics"]["relativeHumidity"])
expect(second_entry["barometric_pressure"]).to be_within(1e-6).of(second_latest["environment_metrics"]["barometricPressure"])
expect_same_value(second_entry["gas_resistance"], second_latest.dig("environment_metrics", "gasResistance"))
expect_same_value(second_entry["iaq"], second_latest.dig("environment_metrics", "iaq"))
expect_same_value(second_entry["distance"], second_latest.dig("environment_metrics", "distance"))
expect_same_value(second_entry["lux"], second_latest.dig("environment_metrics", "lux"))
expect_same_value(second_entry["white_lux"], second_latest.dig("environment_metrics", "whiteLux"))
expect_same_value(second_entry["ir_lux"], second_latest.dig("environment_metrics", "irLux"))
expect_same_value(second_entry["uv_lux"], second_latest.dig("environment_metrics", "uvLux"))
expect_same_value(second_entry["wind_direction"], second_latest.dig("environment_metrics", "windDirection"))
expect_same_value(second_entry["wind_speed"], second_latest.dig("environment_metrics", "windSpeed"))
expect_same_value(second_entry["wind_gust"], second_latest.dig("environment_metrics", "windGust"))
expect_same_value(second_entry["wind_lull"], second_latest.dig("environment_metrics", "windLull"))
expect_same_value(second_entry["weight"], second_latest.dig("environment_metrics", "weight"))
expect_same_value(second_entry["radiation"], second_latest.dig("environment_metrics", "radiation"))
expect_same_value(second_entry["rainfall_1h"], second_latest.dig("environment_metrics", "rainfall1h"))
expect_same_value(second_entry["rainfall_24h"], second_latest.dig("environment_metrics", "rainfall24h"))
expect_same_value(second_entry["soil_moisture"], second_latest.dig("environment_metrics", "soilMoisture"))
expect_same_value(second_entry["soil_temperature"], second_latest.dig("environment_metrics", "soilTemperature"))
expect_api_value(second_entry, "temperature", telemetry_metric(second_latest, "temperature"))
expect_api_value(second_entry, "relative_humidity", telemetry_metric(second_latest, "relative_humidity"))
expect_api_value(second_entry, "barometric_pressure", telemetry_metric(second_latest, "barometric_pressure"))
expect_same_value(second_entry["gas_resistance"], telemetry_metric(second_latest, "gas_resistance"))
expect_same_value(second_entry["iaq"], telemetry_metric(second_latest, "iaq"))
expect_same_value(second_entry["distance"], telemetry_metric(second_latest, "distance"))
expect_same_value(second_entry["lux"], telemetry_metric(second_latest, "lux"))
expect_same_value(second_entry["white_lux"], telemetry_metric(second_latest, "white_lux"))
expect_same_value(second_entry["ir_lux"], telemetry_metric(second_latest, "ir_lux"))
expect_same_value(second_entry["uv_lux"], telemetry_metric(second_latest, "uv_lux"))
expect_same_value(second_entry["wind_direction"], telemetry_metric(second_latest, "wind_direction"))
expect_same_value(second_entry["wind_speed"], telemetry_metric(second_latest, "wind_speed"))
expect_same_value(second_entry["wind_gust"], telemetry_metric(second_latest, "wind_gust"))
expect_same_value(second_entry["wind_lull"], telemetry_metric(second_latest, "wind_lull"))
expect_same_value(second_entry["weight"], telemetry_metric(second_latest, "weight"))
expect_same_value(second_entry["radiation"], telemetry_metric(second_latest, "radiation"))
expect_same_value(second_entry["rainfall_1h"], telemetry_metric(second_latest, "rainfall_1h", "rainfall1h"))
expect_same_value(second_entry["rainfall_24h"], telemetry_metric(second_latest, "rainfall_24h", "rainfall24h"))
expect_same_value(second_entry["soil_moisture"], telemetry_metric(second_latest, "soil_moisture"))
expect_same_value(second_entry["soil_temperature"], telemetry_metric(second_latest, "soil_temperature"))
end
it "excludes telemetry entries older than seven days" do