Device config is now done through lua
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 7m59s
Build and deploy automation_rs / Build Docker image (push) Successful in 46s
Build and deploy automation_rs / Deploy Docker container (push) Has been skipped

This commit is contained in:
Dreaded_X 2024-04-24 02:31:05 +02:00
parent 8b0c1ae352
commit 0d1e15c676
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
23 changed files with 827 additions and 554 deletions

389
Cargo.lock generated
View File

@ -61,7 +61,7 @@ checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -76,6 +76,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"automation_macro",
"axum",
"bytes",
"console-subscriber",
@ -86,6 +87,7 @@ dependencies = [
"google-home",
"impl_cast",
"indexmap 2.0.0",
"mlua",
"paste",
"pollster",
"regex",
@ -104,6 +106,14 @@ dependencies = [
"wakey",
]
[[package]]
name = "automation_macro"
version = "0.1.0"
dependencies = [
"quote",
"syn 2.0.60",
]
[[package]]
name = "axum"
version = "0.6.20"
@ -112,7 +122,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core",
"bitflags",
"bitflags 1.3.2",
"bytes",
"futures-util",
"http",
@ -186,6 +196,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"
@ -344,7 +370,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -355,7 +381,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -397,7 +423,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -406,6 +432,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"
@ -510,7 +555,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -632,6 +677,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"
@ -766,7 +820,7 @@ name = "impl_cast"
version = "0.1.0"
dependencies = [
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -806,6 +860,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"
@ -829,9 +892,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"
@ -849,6 +918,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"
@ -899,7 +987,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]]
@ -982,6 +1116,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"
@ -1017,7 +1160,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1032,6 +1175,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"
@ -1045,10 +1194,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",
]
@ -1070,7 +1243,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",
@ -1087,9 +1260,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",
]
@ -1246,6 +1419,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"
@ -1328,7 +1520,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]]
@ -1353,7 +1545,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",
@ -1372,22 +1564,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]]
@ -1419,7 +1621,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1460,7 +1662,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1517,7 +1719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -1554,9 +1756,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",
@ -1586,7 +1788,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1657,7 +1859,7 @@ dependencies = [
"socket2 0.5.3",
"tokio-macros",
"tracing",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -1693,7 +1895,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1823,7 +2025,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
]
[[package]]
@ -1930,6 +2132,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"
@ -1976,7 +2184,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
"wasm-bindgen-shared",
]
@ -2010,7 +2218,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -2050,6 +2258,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"
@ -2078,7 +2298,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]]
@ -2087,7 +2307,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]]
@ -2096,13 +2325,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]]
@ -2111,42 +2356,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"
@ -2155,3 +2448,9 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"

View File

@ -4,9 +4,10 @@ version = "0.1.0"
edition = "2021"
[workspace]
members = ["impl_cast", "google-home"]
members = ["impl_cast", "google-home", "automation_macro"]
[dependencies]
automation_macro = { path = "./automation_macro" }
rumqttc = "0.18"
serde = { version = "1.0.149", features = ["derive"] }
serde_json = "1.0.89"
@ -41,6 +42,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" }

View File

@ -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"

View File

@ -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<dyn crate::device_manager::DeviceConfig> = Box::new(config);
Ok(config)
});
}
}
};
gen.into()
}

160
config.lua Normal file
View File

@ -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",
})
)

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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<NtfyConfig>,
pub presence: PresenceConfig,
pub devices: IndexMap<String, DeviceConfigs>,
pub schedule: Schedule,
}

View File

@ -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, As, AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device,
HueBridgeConfig, HueGroupConfig, IkeaOutletConfig, KasaOutletConfig, LightSensorConfig,
WakeOnLANConfig, WasherConfig,
};
use crate::devices::{As, 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<Box<dyn Device>, 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<dyn DeviceConfig> {}
pub type WrappedDevice = Arc<RwLock<Box<dyn Device>>>;
pub type DeviceMap = HashMap<String, WrappedDevice>;
@ -160,24 +140,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()
}
@ -261,3 +223,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<dyn DeviceConfig> = 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)
},
)
}
}

View File

@ -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<Box<dyn Device>, 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 {

View File

@ -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};
@ -22,7 +23,7 @@ pub struct AudioSetupConfig {
#[async_trait]
impl DeviceConfig for AudioSetupConfig {
async fn create(
self,
&self,
identifier: &str,
ext: &ConfigExternal,
) -> Result<Box<dyn Device>, DeviceConfigError> {
@ -40,7 +41,10 @@ impl DeviceConfig for AudioSetupConfig {
))?;
if !As::<dyn OnOff>::is(mixer.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
return Err(DeviceConfigError::MissingTrait(
self.mixer.clone(),
"OnOff".into(),
));
}
let speakers =
@ -54,14 +58,14 @@ impl DeviceConfig for AudioSetupConfig {
if !As::<dyn OnOff>::is(speakers.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(
self.speakers,
self.speakers.clone(),
"OnOff".into(),
));
}
let device = AudioSetup {
identifier: identifier.into(),
mqtt: self.mqtt,
config: self.clone(),
mixer,
speakers,
};
@ -71,10 +75,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,
}
@ -88,7 +93,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) {

View File

@ -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<Box<dyn Device>, DeviceConfigError> {
@ -89,8 +90,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,
@ -108,11 +108,11 @@ struct Trigger {
timeout: Duration, // Timeout in seconds
}
#[derive(Debug)]
struct ContactSensor {
#[derive(Debug, LuaDevice)]
pub struct ContactSensor {
identifier: String,
mqtt: MqttDeviceConfig,
presence: Option<PresenceDeviceConfig>,
#[config]
config: ContactSensorConfig,
client: AsyncClient,
overall_presence: bool,
@ -138,7 +138,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) {
@ -185,7 +185,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,
};

View File

@ -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<Box<dyn Device>, 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();

View File

@ -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<Box<dyn Device>, 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");

View File

@ -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<Box<dyn Device>, 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<MqttDeviceConfig>,
#[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)
};

View File

@ -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<Box<dyn Device>, 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<Duration>,
remotes: Vec<MqttDeviceConfig>,
#[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:?})...");

View File

@ -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<Box<dyn Device>, 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<bool, errors::ErrorCode> {
let mut stream = TcpStream::connect(self.addr)
let mut stream = TcpStream::connect::<SocketAddr>((self.config.ip, 9999).into())
.await
.or::<DeviceError>(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::<SocketAddr>((self.config.ip, 9999).into())
.await
.or::<DeviceError>(Err(DeviceError::DeviceOffline))?;

View File

@ -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<Box<dyn Device>, 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

View File

@ -15,19 +15,19 @@ mod washer;
use google_home::device::AsGoogleHomeDevice;
use google_home::traits::OnOff;
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;

View File

@ -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<Box<dyn Device>, 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(

View File

@ -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<Box<dyn Device>, 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"
);

View File

@ -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;
@ -59,10 +62,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();
@ -79,6 +78,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);