Compare commits

..

25 Commits

Author SHA1 Message Date
J / Jacob Babich
4786b1e6ba chore: clean up emitter-and-signal library design 2026-07-15 14:33:51 -04:00
J / Jacob Babich
a09a97d06d chore: replace all #[snafu(display(...))] with regular doc comments /// 2026-07-15 13:48:41 -04:00
4eb8a752cc feat(entrypoint): trial the power sensor signal for the smart plugs connected to the washer and dryer currently 2026-07-15 02:41:13 -04:00
J / Jacob Babich
8d3cde3c43 chore(driver-kasa): rename impl_protocol to protocol to match with home-assistant/light 2026-07-15 01:06:34 -04:00
J / Jacob Babich
cb216d0a0a chore: rename Numeric generic parameter to Number so that it's a noun and not mistakeable for a trait 2026-07-15 01:00:50 -04:00
J / Jacob Babich
8b966c1210 feat: make power sensor signal support any numeric type 2026-07-15 00:57:59 -04:00
J / Jacob Babich
a2d6d9f4c2 feat: make the input_number attributes and signal support any numeric type 2026-07-15 00:52:01 -04:00
J / Jacob Babich
8ab8dd3441 chore: address some clippy concerns 2026-07-15 00:32:04 -04:00
J / Jacob Babich
224ee7732f chore: upgrade to fjall 3 2026-07-15 00:13:47 -04:00
J / Jacob Babich
83b35908f7 chore: update lockfile 2026-07-15 00:09:02 -04:00
e3c54a996e feat: input_number signal support, implement Clone for a lot of error types 2026-07-14 22:53:42 -04:00
38c30d87d0 chore(home-assistant): rename HomeAssistantState::UnexpectedErr to Unexpected because I was expecting it to contain an error type returning to the project after so long 2026-07-14 21:29:50 -04:00
17fabbf178 chore: eliminate all usage of pyo3::prelude::* 2026-07-14 20:50:07 -04:00
6abe278aa3 chore(home-assistant): streamline Python conversion for UnexpectedState 2026-07-14 20:29:32 -04:00
120ad97c53 chore(home-assistant): streamline Python conversion for LightState 2026-07-14 20:27:51 -04:00
338a8c571c chore(home-assistant): streamline Python conversion for ServiceDomain and ServiceId 2026-07-14 20:24:27 -04:00
34d0266c1f chore: make MobileAppDeviceId type instead of using ObjectId as that is the wrong abstraction 2026-07-14 20:14:45 -04:00
ac0f574d2b chore(home-assistant): remove unused imports in object_id 2026-07-14 20:06:21 -04:00
9863029eb9 chore(home-assistant): streamline ObjectId's Python definitions 2026-07-14 20:04:15 -04:00
J / Jacob Babich
41cf9d0d09 fix: remove dbg! in StringLiteral ToTokens implementation 2026-07-12 19:39:57 -04:00
J / Jacob Babich
d6418b5fb0 chore(entrypoint): remove unused imports 2026-07-12 19:39:21 -04:00
J / Jacob Babich
c0737c4420 chore: remove unused imports 2026-07-12 19:38:27 -04:00
J / Jacob Babich
53a2fc2a35 chore(protocol): address async fn in extension traits 2026-07-12 19:34:25 -04:00
J / Jacob Babich
044243ae3c chore: update various dependencies (except fjall) to their latest versions 2026-07-12 14:13:06 -04:00
J / Jacob Babich
7f3c2ac30c fix(home-assistant): expose sensor::device_classes::power for use 2026-07-12 14:07:12 -04:00
58 changed files with 766 additions and 667 deletions

531
Cargo.lock generated
View File

