commit 1756e1f91977521b04869a227d862cad02aa2d2b Author: Markus Zehnder Date: Fri Jul 25 17:31:37 2025 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..582689d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/out +/sys_img +/target +.DS_Store +*.bak +Cargo.lock.tmp diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/copyright/MIT_or_Apache_2_0.xml b/.idea/copyright/MIT_or_Apache_2_0.xml new file mode 100644 index 0000000..04a8eaf --- /dev/null +++ b/.idea/copyright/MIT_or_Apache_2_0.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..5c77487 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..615e796 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dd8986a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,26 @@ + + + + + + + + + Pylint + + + Python + + + + + Pylint + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..cf90e98 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_demo.xml b/.idea/runConfigurations/Run_demo.xml new file mode 100644 index 0000000..1a7cfcd --- /dev/null +++ b/.idea/runConfigurations/Run_demo.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_release_demo.xml b/.idea/runConfigurations/Run_release_demo.xml new file mode 100644 index 0000000..baf8ca9 --- /dev/null +++ b/.idea/runConfigurations/Run_release_demo.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/clippy.xml b/.idea/runConfigurations/clippy.xml new file mode 100644 index 0000000..213e339 --- /dev/null +++ b/.idea/runConfigurations/clippy.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AOOstar-rs.iml b/AOOstar-rs.iml new file mode 100644 index 0000000..2fecef3 --- /dev/null +++ b/AOOstar-rs.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0032054 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1753 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "aoostar-rs" +version = "0.1.0" +dependencies = [ + "ab_glyph", + "anyhow", + "bytes", + "clap", + "env_logger", + "image", + "imageproc", + "log", + "once_cell", + "serde", + "serde_json", + "serde_repr", + "serialport", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" + +[[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.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clap" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imageproc" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2393fb7808960751a52e8a154f67e7dd3f8a2ef9bd80d1553078a7b4e8ed3f0d" +dependencies = [ + "ab_glyph", + "approx", + "getrandom 0.2.16", + "image", + "itertools", + "nalgebra", + "num", + "rand", + "rand_distr", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libudev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "nalgebra" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +dependencies = [ + "approx", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[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.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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 = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serialport" +version = "4.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "core-foundation", + "core-foundation-sys", + "io-kit-sys", + "libudev", + "mach2", + "nix", + "scopeguard", + "unescaper", + "winapi", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unescaper" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" +dependencies = [ + "thiserror 2.0.12", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..de78294 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "aoostar-rs" +version = "0.1.0" +edition = "2024" +rust-version = "1.88" +authors = ["Markus Zehnder"] +license = "MIT or Apache-2.0" + +[profile.release] +strip = true # Automatically strip symbols from the binary. + +[[bin]] +name = "asterctl" +path = "src/main.rs" + +[dependencies] +anyhow = "1.0.98" +bytes = "1.10.1" +clap = { version = "4.5.41", features = ["derive"] } +serialport = "4.7.2" +image = "0.25.6" +imageproc = { version = "0.25.0", default-features = false } +ab_glyph = { version = "0.2.23", default-features = false, features = ["std"] } +log = "0.4.27" +env_logger = "0.11.8" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.141" +serde_repr = "0.1.20" +once_cell = "1.21.3" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..59f49a9 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +MIT License + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Monitor3.json b/Monitor3.json new file mode 100644 index 0000000..d578961 --- /dev/null +++ b/Monitor3.json @@ -0,0 +1,3767 @@ +{ + "setup": { + "type": 1, + "offDisplay": true, + "controlParams": true, + "controlDiskTemp": true, + "customPanel": false, + "language": 1, + "switchTime": "30", + "operationMode": 0, + "theme": 0, + "diskUpdate": 300, + "ha_url": "", + "ha_token": "", + "refresh": 1 + }, + "mianban": [ + 1, + 2 + ], + "diy": [ + { + "type": 5, + "img": "default_1_index.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "CPU温度", + "label": "cpu_temperature", + "x": 195, + "y": 110, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 120, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "CPU占用", + "label": "cpu_percent", + "x": 200, + "y": 285, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 60, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "%", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM占用", + "label": "memory_usage", + "x": 560, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM温度", + "label": "memory_Temperature", + "x": 560, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "tt hh:mm:ss", + "label": "DATE_m_d_h_m_2", + "x": 515, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "下午 06:08:08", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "External IP Address", + "label": "net_ip_address", + "x": 515, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "146.56.182.244", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU占用", + "label": "gpu_core", + "x": 852, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU温度", + "label": "gpu_temperature", + "x": 852, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络上传", + "label": "net_upload_speed", + "x": 852, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "100 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络下载", + "label": "net_download_speed", + "x": 852, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "120 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_1_hdd.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "主板温度", + "label": "motherboard_temperature", + "x": 315, + "y": 105, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 40, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "right", + "integerDigits": -1, + "decimalDigits": 1, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态1", + "label": "storage_ssd[0]['temperature']", + "x": 265, + "y": 181, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态1-进度条", + "label": "storage_ssd[0]['used']", + "x": 50, + "y": 157, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态2", + "label": "storage_ssd[1]['temperature']", + "x": 265, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态2-进度条", + "label": "storage_ssd[1]['used']", + "x": 50, + "y": 220, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态3", + "label": "storage_ssd[2]['temperature']", + "x": 265, + "y": 306, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态3-进度条", + "label": "storage_ssd[2]['used']", + "x": 50, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态4", + "label": "storage_ssd[3]['temperature']", + "x": 580, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态4-进度条", + "label": "storage_ssd[3]['used']", + "x": 400, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态5", + "label": "storage_ssd[4]['temperature']", + "x": 580, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态5-进度条", + "label": "storage_ssd[4]['used']", + "x": 400, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘1", + "label": "storage_hdd[0]['temperature']", + "x": 580, + "y": 247, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘1-进度条", + "label": "storage_hdd[0]['used']", + "x": 400, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘2", + "label": "storage_hdd[1]['temperature']", + "x": 580, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘2-进度条", + "label": "storage_hdd[1]['used']", + "x": 400, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘3", + "label": "storage_hdd[2]['temperature']", + "x": 870, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘3-进度条", + "label": "storage_hdd[2]['used']", + "x": 691, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘4", + "label": "storage_hdd[3]['temperature']", + "x": 870, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘4-进度条", + "label": "storage_hdd[3]['used']", + "x": 691, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘5", + "label": "storage_hdd[4]['temperature']", + "x": 870, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘5-进度条", + "label": "storage_hdd[4]['used']", + "x": 691, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘6", + "label": "storage_hdd[5]['temperature']", + "x": 870, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘6-进度条", + "label": "storage_hdd[5]['used']", + "x": 691, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_2_index.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "CPU温度", + "label": "cpu_temperature", + "x": 195, + "y": 110, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 120, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "CPU占用", + "label": "cpu_percent", + "x": 200, + "y": 285, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 60, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "%", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM占用", + "label": "memory_usage", + "x": 560, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM温度", + "label": "memory_Temperature", + "x": 560, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "tt hh:mm:ss", + "label": "DATE_m_d_h_m_2", + "x": 515, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "下午 06:08:08", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "External IP Address", + "label": "net_ip_address", + "x": 515, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "146.56.182.244", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU占用", + "label": "gpu_core", + "x": 852, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU温度", + "label": "gpu_temperature", + "x": 852, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络上传", + "label": "net_upload_speed", + "x": 852, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "100 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络下载", + "label": "net_download_speed", + "x": 852, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "120 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_2_hdd.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "主板温度", + "label": "motherboard_temperature", + "x": 315, + "y": 105, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 40, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "right", + "integerDigits": -1, + "decimalDigits": 1, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态1", + "label": "storage_ssd[0]['temperature']", + "x": 265, + "y": 181, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态1-进度条", + "label": "storage_ssd[0]['used']", + "x": 50, + "y": 157, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态2", + "label": "storage_ssd[1]['temperature']", + "x": 265, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态2-进度条", + "label": "storage_ssd[1]['used']", + "x": 50, + "y": 220, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态3", + "label": "storage_ssd[2]['temperature']", + "x": 265, + "y": 306, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态3-进度条", + "label": "storage_ssd[2]['used']", + "x": 50, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态4", + "label": "storage_ssd[3]['temperature']", + "x": 580, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态4-进度条", + "label": "storage_ssd[3]['used']", + "x": 400, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态5", + "label": "storage_ssd[4]['temperature']", + "x": 580, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态5-进度条", + "label": "storage_ssd[4]['used']", + "x": 400, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘1", + "label": "storage_hdd[0]['temperature']", + "x": 580, + "y": 247, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘1-进度条", + "label": "storage_hdd[0]['used']", + "x": 400, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘2", + "label": "storage_hdd[1]['temperature']", + "x": 580, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘2-进度条", + "label": "storage_hdd[1]['used']", + "x": 400, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘3", + "label": "storage_hdd[2]['temperature']", + "x": 870, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘3-进度条", + "label": "storage_hdd[2]['used']", + "x": 691, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘4", + "label": "storage_hdd[3]['temperature']", + "x": 870, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘4-进度条", + "label": "storage_hdd[3]['used']", + "x": 691, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘5", + "label": "storage_hdd[4]['temperature']", + "x": 870, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘5-进度条", + "label": "storage_hdd[4]['used']", + "x": 691, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘6", + "label": "storage_hdd[5]['temperature']", + "x": 870, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘6-进度条", + "label": "storage_hdd[5]['used']", + "x": 691, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_3_index.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "CPU温度", + "label": "cpu_temperature", + "x": 195, + "y": 110, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 120, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "CPU占用", + "label": "cpu_percent", + "x": 200, + "y": 285, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 60, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "%", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM占用", + "label": "memory_usage", + "x": 560, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM温度", + "label": "memory_Temperature", + "x": 560, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "tt hh:mm:ss", + "label": "DATE_m_d_h_m_2", + "x": 515, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "下午 06:08:08", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "External IP Address", + "label": "net_ip_address", + "x": 515, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "146.56.182.244", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU占用", + "label": "gpu_core", + "x": 852, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU温度", + "label": "gpu_temperature", + "x": 852, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络上传", + "label": "net_upload_speed", + "x": 852, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "100 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络下载", + "label": "net_download_speed", + "x": 852, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "120 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_3_hdd.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "主板温度", + "label": "motherboard_temperature", + "x": 315, + "y": 105, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 40, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "right", + "integerDigits": -1, + "decimalDigits": 1, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态1", + "label": "storage_ssd[0]['temperature']", + "x": 265, + "y": 181, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态1-进度条", + "label": "storage_ssd[0]['used']", + "x": 50, + "y": 157, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态2", + "label": "storage_ssd[1]['temperature']", + "x": 265, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态2-进度条", + "label": "storage_ssd[1]['used']", + "x": 50, + "y": 220, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态3", + "label": "storage_ssd[2]['temperature']", + "x": 265, + "y": 306, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态3-进度条", + "label": "storage_ssd[2]['used']", + "x": 50, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态4", + "label": "storage_ssd[3]['temperature']", + "x": 580, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态4-进度条", + "label": "storage_ssd[3]['used']", + "x": 400, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态5", + "label": "storage_ssd[4]['temperature']", + "x": 580, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态5-进度条", + "label": "storage_ssd[4]['used']", + "x": 400, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘1", + "label": "storage_hdd[0]['temperature']", + "x": 580, + "y": 247, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘1-进度条", + "label": "storage_hdd[0]['used']", + "x": 400, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘2", + "label": "storage_hdd[1]['temperature']", + "x": 580, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘2-进度条", + "label": "storage_hdd[1]['used']", + "x": 400, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘3", + "label": "storage_hdd[2]['temperature']", + "x": 870, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘3-进度条", + "label": "storage_hdd[2]['used']", + "x": 691, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘4", + "label": "storage_hdd[3]['temperature']", + "x": 870, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘4-进度条", + "label": "storage_hdd[3]['used']", + "x": 691, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘5", + "label": "storage_hdd[4]['temperature']", + "x": 870, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘5-进度条", + "label": "storage_hdd[4]['used']", + "x": 691, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘6", + "label": "storage_hdd[5]['temperature']", + "x": 870, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘6-进度条", + "label": "storage_hdd[5]['used']", + "x": 691, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_4_index.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "CPU温度", + "label": "cpu_temperature", + "x": 195, + "y": 110, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 120, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "CPU占用", + "label": "cpu_percent", + "x": 200, + "y": 285, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 60, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "%", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM占用", + "label": "memory_usage", + "x": 560, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "RAM温度", + "label": "memory_Temperature", + "x": 560, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "tt hh:mm:ss", + "label": "DATE_m_d_h_m_2", + "x": 515, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "下午 06:08:08", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "External IP Address", + "label": "net_ip_address", + "x": 515, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "146.56.182.244", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU占用", + "label": "gpu_core", + "x": 852, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " %", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "GPU温度", + "label": "gpu_temperature", + "x": 852, + "y": 131, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络上传", + "label": "net_upload_speed", + "x": 852, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "100 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 1, + "name": "网络下载", + "label": "net_download_speed", + "x": 852, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "120 K/S", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": -1, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + } + ] + }, + { + "type": 5, + "img": "default_4_hdd.jpg", + "sensor": [ + { + "mode": 1, + "type": 1, + "name": "主板温度", + "label": "motherboard_temperature", + "x": 315, + "y": 105, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "98", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 40, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "right", + "integerDigits": -1, + "decimalDigits": 1, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态1", + "label": "storage_ssd[0]['temperature']", + "x": 265, + "y": 181, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态1-进度条", + "label": "storage_ssd[0]['used']", + "x": 50, + "y": 157, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态2", + "label": "storage_ssd[1]['temperature']", + "x": 265, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态2-进度条", + "label": "storage_ssd[1]['used']", + "x": 50, + "y": 220, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态3", + "label": "storage_ssd[2]['temperature']", + "x": 265, + "y": 306, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态3-进度条", + "label": "storage_ssd[2]['used']", + "x": 50, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress1.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态4", + "label": "storage_ssd[3]['temperature']", + "x": 580, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态4-进度条", + "label": "storage_ssd[3]['used']", + "x": 400, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "固态5", + "label": "storage_ssd[4]['temperature']", + "x": 580, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "固态5-进度条", + "label": "storage_ssd[4]['used']", + "x": 400, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘1", + "label": "storage_hdd[0]['temperature']", + "x": 580, + "y": 247, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘1-进度条", + "label": "storage_hdd[0]['used']", + "x": 400, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘2", + "label": "storage_hdd[1]['temperature']", + "x": 580, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘2-进度条", + "label": "storage_hdd[1]['used']", + "x": 400, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘3", + "label": "storage_hdd[2]['temperature']", + "x": 870, + "y": 70, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘3-进度条", + "label": "storage_hdd[2]['used']", + "x": 691, + "y": 45, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘4", + "label": "storage_hdd[3]['temperature']", + "x": 870, + "y": 130, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘4-进度条", + "label": "storage_hdd[3]['used']", + "x": 691, + "y": 106, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘5", + "label": "storage_hdd[4]['temperature']", + "x": 870, + "y": 246, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘5-进度条", + "label": "storage_hdd[4]['used']", + "x": 691, + "y": 221, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 1, + "type": 2, + "name": "硬盘6", + "label": "storage_hdd[5]['temperature']", + "x": 870, + "y": 307, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": " ℃", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "", + "xz_x": 0, + "xz_y": 0 + }, + { + "mode": 3, + "type": 2, + "name": "硬盘6-进度条", + "label": "storage_hdd[5]['used']", + "x": 691, + "y": 282, + "width": 0, + "height": 0, + "textDirection": 0, + "direction": 1, + "value": "7", + "fontFamily": "HarmonyOS_Sans_SC_Bold", + "fontSize": 24, + "fontColor": -1, + "fontWeight": "normal", + "textAlign": "center", + "integerDigits": -1, + "decimalDigits": 0, + "unit": "", + "minAngle": 0, + "maxAngle": 180, + "minValue": 0, + "maxValue": 100, + "pic": "progress2.png", + "xz_x": 0, + "xz_y": 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0563a7 --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ +# AOOSTAR WTR MAX Screen Control + +Reverse engineering the [AOOSTAR WTR MAX](https://aoostar.com/products/aoostar-wtr-max-amd-r7-pro-8845hs-11-bays-mini-pc) +display protocol, with a proof-of-concept application written in Rust. +This project should also support the GEM12+ PRO device. + +**Disclaimer: ‼️ EXPERIMENTAL — use at your own risk ‼️** + +> I take no responsibility for the use of this software. +> There is no official documentation available; +> all display control commands have been reverse engineered from the original AOOSTAR-X software. + +- It may or may not work. +- It could crash the display firmware, requiring a power cycle. +- It could even brick the display firmware. +- You have been warned! + +The risk remains until the manufacturer provides official documentation, and the protocol can be reviewed. +Note: Multiple attempts to contact the manufacturer for documentation have received no response. + +With that out of the way, on to the fun stuff! + +## Features + +- Control the AOOSTAR WTR MAX and GEM12+ PRO second screen from Linux. +- Switch the display on or off. +- Display images (with automatic scaling and partial update support). +- Proof-of-concept demo for drawing shapes and text. +- USB device/serial port selection. + + +## Display + +Known information: + +- **Screen size:** 2.86" ≈ 68 × 27 mm +- **Resolution:** 960 × 376 +- **Manufacturer:** Synwit +- **Connected over USB UART** with a proprietary serial communication protocol: + - **USB device ID:** `416:90A1` (as shown by `lsusb`) + - **Linux device (example on Debian):** `/dev/ttyACM0` + - **1,500,000 baud**, 8N1 (likely ignored; actual USB transfer speed is much higher) + + +## Reverse Engineering + +### Motivation + +Developing open client software to use the embedded second screen on various Linux distributions. +It *might* also work on Windows, but I neither have that OS, nor plan to install it. + +The official proprietary AOOSTAR-X display software is not suitable for NAS and security-minded users: + +- All-in-one solution that attempts to do everything, from sensor reading to running a web server for control and configuration (*exposed on all interfaces!*). + I prefer using existing monitoring tools and combining them to my liking. +- Resource hungry, written in Python. Archive of v1.3.4 is 178 MB. +- Closed source, requires root access, distributed over filesharing sites, some without HTTPS. +- Built-in expiration date. One must regularly update the software without being able to verify the source. +- Many untranslated messages in Chinese and missing instructions for included features. + +The display remains on continuously (24×7) if the official software is not running. + +### Goals + +- [ ] Reverse engineer the LCD serial protocol to provide open screen software. + - Utilize the official AOOSTAR-X display software by sniffing USB communication, using `strace`, and decompiling the Python app. +- [ ] Document known commands so clients in other programming languages can be written. +- [ ] Eventually, create a Rust crate for easy integration into other Rust applications. + +**Out of scope:** + +- Reverse engineering the microcontroller firmware on the display board. + That would be an interesting task — potentially uncovering additional display commands — but is outside the project's current scope. +- Reimplementing the full AOOSTAR-X display software, which is overly complex for most use cases. + +## Installation + +Add your user to the `dialout` group for access to `/dev/ttyACM0`: + +```shell +sudo usermod -a -G dialout $USER +``` + +> You may have to log out and back in for group changes to take effect. + +## Build + +A recent [Rust](https://rustup.rs/) toolchain is required. +On Ubuntu 25.04, you can install build dependencies with: + +```shell +sudo apt install build-essential pkg-config libudev-dev +``` + +A release build is highly recommended, as it significantly improves graphics performance: + +```shell +cargo build --release +``` + + +## Demo App Usage + +Currently, the project includes a proof-of-concept demo application that loads an image, draws rectangles, and writes +text over the image. + +By default, the original LCD USB UART device `416:90A1` is used. See optional parameters to specify a different device. + +```shell +cargo run --release -- --demo --config Monitor3.json +``` + +The `--config` parameter is optional. It loads the official configuration file and displays the defined sensors in the +first panel. + +### Parameters + +- `--device /dev/ttyACM0` — Specify the serial device. +- `--usb 0403:6001` — Specify the USB UART device by USB **VID:PID** (hexadecimal, as shown by `lsusb`). +- `--help` — Show all options. + + +### Control Commands + +Besides demo mode, the following control commands have been implemented. + +The `asterctl` binary is built in `./target/release`. +Alternatively, use `cargo run --release --` to build and run automatically. + +> Aster: Greek for star and similar to AOOSTAR. + +**Switch display on:** + +```shell +asterctl --on +``` + +**Switch display off:** + +```shell +asterctl --off +``` + +**Load and display an image:** + +```shell +asterctl --image img/aybabtu.png +``` + +This expects a 960 × 376 image (other sizes are automatically scaled and the aspect ratio is ignored). +See Rust image crate for [support image formats](https://github.com/image-rs/image?tab=readme-ov-file#supported-image-formats). + +## Development + +- When sending an image to the screen, the image must be in **RGB565** format (16 bits per pixel). + - All graphic operations are performed on the loaded RGB888 image buffer. + - The image is automatically converted to RGB565 when sending it to the display. +- The 1.5 Mbps baud rate set in the client is ignored, as actual USB bulk transfer achieves much higher throughput. +For reference, at the nominal serial rate (~1,500,000 baud), it would take approximately 6 seconds to transfer a full image of 721,920 bytes (960 × 376 × 2): + - Display protocol: payload per chunk = 47 bytes; header per chunk = 12 bytes + - Number of chunks: 721,920 / 47 ≈ 15,360 chunks + - Total transmitted data: 15,360 chunks × 59 bytes/chunk = 906,240 bytes + - Serial frame format: 1 start bit + 8 data bits + 1 stop bit = 10 bits/byte + - Effective byte rate: 1,500,000 bits/sec / 10 bits/byte = 150,000 bytes/sec + - Transfer time: 906,240 bytes / 150,000 bytes/sec ≈ 6 seconds +- **Performance:** + - Displaying the first fullscreen image takes around 1.3 seconds. + - When switching the display on, the old image is immediately shown. + - Once the new image is fully transferred and the end-header command is sent, the display firmware switches to the new image. +- **Partial Updates:** + - A frame cache is used to send only changed chunks after the initial image is displayed, greatly speeding up partial screen updates. + - The chunk size is 47 bytes, determined from the original app. It is unknown if other chunk sizes are supported. + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please note that this software is currently in its initial development and will have major changes until the mentioned +goals above are reached! + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + diff --git a/fonts/DejaVuSans.ttf b/fonts/DejaVuSans.ttf new file mode 100644 index 0000000..e5f7eec Binary files /dev/null and b/fonts/DejaVuSans.ttf differ diff --git a/fonts/DejaVuSans.ttf.LICENSE b/fonts/DejaVuSans.ttf.LICENSE new file mode 100644 index 0000000..df52c17 --- /dev/null +++ b/fonts/DejaVuSans.ttf.LICENSE @@ -0,0 +1,187 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license (“Fonts”) and associated +documentation +files (the “Font Software”), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, +and/or sell copies of the Font Software, and to permit persons to whom +the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be +included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional +glyphs or characters may be added to the Fonts, only if the fonts are +renamed +to names not containing either the words “Bitstream” or the word “Vera”. + +This License becomes null and void to the extent applicable to Fonts or +Font Software +that has been modified and is distributed under the “Bitstream Vera” +names. + +The Font Software may be sold as part of a larger software package but +no copy +of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, +SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN +ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR +INABILITY TO USE +THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, +and Bitstream Inc., shall not be used in advertising or otherwise to promote +the sale, use or other dealings in this Font Software without prior written +authorization from the GNOME Foundation or Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by +the American Mathematical Society. This is not meant to restrict in any way +the legitimate use of the fonts, such as (but not limited to) electronic +distribution of documents containing these fonts, inclusion of these fonts +into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +$Id$ diff --git a/img/aybabtu.png b/img/aybabtu.png new file mode 100644 index 0000000..6bc4be8 Binary files /dev/null and b/img/aybabtu.png differ diff --git a/src/cfg.rs b/src/cfg.rs new file mode 100644 index 0000000..df98be0 --- /dev/null +++ b/src/cfg.rs @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025 Markus Zehnder + +//! AOOSTAR-X json configuration file format. +//! +//! Derived from the available Monitor3.json file in AOOSTAR-X v1.3.4. +//! Likely not fully compatible with files created with the original editor. + +use image::Rgb; +use imageproc::definitions::HasWhite; +use log::warn; +use serde::de::Visitor; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::io::BufReader; +use std::num::ParseIntError; +use std::ops::Deref; +use std::{fmt, fs}; + +pub fn load_cfg(path: &str) -> anyhow::Result { + let file = fs::File::open(path)?; + let reader = BufReader::new(file); + let config: MonitorConfig = serde_json::from_reader(reader)?; + + for active in config.active_panels.clone() { + if active == 0 || active > config.panels.len() as u32 { + warn!("Ignoring invalid active panel {active}"); + continue; + } + let panel = &config.panels[active as usize - 1]; + + println!( + "Panel {active}: type={}, {}", + panel.panel_type, + panel.img.as_deref().unwrap_or_default() + ); + for sensor in &panel.sensor { + println!( + " {}: {} {} {}", + sensor.label, + sensor + .name + .as_deref() + .or(sensor.item_name.as_deref()) + .unwrap_or_default(), + sensor.value.as_deref().unwrap_or_default(), + sensor.unit.as_deref().unwrap_or_default() + ); + } + } + + Ok(config) +} + +/// AOOSTAR-X monitor json configuration file +#[derive(Debug, Serialize, Deserialize)] +pub struct MonitorConfig { + /// _Not used_ + pub credentials: Option, + pub setup: Setup, + /// Panels: 1-based index into diy[i] + #[serde(rename = "mianban")] + pub active_panels: Vec, + /// Custom panels / DIY "Do It Yourself", + #[serde(rename = "diy")] + pub panels: Vec, +} + +/// Web-app user login +#[derive(Debug, Serialize, Deserialize)] +pub struct Credentials { + pub username: String, + pub password: String, +} + +/// Configuration Settings +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Setup { + /// Default: true + pub off_display: bool, + /// Selection of default panels based on theme / control_params / control_disk_temp ? + pub theme: i32, + /// ? Default: true + pub control_params: bool, + /// ? Default: true + pub control_disk_temp: bool, + /// Default: false + pub custom_panel: bool, + /// Language index. Default: 0 + pub language: Language, + /// Switch time between panels (?) in seconds, interpreted as int. Default: 5 + pub switch_time: Option, // existed as "30" string + /// Operation mode: performance, power saving, etc. + pub operation_mode: Option, + /// Operation type 1 or 2 (?). Default: 1 + #[serde(rename = "type")] + pub operation_type: Option, + /// Default: 300 + pub disk_update: i32, + /// Home Assistant URL + #[serde(deserialize_with = "empty_string_as_none")] + #[serde(rename = "ha_url")] + pub ha_url: Option, // "" in JSON ⇒ Option + /// Home Assistant long-lived access token + #[serde(deserialize_with = "empty_string_as_none")] + #[serde(rename = "ha_token")] + pub ha_token: Option, // "" in JSON ⇒ Option + /// Panel refresh in seconds. Default: 1 + pub refresh: f32, +} + +#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq)] +#[repr(u8)] +pub enum Language { + Chinese = 0, + English = 1, + Japanese = 2, +} + +#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq)] +#[repr(i16)] +pub enum OperationMode { + None = -1, + HighPerformance = 0, + Intelligent = 1, + PowerSaving = 2, + Custom30W = 3, + Custom20W = 4, + Custom10W = 5, +} + +/// Custom DIY panel definition +#[derive(Debug, Serialize, Deserialize)] +pub struct Panel { + /// Custom panel id + pub id: Option, + /// Custom panel name + pub name: Option, + /// TODO + pub checked: Option, + /// TODO panel type: 5 = built-in? 6 = custom ? + #[serde(rename = "type")] + pub panel_type: i32, + /// Background image filename + pub img: Option, + /// Sensors + pub sensor: Vec, +} + +/// One Data Display Unit +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Sensor { + /// Sensor mode: text, fan, progress, pointer + pub mode: SensorMode, + /// Sensor type. TODO verify sensor type values + /// - 1 Time / Date Labels + /// - 2 Windows-specific system info + /// - 3 Hardware value + /// - 4 AIDA64 sensor + /// - 5 HA sensor + /// - 6 http fetch from url + /// - 7 system info ? + /// - 8 lm-sensor ? + #[serde(rename = "type")] + pub sensor_type: i32, + /// Label name for internal panels. + pub name: Option, + /// Label name for custom panels. + pub item_name: Option, + /// TODO Data source? + pub label: String, + + /// x-position. Custom panel coordinates are stored as float! + pub x: f32, + /// x-position. TODO unit + pub y: f32, + pub width: Option, + pub height: Option, + + pub text_direction: i32, // layout direction + pub direction: i32, // sensor orientation, 0/1 + + #[serde(deserialize_with = "empty_string_as_none")] + pub value: Option, // "" or numbers, so Option + + pub font_family: String, + pub font_size: i32, + /// Font color in `#RRGGBB` notation, or -1 if not set. #ffffff = white, #ff0000 = red + pub font_color: FontColor, + pub font_weight: FontWeight, + pub text_align: TextAlign, + + #[serde(deserialize_with = "option_none_if_minus_one")] + pub integer_digits: Option, // -1 ≈ unset ⇒ Option + #[serde(deserialize_with = "option_none_if_minus_one")] + pub decimal_digits: Option, // -1 ≈ unset ⇒ Option + + /// Optional unit text to print after the value + #[serde(deserialize_with = "empty_string_as_none")] + pub unit: Option, + + pub min_angle: i32, + pub max_angle: i32, + pub min_value: i32, + pub max_value: i32, + + /// TODO determine meaning of: pic - render picture? + #[serde(deserialize_with = "empty_string_as_none")] + pub pic: Option, // "" when unused + /// Pivot x + #[serde(rename = "xz_x")] + pub xz_x: Option, + /// Pivot y + #[serde(rename = "xz_y")] + pub xz_y: Option, + + /// For type = 6 + pub url: Option, + /// For type = 6 + pub data: Option, + /// For type = 6 + pub interval: Option, +} + +#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq)] +#[repr(u8)] +pub enum SensorMode { + Text = 1, + Fan = 2, + Progress = 3, + Pointer = 4, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum TimeDateLabel { + #[serde(rename = "DATE_year")] + Year, + #[serde(rename = "DATE_month")] + Month, + #[serde(rename = "DATE_day")] + Day, + #[serde(rename = "DATE_hour")] + Hour, + #[serde(rename = "DATE_minute")] + Minute, + #[serde(rename = "DATE_second")] + Second, + #[serde(rename = "DATE_m_d_h_m_1")] + MDHM1, + #[serde(rename = "DATE_m_d_h_m_2")] + MDHM2, + #[serde(rename = "DATE_m_d_1")] + MD1, + #[serde(rename = "DATE_m_d_2")] + MD2, + #[serde(rename = "DATE_y_m_d_1")] + YMD1, + #[serde(rename = "DATE_y_m_d_2")] + YMD2, + #[serde(rename = "DATE_y_m_d_3")] + YMD3, + #[serde(rename = "DATE_y_m_d_4")] + YMD4, + #[serde(rename = "DATE_h_m_s_1")] + HMS1, + #[serde(rename = "DATE_h_m_s_2")] + HMS2, + #[serde(rename = "DATE_h_m_s_3")] + HMS3, + #[serde(rename = "DATE_h_m_1")] + HM1, + #[serde(rename = "DATE_h_m_2")] + HM2, + #[serde(rename = "DATE_h_m_3")] + HM3, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum FontWeight { + Normal, + Bold, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum TextAlign { + Left, + Center, + Right, +} + +fn option_none_if_minus_one<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + match Option::::deserialize(deserializer)? { + Some(-1) | None => Ok(None), + Some(other) => Ok(Some(other)), + } +} + +/// Special font color type since it is represented either as numeric -1 or as a string :-( +/// +/// A good serde programming exercise... +#[derive(Debug, Clone, Copy)] +pub struct FontColor(Rgb); + +impl Default for FontColor { + fn default() -> Self { + FontColor(Rgb::white()) + } +} + +impl Deref for FontColor { + type Target = Rgb; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom<&str> for FontColor { + type Error = ParseIntError; + + fn try_from(value: &str) -> Result { + if value.len() != 7 || value.starts_with('#') { + warn!("Invalid font color: {value}"); + Ok(FontColor::default()) + } else { + Ok(FontColor(Rgb([ + u8::from_str_radix(&value[1..3], 16)?, + u8::from_str_radix(&value[3..5], 16)?, + u8::from_str_radix(&value[5..7], 16)?, + ]))) + } + } +} + +impl From> for FontColor { + fn from(value: Rgb) -> Self { + FontColor(value) + } +} + +impl From for Rgb { + fn from(val: FontColor) -> Self { + val.0 + } +} + +impl Serialize for FontColor { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + format!("#{:02x}{:02x}{:02x}", self.0[0], self.0[1], self.0[2]).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for FontColor { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MyVisitor; + + impl<'de> Visitor<'de> for MyVisitor { + type Value = FontColor; + + fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("integer or string") + } + + fn visit_i64(self, val: i64) -> Result + where + E: serde::de::Error, + { + match val { + -1 => Ok(FontColor::default()), + _ => Err(E::custom("invalid integer value, expected -1")), + } + } + + fn visit_str(self, val: &str) -> Result + where + E: serde::de::Error, + { + if val.trim().is_empty() { + return Ok(FontColor::default()); + } + match val.parse::() { + Ok(val) => self.visit_i32(val), + Err(_) => val + .try_into() + .map_err(|e| E::custom(format!("invalid font color value: {e}"))), + } + } + } + + deserializer.deserialize_any(MyVisitor) + } +} + +fn empty_string_as_none<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let option = Option::::deserialize(deserializer)?; + Ok(option.and_then(|s| if s.trim().is_empty() { None } else { Some(s) })) +} diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..8c58363 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025 Markus Zehnder + +use crate::img::rgb888_to_565; +use anyhow::{Context, anyhow}; +use bytes::{BufMut, BytesMut}; +use image::RgbImage; +use log::{debug, error, info, warn}; +use serialport::{SerialPort, SerialPortType}; +use std::io::{Read, Write}; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +pub const DISPLAY_SIZE: (u32, u32) = (960, 376); + +const SERIAL_RETRY: u8 = 3; +const UART_BAUDRATE: u32 = 1_500_000; + +const USB_UART_VID: u16 = 0x416; +const USB_UART_PID: u16 = 0x90A1; + +const IMG_CHUNK_SIZE: usize = 47; + +static DISPLAY_OFF: [u8; 8] = [0xAA, 0x55, 0xAA, 0x55, 0x0A, 0x00, 0x00, 0x00]; +static DISPLAY_ON: [u8; 8] = [0xAA, 0x55, 0xAA, 0x55, 0x0B, 0x00, 0x00, 0x00]; + +static HEADER_START: [u8; 16] = [ + 0xAA, 0x55, 0xAA, 0x55, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x2F, 0x00, 0x04, 0x0B, 0x00, +]; +static HEADER_END: [u8; 8] = [0xAA, 0x55, 0xAA, 0x55, 0x06, 0x00, 0x00, 0x00]; +static HEADER: [u8; 8] = [0xAA, 0x55, 0xAA, 0x55, 0x08, 0x00, 0x00, 0x00]; + +#[derive(Default)] +pub struct AooScreenBuilder { + timeout: Option, + enable_cache: Option, + no_init_check: Option, +} + +#[allow(dead_code)] +impl AooScreenBuilder { + pub fn new() -> Self { + Self::default() + } + /// Set the amount of time to wait to receive data before timing out. Defaults to 1 sec. + pub fn timeout(&mut self, timeout: Duration) -> &mut Self { + self.timeout = Some(timeout); + self + } + + /// Cache previous frame sent to display for future diff updates. Enabled by default. + pub fn enable_cache(&mut self, enable: bool) -> &mut Self { + self.enable_cache = Some(enable); + self + } + + /// Disable LCD initialization check and only write data to the display. Defaults to false. + pub fn no_init_check(&mut self, no_check: bool) -> &mut Self { + self.no_init_check = Some(no_check); + self + } + + /// Open the default AOOSTAR LCD USB UART device 416:90A1. + pub fn open_default(self) -> anyhow::Result { + self.open_usb(USB_UART_VID, USB_UART_PID) + } + + /// Open the specified USB UART device id. Format: vid:pid + pub fn open_usb_id(self, id: &str) -> anyhow::Result { + let (vid, pid) = id + .split_once(':') + .with_context(|| "Error parsing serial port ID. Expected `vid:pid` format.")?; + self.open_usb(u16::from_str_radix(vid, 16)?, u16::from_str_radix(pid, 16)?) + } + + /// Open the specified USB UART + pub fn open_usb(self, vid: u16, pid: u16) -> anyhow::Result { + let serial_dev = find_usb_serial_port(vid, pid)?; + self.open_device(&serial_dev) + } + + /// Open the specified serial device + pub fn open_device(self, device: &str) -> anyhow::Result { + let port = serialport::new(device, UART_BAUDRATE) + .timeout(self.timeout.unwrap_or(Duration::from_millis(1000))) + .open() + .with_context(|| format!("Error opening serial port: {device}"))?; + + info!( + "Opened serial port {device}: baud={}, {}:{}:{}", + port.baud_rate()?, + port.data_bits()?, + port.parity()?, + port.stop_bits()? + ); + + Ok(AooScreen { + port: Some(port), + enable_cache: self.enable_cache.unwrap_or(true), + prev_frame: None, + no_init_check: self.no_init_check.unwrap_or(false), + }) + } +} + +pub struct AooScreen { + port: Option>, + enable_cache: bool, + prev_frame: Option, + no_init_check: bool, +} + +#[allow(dead_code)] +impl AooScreen { + pub fn init(&mut self) -> anyhow::Result<()> { + let port = self.port.as_mut().ok_or(anyhow!("LCD port not open"))?; + + port.write(&DISPLAY_ON) + .with_context(|| "Error sending display on command")?; + + if self.no_init_check { + warn!("Test mode: only writing to the display"); + } else { + // quick and dirty response check as in the original app + sleep(Duration::from_secs(1)); + + let available = port + .bytes_to_read() + .with_context(|| "Failed to get available bytes from serial port")?; + if available == 0 { + return Err(anyhow!("Initialization failed, no response received")); + } + let mut serial_buf: Vec = vec![0; available as usize]; + port.read(serial_buf.as_mut_slice()) + .with_context(|| "Failed to read from serial port")?; + + let marker = b'A'; + if !serial_buf.contains(&marker) { + return Err(anyhow!( + "Initialization failed, received: {}", + String::from_utf8_lossy(&serial_buf) + )); + } + } + + info!("Display initialized!"); + + Ok(()) + } + + pub fn close(&mut self) { + if self.port.is_some() { + if let Err(e) = self.off() { + warn!("Failed to close display: {e}"); + } + self.port = None; + } + } + + pub fn on(&mut self) -> anyhow::Result<()> { + self.send(&DISPLAY_ON) + .with_context(|| "Failed to send display on") + } + + pub fn off(&mut self) -> anyhow::Result<()> { + self.send(&DISPLAY_OFF) + .with_context(|| "Failed to send display off") + } + + pub fn send_image(&mut self, rgb_img: &RgbImage) -> anyhow::Result<()> { + let img_rgb565 = rgb888_to_565(rgb_img)?; + debug!( + "Start sending image (size {}) {} cache... ", + img_rgb565.len(), + if self.enable_cache && self.prev_frame.is_some() { + "with" + } else { + "without" + } + ); + + let start_time = Instant::now(); + self.send(&HEADER_START) + .with_context(|| "Failed to send header start")?; + + let mut buf = BytesMut::with_capacity(HEADER.len() + 4 + IMG_CHUNK_SIZE); + let mut sent_chunks = 0; + for (idx, chunk) in img_rgb565.chunks(IMG_CHUNK_SIZE).enumerate() { + let offset = idx * IMG_CHUNK_SIZE; + + if self.enable_cache + && let Some(cache) = self.prev_frame.as_mut() + { + let offset = idx * IMG_CHUNK_SIZE; + if offset + IMG_CHUNK_SIZE <= cache.len() + && cache[offset..offset + IMG_CHUNK_SIZE].eq(chunk) + { + // Block is unchanged from the previous frame; skip sending + continue; + } + } + + buf.clear(); + buf.extend(&HEADER); + buf.put_u32_le(offset as u32); + buf.extend(chunk); + + self.send(&buf) + .with_context(|| format!("Failed to send image data chunk {idx}"))?; + sent_chunks += 1; + } + + self.send(&HEADER_END) + .with_context(|| "Failed to send header end")?; + + if self.enable_cache { + self.prev_frame.replace(img_rgb565); + } + + debug!( + "Image sent: {}ms, {sent_chunks} chunks", + start_time.elapsed().as_millis() + ); + + Ok(()) + } + + pub fn enable_cache(&mut self, enable: bool) { + self.enable_cache = enable; + if !enable { + self.clear_cache(); + } + } + + pub fn is_cache_enabled(&self) -> bool { + self.enable_cache + } + + pub fn clear_cache(&mut self) { + self.prev_frame = None; + } + + fn send(&mut self, data: &[u8]) -> anyhow::Result<()> { + // TODO not sure if retry logic is required. Need a real device to test... + let mut retry = 0; + + let port = self.port.as_mut().ok_or(anyhow!("LCD port not open"))?; + + loop { + return match port.write_all(data) { + Ok(()) => { + port.flush()?; + Ok(()) + } + Err(e) => { + debug!( + "Bytes queued to send: {}", + port.bytes_to_write() + .with_context(|| "Error calling bytes_to_write")? + ); + if retry < SERIAL_RETRY { + warn!("Failed to write to display, retrying! Error: {e}"); + retry += 1; + continue; + } + error!("Failed to write to display: {e}"); + Err(e.into()) + } + }; + } + } +} + +pub fn find_usb_serial_port(vid: u16, pid: u16) -> serialport::Result { + info!("Looking for USB serial port {vid:x}:{pid:x}"); + let ports = serialport::available_ports()?; + for p in ports { + debug!("Found serial port: {}", p.port_name); + if let SerialPortType::UsbPort(info) = p.port_type { + if info.pid == pid && info.vid == vid { + return Ok(p.port_name); + } + } + } + + Err(serialport::Error::new( + serialport::ErrorKind::NoDevice, + format!("USB serial port {vid:x}:{pid:x} not found"), + )) +} diff --git a/src/font.rs b/src/font.rs new file mode 100644 index 0000000..2b38014 --- /dev/null +++ b/src/font.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025 Markus Zehnder + +use ab_glyph::{FontArc, FontRef, FontVec}; +use anyhow::{Context, anyhow}; +use log::warn; +use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; + +static DEFAULT_TTF_FONT: Lazy = Lazy::new(|| { + FontArc::new( + FontRef::try_from_slice(include_bytes!("../fonts/DejaVuSans.ttf")) + .expect("Failed to load default font"), + ) +}); + +pub struct FontHandler { + ttf_path: PathBuf, + ttf_cache: HashMap, +} + +impl FontHandler { + pub fn new(ttf_path: impl Into) -> Self { + Self { + ttf_path: ttf_path.into(), + ttf_cache: Default::default(), + } + } + + pub fn default_font() -> FontArc { + DEFAULT_TTF_FONT.clone() + } + + pub fn get_ttf_font_or_default(&mut self, name: &str) -> FontArc { + self.get_ttf_font(name).unwrap_or_else(|e| { + warn!("Failed to load font: {e}. Using default"); + FontHandler::default_font() + }) + } + + pub fn get_ttf_font(&mut self, name: &str) -> anyhow::Result { + if let Some(font) = self.ttf_cache.get(name) { + return Ok(font.clone()); + } + let mut path = self.ttf_path.join(name); + path.set_extension("ttf"); + + if !path.exists() { + return Err(anyhow!("{name}.ttf not found")); + } + + let data = fs::read(path).with_context(|| format!("Error reading font {name}.ttf"))?; + let font = FontArc::new( + FontVec::try_from_vec(data) + .with_context(|| format!("Error parsing font {name}.ttf"))?, + ); + + self.ttf_cache.insert(name.to_string(), font.clone()); + + Ok(font) + } +} diff --git a/src/img.rs b/src/img.rs new file mode 100644 index 0000000..a8929ac --- /dev/null +++ b/src/img.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025 Markus Zehnder + +use bytes::{BufMut, BytesMut}; +use image::imageops::FilterType; +use image::{GenericImageView, ImageReader, RgbImage}; +use log::{debug, warn}; +use std::path::Path; + +pub fn load_image

(path: P, size: (u32, u32)) -> anyhow::Result +where + P: AsRef, +{ + let img = ImageReader::open(path)?.decode()?; + debug!( + "Image dimensions: {:?}, {:?}", + img.dimensions(), + img.color() + ); + + if img.dimensions() != size { + warn!( + "Resizing invalid image dimensions {:?} to expected size {:?}, ignoring aspect ratio", + img.dimensions(), + size + ); + Ok(img + .resize_exact(size.0, size.1, FilterType::Lanczos3) + .to_rgb8()) + } else { + Ok(img.to_rgb8()) + } +} + +pub fn rgb888_to_565(rgb_img: &RgbImage) -> anyhow::Result { + let mut img_rgb565 = + BytesMut::with_capacity(rgb_img.width() as usize * rgb_img.height() as usize * 2); + + for (_x, _y, pixel) in rgb_img.enumerate_pixels() { + img_rgb565.put_u16_le( + ((pixel.0[0] & 248) as u16) << 8 + | ((pixel.0[1] & 252) as u16) << 3 + | ((pixel.0[2] as u16) >> 3), + ); + } + Ok(img_rgb565) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..93cbe8b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025 Markus Zehnder + +mod cfg; +mod display; +mod font; +mod img; + +use crate::cfg::Panel; +use crate::display::{AooScreen, AooScreenBuilder, DISPLAY_SIZE}; +use crate::font::FontHandler; +use ab_glyph::{Font, PxScale}; +use clap::Parser; +use env_logger::Env; +use image::imageops::FilterType; +use image::{ImageReader, Rgb, RgbImage}; +use imageproc::drawing::{draw_line_segment_mut, draw_text_mut}; +use log::{debug, info, warn}; +use std::fs; +use std::io::Cursor; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +/// AOOSTAR WTR MAX and GEM12+ PRO screen control. +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Serial device, for example "/dev/cu.usbserial-AB0KOHLS". Takes priority over --usb option. + #[arg(short, long)] + device: Option, + + /// USB serial UART "vid:pid" in hex notation (lsusb output). Default: 416:90A1 + #[arg(short, long)] + usb: Option, + + /// Switch display on and exit. This will show the last displayed image. + #[arg(long)] + on: bool, + + /// Switch display off and exit. + #[arg(long)] + off: bool, + + /// Image to display, other sizes than 960x376 will be scaled. + #[arg(short, long)] + image: Option, + + /// Run a demo + #[arg(long)] + demo: bool, + + /// Only for demo mode: AOOSTAR-X json configuration file to parse. + #[arg(short, long)] + config: Option, + + /// Switch off display n seconds after loading image or running demo. + #[arg(short, long)] + off_after: Option, + + /// Test mode: only write to the display without checking response. + #[arg(short, long)] + write_only: bool, + + /// Test mode: save changed images in ./out folder. + #[arg(short, long)] + save: bool, +} + +fn main() -> anyhow::Result<()> { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + + let args = Args::parse(); + if let Some(config) = args.config.as_ref() { + let _cfg = cfg::load_cfg(config)?; + } + + // initialize display with given UART port parameter + let mut builder = AooScreenBuilder::new(); + builder.no_init_check(args.write_only); + let mut screen = if let Some(device) = args.device { + builder.open_device(&device)? + } else if let Some(usb) = args.usb { + builder.open_usb_id(&usb)? + } else { + builder.open_default()? + }; + + // process simple commands + if args.off { + screen.off()?; + return Ok(()); + } else if args.on { + screen.on()?; + return Ok(()); + } + + // switch on screen for remaining commands + screen.init()?; + + if let Some(image) = args.image { + info!("Loading and displaying background image {image}..."); + let rgb_img = img::load_image(&image, DISPLAY_SIZE)?; + let timestamp = Instant::now(); + screen.send_image(&rgb_img)?; + debug!("Image sent in {}ms", timestamp.elapsed().as_millis()); + } + + if args.demo { + info!("Loading and displaying demo..."); + run_demo(&mut screen, args.config.as_deref(), args.save)?; + } + + if let Some(off) = args.off_after { + info!("Switching off display in {off}s"); + sleep(Duration::from_secs(off as u64)); + screen.off()?; + } + + info!("Bye bye!"); + + Ok(()) +} + +fn run_demo(screen: &mut AooScreen, config: Option<&str>, save_images: bool) -> anyhow::Result<()> { + let rgb_img = demo_image()?; + + // fill left and right side of the loaded image with neighboring pixel color + const WIDTH: u32 = 108; + let rgb_img = demo_blinds(screen, &rgb_img, WIDTH, save_images)?; + + // print demo text over background image + demo_text(screen, &rgb_img, save_images)?; + + if let Some(config) = config { + let cfg = cfg::load_cfg(config)?; + for active in cfg.active_panels.clone() { + if active == 0 || active > cfg.panels.len() as u32 { + warn!("Ignoring invalid active panel {active}"); + continue; + } + let panel = &cfg.panels[active as usize - 1]; + demo_panel(screen, &rgb_img, panel, save_images)?; + break; + } + } + + Ok(()) +} + +fn demo_image() -> anyhow::Result { + let reader = ImageReader::new(Cursor::new(include_bytes!("../img/aybabtu.png"))) + .with_guessed_format() + .expect("Cursor io never fails"); + + Ok(reader + .decode()? + .resize_exact(DISPLAY_SIZE.0, DISPLAY_SIZE.1, FilterType::Lanczos3) + .to_rgb8()) +} + +fn demo_text( + screen: &mut AooScreen, + background: &RgbImage, + save_images: bool, +) -> anyhow::Result<()> { + let text = "ALL YOUR BASE ARE BELONG TO US."; + let text_upd_delay = Duration::from_millis(0); + let font = FontHandler::default_font(); + let height = 36.0; + let scale = PxScale { + x: height, + y: height, + }; + + if save_images { + fs::create_dir_all("out")?; + } + + for text_idx in 0..text.len() { + info!("Printing: {}", &text[0..text_idx + 1]); + let text_upd = Instant::now(); + let mut rgb_img = background.clone(); + draw_text_mut( + &mut rgb_img, + Rgb([118u8, 118u8, 97u8]), + 4 * 47, + 300, + scale, + &font, + &text[0..text_idx + 1], + ); + + if save_images { + rgb_img.save_with_format( + format!("out/demo_text-{text_idx}.png"), + image::ImageFormat::Png, + )?; + } + + screen.send_image(&rgb_img)?; + + let elapsed = text_upd.elapsed(); + if elapsed < text_upd_delay { + sleep(text_upd_delay - elapsed); + } + } + + Ok(()) +} + +// CPU intensive! Release build is ~ 5x faster on M1 Max +fn demo_blinds( + screen: &mut AooScreen, + background: &RgbImage, + width: u32, + save_images: bool, +) -> anyhow::Result { + let mut rgb_img = background.clone(); + + info!("Masking {width} pixels of left & right image..."); + + if save_images { + fs::create_dir_all("out")?; + } + + for y in 0..DISPLAY_SIZE.1 { + let color = *rgb_img.get_pixel(width + 1, y); + // draw_antialiased_line_segment_mut( + // &mut rgb_img, + // (0, y as i32), + // (width as i32, y as i32), + // color, + // interpolate, + // ); + draw_line_segment_mut( + &mut rgb_img, + (0.0, y as f32), + (width as f32, y as f32), + color, + ); + let color = *rgb_img.get_pixel(DISPLAY_SIZE.0 - width - 1, y); + // draw_antialiased_line_segment_mut( + // &mut rgb_img, + // ((DISPLAY_SIZE.0 - width) as i32, y as i32), + // (DISPLAY_SIZE.0 as i32, y as i32), + // color, + // interpolate, + // ); + draw_line_segment_mut( + &mut rgb_img, + ((DISPLAY_SIZE.0 - width) as f32, y as f32), + (DISPLAY_SIZE.0 as f32, y as f32), + color, + ); + + if y % 5 == 0 { + screen.send_image(&rgb_img)?; + } + + if save_images { + rgb_img + .save_with_format(format!("out/demo_blinds-{y}.png"), image::ImageFormat::Png)?; + } + } + + screen.send_image(&rgb_img)?; + + Ok(rgb_img) +} + +fn demo_panel( + screen: &mut AooScreen, + background: &RgbImage, + panel: &Panel, + save_image: bool, +) -> anyhow::Result<()> { + info!("Displaying panel information..."); + + let mut rgb_img = background.clone(); + + let mut fh = FontHandler::new("fonts"); + + for sensor in &panel.sensor { + println!( + "({:03},{:03}): {}{}", + sensor.x, + sensor.y, + sensor.value.as_deref().unwrap_or_default(), + sensor.unit.as_deref().unwrap_or_default() + ); + + if let Some(value) = &sensor.value { + let font = fh.get_ttf_font_or_default(&sensor.font_family); + + let text = format!("{value}{}", sensor.unit.as_deref().unwrap_or_default()); + let scale = font.pt_to_px_scale(sensor.font_size as f32).unwrap(); + draw_text_mut( + &mut rgb_img, + sensor.font_color.into(), + // TODO figure out x,y unit conversion, something is off, probably in font scaling + sensor.x as i32, + sensor.y as i32, + scale, + &font, + &text, + ); + + screen.send_image(&rgb_img)?; + } + } + + if save_image { + fs::create_dir_all("out")?; + rgb_img.save_with_format("out/panel.png", image::ImageFormat::Png)?; + } + + Ok(()) +}