working client/server comms

This commit is contained in:
Alex Janka 2023-12-28 12:41:05 +11:00
parent af060b8d40
commit 8bef080db9
16 changed files with 849 additions and 35 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target
/test-config
/client/dist

507
Cargo.lock generated
View file

@ -89,12 +89,66 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9"
[[package]]
name = "anymap2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
[[package]]
name = "async-stream"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "async-trait"
version = "0.1.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "atomic"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
[[package]]
name = "atomic"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
dependencies = [
"bytemuck",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -131,6 +185,12 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "binascii"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
name = "bincode"
version = "1.3.3"
@ -176,6 +236,12 @@ version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "bytes"
version = "1.5.0"
@ -256,6 +322,9 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
name = "client"
version = "0.1.0"
dependencies = [
"reqwasm",
"shared-types",
"wasm-bindgen-futures",
"yew",
]
@ -273,7 +342,7 @@ checksum = "79cff32df5cfea75e6484eeff0b4e48ad3977fb6582676a7862b3590dddc7a87"
dependencies = [
"serde",
"serde_json",
"yansi",
"yansi 0.5.1",
]
[[package]]
@ -303,13 +372,24 @@ dependencies = [
"version_check",
]
[[package]]
name = "cookie"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa"
dependencies = [
"cookie",
"cookie 0.16.2",
"idna 0.2.3",
"log",
"publicsuffix",
@ -377,6 +457,39 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "devise"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8"
dependencies = [
"devise_codegen",
"devise_core",
]
[[package]]
name = "devise_codegen"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6"
dependencies = [
"devise_core",
"quote",
]
[[package]]
name = "devise_core"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
dependencies = [
"bitflags 2.4.1",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.43",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -387,6 +500,12 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encoding_rs"
version = "0.8.33"
@ -418,6 +537,20 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "figment"
version = "0.10.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "649f3e5d826594057e9a519626304d8da859ea8a0b18ce99500c586b8d45faee"
dependencies = [
"atomic 0.6.0",
"pear",
"serde",
"toml",
"uncased",
"version_check",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -525,6 +658,19 @@ dependencies = [
"slab",
]
[[package]]
name = "generator"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
dependencies = [
"cc",
"libc",
"log",
"rustversion",
"windows",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@ -554,6 +700,12 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "gloo"
version = "0.8.1"
@ -715,6 +867,26 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2899cb1a13be9020b010967adc6b2a8a343b6f1428b90238c9d53ca24decc6db"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils 0.1.7",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.3.1"
@ -1097,6 +1269,25 @@ dependencies = [
"syn 2.0.43",
]
[[package]]
name = "include_dir"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
dependencies = [
"include_dir_macros",
]
[[package]]
name = "include_dir_macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "indexmap"
version = "2.1.0"
@ -1105,8 +1296,15 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "inlinable_string"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]]
name = "ipnet"
version = "2.9.0"
@ -1179,6 +1377,21 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "loom"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
"serde",
"serde_json",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "matchers"
version = "0.1.0"
@ -1258,6 +1471,26 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "multer"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"httparse",
"log",
"memchr",
"mime",
"spin",
"tokio",
"tokio-util",
"version_check",
]
[[package]]
name = "native-tls"
version = "0.2.11"
@ -1399,6 +1632,29 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "pear"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8"
dependencies = [
"inlinable_string",
"pear_codegen",
"yansi 1.0.0-rc.1",
]
[[package]]
name = "pear_codegen"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e22670e8eb757cff11d6c199ca7b987f352f0346e0be4dd23869ec72cb53c77"
dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.43",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -1494,7 +1750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
"toml_edit 0.19.15",
]
[[package]]
@ -1530,6 +1786,19 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "proc-macro2-diagnostics"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
"version_check",
"yansi 1.0.0-rc.1",
]
[[package]]
name = "prokio"
version = "0.1.0"
@ -1611,6 +1880,26 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "ref-cast"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53313ec9f12686aeeffb43462c3ac77aa25f590a5f630eb2cde0de59417b29c7"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2566c4bf6845f2c2e83b27043c3f5dfcd5ba8f2937d6c00dc009bfb51a079dc4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "regex"
version = "1.10.2"
@ -1655,6 +1944,15 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwasm"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b89870d729c501fa7a68c43bf4d938bbb3a8c156d333d90faa0e8b3e3212fb"
dependencies = [
"gloo-net 0.1.0",
]
[[package]]
name = "reqwest"
version = "0.11.23"
@ -1663,7 +1961,7 @@ checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
dependencies = [
"base64",
"bytes",
"cookie",
"cookie 0.16.2",
"cookie_store",
"encoding_rs",
"futures-core",
@ -1714,6 +2012,88 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rocket"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
dependencies = [
"async-stream",
"async-trait",
"atomic 0.5.3",
"binascii",
"bytes",
"either",
"figment",
"futures",
"indexmap",
"log",
"memchr",
"multer",
"num_cpus",
"parking_lot",
"pin-project-lite",
"rand",
"ref-cast",
"rocket_codegen",
"rocket_http",
"serde",
"serde_json",
"state",
"tempfile",
"time",
"tokio",
"tokio-stream",
"tokio-util",
"ubyte",
"version_check",
"yansi 1.0.0-rc.1",
]
[[package]]
name = "rocket_codegen"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
dependencies = [
"devise",
"glob",
"indexmap",
"proc-macro2",
"quote",
"rocket_http",
"syn 2.0.43",
"unicode-xid",
"version_check",
]
[[package]]
name = "rocket_http"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
dependencies = [
"cookie 0.18.0",
"either",
"futures",
"http",
"hyper",
"indexmap",
"log",
"memchr",
"pear",
"percent-encoding",
"pin-project-lite",
"ref-cast",
"serde",
"smallvec",
"stable-pattern",
"state",
"time",
"tokio",
"uncased",
]
[[package]]
name = "ron"
version = "0.8.1"
@ -1837,6 +2217,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -1935,6 +2321,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -1967,6 +2362,15 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shared-types"
version = "0.1.0"
dependencies = [
"serde",
"teslatte",
"thiserror",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -2013,6 +2417,24 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "stable-pattern"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045"
dependencies = [
"memchr",
]
[[package]]
name = "state"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
dependencies = [
"loom",
]
[[package]]
name = "strsim"
version = "0.10.0"
@ -2145,9 +2567,13 @@ dependencies = [
name = "tesla-charge-controller"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"include_dir",
"rocket",
"ron",
"serde",
"shared-types",
"teslatte",
"thiserror",
"tokio",
@ -2359,11 +2785,26 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.21.0",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
@ -2376,6 +2817,19 @@ dependencies = [
"winnow",
]
[[package]]
name = "toml_edit"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower-service"
version = "0.3.2"
@ -2455,6 +2909,25 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ubyte"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
dependencies = [
"serde",
]
[[package]]
name = "uncased"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
dependencies = [
"serde",
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.14"
@ -2488,6 +2961,12 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "untrusted"
version = "0.9.0"
@ -2557,6 +3036,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"serde",
"serde_json",
"wasm-bindgen-macro",
]
@ -2654,6 +3135,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-core"
version = "0.51.1"
@ -2820,6 +3310,15 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yansi"
version = "1.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
dependencies = [
"is-terminal",
]
[[package]]
name = "yew"
version = "0.21.0"

View file

@ -1,4 +1,10 @@
[workspace]
members = ["server", "client"]
members = ["server", "client", "shared-types"]
default-members = ["server"]
resolver = "2"
[workspace.dependencies]
shared-types = { path = "./shared-types" }
serde = { version = "1.0", features = ["derive"] }
teslatte = { path = "./vendored/teslatte" }
thiserror = "1.0"

View file

@ -6,4 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
shared-types = { workspace = true }
yew = { version = "0.21", features = ["csr"] }
reqwasm = "0.5"
wasm-bindgen-futures = "0.4"

View file

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<title>Yew App</title>
<title>Tesla Charge Control</title>
</head>
<body></body>

View file

@ -1,20 +1,49 @@
use yew::prelude::*;
const API_URL: &str = if cfg!(debug_assertions) {
"http://localhost:8000"
} else {
""
};
#[function_component]
fn App() -> Html {
let counter = use_state(|| 0);
let counter = use_state(Vec::new);
let onclick = {
let counter = counter.clone();
move |_| {
let value = *counter + 1;
counter.set(value);
let counter = counter.clone();
wasm_bindgen_futures::spawn_local(async move {
counter.set(
match reqwasm::http::Request::get(&format!("{API_URL}/charge-state"))
.send()
.await
{
Ok(response) => match response.json::<shared_types::ChargeState>().await {
Ok(v) => vec![
format!("Battery {}%", v.battery_level),
format!("Range: {:.1}km", v.range_km()),
format!("Charging at {} amps", v.charge_amps),
],
Err(e) => vec![format!("error getting text: {e:#?}")],
},
Err(e) => vec![format!("request error: {e:#?}")],
},
);
})
}
};
let counter = <Vec<std::string::String> as Clone>::clone(&counter);
let text_elements: Vec<yew::virtual_dom::VNode> = counter
.iter()
.map(|text| {
html! {<p>{text}</p>}
})
.collect();
html! {
<div>
<button {onclick}>{ "+1" }</button>
<p>{ *counter }</p>
<button {onclick}>{ "refresh" }</button>
{text_elements}
</div>
}
}

View file

@ -24,9 +24,13 @@ assets = [
]
[dependencies]
shared-types = { workspace = true, features = ["teslatte"] }
clap = { version = "4.0", features = ["derive"] }
ron = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde = { workspace = true }
tokio = { version = "1.35.1", features = ["full"] }
teslatte = { path = "../vendored/teslatte" }
thiserror = "1.0"
teslatte = { workspace = true }
thiserror = { workspace = true }
rocket = { version = "0.5", features = ["json"] }
anyhow = "1.0"
include_dir = "0.7"

3
server/Rocket.toml Normal file
View file

@ -0,0 +1,3 @@
[default.shutdown]
ctrlc = false
signals = ["term", "hup"]

View file

@ -2,15 +2,22 @@ use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::Coords;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Config {
watch_interval: Duration,
pub watch_interval: Duration,
pub coords: Coords,
}
impl Default for Config {
fn default() -> Self {
Self {
watch_interval: Duration::from_secs(60),
coords: Coords {
latitude: 0.,
longitude: 0.,
},
}
}
}

View file

@ -16,6 +16,7 @@ impl SaveError {
}
}
}
#[derive(Error, Debug)]
pub enum AuthLoadError {
#[error("stdio error")]
@ -38,3 +39,15 @@ impl AuthLoadError {
}
}
}
#[derive(Error, Debug)]
pub enum RequestError {
#[error("stdio error")]
StdIo(#[from] std::io::Error),
#[error("ron - spanned error")]
RonSpanned(#[from] ron::error::SpannedError),
#[error("teslatte error")]
Teslatte(#[from] teslatte::error::TeslatteError),
#[error("save error")]
Save(#[from] SaveError),
}

View file

@ -1,15 +1,21 @@
#[macro_use]
extern crate rocket;
use anyhow::Result;
use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, time::Duration};
use std::path::PathBuf;
use teslatte::{
auth::{AccessToken, RefreshToken},
FleetApi, FleetVehicleApi,
FleetApi,
};
use crate::{config::Config, errors::*};
mod config;
mod errors;
mod server;
#[derive(Parser, Debug, Clone)]
#[clap(author, version, about, long_about = None)]
@ -31,8 +37,9 @@ enum Commands {
#[tokio::main]
async fn main() {
let args = Args::parse();
let auth_path = args.config_dir.join("auth");
// let config_path = args.config_dir.join("config");
let config_path = args.config_dir.join("config");
match args.command {
Commands::GenerateConfig => {
@ -43,18 +50,27 @@ async fn main() {
}
Commands::Watch => match get_auth(auth_path).await {
Ok(api) => {
let config: Config =
ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap();
let products = api.products().await;
match products {
Ok(res) => match res.first() {
Some(teslatte::products::Product::Vehicle(vehicle)) => {
api.wake_up(&vehicle.vin).await.unwrap();
loop {
match api.flash_lights(&vehicle.vin).await {
Ok(_r) => println!("flashed"),
Err(e) => println!("error: {e:#?}"),
}
std::thread::sleep(Duration::from_secs(10));
}
// match api.wake_up(&vehicle.vin).await {
// Ok(_r) => {
// println!("woke up! response: {_r:#?}");
// }
// Err(e) => {
// println!("error waking up: {e:#?}");
// }
// }
server::launch_server(server::ServerState {
config,
api,
vehicle: vehicle.clone(),
})
.await;
}
_ => println!("No first item"),
},
@ -66,15 +82,18 @@ async fn main() {
}
}
#[derive(Debug)]
pub enum MaybePostResponse {
None,
PostResponse(teslatte::PostResponse),
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct Coords {
pub latitude: f64,
pub longitude: f64,
}
impl From<teslatte::PostResponse> for MaybePostResponse {
fn from(value: teslatte::PostResponse) -> Self {
MaybePostResponse::PostResponse(value)
const COORD_PRECISION: f64 = 0.001;
impl Coords {
fn overlaps(&self, other: &Coords) -> bool {
(self.latitude - other.latitude).abs() < COORD_PRECISION
&& (self.longitude - other.longitude).abs() < COORD_PRECISION
}
}
@ -84,6 +103,7 @@ async fn get_auth(auth_path: PathBuf) -> Result<FleetApi, AuthLoadError> {
api.refresh().await?;
println!("Refreshed auth key");
save_key(auth_path, &api)?;
// api.print_responses = teslatte::PrintResponses::Pretty;
Ok(api)
}

172
server/src/server/mod.rs Normal file
View file

@ -0,0 +1,172 @@
use std::path::PathBuf;
use anyhow::{Context, Result};
use include_dir::{include_dir, Dir};
use rocket::{
fairing::{Fairing, Info, Kind},
http::{ContentType, Header, Status},
outcome::IntoOutcome,
response::Responder,
route::{Handler, Outcome},
serde::json::Json,
Data, Request, Response, State,
};
use teslatte::{
vehicles::{Endpoint, GetVehicleData, VehicleData},
FleetApi, FleetVehicleApi,
};
use crate::{config::Config, Coords};
pub struct ServerState {
pub config: Config,
pub api: FleetApi,
pub vehicle: Box<VehicleData>,
}
pub async fn launch_server(state: ServerState) {
let _ = rocket(state).launch().await;
}
fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(Cors)
.manage(state)
.mount("/", UiStatic {})
.mount("/", routes![home, charge_state,])
}
static UI_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../client/dist");
#[derive(Clone, Copy, Debug)]
struct UiStatic {}
impl From<UiStatic> for Vec<rocket::Route> {
fn from(server: UiStatic) -> Self {
vec![rocket::Route::ranked(
None,
rocket::http::Method::Get,
"/<path..>",
server,
)]
}
}
#[rocket::async_trait]
impl Handler for UiStatic {
async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r> {
use rocket::http::uri::fmt::Path;
let path = req
.segments::<rocket::http::uri::Segments<'_, Path>>(0..)
.ok()
.and_then(|segments| segments.to_path_buf(true).ok());
println!("path: {path:#?}");
match path {
Some(p) => {
if p.as_os_str() == "" {
let index = UI_DIR.get_file("index.html").map(|v| RawHtml {
data: v.contents().to_vec(),
name: PathBuf::from("index.html"),
});
index.respond_to(req).or_forward((data, Status::NotFound))
} else {
let file = UI_DIR.get_file(&p).map(|v| RawHtml {
data: v.contents().to_vec(),
name: p,
});
file.respond_to(req).or_forward((data, Status::NotFound))
}
}
None => Outcome::forward(data, Status::NotFound),
}
}
}
struct RawHtml {
data: Vec<u8>,
name: PathBuf,
}
impl<'r> Responder<'r, 'static> for RawHtml {
fn respond_to(self, request: &'r Request<'_>) -> rocket::response::Result<'static> {
let mut response = self.data.respond_to(request)?;
if let Some(ext) = self.name.extension() {
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) {
response.set_header(ct);
}
}
Ok(response)
}
}
#[get("/home")]
async fn home(state: &State<ServerState>) -> Option<String> {
let coords = state.get_coords().await.ok()?;
Some(if coords.overlaps(&state.config.coords) {
String::from("At home")
} else {
String::from("Not home")
})
}
#[get("/charge-state")]
async fn charge_state(state: &State<ServerState>) -> Option<Json<shared_types::ChargeState>> {
let charge_state = state.get_charge_state().await.ok()?;
Some(Json(charge_state))
}
impl ServerState {
async fn get_coords(&self) -> Result<Coords> {
let vehicle_data = self
.api
.vehicle_data(&GetVehicleData {
vehicle_id: self.vehicle.id.clone(),
endpoints: vec![Endpoint::LocationData].into(),
})
.await?;
let drive_state = vehicle_data.drive_state.context("no drive state")?;
let latitude = drive_state.latitude.context("no latitude")?;
let longitude = drive_state.longitude.context("no longitude")?;
Ok(Coords {
latitude,
longitude,
})
}
async fn get_charge_state(&self) -> Result<shared_types::ChargeState> {
let vehicle_data = self
.api
.vehicle_data(&GetVehicleData {
vehicle_id: self.vehicle.id.clone(),
endpoints: vec![Endpoint::ChargeState].into(),
})
.await?;
let charge_state = vehicle_data.charge_state.context("no drive state")?;
Ok(charge_state.try_into()?)
}
}
pub struct Cors;
#[rocket::async_trait]
impl Fairing for Cors {
fn info(&self) -> Info {
Info {
name: "Add CORS headers to responses",
kind: Kind::Response,
}
}
async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
response.set_header(Header::new(
"Access-Control-Allow-Methods",
"POST, GET, PATCH, OPTIONS",
));
response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
}
}

13
shared-types/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "shared-types"
version = "0.1.0"
edition = "2021"
[features]
default = []
teslatte = ["dep:teslatte"]
[dependencies]
serde = { workspace = true }
teslatte = { workspace = true, optional = true }
thiserror = { workspace = true }

20
shared-types/src/lib.rs Normal file
View file

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
#[cfg(feature = "teslatte")]
mod teslatte_impls;
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct ChargeState {
pub battery_level: i64,
pub battery_range: f64,
pub charge_amps: i64,
pub charge_current_request: i64,
pub charge_current_request_max: i64,
pub charge_enable_request: bool,
}
impl ChargeState {
pub fn range_km(&self) -> f64 {
self.battery_range * 1.60934
}
}

View file

@ -0,0 +1,24 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConvertError {
#[error("Error")]
Error,
}
impl TryFrom<teslatte::vehicles::ChargeState> for crate::ChargeState {
type Error = ConvertError;
fn try_from(
value: teslatte::vehicles::ChargeState,
) -> std::prelude::v1::Result<Self, Self::Error> {
Ok(crate::ChargeState {
battery_level: value.battery_level,
battery_range: value.battery_range,
charge_amps: value.charge_amps,
charge_current_request: value.charge_current_request,
charge_current_request_max: value.charge_current_request_max,
charge_enable_request: value.charge_enable_request,
})
}
}

@ -1 +1 @@
Subproject commit 22bfecd8138ecacfe171255244360917f77ddb00
Subproject commit d1c1488d7d666563520f14bd023f9ff78e425860