@@ -78,7 +78,7 @@ dependencies = [
"chrono-tz",
"derive_more",
"ijson",
"itertools",
"itertools 0.15.0",
"pyo3",
"snafu",
]
@@ -186,6 +186,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.0"
@@ -228,6 +234,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.12.1"
@@ -236,15 +248,15 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "byteview"
version = "0.6.1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6236364b88b9b6d0bc181ba374cf1ab55ba3ef97a1cb6f8cddad48a273767fb5"
checksum = "1c53ba0f290bfc610084c05582d9c5d421662128fc69f4bf236707af6fd321b9"
[[package]]
name = "cc"
version = "1.2.66"
version = "1.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -264,6 +276,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
"cpufeatures",
"rand_core 0.10.1",
]
[[package]]
name = "chrono"
version = "0.4.45"
@@ -385,6 +408,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "critical-section"
version = "1.2.0"
@@ -496,6 +528,37 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "defmt"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]]
name = "deranged"
version = "0.5.8"
@@ -539,12 +602,6 @@ dependencies = [
"syn",
]
[[package]]
name = "double-ended-peekable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0d05e1c0dbad51b52c38bda7adceef61b9efc2baf04acfe8726a8c4630a6f57"
[[package]]
name = "driver-kasa"
version = "0.1.0"
@@ -666,21 +723,30 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fjall"
version = "2.11.2"
version = "3.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25ad44cd4360a0448a9b5a0a6f1c7a621101cca4578706d43c9a821418aebc"
checksum = "9fcdc69609906151dff9b534e30eaf8515082055d36f628e382bd0b5d6a1d362"
dependencies = [
"byteorder",
"byteorder-lite",
"byteview",
"dashmap 6.2.1",
"flume",
"log",
"lsm-tree",
"path-absolutize",
"std-semaphore",
"lz4_flex",
"tempfile",
"xxhash-rust",
]
[[package]]
name = "flume"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
dependencies = [
"spin",
]
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -778,18 +844,6 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi 5.3.0",
"wasip2",
]
[[package]]
name = "getrandom"
version = "0.4.3"
@@ -798,7 +852,8 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"r-efi",
"rand_core 0.10.1",
]
[[package]]
@@ -807,7 +862,7 @@ version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"libc",
"libgit2-sys",
"log",
@@ -826,12 +881,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "guardian"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f"
[[package]]
name = "hash32"
version = "0.2.1"
@@ -912,7 +961,7 @@ dependencies = [
"tracing",
"typed-builder",
"ulid",
"uom 0.37.0",
"uom",
"url",
]
@@ -928,9 +977,9 @@ dependencies = [
[[package]]
name = "http-body"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
dependencies = [
"bytes",
"http",
@@ -938,9 +987,9 @@ dependencies = [
[[package]]
name = "http-body-util"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
dependencies = [
"bytes",
"futures-core",
@@ -1179,15 +1228,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]]
name = "interval-heap"
version = "0.0.5"
@@ -1218,19 +1258,70 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e"
dependencies = [
"defmt",
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-link",
]
[[package]]
name = "jiff-static"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jiff-tzdb"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e"
[[package]]
name = "jiff-tzdb-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
[[package]]
name = "jobserver"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom 0.4.3",
"getrandom",
"libc",
]
@@ -1325,33 +1416,34 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "lsm-tree"
version = "2.10.4"
version = "3.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799399117a2bfb37660e08be33f470958babb98386b04185288d829df362ea15"
checksum = "39ca67401338b98d58447387dd5230552d2241bc388206e491d137b18dfea9d6"
dependencies = [
"byteorder",
"byteorder-lite",
"byteview",
"crossbeam-skiplist",
"double-ended-peekable",
"enum_dispatch",
"guardian",
"interval-heap",
"log",
"lz4_flex",
"path-absolutize",
"quick_cache",
"rustc-hash",
"self_cell",
"sfa",
"tempfile",
"value-log",
"varint-rs",
"xxhash-rust",
]
[[package]]
name = "lz4_flex"
version = "0.11.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e"
dependencies = [
"twox-hash",
]
[[package]]
name = "mac_address"
@@ -1393,9 +1485,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mio"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
dependencies = [
"libc",
"wasi",
@@ -1404,13 +1496,13 @@ dependencies = [
[[package]]
name = "mitsein"
version = "0.8.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b274adb8f25abe29f072083a61653188837fb768f0da534170e4f6162c93b47d"
checksum = "8a49aef75c004fb496a23f175395415ecfe2a630e4c6175ae6f7d9cc1a19332e"
dependencies = [
"either",
"indexmap 2.14.0",
"itertools",
"itertools 0.14.0",
"schemars 1.2.1",
"serde",
"smallvec",
@@ -1422,7 +1514,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -1453,15 +1545,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -1511,24 +1594,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "path-absolutize"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5"
dependencies = [
"path-dedot",
]
[[package]]
name = "path-dedot"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397"
dependencies = [
"once_cell",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -1574,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand 0.8.6",
"rand 0.8.7",
]
[[package]]
@@ -1626,6 +1691,15 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]]
name = "postcard"
version = "1.1.3"
@@ -1654,15 +1728,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -1679,6 +1744,7 @@ dependencies = [
"deranged",
"derive_more",
"ext-trait",
"futures",
"palette",
"serde",
"snafu",
@@ -1687,30 +1753,28 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.27.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d"
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
dependencies = [
"chrono",
"chrono-tz",
"indoc",
"libc",
"memoffset",
"once_cell",
"portable-atomic",
"pyo3-build-config",
"pyo3-ffi",
"pyo3-macros",
"unindent",
]
[[package]]
name = "pyo3-async-runtimes"
version = "0.27.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57ddb5b570751e93cc6777e81fee8087e59cd53b5043292f2a6d59d5bd80fdfd"
checksum = "b3ef68daa7316a3fac65e5e18b2203f010346de1c1c53456811a2624673ab046"
dependencies = [
"futures",
"futures-channel",
"futures-util",
"once_cell",
"pin-project-lite",
"pyo3",
@@ -1719,18 +1783,18 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.27.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6"
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
dependencies = [
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.27.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089"
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
dependencies = [
"libc",
"pyo3-build-config",
@@ -1738,9 +1802,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.27.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02"
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -1750,13 +1814,12 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.27.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9"
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
dependencies = [
"heck",
"proc-macro2",
"pyo3-build-config",
"quote",
"syn",
]
@@ -1808,12 +1871,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
@@ -1822,31 +1879,22 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.4"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
"chacha20",
"getrandom",
"rand_core 0.10.1",
]
[[package]]
@@ -1857,12 +1905,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_core"
version = "0.9.5"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rand_xoshiro"
@@ -1899,7 +1944,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
"bitflags 2.13.0",
]
[[package]]
@@ -1943,7 +1988,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"errno",
"libc",
"linux-raw-sys",
@@ -2085,16 +2130,26 @@ dependencies = [
]
[[package]]
name = "shadow-rs"
version = "1.7.1"
name = "sfa"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c798acfc78a69c7b038adde44084d8df875555b091da42c90ae46257cdcc41a"
checksum = "a1296838937cab56cd6c4eeeb8718ec777383700c33f060e2869867bd01d1175"
dependencies = [
"byteorder-lite",
"log",
"xxhash-rust",
]
[[package]]
name = "shadow-rs"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd39b4b2077bd36e60ca28c31d494046e747759cb9b507a7d177bb64787c39e"
dependencies = [
"const_format",
"git2",
"is_debug",
"time",
"tzdb",
"jiff",
]
[[package]]
@@ -2165,23 +2220,23 @@ dependencies = [
"tracing",
"tracing-appender",
"tracing-subscriber",
"uom 0.36.0",
"uom",
]
[[package]]
name = "snafu"
version = "0.8.9"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2"
checksum = "d1a012328be2e3f5d5f6f3218147ca02588cea4cb865e876849ab6debcf36522"
dependencies = [
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.8.9"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451"
checksum = "5f103c50866b8743da9429b8a581d81a27c2d3a9c4ac7df8f8571c1dd7896eda"
dependencies = [
"heck",
"proc-macro2",
@@ -2191,9 +2246,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys",
@@ -2201,9 +2256,9 @@ dependencies = [
[[package]]
name = "spin"
version = "0.9.8"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
dependencies = [
"lock_api",
]
@@ -2214,12 +2269,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "std-semaphore"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ae9eec00137a8eed469fb4148acd9fc6ac8c3f9b110f52cd34698c8b5bfa0e"
[[package]]
name = "string-literal"
version = "0.1.0"
@@ -2252,18 +2301,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
dependencies = [
"heck",
"proc-macro2",
@@ -2279,9 +2328,9 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
[[package]]
name = "syn"
version = "2.0.118"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
@@ -2318,7 +2367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
@@ -2360,9 +2409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
dependencies = [
"deranged",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde_core",
"time-core",
@@ -2534,19 +2581,25 @@ dependencies = [
]
[[package]]
name = "typed-builder"
version = "0.22.0"
name = "twox-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "398a3a3c918c96de527dc11e6e846cd549d4508030b8a33e1da12789c856b81a"
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
[[package]]
name = "typed-builder"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda"
dependencies = [
"typed-builder-macro",
]
[[package]]
name = "typed-builder-macro"
version = "0.22.0"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e48cea23f68d1f78eb7bc092881b6bb88d3d6b5b7e6234f6f9c911da1ffb221"
checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26"
dependencies = [
"proc-macro2",
"quote",
@@ -2559,39 +2612,13 @@ version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "tz-rs"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc6c929ffa10fb34f4a3c7e9a73620a83ef2e85e47f9ec3381b8289e6762f42"
[[package]]
name = "tzdb"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d4e985b6dda743ae7fd4140c28105316ffd75bc58258ee6cc12934e3eb7a0c"
dependencies = [
"iana-time-zone",
"tz-rs",
"tzdb_data",
]
[[package]]
name = "tzdb_data"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "febaa995f6852367564e16c3369988b99d471d43ed12b1255b8d294661797f1c"
dependencies = [
"tz-rs",
]
[[package]]
name = "ulid"
version = "1.2.1"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe"
checksum = "39493d572b98fe1d142e132e7a726c50c68121bce84429216a5ec20e1ad7fec5"
dependencies = [
"rand 0.9.4",
"rand 0.10.2",
"web-time",
]
@@ -2613,27 +2640,11 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unindent"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
[[package]]
name = "uom"
version = "0.36.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffd36e5350a65d112584053ee91843955826bf9e56ec0d1351214e01f6d7cd9c"
dependencies = [
"num-traits",
"typenum",
]
[[package]]
name = "uom"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd5cfe7d84f6774726717f358a37f5bca8fca273bed4de40604ad129d1107b49"
checksum = "a739f83872836c82a4f2527d4e54b37007b3de68cafe7edde95fd695968bf4b9"
dependencies = [
"num-traits",
"typenum",
@@ -2669,23 +2680,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "value-log"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62fc7c4ce161f049607ecea654dca3f2d727da5371ae85e2e4f14ce2b98ed67c"
dependencies = [
"byteorder",
"byteview",
"interval-heap",
"log",
"path-absolutize",
"rustc-hash",
"tempfile",
"varint-rs",
"xxhash-rust",
]
[[package]]
name = "varint-rs"
version = "2.2.1"
@@ -2710,15 +2704,6 @@ version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
@@ -2864,12 +2849,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "writeable"
version = "0.6.3"
@@ -2878,9 +2857,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "xxhash-rust"
version = "0.8.16"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576"
checksum = "985eec839aaf2a1270af8f4ebcf63cf9401cfd90f0902f97c28d9f104ffbde72"
[[package]]
name = "yoke"
@@ -2905,26 +2884,6 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
@@ -2981,6 +2940,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

View File

@@ -27,18 +27,19 @@ chrono-tz = "0.10.4"
deranged = "0.5"
derive_more = "2.1.0"
ext-trait = "2.0.1"
mitsein = "0.8"
futures = "0.3.32"
mitsein = "0.9"
palette = "0.7"
proc-macro2 = "1.0.106"
pyo3 = "0.27"
pyo3-async-runtimes = "0.27"
pyo3 = "0.29"
pyo3-async-runtimes = "0.29"
quote = "1.0.46"
serde = "1.0.228"
snafu = "0.8.9"
strum = "0.27.2"
snafu = "0.9"
strum = "0.28"
syn = "2.0.118"
tokio = "1.48.0"
tracing = "0.1.43"
typed-builder = "0.22"
typed-builder-macro = "0.22"
typed-builder = "0.23"
typed-builder-macro = "0.23"
url = "2.5"

View File

@@ -12,6 +12,6 @@ chrono = { workspace = true }
chrono-tz = { workspace = true }
derive_more = { workspace = true }
ijson = "0.1.4"
itertools = "0.14.0"
itertools = "0.15"
pyo3 = { workspace = true, optional = true, features = ["chrono", "chrono-tz"] }
snafu = { workspace = true }

View File

@@ -4,8 +4,8 @@ use ijson::{IArray, INumber, IObject, IString, IValue};
#[cfg(feature = "pyo3")]
use pyo3::{
exceptions::{PyException, PyTypeError, PyValueError},
prelude::*,
types::{PyList, PyNone},
types::{PyAnyMethods as _, PyList, PyNone, PyTypeMethods as _},
Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python,
};
use snafu::{ResultExt, Snafu};
@@ -41,9 +41,10 @@ impl From<MapKey> for Arbitrary {
#[derive(Debug, Snafu)]
pub enum MapKeyFromArbitraryError {
#[snafu(display("floats aren't supported as map keys yet. got {value:?}"))]
/// floats aren't supported as map keys yet. got {value:?}
FloatNotSupported { value: FiniteF64 },
#[snafu(display("a map cannot be a map key. got {value:?}"))]
/// a map cannot be a map key. got {value:?}
MapCannotBeAMapKey { value: Map },
}

View File

@@ -4,8 +4,8 @@ use snafu::Snafu;
#[derive(Debug, Clone, derive_more::Into)]
pub struct FiniteF64(f64);
/// {value:?} is not finite
#[derive(Debug, Snafu)]
#[snafu(display("{value:?} is not finite"))]
pub struct NotFinite {
value: f64,
}

View File

@@ -7,8 +7,8 @@ use itertools::Itertools;
#[cfg(feature = "pyo3")]
use pyo3::{
exceptions::PyTypeError,
prelude::*,
types::{PyNone, PyTuple},
types::{PyAnyMethods as _, PyNone, PyTuple, PyTypeMethods as _},
Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python,
};
use snafu::{ResultExt, Snafu};

View File

@@ -1,6 +1,6 @@
use crate::messages::{
GetSysInfo, GetSysInfoResponse, LB130USSys, LightState, Off, On, SetLightLastOn, SetLightOff,
SetLightState, SetLightStateArgs, SetLightStateResponse, SetLightTo, SysInfo,
GetSysInfo, GetSysInfoResponse, LB130USSys, SetLightState, SetLightStateArgs,
SetLightStateResponse, SysInfo,
};
use backon::{FibonacciBuilder, Retryable};
@@ -186,8 +186,7 @@ async fn send_request<
let incoming_length = reader.read_u32().await.context(ReadSnafu)?;
tracing::info!(?incoming_length);
let mut incoming_message = Vec::new();
incoming_message.resize(incoming_length as usize, 0);
let mut incoming_message = vec![0; incoming_length as usize];
reader
.read_exact(&mut incoming_message)
.await

View File

@@ -1,3 +1,3 @@
pub mod connection;
mod impl_protocol;
pub mod messages;
mod protocol;

View File

@@ -178,7 +178,7 @@ impl<'de> Deserialize<'de> for MaybeKelvin {
match u16::deserialize(deserializer)? {
0 => Ok(MaybeKelvin(None)),
value => {
let kelvin = Kelvin::try_from(value).map_err(|e| {
let kelvin = Kelvin::try_from(value).map_err(|_e| {
serde::de::Error::custom(format!(
"{value} is not in the range {}..{}",
Kelvin::MIN,

View File

@@ -1,5 +1,3 @@
use std::convert::Infallible;
use palette::{encoding::Srgb, Hsv, IntoColor};
use protocol::light::{GetState, SetState, TurnToColor, TurnToTemperature};
use snafu::{ResultExt, Snafu};
@@ -7,8 +5,8 @@ use snafu::{ResultExt, Snafu};
use crate::{
connection::{HandleError, LB130USHandle},
messages::{
Angle, Hsb, LightState, Off, On, Percentage, SetLightHsv, SetLightKelvin, SetLightLastOn,
SetLightOff, SetLightStateArgs, SetLightTo,
LightState, Off, On, SetLightHsv, SetLightKelvin, SetLightLastOn, SetLightOff,
SetLightStateArgs, SetLightTo,
},
};

View File

@@ -1,9 +1,9 @@
use snafu::Snafu;
pub mod emitter;
pub mod emitter_ext;
mod emitter_ext;
pub mod signal;
pub mod signal_ext;
mod signal_ext;
pub use emitter::Emitter;
pub use emitter_ext::EmitterExt;

View File

@@ -1,30 +1,24 @@
use std::future::Future;
use ext_trait::extension;
use snafu::{ResultExt, Snafu};
use tokio::select;
use crate::ProducerExited;
use super::signal::{JoinError, Signal};
#[derive(Debug, Snafu)]
pub struct ProducerAlreadyExited {
source: ProducerExited,
}
#[extension(pub trait SignalExt)]
impl<T> Signal<T> {
fn map<M, F>(
self,
mut func: F,
) -> Result<(Signal<M>, impl Future<Output = Result<(), JoinError>>), ProducerAlreadyExited>
) -> Result<(Signal<M>, impl Future<Output = Result<(), JoinError>>), ProducerExited>
where
T: 'static + Sync + Send + Clone,
M: 'static + Sync + Send + Clone,
F: 'static + Send + FnMut(T) -> M,
{
let initial = func(self.subscribe().context(ProducerAlreadyExitedSnafu)?.get());
let initial = func(self.subscribe()?.get());
Ok(Signal::new(initial, |mut publisher_stream| async move {
while let Some(publisher) = publisher_stream.wait().await {

View File

@@ -32,13 +32,13 @@ pyo3 = { workspace = true, features = [
"extension-module",
] }
pyo3-async-runtimes = { workspace = true, features = ["tokio-runtime"] }
shadow-rs = { version = "1.0.1", default-features = false }
shadow-rs = { version = "2", default-features = false }
snafu = { workspace = true }
tokio = { workspace = true, features = ["time"] }
tracing = { workspace = true }
tracing-appender = "0.2.3"
tracing-subscriber = "0.3.17"
uom = "0.36.0"
uom = "0.38.0"
[build-dependencies]
shadow-rs = "1.0.1"
shadow-rs = "2"

View File

@@ -1,24 +1,22 @@
use std::{num::NonZeroUsize, path::PathBuf, str::FromStr, time::Duration};
use std::{path::PathBuf, str::FromStr, time::Duration};
use clap::Parser;
use driver_kasa::connection::LB130USHandle;
use futures::stream::{FuturesUnordered, StreamExt};
use home_assistant::{
event::context::context::Context, home_assistant::HomeAssistant, light::HomeAssistantLight,
notify::service::mobile_app::StandardNotification, object_id::ObjectId,
use futures::{TryFutureExt, stream::{FuturesUnordered, StreamExt}};
use home_assistant::{home_assistant::HomeAssistant, object_id::ObjectId};
use protocol::light::{Kelvin, TurnToTemperature};
use pyo3::{
pyfunction, pymodule,
types::{PyModule, PyModuleMethods as _},
wrap_pyfunction, Bound, PyAny, PyResult, Python,
};
use persisted::{persisted, Config, Keyspace, Partition, PartitionCreateOptions};
use protocol::light::{IsOff, IsOn, Kelvin, TurnToTemperature};
use pyo3::prelude::*;
use shadow_rs::shadow;
use tokio::{
task::{spawn_blocking, JoinSet},
time::{interval, MissedTickBehavior},
join, task::JoinSet, time::{MissedTickBehavior, interval}
};
use tracing::{level_filters::LevelFilter, Level};
use tracing_appender::rolling::{self, RollingFileAppender};
use tracing_subscriber::{
fmt::{self, fmt, format::FmtSpan},
fmt::{self, format::FmtSpan},
layer::SubscriberExt,
registry,
util::SubscriberInitExt,
@@ -191,6 +189,58 @@ async fn real_main(
// }, context, target, false).await;
// dbg!(total_silence_result);
// }
},
async {
let plug_awp04l_1_power_object_id = "plug_awp04l_1_power";
let plug_awp04l_2_power_object_id = "plug_awp04l_2_power";
let plug_awp04l_1_power_object_id = ObjectId::from_str(plug_awp04l_1_power_object_id)
.expect("statically written and known to be correct");
let plug_awp04l_2_power_object_id = ObjectId::from_str(plug_awp04l_2_power_object_id)
.expect("statically written and known to be correct");
let plug_awp04l_1_power_signal_result = Python::attach(|py| {
home_assistant::sensor::device_classes::power::signal::<f64>(
py,
&home_assistant,
plug_awp04l_1_power_object_id,
)
});
let plug_awp04l_2_power_signal_result = Python::attach(|py| {
home_assistant::sensor::device_classes::power::signal::<f64>(
py,
&home_assistant,
plug_awp04l_2_power_object_id,
)
});
let mut tasks = JoinSet::new();
if let Ok((plug_awp04l_1_power_signal, task)) = plug_awp04l_1_power_signal_result {
tracing::error!("listening to the plug_awp04l_1_power_signal");
tasks.spawn(task.unwrap_or_else(|e| panic!("TODO: {e}")));
tasks.spawn(
plug_awp04l_1_power_signal
.subscribe()
.expect("TODO")
.for_each(|plug_awp04l_1_power| async move {
tracing::warn!(?plug_awp04l_1_power)
}),
);
}
if let Ok((plug_awp04l_2_power_signal, task)) = plug_awp04l_2_power_signal_result {
tracing::error!("listening to the plug_awp04l_2_power_signal");
tasks.spawn(task.unwrap_or_else(|e| panic!("TODO: {e}")));
tasks.spawn(
plug_awp04l_2_power_signal
.subscribe()
.expect("TODO")
.for_each(|plug_awp04l_2_power| async move {
tracing::warn!(?plug_awp04l_2_power)
}),
);
}
tasks.join_all().await;
}
)
.0

View File

@@ -31,6 +31,6 @@ strum = { workspace = true, features = ["derive"] }
tokio = { workspace = true }
tracing = { optional = true, workspace = true }
typed-builder = { workspace = true }
ulid = "1.2.0"
uom = "0.37.0"
ulid = "2"
uom = "0.38"
url = { workspace = true }

View File

@@ -12,13 +12,13 @@ pub struct EntityId(pub Domain, pub ObjectId);
#[derive(Debug, Clone, Snafu)]
pub enum EntityIdParsingError {
#[snafu(display("entity IDs have a dot / period in them, e.g. light.kitchen_lamp"))]
/// entity IDs have a dot / period in them, e.g. light.kitchen_lamp
MissingDot,
#[snafu(display("could not parse the domain part of the entity ID"))]
/// could not parse the domain part of the entity ID
ParsingDomain { source: <Domain as FromStr>::Err },
#[snafu(display("could not parse the object ID part of the entity ID"))]
/// could not parse the object ID part of the entity ID
ParsingObjectId { source: ObjectIdParsingError },
}

View File

@@ -1,39 +0,0 @@
use super::id::Id;
use once_cell::sync::OnceCell;
use pyo3::{prelude::*, types::PyType};
/// The context that triggered something.
#[derive(Debug, FromPyObject)]
pub struct Context<Event> {
pub id: Id,
pub user_id: Option<String>,
pub parent_id: Option<String>,
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Event` type here
/// or for the `Context` type in [`Event`]
pub origin_event: Event,
}
impl<'py, Event: IntoPyObject<'py>> IntoPyObject<'py> for Context<Event> {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
static HOMEASSISTANT_CORE: OnceCell<Py<PyModule>> = OnceCell::new();
let homeassistant_core = HOMEASSISTANT_CORE
.get_or_try_init(|| Result::<_, PyErr>::Ok(py.import("homeassistant.core")?.unbind()))?
.bind(py);
let context_class = homeassistant_core.getattr("Context")?;
let context_class = context_class.cast_into::<PyType>()?;
let context_instance = context_class.call1((self.user_id, self.parent_id, self.id))?;
context_instance.setattr("origin_event", self.origin_event)?;
Ok(context_instance)
}
}

View File

@@ -4,38 +4,38 @@ use python_utils::{FromPyFromStr, ToStrToPy};
use ulid::Ulid;
#[derive(Debug, Clone, FromPyFromStr, ToStrToPy)]
pub enum Id {
pub enum ContextId {
Ulid(Ulid),
Other(Arc<str>),
}
impl From<String> for Id {
impl From<String> for ContextId {
fn from(s: String) -> Self {
if let Ok(ulid) = s.parse() {
Id::Ulid(ulid)
ContextId::Ulid(ulid)
} else {
Id::Other(s.into())
ContextId::Other(s.into())
}
}
}
impl FromStr for Id {
impl FromStr for ContextId {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(ulid) = s.parse() {
Ok(Id::Ulid(ulid))
Ok(ContextId::Ulid(ulid))
} else {
Ok(Id::Other(s.into()))
Ok(ContextId::Other(s.into()))
}
}
}
impl Display for Id {
impl Display for ContextId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Id::Ulid(ulid) => write!(f, "{ulid}"),
Id::Other(other) => write!(f, "{other}"),
ContextId::Ulid(ulid) => write!(f, "{ulid}"),
ContextId::Other(other) => write!(f, "{other}"),
}
}
}

View File

@@ -1,2 +1,44 @@
pub mod context;
pub mod id;
use once_cell::sync::OnceCell;
use pyo3::{
types::{PyAnyMethods, PyModule, PyType},
Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, Python,
};
mod context_id;
pub use context_id::ContextId;
/// The context that triggered something.
#[derive(Debug, FromPyObject)]
pub struct Context<Event> {
pub id: ContextId,
pub user_id: Option<String>,
pub parent_id: Option<String>,
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Event` type here
/// or for the `Context` type in [`Event`]
pub origin_event: Event,
}
impl<'py, Event: IntoPyObject<'py>> IntoPyObject<'py> for Context<Event> {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
static HOMEASSISTANT_CORE: OnceCell<Py<PyModule>> = OnceCell::new();
let homeassistant_core = HOMEASSISTANT_CORE
.get_or_try_init(|| Result::<_, PyErr>::Ok(py.import("homeassistant.core")?.unbind()))?
.bind(py);
let context_class = homeassistant_core.getattr("Context")?;
let context_class = context_class.cast_into::<PyType>()?;
let context_instance = context_class.call1((self.user_id, self.parent_id, self.id))?;
context_instance.setattr("origin_event", self.origin_event)?;
Ok(context_instance)
}
}

View File

@@ -1,27 +0,0 @@
use chrono::{DateTime, Utc};
use pyo3::prelude::*;
use super::event_origin::EventOrigin;
/// Representation of an event within the bus.
#[derive(Debug, FromPyObject)]
pub struct Event<Type, Data, Context> {
pub event_type: Type,
pub data: Data,
pub origin: EventOrigin,
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Context` type here
/// or for the `Event` type in [`Context`]
pub context: Context,
time_fired_timestamp: f64,
}
impl<Type, Data, Context> Event<Type, Data, Context> {
pub fn time_fired(&self) -> Option<DateTime<Utc>> {
const NANOS_PER_SEC: i32 = 1_000_000_000;
let secs = self.time_fired_timestamp as i64;
let nsecs = (self.time_fired_timestamp.fract() * (NANOS_PER_SEC as f64)) as u32;
DateTime::from_timestamp(secs, nsecs)
}
}

View File

@@ -2,7 +2,8 @@ use std::str::FromStr;
use pyo3::{
exceptions::{PyException, PyTypeError, PyValueError},
prelude::*,
types::PyAnyMethods,
Borrowed, FromPyObject, PyAny, PyErr,
};
use snafu::{ResultExt, Snafu};

View File

@@ -1,4 +1,31 @@
pub mod context;
pub mod event;
pub mod event_origin;
pub mod specific;
use chrono::{DateTime, Utc};
use pyo3::FromPyObject;
pub use event_origin::{EventOrigin, ExtractEventOriginError};
/// Representation of an event within the bus.
#[derive(Debug, FromPyObject)]
pub struct Event<Type, Data, Context> {
pub event_type: Type,
pub data: Data,
pub origin: EventOrigin,
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Context` type here
/// or for the `Event` type in [`Context`]
pub context: Context,
time_fired_timestamp: f64,
}
impl<Type, Data, Context> Event<Type, Data, Context> {
pub fn time_fired(&self) -> Option<DateTime<Utc>> {
const NANOS_PER_SEC: i32 = 1_000_000_000;
let secs = self.time_fired_timestamp as i64;
let nsecs = (self.time_fired_timestamp.fract() * (NANOS_PER_SEC as f64)) as u32;
DateTime::from_timestamp(secs, nsecs)
}
}

View File

@@ -1,9 +1,5 @@
use std::fmt::Display;
use std::str::FromStr;
use pyo3::FromPyObject;
use python_utils::{FromPyFromStr, ToStrToPy};
use snafu::Snafu;
use string_literal::StringLiteral;
use crate::{entity_id::EntityId, state_object::StateObject};
@@ -36,7 +32,7 @@ pub type Event<
NewAttributes,
NewStateContextEvent,
Context,
> = super::super::event::Event<
> = super::super::Event<
Type,
Data<
OldState,

View File

@@ -1,7 +1,8 @@
use std::convert::Infallible;
use pyo3::prelude::*;
use std::{convert::Infallible, sync::Arc};
use pyo3::{
types::PyAnyMethods as _, Borrowed, Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, Python,
};
use python_utils::{detach, validate_type_by_name, TypeByNameValidationError};
use snafu::{ResultExt, Snafu};
@@ -32,22 +33,26 @@ impl<'py> IntoPyObject<'py> for &HomeAssistant {
}
}
#[derive(Debug, Snafu)]
#[derive(Debug, Clone, Snafu)]
pub enum GetStatesError {
/// couldn't get the `states` attribute on the Home Assistant object
GetStatesAttributeError { source: PyErr },
GetStatesAttributeError { source: Arc<PyErr> },
/// couldn't extract the `states` as a [`StateMachine`]
ExtractStateMachineError { source: TypeByNameValidationError },
ExtractStateMachineError {
source: Arc<TypeByNameValidationError>,
},
}
#[derive(Debug, Snafu)]
#[derive(Debug, Clone, Snafu)]
pub enum GetServicesError {
/// couldn't get the `services` attribute on the Home Assistant object
GetServicesAttributeError { source: PyErr },
GetServicesAttributeError { source: Arc<PyErr> },
/// couldn't extract the `states` as a [`ServiceRegistry`]
ExtractServiceRegistryError { source: TypeByNameValidationError },
ExtractServiceRegistryError {
source: Arc<TypeByNameValidationError>,
},
}
impl HomeAssistant {
@@ -73,15 +78,23 @@ impl HomeAssistant {
let states = self
.0
.getattr(py, "states")
.map_err(Arc::new)
.context(GetStatesAttributeSnafu)?;
states.extract(py).context(ExtractStateMachineSnafu)
states
.extract(py)
.map_err(Arc::new)
.context(ExtractStateMachineSnafu)
}
pub fn services(&self, py: Python<'_>) -> Result<ServiceRegistry, GetServicesError> {
let services = self
.0
.getattr(py, "services")
.map_err(Arc::new)
.context(GetServicesAttributeSnafu)?;
services.extract(py).context(ExtractServiceRegistrySnafu)
services
.extract(py)
.map_err(Arc::new)
.context(ExtractServiceRegistrySnafu)
}
}

View File

@@ -0,0 +1,16 @@
use pyo3::FromPyObject;
use super::InputNumberMode;
#[derive(Debug, FromPyObject)]
#[pyo3(from_item_all)]
pub struct InputNumberAttributes<Number> {
initial: Option<Number>,
editable: bool,
min: Number,
max: Number,
step: Number,
mode: InputNumberMode,
// todo: CustomUnitOfMeasurement type? probably not?
unit_of_measurement: Option<String>,
}

View File

@@ -1 +1,75 @@
use std::{future::Future, str::FromStr, sync::Arc};
use emitter_and_signal::{Signal, SignalExt};
use pyo3::{FromPyObject, Py, PyAny, PyErr, Python};
use snafu::{ResultExt, Snafu};
use crate::{
domain::Domain,
entity_id::EntityId,
home_assistant::HomeAssistant,
object_id::ObjectId,
state::HomeAssistantState,
state_object::{self, StateObject, StateObjectSignalError},
};
mod attributes;
mod mode;
pub use attributes::InputNumberAttributes;
pub use mode::InputNumberMode;
#[derive(Debug, Clone, Snafu)]
pub enum CreateSignalError {
/// couldn't get the underlying state object signal
StateObjectSignalError {
source: state_object::CreateSignalError,
},
/// couldn't map the state object to a power value
MappedSignalError {
source: emitter_and_signal::ProducerExited,
},
}
pub fn signal<
'py,
Number: 'static + uom::num::Num + Clone + Send + Sync + FromStr + for<'a, 'py2> FromPyObject<'a, 'py2>,
>(
py: Python<'py>,
home_assistant: &'py HomeAssistant,
object_id: ObjectId,
) -> Result<
(
Signal<Option<Arc<Result<HomeAssistantState<Number>, StateObjectSignalError<Arc<PyErr>>>>>>,
impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>,
),
CreateSignalError,
> {
let entity_id = EntityId(Domain::InputNumber, object_id);
let (signal, task1) = StateObject::<
HomeAssistantState<Number>,
InputNumberAttributes<Number>,
Py<PyAny>,
>::signal(py, home_assistant, entity_id)
.context(StateObjectSignalSnafu)?;
let (signal, task2) = signal
.map(|state_object_arc_result_option| {
state_object_arc_result_option.map(|state_object_arc_result| {
Arc::new(
(*state_object_arc_result)
.as_ref()
.map(|state_object| state_object.state.clone())
.map_err(|e| e.clone()),
)
})
})
.context(MappedSignalSnafu)?;
Ok((
signal,
async move { tokio::try_join!(task1, task2).map(|_| ()) },
))
}

View File

@@ -0,0 +1,10 @@
use python_utils::{FromPyFromStr, ToStrToPy};
use strum::EnumString;
#[derive(Debug, Clone, Default, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
#[strum(serialize_all = "snake_case")]
pub enum InputNumberMode {
Box,
#[default]
Slider,
}

View File

@@ -1,4 +1,4 @@
use pyo3::prelude::*;
use pyo3::FromPyObject;
#[derive(Debug, FromPyObject)]
#[pyo3(from_item_all)]

View File

@@ -1,5 +1,7 @@
use std::sync::Arc;
use attributes::LightAttributes;
use pyo3::prelude::*;
use pyo3::{FromPyObject, Py, PyAny, Python};
use snafu::{ResultExt, Snafu};
use state::LightState;
@@ -29,15 +31,15 @@ impl HomeAssistantLight {
}
}
#[derive(Debug, Snafu)]
#[derive(Debug, Clone, Snafu)]
pub enum GetStateObjectError {
/// couldn't get the state machine registry
GetStatesError { source: GetStatesError },
/// this state object exists in the state machine registry, but it couldn't be extracted as a light state object
GetStateError { source: GetStateError<
GetStateError { source: Arc<GetStateError<
<StateObject<HomeAssistantState<LightState>, LightAttributes, Py<PyAny>> as FromPyObject<'static, 'static>>::Error
> },
>> },
/// this entity does not have a state object in the registry
EntityMissing,
@@ -55,6 +57,7 @@ impl HomeAssistantLight {
let entity_id = self.entity_id();
let state_object = states
.get(py, entity_id)
.map_err(Arc::new)
.context(GetStateSnafu)?
.ok_or(GetStateObjectError::EntityMissing)?;

View File

@@ -3,11 +3,11 @@ use super::{GetStateObjectError, HomeAssistantLight};
use crate::home_assistant::GetServicesError;
use crate::service_registry::CallServiceError;
use crate::{
event::context::context::Context,
event::context::Context,
state::{ErrorState, HomeAssistantState, UnexpectedState},
};
use protocol::light::{GetState, SetState};
use pyo3::prelude::*;
use pyo3::Python;
use python_utils::IsNone;
use snafu::{ResultExt, Snafu};
@@ -30,9 +30,7 @@ impl GetState for HomeAssistantLight {
HomeAssistantState::Err(error_state) => {
Err(GetStateError::Error { state: error_state })
}
HomeAssistantState::UnexpectedErr(state) => {
Err(GetStateError::UnexpectedError { state })
}
HomeAssistantState::Unexpected(state) => Err(GetStateError::UnexpectedError { state }),
}
}
}

View File

@@ -1,54 +1,13 @@
use std::str::FromStr;
use pyo3::{
exceptions::{PyException, PyValueError},
prelude::*,
};
use snafu::{ResultExt, Snafu};
use python_utils::{FromPyFromStr, ToStrToPy};
use strum::EnumString;
#[derive(Debug, Clone, EnumString, strum::Display)]
#[derive(Debug, Clone, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
#[strum(serialize_all = "snake_case")]
pub enum LightState {
On,
Off,
}
#[derive(Debug, Snafu)]
pub enum ExtractLightStateError {
/// couldn't extract the object as a string
ExtractStringError { source: PyErr },
/// couldn't parse the string as a [`LightState`]
ParseError {
source: <LightState as FromStr>::Err,
},
}
impl From<ExtractLightStateError> for PyErr {
fn from(error: ExtractLightStateError) -> Self {
match &error {
ExtractLightStateError::ExtractStringError { .. } => {
PyException::new_err(error.to_string())
}
ExtractLightStateError::ParseError { .. } => PyValueError::new_err(error.to_string()),
}
}
}
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
impl<'a, 'py> FromPyObject<'a, 'py> for LightState {
type Error = ExtractLightStateError;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
let s = ob.extract::<&str>().context(ExtractStringSnafu)?;
let state = LightState::from_str(&s).context(ParseSnafu)?;
Ok(state)
}
}
impl From<LightState> for protocol::light::State {
fn from(light_state: LightState) -> Self {
match light_state {

View File

@@ -1,6 +1,9 @@
use arbitrary_value::{arbitrary::Arbitrary, map::Map};
use once_cell::sync::OnceCell;
use pyo3::{prelude::*, types::PyTuple};
use pyo3::{
types::{PyAnyMethods as _, PyModule, PyTuple},
Borrowed, FromPyObject, IntoPyObject, Py, PyAny, PyErr, Python,
};
use python_utils::{detach, validate_type_by_name, TypeByNameValidationError};
#[derive(Debug)]
@@ -54,7 +57,7 @@ pub struct LogData<ExcInfo> {
}
impl HassLogger {
pub fn new(py: Python<'_>, name: &str) -> PyResult<Self> {
pub fn new(py: Python<'_>, name: &str) -> Result<Self, PyErr> {
static LOGGING_MODULE: OnceCell<Py<PyModule>> = OnceCell::new();
let logging_module = LOGGING_MODULE
@@ -71,7 +74,7 @@ impl HassLogger {
msg: &str,
args: Vec<Arbitrary>,
log_data: Option<LogData<ExcInfo>>,
) -> PyResult<()> {
) -> Result<(), PyErr> {
let mut all_args = vec![msg.into_pyobject(py)?.into_any()];
for arg in args {
let arg = arg.into_pyobject(py)?;
@@ -94,7 +97,7 @@ impl HassLogger {
msg: &str,
args: Vec<Arbitrary>,
log_data: Option<LogData<ExcInfo>>,
) -> PyResult<()> {
) -> Result<(), PyErr> {
let mut all_args = vec![msg.into_pyobject(py)?.into_any()];
for arg in args {
let arg = arg.into_pyobject(py)?;
@@ -117,7 +120,7 @@ impl HassLogger {
msg: &str,
args: Vec<Arbitrary>,
log_data: Option<LogData<ExcInfo>>,
) -> PyResult<()> {
) -> Result<(), PyErr> {
let mut all_args = vec![msg.into_pyobject(py)?.into_any()];
for arg in args {
let arg = arg.into_pyobject(py)?;
@@ -141,7 +144,7 @@ impl HassLogger {
msg: &str,
args: Vec<Arbitrary>,
log_data: Option<LogData<ExcInfo>>,
) -> PyResult<()> {
) -> Result<(), PyErr> {
let mut all_args = vec![msg.into_pyobject(py)?.into_any()];
for arg in args {
let arg = arg.into_pyobject(py)?;
@@ -164,7 +167,7 @@ impl HassLogger {
msg: &str,
args: Vec<Arbitrary>,
log_data: Option<LogData<ExcInfo>>,
) -> PyResult<()> {
) -> Result<(), PyErr> {
let mut all_args = vec![msg.into_pyobject(py)?.into_any()];
for arg in args {
let arg = arg.into_pyobject(py)?;

View File

@@ -1,13 +1,11 @@
use std::str::FromStr;
use strum::EnumString;
use crate::{
notify::service::mobile_app::SpecialMessage,
object_id::ObjectId,
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
};
use crate::service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall};
use super::super::{NotifyMobileAppServiceData, NotifyMobileAppServiceDataData};
use super::super::{
MobileAppDeviceId, NotifyMobileAppServiceData, NotifyMobileAppServiceDataData, SpecialMessage,
};
#[derive(Debug, Clone, EnumString, strum::Display)]
#[strum(serialize_all = "snake_case")]
@@ -33,7 +31,7 @@ pub enum Filter {
#[derive(Debug, Clone, typed_builder::TypedBuilder)]
pub struct DoNotDisturb {
pub object_id: ObjectId,
pub device_id: MobileAppDeviceId,
pub filter: Filter,
}
@@ -41,11 +39,11 @@ impl IntoServiceCall for DoNotDisturb {
type ServiceData = NotifyMobileAppServiceData;
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
let DoNotDisturb { object_id, filter } = self;
let DoNotDisturb { device_id, filter } = self;
let service_domain = ServiceDomain::from_str("notify").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{object_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{device_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_data = NotifyMobileAppServiceData::builder()
.message(SpecialMessage::CommandDnd.to_string())

View File

@@ -1,27 +1,23 @@
use std::str::FromStr;
use crate::{
notify::service::mobile_app::SpecialMessage,
object_id::ObjectId,
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
};
use crate::service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall};
use super::super::NotifyMobileAppServiceData;
use super::super::{MobileAppDeviceId, NotifyMobileAppServiceData, SpecialMessage};
#[derive(Debug, Clone, typed_builder::TypedBuilder)]
pub struct RequestLocationUpdate {
pub object_id: ObjectId,
pub device_id: MobileAppDeviceId,
}
impl IntoServiceCall for RequestLocationUpdate {
type ServiceData = NotifyMobileAppServiceData;
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
let RequestLocationUpdate { object_id } = self;
let RequestLocationUpdate { device_id } = self;
let service_domain = ServiceDomain::from_str("notify").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{object_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{device_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_data = NotifyMobileAppServiceData::builder()
.message(SpecialMessage::RequestLocationUpdate.to_string())

View File

@@ -0,0 +1,8 @@
use python_utils::{FromPyFromStr, ToStrToPy};
use crate::slug::Slug;
pub use crate::slug::SlugParsingError as ParseMobileAppDeviceIdError;
#[derive(Debug, Clone, derive_more::FromStr, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct MobileAppDeviceId(pub Slug);

View File

@@ -1,7 +1,7 @@
use std::{convert::Infallible, str::FromStr};
use std::str::FromStr;
use pyo3::{
types::{PyDict, PyDictMethods, PyString},
types::{PyDict, PyDictMethods},
Bound, IntoPyObject, PyErr, Python,
};
use python_utils::{FromPyFromStr, IntoPyObjectViaDisplay, ToStrToPy};
@@ -10,10 +10,12 @@ use strum::EnumString;
use url::Url;
pub mod command;
pub mod device_id;
pub mod standard;
pub mod text_to_speech;
pub use command::*;
pub use device_id::MobileAppDeviceId;
pub use standard::StandardNotification;
pub use text_to_speech::TextToSpeech;

View File

@@ -2,19 +2,16 @@ use std::str::FromStr;
use mitsein::vec1::Vec1;
use crate::{
object_id::ObjectId,
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
};
use crate::service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall};
use super::{
Action, NonSpecialMessage, NotifyMobileAppServiceData, NotifyMobileAppServiceDataData,
Visibility,
Action, MobileAppDeviceId, NonSpecialMessage, NotifyMobileAppServiceData,
NotifyMobileAppServiceDataData, Visibility,
};
#[derive(Debug, Clone, typed_builder::TypedBuilder)]
pub struct StandardNotification {
pub object_id: ObjectId,
pub device_id: MobileAppDeviceId,
#[builder(default, setter(strip_option))]
pub title: Option<String>,
@@ -32,7 +29,7 @@ impl IntoServiceCall for StandardNotification {
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
let StandardNotification {
object_id,
device_id,
title,
message,
actions,
@@ -41,7 +38,7 @@ impl IntoServiceCall for StandardNotification {
let service_domain = ServiceDomain::from_str("notify").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{object_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{device_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_data = NotifyMobileAppServiceData::builder()
.title_option(title)

View File

@@ -1,16 +1,15 @@
use std::str::FromStr;
use crate::{
notify::service::mobile_app::SpecialMessage,
object_id::ObjectId,
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
};
use crate::service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall};
use super::{MediaStream, NotifyMobileAppServiceData, NotifyMobileAppServiceDataData};
use super::{
MediaStream, MobileAppDeviceId, NotifyMobileAppServiceData, NotifyMobileAppServiceDataData,
SpecialMessage,
};
#[derive(Debug, Clone, typed_builder::TypedBuilder)]
pub struct TextToSpeech {
pub object_id: ObjectId,
pub device_id: MobileAppDeviceId,
pub message: String,
pub media_stream: Option<MediaStream>,
@@ -21,14 +20,14 @@ impl IntoServiceCall for TextToSpeech {
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
let TextToSpeech {
object_id,
device_id,
message,
media_stream,
} = self;
let service_domain = ServiceDomain::from_str("notify").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{object_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_id = ServiceId::from_str(&format!("mobile_app_{device_id}")).expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
let service_data = NotifyMobileAppServiceData::builder()
.message(SpecialMessage::Tts.to_string())

View File

@@ -1,21 +1,8 @@
use std::convert::Infallible;
use pyo3::{prelude::*, types::PyString};
use python_utils::{FromPyFromStr, ToStrToPy};
use super::slug::Slug;
pub use super::slug::SlugParsingError as ObjectIdParsingError;
#[derive(Debug, Clone, derive_more::Display, derive_more::FromStr)]
#[derive(Debug, Clone, derive_more::FromStr, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct ObjectId(pub Slug);
impl<'py> IntoPyObject<'py> for ObjectId {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}

View File

@@ -1 +1 @@
mod power;
pub mod power;

View File

@@ -1,10 +1,19 @@
use std::{future::Future, sync::Arc};
use std::{future::Future, str::FromStr, sync::Arc};
use emitter_and_signal::{Signal, SignalExt};
use pyo3::{FromPyObject, Py, PyAny, PyErr, Python};
use python_utils::{FromPyFromStr, FromPyObjectViaParse, ToStrToPy};
use python_utils::{FromPyFromStr, ToStrToPy};
use snafu::{ResultExt, Snafu};
use string_literal::StringLiteral;
use uom::{
si::{
energy::btu,
power::{gigawatt, kilowatt, megawatt, milliwatt, terawatt, watt},
time::hour,
Units, SI,
},
Conversion,
};
use super::super::state_classes::measurement::Measurement;
use crate::{
@@ -12,6 +21,7 @@ use crate::{
entity_id::EntityId,
home_assistant::HomeAssistant,
object_id::ObjectId,
state::HomeAssistantState,
state_object::{self, StateObject, StateObjectSignalError},
unit_of_measurement::power::UnitOfMeasurement,
};
@@ -37,25 +47,45 @@ pub enum CreateSignalError {
/// couldn't map the state object to a power value
MappedSignalError {
source: emitter_and_signal::signal_ext::ProducerAlreadyExited,
source: emitter_and_signal::ProducerExited,
},
}
pub fn signal<'py>(
pub fn signal<'py, Number>(
py: Python<'py>,
home_assistant: &'py HomeAssistant,
object_id: ObjectId,
) -> Result<
(
Signal<Option<Arc<Result<uom::si::f64::Power, StateObjectSignalError<PyErr>>>>>,
Signal<
Option<
Result<
HomeAssistantState<uom::si::quantities::Power<Number>>,
StateObjectSignalError<Arc<PyErr>>,
>,
>,
>,
impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>,
),
CreateSignalError,
> {
>
where
Number: 'static + uom::num::Num + Clone + Send + Sync + FromStr,
Number: uom::num::Num + uom::Conversion<Number, T = Number>,
milliwatt: Conversion<Number, T = Number>,
watt: Conversion<Number, T = Number>,
kilowatt: Conversion<Number, T = Number>,
megawatt: Conversion<Number, T = Number>,
gigawatt: Conversion<Number, T = Number>,
terawatt: Conversion<Number, T = Number>,
btu: Conversion<Number, T = Number>,
hour: Conversion<Number, T = Number>,
SI<Number>: Units<Number>,
{
let entity_id = EntityId(Domain::Sensor, object_id);
let (signal, task1) =
StateObject::<FromPyObjectViaParse<f64>, PowerSensorAttributes, Py<PyAny>>::signal(
StateObject::<HomeAssistantState<Number>, PowerSensorAttributes, Py<PyAny>>::signal(
py,
home_assistant,
entity_id,
@@ -65,18 +95,17 @@ pub fn signal<'py>(
let (signal, task2) = signal
.map(|state_object_result_option| {
state_object_result_option.map(|state_object_result| {
Arc::new(
Result::as_ref(&state_object_result)
.map(|state_object| {
let amount = state_object.state.0;
let unit_of_measurement = state_object.attributes.unit_of_measurement;
let power = unit_of_measurement.into_uom(amount);
power
.map(
|StateObject {
state, attributes, ..
}| {
state.as_ref().map(|amount| {
attributes.unit_of_measurement.into_uom(amount.clone())
})
.map_err(|e| todo!()),
},
)
.map_err(Clone::clone)
})
})
.context(MappedSignalSnafu)?;

View File

@@ -1,21 +1,8 @@
use std::convert::Infallible;
use pyo3::{prelude::*, types::PyString};
use python_utils::{FromPyFromStr, ToStrToPy};
use super::super::slug::Slug;
pub use super::super::slug::SlugParsingError as ServiceDomainParsingError;
#[derive(Debug, Clone, derive_more::Display, derive_more::FromStr)]
#[derive(Debug, Clone, derive_more::FromStr, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct ServiceDomain(pub Slug);
impl<'py> IntoPyObject<'py> for ServiceDomain {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}

View File

@@ -1,21 +1,8 @@
use std::convert::Infallible;
use pyo3::{prelude::*, types::PyString};
use python_utils::{FromPyFromStr, ToStrToPy};
use super::super::slug::Slug;
pub use super::super::slug::SlugParsingError as ServiceIdParsingError;
#[derive(Debug, Clone, derive_more::Display, derive_more::FromStr)]
#[derive(Debug, Clone, derive_more::FromStr, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct ServiceId(pub Slug);
impl<'py> IntoPyObject<'py> for ServiceId {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}

View File

@@ -1,7 +1,9 @@
use super::{event::context::context::Context, service::IntoServiceCall};
use super::{event::context::Context, service::IntoServiceCall};
use pyo3::{
conversion::FromPyObjectOwned,
exceptions::{PyException, PyTypeError},
prelude::*,
types::PyAnyMethods as _,
Borrowed, FromPyObject, IntoPyObject, Py, PyAny, PyErr, Python,
};
use python_utils::{detach, validate_type_by_name, TypeByNameValidationError};
use snafu::{ResultExt, Snafu};
@@ -73,7 +75,7 @@ impl ServiceRegistry {
return_response,
);
let future = Python::attach::<_, PyResult<_>>(|py| {
let future = Python::attach::<_, Result<_, PyErr>>(|py| {
let service_registry = self.0.bind(py);
let awaitable = service_registry.call_method("async_call", args, None)?;
pyo3_async_runtimes::tokio::into_future(awaitable)

View File

@@ -7,8 +7,8 @@ use snafu::Snafu;
#[derive(Debug, Clone, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct Slug(Arc<str>);
/// expected a lowercase ASCII alphabetical character (i.e. a through z) or a digit (i.e. 0 through 9) or an underscore (i.e. _) but encountered {encountered}
#[derive(Debug, Clone, Snafu)]
#[snafu(display("expected a lowercase ASCII alphabetical character (i.e. a through z) or a digit (i.e. 0 through 9) or an underscore (i.e. _) but encountered {encountered}"))]
pub struct SlugParsingError {
encountered: char,
}

View File

@@ -4,7 +4,7 @@ use strum::EnumString;
/// A state in Home Assistant that is known to represent an error of some kind:
/// * `unavailable` (the device is likely offline or unreachable from the Home Assistant instance)
/// * `unknown` (I don't know how to explain this one)
#[derive(Debug, Clone, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
#[derive(Debug, Clone, Copy, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
#[strum(serialize_all = "snake_case")]
pub enum ErrorState {
Unavailable,

View File

@@ -12,7 +12,7 @@ pub use unexpected_state::UnexpectedState;
pub enum HomeAssistantState<State> {
Ok(State),
Err(ErrorState),
UnexpectedErr(UnexpectedState),
Unexpected(UnexpectedState),
}
impl<State> From<String> for HomeAssistantState<State>
@@ -28,7 +28,7 @@ where
return HomeAssistantState::Err(error);
}
HomeAssistantState::UnexpectedErr(UnexpectedState(s.into()))
HomeAssistantState::Unexpected(UnexpectedState(s.into()))
}
}
@@ -44,6 +44,45 @@ impl<State: FromStr> FromStr for HomeAssistantState<State> {
return Ok(HomeAssistantState::Err(error));
}
Ok(HomeAssistantState::UnexpectedErr(UnexpectedState(s.into())))
Ok(HomeAssistantState::Unexpected(UnexpectedState(s.into())))
}
}
impl<State> HomeAssistantState<State> {
pub fn as_ref(&self) -> HomeAssistantState<&State> {
match self {
HomeAssistantState::Ok(state) => HomeAssistantState::Ok(state),
HomeAssistantState::Err(error_state) => HomeAssistantState::Err(*error_state),
HomeAssistantState::Unexpected(unexpected_state) => {
// TODO: just considered cheap enough to clone implicitly
HomeAssistantState::Unexpected(unexpected_state.clone())
}
}
}
pub fn map<Mapped>(self, f: impl FnOnce(State) -> Mapped) -> HomeAssistantState<Mapped> {
match self {
HomeAssistantState::Ok(state) => HomeAssistantState::Ok(f(state)),
HomeAssistantState::Err(error_state) => HomeAssistantState::Err(error_state),
HomeAssistantState::Unexpected(unexpected_state) => {
HomeAssistantState::Unexpected(unexpected_state)
}
}
}
}
impl<State> HomeAssistantState<&State> {
pub fn cloned(self) -> HomeAssistantState<State>
where
State: Clone,
{
self.map(Clone::clone)
}
pub fn copied(self) -> HomeAssistantState<State>
where
State: Copy,
{
self.map(|&s| s)
}
}

View File

@@ -1,35 +1,14 @@
use std::sync::Arc;
use std::{convert::Infallible, str::FromStr, sync::Arc};
use pyo3::{exceptions::PyException, prelude::*};
use snafu::{ResultExt, Snafu};
use python_utils::{FromPyFromStr, ToStrToPy};
#[derive(Debug, Clone, derive_more::Display)]
#[derive(Debug, Clone, derive_more::Display, FromPyFromStr, ToStrToPy)]
pub struct UnexpectedState(pub Arc<str>);
#[derive(Debug, Snafu)]
pub enum ExtractUnexpectedStateError {
/// couldn't extract the object as a string
ExtractStringError { source: PyErr },
}
impl FromStr for UnexpectedState {
type Err = Infallible;
impl From<ExtractUnexpectedStateError> for PyErr {
fn from(error: ExtractUnexpectedStateError) -> Self {
match &error {
ExtractUnexpectedStateError::ExtractStringError { .. } => {
PyException::new_err(error.to_string())
}
}
}
}
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
impl<'a, 'py> FromPyObject<'a, 'py> for UnexpectedState {
type Error = ExtractUnexpectedStateError;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
let s = ob.extract::<String>().context(ExtractStringSnafu)?;
let s = s.into();
Ok(UnexpectedState(s))
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.into()))
}
}

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use super::entity_id::EntityId;
use super::state_object::StateObject;
use pyo3::prelude::*;
use pyo3::{conversion::FromPyObjectOwned, Borrowed, FromPyObject, Py, PyAny, PyErr, Python};
use python_utils::{detach, validate_type_by_name, TypeByNameValidationError};
use snafu::{ResultExt, Snafu};
@@ -53,6 +53,7 @@ impl StateMachine {
.call_method1(py, "get", args)
.map_err(Arc::new)
.context(GetStateObjectSnafu)?;
Ok(state.extract(py).context(ExtractStateObjectSnafu)?)
state.extract(py).context(ExtractStateObjectSnafu)
}
}

View File

@@ -1,5 +1,5 @@
use super::{
event::{context::context::Context, specific::state_changed},
event::{context::Context, specific::state_changed},
home_assistant::HomeAssistant,
};
use crate::{entity_id::EntityId, home_assistant::GetStatesError, state_machine::GetStateError};
@@ -7,8 +7,8 @@ use chrono::{DateTime, Utc};
use emitter_and_signal::signal::Signal;
use once_cell::sync::OnceCell;
use pyo3::{
prelude::*,
types::{PyCFunction, PyDict, PyTuple},
types::{PyAnyMethods as _, PyCFunction, PyDict, PyModule, PyTuple},
Bound, FromPyObject, IntoPyObject as _, Py, PyAny, PyErr, Python,
};
use snafu::{ResultExt, Snafu};
use std::{future::Future, sync::Arc};
@@ -28,7 +28,7 @@ pub struct StateObject<State, Attributes, ContextEvent> {
pub type ExtractStateObjectError<'a, 'py, State, Attributes, ContextEvent> =
<StateObject<State, Attributes, ContextEvent> as FromPyObject<'a, 'py>>::Error;
#[derive(Debug, Snafu)]
#[derive(Debug, Clone, Snafu)]
pub enum CreateSignalError {
/// couldn't get the state machine from the Home Assistant object
GetStatesError { source: GetStatesError },
@@ -62,7 +62,7 @@ impl<
Arc<
Result<
Self,
StateObjectSignalError<<Self as FromPyObject<'a, 'py>>::Error>,
StateObjectSignalError<Arc<<Self as FromPyObject<'a, 'py>>::Error>>,
>,
>,
>,
@@ -74,6 +74,16 @@ impl<
let state_machine = home_assistant.states(py).context(GetStatesSnafu)?;
let current = state_machine
.get(py, entity_id.clone())
.map_err(|e| match e {
GetStateError::GetStateObjectError { source } => {
GetStateError::GetStateObjectError { source }
}
GetStateError::ExtractStateObjectError { source } => {
GetStateError::ExtractStateObjectError {
source: Arc::new(source),
}
}
})
.context(GetFromStateMachineSnafu)
.transpose();
@@ -86,7 +96,7 @@ impl<
while let Some(publisher) = publisher_stream.wait().await {
let (new_state_sender, mut new_state_receiver) = mpsc::channel(8);
let untrack = Python::attach::<_, PyResult<_>>(|py| {
let untrack = Python::attach::<_, Result<_, PyErr>>(|py| {
static EVENT_MODULE: OnceCell<Py<PyModule>> = OnceCell::new();
let event_module = EVENT_MODULE

View File

@@ -1,5 +1,3 @@
use std::str::FromStr;
use python_utils::{FromPyFromStr, ToStrToPy};
use strum::EnumString;
use uom::{
@@ -36,7 +34,7 @@ pub enum UnitOfMeasurement {
impl UnitOfMeasurement {
pub fn into_uom<V>(&self, amount: V) -> Power<V>
where
V: uom::num::Num + uom::Conversion<V, T = V>,
V: uom::num::Num + Conversion<V, T = V>,
milliwatt: Conversion<V, T = V>,
watt: Conversion<V, T = V>,
kilowatt: Conversion<V, T = V>,

View File

@@ -7,7 +7,7 @@ license.workspace = true
[dependencies]
bytes = { workspace = true }
emitter-and-signal = { path = "../emitter-and-signal" }
fjall = "2.11"
fjall = "3"
postcard = { version = "1.1", features = ["use-std"] }
serde = { workspace = true }
snafu = { workspace = true }

View File

@@ -1,6 +1,6 @@
pub use bytes::Bytes;
use emitter_and_signal::signal::{JoinError, Signal};
pub use fjall::{Config, Keyspace, Partition, PartitionCreateOptions};
pub use fjall::{Config, Database, Keyspace, KeyspaceCreateOptions};
use serde::{Deserialize, Serialize};
use snafu::{OptionExt, ResultExt, Snafu};
use std::{fmt::Debug, future::Future, num::NonZeroUsize, ops::Deref, sync::Arc};
@@ -23,7 +23,7 @@ pub enum PersistedError {
}
pub async fn persisted<T: Debug + Send + Sync + 'static + Serialize + for<'a> Deserialize<'a>>(
partition: Partition,
keyspace: Keyspace,
identifier: Bytes,
buffer: NonZeroUsize,
) -> (
@@ -32,9 +32,9 @@ pub async fn persisted<T: Debug + Send + Sync + 'static + Serialize + for<'a> De
impl Future<Output = Result<(), JoinError>>,
) {
let initial = spawn_blocking({
let partition = partition.clone();
let keyspace = keyspace.clone();
let identifier = identifier.clone();
move || partition.get(identifier.deref())
move || keyspace.get(identifier.deref())
})
.await
.unwrap()
@@ -60,9 +60,9 @@ pub async fn persisted<T: Debug + Send + Sync + 'static + Serialize + for<'a> De
// Stand-in for Option::async_and_then
let persisted_res = match serialized_res {
Ok(serialized) => spawn_blocking({
let partition = partition.clone();
let keyspace = keyspace.clone();
let identifier = identifier.clone();
move || partition.insert(identifier.deref(), serialized)
move || keyspace.insert(identifier.deref(), serialized)
}).await.unwrap().context(SavingSnafu),
Err(error) => Err(error),
};

View File

@@ -12,6 +12,7 @@ serde = ["dep:serde"]
deranged = { workspace = true }
derive_more = { workspace = true }
ext-trait = { workspace = true }
futures = { workspace = true }
palette = { workspace = true }
snafu = { workspace = true }
strum = { workspace = true, features = ["derive"] }

View File

@@ -1,7 +1,8 @@
use std::{error::Error, future::Future};
use deranged::RangedU16;
use snafu::{ResultExt, Snafu};
use futures::TryFutureExt as _;
use snafu::{ResultExt as _, Snafu};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, strum::Display, strum::EnumIs,
@@ -44,15 +45,15 @@ pub trait GetState {
#[ext_trait::extension(pub trait IsOff)]
impl<T: GetState> T {
async fn is_off(&self) -> Result<bool, T::Error> {
Ok(self.get_state().await?.is_off())
fn is_off(&self) -> impl Future<Output = Result<bool, T::Error>> + Send {
self.get_state().map_ok(|state| state.is_off())
}
}
#[ext_trait::extension(pub trait IsOn)]
impl<T: GetState> T {
async fn is_on(&self) -> Result<bool, T::Error> {
Ok(self.get_state().await?.is_on())
fn is_on(&self) -> impl Future<Output = Result<bool, T::Error>> + Send {
self.get_state().map_ok(|state| state.is_on())
}
}
@@ -63,15 +64,15 @@ pub trait SetState {
#[ext_trait::extension(pub trait TurnOff)]
impl<T: SetState> T {
async fn turn_off(&mut self) -> Result<(), T::Error> {
self.set_state(State::Off).await
fn turn_off(&mut self) -> impl Future<Output = Result<(), T::Error>> + Send {
self.set_state(State::Off)
}
}
#[ext_trait::extension(pub trait TurnOn)]
impl<T: SetState> T {
async fn turn_on(&mut self) -> Result<(), T::Error> {
self.set_state(State::On).await
fn turn_on(&mut self) -> impl Future<Output = Result<(), T::Error>> + Send {
self.set_state(State::On)
}
}

View File

@@ -1,6 +1,7 @@
use pyo3::{
exceptions::{PyException, PyTypeError},
prelude::*,
types::{PyAnyMethods as _, PyStringMethods, PyTypeMethods as _},
Borrowed, Bound, Py, PyAny, PyErr,
};
use snafu::{ResultExt, Snafu};
@@ -84,5 +85,5 @@ pub fn validate_type_by_name(
});
}
return Ok(());
Ok(())
}

View File

@@ -50,7 +50,6 @@ impl ToTokens for StringLiteral {
}
}
};
dbg!(&output.to_string());
tokens.extend(output);
}

View File

@@ -15,7 +15,7 @@ where
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { actual, expected } = self;
let expected_str = <&'static str>::from(&expected);
let expected_str = <&'static str>::from(expected);
write!(f, "expected {expected_str:?} but got {actual:?}")
}