diff --git a/Cargo.lock b/Cargo.lock index daa4110..82078b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -77,6 +77,7 @@ dependencies = [ "anyhow", "async-trait", "automation_cast", + "automation_macro", "axum", "bytes", "console-subscriber", @@ -86,6 +87,7 @@ dependencies = [ "futures", "google-home", "indexmap 2.0.0", + "mlua", "paste", "pollster", "regex", @@ -108,6 +110,14 @@ dependencies = [ name = "automation_cast" version = "0.1.0" +[[package]] +name = "automation_macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.60", +] + [[package]] name = "axum" version = "0.6.20" @@ -116,7 +126,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -190,6 +200,22 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -348,7 +374,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -359,7 +385,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -401,7 +427,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -410,6 +436,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eui48" version = "1.1.0" @@ -514,7 +559,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -636,6 +681,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.9" @@ -802,6 +856,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -825,9 +888,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -845,6 +914,25 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "lua-src" +version = "546.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da0daa7eee611a4c30c8f5ee31af55266e26e573971ba9336d2993e2da129b2" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.5.7+d06beb0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d251fdacdabbf87704cf48ac1f8b1eb23d6e10855c3ee08e5beb25b4be2e9e4" +dependencies = [ + "cc", + "which", +] + [[package]] name = "matchers" version = "0.1.0" @@ -895,7 +983,53 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "mlua" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9bed6bce296397a9d6a86f995dd10a547a4e6949825d45225906bdcbfe7367" +dependencies = [ + "bstr", + "erased-serde", + "futures-util", + "mlua-sys", + "mlua_derive", + "num-traits", + "once_cell", + "rustc-hash", + "serde", + "serde-value", +] + +[[package]] +name = "mlua-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16a9ba1dd2c6ac971b204262d434c24d65067038598f0638b64e5dca28d52b8" +dependencies = [ + "cc", + "cfg-if", + "lua-src", + "luajit-src", + "pkg-config", +] + +[[package]] +name = "mlua_derive" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaade5f94e5829db58791664ba98f35fea6a3ffebc783becb51dc97c7a21abee" +dependencies = [ + "itertools 0.12.1", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.60", ] [[package]] @@ -978,6 +1112,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -1013,7 +1156,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1028,6 +1171,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "pollster" version = "0.2.5" @@ -1041,10 +1190,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -1066,7 +1239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -1083,9 +1256,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1242,6 +1415,25 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.20.8" @@ -1324,7 +1516,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1349,7 +1541,7 @@ version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1368,22 +1560,32 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.190" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.190" +name = "serde-value" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1415,7 +1617,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1456,7 +1658,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1513,7 +1715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1550,9 +1752,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -1582,7 +1784,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1653,7 +1855,7 @@ dependencies = [ "socket2 0.5.3", "tokio-macros", "tracing", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1689,7 +1891,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1819,7 +2021,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", ] [[package]] @@ -1926,6 +2128,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wakey" version = "0.3.0" @@ -1972,7 +2180,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -2006,7 +2214,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2046,6 +2254,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "which" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2074,7 +2294,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -2083,7 +2303,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -2092,13 +2321,29 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" 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", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2107,42 +2352,90 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winreg" version = "0.10.1" @@ -2151,3 +2444,9 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" diff --git a/Cargo.toml b/Cargo.toml index 50790d1..9bd1be7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2021" [workspace] -members = ["google-home", "automation_cast"] +members = ["google-home", "automation_macro", "automation_cast"] + [dependencies] +automation_macro = { path = "./automation_macro" } automation_cast = { path = "./automation_cast/" } rumqttc = "0.18" serde = { version = "1.0.149", features = ["derive"] } @@ -41,6 +43,13 @@ enum_dispatch = "0.3.12" indexmap = { version = "2.0.0", features = ["serde"] } serde_yaml = "0.9.27" tokio-cron-scheduler = "0.9.4" +mlua = { version = "0.9.7", features = [ + "lua54", + "vendored", + "macros", + "serialize", + "async", +] } [patch.crates-io] wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" } diff --git a/automation_macro/Cargo.toml b/automation_macro/Cargo.toml new file mode 100644 index 0000000..4ae24b5 --- /dev/null +++ b/automation_macro/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "automation_macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.36" +syn = "2.0.60" diff --git a/automation_macro/src/lib.rs b/automation_macro/src/lib.rs new file mode 100644 index 0000000..a42e6cb --- /dev/null +++ b/automation_macro/src/lib.rs @@ -0,0 +1,54 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +#[proc_macro_derive(LuaDevice, attributes(config))] +pub fn lua_device_derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + impl_lua_device_macro(&ast) +} + +fn impl_lua_device_macro(ast: &syn::DeriveInput) -> TokenStream { + let name = &ast.ident; + let name_string = name.to_string(); + // TODO: Handle errors properly + // This includes making sure one, and only one config is specified + let config = if let syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }), + .. + }) = ast.data + { + named + .iter() + .find(|&field| { + field + .attrs + .iter() + .any(|attr| attr.path().is_ident("config")) + }) + .map(|field| field.ty.clone()) + .unwrap() + } else { + unimplemented!() + }; + + let gen = quote! { + impl #name { + pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> { + lua.globals().set(#name_string, lua.create_proxy::<#name>()?) + } + } + impl mlua::UserData for #name { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_function("new", |lua, config: mlua::Value| { + let config: #config = mlua::LuaSerdeExt::from_value(lua, config)?; + let config: Box = Box::new(config); + Ok(config) + }); + } + } + }; + + gen.into() +} diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..1890ba4 --- /dev/null +++ b/config.lua @@ -0,0 +1,160 @@ +print("Hello from lua") + +local debug, value = pcall(automation.util.get_env, "DEBUG") +if debug and value ~= "true" then + debug = false +end + +local function mqtt_z2m(topic) + return "zigbee2mqtt/" .. topic +end + +local function mqtt_automation(topic) + return "automation/" .. topic +end + +automation.device_manager:create( + "debug_bridge", + DebugBridge.new({ + topic = mqtt_automation("debug"), + }) +) + +local hue_ip = "10.0.0.146" +local hue_token = automation.util.get_env("HUE_TOKEN") + +automation.device_manager:create( + "hue_bridge", + HueBridge.new({ + ip = hue_ip, + login = hue_token, + flags = { + presence = 41, + darkness = 43, + }, + }) +) + +automation.device_manager:create( + "living_light_sensor", + LightSensor.new({ + topic = mqtt_z2m("living/light"), + min = 22000, + max = 23500, + }) +) + +automation.device_manager:create( + "living_zeus", + WakeOnLAN.new({ + name = "Zeus", + room = "Living Room", + topic = mqtt_automation("appliance/living_room/zeus"), + mac_address = "30:9c:23:60:9c:13", + broadcast_ip = "10.0.0.255", + }) +) + +local living_mixer = automation.device_manager:create("living_mixer", KasaOutlet.new({ ip = "10.0.0.49" })) +local living_speakers = automation.device_manager:create("living_speakers", KasaOutlet.new({ ip = "10.0.0.182" })) + +automation.device_manager:create( + "living_audio", + AudioSetup.new({ + topic = mqtt_z2m("living/remote"), + mixer = living_mixer, + speakers = living_speakers, + }) +) + +automation.device_manager:create( + "kitchen_kettle", + IkeaOutlet.new({ + outlet_type = "Kettle", + name = "Kettle", + room = "Kitchen", + topic = mqtt_z2m("kitchen/kettle"), + timeout = debug and 5 or 300, + remotes = { + { topic = mqtt_z2m("bedroom/remote") }, + { topic = mqtt_z2m("kitchen/remote") }, + }, + }) +) + +automation.device_manager:create( + "batchroom_light", + IkeaOutlet.new({ + outlet_type = "Light", + name = "Light", + room = "Bathroom", + topic = mqtt_z2m("batchroom/light"), + timeout = debug and 60 or 45 * 60, + }) +) + +automation.device_manager:create( + "bathroom_washer", + Washer.new({ + topic = mqtt_z2m("batchroom/washer"), + threshold = 1, + }) +) + +automation.device_manager:create( + "workbench_charger", + IkeaOutlet.new({ + outlet_type = "Charger", + name = "Charger", + room = "Workbench", + topic = mqtt_z2m("workbench/charger"), + timeout = debug and 5 or 20 * 3600, + }) +) + +automation.device_manager:create( + "workbench_outlet", + IkeaOutlet.new({ + name = "Outlet", + room = "Workbench", + topic = mqtt_z2m("workbench/outlet"), + }) +) + +local hallway_lights = automation.device_manager:create( + "hallway_lights", + HueGroup.new({ + ip = hue_ip, + login = hue_token, + group_id = 81, + scene_id = "3qWKxGVadXFFG4o", + timer_id = 1, + remotes = { + { topic = mqtt_z2m("hallway/remote") }, + }, + }) +) + +automation.device_manager:create( + "hallway_frontdoor", + ContactSensor.new({ + topic = mqtt_z2m("hallway/frontdoor"), + presence = { + topic = mqtt_automation("presence/contact/frontdoor"), + timeout = debug and 10 or 15 * 60, + }, + trigger = { + devices = { hallway_lights }, + timeout = debug and 10 or 2 * 60, + }, + }) +) + +automation.device_manager:create( + "bedroom_air_filter", + AirFilter.new({ + name = "Air Filter", + room = "Bedroom", + topic = "pico/filter/bedroom", + }) +) diff --git a/config/ares.dev.yml b/config/ares.dev.yml deleted file mode 100644 index 45d321d..0000000 --- a/config/ares.dev.yml +++ /dev/null @@ -1,66 +0,0 @@ -openid: - base_url: "https://login.huizinga.dev/api/oidc" - -mqtt: - host: "olympus.vpn.huizinga.dev" - port: 8883 - client_name: "automation-ares" - username: "mqtt" - password: "${MQTT_PASSWORD}" - tls: true - -ntfy: - topic: "${NTFY_TOPIC}" - -presence: - topic: "automation_dev/presence/+/#" - -devices: - debug_bridge: - !DebugBridge - topic: "automation_dev/debug" - - living_light_sensor: - !LightSensor - topic: "zigbee2mqtt_dev/living/light" - min: 23000 - max: 25000 - - kitchen_kettle: - !IkeaOutlet - outlet_type: "Kettle" - name: "Kettle" - room: "Kitchen" - topic: "zigbee2mqtt/kitchen/kettle" - timeout: 5 - remotes: - - topic: "zigbee2mqtt/bedroom/remote" - - topic: "zigbee2mqtt/kitchen/remote" - - workbench_charger: - !IkeaOutlet - outlet_type: "Charger" - name: "Charger" - room: "Workbench" - topic: "zigbee2mqtt/workbench/charger" - timeout: 5 - - workbench_outlet: - !IkeaOutlet - name: "Outlet" - room: "Workbench" - topic: "zigbee2mqtt/workbench/outlet" - - living_zeus: - !WakeOnLAN - name: "Zeus" - room: "Living Room" - topic: "automation/appliance/living_room/zeus" - mac_address: "30:9c:23:60:9c:13" - - hallway_frontdoor: - !ContactSensor - topic: "zigbee2mqtt/hallway/frontdoor" - presence: - topic: "automation_dev/presence/contact/frontdoor" - timeout: 10 diff --git a/config/config.yml b/config/config.yml index 4a62edb..12e6265 100644 --- a/config/config.yml +++ b/config/config.yml @@ -14,121 +14,12 @@ ntfy: presence: topic: "automation/presence/+/#" -devices: - debug_bridge: - !DebugBridge - topic: "automation/debug" - - hue_bridge: - !HueBridge - ip: &hue_ip "10.0.0.146" - login: &hue_token "${HUE_TOKEN}" - flags: { presence: 41, darkness: 43 } - - - living_light_sensor: - !LightSensor - topic: "zigbee2mqtt/living/light" - min: 22000 - max: 23500 - - living_zeus: - !WakeOnLAN - name: "Zeus" - room: "Living Room" - topic: "automation/appliance/living_room/zeus" - mac_address: "30:9c:23:60:9c:13" - broadcast_ip: "10.0.0.255" - - &mixer living_mixer: - !KasaOutlet - ip: "10.0.0.49" - - &speakers living_speakers: - !KasaOutlet - ip: "10.0.0.182" - - living_audio: - !AudioSetup - topic: "zigbee2mqtt/living/remote" - mixer: *mixer - speakers: *speakers - - - kitchen_kettle: - !IkeaOutlet - outlet_type: "Kettle" - name: "Kettle" - room: "Kitchen" - topic: "zigbee2mqtt/kitchen/kettle" - timeout: 300 - remotes: - - topic: "zigbee2mqtt/bedroom/remote" - - topic: "zigbee2mqtt/kitchen/remote" - - - bathroom_light: - !IkeaOutlet - type: "IkeaOutlet" - outlet_type: "Light" - name: "Light" - room: "Bathroom" - topic: "zigbee2mqtt/bathroom/light" - timeout: 2700 - - bathroom_washer: - !Washer - topic: "zigbee2mqtt/bathroom/washer" - threshold: 1 - - workbench_charger: - !IkeaOutlet - outlet_type: "Charger" - name: "Charger" - room: "Workbench" - topic: "zigbee2mqtt/workbench/charger" - timeout: 72000 - - workbench_outlet: - !IkeaOutlet - name: "Outlet" - room: "Workbench" - topic: "zigbee2mqtt/workbench/outlet" - - - hallway_lights: - !HueGroup - ip: *hue_ip - login: *hue_token - group_id: 81 - scene_id: "3qWKxGVadXFFG4o" - timer_id: 1 - remotes: - - topic: "zigbee2mqtt/hallway/remote" - - hallway_frontdoor: - !ContactSensor - topic: "zigbee2mqtt/hallway/frontdoor" - presence: - topic: "automation/presence/contact/frontdoor" - timeout: 900 - trigger: - devices: ["hallway_lights"] - timeout: 60 - - - &air_filter bedroom_air_filter: - !AirFilter - name: "Air Filter" - room: "Bedroom" - topic: "pico/filter/bedroom" - # Run the air filter everyday for 19:00 to 20:00 schedule: 0 0 19 * * *: on: - - *air_filter + - "bedroom_air_filter" 0 0 20 * * *: off: - - *air_filter + - "bedroom_air_filter" diff --git a/config/zeus.dev.yml b/config/zeus.dev.yml index 6815dc9..3aaac6b 100644 --- a/config/zeus.dev.yml +++ b/config/zeus.dev.yml @@ -15,119 +15,11 @@ ntfy: presence: topic: "automation_dev/presence/+/#" -devices: - debug_bridge: - !DebugBridge - topic: "automation_dev/debug" - - hue_bridge: - !HueBridge - ip: &hue_ip "10.0.0.146" - login: &hue_token "${HUE_TOKEN}" - flags: { presence: 41, darkness: 43 } - - - living_light_sensor: - !LightSensor - topic: "zigbee2mqtt_dev/living/light" - min: 23000 - max: 25000 - - living_zeus: - !WakeOnLAN - name: "Zeus" - room: "Living Room" - topic: "automation/appliance/living_room/zeus" - mac_address: "30:9c:23:60:9c:13" - - &mixer living_mixer: - !KasaOutlet - ip: "10.0.0.49" - - &speakers living_speakers: - !KasaOutlet - ip: "10.0.0.182" - - living_audio: - !AudioSetup - topic: "zigbee2mqtt/living/remote" - mixer: *mixer - speakers: *speakers - - - kitchen_kettle: - !IkeaOutlet - outlet_type: "Kettle" - name: "Kettle" - room: "Kitchen" - topic: "zigbee2mqtt/kitchen/kettle" - timeout: 5 - remotes: - - topic: "zigbee2mqtt/bedroom/remote" - - topic: "zigbee2mqtt/kitchen/remote" - - - bathroom_light: - !IkeaOutlet - type: "IkeaOutlet" - outlet_type: "Light" - name: "Light" - room: "Bathroom" - topic: "zigbee2mqtt/bathroom/light" - timeout: 60 - - bathroom_washer: - !Washer - topic: "zigbee2mqtt/bathroom/washer" - threshold: 1 - - workbench_charger: - !IkeaOutlet - outlet_type: "Charger" - name: "Charger" - room: "Workbench" - topic: "zigbee2mqtt/workbench/charger" - timeout: 5 - - &outlet workbench_outlet: - !IkeaOutlet - name: "Outlet" - room: "Workbench" - topic: "zigbee2mqtt/workbench/outlet" - - - hallway_lights: - !HueGroup - ip: *hue_ip - login: *hue_token - group_id: 81 - scene_id: "3qWKxGVadXFFG4o" - timer_id: 1 - remotes: - - topic: "zigbee2mqtt/hallway/remote" - - hallway_frontdoor: - !ContactSensor - topic: "zigbee2mqtt/hallway/frontdoor" - presence: - topic: "automation_dev/presence/contact/frontdoor" - timeout: 10 - trigger: - devices: ["hallway_lights"] - timeout: 10 - - - bedroom_air_filter: - !AirFilter - name: "Air Filter" - room: "Bedroom" - topic: "pico/filter/bedroom" - -# schedule: -# 0/30 * * * * *: -# on: -# - *outlet -# -# 15/30 * * * * *: -# off: -# - *outlet +schedule: + # 0/30 * * * * *: + # on: + # - *outlet + # + # 15/30 * * * * *: + # off: + # - *outlet diff --git a/src/config.rs b/src/config.rs index 4a1ae7e..48b19ab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,14 +2,12 @@ use std::fs; use std::net::{Ipv4Addr, SocketAddr}; use std::time::Duration; -use indexmap::IndexMap; use regex::{Captures, Regex}; use rumqttc::{MqttOptions, Transport}; use serde::{Deserialize, Deserializer}; use tracing::debug; use crate::auth::OpenIDConfig; -use crate::device_manager::DeviceConfigs; use crate::devices::PresenceConfig; use crate::error::{ConfigParseError, MissingEnv}; use crate::schedule::Schedule; @@ -23,7 +21,6 @@ pub struct Config { pub fullfillment: FullfillmentConfig, pub ntfy: Option, pub presence: PresenceConfig, - pub devices: IndexMap, pub schedule: Schedule, } diff --git a/src/device_manager.rs b/src/device_manager.rs index 0a1833a..f27de76 100644 --- a/src/device_manager.rs +++ b/src/device_manager.rs @@ -6,16 +6,11 @@ use enum_dispatch::enum_dispatch; use futures::future::join_all; use google_home::traits::OnOff; use rumqttc::{matches, AsyncClient, QoS}; -use serde::Deserialize; use tokio::sync::{RwLock, RwLockReadGuard}; use tokio_cron_scheduler::{Job, JobScheduler}; use tracing::{debug, error, instrument, trace}; -use crate::devices::{ - AirFilterConfig, AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device, - HueBridgeConfig, HueGroupConfig, IkeaOutletConfig, KasaOutletConfig, LightSensorConfig, - WakeOnLANConfig, WasherConfig, -}; +use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence}; use crate::schedule::{Action, Schedule}; @@ -30,27 +25,12 @@ pub struct ConfigExternal<'a> { #[enum_dispatch] pub trait DeviceConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError>; } - -#[derive(Debug, Deserialize)] -#[enum_dispatch(DeviceConfig)] -pub enum DeviceConfigs { - AirFilter(AirFilterConfig), - AudioSetup(AudioSetupConfig), - ContactSensor(ContactSensorConfig), - DebugBridge(DebugBridgeConfig), - IkeaOutlet(IkeaOutletConfig), - KasaOutlet(KasaOutletConfig), - WakeOnLAN(WakeOnLANConfig), - Washer(WasherConfig), - HueBridge(HueBridgeConfig), - HueGroup(HueGroupConfig), - LightSensor(LightSensorConfig), -} +impl mlua::UserData for Box {} pub type WrappedDevice = Arc>>; pub type DeviceMap = HashMap; @@ -163,24 +143,6 @@ impl DeviceManager { self.devices.write().await.insert(id, device); } - pub async fn create( - &self, - identifier: &str, - device_config: DeviceConfigs, - ) -> Result<(), DeviceConfigError> { - let ext = ConfigExternal { - client: &self.client, - device_manager: self, - event_channel: &self.event_channel, - }; - - let device = device_config.create(identifier, &ext).await?; - - self.add(device).await; - - Ok(()) - } - pub fn event_channel(&self) -> EventChannel { self.event_channel.clone() } @@ -264,3 +226,32 @@ impl DeviceManager { } } } + +impl mlua::UserData for DeviceManager { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_async_method( + "create", + |_lua, this, (identifier, config): (String, mlua::Value)| async move { + // TODO: Handle the error here properly + let config: Box = config.as_userdata().unwrap().take()?; + + let ext = ConfigExternal { + client: &this.client, + device_manager: this, + event_channel: &this.event_channel, + }; + + let device = config + .create(&identifier, &ext) + .await + .map_err(mlua::ExternalError::into_lua_err)?; + + let id = device.get_id().to_owned(); + + this.add(device).await; + + Ok(id) + }, + ) + } +} diff --git a/src/devices/air_filter.rs b/src/devices/air_filter.rs index 3a016fe..efc7675 100644 --- a/src/devices/air_filter.rs +++ b/src/devices/air_filter.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use automation_macro::LuaDevice; use google_home::device::Name; use google_home::errors::ErrorCode; use google_home::traits::{AvailableSpeeds, FanSpeed, HumiditySetting, OnOff, Speed, SpeedValues}; @@ -15,7 +16,7 @@ use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct AirFilterConfig { #[serde(flatten)] info: InfoConfig, @@ -26,14 +27,13 @@ pub struct AirFilterConfig { #[async_trait] impl DeviceConfig for AirFilterConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = AirFilter { identifier: identifier.into(), - info: self.info, - mqtt: self.mqtt, + config: self.clone(), client: ext.client.clone(), last_known_state: AirFilterState { state: AirFilterFanState::Off, @@ -45,11 +45,11 @@ impl DeviceConfig for AirFilterConfig { } } -#[derive(Debug)] +#[derive(Debug, LuaDevice)] pub struct AirFilter { identifier: String, - info: InfoConfig, - mqtt: MqttDeviceConfig, + #[config] + config: AirFilterConfig, client: AsyncClient, last_known_state: AirFilterState, @@ -59,7 +59,7 @@ impl AirFilter { async fn set_speed(&self, state: AirFilterFanState) { let message = SetAirFilterFanState::new(state); - let topic = format!("{}/set", self.mqtt.topic); + let topic = format!("{}/set", self.config.mqtt.topic); // TODO: Handle potential errors here self.client .publish( @@ -83,7 +83,7 @@ impl Device for AirFilter { #[async_trait] impl OnMqtt for AirFilter { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: Publish) { @@ -111,7 +111,7 @@ impl GoogleHomeDevice for AirFilter { } fn get_device_name(&self) -> Name { - Name::new(&self.info.name) + Name::new(&self.config.info.name) } fn get_id(&self) -> &str { @@ -123,7 +123,7 @@ impl GoogleHomeDevice for AirFilter { } fn get_room_hint(&self) -> Option<&str> { - self.info.room.as_deref() + self.config.info.room.as_deref() } fn will_report_state(&self) -> bool { diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index cc99adc..8bfc01a 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use automation_macro::LuaDevice; use google_home::traits::OnOff; use serde::Deserialize; use tracing::{debug, error, trace, warn}; @@ -21,7 +22,7 @@ pub struct AudioSetupConfig { #[async_trait] impl DeviceConfig for AudioSetupConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { @@ -41,7 +42,10 @@ impl DeviceConfig for AudioSetupConfig { { let mixer = mixer.read().await; if (mixer.as_ref().cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into())); + return Err(DeviceConfigError::MissingTrait( + self.mixer.clone(), + "OnOff".into(), + )); } } @@ -57,13 +61,16 @@ impl DeviceConfig for AudioSetupConfig { { let speakers = speakers.read().await; if (speakers.as_ref().cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into())); + return Err(DeviceConfigError::MissingTrait( + self.mixer.clone(), + "OnOff".into(), + )); } } let device = AudioSetup { identifier: identifier.into(), - mqtt: self.mqtt, + config: self.clone(), mixer, speakers, }; @@ -73,10 +80,11 @@ impl DeviceConfig for AudioSetupConfig { } // TODO: We need a better way to store the children devices -#[derive(Debug)] -struct AudioSetup { +#[derive(Debug, LuaDevice)] +pub struct AudioSetup { identifier: String, - mqtt: MqttDeviceConfig, + #[config] + config: AudioSetupConfig, mixer: WrappedDevice, speakers: WrappedDevice, } @@ -90,7 +98,7 @@ impl Device for AudioSetup { #[async_trait] impl OnMqtt for AudioSetup { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: rumqttc::Publish) { diff --git a/src/devices/contact_sensor.rs b/src/devices/contact_sensor.rs index cc10f19..c7b03fb 100644 --- a/src/devices/contact_sensor.rs +++ b/src/devices/contact_sensor.rs @@ -1,6 +1,7 @@ use std::time::Duration; use async_trait::async_trait; +use automation_macro::LuaDevice; use google_home::traits::OnOff; use rumqttc::AsyncClient; use serde::Deserialize; @@ -47,7 +48,7 @@ pub struct ContactSensorConfig { #[async_trait] impl DeviceConfig for ContactSensorConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { @@ -92,8 +93,7 @@ impl DeviceConfig for ContactSensorConfig { let device = ContactSensor { identifier: identifier.into(), - mqtt: self.mqtt, - presence: self.presence, + config: self.clone(), client: ext.client.clone(), overall_presence: DEFAULT_PRESENCE, is_closed: true, @@ -111,11 +111,11 @@ struct Trigger { timeout: Duration, // Timeout in seconds } -#[derive(Debug)] -struct ContactSensor { +#[derive(Debug, LuaDevice)] +pub struct ContactSensor { identifier: String, - mqtt: MqttDeviceConfig, - presence: Option, + #[config] + config: ContactSensorConfig, client: AsyncClient, overall_presence: bool, @@ -141,7 +141,7 @@ impl OnPresence for ContactSensor { #[async_trait] impl OnMqtt for ContactSensor { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: rumqttc::Publish) { @@ -191,7 +191,7 @@ impl OnMqtt for ContactSensor { // Check if this contact sensor works as a presence device // If not we are done here - let presence = match &self.presence { + let presence = match &self.config.presence { Some(presence) => presence, None => return, }; diff --git a/src/devices/debug_bridge.rs b/src/devices/debug_bridge.rs index b496db4..f5383a8 100644 --- a/src/devices/debug_bridge.rs +++ b/src/devices/debug_bridge.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use automation_macro::LuaDevice; use rumqttc::AsyncClient; use serde::Deserialize; use tracing::warn; @@ -10,7 +11,7 @@ use crate::error::DeviceConfigError; use crate::event::{OnDarkness, OnPresence}; use crate::messages::{DarknessMessage, PresenceMessage}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct DebugBridgeConfig { #[serde(flatten)] pub mqtt: MqttDeviceConfig, @@ -19,13 +20,13 @@ pub struct DebugBridgeConfig { #[async_trait] impl DeviceConfig for DebugBridgeConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = DebugBridge { identifier: identifier.into(), - mqtt: self.mqtt, + config: self.clone(), client: ext.client.clone(), }; @@ -33,10 +34,11 @@ impl DeviceConfig for DebugBridgeConfig { } } -#[derive(Debug)] +#[derive(Debug, LuaDevice)] pub struct DebugBridge { identifier: String, - mqtt: MqttDeviceConfig, + #[config] + config: DebugBridgeConfig, client: AsyncClient, } @@ -50,7 +52,7 @@ impl Device for DebugBridge { impl OnPresence for DebugBridge { async fn on_presence(&mut self, presence: bool) { let message = PresenceMessage::new(presence); - let topic = format!("{}/presence", self.mqtt.topic); + let topic = format!("{}/presence", self.config.mqtt.topic); self.client .publish( topic, @@ -62,7 +64,7 @@ impl OnPresence for DebugBridge { .map_err(|err| { warn!( "Failed to update presence on {}/presence: {err}", - self.mqtt.topic + self.config.mqtt.topic ) }) .ok(); @@ -73,7 +75,7 @@ impl OnPresence for DebugBridge { impl OnDarkness for DebugBridge { async fn on_darkness(&mut self, dark: bool) { let message = DarknessMessage::new(dark); - let topic = format!("{}/darkness", self.mqtt.topic); + let topic = format!("{}/darkness", self.config.mqtt.topic); self.client .publish( topic, @@ -85,7 +87,7 @@ impl OnDarkness for DebugBridge { .map_err(|err| { warn!( "Failed to update presence on {}/presence: {err}", - self.mqtt.topic + self.config.mqtt.topic ) }) .ok(); diff --git a/src/devices/hue_bridge.rs b/src/devices/hue_bridge.rs index 05e7e3e..90cf78e 100644 --- a/src/devices/hue_bridge.rs +++ b/src/devices/hue_bridge.rs @@ -1,6 +1,7 @@ -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::Ipv4Addr; use async_trait::async_trait; +use automation_macro::LuaDevice; use serde::{Deserialize, Serialize}; use tracing::{error, trace, warn}; @@ -21,7 +22,7 @@ pub struct FlagIDs { pub darkness: isize, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct HueBridgeConfig { pub ip: Ipv4Addr, pub login: String, @@ -31,27 +32,24 @@ pub struct HueBridgeConfig { #[async_trait] impl DeviceConfig for HueBridgeConfig { async fn create( - self, + &self, identifier: &str, _ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = HueBridge { identifier: identifier.into(), - addr: (self.ip, 80).into(), - login: self.login, - flag_ids: self.flags, + config: self.clone(), }; Ok(Box::new(device)) } } -#[derive(Debug)] -struct HueBridge { +#[derive(Debug, LuaDevice)] +pub struct HueBridge { identifier: String, - addr: SocketAddr, - login: String, - flag_ids: FlagIDs, + #[config] + config: HueBridgeConfig, } #[derive(Debug, Serialize)] @@ -62,13 +60,13 @@ struct FlagMessage { impl HueBridge { pub async fn set_flag(&self, flag: Flag, value: bool) { let flag_id = match flag { - Flag::Presence => self.flag_ids.presence, - Flag::Darkness => self.flag_ids.darkness, + Flag::Presence => self.config.flags.presence, + Flag::Darkness => self.config.flags.darkness, }; let url = format!( - "http://{}/api/{}/sensors/{flag_id}/state", - self.addr, self.login + "http://{}:80/api/{}/sensors/{flag_id}/state", + self.config.ip, self.config.login ); trace!(?flag, flag_id, value, "Sending request to change flag"); diff --git a/src/devices/hue_light.rs b/src/devices/hue_light.rs index 04ab9bc..4431018 100644 --- a/src/devices/hue_light.rs +++ b/src/devices/hue_light.rs @@ -1,8 +1,9 @@ -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::Ipv4Addr; use std::time::Duration; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; +use automation_macro::LuaDevice; use google_home::errors::ErrorCode; use google_home::traits::OnOff; use rumqttc::Publish; @@ -31,51 +32,42 @@ pub struct HueGroupConfig { #[async_trait] impl DeviceConfig for HueGroupConfig { async fn create( - self, + &self, identifier: &str, _ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = HueGroup { identifier: identifier.into(), - addr: (self.ip, 80).into(), - login: self.login, - group_id: self.group_id, - scene_id: self.scene_id, - timer_id: self.timer_id, - remotes: self.remotes, + config: self.clone(), }; Ok(Box::new(device)) } } -#[derive(Debug)] -struct HueGroup { +#[derive(Debug, LuaDevice)] +pub struct HueGroup { identifier: String, - addr: SocketAddr, - login: String, - group_id: isize, - timer_id: isize, - scene_id: String, - remotes: Vec, + #[config] + config: HueGroupConfig, } // Couple of helper function to get the correct urls impl HueGroup { fn url_base(&self) -> String { - format!("http://{}/api/{}", self.addr, self.login) + format!("http://{}:80/api/{}", self.config.ip, self.config.login) } fn url_set_schedule(&self) -> String { - format!("{}/schedules/{}", self.url_base(), self.timer_id) + format!("{}/schedules/{}", self.url_base(), self.config.timer_id) } fn url_set_action(&self) -> String { - format!("{}/groups/{}/action", self.url_base(), self.group_id) + format!("{}/groups/{}/action", self.url_base(), self.config.group_id) } fn url_get_state(&self) -> String { - format!("{}/groups/{}", self.url_base(), self.group_id) + format!("{}/groups/{}", self.url_base(), self.config.group_id) } } @@ -88,7 +80,8 @@ impl Device for HueGroup { #[async_trait] impl OnMqtt for HueGroup { fn topics(&self) -> Vec<&str> { - self.remotes + self.config + .remotes .iter() .map(|mqtt| mqtt.topic.as_str()) .collect() @@ -122,7 +115,7 @@ impl OnOff for HueGroup { self.stop_timeout().await.unwrap(); let message = if on { - message::Action::scene(self.scene_id.clone()) + message::Action::scene(self.config.scene_id.clone()) } else { message::Action::on(false) }; diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index dd9f5ee..cba7dfb 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -2,6 +2,7 @@ use std::time::Duration; use anyhow::Result; use async_trait::async_trait; +use automation_macro::LuaDevice; use google_home::errors::ErrorCode; use google_home::traits::{self, OnOff}; use google_home::types::Type; @@ -50,7 +51,7 @@ fn default_outlet_type() -> OutletType { #[async_trait] impl DeviceConfig for IkeaOutletConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { @@ -63,11 +64,7 @@ impl DeviceConfig for IkeaOutletConfig { let device = IkeaOutlet { identifier: identifier.into(), - info: self.info, - mqtt: self.mqtt, - outlet_type: self.outlet_type, - timeout: self.timeout, - remotes: self.remotes, + config: self.clone(), client: ext.client.clone(), last_known_state: false, handle: None, @@ -77,14 +74,11 @@ impl DeviceConfig for IkeaOutletConfig { } } -#[derive(Debug)] -struct IkeaOutlet { +#[derive(Debug, LuaDevice)] +pub struct IkeaOutlet { identifier: String, - info: InfoConfig, - mqtt: MqttDeviceConfig, - outlet_type: OutletType, - timeout: Option, - remotes: Vec, + #[config] + config: IkeaOutletConfig, client: AsyncClient, last_known_state: bool, @@ -118,19 +112,20 @@ impl Device for IkeaOutlet { impl OnMqtt for IkeaOutlet { fn topics(&self) -> Vec<&str> { let mut topics: Vec<_> = self + .config .remotes .iter() .map(|mqtt| mqtt.topic.as_str()) .collect(); - topics.push(&self.mqtt.topic); + topics.push(&self.config.mqtt.topic); topics } async fn on_mqtt(&mut self, message: Publish) { // Check if the message is from the deviec itself or from a remote - if matches(&message.topic, &self.mqtt.topic) { + if matches(&message.topic, &self.config.mqtt.topic) { // Update the internal state based on what the device has reported let state = match OnOffMessage::try_from(message) { Ok(state) => state.state(), @@ -152,7 +147,7 @@ impl OnMqtt for IkeaOutlet { self.last_known_state = state; // If this is a kettle start a timeout for turning it of again - if state && let Some(timeout) = self.timeout { + if state && let Some(timeout) = self.config.timeout { self.start_timeout(timeout).await.unwrap(); } } else { @@ -178,7 +173,7 @@ impl OnMqtt for IkeaOutlet { impl OnPresence for IkeaOutlet { async fn on_presence(&mut self, presence: bool) { // Turn off the outlet when we leave the house (Not if it is a battery charger) - if !presence && self.outlet_type != OutletType::Charger { + if !presence && self.config.outlet_type != OutletType::Charger { debug!(id = self.identifier, "Turning device off"); self.set_on(false).await.ok(); } @@ -187,7 +182,7 @@ impl OnPresence for IkeaOutlet { impl GoogleHomeDevice for IkeaOutlet { fn get_device_type(&self) -> Type { - match self.outlet_type { + match self.config.outlet_type { OutletType::Outlet => Type::Outlet, OutletType::Kettle => Type::Kettle, OutletType::Light => Type::Light, // Find a better device type for this, ideally would like to use charger, but that needs more work @@ -196,7 +191,7 @@ impl GoogleHomeDevice for IkeaOutlet { } fn get_device_name(&self) -> device::Name { - device::Name::new(&self.info.name) + device::Name::new(&self.config.info.name) } fn get_id(&self) -> &str { @@ -208,7 +203,7 @@ impl GoogleHomeDevice for IkeaOutlet { } fn get_room_hint(&self) -> Option<&str> { - self.info.room.as_deref() + self.config.info.room.as_deref() } fn will_report_state(&self) -> bool { @@ -224,7 +219,7 @@ impl traits::OnOff for IkeaOutlet { } async fn set_on(&mut self, on: bool) -> Result<(), ErrorCode> { - set_on(self.client.clone(), &self.mqtt.topic, on).await; + set_on(self.client.clone(), &self.config.mqtt.topic, on).await; Ok(()) } @@ -240,7 +235,7 @@ impl crate::traits::Timeout for IkeaOutlet { // TODO: Impl Drop for IkeaOutlet that will abort the handle if the IkeaOutlet // get dropped let client = self.client.clone(); - let topic = self.mqtt.topic.clone(); + let topic = self.config.mqtt.topic.clone(); let id = self.identifier.clone(); self.handle = Some(tokio::spawn(async move { debug!(id, "Starting timeout ({timeout:?})..."); diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index 5de2f22..dab2462 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -2,6 +2,7 @@ use std::net::{Ipv4Addr, SocketAddr}; use std::str::Utf8Error; use async_trait::async_trait; +use automation_macro::LuaDevice; use bytes::{Buf, BufMut}; use google_home::errors::{self, DeviceError}; use google_home::traits; @@ -23,7 +24,7 @@ pub struct KasaOutletConfig { #[async_trait] impl DeviceConfig for KasaOutletConfig { async fn create( - self, + &self, identifier: &str, _ext: &ConfigExternal, ) -> Result, DeviceConfigError> { @@ -31,17 +32,18 @@ impl DeviceConfig for KasaOutletConfig { let device = KasaOutlet { identifier: identifier.into(), - addr: (self.ip, 9999).into(), + config: self.clone(), }; Ok(Box::new(device)) } } -#[derive(Debug)] -struct KasaOutlet { +#[derive(Debug, LuaDevice)] +pub struct KasaOutlet { identifier: String, - addr: SocketAddr, + #[config] + config: KasaOutletConfig, } impl Device for KasaOutlet { @@ -214,7 +216,7 @@ impl Response { #[async_trait] impl traits::OnOff for KasaOutlet { async fn is_on(&self) -> Result { - let mut stream = TcpStream::connect(self.addr) + let mut stream = TcpStream::connect::((self.config.ip, 9999).into()) .await .or::(Err(DeviceError::DeviceOffline))?; @@ -248,7 +250,7 @@ impl traits::OnOff for KasaOutlet { } async fn set_on(&mut self, on: bool) -> Result<(), errors::ErrorCode> { - let mut stream = TcpStream::connect(self.addr) + let mut stream = TcpStream::connect::((self.config.ip, 9999).into()) .await .or::(Err(DeviceError::DeviceOffline))?; diff --git a/src/devices/light_sensor.rs b/src/devices/light_sensor.rs index 02c226d..2dc086f 100644 --- a/src/devices/light_sensor.rs +++ b/src/devices/light_sensor.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use automation_macro::LuaDevice; use rumqttc::Publish; use serde::Deserialize; use tracing::{debug, trace, warn}; @@ -25,16 +26,14 @@ pub const DEFAULT: bool = false; #[async_trait] impl DeviceConfig for LightSensorConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = LightSensor { identifier: identifier.into(), tx: ext.event_channel.get_tx(), - mqtt: self.mqtt, - min: self.min, - max: self.max, + config: self.clone(), is_dark: DEFAULT, }; @@ -42,13 +41,13 @@ impl DeviceConfig for LightSensorConfig { } } -#[derive(Debug)] +#[derive(Debug, LuaDevice)] pub struct LightSensor { identifier: String, + #[config] + config: LightSensorConfig, + tx: event::Sender, - mqtt: MqttDeviceConfig, - min: isize, - max: isize, is_dark: bool, } @@ -61,7 +60,7 @@ impl Device for LightSensor { #[async_trait] impl OnMqtt for LightSensor { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: Publish) { @@ -74,17 +73,17 @@ impl OnMqtt for LightSensor { }; debug!("Illuminance: {illuminance}"); - let is_dark = if illuminance <= self.min { + let is_dark = if illuminance <= self.config.min { trace!("It is dark"); true - } else if illuminance >= self.max { + } else if illuminance >= self.config.max { trace!("It is light"); false } else { trace!( "In between min ({}) and max ({}) value, keeping current state: {}", - self.min, - self.max, + self.config.min, + self.config.max, self.is_dark ); self.is_dark diff --git a/src/devices/mod.rs b/src/devices/mod.rs index f214c94..0e6c8f3 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -18,19 +18,19 @@ use automation_cast::Cast; use google_home::traits::OnOff; use google_home::GoogleHomeDevice; -pub use self::air_filter::AirFilterConfig; -pub use self::audio_setup::AudioSetupConfig; -pub use self::contact_sensor::ContactSensorConfig; -pub use self::debug_bridge::DebugBridgeConfig; -pub use self::hue_bridge::HueBridgeConfig; -pub use self::hue_light::HueGroupConfig; -pub use self::ikea_outlet::IkeaOutletConfig; -pub use self::kasa_outlet::KasaOutletConfig; +pub use self::air_filter::*; +pub use self::audio_setup::*; +pub use self::contact_sensor::*; +pub use self::debug_bridge::*; +pub use self::hue_bridge::*; +pub use self::hue_light::*; +pub use self::ikea_outlet::*; +pub use self::kasa_outlet::*; pub use self::light_sensor::{LightSensor, LightSensorConfig}; pub use self::ntfy::{Notification, Ntfy}; pub use self::presence::{Presence, PresenceConfig, DEFAULT_PRESENCE}; -pub use self::wake_on_lan::WakeOnLANConfig; -pub use self::washer::WasherConfig; +pub use self::wake_on_lan::*; +pub use self::washer::*; use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence}; use crate::traits::Timeout; diff --git a/src/devices/wake_on_lan.rs b/src/devices/wake_on_lan.rs index 7d975ba..afece56 100644 --- a/src/devices/wake_on_lan.rs +++ b/src/devices/wake_on_lan.rs @@ -1,6 +1,7 @@ use std::net::Ipv4Addr; use async_trait::async_trait; +use automation_macro::LuaDevice; use eui48::MacAddress; use google_home::errors::ErrorCode; use google_home::traits::{self, Scene}; @@ -35,7 +36,7 @@ fn default_broadcast_ip() -> Ipv4Addr { #[async_trait] impl DeviceConfig for WakeOnLANConfig { async fn create( - self, + &self, identifier: &str, _ext: &ConfigExternal, ) -> Result, DeviceConfigError> { @@ -48,23 +49,18 @@ impl DeviceConfig for WakeOnLANConfig { let device = WakeOnLAN { identifier: identifier.into(), - info: self.info, - mqtt: self.mqtt, - mac_address: self.mac_address, - broadcast_ip: self.broadcast_ip, + config: self.clone(), }; Ok(Box::new(device)) } } -#[derive(Debug)] -struct WakeOnLAN { +#[derive(Debug, LuaDevice)] +pub struct WakeOnLAN { identifier: String, - info: InfoConfig, - mqtt: MqttDeviceConfig, - mac_address: MacAddress, - broadcast_ip: Ipv4Addr, + #[config] + config: WakeOnLANConfig, } impl Device for WakeOnLAN { @@ -76,7 +72,7 @@ impl Device for WakeOnLAN { #[async_trait] impl OnMqtt for WakeOnLAN { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: Publish) { @@ -98,7 +94,7 @@ impl GoogleHomeDevice for WakeOnLAN { } fn get_device_name(&self) -> device::Name { - let mut name = device::Name::new(&self.info.name); + let mut name = device::Name::new(&self.config.info.name); name.add_default_name("Computer"); name @@ -113,7 +109,7 @@ impl GoogleHomeDevice for WakeOnLAN { } fn get_room_hint(&self) -> Option<&str> { - self.info.room.as_deref() + self.config.info.room.as_deref() } } @@ -123,25 +119,31 @@ impl traits::Scene for WakeOnLAN { if activate { debug!( id = self.identifier, - "Activating Computer: {} (Sending to {})", self.mac_address, self.broadcast_ip + "Activating Computer: {} (Sending to {})", + self.config.mac_address, + self.config.broadcast_ip ); - let wol = - wakey::WolPacket::from_bytes(&self.mac_address.to_array()).map_err(|err| { + let wol = wakey::WolPacket::from_bytes(&self.config.mac_address.to_array()).map_err( + |err| { error!(id = self.identifier, "invalid mac address: {err}"); google_home::errors::DeviceError::TransientError - })?; + }, + )?; - wol.send_magic_to((Ipv4Addr::new(0, 0, 0, 0), 0), (self.broadcast_ip, 9)) - .await - .map_err(|err| { - error!(id = self.identifier, "Failed to activate computer: {err}"); - google_home::errors::DeviceError::TransientError.into() - }) - .map(|_| debug!(id = self.identifier, "Success!")) + wol.send_magic_to( + (Ipv4Addr::new(0, 0, 0, 0), 0), + (self.config.broadcast_ip, 9), + ) + .await + .map_err(|err| { + error!(id = self.identifier, "Failed to activate computer: {err}"); + google_home::errors::DeviceError::TransientError.into() + }) + .map(|_| debug!(id = self.identifier, "Success!")) } else { debug!( id = self.identifier, - "Trying to deactive computer, this is not currently supported" + "Trying to deactivate computer, this is not currently supported" ); // We do not support deactivating this scene Err(ErrorCode::DeviceError( diff --git a/src/devices/washer.rs b/src/devices/washer.rs index 3c2a0f1..e5212c4 100644 --- a/src/devices/washer.rs +++ b/src/devices/washer.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use automation_macro::LuaDevice; use rumqttc::Publish; use serde::Deserialize; use tracing::{debug, error, warn}; @@ -21,15 +22,14 @@ pub struct WasherConfig { #[async_trait] impl DeviceConfig for WasherConfig { async fn create( - self, + &self, identifier: &str, ext: &ConfigExternal, ) -> Result, DeviceConfigError> { let device = Washer { identifier: identifier.into(), - mqtt: self.mqtt, + config: self.clone(), event_channel: ext.event_channel.clone(), - threshold: self.threshold, running: 0, }; @@ -39,13 +39,13 @@ impl DeviceConfig for WasherConfig { // TODO: Add google home integration -#[derive(Debug)] -struct Washer { +#[derive(Debug, LuaDevice)] +pub struct Washer { identifier: String, - mqtt: MqttDeviceConfig, + #[config] + config: WasherConfig, event_channel: EventChannel, - threshold: f32, running: isize, } @@ -63,7 +63,7 @@ const HYSTERESIS: isize = 10; #[async_trait] impl OnMqtt for Washer { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: Publish) { @@ -77,12 +77,12 @@ impl OnMqtt for Washer { // debug!(id = self.identifier, power, "Washer state update"); - if power < self.threshold && self.running >= HYSTERESIS { + if power < self.config.threshold && self.running >= HYSTERESIS { // The washer is done running debug!( id = self.identifier, power, - threshold = self.threshold, + threshold = self.config.threshold, "Washer is done" ); @@ -102,15 +102,15 @@ impl OnMqtt for Washer { { warn!("There are no receivers on the event channel"); } - } else if power < self.threshold { + } else if power < self.config.threshold { // Prevent false positives self.running = 0; - } else if power >= self.threshold && self.running < HYSTERESIS { + } else if power >= self.config.threshold && self.running < HYSTERESIS { // Washer could be starting debug!( id = self.identifier, power, - threshold = self.threshold, + threshold = self.config.threshold, "Washer is starting" ); diff --git a/src/main.rs b/src/main.rs index e40df8e..511efb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,13 @@ #![feature(async_closure)] -use std::process; +use std::{fs, process}; use automation::auth::{OpenIDConfig, User}; use automation::config::Config; use automation::device_manager::DeviceManager; -use automation::devices::{Ntfy, Presence}; +use automation::devices::{ + AirFilter, AudioSetup, ContactSensor, DebugBridge, HueBridge, HueGroup, IkeaOutlet, KasaOutlet, + LightSensor, Ntfy, Presence, WakeOnLAN, Washer, +}; use automation::error::ApiError; use automation::mqtt; use axum::extract::FromRef; @@ -60,10 +63,6 @@ async fn app() -> anyhow::Result<()> { // Setup the device handler let device_manager = DeviceManager::new(client.clone()); - for (id, device_config) in config.devices { - device_manager.create(&id, device_config).await?; - } - device_manager.add_schedule(config.schedule).await; let event_channel = device_manager.event_channel(); @@ -80,6 +79,47 @@ async fn app() -> anyhow::Result<()> { device_manager.add(Box::new(ntfy)).await; } + // Lua testing + { + let lua = mlua::Lua::new(); + let automation = lua.create_table()?; + + automation.set("device_manager", device_manager.clone())?; + + let util = lua.create_table()?; + let get_env = lua.create_function(|_lua, name: String| { + std::env::var(name).map_err(mlua::ExternalError::into_lua_err) + })?; + util.set("get_env", get_env)?; + automation.set("util", util)?; + + lua.globals().set("automation", automation)?; + + // Register all the device types + AirFilter::register_with_lua(&lua)?; + AudioSetup::register_with_lua(&lua)?; + ContactSensor::register_with_lua(&lua)?; + DebugBridge::register_with_lua(&lua)?; + HueBridge::register_with_lua(&lua)?; + HueGroup::register_with_lua(&lua)?; + IkeaOutlet::register_with_lua(&lua)?; + KasaOutlet::register_with_lua(&lua)?; + LightSensor::register_with_lua(&lua)?; + WakeOnLAN::register_with_lua(&lua)?; + Washer::register_with_lua(&lua)?; + + // TODO: Make this not hardcoded + let filename = "config.lua"; + let file = fs::read_to_string(filename)?; + match lua.load(file).set_name(filename).exec_async().await { + Err(error) => { + println!("{error}"); + Err(error) + } + result => result, + }?; + } + // Wrap the mqtt eventloop and start listening for message // NOTE: We wait until all the setup is done, as otherwise we might miss some messages mqtt::start(eventloop, &event_channel);