From 99021f39943fd5e1e4676f59107d80870c7113ff Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 29 Jan 2021 23:06:59 +0100 Subject: [PATCH] Add first weather text support; refactoring --- Cargo.lock | 697 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 +- src/hermes.rs | 144 ++++++++++ src/lib.rs | 219 +++++++--------- src/main.rs | 70 +---- src/weather.rs | 94 +++++++ 6 files changed, 1038 insertions(+), 192 deletions(-) create mode 100644 src/hermes.rs create mode 100644 src/weather.rs diff --git a/Cargo.lock b/Cargo.lock index 4fca009..cb57be8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,27 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -23,6 +44,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "bumpalo" version = "3.5.0" @@ -68,6 +95,37 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "encoding_rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "event-listener" version = "2.5.1" @@ -80,12 +138,107 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +[[package]] +name = "futures-sink" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" + +[[package]] +name = "futures-task" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" + +[[package]] +name = "futures-util" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + [[package]] name = "hermit-abi" version = "0.1.18" @@ -106,6 +259,86 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "hyper" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.4", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.9" @@ -115,6 +348,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + [[package]] name = "itoa" version = "0.4.7" @@ -160,12 +399,24 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "mio" version = "0.7.7" @@ -197,6 +448,24 @@ dependencies = [ "bytes", ] +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -222,6 +491,39 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +[[package]] +name = "openssl" +version = "0.10.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -242,23 +544,87 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "smallvec", "winapi", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +dependencies = [ + "pin-project-internal 1.0.4", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "pollster" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a0c427c464eaca5923b642b3b346792b9c98edd91026aa6a753435e42d2a07b" +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -277,16 +643,111 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd281b1030aa675fb90aa994d07187645bb3c8fc756ca766e7c3070b439de9de" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rhasspy-skill-server" version = "0.1.0" dependencies = [ + "dotenv", + "reqwest", "rumqttc", "serde", "serde_json", @@ -344,6 +805,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -360,6 +831,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.123" @@ -391,6 +885,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.3.0" @@ -400,6 +906,12 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + [[package]] name = "smallvec" version = "1.6.1" @@ -434,6 +946,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand", + "redox_syscall 0.2.4", + "remove_dir_all", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.23" @@ -454,6 +980,21 @@ dependencies = [ "syn", ] +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tokio" version = "1.1.0" @@ -485,6 +1026,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.22.0" @@ -496,6 +1047,93 @@ dependencies = [ "webpki", ] +[[package]] +name = "tokio-stream" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb971a26599ffd28066d387f109746df178eff14d5ea1e235015c5601967a4b" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.1" @@ -508,6 +1146,40 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.70" @@ -515,6 +1187,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" dependencies = [ "cfg-if 1.0.0", + "serde", + "serde_json", "wasm-bindgen-macro", ] @@ -533,6 +1207,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.70" @@ -603,3 +1289,12 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml index 5846d22..c185ddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,10 @@ license = "MIT" keywords = ["mqtt", "rhasspy", "hermes", "voice assistant"] [dependencies] +dotenv = "0.15.0" +reqwest = { version = "0.11", features = ["json"] } # Use the GIt version until the crate is fully stabilized on Tokio 1.0 -rumqttc = { git = "https://github.com/bytebeamio/rumqtt", version = "0.5.0" } +rumqttc = { git = "https://github.com/bytebeamio/rumqtt", version = "0.5" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tokio = "1.0" +tokio = { version = "1.0", features = ["full"] } diff --git a/src/hermes.rs b/src/hermes.rs new file mode 100644 index 0000000..610b664 --- /dev/null +++ b/src/hermes.rs @@ -0,0 +1,144 @@ +use serde::{Deserialize, Deserializer, Serialize}; +use std::collections::HashMap; + +// Hermes types + +#[derive(Debug, Deserialize)] +struct HermesIntent { + input: String, + #[serde(rename = "rawInput")] + input_raw: String, + intent: HermesIntentClassification, + #[serde(rename = "sessionId")] + session_id: String, + #[serde(rename = "siteId")] + site_id: String, + slots: Vec, +} + +#[derive(Debug, Deserialize)] +struct HermesIntentClassification { + #[serde(rename = "confidenceScore")] + confidence: f32, + #[serde(rename = "intentName")] + name: String, +} + +#[derive(Debug, Deserialize)] +struct HermesSlot { + confidence: f32, + entity: String, + #[serde(rename = "slotName")] + name: String, + value: HermesSlotValue, + #[serde(rename = "rawValue")] + value_raw: String, +} + +#[derive(Debug, Deserialize)] +struct HermesSlotValue { + value: String, +} + +#[derive(Debug, Serialize)] +struct HermesResponse { + #[serde(rename = "sessionId")] + session_id: String, + text: Option, +} + +// Native types + +#[derive(Debug)] +pub struct Intent { + pub name: String, + pub input: String, + pub input_raw: String, + pub confidence: f32, + pub session_id: String, + pub site_id: String, + pub slots: HashMap, +} + +impl From for Intent { + fn from(hermes_intent: HermesIntent) -> Self { + let slots = hermes_intent + .slots + .into_iter() + .map(|hermes_slot| (hermes_slot.name.clone(), Slot::from(hermes_slot))) + .collect(); + + Self { + name: hermes_intent.intent.name, + input: hermes_intent.input, + input_raw: hermes_intent.input_raw, + confidence: hermes_intent.intent.confidence, + session_id: hermes_intent.session_id, + site_id: hermes_intent.site_id, + slots, + } + } +} + +#[derive(Debug)] +pub struct Slot { + pub confidence: f32, + pub entity: String, + pub value: String, + pub value_raw: String, +} + +impl From for Slot { + fn from(slot_data: HermesSlot) -> Self { + Self { + confidence: slot_data.confidence, + entity: slot_data.entity, + value: slot_data.value.value, + value_raw: slot_data.value_raw, + } + } +} + +impl<'de> Deserialize<'de> for Intent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let intent_data = HermesIntent::deserialize(deserializer)?; + Ok(Intent::from(intent_data)) + } +} + +#[derive(Debug)] +pub struct Response { + pub session_id: String, + pub text: Option, +} + +impl Response { + pub fn new(session_id: String) -> Self { + let text = None; + Self { session_id, text } + } + + pub fn with_text(mut self, text: impl Into) -> Self { + self.text = Some(text.into()); + self + } +} + +impl From for Vec { + fn from(response: Response) -> Vec { + let hermes_response = HermesResponse::from(response); + serde_json::to_vec(&hermes_response).unwrap() + } +} + +impl From for HermesResponse { + fn from(response: Response) -> Self { + HermesResponse { + session_id: response.session_id, + text: response.text, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 664a0da..8512b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,144 +1,111 @@ #![warn(clippy::all)] -use serde::{Deserialize, Deserializer, Serialize}; -use std::collections::HashMap; +use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS}; +use std::error::Error; -// Hermes protocol types +use crate::hermes::{Intent, Response}; -#[derive(Debug, Deserialize)] -struct HermesIntent { - input: String, - #[serde(rename = "rawInput")] - input_raw: String, - intent: HermesIntentClassification, - #[serde(rename = "sessionId")] - session_id: String, - #[serde(rename = "siteId")] - site_id: String, - slots: Vec, +pub mod hermes; +pub mod weather; + +// Main context + +#[derive(Default, Debug)] +pub struct Context { + pub(crate) openweather_api_key: String, + pub(crate) mqtt_host: String, + pub(crate) mqtt_port: u16, } -#[derive(Debug, Deserialize)] -struct HermesIntentClassification { - #[serde(rename = "confidenceScore")] - confidence: f32, - #[serde(rename = "intentName")] - name: String, +impl Context { + pub fn new() -> Result> { + use std::env; + + Ok(Context { + openweather_api_key: env::var("OPENWEATHER_API_KEY")?, + mqtt_host: env::var("MQTT_HOST")?, + mqtt_port: env::var("MQTT_PORT")?.parse()?, + }) + } } -#[derive(Debug, Deserialize)] -struct HermesSlot { - confidence: f32, - entity: String, - #[serde(rename = "slotName")] - name: String, - value: HermesSlotValue, - #[serde(rename = "rawValue")] - value_raw: String, -} +// Handling -#[derive(Debug, Deserialize)] -struct HermesSlotValue { - value: String, -} +pub async fn handle(context: &mut Context, intent: Intent) -> Option { + println!( + ">>> Detected intent: {} (confidence: {:.2})", + intent.name, intent.confidence + ); -#[derive(Debug, Serialize)] -struct HermesResponse { - #[serde(rename = "sessionId")] - session_id: String, - text: Option, -} - -#[derive(Debug)] -pub struct Intent { - pub name: String, - pub input: String, - pub input_raw: String, - pub confidence: f32, - pub session_id: String, - pub site_id: String, - pub slots: HashMap, -} - -impl From for Intent { - fn from(hermes_intent: HermesIntent) -> Self { - let slots = hermes_intent - .slots - .into_iter() - .map(|hermes_slot| (hermes_slot.name.clone(), Slot::from(hermes_slot))) - .collect(); - - Self { - name: hermes_intent.intent.name, - input: hermes_intent.input, - input_raw: hermes_intent.input_raw, - confidence: hermes_intent.intent.confidence, - session_id: hermes_intent.session_id, - site_id: hermes_intent.site_id, - slots, + match intent.name.as_ref() { + "test" => { + let choice = intent.slots.get("choice").unwrap(); + Some( + Response::new(intent.session_id) + .with_text(format!("De test is geslaagd: {}", choice.value)), + ) + } + "WeatherForecastLocal" => Some(match weather::get_forecast(&context).await { + Ok(description) => Response::new(intent.session_id).with_text(description), + Err(e) => { + println!("!!! Encountered weather API error: {}", e); + Response::new(intent.session_id).with_text("Ik kon het weerbericht niet ophalen!") + } + }), + _ => { + println!("??? Ignoring unsupported intent: {:?}", intent.name); + None } } } -#[derive(Debug)] -pub struct Slot { - pub confidence: f32, - pub entity: String, - pub value: String, - pub value_raw: String, -} +pub async fn event_loop(mut context: Context) -> Result<(), Box> { + let mut mqttoptions = MqttOptions::new( + "rhasspy-skill-server", + &context.mqtt_host, + context.mqtt_port, + ); + mqttoptions.set_keep_alive(10); -impl From for Slot { - fn from(slot_data: HermesSlot) -> Self { - Self { - confidence: slot_data.confidence, - entity: slot_data.entity, - value: slot_data.value.value, - value_raw: slot_data.value_raw, - } - } -} - -impl<'de> Deserialize<'de> for Intent { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let intent_data = HermesIntent::deserialize(deserializer)?; - Ok(Intent::from(intent_data)) - } -} - -#[derive(Debug)] -pub struct Response { - pub session_id: String, - pub text: Option, -} - -impl Response { - pub fn new(session_id: String) -> Self { - let text = None; - Self { session_id, text } - } - - pub fn with_text(mut self, text: impl Into) -> Self { - self.text = Some(text.into()); - self - } -} - -impl From for Vec { - fn from(response: Response) -> Vec { - let hermes_response = HermesResponse::from(response); - serde_json::to_vec(&hermes_response).unwrap() - } -} - -impl From for HermesResponse { - fn from(response: Response) -> Self { - HermesResponse { - session_id: response.session_id, - text: response.text, + let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10); + client.subscribe("hermes/intent/#", QoS::AtMostOnce).await?; + + loop { + let event = eventloop.poll().await?; + if let Event::Incoming(Packet::Publish(published)) = event { + let result: Result = serde_json::from_slice(published.payload.as_ref()); + let response = match result { + Ok(intent) => handle(&mut context, intent).await, + Err(e) => { + println!( + "!!! Could not parse intent payload: {} at\n{:?}", + e, published.payload + ); + continue; + } + }; + + if let Some(response) = response { + let text = response.text.clone(); + let result = client + .publish( + "hermes/dialogueManager/endSession", + QoS::AtMostOnce, + false, + response, + ) + .await; + match result { + Ok(_) => { + if let Some(text) = text { + println!("<<< Reacted with a response: {}!", text); + } else { + println!("<<< Reacted with a silent response"); + } + } + Err(e) => println!("!!! Could not publish intent payload: {}", e), + } + } } } } diff --git a/src/main.rs b/src/main.rs index 3aaf689..42746ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,68 +1,12 @@ -use rhasspy_skill_server::{Intent, Response}; -use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS}; - -#[derive(Default, Debug)] -struct Context; - -async fn handle(_context: &mut Context, intent: Intent) -> Option { - println!( - ">>> Detected intent: {} (confidence: {})", - intent.name, intent.confidence - ); - - match intent.name.as_ref() { - "test" => { - let choice = intent.slots.get("choice").unwrap(); - Some( - Response::new(intent.session_id) - .with_text(format!("De test is geslaagd: {}", choice.value)), - ) - } - _ => { - println!("??? Ignoring unsupported intent: {:?}", intent.name); - None - } - } -} +use rhasspy_skill_server::{event_loop, Context}; +use std::error::Error; #[tokio::main] -async fn main() -> Result<(), Box> { - let mut mqttoptions = MqttOptions::new("rhasspy-skill-server", "void", 1883); - mqttoptions.set_keep_alive(5); +async fn main() -> Result<(), Box> { + dotenv::dotenv().ok(); - let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10); - client.subscribe("hermes/intent/#", QoS::AtMostOnce).await?; + let context = Context::new()?; + event_loop(context).await?; - let mut context = Context::default(); - loop { - let event = eventloop.poll().await?; - if let Event::Incoming(Packet::Publish(published)) = event { - let result: Result = serde_json::from_slice(published.payload.as_ref()); - let response = match result { - Ok(intent) => handle(&mut context, intent).await, - Err(e) => { - println!( - "!!! Could not parse intent payload: {} at\n{:?}", - e, published.payload - ); - continue; - } - }; - - if let Some(response) = response { - let result = client - .publish( - "hermes/dialogueManager/endSession", - QoS::AtMostOnce, - false, - response, - ) - .await; - match result { - Ok(_) => println!("<<< Reacted with a response!"), - Err(e) => println!("!!! Could not publish intent payload: {}", e), - } - } - } - } + Ok(()) } diff --git a/src/weather.rs b/src/weather.rs new file mode 100644 index 0000000..6b9b949 --- /dev/null +++ b/src/weather.rs @@ -0,0 +1,94 @@ +use reqwest::{Client, Response, Url}; +use serde::Deserialize; +use std::error::Error; + +use crate::Context; + +const LATITUDE: &'static str = "51.43"; +const LONGITUDE: &'static str = "5.47"; + +#[derive(Debug, Deserialize)] +struct Current { + weather: Vec, + main: Main, +} + +#[derive(Debug, Deserialize)] +struct Weather { + description: String, +} + +#[derive(Debug, Deserialize)] +struct Main { + temp: f32, + feels_like: f32, +} + +#[derive(Debug, Deserialize)] +struct OneCall { + daily: Vec, +} + +#[derive(Debug, Deserialize)] +struct Daily { + temp: Temp, + weather: Vec, + pop: f32, +} + +#[derive(Debug, Deserialize)] +struct Temp { + day: f32, +} + +async fn request(context: &Context, url: &str) -> reqwest::Result { + let client = Client::new(); + let mut url = Url::parse(url).unwrap(); + url.query_pairs_mut() + .append_pair("appid", &context.openweather_api_key) + .append_pair("lat", LATITUDE) + .append_pair("lon", LONGITUDE) + .append_pair("units", "metric") + .append_pair("lang", "nl"); + client.get(url).send().await +} + +pub async fn get_weather(context: &Context) -> Result> { + let url = "https://api.openweathermap.org/data/2.5/weather"; + let resp = request(&context, &url).await?; + let cur_weather: Current = resp.json().await?; + + let description = cur_weather.weather.first().unwrap().description.clone(); + let temperature = cur_weather.main.temp.round() as u32; + let temperature_feel = cur_weather.main.feels_like.round() as u32; + let weather = format!( + "Het weer in Eindhoven is op het moment {description} en het is {temperature} graden \ + buiten en dat voelt als {temperature_feel} graden", + description = description, + temperature = temperature, + temperature_feel = temperature_feel, + ); + + Ok(weather) +} + +pub async fn get_forecast(context: &Context) -> Result> { + let url = "https://api.openweathermap.org/data/2.5/onecall"; + let resp = request(&context, &url).await?; + let weather_forecast: OneCall = resp.json().await?; + + let today = weather_forecast.daily.first().unwrap(); + let description = today.weather.first().unwrap().description.clone(); + let temperature_day = today.temp.day.round() as u32; + let rain_pct = (today.pop * 100.0).round() as u8; + + let forecast = format!( + "De weersverwachting van vandaag voor Eindhoven is {description} met een \ + middagtemperatuur van {temperature_day} graden en {rain_pct} procent kans op regen", + description = description, + temperature_day = temperature_day, + rain_pct = rain_pct, + ); + + Ok(forecast) +}