diff --git a/Cargo.lock b/Cargo.lock index 773976c..620ac83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,19 +3,45 @@ version = 3 [[package]] -name = "addr2line" -version = "0.17.0" +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "gimli", + "crypto-common", + "generic-array", ] [[package]] -name = "adler" -version = "1.0.2" +name = "aes" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] [[package]] name = "aho-corasick" @@ -32,7 +58,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -44,6 +70,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "async-ssh2-tokio" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb0367d51247c7eff825eec9fa3c1a4710b4042dd5c6f2ef2fdd105af9addee" +dependencies = [ + "async-trait", + "russh", + "russh-keys", + "thiserror", +] + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "atty" version = "0.2.14" @@ -52,49 +101,42 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] name = "autocfg" -version = "0.1.7" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "autocfg" -version = "1.0.1" +name = "base-x" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] -name = "backtrace" -version = "0.3.63" +name = "base64" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" [[package]] -name = "base-x" -version = "0.2.11" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "base64" -version = "0.10.1" +name = "bcrypt-pbkdf" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ - "byteorder", + "blowfish", + "pbkdf2 0.12.2", + "sha2", ] [[package]] @@ -144,6 +186,12 @@ dependencies = [ "which 4.4.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -162,6 +210,34 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "bme280" version = "0.3.0" @@ -183,17 +259,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "either", - "iovec", -] - [[package]] name = "bytes" version = "1.2.1" @@ -212,6 +277,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.0.72" @@ -248,6 +322,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + [[package]] name = "chrono" version = "0.4.19" @@ -258,7 +343,17 @@ dependencies = [ "num-integer", "num-traits", "time 0.1.44", - "winapi 0.3.9", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", ] [[package]] @@ -296,6 +391,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "const_fn" version = "0.4.9" @@ -303,104 +404,133 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] -name = "cookie" -version = "0.12.0" +name = "core-foundation" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" dependencies = [ - "time 0.1.44", - "url 1.7.2", + "core-foundation-sys", + "libc", ] [[package]] -name = "cookie_store" -version = "0.7.0" +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ - "cookie", - "failure", - "idna 0.1.5", - "log", - "publicsuffix", - "serde", - "serde_json", - "time 0.1.44", - "try_from", - "url 1.7.2", + "libc", ] [[package]] -name = "core-foundation" -version = "0.9.2" +name = "crc32fast" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "core-foundation-sys", - "libc", + "cfg-if 1.0.0", ] [[package]] -name = "core-foundation-sys" -version = "0.8.3" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] [[package]] -name = "crc32fast" -version = "1.3.0" +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version 0.4.0", + "subtle", + "zeroize", ] [[package]] -name = "crossbeam-deque" -version = "0.7.4" +name = "curve25519-dalek-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", - "maybe-uninit", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] -name = "crossbeam-epoch" -version = "0.8.2" +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "der" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ - "autocfg 1.0.1", - "cfg-if 0.1.10", - "crossbeam-utils", - "lazy_static", - "maybe-uninit", - "memoffset 0.5.6", - "scopeguard", + "const-oid", + "zeroize", ] [[package]] -name = "crossbeam-queue" -version = "0.2.3" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils", - "maybe-uninit", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] -name = "crossbeam-utils" -version = "0.7.2" +name = "dirs" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "autocfg 1.0.1", - "cfg-if 0.1.10", - "lazy_static", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", ] [[package]] @@ -410,10 +540,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] -name = "dtoa" -version = "0.4.8" +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "zeroize", +] [[package]] name = "either" @@ -479,28 +627,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.85", - "synstructure", -] - [[package]] name = "fastrand" version = "1.6.0" @@ -510,15 +636,19 @@ dependencies = [ "instant", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", "miniz_oxide", ] @@ -550,37 +680,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", - "percent-encoding 2.1.0", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags 1.3.2", - "fuchsia-zircon-sys", + "percent-encoding", ] -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures" version = "0.3.19" @@ -612,16 +714,6 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures 0.1.31", - "num_cpus", -] - [[package]] name = "futures-executor" version = "0.3.19" @@ -680,6 +772,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -691,6 +793,17 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "gettext-rs" version = "0.7.0" @@ -712,10 +825,14 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.26.1" +name = "ghash" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] [[package]] name = "glob" @@ -730,28 +847,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "409296415b8abc7b47e5b77096faae14595c53724972da227434fc8f4b05ec8b" dependencies = [ "bitflags 1.3.2", - "futures 0.3.19", + "futures", "libc", - "nix", - "tokio 1.15.0", + "nix 0.23.1", + "tokio", ] [[package]] name = "h2" -version = "0.1.26" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ - "byteorder", - "bytes 0.4.12", + "bytes", "fnv", - "futures 0.1.31", + "futures-core", + "futures-sink", + "futures-util", "http", "indexmap", - "log", "slab", - "string", - "tokio-io", + "tokio", + "tokio-util 0.7.2", + "tracing", ] [[package]] @@ -769,34 +887,54 @@ dependencies = [ "libc", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" -version = "0.1.21" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 0.4.12", + "bytes", "fnv", - "itoa 0.4.8", + "itoa", ] [[package]] name = "http-body" -version = "0.1.0" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", + "bytes", "http", - "tokio-buf", + "pin-project-lite", ] [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -809,45 +947,39 @@ dependencies = [ [[package]] name = "hyper" -version = "0.12.36" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "futures-cpupool", + "bytes", + "futures-channel", + "futures-core", + "futures-util", "h2", "http", "http-body", "httparse", - "iovec", - "itoa 0.4.8", - "log", - "net2", - "rustc_version", - "time 0.1.44", - "tokio 0.1.22", - "tokio-buf", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", "want", ] [[package]] name = "hyper-tls" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", + "bytes", "hyper", "native-tls", - "tokio-io", + "tokio", + "tokio-native-tls", ] [[package]] @@ -859,18 +991,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "libc", - "nix", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "nix 0.23.1", ] [[package]] @@ -890,10 +1011,20 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ - "autocfg 1.0.1", + "autocfg", "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -913,19 +1044,10 @@ dependencies = [ ] [[package]] -name = "iovec" -version = "0.1.4" +name = "ipnet" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" @@ -934,13 +1056,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "js-sys" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "wasm-bindgen", ] [[package]] @@ -968,7 +1089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if 1.0.0", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -994,32 +1115,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libssh2-sys" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-embedded-hal" version = "0.3.2" @@ -1048,7 +1143,7 @@ dependencies = [ "objc", "objc-foundation", "regex", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1062,9 +1157,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] @@ -1094,10 +1189,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] -name = "maybe-uninit" -version = "2.0.0" +name = "md5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" @@ -1105,22 +1200,13 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "memoffset" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg 1.0.1", -] - [[package]] name = "memoffset" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.0.1", + "autocfg", ] [[package]] @@ -1129,16 +1215,6 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -[[package]] -name = "mime_guess" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1147,65 +1223,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", - "autocfg 1.0.1", -] - -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", ] [[package]] name = "mio" -version = "0.7.14" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", - "miow 0.3.7", - "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", ] [[package]] @@ -1220,9 +1254,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -1252,27 +1286,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" [[package]] -name = "net2" -version = "0.2.37" +name = "nix" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" dependencies = [ - "cfg-if 0.1.10", + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", "libc", - "winapi 0.3.9", + "memoffset", ] [[package]] name = "nix" -version = "0.23.1" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ + "autocfg", "bitflags 1.3.2", - "cc", "cfg-if 1.0.0", "libc", - "memoffset 0.6.5", + "memoffset", + "pin-utils", ] [[package]] @@ -1296,12 +1333,15 @@ dependencies = [ ] [[package]] -name = "ntapi" -version = "0.3.6" +name = "num-bigint" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "winapi 0.3.9", + "autocfg", + "num-integer", + "num-traits", + "rand 0.8.5", ] [[package]] @@ -1310,17 +1350,17 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.1", + "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ - "autocfg 1.0.1", + "autocfg", ] [[package]] @@ -1362,43 +1402,44 @@ dependencies = [ "objc", ] -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "opensesame" -version = "0.7.9" +version = "0.8.0" dependencies = [ + "async-ssh2-tokio", "bme280", "chrono", "elektra", - "futures 0.3.19", + "futures", "gettext-rs", "gpio-cdev", "i2cdev", "libmodbus", "linux-embedded-hal", "mlx9061x", + "nix 0.25.1", "reqwest", + "serde", + "serde_json", "serial_test", "signal-hook", "signal-hook-tokio", - "ssh2", "sunrise", "systemstat", - "tokio 1.15.0", + "tokio", + "tokio-util 0.6.10", ] [[package]] @@ -1427,7 +1468,7 @@ version = "0.9.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" dependencies = [ - "autocfg 1.0.1", + "autocfg", "cc", "libc", "pkg-config", @@ -1435,15 +1476,10 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.9.0" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.2", - "rustc_version", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" @@ -1457,69 +1493,78 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "instant", - "lock_api 0.4.5", - "parking_lot_core 0.8.5", + "lock_api 0.4.6", + "parking_lot_core 0.9.8", ] [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if 0.1.10", "cloudabi", "libc", "redox_syscall 0.1.57", - "rustc_version", - "smallvec 0.6.14", - "winapi 0.3.9", + "smallvec", + "winapi", ] [[package]] name = "parking_lot_core" -version = "0.7.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if 1.0.0", "libc", - "redox_syscall 0.1.57", - "smallvec 1.8.0", - "winapi 0.3.9", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", ] [[package]] -name = "parking_lot_core" -version = "0.8.5" +name = "password-hash" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall 0.2.10", - "smallvec 1.8.0", - "winapi 0.3.9", + "base64ct", + "rand_core 0.6.4", + "subtle", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "pbkdf2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] [[package]] -name = "percent-encoding" -version = "1.0.1" +name = "pbkdf2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" @@ -1539,12 +1584,51 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1576,16 +1660,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "publicsuffix" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4ce31ff0a27d93c8de1849cf58162283752f065a90d508f1105fa6c9a213f" -dependencies = [ - "idna 0.2.3", - "url 2.2.2", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -1601,46 +1675,28 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.7", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi 0.3.9", -] - [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] -name = "rand_chacha" -version = "0.1.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "autocfg 0.1.7", - "rand_core 0.3.1", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -1654,36 +1710,31 @@ dependencies = [ ] [[package]] -name = "rand_core" +name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.4.2", + "ppv-lite86", + "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", ] [[package]] -name = "rand_hc" -version = "0.1.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.10", ] [[package]] @@ -1696,148 +1747,184 @@ dependencies = [ ] [[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" +name = "redox_syscall" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.9", -] +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] -name = "rand_os" -version = "0.1.3" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.9", + "bitflags 1.3.2", ] [[package]] -name = "rand_pcg" -version = "0.1.2" +name = "redox_syscall" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "autocfg 0.1.7", - "rand_core 0.4.2", + "bitflags 1.3.2", ] [[package]] -name = "rand_xorshift" -version = "0.1.1" +name = "redox_users" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.10", + "redox_syscall 0.2.16", + "thiserror", ] [[package]] -name = "rdrand" -version = "0.4.0" +name = "regex" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ - "rand_core 0.3.1", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "redox_syscall" -version = "0.1.57" +name = "regex-syntax" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] -name = "redox_syscall" -version = "0.2.10" +name = "remove_dir_all" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "bitflags 1.3.2", + "winapi", ] [[package]] -name = "regex" -version = "1.6.0" +name = "reqwest" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "russh" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "ae0efcc0f4cd6c062c07e572ce4b806e3967fa029fcbfcc0aa98fb5910a37925" +dependencies = [ + "aes", + "aes-gcm", + "async-trait", + "bitflags 2.4.0", + "byteorder", + "chacha20", + "ctr", + "curve25519-dalek", + "digest", + "flate2", + "futures", + "generic-array", + "hex-literal", + "hmac", + "log", + "num-bigint", + "once_cell", + "poly1305", + "rand 0.8.5", + "russh-cryptovec", + "russh-keys", + "sha1 0.10.5", + "sha2", + "subtle", + "thiserror", + "tokio", + "tokio-util 0.7.2", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "russh-cryptovec" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "c3fdf036c2216b554053d19d4af45c1722d13b00ac494ea19825daf4beac034e" dependencies = [ - "winapi 0.3.9", + "libc", + "winapi", ] [[package]] -name = "reqwest" -version = "0.9.24" +name = "russh-keys" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" +checksum = "557ab9190022dff78116ebed5e391abbd3f424b06cd643dfe262346ab91ed8c9" dependencies = [ - "base64", - "bytes 0.4.12", - "cookie", - "cookie_store", - "encoding_rs", - "flate2", - "futures 0.1.31", - "http", - "hyper", - "hyper-tls", + "aes", + "async-trait", + "bcrypt-pbkdf", + "bit-vec", + "block-padding", + "byteorder", + "cbc", + "ctr", + "data-encoding", + "dirs", + "ed25519-dalek", + "futures", + "hmac", + "inout", "log", - "mime", - "mime_guess", - "native-tls", + "md5", + "num-bigint", + "num-integer", + "pbkdf2 0.11.0", + "rand 0.7.3", + "rand_core 0.6.4", + "russh-cryptovec", "serde", - "serde_json", - "serde_urlencoded", - "time 0.1.44", - "tokio 0.1.22", - "tokio-executor", - "tokio-io", - "tokio-threadpool", - "tokio-timer", - "url 1.7.2", - "uuid", - "winreg", + "sha2", + "thiserror", + "tokio", + "tokio-stream", + "yasna", ] -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1850,7 +1937,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.18", ] [[package]] @@ -1866,7 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1907,6 +2003,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1915,45 +2017,45 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.133" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.133" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 1.0.85", + "syn 2.0.29", ] [[package]] name = "serde_json" -version = "1.0.74" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ - "itoa 1.0.1", + "itoa", "ryu", "serde", ] [[package]] name = "serde_urlencoded" -version = "0.5.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "dtoa", - "itoa 0.4.8", + "form_urlencoded", + "itoa", + "ryu", "serde", - "url 1.7.2", ] [[package]] @@ -2008,12 +2110,34 @@ dependencies = [ "sha1_smol", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + [[package]] name = "sha1_smol" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "0.1.1" @@ -2053,23 +2177,20 @@ checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" dependencies = [ "libc", "signal-hook", - "tokio 1.15.0", + "tokio", ] [[package]] -name = "slab" -version = "0.4.5" +name = "signature" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" [[package]] -name = "smallvec" -version = "0.6.14" +name = "slab" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "smallvec" @@ -2086,6 +2207,16 @@ dependencies = [ "embedded-crc-macros", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spidev" version = "0.5.1" @@ -2094,19 +2225,17 @@ checksum = "5c43e891adf1abc1e09b10f80c8d91959ee20ec28425c6dadac78844ba4c709f" dependencies = [ "bitflags 1.3.2", "libc", - "nix", + "nix 0.23.1", ] [[package]] -name = "ssh2" -version = "0.9.3" +name = "spki" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269343e64430067a14937ae0e3c4ec604c178fb896dde0964b1acd22b3e2eeb1" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ - "bitflags 1.3.2", - "libc", - "libssh2-sys", - "parking_lot 0.11.2", + "base64ct", + "der", ] [[package]] @@ -2125,7 +2254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -2157,7 +2286,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha1", + "sha1 0.6.1", "syn 1.0.85", ] @@ -2167,21 +2296,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes 0.4.12", -] - [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "sunrise" version = "1.0.0" @@ -2214,25 +2340,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.85", - "unicode-xid", -] - [[package]] name = "sysfs_gpio" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ef9c9bcbfeb596ce4da59b2c59736235f35dcd516f03958ea10834473224157" dependencies = [ - "nix", + "nix 0.23.1", ] [[package]] @@ -2246,7 +2360,7 @@ dependencies = [ "lazy_static", "libc", "nom 7.1.1", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2264,9 +2378,9 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "libc", - "redox_syscall 0.2.10", + "redox_syscall 0.2.16", "remove_dir_all", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2296,6 +2410,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "time" version = "0.1.44" @@ -2304,7 +2438,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2319,7 +2453,7 @@ dependencies = [ "stdweb", "time-macros", "version_check", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2362,190 +2496,133 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "mio 0.6.23", - "num_cpus", - "tokio-current-thread", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", -] - -[[package]] -name = "tokio" -version = "1.15.0" +version = "1.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" dependencies = [ - "bytes 1.2.1", + "bytes", "libc", "memchr", - "mio 0.7.14", + "mio", "num_cpus", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", + "socket2", "tokio-macros", - "winapi 0.3.9", + "winapi", ] [[package]] -name = "tokio-buf" -version = "0.1.1" +name = "tokio-macros" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "bytes 0.4.12", - "either", - "futures 0.1.31", + "proc-macro2", + "quote", + "syn 1.0.85", ] [[package]] -name = "tokio-current-thread" -version = "0.1.7" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "futures 0.1.31", - "tokio-executor", + "native-tls", + "tokio", ] [[package]] -name = "tokio-executor" -version = "0.1.10" +name = "tokio-stream" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ - "crossbeam-utils", - "futures 0.1.31", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "tokio-io" -version = "0.1.13" +name = "tokio-util" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", + "bytes", + "futures-core", + "futures-sink", "log", + "pin-project-lite", + "tokio", ] [[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.85", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" +name = "tokio-util" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ - "crossbeam-utils", - "futures 0.1.31", - "lazy_static", - "log", - "mio 0.6.23", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] -name = "tokio-sync" -version = "0.1.8" +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures 0.1.31", -] +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "tokio-tcp" -version = "0.1.4" +name = "tracing" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "iovec", - "mio 0.6.23", - "tokio-io", - "tokio-reactor", + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "tokio-threadpool" -version = "0.1.18" +name = "tracing-attributes" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ - "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils", - "futures 0.1.31", - "lazy_static", - "log", - "num_cpus", - "slab", - "tokio-executor", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] -name = "tokio-timer" -version = "0.2.13" +name = "tracing-core" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ - "crossbeam-utils", - "futures 0.1.31", - "slab", - "tokio-executor", + "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "try_from" -version = "0.3.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" -dependencies = [ - "cfg-if 0.1.10", -] +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] -name = "unicase" -version = "2.6.0" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" @@ -2581,14 +2658,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] -name = "url" -version = "1.7.2" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", + "crypto-common", + "subtle", ] [[package]] @@ -2598,18 +2674,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", - "idna 0.2.3", + "idna", "matches", - "percent-encoding 2.1.0", -] - -[[package]] -name = "uuid" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -dependencies = [ - "rand 0.6.5", + "percent-encoding", ] [[package]] @@ -2638,12 +2705,10 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "want" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "futures 0.1.31", - "log", "try-lock", ] @@ -2659,11 +2724,17 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2671,24 +2742,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.85", + "syn 2.0.29", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2696,22 +2779,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.85", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "which" @@ -2733,12 +2826,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -2749,12 +2836,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -2767,7 +2848,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2776,21 +2857,94 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "winreg" -version = "0.6.2" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi 0.3.9", + "cfg-if 1.0.0", + "windows-sys", ] [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "yasna" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "bit-vec", + "num-bigint", ] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index c3048f5..5b5ae2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "opensesame" -version = "0.7.9" +version = "0.8.0" authors = ["Opensesame Contributors"] -edition = "2018" +edition = "2021" readme = "README.md" license = "BSD" description = "Awesome home automation" @@ -44,7 +44,7 @@ i2cdev = "0.5.1" elektra = { version = "0.11.0", features = ["pkg-config"] } -reqwest = "0.9.24" +reqwest = { version = "0.11", features = ["json"] } bme280 = "0.3.0" mlx9061x = "0.2.1" @@ -52,17 +52,22 @@ linux-embedded-hal = { version = "0.3", features = ["gpio_cdev"] } systemstat = "0.1.11" -ssh2 = "0.9.3" +nix = "0.25" + chrono = "0.4.19" signal-hook = "0.3.14" signal-hook-tokio = "0.3.1" tokio = { version = "1.15.0", features = ["full"] } +tokio-util = "0.6.9" +async-ssh2-tokio = "0.7.1" futures = "0.3.19" - gettext-rs = "0.7.0" +serde = "1.0.188" +serde_json = "1.0.106" + serial_test = "0.4.0" # TODO, remove when issues.libelektra.org/4466 is fixed sunrise = "1.0.0" diff --git a/debian/service b/debian/service index d3b1f84..c00ae0d 100644 --- a/debian/service +++ b/debian/service @@ -1,7 +1,7 @@ [Unit] Description=opensesame -Wants=network-online.target -After=network.target network-online.target +Wants=network-online.target nss-lookup.target +After=network.target network-online.target nss-lookup.target [Service] Type=simple diff --git a/doc/Testreport_Async_Refactor.md b/doc/Testreport_Async_Refactor.md new file mode 100644 index 0000000..dcdffc9 --- /dev/null +++ b/doc/Testreport_Async_Refactor.md @@ -0,0 +1,479 @@ +# Testing Async +This is the testreport of the async refector of opensesame. (`-`) means that the test failed, (`~`) something went wrong but it works and (`+`) means that everything went as expected. Here is a list of the modules which need to be tested. + +1. **Signals** +2. **Buttons (+Bell)** +3. **Buttons+Garage** +4. **Sensors** +5. **ModIR** +6. **Environment** +7. **Weatherstation** +8. **Battery** +9. **Watchdog** +10. **Ping** + + +## Signals +### Configuration +```toml +nextcloud.chat = "" +nextcloud.chat.licht = "" +nextcloud.chat.ping = "" +nextcloud.chat.commands = "" +nextcloud.user = "" +nextcloud.pass = "" +nextcloud.url = "" +nextcloud.format.datetime = "%d.%m.%Y %H:%M:%S" +nextcloud.format.time = "%H:%M:%S" +validator.test1 = "14, 15 ,13 ,15, 11, 15, 7, 15" +validator.test2 = "14, 12, 14, 15, 11, 15" +buttons.enable = 0 +bell.enable = 0 +garage.enable = 0 +sensors.enable = 0 +environment.enable = 0 +ir.enable = 0 +weatherstation.enable = 0 +bat.enable = 0 +watchdog.enable = 0 +ping.enable = 0 +``` +### (`+`): Is working and handles signals. + +## Buttons +### Configuration +```toml +... +buttons.enable = 1 +... +``` +### (`+`): light button; light switch; light permanent; light time extended; pin validation +### (`~`): First entering of pin caused time `sequence timeout`, the timeout was too fast. Besides this one failure it worked fine. Cloud be a startup issue. + +## Buttons + Bell +### Configuration +```toml +... +buttons.enable = 1 +bell.enable = 1 +... +``` +### (`+`): bell and buttons worked + +## Buttons + Garage +### Configuration +```toml +... +buttons.enable = 1 +garage.enable = 1 +... +``` +### (`+`): endposition garage door; TasterTorUnten (no NC message); TasterUnten (no NC message); TasterOben +### (`~`): TasterTorOben switched only the light indoor at the first time, after a retry it switch both + +## Sensors +### Configuration +```toml +... +sensors.enable = 1 +sensors.device = "/dev/ttyACM0" +sensors."#0".loc = "Wohnzimmer" +sensors."#0".alarm = 50 +sensors."#0".chat = 20 +sensors."#1".loc = "Badezimmer" +sensors."#1".alarm = 70 +sensors."#1".chat = 35 +sensors."#2".loc = "Küche" +sensors."#2".alarm = 5 +sensors."#2".chat = 3 +... +``` +For simulating the sensors we used the methode with is described in [DevelopmentSetup](./DevelopmentSetup.md). With the following script: +```bash +COUNTER=0; +while true; do + let COUNTER=COUNTER+1 + if [ $COUNTER -eq 20 ]; then + data="23 35 4 0 0 0 0 0 0 0 0 0" + elif [ $COUNTER -eq 100 ]; then + data="55 80 10 0 0 0 0 0 0 0 0 0" + elif [ $COUNTER -eq 130 ]; then + data="1005 2015 1102 0 0 0 0 0 0 0 0 0" + else + data="0 0 0 0 0 0 0 0 0 0 0 0" + fi + echo "$data" + sleep 60 +done +``` +### (`+`): Chat and Alarm trigger worked as expected + +## Weatherstation +### Configuration +```toml +... +weatherstation.enable = 1 +weatherstation.data.interval = 60 +weatherstation.opensensemap.id = "64cb602193c69500072a580f" +weatherstation.opensensemap.token = "7bbb014ffbf974255caef2f88525b0512bd0817d9d222f70c7741a4a9cd56c6c" +... +``` +### (`+`): Works as expected, sends warning to Nextcloud and updates opensensemap.org + +## Buttons + Bell + PWR + Envirionment + Battery + Watchdog +### Configuration +```toml +nextcloud.chat = "" +nextcloud.chat.licht = "" +nextcloud.chat.ping = "" +nextcloud.chat.commands = "" +nextcloud.user = "" +nextcloud.pass = "" +nextcloud.url = "https://nextcloud.markus-raab.org/nextcloud" +nextcloud.format.datetime = "%d.%m.%Y %H:%M:%S" +nextcloud.format.time = "%H:%M:%S" +validator.felix = "14, 15 ,13 ,15, 11, 15, 7, 15" +validator.sophie = "14, 12, 14, 15, 11, 15" +buttons.enable = 1 +bell.enable = 1 +pwr.enable = 1 +environment.enable = 1 +bat.enable = 1 +watchdog.enable = 1 +watchdog.path = "/dev/watchdog" +watchdog.interval = 10 +environment.device = "/dev/i2c-2" +environment.name = "Wohnzimmer" +environment.data.interval = 60 +``` +### Hardware Test and Documentation +#### Buttons + +
+ +| **Pin-Buttons** | **Result** | +| -------------------- | ------------ | +| Validation | `+` | +| Input Too Long | `+` | +| Input Timeout | `+` | + +
+
+ +| **Light-Buttons** | **Result** | +| --------------------- | ------------ | +| Light extended | `+` | +| Remove permanet Light | `+` | + +
+
+ +| **Light-Taster** | **Result** | +| ---------------- | ---------- | +| Light extended | `+` | +| Permanent Light | `+` | +| Remove permanet Light | `+` | + +
+ +#### Bell +
+ +| **Bell-Button** | **Result** | +| ---------------- | ---------- | +| Bell ringing | `+` | + +
+
+ +| **Bell-Taster** | **Result** | +| ---------------- | ---------- | +| Bell ringing | `+` | + +
+ +#### PWR +
+ +| **PWR reset** | **Result** | +| ---------------- | ---------- | +| do reset of MOD-IO2-Boards | `+` | + +
+ +#### Environment +
+ +| **Air Quality** | **Result** | +| ---------------- | ---------- | +| Information about environment data | `+` | + +
+ +#### Battery +
+ +| **Check Capacity** | **Result** | +| ---------------- | ---------- | +| Waring if capacity is below 50% | `+` | + +
+ +#### Watchdog +
+ +| **Stop triggering to watchdog** | **Result** | +| ---------------- | ---------- | +| Reboot | `+` | + +
+ +## Buttons + Audio Bell + Garage + Battery + Watchdog +### Configuration +```toml +nextcloud.chat = "" +nextcloud.chat.licht = "" +nextcloud.chat.ping = "" +nextcloud.chat.commands = "" +nextcloud.user = "" +nextcloud.pass = "" +nextcloud.url = "https://nextcloud.markus-raab.org/nextcloud" +nextcloud.format.datetime = "%d.%m.%Y %H:%M:%S" +nextcloud.format.time = "%H:%M:%S" +validator.felix = "14, 15 ,13 ,15, 11, 15, 7, 15" +validator.sophie = "14, 12, 14, 15, 11, 15" +buttons.enable = 1 +audio.bell = "/home/olimex/bell_sound.ogg" +garage.enable = 1 +bat.enable = 1 +watchdog.enable = 1 +watchdog.path = "/dev/watchdog" +watchdog.interval = 10 +``` +### Hardware Test and Documentation +#### Buttons +
+ +| **Pin-Buttons** | **Result** | +| --------------- | ------------ | +| Validation | `+` | +| Input Too Long | `+` | +| Input Timeout | `+` | + +
+
+ +| **Light-Buttons** | **Result** | +| --------------------- | ------------ | +| Light extended | `+` | +| Remove permanet Light | `+` | + +
+
+ +| **Light-Taster** | **Result** | +| ---------------- | ---------- | +| Light extended | `+` | +| Permanent Light | `+` | +| Remove permanet Light | `+` | + +
+ +#### Audio Bell +
+ +| **Audio Output** | **Result** | +| ---------------- | ---------- | +| Triggered by Nextcloud | `+` | +| Triggered by Signals | `+` | +| Triggered by Bell-Button | `+` | + +
+ +#### Garage + +
+ +| **End position garage door** | **Result** | +| --------------------------- | ---------- | +| Information opened | `+` | +| Information closed | `+` | + +
+
+ +| **TasterTorUnten and TasterUnten** | **Result** | +| ------------------ | ---------- | +| Open Door | `+` | + +
+
+ +| **TasterTorOben** | **Result** | +| --------------------- | ---------- | +| Switch both Lights on | `+` | + +
+
+ +| **TasterOben** | **Result** | +| --------------------- | ---------- | +| Switch inner Light on | `+` | + +
+ +#### Battery +
+ +| **Check Capacity** | **Result** | +| ---------------- | ---------- | +| Waring if capacity is below 50% | `+` | + +
+ +#### Watchdog +
+ +| **Stop triggering to watchdog** | **Result** | +| ---------------- | ---------- | +| Reboot | `+` | + +
+ + +## Weatherstation + Sensors + Audio Bell + Battery + Watchdog. +### Configuration +```toml +nextcloud.chat = "" +nextcloud.chat.licht = "" +nextcloud.chat.ping = "" +nextcloud.chat.commands = "" +nextcloud.user = "" +nextcloud.pass = "" +nextcloud.url = "https://nextcloud.markus-raab.org/nextcloud" +nextcloud.format.datetime = "%d.%m.%Y %H:%M:%S" +nextcloud.format.time = "%H:%M:%S" +audio.bell = "/home/olimex/bell_sound.ogg" +bat.enable = 1 +sensors.enable = 1 +sensors.device = "/home/olimex/dev/ttyACM0" +sensors."#0".loc = "0" +sensors."#1".loc = "1" +sensors."#2".loc = "2" +sensors."#3".loc = "3" +sensors."#4".loc = "4" +sensors."#5".loc = "5" +sensors."#6".loc = "6" +sensors."#7".loc = "7" +sensors."#8".loc = "8" +sensors."#9".loc = "9" +sensors."#10".loc = "10" +sensors."#11".loc = "11" +weatherstation.enable = 1 +weatherstation.opensensemap.id = "" +weatherstation.opensensemap.token = "" +weatherstation.data.interval = 60 +watchdog.enable = 1 +watchdog.path = "/dev/watchdog" +watchdog.interval = 10 + +``` +### Hardware Test and Documentation +#### Weatherstation +
+ +| **Warnings** | **Result** | +| ---------------- | ---------- | +| Warnings are send to Nextcloud | `+` | + +
+
+ +| **Opensensemap** | **Result** | +| ---------------- | ---------- | +| Publish to Opensensemap | `+` | + +
+ +#### Sensors +
+ +| **Warning and Alarms** | **Result** | +| ---------------- | ---------- | +| Information in Nextcloud Chat | `+` | + +
+ +**Bash Script:** + +```bash +#!/bin/bash + +echo "152 237 279 275 177 166 90 440 59 370 423 9" +sleep 60; +echo "153 237 279 258 177 166 106 441 81 370 429 22" +sleep 60; +echo "293 305 440 419 296 274 265 565 215 513 548 80" +sleep 60; +echo "340 349 505 426 356 369 364 628 344 576 594 145" +sleep 60; +echo "340 366 495 463 339 389 372 654 369 598 597 155" +sleep 60; +echo "371 388 514 465 348 395 392 676 410 625 618 180" +sleep 60; +echo "393 399 505 423 357 403 395 692 426 642 629 187" +sleep 60; +echo "359 395 491 414 352 391 357 693 370 639 595 144" +sleep 60; +echo "318 374 453 380 310 352 313 637 296 588 570 108" +sleep 60; +echo "304 365 439 368 298 328 283 618 244 563 562 82" +sleep 60; +echo "290 361 421 376 285 312 259 596 220 533 552 76" +sleep 60; +echo "270 353 393 347 264 278 218 571 169 487 536 53" +sleep 60; +echo "249 347 362 338 240 241 175 554 118 454 518 30" +sleep 60; +echo "236 342 343 341 228 227 160 545 103 442 507 25" +sleep 60; +echo "224 339 330 294 217 215 146 537 89 433 496 20" +sleep 60; +echo "216 335 318 304 209 207 138 533 83 429 489 18" +sleep 60; +echo "208 330 309 324 203 201 129 527 76 424 482 15" +sleep 60; +echo "201 326 301 275 197 195 122 524 73 423 477 14" + +while true; do + sleep 60; + echo "196 330 303 298 192 190 115 517 69 416 469 12" +done +``` +##### Audio Bell +
+ +| **Audio Output** | **Result** | +| ---------------- | ---------- | +| Triggered by Nextcloud | `+` | +| Triggered by Signals | `+` | + +
+ +#### Battery +
+ +| **Check Capacity** | **Result** | +| ---------------- | ---------- | +| Waring if capacity is below 50% | `+` | + +
+ +#### Watchdog +
+ +| **Stop triggering to watchdog** | **Result** | +| ---------------- | ---------- | +| Reboot | `+` | + +
\ No newline at end of file diff --git a/doc/modules.md b/doc/modules.md new file mode 100644 index 0000000..c0afc6c --- /dev/null +++ b/doc/modules.md @@ -0,0 +1,56 @@ +# Opensesame + +## audio.rs +This module handles audio output for playing fire alarms and bell sounds. This module can receive commands from the Module Buttons (Bell), Environment (FireAlarm), Nextcloud (FireAlarm, Bell), and Signals (FireAlarm, Bell). + +## bat.rs +Checks the battery capacity every ten minutes and outputs to Nextcloud if it falls below 50%. If it goes below 50%, the threshold for the next Nextcloud message is set to 40%. + +## buttons.rs +This module implements the `do_reset` if an error occurs on the MOD-IO2 modules. In the `async get_background_task`, we implemented how the buttons were handled in the old main, including the validator and command receiver. The command receiver receives commands like `opendoor` (used by Garage, Nextcloud), `ring_bell` (used by Environment, Signals), `switchlights` (used by Garage, Nextcloud), and `RingBellAlarm` (used by Signals). + +## clima_sensor_us.rs +This module works independently, sending warnings to Nextcloud and publishing to opensensemap. We needed to implement `Send` to use libmodbus with async functions. + +## config.rs +No changes were made to this module. + +## environment.rs +Changed the functions `rememberBaseline`, `restoreBaseline`, and added a `Mutex` of state. We moved `handle_environment` from the main into the `get_background_task`. This module receives commands (RestoreBaseline, RememberBaseline) from Signals. + +## garage.rs +This module is checked at intervals of 10 milliseconds, triggering Nextcloud Chat or Buttons commands if the button is pressed. + +## mod_ir_temp.rs +This module is triggered at given intervals and warns by sending Nextcloud messages. + +## nextcloud.rs +Implements two loops: one for sending (`message_sender_loop`) messages and status to Nextcloud, and the other for receiving (`command_loop`) messages/commands from Nextcloud. Commands can be sent via Nextcloud chat by typing "\opensesame" to open the door, or other commands like "\ring_bell", "\fire_alarm", "\status", and "\switchlights true true". + +## ping.rs +This module sends a ping message to Nextcloud if it receives the `SendPing` event. Other functions update `Env`, `EnvStatus`, `EnvError`, and `BatCapacity`, but these commands aren't used yet. They need to be triggered at the right spot in Environment and Battery to keep the ping up to date. + +## pwr.rs +No significant changes were made to this module. + +## sensors.rs +No major changes, only the `get_background_task` was added with a similar implementation as in the old version. + +## signals.rs +This module listens to system signals and executes the same events as in the old version. + +## ssh.rs +Changed to an async function. + +## types.rs +This module contains the error types defined so far. + +## validator.rs +No changes were made to this module. + +## watchdog.rs +This background task is triggered every few seconds and writes to the specified file. The `test_get_background_task` function is used to simulate the watchdog stopping writing to the watchdog file. + +## main.rs +In this loop, we initialize every module if it is enabled. Additionally, the MPSC (Multiple Producer, Single Consumer) channels are initialized here. To configure each module without encountering issues related to multiple access to one config object, we read the configuration in the main. + diff --git a/files/emu_dev/emulate.sh b/files/emu_dev/emulate.sh new file mode 100755 index 0000000..0cfc028 --- /dev/null +++ b/files/emu_dev/emulate.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +socat -d -d pty,raw,echo=0,link=../fakettyACM0 exec:./payload.sh \ No newline at end of file diff --git a/files/emu_dev/payload.sh b/files/emu_dev/payload.sh new file mode 100755 index 0000000..06286d7 --- /dev/null +++ b/files/emu_dev/payload.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +while true; do + data="12 23 45 66 554 34 23 54 12 32 32 43" + echo "$data" + sleep 10 +done \ No newline at end of file diff --git a/files/opensesame.spec b/files/opensesame.spec index e3d2c26..2cfe2e9 100644 --- a/files/opensesame.spec +++ b/files/opensesame.spec @@ -54,6 +54,11 @@ description = which chat to use for sending ping messages. Note: this chat will required = check/length/max = 8 +[nextcloud/chat/commands] +description = which chat is used to send commands to Opensesame. +required = +check/length/max = 8 + [nextcloud/format/time] description=Format to be used for formatting time within Nextcloud messages, e.g. when entry gets prohibited because of time. By default ISO 8601 (Hour-minute-second format). Example is locales time. see/#0 = nextcloud/format/date @@ -116,6 +121,14 @@ type = boolean example = 1 default = 0 +[watchdog/interval] +description=Trigger interval in seconds for Watchdog +default = 10 + +[watchdog/path] +description=Device path of Watchdog +default = "/dev/watchdog" + [environment/device] description=Which device to use for the environment sensor. /dev/null means that no environment sensor is connected. example = "/dev/i2c" @@ -123,9 +136,14 @@ default = "/dev/null" [environment/data/interval] description=How often to get new data (default: every 60 seconds, which is the highest interval) -default = 6000 +default = 60 type = unsigned_short +[sensors/enable] +description = If the sensors module is enabled +type = boolean +default = 0 + [sensors] description = a list of up to 12 MQ135 sensors @@ -161,6 +179,9 @@ type = unsigned_short description = maximum value measured during calibration type = unsigned_short +[sensors/device] +description = path of the sensor device +default = "/dev/ttyACM0" [weatherstation/enable] description = enables/disables weatherstation @@ -173,6 +194,7 @@ description = Which Opensensemap senseBoxes should be connected to the weather s [weatherstation/opensensemap/token] description = Access-Token for Opensensemap senseBoxes, see doc/Opensensemap.md + [ir/enable] description=enables/disables MOD-IR-TEMP sensor example=1 diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..f467a3d --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,119 @@ +use futures::never::Never; + +use gettextrs::gettext; +use tokio::{ + io, + process::Command, + spawn, + sync::mpsc::{Receiver, Sender}, +}; +use tokio_util::sync::CancellationToken; + +use crate::{ + nextcloud::{NextcloudChat, NextcloudEvent}, + ssh::exec_ssh_command, + types::ModuleError, +}; + +// play audio file with argument. If you do not have an argument, simply pass --quiet again +async fn play_audio_file( + file: String, + _arg: &str, + cancellation_token: CancellationToken, +) -> Result<(), io::Error> { + if file != "/dev/null" { + let mut command = Command::new("ogg123").arg("--quiet").arg(file).spawn()?; + + // Wait for the process to finish + let _ = tokio::select! { + result = command.wait() => result, + _ = cancellation_token.cancelled() => { + // If cancellation is requested, kill the audio playback process + let _ = command.kill().await; + return Ok(()); + } + }; + } + Ok(()) +} + +pub enum AudioEvent { + Bell, + FireAlarm, +} + +pub struct Audio { + bell_path: String, + fire_alarm_path: String, +} + +impl Audio { + pub fn new(bell_path: String, fire_alarm_path: String) -> Self { + Audio { + bell_path, + fire_alarm_path, + } + } + + pub async fn get_background_task( + self, + mut audio_receiver: Receiver, + nextcloud_sender: Sender, + ) -> Result { + let mut maybe_cancellation_token: Option = Option::None; + + while let Some(event) = audio_receiver.recv().await { + if maybe_cancellation_token.is_some() { + maybe_cancellation_token.unwrap().cancel(); + }; + maybe_cancellation_token = Option::Some(CancellationToken::new()); + match event { + AudioEvent::Bell => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🔔 Ringing the Audio Bell "), + )) + .await?; + spawn(play_audio_file( + self.bell_path.clone(), + "", + maybe_cancellation_token.clone().unwrap(), + )); + } + AudioEvent::FireAlarm => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🚨 Audio Fire Alarm!"), + )) + .await?; + spawn(play_audio_file( + self.fire_alarm_path.clone(), + "--repeat", + maybe_cancellation_token.clone().unwrap(), + )); + let nextcloud_sender_clone = nextcloud_sender.clone(); + spawn(async move { + let ssh_result = + exec_ssh_command(String::from("killall -SIGUSR2 opensesame")).await; + if let Err(err) = ssh_result { + let _ = nextcloud_sender_clone + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!( + "Couldn't send SIGUSR2 to other opensesame instance: {}", + err + ), + )) + .await; + } + }); + } + } + } + Err(ModuleError::new(String::from( + "audio background task exited", + ))) + } +} diff --git a/src/bat.rs b/src/bat.rs index 898e2f2..524b367 100644 --- a/src/bat.rs +++ b/src/bat.rs @@ -1,21 +1,78 @@ // workaround until https://github.com/svartalf/rust-battery/issues/96 is solved -const CAPACITY_FILE: &'static str = "/sys/class/power_supply/axp20x-battery/capacity"; +const CAPACITY_FILE: &str = "/sys/class/power_supply/axp20x-battery/capacity"; +use futures::never::Never; +use gettextrs::gettext; use std::fmt; use std::fs; +use systemstat::Duration; +use tokio::sync::mpsc::Sender; +use tokio::time::interval; -pub struct Bat {} +use crate::nextcloud::NextcloudChat; +use crate::nextcloud::NextcloudEvent; +use crate::types::ModuleError; + +const START_CAPACITY_THRESHOLD: u8 = 50; + +pub struct Bat { + capacity: u8, + capacity_threshold: u8, +} impl Bat { pub fn new() -> Self { - Self {} + Self { + capacity: 0, + capacity_threshold: START_CAPACITY_THRESHOLD, + } } pub fn capacity(&self) -> u8 { match fs::read_to_string(CAPACITY_FILE) { - Ok(str) => return str.trim_end().parse::().unwrap(), - Err(_err) => return 100, + Ok(str) => str.trim_end().parse::().unwrap(), + Err(_err) => 0, + } + } + + pub async fn get_background_task( + mut self, + nextcloud_sender: Sender, + ) -> Result { + let mut interval = interval(Duration::from_secs(600)); + loop { + interval.tick().await; + let new_capacity = self.capacity(); + + if new_capacity != self.capacity { + self.capacity = new_capacity; + if self.capacity < self.capacity_threshold { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🪫 Battery Capacity is below {}% at {}%", + self.capacity_threshold, + self.capacity + ), + )) + .await?; + if self.capacity_threshold - 10 > 0 { + self.capacity_threshold -= 10; + } else { + self.capacity_threshold = 0; + } + } else if self.capacity == 100 { + self.capacity_threshold = START_CAPACITY_THRESHOLD; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("🔋 Battery Capacity is back to {}%", self.capacity), + )) + .await?; + } + } } } } diff --git a/src/buttons.rs b/src/buttons.rs index a275c71..9a89fcd 100644 --- a/src/buttons.rs +++ b/src/buttons.rs @@ -1,8 +1,30 @@ +use std::cmp::Ordering; + +use chrono::Datelike; +use chrono::Local; +use chrono::Timelike; +use futures::never::Never; +use gettextrs::gettext; use i2cdev::core::*; use i2cdev::linux::LinuxI2CDevice; use i2cdev::linux::LinuxI2CError; +use sunrise::sunrise_sunset; +use systemstat::Duration; +use systemstat::{Platform, System}; + +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::time::interval; +use tokio::time::sleep; +use crate::audio::AudioEvent; use crate::config::Config; +use crate::nextcloud::NextcloudChat; +use crate::nextcloud::NextcloudEvent; +use crate::pwr::Pwr; + +use crate::types::ModuleError; +use crate::validator::{Validation, Validator}; +use crate::watchdog; pub struct Buttons { pub sequence: Vec, @@ -43,12 +65,18 @@ pub struct Buttons { #[derive(PartialEq, Debug)] pub enum StateChange { None, - Err(u8), Pressed(u8), Released(u8), LightsOff, } +pub enum CommandToButtons { + OpenDoor, + RingBell(u32, u32), // maybe implement it with interval + RingBellAlarm(u32), + SwitchLights(bool, bool, String), // This also need to implement the sending of a Message to nextcloud, which is now in Garage +} + const FAILED_COUNTER: u8 = 20; // = 200ms how long to wait after failure before resetting (*10ms) const BELL_MINIMUM_PERIOD: u32 = 20; // = 200ms shortest period time for bell @@ -223,7 +251,7 @@ impl Buttons { timeout_progress = 0; } self.light_timeout -= timeout_progress; - return ret; + ret } fn handle_bell(&mut self) { @@ -252,50 +280,53 @@ impl Buttons { } fn handle_wrong_input(&mut self) -> bool { - if self.wrong_input_timeout == 1 { - self.led_light = false; - self.led1 = false; - self.led2 = false; - self.led3 = false; - self.led4 = false; - self.wrong_input_timeout = 0; - return false; - } else if self.wrong_input_timeout > 1 { - self.wrong_input_timeout -= 1; - return false; + match self.wrong_input_timeout.cmp(&1) { + Ordering::Equal => { + self.led_light = false; + self.led1 = false; + self.led2 = false; + self.led3 = false; + self.led4 = false; + self.wrong_input_timeout = 0; + false + } + Ordering::Greater => { + self.wrong_input_timeout -= 1; + false + } + Ordering::Less => true, } - return true; } /// to be periodically called, e.g. every 10 ms - pub fn handle(&mut self) -> StateChange { + pub fn handle(&mut self) -> Result { // wait for recover if self.failed_counter > 1 { self.failed_counter -= 1; - return StateChange::None; + return Ok(StateChange::None); // try to recover } else if self.failed_counter == 1 { self.pins1 = PINS1_INIT; self.pins2 = PINS2_INIT; self.init(); self.failed_counter = 0; - return StateChange::None; + return Ok(StateChange::None); } let epins1 = self.board20.smbus_read_byte_data(GET_PORTS); - if epins1.is_err() { + if let Err(error) = epins1 { self.failed_counter = FAILED_COUNTER; self.led1 = true; self.led2 = true; - return StateChange::Err(20); + return Err(error); } let epins2 = self.board21.smbus_read_byte_data(GET_PORTS); - if epins2.is_err() { + if let Err(error) = epins2 { self.failed_counter = FAILED_COUNTER; self.led1 = true; self.led3 = true; - return StateChange::Err(21); + return Err(error); } let mut pins1 = epins1.unwrap() & ALL_BUTTONS; @@ -361,7 +392,7 @@ impl Buttons { .unwrap(); self.pins2 = pins2; } - return ret; + Ok(ret) } /// opensesame! @@ -447,7 +478,234 @@ impl Buttons { .smbus_write_byte_data(SET_RELAYS_ON, RELAY_LICHT_INNEN) .unwrap(); } - return ret; + ret + } + + async fn do_reset( + nextcloud_sender: Sender, + pwr: &mut Pwr, + ) -> Result<(), ModuleError> { + if pwr.enabled() { + pwr.switch(false); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋 Turned PWR_SWITCH off"), + )) + .await?; + sleep(Duration::from_millis(watchdog::SAFE_TIMEOUT)).await; + + pwr.switch(true); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋 Turned PWR_SWITCH on"), + )) + .await?; + sleep(Duration::from_millis(watchdog::SAFE_TIMEOUT)).await; + } + Ok(()) + } + + pub async fn get_background_task( + mut self, + mut validator: Validator, + mut pwr: Pwr, + time_format: String, + mut command_receiver: Receiver, + nextcloud_sender: Sender, + audio_sender: Sender, + location_latitude: f64, + location_longitude: f64, + ) -> Result { + let mut interval = interval(Duration::from_millis(10)); + loop { + interval.tick().await; + if let Ok(command) = command_receiver.try_recv() { + match command { + CommandToButtons::OpenDoor => { + self.open_door(); + } + CommandToButtons::RingBell(period, counter) => { + self.ring_bell(period, counter); + } + CommandToButtons::SwitchLights(inside, outside, text) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!("{}. {}", text, self.switch_lights(inside, outside)), + )) + .await?; + } + CommandToButtons::RingBellAlarm(period) => { + self.ring_bell_alarm(period); + } + } + } + + match self.handle() { + Ok(StateChange::Pressed(button)) => match button { + BUTTON_BELL => { + let now = Local::now(); + if now.hour() >= 7 && now.hour() <= 21 { + self.ring_bell(2, 5); + audio_sender.send(AudioEvent::Bell).await?; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🔔 Pressed button bell."), + )) + .await?; + } else { + self.show_wrong_input(); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🔕 Did not ring bell (button was pressed) because the time 🌜 is {}, {}", + now.format(&time_format) + ), + )) + .await?; + } + } + TASTER_INNEN => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!( + "💡 Pressed switch inside. {}.", + self.switch_lights(true, true) + ), + )) + .await?; + } + TASTER_AUSSEN => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!( + "💡 Pressed switch outside or light button. {}.", + self.switch_lights(false, true), + ), + )) + .await?; + } + TASTER_GLOCKE => { + let now = Local::now(); + if now.hour() >= 7 && now.hour() <= 21 { + self.ring_bell(5, 5); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🔔 Pressed switch bell."), + )) + .await?; + } else { + self.show_wrong_input(); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🔕 Did not ring bell (taster outside) because the time 🌜 is {}, {}", + now.format(&time_format) + ), + )) + .await?; + } + } + _ => panic!("🔘 Pressed {}", button), + }, + Ok(StateChange::Released(_button)) => (), + Ok(StateChange::LightsOff) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext("🕶️ Light was turned off."), + )) + .await?; + } + Ok(StateChange::None) => (), + Err(board) => { + let sys = System::new(); + let loadavg = sys.load_average().unwrap(); + //TODO implementierung von Ping Senden + nextcloud_sender + .send(NextcloudEvent::Chat(NextcloudChat::Ping, gettext!("⚠️ Error reading buttons of board {}. Load average: {} {} {}, Memory usage: {}, Swap: {}, CPU temp: {}", board, loadavg.one, loadavg.five, loadavg.fifteen, sys.memory().unwrap().total, sys.swap().unwrap().total, sys.cpu_temp().unwrap()))) + .await?; + Buttons::do_reset(nextcloud_sender.clone(), &mut pwr).await?; + } + } + // Validation required Buttons + // Validation benötigt button, somit threads abhängig!!!; channel zwischen buttons und validator? damit validator nur getriggert ist wenn buttons sich ändert? + // Validation start + let sequence = self.sequence.to_vec(); + match validator.validate(&mut self.sequence) { + Validation::Validated(user) => { + self.open_door(); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("🤗 Opened for {}", user), + )) + .await?; + let now = Local::now(); + let (sunrise, sunset) = sunrise_sunset( + location_latitude, + location_longitude, + now.year(), + now.month(), + now.day(), + ); + if now.timestamp() < sunrise || now.timestamp() > sunset { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!( + "💡 Switch lights in and out. {}", + self.switch_lights(true, true) + ), + )) + .await?; + } else { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!( + "🕶️ Don't switch lights as its day. Now: {} Sunrise: {} Sunset: {}", + now.timestamp(), + sunrise, + sunset + ), + )) + .await?; + } + } + Validation::Timeout => { + if sequence != vec![0, 15] { + self.show_wrong_input(); + self.ring_bell(20, 0); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("⌛ Timeout with sequence {}", format!("{:?}", sequence)), + )) + .await?; + } + } + Validation::SequenceTooLong => { + self.show_wrong_input(); + self.ring_bell(20, 0); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("⌛ Sequence {} too long", format!("{:?}", sequence)), + )) + .await?; + } + Validation::None => (), + } + } } } diff --git a/src/clima_sensor_us.rs b/src/clima_sensor_us.rs index fcf1a21..5714dff 100644 --- a/src/clima_sensor_us.rs +++ b/src/clima_sensor_us.rs @@ -7,12 +7,20 @@ extern crate libmodbus; use crate::config::Config; -use libmodbus::*; +use crate::nextcloud::{NextcloudChat, NextcloudEvent}; +use crate::types::ModuleError; +use futures::never::Never; +use gettextrs::gettext; +use libmodbus::{Modbus, ModbusClient, ModbusRTU, RequestToSendMode, SerialMode}; use reqwest::header::HeaderMap; use reqwest::Client; +use serde::Serialize; +use std::io; +use tokio::sync::mpsc::Sender; +use tokio::time::Interval; ///Constants -const DEVICE: &'static str = "/dev/ttyS5"; +const DEVICE: &str = "/dev/ttyS5"; const BAUDRATE: i32 = 9600; const PARITY: char = 'N'; const DATA_BITS: i32 = 8; @@ -60,11 +68,8 @@ const REG_GLOBAL_RADIATION: u16 = 0x8913; const REG_PITCH_MAGNETIC_COMPASS_NS: u16 = 0x8915; const REG_ROLL_MAGNETIC_COMPASS_EW: u16 = 0x8917; -//OpenSenseMap -const UPDATE_FREQUENCY: u32 = 0; // 1min - //Elements of tuple (opensensemap-id, reg-address, factor, datatype(signed or unsigned)) -const OPENSENSE_CLIMA_DATA: [(&'static str, u16, f32, char); 36] = [ +const OPENSENSE_CLIMA_DATA: [(&str, u16, f32, char); 36] = [ ("64cb602193c69500072a5813", REG_MEAN_WIND_SPEED, 10.0, 'u'), ("64cb7c21d588b90007d69a5f", REG_MEAN_WIND_DIREC, 10.0, 'u'), ("64cb7c21d588b90007d69a60", REG_AIR_TEMP, 10.0, 's'), @@ -171,7 +176,6 @@ fn conv_vec_to_value_u(vec: (u16, u16)) -> Result { #[derive(Clone, Copy, PartialEq)] pub enum TempWarningStateChange { - None, ChangeToCloseWindow, ChangeToWarningTempNoWind, ChangeToWarningTemp, @@ -180,7 +184,6 @@ pub enum TempWarningStateChange { #[derive(Clone, Copy, PartialEq)] pub enum TempWarning { - None, RemoveWarning, CloseWindow, WarningTempNoWind, @@ -188,13 +191,21 @@ pub enum TempWarning { } pub struct ClimaSensorUS { - ctx: Option, + ctx: Modbus, opensensebox_id: String, - opensense_access_token: String, - warning_active: TempWarning, - opensensemap_counter: u32, + warning_active: Option, + client: Client, + headers: HeaderMap, +} + +#[derive(Serialize)] +struct SensorValue { + sensor: &'static str, + value: f32, } +unsafe impl Send for ClimaSensorUS {} + impl ClimaSensorUS { // Temperature pub const CANCLE_TEMP: f32 = 20.0; @@ -203,57 +214,32 @@ impl ClimaSensorUS { pub const NO_WIND_SPEED: f32 = 0.3; pub const WARNING_TEMP: f32 = 35.0; - // This function is for assign a default value of the ClimaSensorUS module - pub fn new_default() -> Self { - Self { - ctx: None, - opensensebox_id: "0".to_string(), - opensense_access_token: "0".to_string(), - warning_active: TempWarning::None, - opensensemap_counter: 0, - } - } - pub fn new(config: &mut Config) -> Result { - let mut s = Self { - ctx: None, - opensensebox_id: config.get::("weatherstation/opensensemap/id"), - opensense_access_token: config.get::("weatherstation/opensensemap/token"), - warning_active: TempWarning::None, - opensensemap_counter: 0, - }; - if config.get_bool("weatherstation/enable") { - if let Err(error) = s.init() { - return Err(error); - } - } - Ok(s) - } - - fn init(&mut self) -> Result<(), libmodbus::Error> { - match Modbus::new_rtu(DEVICE, BAUDRATE, PARITY, DATA_BITS, STOP_BITS) { - Ok(conn) => { - self.ctx = Some(conn); - } - Err(error) => { - return Err(error); - } - } - - if let Some(conn) = &mut self.ctx { - if let Err(error) = conn.set_slave(SLAVE_ID) { - return Err(error); - } else if let Err(error) = conn.rtu_set_serial_mode(SerialMode::RtuRS232) { - return Err(error); - } else if let Err(error) = conn.rtu_set_rts(RequestToSendMode::RtuRtsUp) { - return Err(error); - } else if let Err(error) = conn.rtu_set_custom_rts(RequestToSendMode::RtuRtsUp) { - return Err(error); - } else if let Err(error) = conn.connect() { - return Err(error); - } - } - Ok(()) + let opensensebox_id = config.get::("weatherstation/opensensemap/id"); + let opensense_access_token = config.get::("weatherstation/opensensemap/token"); + let warning_active = Option::None; + + let client = Client::new(); + + let mut headers = HeaderMap::new(); + headers.insert("Authorization", opensense_access_token.parse().unwrap()); + headers.insert("Content-Type", "application/json".parse().unwrap()); + + let mut modbus = Modbus::new_rtu(DEVICE, BAUDRATE, PARITY, DATA_BITS, STOP_BITS)?; + + modbus.set_slave(SLAVE_ID)?; + modbus.rtu_set_serial_mode(SerialMode::RtuRS232)?; + modbus.rtu_set_rts(RequestToSendMode::RtuRtsUp)?; + modbus.rtu_set_custom_rts(RequestToSendMode::RtuRtsUp)?; + modbus.connect()?; + + Ok(Self { + ctx: modbus, + opensensebox_id, + warning_active, + client, + headers, + }) } /// This function should be called periodically to check the sensors' values. @@ -264,159 +250,131 @@ impl ClimaSensorUS { /// (Input Register - 0x04) wind-reg address 0x7533; typ U32; real_result = response_wind/10 /// The return value is bool on success, true if alarm is active and false is alarm is not active /// If no ctx is configured the this function returns always false, so no warning is triggered - pub fn handle(&mut self) -> Result { - match &self.ctx { - Some(conn) => { - let mut response_temp = vec![0u16; 2]; - let mut response_wind = vec![0u16; 2]; - - if let Err(error) = conn.read_input_registers(REG_AIR_TEMP, 2, &mut response_temp) { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - error.to_string(), - )); - } - if let Err(error) = - conn.read_input_registers(REG_MEAN_WIND_SPEED, 2, &mut response_wind) - { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - error.to_string(), - )); - } - - let temp: f32 = (conv_vec_to_value_s((response_temp[0], response_temp[1])).unwrap() - as f32) / 10.0; - let wind: f32 = (conv_vec_to_value_u((response_wind[0], response_wind[1])).unwrap() - as f32) / 10.0; - #[cfg(debug_assertions)] - println!( - "Weatherstation: temperature {} °C, windspeed {} m/s", - temp, wind - ); - //check if new data should be published to opensensemap.org - if self.opensensemap_counter == UPDATE_FREQUENCY { - self.opensensemap_counter = 0; - match self.publish_to_opensensemap() { - Ok(_) => {} - Err(error) => return Err(error), - } - } else { - self.opensensemap_counter += 1; - } - - Ok(self.set_warning_active(temp, wind)) - } - None => Ok(TempWarningStateChange::None), + async fn handle(&mut self) -> Result, libmodbus::Error> { + let mut response_temp = vec![0u16; 2]; + let mut response_wind = vec![0u16; 2]; + + self.ctx + .read_input_registers(REG_AIR_TEMP, 2, &mut response_temp)?; + self.ctx + .read_input_registers(REG_MEAN_WIND_SPEED, 2, &mut response_wind)?; + + let temp: f32 = + (conv_vec_to_value_s((response_temp[0], response_temp[1])).unwrap() as f32) / 10.0; + let wind: f32 = + (conv_vec_to_value_u((response_wind[0], response_wind[1])).unwrap() as f32) / 10.0; + + //check if new data should be published to opensensemap.org + match self.publish_to_opensensemap().await { + Ok(_) => {} + // TODO + Err(error) => return Err(libmodbus::Error::IoError(error)), } + + Ok(ClimaSensorUS::set_warning_active( + &mut self.warning_active, + temp, + wind, + )) } /// This function is used to set the warning_active variable and compare it with the new value. - fn set_warning_active(&mut self, temp: f32, wind: f32) -> TempWarningStateChange { - let new_warning: TempWarning; - let mut result: TempWarningStateChange = TempWarningStateChange::None; + fn set_warning_active( + warning_active: &mut Option, + temp: f32, + wind: f32, + ) -> Option { + let new_warning; + let mut result: Option = Option::None; if temp > ClimaSensorUS::WARNING_TEMP { - new_warning = TempWarning::WarningTemp; + new_warning = Some(TempWarning::WarningTemp); } else if temp > ClimaSensorUS::NO_WIND_TEMP && wind < ClimaSensorUS::NO_WIND_SPEED - && !matches!(self.warning_active, TempWarning::WarningTemp) + && !matches!(warning_active, Option::Some(TempWarning::WarningTemp)) { - new_warning = TempWarning::WarningTempNoWind; + new_warning = Some(TempWarning::WarningTempNoWind); } else if temp >= ClimaSensorUS::CLOSE_WINDOW_TEMP && !matches!( - self.warning_active, - TempWarning::WarningTemp | TempWarning::WarningTempNoWind + warning_active, + Option::Some(TempWarning::WarningTemp) + | Option::Some(TempWarning::WarningTempNoWind) ) { - new_warning = TempWarning::CloseWindow; + new_warning = Some(TempWarning::CloseWindow); } else if !matches!( - self.warning_active, - TempWarning::None | TempWarning::RemoveWarning + warning_active, + Option::None | Option::Some(TempWarning::RemoveWarning) ) && temp < ClimaSensorUS::CANCLE_TEMP { - new_warning = TempWarning::RemoveWarning; + new_warning = Some(TempWarning::RemoveWarning); } else { - new_warning = self.warning_active; + new_warning = *warning_active; } // compaire old and new value of TempWarning - if self.warning_active != new_warning { + if *warning_active != new_warning { result = match new_warning { - TempWarning::RemoveWarning => { - self.warning_active = new_warning; - TempWarningStateChange::ChangeToRemoveWarning + Some(TempWarning::RemoveWarning) => { + Option::Some(TempWarningStateChange::ChangeToRemoveWarning) } - TempWarning::CloseWindow => { - self.warning_active = new_warning; - TempWarningStateChange::ChangeToCloseWindow + Some(TempWarning::CloseWindow) => { + Option::Some(TempWarningStateChange::ChangeToCloseWindow) } - TempWarning::WarningTempNoWind => { - self.warning_active = new_warning; - TempWarningStateChange::ChangeToWarningTempNoWind + Some(TempWarning::WarningTempNoWind) => { + Option::Some(TempWarningStateChange::ChangeToWarningTempNoWind) } - TempWarning::WarningTemp => { - self.warning_active = new_warning; - TempWarningStateChange::ChangeToWarningTemp - } - TempWarning::None => { - self.warning_active = new_warning; - TempWarningStateChange::None + Some(TempWarning::WarningTemp) => { + Option::Some(TempWarningStateChange::ChangeToWarningTemp) } + Option::None => Option::None, }; + *warning_active = new_warning; } result } /// This method creates a json payload out of the array `OPENSENSE_CLIMA_DATA` and the data from the weather station - pub fn create_json(&mut self) -> Result { - match &self.ctx { - Some(conn) => { - let mut json_payload: String = "[".to_string(); - - for tuple_data in OPENSENSE_CLIMA_DATA.iter() { - let mut response = vec![0u16; 2]; - match conn.read_input_registers(tuple_data.1, 2, &mut response) { - Ok(_) => { - let value: f32; - if tuple_data.3 == 's' { - match conv_vec_to_value_s((response[0], response[1])) { - Ok(conv_response) => { - value = conv_response as f32 / tuple_data.2; - } - Err(_) => { - value = ERROR_CODE_S32 as f32; - } - } - } else { - match conv_vec_to_value_u((response[0], response[1])) { - Ok(conv_response) => { - value = conv_response as f32 / tuple_data.2; - } - Err(_) => { - value = ERROR_CODE_U32 as f32; - } - } - } - if value != ERROR_CODE_S32 as f32 && value != ERROR_CODE_U32 as f32 { - json_payload.push_str(&format!( - "{}\"sensor\":\"{}\",\"value\":\"{}\"{},", - '{', tuple_data.0, value, '}' - )); - } + async fn collect_sensor_values(&mut self) -> Vec { + let mut sensor_values = vec![]; + + for tuple_data in OPENSENSE_CLIMA_DATA.iter() { + let mut response = vec![0u16; 2]; + + // Leave out sensor with error + if self + .ctx + .read_input_registers(tuple_data.1, 2, &mut response) + .is_ok() + { + let value: f32; + if tuple_data.3 == 's' { + match conv_vec_to_value_s((response[0], response[1])) { + Ok(conv_response) => { + value = conv_response as f32 / tuple_data.2; + } + Err(_) => { + value = ERROR_CODE_S32 as f32; } - Err(_) => {} } + } else { + match conv_vec_to_value_u((response[0], response[1])) { + Ok(conv_response) => { + value = conv_response as f32 / tuple_data.2; + } + Err(_) => { + value = ERROR_CODE_U32 as f32; + } + } + } + if value != ERROR_CODE_S32 as f32 && value != ERROR_CODE_U32 as f32 { + sensor_values.push(SensorValue { + sensor: tuple_data.0, + value, + }); } - //remove last ',' - json_payload.pop(); - json_payload.push_str(&"]"); - Ok(json_payload) } - None => Err(Error::Rtu { - msg: ("No modbus connection configured".to_string()), - source: (std::io::Error::new(std::io::ErrorKind::NotFound, "Not configured")), - }), } + sensor_values } /// This function pulls data from the weatherstation and forms a json file out of the weather station data and the opensensemap-sensor-id. @@ -424,140 +382,155 @@ impl ClimaSensorUS { /// All information needed are stored in a const array of tuples. The tuples contain the opensensemap-sensor-id, register-address, factor and datatype. /// The return value indicates if the api request was successfully or not. /// Information about the reading of registers can be accessed through the json_payload - pub fn publish_to_opensensemap(&mut self) -> Result<(), std::io::Error> { - match self.create_json() { - Ok(json) => { - //Send JSON to https://api.opensensemap.org - let mut headers = HeaderMap::new(); - headers.insert( - "Authorization", - self.opensense_access_token.parse().unwrap(), - ); - headers.insert("Content-Type", "application/json".parse().unwrap()); - let result = Client::new() - .post(&format!( - "https://api.opensensemap.org/boxes/{}/data", - self.opensensebox_id - )) - .headers(headers) - .body(json) - .send(); - match result { - Ok(response) => match response.error_for_status() { - Ok(_response) => Ok(()), - Err(error) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - error.to_string(), - )), - }, - Err(error) => Err(std::io::Error::new( - std::io::ErrorKind::ConnectionRefused, - error.to_string(), - )), - } - } + async fn publish_to_opensensemap(&mut self) -> Result<(), io::Error> { + let json = serde_json::to_string(&self.collect_sensor_values().await).unwrap(); + + //Send JSON to https://api.opensensemap.org + let result = self + .client + .post(&format!( + "https://api.opensensemap.org/boxes/{}/data", + self.opensensebox_id + )) + .headers(self.headers.clone()) + .body(json) + .send() + .await; + match result { + Ok(response) => match response.error_for_status() { + Ok(_response) => Ok(()), + Err(error) => Err(std::io::Error::new( + std::io::ErrorKind::Other, + error.to_string(), + )), + }, Err(error) => Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, + std::io::ErrorKind::ConnectionRefused, error.to_string(), )), } } + + pub async fn get_background_task( + mut self, + mut interval: Interval, + nextcloud_sender: Sender, + ) -> Result { + loop { + match self.handle().await { + Ok(Some(temp_warning)) => { + let message = match temp_warning { + TempWarningStateChange::ChangeToCloseWindow => gettext!( + "🌡️ Temperature above {} °C, close the window", + ClimaSensorUS::CLOSE_WINDOW_TEMP + ), + TempWarningStateChange::ChangeToWarningTempNoWind => gettext!( + "🌡️ Temperature above {} °C and no Wind", + ClimaSensorUS::NO_WIND_TEMP + ), + TempWarningStateChange::ChangeToWarningTemp => { + gettext!("🌡️ Temperature above {} °C", ClimaSensorUS::WARNING_TEMP) + } + TempWarningStateChange::ChangeToRemoveWarning => gettext!( + "🌡 Temperature again under {} °C, warning was removed", + ClimaSensorUS::CANCLE_TEMP + ), + }; + nextcloud_sender + .send(NextcloudEvent::Chat(NextcloudChat::Default, message)) + .await?; + } + Ok(None) => (), + Err(error) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Error from weather station: {}", error), + )) + .await?; + } + }; + interval.tick().await; + } + } } #[cfg(test)] mod tests { use super::*; - #[test] + #[tokio::test] #[ignore] - fn test_handle() { + async fn test_handle() { let mut config: Config = Config::new("/sw/libelektra/opensesame/#0/current"); let mut weatherstation = ClimaSensorUS::new(&mut config).expect("Failed to init libmodbus connection"); - match weatherstation.handle().unwrap() { - TempWarningStateChange::ChangeToCloseWindow => println!("ChangeToCloseWindow"), - TempWarningStateChange::ChangeToWarningTempNoWind => { + match weatherstation.handle().await { + Ok(Some(TempWarningStateChange::ChangeToCloseWindow)) => { + println!("ChangeToCloseWindow") + } + Ok(Some(TempWarningStateChange::ChangeToWarningTempNoWind)) => { println!("ChangeToWarningTempNoWind") } - TempWarningStateChange::ChangeToWarningTemp => println!("ChangeToWarningTemp"), - TempWarningStateChange::ChangeToRemoveWarning => println!("ChangeToRemoveWarning"), - TempWarningStateChange::None => println!("None"), + Ok(Some(TempWarningStateChange::ChangeToWarningTemp)) => { + println!("ChangeToWarningTemp") + } + Ok(Some(TempWarningStateChange::ChangeToRemoveWarning)) => { + println!("ChangeToRemoveWarning") + } + Ok(Option::None) => println!("None"), + Err(_error) => println!("Error"), } } #[test] fn test_set_warning_active() { - let mut clima_sens = ClimaSensorUS { - ctx: None, - opensensebox_id: "null".to_string(), - opensense_access_token: "null".to_string(), - warning_active: TempWarning::None, - opensensemap_counter: 0, - }; - - assert!(clima_sens.set_warning_active(15.0, 0.1) == TempWarningStateChange::None); - assert!(matches!(clima_sens.warning_active, TempWarning::None)); - assert!(clima_sens.set_warning_active(15.0, 3.5) == TempWarningStateChange::None); - assert!(matches!(clima_sens.warning_active, TempWarning::None)); + let mut warning_active = Option::None; + + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 15.0, 0.1).is_none()); + assert!(matches!(warning_active, Option::None)); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 15.0, 3.5).is_none()); + assert!(matches!(warning_active, Option::None)); assert!( - clima_sens.set_warning_active(25.0, 0.1) == TempWarningStateChange::ChangeToCloseWindow + ClimaSensorUS::set_warning_active(&mut warning_active, 25.0, 0.1) + == Some(TempWarningStateChange::ChangeToCloseWindow) ); - assert!(matches!( - clima_sens.warning_active, - TempWarning::CloseWindow - )); - assert!(clima_sens.set_warning_active(25.0, 3.5) == TempWarningStateChange::None); - assert!(matches!( - clima_sens.warning_active, - TempWarning::CloseWindow - )); + assert!(matches!(warning_active, Some(TempWarning::CloseWindow))); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 25.0, 3.5).is_none()); + assert!(matches!(warning_active, Some(TempWarning::CloseWindow))); assert!( - clima_sens.set_warning_active(33.0, 0.1) - == TempWarningStateChange::ChangeToWarningTempNoWind + ClimaSensorUS::set_warning_active(&mut warning_active, 33.0, 0.1) + == Some(TempWarningStateChange::ChangeToWarningTempNoWind) ); assert!(matches!( - clima_sens.warning_active, - TempWarning::WarningTempNoWind + warning_active, + Some(TempWarning::WarningTempNoWind) )); - assert!(clima_sens.set_warning_active(33.0, 3.5) == TempWarningStateChange::None); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 33.0, 3.5).is_none()); assert!(matches!( - clima_sens.warning_active, - TempWarning::WarningTempNoWind + warning_active, + Some(TempWarning::WarningTempNoWind) )); assert!( - clima_sens.set_warning_active(36.0, 0.1) == TempWarningStateChange::ChangeToWarningTemp + ClimaSensorUS::set_warning_active(&mut warning_active, 36.0, 0.1) + == Some(TempWarningStateChange::ChangeToWarningTemp) ); - assert!(matches!( - clima_sens.warning_active, - TempWarning::WarningTemp - )); - assert!(clima_sens.set_warning_active(36.0, 3.5) == TempWarningStateChange::None); - assert!(matches!( - clima_sens.warning_active, - TempWarning::WarningTemp - )); - assert!(clima_sens.set_warning_active(25.3, 3.4) == TempWarningStateChange::None); - assert!(matches!( - clima_sens.warning_active, - TempWarning::WarningTemp - )); + assert!(matches!(warning_active, Some(TempWarning::WarningTemp))); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 36.0, 3.5).is_none()); + assert!(matches!(warning_active, Some(TempWarning::WarningTemp))); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 25.3, 3.4).is_none()); + assert!(matches!(warning_active, Some(TempWarning::WarningTemp))); assert!( - clima_sens.set_warning_active(15.0, 0.1) - == TempWarningStateChange::ChangeToRemoveWarning + ClimaSensorUS::set_warning_active(&mut warning_active, 15.0, 0.1) + == Some(TempWarningStateChange::ChangeToRemoveWarning) ); - assert!(matches!( - clima_sens.warning_active, - TempWarning::RemoveWarning - )); - assert!(clima_sens.set_warning_active(15.0, 3.5) == TempWarningStateChange::None); - assert!(matches!( - clima_sens.warning_active, - TempWarning::RemoveWarning - )); + assert!(matches!(warning_active, Some(TempWarning::RemoveWarning))); + assert!(ClimaSensorUS::set_warning_active(&mut warning_active, 15.0, 3.5).is_none()); + assert!(matches!(warning_active, Some(TempWarning::RemoveWarning))); } #[test] diff --git a/src/config.rs b/src/config.rs index 23432b6..9f0187d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,8 @@ pub struct Config<'a> { ks: KeySet, } +unsafe impl<'a> Send for Config<'a> {} + impl Config<'_> { pub fn new(parent: &str) -> Self { let mut s = Self { @@ -58,9 +60,9 @@ impl Config<'_> { pub fn get_hash_map_vec_u8(&mut self, name: &str) -> HashMap, String> { let mut lookup_key = self.parent_key.duplicate(CopyOption::KEY_CP_NAME); - lookup_key - .add_name(name) - .expect(format!("Could not add '{}' to '{}'!", name, self.parent_key.name()).as_str()); + lookup_key.add_name(name).unwrap_or_else(|_| { + panic!("Could not add '{}' to '{}'!", name, self.parent_key.name()) + }); let mut ret = HashMap::new(); for (_i, key) in self.ks.iter_mut().enumerate() { if key.is_directly_below(&lookup_key) { @@ -74,25 +76,25 @@ impl Config<'_> { ); } } - return ret; + ret } pub fn get_bool(&mut self, name: &str) -> bool { let mut lookup_key = self.parent_key.duplicate(CopyOption::KEY_CP_NAME); - lookup_key - .add_name(name) - .expect(format!("Could not add '{}' to '{}'!", name, self.parent_key.name()).as_str()); + lookup_key.add_name(name).unwrap_or_else(|_| { + panic!("Could not add '{}' to '{}'!", name, self.parent_key.name()) + }); if let Some(found_key) = self.ks.lookup(lookup_key, LookupOption::KDB_O_NONE) { - return found_key.value().to_string() == "1"; + return found_key.value() == "1"; } - return false; + false } pub fn get_option(&mut self, name: &str) -> Option { let mut lookup_key = self.parent_key.duplicate(CopyOption::KEY_CP_NAME); - lookup_key - .add_name(name) - .expect(format!("Could not add '{}' to '{}'!", name, self.parent_key.name()).as_str()); + lookup_key.add_name(name).unwrap_or_else(|_| { + panic!("Could not add '{}' to '{}'!", name, self.parent_key.name()) + }); if let Some(found_key) = self.ks.lookup(lookup_key, LookupOption::KDB_O_NONE) { if let Ok(ret) = found_key.value().parse::() { return Some(ret); @@ -103,12 +105,12 @@ impl Config<'_> { pub fn get(&mut self, name: &str) -> T { let mut lookup_key = self.parent_key.duplicate(CopyOption::KEY_CP_NAME); - lookup_key - .add_name(name) - .expect(format!("Could not add '{}' to '{}'!", name, self.parent_key.name()).as_str()); + lookup_key.add_name(name).unwrap_or_else(|_| { + panic!("Could not add '{}' to '{}'!", name, self.parent_key.name()) + }); if let Some(found_key) = self.ks.lookup(lookup_key, LookupOption::KDB_O_NONE) { if let Ok(ret) = found_key.value().parse::() { - return ret; + ret } else { panic!( "Could not convert '{}' to type '{}' from key '{}'!", diff --git a/src/environment.rs b/src/environment.rs index eb6c7ff..c27fba1 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,15 +1,28 @@ -use crate::config::Config; - -use std::fmt; - +use bme280::i2c::BME280; +use futures::never::Never; +use gettextrs::gettext; use i2cdev::core::*; use i2cdev::linux::LinuxI2CDevice; - use linux_embedded_hal::{Delay, I2cdev}; - -use bme280::i2c::BME280; - -pub struct Environment { +use std::{fmt, sync::Arc}; +use systemstat::Duration; +use tokio::{ + sync::{ + mpsc::{Receiver, Sender}, + Mutex, + }, + time::{sleep, Interval}, +}; + +use crate::{ + audio::AudioEvent, + buttons::CommandToButtons, + config::Config, + nextcloud::{NextcloudChat, NextcloudEvent, NextcloudStatus}, + types::ModuleError, +}; + +pub struct Environment<'a> { pub co2: u16, pub voc: u16, pub temperature: f32, @@ -23,13 +36,13 @@ pub struct Environment { pub app_version: u16, data: Vec, - read_counter: u16, - board5a: Option, + pub board5a: Option, bme280: Option>, first_time: bool, - data_interval: u16, + pub data_interval: u16, pub baseline: u16, pub name: String, + state_mutex: Arc>>, } const LOW_CO2_OK_QUALITY: u16 = 3000; @@ -78,7 +91,7 @@ const ALG_RESULT_LENGTH: u8 = 5; // const BOARD77: u16 = 0x77; -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Copy, Clone)] pub enum AirQualityChange { Error, @@ -91,6 +104,11 @@ pub enum AirQualityChange { FireAlarm, } +pub enum EnvEvent { + RememberBaseline, + RestoreBaseline, +} + fn set_env_data_ccs811(board5a: &mut LinuxI2CDevice, temperature: f32, humidity: f32) { let (temp_conv, hum_conv) = Environment::convert_env_data(temperature, humidity); @@ -107,8 +125,8 @@ fn set_env_data_ccs811(board5a: &mut LinuxI2CDevice, temperature: f32, humidity: .unwrap(); } -impl Environment { - pub fn new(config: &mut Config) -> Self { +impl<'a> Environment<'a> { + pub fn new(config: &mut Config, state_mutex: Arc>>) -> Self { let dev_name = config.get::("environment/device"); if dev_name == "/dev/null" { Self { @@ -121,7 +139,6 @@ impl Environment { error: 0, air_quality: AirQualityChange::Ok, data: Vec::new(), - read_counter: 0, app_version: 0, boot_version: 0, board5a: None, @@ -130,6 +147,7 @@ impl Environment { data_interval: 0, baseline: 0, name: config.get::("environment/name"), + state_mutex, } } else { let i2c_bus = I2cdev::new(dev_name).unwrap(); @@ -143,7 +161,6 @@ impl Environment { error: 0, air_quality: AirQualityChange::Ok, data: Vec::new(), - read_counter: 0, app_version: 0, boot_version: 0, board5a: Some( @@ -155,6 +172,7 @@ impl Environment { data_interval: config.get::("environment/data/interval"), baseline: 0, name: config.get::("environment/name"), + state_mutex, }; //if sending SW_RESET failes it disables ccs811 match s @@ -173,6 +191,29 @@ impl Environment { } } + /// This function need to be called a few seconds after the creation of the object + /// It inits the connection to the ccS811 sensor + #[allow(non_snake_case)] + pub fn init_ccs811(&mut self) -> bool { + match self.board5a.as_mut() { + Some(board5a) => { + board5a.smbus_write_byte(APP_START).unwrap(); + board5a + .smbus_write_byte_data(MEAS_MODE, MEAS_MODE_DATA) + .unwrap(); + + self.boot_version = board5a.smbus_read_word_data(FW_BOOT_VERSION).unwrap(); + self.app_version = board5a.smbus_read_word_data(FW_APP_VERSION).unwrap(); + + self.status = board5a.smbus_read_byte_data(STATUS).unwrap(); + self.error = board5a.smbus_read_byte_data(ERROR_ID).unwrap(); + self.first_time = false; + true + } + None => false, + } + } + fn calculate_air_quality(&mut self) -> bool { self.co2 = self.data[0].into(); self.co2 <<= 8; @@ -211,11 +252,12 @@ impl Environment { } else { is_changed = false; } - return is_changed; + is_changed } /// go back to remembered state - pub fn restore_baseline(&mut self, state: &mut Config) { + async fn restore_baseline(&mut self) { + let mut state = self.state_mutex.lock().await; match self.board5a.as_mut() { None => (), Some(board5a) => { @@ -227,12 +269,13 @@ impl Environment { } /// remember for later - pub fn remember_baseline(&mut self, state: &mut Config) { + async fn remember_baseline(&mut self) { + let mut state = self.state_mutex.lock().await; state.set("environment/baseline", &self.baseline.to_string()); } fn print_values(&self) -> String { - return format!("Temperature: {} °C, CO₂: {} ppm, VOC: {} ppb, Humidity: {} %, Pressure {} pascals, Baseline: {}", self.temperature, self.co2, self.voc, self.humidity, self.pressure, self.baseline); + format!("Temperature: {} °C, CO₂: {} ppm, VOC: {} ppb, Humidity: {} %, Pressure {} pascals, Baseline: {}", self.temperature, self.co2, self.voc, self.humidity, self.pressure, self.baseline) } fn convert_env_data(temperature: f32, humidity: f32) -> (u16, u16) { @@ -248,40 +291,31 @@ impl Environment { not set by the application) to compensate for changes in relative humidity and ambient temperature.*/ - return ( + ( ((temperature + 25.0f32) * 512.0f32 + 0.5f32) as u16, (humidity * 512.0f32 + 0.5f32) as u16, - ); + ) } /// to be periodically called every 10 ms + /// Return value indicates if data has been changed pub fn handle(&mut self) -> bool { match self.board5a.as_mut() { None => match self.bme280.as_mut() { None => false, Some(bme280) => { - self.read_counter += 1; - if self.read_counter == self.data_interval { - self.read_counter = 0; - - let measurement = bme280.measure().unwrap(); + let measurement = bme280.measure().unwrap(); - self.temperature = measurement.temperature; - self.humidity = measurement.humidity; - self.pressure = measurement.pressure; + self.temperature = measurement.temperature; + self.humidity = measurement.humidity; + self.pressure = measurement.pressure; - return true; - } - return false; + true } }, Some(board5a) => { - self.read_counter += 1; - // check if we get new data - if !self.first_time && self.read_counter == self.data_interval { - self.read_counter = 0; - + if !self.first_time { let measurement = self.bme280.as_mut().unwrap().measure().unwrap(); set_env_data_ccs811(board5a, measurement.temperature, measurement.humidity); self.temperature = measurement.temperature; @@ -312,30 +346,120 @@ impl Environment { self.data = data; return self.calculate_air_quality(); } + false + } + } + } - if self.read_counter == RESET_INTERVAL && self.first_time { - board5a.smbus_write_byte(APP_START).unwrap(); - board5a - .smbus_write_byte_data(MEAS_MODE, MEAS_MODE_DATA) - .unwrap(); - - self.boot_version = board5a.smbus_read_word_data(FW_BOOT_VERSION).unwrap(); - self.app_version = board5a.smbus_read_word_data(FW_APP_VERSION).unwrap(); + pub async fn get_background_task( + mut self, + mut interval: Interval, + nextcloud_sender: Sender, + command_sender: Sender, + audio_sender: Sender, + mut environment_receiver: Receiver, + garage_enabled: bool, + ) -> Result { + let mut old_airquality = AirQualityChange::Error; + if self.board5a.is_some() { + sleep(Duration::from_millis(RESET_INTERVAL.into())).await; + self.init_ccs811(); + } - self.status = board5a.smbus_read_byte_data(STATUS).unwrap(); - self.error = board5a.smbus_read_byte_data(ERROR_ID).unwrap(); - self.first_time = false; - self.read_counter = 0; - return false; + loop { + interval.tick().await; + if let Ok(env) = environment_receiver.try_recv() { + match env { + EnvEvent::RememberBaseline => { + self.remember_baseline().await; + } + EnvEvent::RestoreBaseline => { + self.restore_baseline().await; + } } + } + + if self.handle() && self.air_quality != old_airquality { + old_airquality = self.air_quality; + nextcloud_sender + .send(NextcloudEvent::Status( + NextcloudStatus::Env, + format!("💨 {:?}", self.air_quality), + )) + .await?; + + match self.air_quality { + AirQualityChange::Error => { + let error = gettext!( + "⚠️ Error {:#02b} reading environment! Status: {:#02b}. {}", + self.error, + self.status, + self + ); + nextcloud_sender + .send(NextcloudEvent::Chat(NextcloudChat::Default, error.clone())) + .await?; + return Err(ModuleError::new(error)); + } + AirQualityChange::Ok => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("💨 Airquality is ok. {}", self), + )) + .await?; + } + AirQualityChange::Moderate => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("💩 Airquality is moderate. {}", self), + )) + .await?; + } + AirQualityChange::Bad => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("💩 Airquality is bad! {}", self), + )) + .await?; + } - return false; + AirQualityChange::FireAlarm => { + let mut state = self.state_mutex.lock().await; + state.set("alarm/fire", &self.name); + } + AirQualityChange::FireBell => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("🚨 Possible fire alarm! Ring bell once! ⏰. {}", self), + )) + .await?; + + command_sender + .send(CommandToButtons::RingBell(20, 0)) + .await?; + if garage_enabled { + audio_sender.send(AudioEvent::FireAlarm).await?; + } + } + AirQualityChange::FireChat => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("🚨 Possible fire alarm! (don't ring yet). {}", self), + )) + .await?; + } + }; } } } } -impl fmt::Display for Environment { +impl fmt::Display for Environment<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}. {}", self.name, self.print_values()) } diff --git a/src/garage.rs b/src/garage.rs index 7405e77..65503e8 100644 --- a/src/garage.rs +++ b/src/garage.rs @@ -1,6 +1,14 @@ +use futures::never::Never; use gpio_cdev::{Chip, LineHandle, LineRequestFlags}; +use systemstat::Duration; +use tokio::{sync::mpsc::Sender, time::interval}; -use crate::config::Config; +use crate::{ + buttons::CommandToButtons, + config::Config, + nextcloud::{NextcloudChat, NextcloudEvent, NextcloudStatus}, + types::ModuleError, +}; const TASTER_EINGANG_OBEN_LINE: u32 = 234; // - Taster Eingang Oben -> Pin40 GPIO234 EINT10 const TASTER_EINGANG_UNTEN_LINE: u32 = 235; // - Taster Eingang Unten -> Pin38 GPIO235 EINT11 @@ -92,7 +100,7 @@ impl Garage { if now == 1 && *prev { *prev = false; } - return ret; + ret } pub fn handle(&mut self) -> GarageChange { @@ -138,7 +146,75 @@ impl Garage { None => (), } - return GarageChange::None; + GarageChange::None + } + + /// This function could be triggered by state changes on GPIO, because the pins are connected with the olimex board + /// So we dont need to run it all few seconds. + pub async fn get_background_task( + mut garage: Garage, + command_sender: Sender, + nextcloud_sender: Sender, + ) -> Result { + let mut interval = interval(Duration::from_millis(10)); + loop { + match garage.handle() { + GarageChange::None => (), + GarageChange::PressedTasterEingangOben => { + command_sender + .send(CommandToButtons::SwitchLights( + true, + false, + "💡 Pressed at entrance top switch. Switch lights in garage" + .to_string(), + )) + .await?; + } + GarageChange::PressedTasterTorOben => { + command_sender + .send(CommandToButtons::SwitchLights( + true, + true, + "💡 Pressed top switch at garage door. Switch lights in and out garage" + .to_string(), + )) + .await?; + } + GarageChange::PressedTasterEingangUnten | GarageChange::PressedTasterTorUnten => { + command_sender.send(CommandToButtons::OpenDoor).await?; + } + + GarageChange::ReachedTorEndposition => { + nextcloud_sender + .send(NextcloudEvent::Status( + NextcloudStatus::Door, + String::from("🔒 Open"), + )) + .await?; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + String::from("🔒 Garage door closed."), + )) + .await?; + } + GarageChange::LeftTorEndposition => { + nextcloud_sender + .send(NextcloudEvent::Status( + NextcloudStatus::Door, + String::from("🔓 Closed"), + )) + .await?; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + String::from("🔓 Garage door open"), + )) + .await?; + } + } + interval.tick().await; + } } } @@ -147,7 +223,7 @@ mod tests { use super::*; use std::{env, thread, time}; - const CONFIG_PARENT: &'static str = "/sw/libelektra/opensesame/#0/current"; + const CONFIG_PARENT: &str = "/sw/libelektra/opensesame/#0/current"; #[ignore] // remove and run with: cargo test print_events -- --nocapture #[test] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4c050bd --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +pub mod audio; +pub mod bat; +pub mod buttons; +pub mod clima_sensor_us; +pub mod config; +pub mod environment; +pub mod garage; +pub mod mod_ir_temp; +pub mod nextcloud; +pub mod ping; +pub mod pwr; +pub mod sensors; +pub mod signals; +pub mod ssh; +pub mod types; +pub mod validator; +pub mod watchdog; diff --git a/src/main.rs b/src/main.rs index eed658d..a337cad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,670 +1,244 @@ -// opensesame - -mod bat; -mod buttons; -mod clima_sensor_us; -mod config; -mod environment; -mod garage; -mod mod_ir_temp; -mod nextcloud; -mod pwr; -mod sensors; -mod ssh; -mod validator; -mod watchdog; - +use chrono::Local; +use futures::future::join_all; +use gettextrs::*; use mlx9061x::Error as MlxError; -use std::fs::File; -use std::io::{prelude::*, BufReader, Error}; -use std::ops::Deref; use std::panic; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::{env, thread, time}; - -use gettextrs::*; - -use chrono::prelude::*; -use sunrise::sunrise_sunset; -use systemstat::{Platform, System}; - -use bat::Bat; -use buttons::Buttons; -use buttons::StateChange; -use clima_sensor_us::{ClimaSensorUS, TempWarningStateChange}; -use config::Config; -use environment::AirQualityChange; -use environment::Environment; -use garage::Garage; -use garage::GarageChange; -use mod_ir_temp::{IrTempStateChange, ModIR}; -use nextcloud::Nextcloud; -use pwr::Pwr; -use sensors::Sensors; -use sensors::SensorsChange; -use ssh::exec_ssh_command; -use validator::Validation; -use validator::Validator; -use watchdog::Watchdog; - -const CONFIG_PARENT: &'static str = "/sw/libelektra/opensesame/#0/current"; -const STATE_PARENT: &'static str = "/state/libelektra/opensesame/#0/current"; - -// play audio file with argument. If you do not have an argument, simply pass --quiet again -fn play_audio_file(file: String, arg: String) { - if file != "/dev/null" { - thread::Builder::new() - .name("ogg123".to_string()) - .spawn(move || { - std::process::Command::new("ogg123") - .arg("--quiet") - .arg(arg) - .arg(file) - .status() - .expect(&gettext("failed to execute process")); - }) - .unwrap(); - } -} +use systemstat::Duration; +use tokio::spawn; +use tokio::sync::{mpsc, Mutex}; +use tokio::time::interval; + +use opensesame::audio::{Audio, AudioEvent}; +use opensesame::bat::Bat; +use opensesame::buttons::{Buttons, CommandToButtons}; +use opensesame::clima_sensor_us::ClimaSensorUS; +use opensesame::config::Config; +use opensesame::environment::{EnvEvent, Environment}; +use opensesame::garage::Garage; +use opensesame::mod_ir_temp::ModIR; +use opensesame::nextcloud::{Nextcloud, NextcloudChat, NextcloudEvent}; +use opensesame::ping::{Ping, PingEvent}; +use opensesame::pwr::Pwr; +use opensesame::sensors::Sensors; +use opensesame::signals::Signals; +use opensesame::types::ModuleError; +use opensesame::validator::Validator; +use opensesame::watchdog::Watchdog; + +const CONFIG_PARENT: &str = "/sw/libelektra/opensesame/#0/current"; +const STATE_PARENT: &str = "/state/libelektra/opensesame/#0/current"; + +#[tokio::main] +async fn main() -> Result<(), ModuleError> { + TextDomain::new("opensesame").init().unwrap(); -fn do_reset(watchdog: &mut Watchdog, nc: &mut Nextcloud, pwr: &mut Pwr) { - if pwr.enabled() { - watchdog.trigger(); - pwr.switch(false); - nc.ping(gettext("👋 Turned PWR_SWITCH off")); - watchdog.trigger(); - thread::sleep(time::Duration::from_millis(watchdog::SAFE_TIMEOUT)); + let mut config = Config::new(CONFIG_PARENT); + let config_mutex = Arc::new(Mutex::new(Config::new(CONFIG_PARENT))); + let state_mutex = Arc::new(Mutex::new(Config::new(STATE_PARENT))); - watchdog.trigger(); - pwr.switch(true); - nc.ping(gettext("👋 Turned PWR_SWITCH on")); - watchdog.trigger(); - thread::sleep(time::Duration::from_millis(watchdog::SAFE_TIMEOUT)); + let date_time_format = config.get::("nextcloud/format/datetime"); + let startup_time = Local::now().format(&date_time_format); - watchdog.trigger(); + // Sender and receiver to open doors/lights etc via Nextcloud + let (command_sender, command_receiver) = mpsc::channel::(32); + // Info to send to next cloud + let (nextcloud_sender, nextcloud_receiver) = mpsc::channel::(32); + // Sender and receiver to set status of System and Send it to Nextcloud + let (ping_sender, ping_receiver) = mpsc::channel::(32); + // Sender and receiver to play audio + let (audio_sender, audio_receiver) = mpsc::channel::(32); + + let (environment_sender, environment_receiver) = mpsc::channel::(32); + + let buttons_enabled = config.get_bool("buttons/enable"); + let garage_enabled = config.get_bool("garage/enable"); + let sensors_enabled = config.get_bool("sensors/enable"); + let modir_enabled = config.get_bool("ir/enable"); + let env_enabled = config.get_bool("environment/enable"); + let weatherstation_enabled = config.get_bool("weatherstation/enable"); + let bat_enabled = config.get_bool("bat/enable"); + let watchdog_enabled = config.get_bool("watchdog/enable"); + let ping_enabled = config.get_bool("ping/enable"); + + let mut tasks = vec![]; + + tasks.push(spawn(Nextcloud::get_background_task( + Nextcloud::new(&mut config), + nextcloud_receiver, + nextcloud_sender.clone(), + command_sender.clone(), + audio_sender.clone(), + ))); + + if garage_enabled { + if !buttons_enabled { + panic!("Garage depends on buttons!"); + } + tasks.push(spawn(Garage::get_background_task( + Garage::new(&mut config), + command_sender.clone(), + nextcloud_sender.clone(), + ))); } -} - -fn handle_environment( - environment: &mut Environment, - nc: &mut Nextcloud, - buttons: Option<&mut Buttons>, - config: &mut Config, -) -> bool { - nc.set_info_environment(format!("💨 {:?}", environment.air_quality)); - match environment.air_quality { - AirQualityChange::Error => nc.send_message(gettext!( - "⚠️ Error {:#02b} reading environment! Status: {:#02b}. {}", - environment.error, - environment.status, - environment.to_string() - )), - - AirQualityChange::Ok => { - nc.send_message(gettext!("💨 Airquality is ok. {}", environment.to_string())) - } - AirQualityChange::Moderate => nc.send_message(gettext!( - "💩 Airquality is moderate. {}", - environment.to_string() - )), - AirQualityChange::Bad => nc.send_message(gettext!( - "💩 Airquality is bad! {}", - environment.to_string() - )), - AirQualityChange::FireAlarm => { - return true; - } - AirQualityChange::FireBell => { - nc.send_message(gettext!( - "🚨 Possible fire alarm! Ring bell once! ⏰. {}", - environment.to_string() - )); - if let Some(buttons) = buttons { - buttons.ring_bell(20, 0); - } - if config.get_bool("garage/enable") { - play_audio_file(config.get::("audio/alarm"), "--quiet".to_string()); - thread::Builder::new() - .name("killall to ring bell".to_string()) - .spawn(move || { - exec_ssh_command("killall -SIGUSR2 opensesame".to_string()); - }) - .unwrap(); - } - } - AirQualityChange::FireChat => nc.send_message(gettext!( - "🚨 Possible fire alarm! (don't ring yet). {}", - environment.to_string() - )), + if buttons_enabled { + let time_format = config.get::("nextcloud/format/time"); + let location_latitude = config.get::("location/latitude"); + let location_longitude = config.get::("location/longitude"); + tasks.push(spawn(Buttons::get_background_task( + Buttons::new(&mut config), + Validator::new(&mut config), + Pwr::new(&mut config), + time_format.to_string(), + command_receiver, + nextcloud_sender.clone(), + audio_sender.clone(), + location_latitude, + location_longitude, + ))); } - return false; -} -fn main() -> Result<(), Error> { - let mut config: Config = Config::new(CONFIG_PARENT); - env::set_var("RUST_BACKTRACE", config.get::("debug/backtrace")); - let mut watchdog = Watchdog::new(&mut config); - - let term = Arc::new(AtomicBool::new(false)); - for signal in signal_hook::consts::TERM_SIGNALS { - signal_hook::flag::register(*signal, Arc::clone(&term))?; + if sensors_enabled { + let device_path = config.get::("sensors/device"); + tasks.push(spawn(Sensors::get_background_task( + Sensors::new(&mut config), + device_path.to_string(), + nextcloud_sender.clone(), + /*state_mutex.clone(), + id(),*/ + ))); } - let sigalrm = Arc::new(AtomicBool::new(false)); - signal_hook::flag::register(signal_hook::consts::SIGALRM, Arc::clone(&sigalrm))?; - - let sigusr1 = Arc::new(AtomicBool::new(false)); - signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&sigusr1))?; - - let sigusr2 = Arc::new(AtomicBool::new(false)); - signal_hook::flag::register(signal_hook::consts::SIGUSR2, Arc::clone(&sigusr2))?; - - let sighup = Arc::new(AtomicBool::new(false)); - signal_hook::flag::register(signal_hook::consts::SIGHUP, Arc::clone(&sighup))?; - - // https://stackoverflow.com/questions/42456497/stdresultresult-panic-to-log - // Alternative: https://github.com/sfackler/rust-log-panics - panic::set_hook(Box::new(|panic_info| { - let (filename, line) = panic_info - .location() - .map(|loc| (loc.file(), loc.line())) - .unwrap_or(("", 0)); - let cause = panic_info - .payload() - .downcast_ref::() - .map(String::deref); - let cause = cause.unwrap_or_else(|| { - panic_info - .payload() - .downcast_ref::<&str>() - .map(|s| *s) - .unwrap_or("") - }); - let mut config: Config = Config::new(CONFIG_PARENT); - let nc: Nextcloud = Nextcloud::new(&mut config); - let text = gettext!("A panic occurred at {}:{}: {}", filename, line, cause); - nc.ping(text.clone()); - eprintln!("{}", text); - })); - - let mut nc: Nextcloud = Nextcloud::new(&mut config); - - let time_format = config.get::("nextcloud/format/time"); - let date_time_format = config.get::("nextcloud/format/datetime"); - let startup_time = Local::now().format(&date_time_format); - - TextDomain::new("opensesame").init().unwrap(); - - nc.ping(gettext!( - "👋 opensesame {} init {}", - env!("CARGO_PKG_VERSION"), - startup_time - )); - - let mut state: Config = Config::new(STATE_PARENT); - - let mut started_message_timeout = 10000; - let enable_ping = config.get_bool("debug/ping/enable"); - let wait_for_ping_timeout = 300000 * config.get::("debug/ping/timeout"); - let mut wait_for_ping = 0; - let mut ping_counter = 0u64; - let mut remember_baseline_counter = 0; - let wait_for_remember_baseline = 300000 * 24 * 7; // 7 days - - if config.get_option::("sensors/#0/loc").is_some() { - let mut environment = Environment::new(&mut config); - let mut weather_station; - - match ClimaSensorUS::new(&mut config) { - Ok(weath_st) => { - weather_station = weath_st; - } - Err(error) => { - weather_station = ClimaSensorUS::new_default(); - nc.ping(gettext!("⚠️ Failed to init libmodbus connection: {}", error)); - } - } - - let mut ir_temp = match ModIR::new(&mut config) { - Ok(sensor) => sensor, + if modir_enabled { + let mod_ir_result = ModIR::new(&mut config); + match mod_ir_result { + Ok(mod_ir) => { + let interval = interval(Duration::from_secs(config.get::("ir/data/interval"))); + tasks.push(spawn(ModIR::get_background_task( + mod_ir, + interval, + nextcloud_sender.clone(), + ))); + } + // TODO: Streamline consistent error handling! Err(error_typ) => { - match error_typ { - MlxError::I2C(error) => { - nc.ping(gettext!("⚠️ Failed to init ModIR: {}", error)); - } - MlxError::ChecksumMismatch => { - nc.ping(gettext!("⚠️ Failed to init ModIR: {}", "ChecksumMismatch")); - } - MlxError::InvalidInputData => { - nc.ping(gettext!("⚠️ Failed to init ModIR: {}", "InvalidInputData")); + let reason = match error_typ { + MlxError::I2C(error) => error.to_string(), + MlxError::ChecksumMismatch | MlxError::InvalidInputData => { + format!("{:?}", error_typ) } }; - ModIR::new_default() - } - }; - - let path = std::path::Path::new("/home/olimex/data.log"); - let mut outfile; - if path.exists() { - outfile = std::fs::OpenOptions::new() - .write(true) - .append(true) - .open(path) - .unwrap(); - } else { - outfile = File::create(path).unwrap(); - } - - let file = File::open("/dev/ttyACM0").unwrap(); - let reader = BufReader::new(file); - - let mut sensors = Sensors::new(&mut config); - - for l in reader.lines() { - if environment.handle() { - handle_environment(&mut environment, &mut nc, None, &mut config); - } - - match weather_station.handle() { - Ok(TempWarningStateChange::ChangeToCloseWindow) => { - nc.send_message(gettext!( - "🌡️ Temperature above {} °C, close the window", - ClimaSensorUS::CLOSE_WINDOW_TEMP - )); - } - Ok(TempWarningStateChange::ChangeToWarningTempNoWind) => { - nc.send_message(gettext!( - "🌡️ Temperature above {} °C and no Wind", - ClimaSensorUS::NO_WIND_TEMP - )); - } - Ok(TempWarningStateChange::ChangeToWarningTemp) => { - nc.send_message(gettext!( - "🌡️ Temperature above {} °C", - ClimaSensorUS::WARNING_TEMP - )); - } - Ok(TempWarningStateChange::ChangeToRemoveWarning) => { - nc.send_message(gettext!( - "🌡 Temperature again under {} °C, warning was removed", - ClimaSensorUS::CANCLE_TEMP - )); - } - Ok(TempWarningStateChange::None) => (), - Err(error) => { - nc.ping(gettext!( - "⚠️ Error from weather station: {}", - error.to_string() - )); - } - } - match ir_temp.handle() { - Ok(state) => match state { - IrTempStateChange::None => (), - IrTempStateChange::ChanedToBothToHot => { - nc.send_message(gettext!( - "🌡️🌡️ ModIR both sensors too hot! Ambient: {} °C, Object: {} °C", - ir_temp.ambient_temp, - ir_temp.object_temp - )); - } - IrTempStateChange::ChangedToAmbientToHot => { - nc.send_message(gettext!( - "🌡️ ModIR ambient sensors too hot! Ambient: {} °C", - ir_temp.ambient_temp - )); - } - IrTempStateChange::ChangedToObjectToHot => { - nc.send_message(gettext!( - "🌡️ ModIR object sensors too hot! Object: {} °C", - ir_temp.object_temp - )); - } - IrTempStateChange::ChangedToCancelled => { - nc.send_message(gettext!( - "🌡 ModIR cancelled warning! Ambient: {} °C, Object: {} °C", - ir_temp.ambient_temp, - ir_temp.object_temp - )); - } - }, - Err(error_typ) => match error_typ { - MlxError::I2C(error) => { - nc.ping(gettext!("⚠️ Error while handling ModIR: {}", error)); - } - MlxError::ChecksumMismatch => { - nc.ping(gettext!( - "⚠️ Error while handling ModIR: {}", - "ChecksumMismatch" - )); - } - MlxError::InvalidInputData => { - nc.ping(gettext!( - "⚠️ Error while handling ModIR: {}", - "InvalidInputData" - )); - } - }, - } - - let line = l.unwrap(); - - // record data - writeln!( - &mut outfile, - "{} {} Env: {} {} {} {} {} {}", - Local::now().format(&date_time_format).to_string(), - line.to_string(), - environment.co2, - environment.voc, - environment.temperature, - environment.humidity, - environment.pressure, - environment.baseline, - ) - .unwrap(); - - match sensors.update(line) { - SensorsChange::None => (), - SensorsChange::Alarm(w) => { - nc.send_message(gettext!("Fire Alarm {}", w)); - /* - state.set("alarm/fire", &w.to_string()); - sighup.store(true, Ordering::Relaxed); - exec_ssh_command(format!("kdb set user:/state/libelektra/opensesame/#0/current/alarm/fire \"{}\"", w)); - */ - } - SensorsChange::Chat(w) => { - nc.send_message(gettext!("Fire Chat {}", w)); - } - } - - if term.load(Ordering::Relaxed) { - environment.remember_baseline(&mut state); - return Ok(()); - } - - if sighup.load(Ordering::Relaxed) { - sighup.store(false, Ordering::Relaxed); - config.sync(); - state.sync(); - environment.restore_baseline(&mut state); - nc.ping(gettext!( - "👋 reloaded config&state in sensor mode for opensesame {} {}", - env!("CARGO_PKG_VERSION"), - startup_time - )); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Failed to init ModIR: {}", reason), + )) + .await?; } } } - let mut pwr = Pwr::new(&mut config); - do_reset(&mut watchdog, &mut nc, &mut pwr); - let mut validator = Validator::new(&mut config); - let mut buttons = Buttons::new(&mut config); - let mut environment = Environment::new(&mut config); - let mut garage = Garage::new(&mut config); - let bat = Bat::new(); - let mut alarm_not_active = true; - - nc.set_info_online(gettext!("🪫 ON {}", bat)); - - while !term.load(Ordering::Relaxed) { - watchdog.trigger(); - - if sigalrm.load(Ordering::Relaxed) { - sigalrm.store(false, Ordering::Relaxed); - buttons.ring_bell_alarm(20); - play_audio_file(config.get::("audio/alarm"), "--repeat".to_string()); - nc.send_message(gettext("🚨 Received alarm")); - } - - if sigusr1.load(Ordering::Relaxed) { - sigusr1.store(false, Ordering::Relaxed); - wait_for_ping = wait_for_ping_timeout + 1; - } - - if sigusr2.load(Ordering::Relaxed) { - sigusr2.store(false, Ordering::Relaxed); - buttons.ring_bell(20, 0); - nc.send_message(gettext("🔔 Received bell")); - play_audio_file(config.get::("audio/bell"), "--quiet".to_string()); - } - - if sighup.load(Ordering::Relaxed) { - nc.ping(gettext!( - "👋reloading config&state for opensesame {} {}", - env!("CARGO_PKG_VERSION"), - startup_time - )); - sighup.store(false, Ordering::Relaxed); - config.sync(); - state.sync(); - environment.restore_baseline(&mut state); - if let Some(alarm) = state.get_option::("alarm/fire") { - if alarm_not_active { - nc.send_message(gettext!( - "🚨 Fire Alarm! Fire Alarm! Fire ALARM! ⏰. {}", - alarm - )); - buttons.ring_bell_alarm(10); - if config.get_bool("garage/enable") { - play_audio_file( - config.get::("audio/alarm"), - "--repeat".to_string(), - ); - thread::Builder::new().name("killall to ring ALARM".to_string()).spawn(move || { - exec_ssh_command(format!("kdb set user:/state/libelektra/opensesame/#0/current/alarm/fire \"{}\"", alarm)); - }).unwrap(); - }; - alarm_not_active = false; - } - } else { - // config option removed, go out of alarm mode - alarm_not_active = true; - } - } - - if started_message_timeout > 1 { - started_message_timeout -= 1; - } else if started_message_timeout == 1 { - nc.ping(gettext!( - "👋 opensesame {} started {}", - env!("CARGO_PKG_VERSION"), - startup_time - )); - started_message_timeout = 0; // job done, disable - nc.set_info_online(gettext!("🔋 ON {}", bat)); - } - - if environment.handle() { - if handle_environment(&mut environment, &mut nc, Some(&mut buttons), &mut config) { - state.set("alarm/fire", &environment.name); - sighup.store(true, Ordering::Relaxed); - } - } - - if enable_ping { - wait_for_ping += 1; - } - if wait_for_ping > wait_for_ping_timeout { - let sys = System::new(); - let loadavg = sys.load_average().unwrap(); - nc.ping (format!("{} Ping! Version {}, Watchdog {}, {}, Status {}, Error {}, Load {} {} {}, Memory usage {}, Swap {}, CPU temp {}, Startup {} Bat {}", ping_counter, env!("CARGO_PKG_VERSION"), watchdog.wait_for_watchdog_trigger, environment.to_string(), environment.status, environment.error, loadavg.one, loadavg.five, loadavg.fifteen, sys.memory().unwrap().total, sys.swap().unwrap().total, sys.cpu_temp().unwrap(), startup_time, bat)); - ping_counter += 1; - wait_for_ping = 0; // restart - } - - match garage.handle() { - GarageChange::None => (), - GarageChange::PressedTasterEingangOben => { - nc.licht(gettext!( - "💡 Pressed at entrance top switch. Switch lights in garage. {}", - buttons.switch_lights(true, false) - )); - } - GarageChange::PressedTasterTorOben => { - nc.licht(gettext!( - "💡 Pressed top switch at garage door. Switch lights in and out garage. {}", - buttons.switch_lights(true, true) - )); - } - GarageChange::PressedTasterEingangUnten | GarageChange::PressedTasterTorUnten => { - buttons.open_door(); - } - - GarageChange::ReachedTorEndposition => { - nc.set_info_door(gettext("🔒 Open")); - nc.send_message(gettext!( - "🔒 Garage door closed. {}", - environment.to_string() - )); - } - GarageChange::LeftTorEndposition => { - nc.set_info_door(gettext("🔓 Closed")); - nc.send_message(gettext!("🔓 Garage door open. {}", environment.to_string())); - } - } - - let changes = buttons.handle(); - match changes { - StateChange::Pressed(button) => { - match button { - buttons::BUTTON_BELL => { - let now = Local::now(); - if now.hour() >= 7 && now.hour() <= 21 { - buttons.ring_bell(2, 5); - if config.get_bool("garage/enable") { - play_audio_file( - config.get::("audio/bell"), - "--quiet".to_string(), - ); - thread::Builder::new() - .name("killall to ring bell".to_string()) - .spawn(move || { - exec_ssh_command("killall -SIGUSR2 opensesame".to_string()); - }) - .unwrap(); - } - nc.send_message(gettext!( - "🔔 Pressed button bell. {}", - environment.to_string() - )); - } else { - buttons.show_wrong_input(); - nc.send_message(gettext!("🔕 Did not ring bell (button was pressed) because the time 🌜 is {}, {}", now.format(&time_format), environment.to_string())); - } - } - buttons::TASTER_INNEN => { - nc.licht(gettext!( - "💡 Pressed switch inside. {}. {}", - buttons.switch_lights(true, true), - environment.to_string() - )); - } - buttons::TASTER_AUSSEN => { - nc.licht(gettext!( - "💡 Pressed switch outside or light button. {}. {}", - buttons.switch_lights(false, true), - environment.to_string() - )); - } - buttons::TASTER_GLOCKE => { - let now = Local::now(); - if now.hour() >= 7 && now.hour() <= 21 { - buttons.ring_bell(5, 5); - nc.send_message(gettext!( - "🔔 Pressed switch bell. {}", - environment.to_string() - )); - } else { - buttons.show_wrong_input(); - nc.send_message(gettext!("🔕 Did not ring bell (taster outside) because the time 🌜 is {}, {}", now.format(&time_format), environment.to_string())); - } - } - _ => panic!("🔘 Pressed {}, {}", button, environment.to_string()), - } - } - StateChange::Released(_button) => (), - StateChange::LightsOff => nc.licht(gettext!( - "🕶️ Light was turned off. {}", - environment.to_string() - )), - StateChange::None => (), - StateChange::Err(board) => { - let sys = System::new(); - let loadavg = sys.load_average().unwrap(); - nc.ping(gettext!("⚠️ Error reading buttons of board {}. Environment: {}, Load average: {} {} {}, Memory usage: {}, Swap: {}, CPU temp: {}, Bat: {}", board, environment.to_string(), loadavg.one, loadavg.five, loadavg.fifteen, sys.memory().unwrap().total, sys.swap().unwrap().total, sys.cpu_temp().unwrap(), bat)); - do_reset(&mut watchdog, &mut nc, &mut pwr); - } - } + if env_enabled { + let interval = interval(Duration::from_secs( + config.get::("environment/data/interval"), + )); + let garage_enabled = config.get_bool("garage/enable"); + tasks.push(spawn(Environment::get_background_task( + Environment::new(&mut config, state_mutex.clone()), + interval, + nextcloud_sender.clone(), + command_sender.clone(), + audio_sender.clone(), + environment_receiver, + garage_enabled, + ))); + } - let sequence = buttons.sequence.to_vec(); - match validator.validate(&mut buttons.sequence) { - Validation::Validated(user) => { - buttons.open_door(); - nc.send_message(gettext!("🤗 Opened for {}", user)); - let now = Local::now(); - let (sunrise, sunset) = sunrise_sunset( - config.get::("location/latitude"), - config.get::("location/longitude"), - now.year(), - now.month(), - now.day(), - ); - if now.timestamp() < sunrise || now.timestamp() > sunset { - nc.licht(gettext!( - "💡 Switch lights in and out. {}", - buttons.switch_lights(true, true) - )); - } else { - nc.licht(gettext!( - "🕶️ Don't switch lights as its day. Now: {} Sunrise: {} Sunset: {}", - now.timestamp(), - sunrise, - sunset - )); - } - } - Validation::Timeout => { - if sequence != vec![0, 15] { - buttons.show_wrong_input(); - buttons.ring_bell(20, 0); - nc.send_message(gettext!( - "⌛ Timeout with sequence {}", - format!("{:?}", sequence) - )); - } + // if env_enabled || buttons_enabled { + let audio_bell = config.get::("audio/bell"); + let audio_alarm = config.get::("audio/alarm"); + tasks.push(spawn(Audio::get_background_task( + Audio::new(audio_bell, audio_alarm), + audio_receiver, + nextcloud_sender.clone(), + ))); + // } + + if weatherstation_enabled { + let clima_sensor_result = ClimaSensorUS::new(&mut config); + let interval = interval(Duration::from_secs( + config.get::("weatherstation/data/interval"), + )); + match clima_sensor_result { + Ok(clima_sensor) => { + tasks.push(spawn(ClimaSensorUS::get_background_task( + clima_sensor, + interval, + nextcloud_sender.clone(), + ))); } - Validation::SequenceTooLong => { - buttons.show_wrong_input(); - buttons.ring_bell(20, 0); - nc.send_message(gettext!( - "⌛ Sequence {} too long", - format!("{:?}", sequence) - )); + Err(error) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Failed to init libmodbus connection: {}", error), + )) + .await?; } - Validation::None => (), } + } - remember_baseline_counter += 1; - if remember_baseline_counter == wait_for_remember_baseline { - environment.remember_baseline(&mut state); - remember_baseline_counter = 0; - } + if bat_enabled { + tasks.push(spawn(Bat::get_background_task( + Bat::new(), + nextcloud_sender.clone(), + ))); + } - thread::sleep(time::Duration::from_millis(10)); + if watchdog_enabled { + let interval = interval(Duration::from_secs(config.get::("watchdog/interval"))); + let path = config.get::("watchdog/path"); + tasks.push(spawn(Watchdog::get_background_task(path, interval))); } - environment.remember_baseline(&mut state); - nc.set_info_online(gettext("📴 OFF")); - nc.ping(gettext!( - "👋 opensesame {} bye-bye {}", - env!("CARGO_PKG_VERSION"), - Local::now().format(&date_time_format).to_string() - )); + if ping_enabled { + tasks.push(spawn(Ping::get_background_task( + Ping::new(startup_time.to_string()), + ping_receiver, + nextcloud_sender.clone(), + ))) + } + let signals = Signals::new( + config_mutex.clone(), + state_mutex.clone(), + ping_enabled, + buttons_enabled, + env_enabled, + startup_time.to_string(), + ping_sender.clone(), + command_sender.clone(), + nextcloud_sender.clone(), + environment_sender.clone(), + audio_sender.clone(), + ); + + tasks.push(spawn(signals.get_background_task())); + + nextcloud_sender.send( + NextcloudEvent::Chat(NextcloudChat::Default, + gettext!("Enabled Modules: \nButtons: {}\n, Garage: {}\n, Sensors: {}\n, ModIR: {}\n, Environment: {}\n, Weatherstation: {}\n, Battery: {}\n, Watchdog: {}\n", + buttons_enabled, + garage_enabled, + sensors_enabled, + modir_enabled, + env_enabled, + weatherstation_enabled, + bat_enabled, + watchdog_enabled, +))).await?; + + join_all(tasks).await; Ok(()) } diff --git a/src/mod_ir_temp.rs b/src/mod_ir_temp.rs index 33acd9f..588ca7e 100644 --- a/src/mod_ir_temp.rs +++ b/src/mod_ir_temp.rs @@ -4,10 +4,17 @@ /// You can also modify the 'THRESHOLD_AMBIENT' and 'THRESHOLD_OBJECT' values. These two thresholds trigger the IrTempStateChange. /// For instance, if 'THRESHOLD_AMBIENT' < 'ambient_temp', then 'ChangedToAmbientTooHot' is triggered. use crate::config::Config; +use crate::nextcloud::{NextcloudChat, NextcloudEvent}; +use crate::types::ModuleError; +use futures::never::Never; +use gettextrs::gettext; use i2cdev::linux::LinuxI2CError; use linux_embedded_hal::{Delay, I2cdev}; use mlx9061x::ic::Mlx90614; +use mlx9061x::Error as MlxError; use mlx9061x::{Error, Mlx9061x, SlaveAddr}; +use tokio::sync::mpsc::Sender; +use tokio::time::Interval; const THRESHOLD_AMBIENT: f32 = 22.0; const THRESHOLD_OBJECT: f32 = 44.0; @@ -36,57 +43,34 @@ pub struct ModIR { _emissivity: f32, active_ambient_state: IrTempState, active_object_state: IrTempState, - data_interval: u16, - read_counter: u16, } impl ModIR { - /// This function is used to initialize a default instance. - pub fn new_default() -> Self { - Self { + /// This function initializes the MOD-IR-TEMP and returns an instance of ModIR upon success. + /// In case of an error, the error code is returned. + pub fn new(config: &mut Config) -> Result> { + let mut s = Self { mlx: None, - device: "/dev/null".to_string(), + device: config.get::("ir/device"), addr: SlaveAddr::Default, ambient_temp: 0.0, object_temp: 0.0, _emissivity: 1.0, active_ambient_state: IrTempState::Normal, active_object_state: IrTempState::Normal, - data_interval: 0, - read_counter: 0, - } - } + }; - /// This function initializes the MOD-IR-TEMP and returns an instance of ModIR upon success. - /// In case of an error, the error code is returned. - pub fn new(config: &mut Config) -> Result> { - let mut s: Self; - if config.get_bool("ir/enable") { - s = Self { - mlx: None, - device: config.get::("ir/device"), - addr: SlaveAddr::Default, - ambient_temp: 0.0, - object_temp: 0.0, - _emissivity: 1.0, - active_ambient_state: IrTempState::Normal, - active_object_state: IrTempState::Normal, - data_interval: config.get::("ir/data/interval"), - read_counter: config.get::("ir/data/interval"), - }; - if s.device != "/dev/null" { - match s.init() { - Ok(_) => { - return Ok(s); - } - Err(error) => { - return Err(error); - } + if s.device != "/dev/null" { + match s.init() { + Ok(_) => { + return Ok(s); + } + Err(error) => { + return Err(error); } } - } else { - s = ModIR::new_default(); } + Ok(s) } @@ -96,7 +80,6 @@ impl ModIR { match Mlx9061x::new_mlx90614(I2cdev::new(&self.device).unwrap(), self.addr, 5) { Ok(mlx_sensor) => { self.mlx = Some(mlx_sensor); - () } Err(error) => { return Err(error); @@ -134,7 +117,7 @@ impl ModIR { self.active_object_state = object_state; return IrTempStateChange::ChangedToCancelled; } - return IrTempStateChange::None; + IrTempStateChange::None } /// This function reads the ambient temperature and object temperature from the MOD-IR-TEMP sensor. @@ -145,37 +128,32 @@ impl ModIR { pub fn handle(&mut self) -> Result> { match &mut self.mlx { Some(mlx_sensor) => { - self.read_counter += 1; - // The '>=` operator is used to execute this function on the first call to initialize data. - if self.read_counter >= self.data_interval { - self.read_counter = 0; - let mut ambient_state = IrTempState::Normal; - let mut object_state = IrTempState::Normal; - match mlx_sensor.ambient_temperature() { - Ok(amb_temp) => { - self.ambient_temp = amb_temp; - if amb_temp > THRESHOLD_AMBIENT { - ambient_state = IrTempState::TooHot; - } - } - Err(error) => { - return Err(error); + let mut ambient_state = IrTempState::Normal; + let mut object_state = IrTempState::Normal; + match mlx_sensor.ambient_temperature() { + Ok(amb_temp) => { + self.ambient_temp = amb_temp; + if amb_temp > THRESHOLD_AMBIENT { + ambient_state = IrTempState::TooHot; } } + Err(error) => { + return Err(error); + } + } - match mlx_sensor.object1_temperature() { - Ok(obj_temp) => { - self.object_temp = obj_temp; - if obj_temp > THRESHOLD_OBJECT { - object_state = IrTempState::TooHot; - } - } - Err(error) => { - return Err(error); + match mlx_sensor.object1_temperature() { + Ok(obj_temp) => { + self.object_temp = obj_temp; + if obj_temp > THRESHOLD_OBJECT { + object_state = IrTempState::TooHot; } } - return Ok(self.set_handle_output(ambient_state, object_state)); + Err(error) => { + return Err(error); + } } + return Ok(self.set_handle_output(ambient_state, object_state)); } None => (), } @@ -187,7 +165,7 @@ impl ModIR { /// However, the emissivity only needs to be adjusted if we are using a specific object for measurement, as indicated [here](https://en.wikipedia.org/wiki/Emissivity). /// The 'emissivity' parameter can be chosen between 0.0 and 1.0. pub fn _change_emissivity(&mut self, emissivity: f32) -> Result> { - if emissivity >= 0.0 && emissivity <= 1.0 { + if (0.0..=1.0).contains(&emissivity) { match &mut self.mlx { Some(mlx_sensor) => match mlx_sensor.set_emissivity(emissivity, &mut Delay {}) { Ok(_) => { @@ -203,7 +181,94 @@ impl ModIR { } } } - return Ok(false); + Ok(false) + } + + pub async fn get_background_task( + mut self, + mut interval: Interval, + nextcloud_sender: Sender, + ) -> Result { + loop { + interval.tick().await; + match self.handle() { + Ok(state) => match state { + IrTempStateChange::None => (), + IrTempStateChange::ChanedToBothToHot => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🌡️🌡️ ModIR both sensors too hot! Ambient: {} °C, Object: {} °C", + self.ambient_temp, + self.object_temp + ), + )) + .await?; + } + IrTempStateChange::ChangedToAmbientToHot => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🌡️ ModIR ambient sensors too hot! Ambient: {} °C", + self.ambient_temp + ), + )) + .await?; + } + IrTempStateChange::ChangedToObjectToHot => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🌡️ ModIR object sensors too hot! Object: {} °C", + self.object_temp + ), + )) + .await?; + } + IrTempStateChange::ChangedToCancelled => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!( + "🌡 ModIR cancelled warning! Ambient: {} °C, Object: {} °C", + self.ambient_temp, + self.object_temp + ), + )) + .await?; + } + }, + Err(error_typ) => match error_typ { + MlxError::I2C(error) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Error while handling ModIR: {}", error), + )) + .await?; + } + MlxError::ChecksumMismatch => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Error while handling ModIR: {}", "ChecksumMismatch"), + )) + .await?; + } + MlxError::InvalidInputData => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Error while handling ModIR: {}", "InvalidInputData"), + )) + .await?; + } + }, + } + } } } @@ -222,8 +287,6 @@ mod tests { _emissivity: 1.0, active_ambient_state: IrTempState::Normal, active_object_state: IrTempState::Normal, - data_interval: 0, - read_counter: 0, }; assert!( diff --git a/src/nextcloud.rs b/src/nextcloud.rs index 95b6ef8..5a2792d 100644 --- a/src/nextcloud.rs +++ b/src/nextcloud.rs @@ -1,101 +1,129 @@ -use reqwest::header; +use crate::{audio::AudioEvent, buttons::CommandToButtons, config::Config, types::ModuleError}; +use futures::{never::Never, try_join}; +use reqwest::{ + header::{HeaderMap, ACCEPT, CONTENT_TYPE}, + Client, +}; +use std::collections::HashMap; +use tokio::{ + sync::mpsc::{Receiver, Sender}, + time::{self, interval}, +}; -use crate::config::Config; +pub enum NextcloudChat { + Default, + Ping, + Licht, +} + +pub enum NextcloudStatus { + Online, + Env, + Door, +} +pub enum NextcloudEvent { + Chat(NextcloudChat, String), + SendStatus, + Status(NextcloudStatus, String), +} + +#[derive(Clone)] pub struct Nextcloud { - url: String, + base_url: String, chat: String, chat_ping: String, chat_licht: String, + chat_commands: String, user: String, - pass: Option, + pass: String, info_door: String, info_environment: String, info_online: String, + client: Client, + headers: HeaderMap, } impl Nextcloud { pub fn new(config: &mut Config) -> Self { + let mut headers = HeaderMap::new(); + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert(ACCEPT, "application/json".parse().unwrap()); + headers.insert("OCS-APIRequest", "true".parse().unwrap()); + let client = reqwest::Client::new(); Self { - url: config.get::("nextcloud/url"), + base_url: config.get::("nextcloud/url"), chat: config.get::("nextcloud/chat"), chat_ping: config.get::("nextcloud/chat/ping"), chat_licht: config.get::("nextcloud/chat/licht"), + chat_commands: config.get::("nextcloud/chat/commands"), user: config.get::("nextcloud/user"), - pass: Some(config.get::("nextcloud/pass")), - info_door: "".to_string(), - info_environment: "".to_string(), - info_online: "".to_string(), + pass: config.get::("nextcloud/pass"), + info_door: String::new(), + info_environment: String::new(), + info_online: String::new(), + client, + headers, } } // sends once, Err if it does not work on network or nextcloud level - fn send_message_once( + async fn send_message_once( &self, - message: String, - chat: String, + message: &str, + chat: &str, ) -> Result { - let mut headers = header::HeaderMap::new(); - headers.insert("Content-Type", "application/json".parse().unwrap()); - headers.insert("Accept", "application/json".parse().unwrap()); - headers.insert("OCS-APIRequest", "true".parse().unwrap()); - - let result = reqwest::Client::new() - .post(&format!( + let mut payload = HashMap::new(); + payload.insert("token", &chat); + payload.insert("message", &message); + let response = self + .client + .post(format!( "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}", - &self.url, chat - )) - .basic_auth(&self.user, self.pass.as_ref()) - .headers(headers) - .body(format!( - "{{\"token\": \"{}\", \"message\": \"{}\"}}", - chat, message + &self.base_url, chat )) - .send(); - match result { - Ok(response) => match response.error_for_status() { - Ok(response) => Ok(response), - Err(error) => Err(error), - }, - Err(error) => Err(error), - } + .basic_auth(&self.user, Some(&self.pass)) + .headers(self.headers.clone()) + .json(&payload) + .send() + .await?; + response.error_for_status() } - pub fn licht(&self, message: String) { - let result = self.send_message_once(message.to_string(), self.chat_licht.to_string()); + async fn licht(&self, message: String) { + let result = self.send_message_once(&message, &self.chat_licht).await; match result { - Ok(_response) => (), + Ok(..) => (), Err(error) => { eprintln!("Couldn't post to licht {} because {}", message, error); } - } + }; } - pub fn ping(&self, message: String) { - let result = self.send_message_once(message.to_string(), self.chat_ping.to_string()); + async fn ping(&self, message: String) { + let result = self.send_message_once(&message, &self.chat_ping).await; match result { - Ok(_response) => (), + Ok(..) => (), Err(error) => { eprintln!("Couldn't ping {} because {}", message, error); } - } + }; } // logs and sends message, retries once, if it fails twice it logs the error - pub fn send_message(&self, message: String) { - let result = self.send_message_once(message.to_string(), self.chat.to_string()); + async fn send_message(&self, message: String) { + let result = self.send_message_once(&message, &self.chat).await; match result { - Ok(_response) => (), + Ok(..) => (), Err(old_error) => { - let result = self.send_message_once( - message.to_string() + " (sent again)", - self.chat.to_string(), - ); + let result = self + .send_message_once(&format!("{} (sent again)", message), &self.chat) + .await; match result { - Ok(_response) => (), + Ok(..) => (), Err(error) => { eprintln!( "Couldn't send {} first because {} and then because {}", @@ -107,48 +135,227 @@ impl Nextcloud { } } - pub fn set_info_online(&mut self, info: String) { + async fn set_info_online(&mut self, info: String) { self.info_online = info; - self.update_status(); + self.send_status().await; } - pub fn set_info_door(&mut self, info: String) { + async fn set_info_door(&mut self, info: String) { self.info_door = info; - self.update_status(); + self.send_status().await; } - pub fn set_info_environment(&mut self, info: String) { + async fn set_info_environment(&mut self, info: String) { self.info_environment = info; - self.update_status(); + self.send_status().await; } - fn update_status(&mut self) { - self.set_status(format!( - "{} {} {}", - self.info_online, self.info_door, self.info_environment - )); + async fn set_status_in_chat(&self) { + self.send_message_once( + &format!( + "Status: {} {} {}", + self.info_online, self.info_door, self.info_environment + ), + &self.chat_commands, + ) + .await + .unwrap(); } - fn set_status(&self, message: String) { - let mut headers = header::HeaderMap::new(); - headers.insert("Content-Type", "application/json".parse().unwrap()); - headers.insert("Accept", "application/json".parse().unwrap()); - headers.insert("OCS-APIRequest", "true".parse().unwrap()); - - let result = reqwest::Client::new() - .put(&format!( + async fn send_status(&self) { + let status = format!( + "{} {} {}", + self.info_online, self.info_door, self.info_environment + ); + let mut payload = HashMap::new(); + payload.insert("message", &status); + let result = self + .client + .put(format!( "{}/ocs/v2.php/apps/user_status/api/v1/user_status/message/custom", - &self.url + &self.base_url )) - .basic_auth(&self.user, self.pass.as_ref()) - .headers(headers) - .body(format!("{{\"message\": \"{}\"}}", message)) - .send(); + .basic_auth(&self.user, Some(&self.pass)) + .headers(self.headers.clone()) + .json(&payload) + .send() + .await; match result { - Ok(_response) => (), + Ok(..) => (), Err(error) => { - eprintln!("Couldn't set status message {} because {}", message, error); + eprintln!("Couldn't set status message {} because {}", status, error); } }; } + + async fn get_last_messages( + &self, + last_known_message_id: &str, + ) -> Result { + let endpoint = format!( + "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}", + self.base_url, self.chat_commands, + ); + let query_params = [ + ("lookIntoFuture", "1"), + ("limit", "100"), + ("lastKnownMessageId", last_known_message_id), + ]; + + let response = self + .client + .get(&endpoint) + .query(&query_params) + .basic_auth(&self.user, Some(&self.pass)) + .headers(self.headers.clone()) + .send() + .await?; + + response.error_for_status() + } + + pub async fn get_background_task( + self, + nextcloud_receiver: Receiver, + nextcloud_sender: Sender, + command_sender: Sender, + audio_sender: Sender, + ) -> Result { + try_join!( + self.clone().message_sender_loop(nextcloud_receiver), + self.command_loop(nextcloud_sender, command_sender, audio_sender) + )?; + Err(ModuleError::new(String::from( + "Exit get_background_task loop!", + ))) + } + + async fn message_sender_loop( + mut self, + mut nextcloud_receiver: Receiver, + ) -> Result { + self.send_message(String::from("Nextcloud stated...")).await; + while let Some(event) = nextcloud_receiver.recv().await { + match event { + NextcloudEvent::Chat(chat, message) => match chat { + NextcloudChat::Default => self.send_message(message).await, + NextcloudChat::Ping => self.ping(message).await, + NextcloudChat::Licht => self.licht(message).await, + }, + NextcloudEvent::SendStatus => self.set_status_in_chat().await, + NextcloudEvent::Status(status, message) => match status { + NextcloudStatus::Online => self.set_info_online(message).await, + NextcloudStatus::Env => self.set_info_environment(message).await, + NextcloudStatus::Door => self.set_info_door(message).await, + }, + } + } + Err(ModuleError::new(String::from( + "Exit Nextcloud messagesender loop!", + ))) + } + + async fn command_loop( + self, + nextcloud_sender: Sender, + command_sender: Sender, + audio_sender: Sender, + ) -> Result { + let a = self + .send_message_once("Started listening to commands here", &self.chat_commands) + .await + .unwrap(); + let mut last_known_message_id = + a.json::().await.unwrap()["ocs"]["data"]["id"].to_string(); + loop { + let response = self.get_last_messages(&last_known_message_id).await; + match response { + Ok(response) => { + let status = response.status().as_u16(); + if status == 200 { + let json = response.json::().await.unwrap(); + + let messages = json["ocs"]["data"].as_array(); + for message in messages + .unwrap() + .iter() + .map(|m| m["message"].as_str().unwrap()) + { + if message.starts_with('\\') { + let command_and_args = message + .strip_prefix('\\') + .unwrap() + .split_whitespace() + .collect::>(); + let command = command_and_args[0]; + let args = &command_and_args[1..]; + match command { + "status" => { + nextcloud_sender.send(NextcloudEvent::SendStatus).await? + } + "setpin" => { + // TODO: How do we access config? + } + "switchlights" => { + if args.len() != 2 { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + String::from( + "Usage: switchlights ", + ), + )) + .await?; + } + + let inner_light = args[0].eq_ignore_ascii_case("true"); + let outer_light = args[1].eq_ignore_ascii_case("true"); + + command_sender + .send(CommandToButtons::SwitchLights( + inner_light, + outer_light, + String::from("Switch lights {} {}"), + )) + .await?; + } + "opensesame" => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + String::from("Opening door"), + )) + .await?; + command_sender.send(CommandToButtons::OpenDoor).await?; + } + "ring_bell" => audio_sender.send(AudioEvent::Bell).await?, + "fire_alarm" => { + audio_sender.send(AudioEvent::FireAlarm).await? + } + _ => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + format!("Unknown command {}!", command), + )) + .await?; + } + } + } + } + if let Some(last_message) = messages.unwrap().last() { + last_known_message_id = last_message["id"].to_string(); + } + } else if status != 304 { + // 304 - Not modified is expected if there are no new messages + println!("Status code was not successful but {}", response.status()); + } + } + Err(err) => { + eprintln!("Error: {:?}", err); + } + } + interval(time::Duration::from_secs(1)).tick().await; + } + } } diff --git a/src/ping.rs b/src/ping.rs new file mode 100644 index 0000000..e195330 --- /dev/null +++ b/src/ping.rs @@ -0,0 +1,85 @@ +use futures::never::Never; +use gettextrs::gettext; +use systemstat::{Platform, System}; +use tokio::sync::mpsc::{Receiver, Sender}; + +use crate::{ + nextcloud::{NextcloudChat, NextcloudEvent}, + types::ModuleError, +}; + +pub enum PingEvent { + UpadeEnv(String), + UpdateEnvStatus(u8), + UpdateEnvError(u8), + UpdateBatCapacity(u8), + SendPing, +} + +pub struct Ping { + ping_counter: u64, + environment: String, + environment_status: u8, + environment_error: u8, + bat_capacity: u8, + startup_time: String, +} + +impl Ping { + pub fn new(startup_time: String) -> Self { + Self { + ping_counter: 0, + environment: String::from(""), + environment_status: 0, + environment_error: 0, + bat_capacity: 0, + startup_time, + } + } + + pub async fn get_background_task( + mut self, + mut ping_receiver: Receiver, + nextcloud_sender: Sender, + ) -> Result { + while let Some(event) = ping_receiver.recv().await { + match event { + PingEvent::UpadeEnv(value) => { + self.environment = value; + } + PingEvent::UpdateEnvStatus(value) => { + self.environment_status = value; + } + PingEvent::UpdateEnvError(value) => { + self.environment_error = value; + } + PingEvent::UpdateBatCapacity(value) => { + self.bat_capacity = value; + } + PingEvent::SendPing => { + let sys = System::new(); + let loadavg = sys.load_average().unwrap(); + + nextcloud_sender.send(NextcloudEvent::Chat(NextcloudChat::Ping,gettext!("{} Ping! Version {}, {}, Status {}, Error {}, Load {} {} {}, Memory usage {}, Swap {}, CPU temp {}, Startup {} Bat {}", + self.ping_counter, + env!("CARGO_PKG_VERSION"), + self.environment, + self.environment_status, + self.environment_error, + loadavg.one, + loadavg.five, + loadavg.fifteen, + sys.memory().unwrap().total, + sys.swap().unwrap().total, + sys.cpu_temp().unwrap(), + self.startup_time, + self.bat_capacity))).await?; + + self.ping_counter += 1; + } + } + } + + Err(ModuleError::new(String::from("Exit Ping loop!"))) + } +} diff --git a/src/pwr.rs b/src/pwr.rs index b5b9d3c..b072925 100644 --- a/src/pwr.rs +++ b/src/pwr.rs @@ -34,8 +34,8 @@ impl Pwr { pub fn enabled(&mut self) -> bool { match &self.pwr_line { - Some(_pwr_line) => return true, - None => return false, + Some(_pwr_line) => true, + None => false, } } diff --git a/src/sensors.rs b/src/sensors.rs index 3fa909e..a478c0b 100644 --- a/src/sensors.rs +++ b/src/sensors.rs @@ -1,6 +1,11 @@ -use crate::config::Config; - +use crate::nextcloud::NextcloudChat; +use crate::{config::Config, nextcloud::NextcloudEvent, types::ModuleError}; +use futures::never::Never; +use gettextrs::gettext; use std::str::FromStr; +use tokio::fs::File; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::sync::mpsc::Sender; const ALPHA: f64 = 0.6; @@ -43,7 +48,7 @@ const ALARM_JUMP: f64 = 60f64; impl Config<'_> { fn get_sensor_element_option(&mut self, nr: u8, name: &str) -> Option { - return self.get_option::(&format!("sensors/#{}/{}", nr, name).to_string()); + self.get_option::(&format!("sensors/#{}/{}", nr, name).to_string()) } fn get_sensor_element(&mut self, nr: u8, name: &str) -> T { @@ -112,7 +117,7 @@ impl Sensors { " Min: {} Avg: {} Max: {} Text: {}", self.sensors[i].min, self.sensors[i].avg, self.sensors[i].max, text ); - return ret; + ret } fn threstext(&mut self, i: usize, text: &str, value: u16) -> String { @@ -145,7 +150,7 @@ impl Sensors { let mut ret = SensorsChange::None; for i in 0..12 { - if self.sensors[i].loc == "" { + if self.sensors[i].loc.is_empty() { continue; } @@ -153,7 +158,7 @@ impl Sensors { if self.init { let expmovavg = ALPHA * values[i] as f64 + (1.0f64 - ALPHA) * self.sensors[i].value as f64; - let prev_expmovavg = self.sensors[i].expmovavg as f64; + let prev_expmovavg = self.sensors[i].expmovavg; match self.sensors[i].triggered { SensorsChange::Alarm(_) => {} SensorsChange::Chat(_) => { @@ -244,7 +249,49 @@ impl Sensors { } // end of if let } self.init = true; - return ret; + ret + } + + pub async fn get_background_task( + mut self, + device_path: String, + nextcloud_sender: Sender, + //state_mutex: Arc>>, + //pid: u32, + ) -> Result { + let device_file = File::open(device_path).await.expect("error here"); + let reader = BufReader::new(device_file); + + let mut lines = reader.lines(); + while let Some(line) = lines.next_line().await? { + match self.update(line.clone()) { + SensorsChange::None => (), + SensorsChange::Alarm(w) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("Fire Alarm {}", w), + )) + .await?; + /*let mut state = state_mutex.lock().await; + state.set("alarm/fire", &w.to_string()); + kill(nix::unistd::Pid::from_raw(pid as i32), Signal::SIGHUP)?; + spawn(exec_ssh_command(format!( + "kdb set user:/state/libelektra/opensesame/#0/current/alarm/fire \"{}\"", + w + )));*/ + } + SensorsChange::Chat(w) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("Fire Chat {}", w), + )) + .await?; + } + } + } + Err(ModuleError::new(String::from("sensors_loop exited"))) } } @@ -256,7 +303,7 @@ mod tests { use serial_test::serial; use std::env; - const CONFIG_PARENT: &'static str = "/sw/libelektra/opensesame/#0/current"; + const CONFIG_PARENT: &str = "/sw/libelektra/opensesame/#0/current"; #[ignore] #[test] diff --git a/src/signals.rs b/src/signals.rs new file mode 100644 index 0000000..f143633 --- /dev/null +++ b/src/signals.rs @@ -0,0 +1,211 @@ +use std::sync::Arc; + +use futures::never::Never; +use gettextrs::gettext; +use signal::unix::signal; +use tokio::{ + select, + signal::{self, unix::SignalKind}, + spawn, + sync::{mpsc::Sender, Mutex}, +}; + +use crate::{ + audio::AudioEvent, + buttons::CommandToButtons, + config::Config, + environment::EnvEvent, + nextcloud::{NextcloudChat, NextcloudEvent}, + ping::PingEvent, + ssh::exec_ssh_command, + types::ModuleError, +}; + +pub struct Signals<'a> { + ping_sender: Sender, + ping_enabled: bool, + command_sender: Sender, + buttons_enabled: bool, + nextcloud_sender: Sender, + environment_sender: Sender, + environment_enabled: bool, + audio_sender: Sender, + alarm_not_active: bool, + startup_time: String, + config_mutex: Arc>>, + state_mutex: Arc>>, +} + +unsafe impl<'a> Send for Signals<'a> {} + +impl<'a> Signals<'a> { + pub fn new( + config_mutex: Arc>>, + state_mutex: Arc>>, + ping_enabled: bool, + buttons_enabled: bool, + environment_enabled: bool, + startup_time: String, + ping_sender: Sender, + command_sender: Sender, + nextcloud_sender: Sender, + environment_sender: Sender, + audio_sender: Sender, + ) -> Self { + Self { + ping_sender, + command_sender, + nextcloud_sender, + environment_sender, + audio_sender, + alarm_not_active: true, + startup_time, + ping_enabled, + buttons_enabled, + environment_enabled, + config_mutex, + state_mutex, + } + } + + async fn sigterm(&mut self) -> Result<(), ModuleError> { + //send message to environment to remember_baseline ans ENV need to use a Mutex of State + //How to enable env to use message recv, if it waits one minute or more for the next loop iteration? + //Two different loops? but they then have different states if a restore baseline is called? + //And only execute if env is enabled, otherwise we get an error + self.environment_sender + .send(EnvEvent::RememberBaseline) + .await?; + Ok(()) + } + + async fn sighup(&mut self) -> Result<(), ModuleError> { + self.nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!( + "👋 reloading config&state for opensesame {} {}", + env!("CARGO_PKG_VERSION"), + self.startup_time + ), + )) + .await?; + + let mut config = self.config_mutex.lock().await; + let mut state = self.state_mutex.lock().await; + config.sync(); + state.sync(); + if self.environment_enabled { + self.environment_sender + .send(EnvEvent::RestoreBaseline) + .await?; + } + self.nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!( + "👋 reloaded config&state in sensor mode for opensesame {} {}", + env!("CARGO_PKG_VERSION"), + self.startup_time + ), + )) + .await?; + + if let Some(alarm) = state.get_option::("alarm/fire") { + if self.alarm_not_active { + self.nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext!("🚨 Fire Alarm! Fire Alarm! Fire ALARM! ⏰. {}", alarm), + )) + .await?; + if self.buttons_enabled { + self.command_sender + .send(CommandToButtons::RingBellAlarm(10)) + .await?; + } + if config.get_bool("garage/enable") { + self.audio_sender.send(AudioEvent::FireAlarm).await?; + spawn(exec_ssh_command(format!( + "kdb set user:/state/libelektra/opensesame/#0/current/alarm/fire \"{}\"", + alarm + ))); + }; + self.alarm_not_active = false; + } + } else { + // config option removed, go out of alarm mode + self.alarm_not_active = true; + } + Ok(()) + } + + async fn sigalarm(&mut self) -> Result<(), ModuleError> { + if self.buttons_enabled { + self.command_sender + .send(CommandToButtons::RingBellAlarm(20)) + .await?; + } + self.audio_sender.send(AudioEvent::FireAlarm).await?; + self.nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🚨 Received alarm"), + )) + .await?; + Ok(()) + } + + async fn sigusr1(&mut self) -> Result<(), ModuleError> { + self.ping_sender.send(PingEvent::SendPing).await?; + Ok(()) + } + + async fn sigusr2(&mut self) -> Result<(), ModuleError> { + if self.buttons_enabled { + self.command_sender + .send(CommandToButtons::RingBell(20, 0)) + .await?; + } + self.audio_sender.send(AudioEvent::Bell).await?; + self.nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🔔 Received bell"), + )) + .await?; + Ok(()) + } + + pub async fn get_background_task(mut self) -> Result { + let mut sig_usr1 = signal(SignalKind::user_defined1())?; + let mut sig_usr2 = signal(signal::unix::SignalKind::user_defined2())?; + let mut sig_alarm = signal(signal::unix::SignalKind::alarm())?; + let mut sig_hanghup = signal(signal::unix::SignalKind::hangup())?; + let mut sig_term = signal(signal::unix::SignalKind::terminate())?; + + loop { + select! { + _ = sig_usr1.recv() => { + if self.ping_enabled { + self.sigusr1().await?; + } + } + _ = sig_usr2.recv() => { + self.sigusr2().await?; + } + _ = sig_alarm.recv() => { + self.sigalarm().await?; + } + _ = sig_hanghup.recv() => { + self.sighup().await?; + } + _ = sig_term.recv() => { + if self.environment_enabled { + self.sigterm().await?; + } + } + } + } + } +} diff --git a/src/ssh.rs b/src/ssh.rs index 4b9997d..a60eb67 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -1,50 +1,19 @@ -use std::io::Read; -use std::net::TcpStream; -use std::path::Path; +use async_ssh2_tokio::*; -use ssh2::Session; +const SSH_HOST_IP: &str = "192.168.178.53"; -fn exec_ssh_command_once(command: String) -> Result> { - let tcp = TcpStream::connect("192.168.178.53:22")?; - let mut sess = Session::new()?; - sess.set_tcp_stream(tcp); - sess.handshake()?; - sess.userauth_pubkey_file( +pub async fn exec_ssh_command(command: String) -> Result<(), Error> { + let auth_method = AuthMethod::PrivateKeyFile { + key_file_name: String::from("/home/olimex/.ssh/id_rsa"), + key_pass: None, + }; + let client = Client::connect( + (SSH_HOST_IP, 22), "olimex", - Some(Path::new("/home/olimex/.ssh/id_rsa.pub")), - Path::new("/home/olimex/.ssh/id_rsa"), - None, - )?; - let mut channel = sess.channel_session()?; - channel.exec(&command)?; - let mut s = String::new(); - channel.read_to_string(&mut s)?; - channel.wait_close().ok(); - return Ok(channel.exit_status()?); -} - -fn exec_ssh_command_log_error(command: String) { - let result = exec_ssh_command_once(command.to_string()); - match result { - Ok(response) => { - if response != 0 { - eprintln!("Couldn't exec {} with exit status {}", &command, response); - } - } - Err(error) => { - eprintln!("Couldn't exec {} because {}", &command, error); - } - } -} - -pub fn exec_ssh_command(command: String) { - let result = exec_ssh_command_once(command.clone()); - match result { - Ok(response) => { - if response != 0 { - exec_ssh_command_log_error(command.clone()); - } - } - Err(_error) => exec_ssh_command_log_error(command.clone()), - } + auth_method, + ServerCheckMethod::NoCheck, + ) + .await?; + client.execute(&command).await?; + Ok(()) } diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..5c5a171 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,57 @@ +use i2cdev::linux::LinuxI2CError; +use nix::errno::Errno; +use std::{error::Error, fmt}; +use tokio::sync::mpsc::error::SendError; + +#[derive(Debug, Clone)] +pub struct ModuleError { + reason: String, +} + +impl ModuleError { + pub fn new(reason: String) -> Self { + Self { reason } + } +} + +impl Error for ModuleError {} + +impl fmt::Display for ModuleError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ModuleErrorr: reason: {}", self.reason) + } +} + +// TODO: These are hacks for now: + +impl From> for ModuleError { + fn from(error: SendError) -> Self { + ModuleError { + reason: format!("SendError: {}", error), + } + } +} + +impl From for ModuleError { + fn from(error: std::io::Error) -> Self { + ModuleError { + reason: format!("std::io::Error: {} {}", error.kind(), error), + } + } +} + +impl From for ModuleError { + fn from(error: LinuxI2CError) -> Self { + ModuleError { + reason: format!("LinuxI2CError: {}", error), + } + } +} + +impl From for ModuleError { + fn from(error: Errno) -> Self { + ModuleError { + reason: format!("Errno: {}", error), + } + } +} diff --git a/src/validator.rs b/src/validator.rs index 786af99..6fa5de2 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -43,7 +43,7 @@ impl Validator { self.timeout = 0; return Validation::Validated(ret); } - return Validation::None; + Validation::None } } @@ -53,7 +53,7 @@ mod tests { use super::*; use std::{env, vec}; - const CONFIG_PARENT: &'static str = "/sw/libelektra/opensesame/#0/current"; + const CONFIG_PARENT: &str = "/sw/libelektra/opensesame/#0/current"; fn setup_test_env(sequence: &str) -> Config { let mut config: Config = Config::new(CONFIG_PARENT); @@ -61,7 +61,7 @@ mod tests { env::set_var("RUST_BACKTRACE", config.get::("debug/backtrace")); config.cut("validator"); - config.add("validator/test", &sequence); + config.add("validator/test", sequence); config } diff --git a/src/watchdog.rs b/src/watchdog.rs index 1b4cf3c..73577ef 100644 --- a/src/watchdog.rs +++ b/src/watchdog.rs @@ -1,35 +1,46 @@ -use std::fs; -use std::io::Write; +use futures::never::Never; -use crate::config::Config; +use tokio::{fs::File, io::AsyncWriteExt, time::Interval}; + +use crate::types::ModuleError; -// pub const MAX_TIMEOUT: u64 = 16 * 1000; // timeout in ms as seen in dmesg pub const SAFE_TIMEOUT: u64 = 15 * 1000; // safe to wait if trigger was done just before -pub struct Watchdog { - handle: Option, - pub wait_for_watchdog_trigger: u64, -} +pub struct Watchdog {} impl Watchdog { - pub fn new(config: &mut Config) -> Self { - Self { - handle: if config.get_bool("watchdog/enable") { - Some(fs::File::create("/dev/watchdog").expect("could not open watchdog")) - } else { - None - }, - wait_for_watchdog_trigger: 0, + pub async fn get_background_task( + path: String, + mut interval: Interval, + ) -> Result { + let mut handle = File::create(path) + .await + .map_err(|_| ModuleError::new(String::from("could not open watchdog")))?; + loop { + interval.tick().await; + handle + .write_all(b"a") + .await + .map_err(|_| ModuleError::new(String::from("could not write to watchdog")))?; } } - pub fn trigger(&mut self) { - if let Some(handle) = &mut self.handle { - self.wait_for_watchdog_trigger += 1; - if self.wait_for_watchdog_trigger > 1000 { - handle.write_all(b"a").expect("could not write to watchdog"); - self.wait_for_watchdog_trigger = 0; - } + //This function is only used to test if the system reboots after this module stops writing to the watchdog file. + //For testing change the get_background_task to test_get_background_task in main.rs + pub async fn test_get_background_task( + path: String, + mut interval: Interval, + ) -> Result { + let mut handle = File::create(path) + .await + .map_err(|_| ModuleError::new(String::from("could not open watchdog")))?; + for _i in 1..=100 { + interval.tick().await; + handle + .write_all(b"a") + .await + .map_err(|_| ModuleError::new(String::from("could not write to watchdog")))?; } + Err(ModuleError::new(String::from("doesnt run for loop"))) } } diff --git a/src/weather_station/dump_all_regs.rs b/src/weather_station/dump_all_regs.rs index d661c5a..7e6931a 100644 --- a/src/weather_station/dump_all_regs.rs +++ b/src/weather_station/dump_all_regs.rs @@ -3,7 +3,7 @@ extern crate libmodbus; use libmodbus::*; ///Constants -const DEVICE: &'static str = "/dev/ttyS5"; +const DEVICE: &str = "/dev/ttyS5"; const BAUDRATE: i32 = 9600; const PARITY: char = 'N'; const DATA_BITS: i32 = 8; @@ -11,7 +11,7 @@ const STOP_BITS: i32 = 1; const SLAVE_ID: u8 = 1; ///All 96 input registers -const INPUT_REG: [(u16, &'static str, &'static str, i32, char); 96] = [ +const INPUT_REG: [(u16, &str, &str, i32, char); 96] = [ (0x7533, "Mittelwert Windgeschwindigkeit", "m/s", 10, 'u'), (0x753B, "Maximalwert Windgeschwindigkeit (Böe) verfügbar wenn AV>=30", "m/s", 10, 'u'), (0x75FB, "Mittelwert Windrichtung", "°", 10, 'u'), @@ -111,7 +111,7 @@ const INPUT_REG: [(u16, &'static str, &'static str, i32, char); 96] = [ ]; ///All x hold registers -const HOLD_REG: [(u16, &'static str, &'static str); 18] = [ +const HOLD_REG: [(u16, &str, &str); 18] = [ (40015, "Befehl AV", "Mittelungsintervall für Windgeschwindigkeit und Windrichtung. 0..6000 (x100ms)"), (40031, "Befehl BP", "Parität, s. Befehl „BP“ Thies Format"), (40005, "Befehl BR", "Baudrate, s. Befehl „BR“ Thies Format"), @@ -138,8 +138,7 @@ fn conv_vec_to_value_s(vec: Vec) -> i32 { } fn conv_vec_to_value_u(vec: Vec) -> u32 { - let usign_val = (vec[0] as u32) << 16 | (vec[1] as u32); - usign_val + (vec[0] as u32) << 16 | (vec[1] as u32) } fn main() { @@ -160,12 +159,11 @@ fn main() { let mut data = vec![0u16; 2]; match ctx.read_input_registers(input_reg.0, 2, &mut data) { Ok(_) => { - let conv_data: f32; - if input_reg.4 == 'u' { - conv_data = (conv_vec_to_value_u(data) as f32) / input_reg.3 as f32; + let conv_data = if input_reg.4 == 'u' { + (conv_vec_to_value_u(data) as f32) / input_reg.3 as f32 } else { - conv_data = (conv_vec_to_value_s(data) as f32) / input_reg.3 as f32; - } + (conv_vec_to_value_s(data) as f32) / input_reg.3 as f32 + }; println!( "{} - {} : {} {}", input_reg.0, input_reg.1, conv_data, input_reg.2 @@ -174,9 +172,7 @@ fn main() { Err(error) => { eprintln!( "{} - {} : [couldn't read data '{}']", - input_reg.0, - input_reg.1, - error.to_string() + input_reg.0, input_reg.1, error ); } } @@ -208,10 +204,7 @@ fn main() { Err(error) => { eprintln!( "{} - {} - {} : [couldn't read data '{}']", - hold_reg.0, - hold_reg.1, - hold_reg.2, - error.to_string() + hold_reg.0, hold_reg.1, hold_reg.2, error ); } }