From 64037ca7ac3d1ed7eb402792d22a1eaf1a87f185 Mon Sep 17 00:00:00 2001 From: Sassan Haradji <me@sassanh.com> Date: Tue, 24 Sep 2024 17:08:19 +0400 Subject: [PATCH] feat(rpc): add `rpc` service with `dispatch` method to let external services dispatch actions and events to the redux bus --- .gitignore | 3 - CHANGELOG.md | 1 + buf.yaml | 3 + poetry.lock | 140 +- pyproject.toml | 21 +- ubo_app/constants.py | 2 + ubo_app/main.py | 10 +- ubo_app/rpc/generated/__init__.py | 0 .../rpc/generated/package_info/__init__.py | 0 .../rpc/generated/package_info/v1/__init__.py | 8 + ubo_app/rpc/generated/store/__init__.py | 0 ubo_app/rpc/generated/store/v1/__init__.py | 172 ++ ubo_app/rpc/generated/ubo/__init__.py | 0 ubo_app/rpc/generated/ubo/v1/__init__.py | 1946 +++++++++++++++++ ubo_app/rpc/message_to_object.py | 115 + ubo_app/rpc/proto/store/v1/store.proto | 30 + ubo_app/rpc/proto/ubo/v1/ubo.proto | 1419 ++++++++++++ ubo_app/rpc/sample_python_client.py | 132 ++ ubo_app/rpc/server.py | 25 + ubo_app/rpc/service.py | 65 + 20 files changed, 4084 insertions(+), 8 deletions(-) create mode 100644 buf.yaml create mode 100644 ubo_app/rpc/generated/__init__.py create mode 100644 ubo_app/rpc/generated/package_info/__init__.py create mode 100644 ubo_app/rpc/generated/package_info/v1/__init__.py create mode 100644 ubo_app/rpc/generated/store/__init__.py create mode 100644 ubo_app/rpc/generated/store/v1/__init__.py create mode 100644 ubo_app/rpc/generated/ubo/__init__.py create mode 100644 ubo_app/rpc/generated/ubo/v1/__init__.py create mode 100644 ubo_app/rpc/message_to_object.py create mode 100644 ubo_app/rpc/proto/store/v1/store.proto create mode 100644 ubo_app/rpc/proto/ubo/v1/ubo.proto create mode 100644 ubo_app/rpc/sample_python_client.py create mode 100644 ubo_app/rpc/server.py create mode 100644 ubo_app/rpc/service.py diff --git a/.gitignore b/.gitignore index f43ccb82..af2691c1 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,3 @@ scripts/packer/output-* /screenshots /snapshot.json /snapshot.bin - -# rpc -/ubo_app/rpc/proto/ubo/v1/ubo.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index 9741ddff..db09ce47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Upcoming - feat(rpc): add a proto generator which parses actions and events files and generates proto files for them +- feat(rpc): add `rpc` service with `dispatch` method to let external services dispatch actions and events to the redux bus ## Version 0.16.2 diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000..0dbf15c1 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,3 @@ +version: v2 +modules: + - path: ubo_app/rpc/proto/ diff --git a/poetry.lock b/poetry.lock index 6782dcad..cd94a2b4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -899,6 +899,124 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "grpcio" +version = "1.66.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, + {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, + {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, + {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, + {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, + {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, + {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, + {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, + {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, + {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, + {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, + {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, + {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, + {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, + {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, + {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, + {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, + {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, + {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, + {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, + {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.66.1)"] + +[[package]] +name = "grpcio-tools" +version = "1.66.1" +description = "Protobuf code generator for gRPC" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio_tools-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:e0c71405399ef59782600b1f0bdebc69ba12d7c9527cd268162a86273971d294"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:df1a174a6f9d3b4c380f005f33352d2e95464f33f021fb08084735a2eb6e23b1"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7d789bfe53fce9e87aa80c3694a366258ce4c41b706258e9228ed4994832b780"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95c44a265ff01fd05166edae9350bc2e7d1d9a95e8f53b8cd04d2ae0a588c583"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b962a8767c3c0f9afe92e0dd6bb0b2305d35195a1053f84d4d31f585b87557ed"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d8616773126ec3cdf747b06a12e957b43ac15c34e4728def91fa67249a7c689a"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0067e79b6001560ac6acc78cca11fd3504fa27f8af46e3cdbac2f4998505e597"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-win32.whl", hash = "sha256:fa4f95a79a34afc3b5464895d091cd1911227fc3ab0441b9a37cd1817cf7db86"}, + {file = "grpcio_tools-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:3acce426f5e643de63019311171f4d31131da8149de518716a95c29a2c12dd38"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9a07e24feb7472419cf70ebbb38dd4299aea696f91f191b62a99b3ee9ff03f89"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:097a069e7c640043921ecaf3e88d7af78ccd40c25dbddc91db2a4a2adbd0393d"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:016fa273dc696c9d8045091ac50e000bce766183a6b150801f51c2946e33dbe3"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ec9f4f964f8e8ed5e9cc13deb678c83d5597074c256805373220627833bc5ad"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3198815814cdd12bdb69b7580d7770a4ad4c8b2093e0bd6b987bc817618e3eec"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:796620fc41d3fbb566d9614ef22bc55df67fac1f1e19c1e0fb6ec48bc9b6a44b"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:222d8dc218560698e1abf652fb47e4015994ec7a265ef46e012fd9c9e77a4d6b"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-win32.whl", hash = "sha256:56e17a11f34df252b4c6fb8aa8cd7b44d162dba9f3333be87ddf7c8bf496622a"}, + {file = "grpcio_tools-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:edd52d667f2aa3c73233be0a821596937f24536647c12d96bfc54aa4cb04747d"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:869b6960d5daffda0dac1a474b44144f0dace0d4336394e499c4f400c5e2f8d9"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:68d9390bf9ba863ac147fc722d6548caa587235e887cac1bc2438212e89d1de7"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:b8660401beca7e3af28722439e07b0bcdca80b4a68f5a5a1138ae7b7780a6abf"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb67b9aa9cd69468bceb933e8e0f89fd13695746c018c4d2e6b3b84e73f3ad97"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5daceb9716e31edc0e1ba0f93303785211438c43502edddad7a919fc4cb3d664"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a86398a4cd0665bc7f09fa90b89bac592c959d2c895bf3cf5d47a98c0f2d24c"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4acb53338072ab3023e418a5c7059cb15686abd1607516fa1453406dd5f69d"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-win32.whl", hash = "sha256:88e04b7546101bc79c868c941777efd5088063a9e4f03b4d7263dde796fbabf7"}, + {file = "grpcio_tools-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:5b4fc56abeafae74140f5da29af1093e88ce64811d77f1a81c3146e9e996fb6a"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:d4dd2ff982c1aa328ef47ce34f07af82f1f13599912fb1618ebc5fe1e14dddb8"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:066648543f786cb74b1fef5652359952455dbba37e832642026fd9fd8a219b5f"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d19d47744c30e6bafa76b3113740e71f382d75ebb2918c1efd62ebe6ba7e20f9"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739c53571130b359b738ac7d6d0a1f772e15779b66df7e6764bee4071cd38689"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2226ff8d3ecba83b7622946df19d6e8e15cb52f761b8d9e2f807b228db5f1b1e"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f4b1498cb8b422fbae32a491c9154e8d47650caf5852fbe6b3b34253e824343"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:93d2d9e14e81affdc63d67c42eb16a8da1b6fecc16442a703ca60eb0e7591691"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-win32.whl", hash = "sha256:d761dfd97a10e4aae73628b5120c64e56f0cded88651d0003d2d80e678c3e7c9"}, + {file = "grpcio_tools-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:e1c2ac0955f5fb87b8444316e475242d194c3f3cd0b7b6e54b889a7b6f05156f"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:5f1f04578b72c281e39274348a61d240c48d5321ba8d7a8838e194099ecbc322"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:da9b0c08dbbf07535ee1b75a22d0acc5675a808a3a3df9f9b21e0e73ddfbb3a9"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e302b4e1fa856d74ff65c65888b3a37153287ce6ad5bad80b2fdf95130accec2"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc3f62494f238774755ff90f0e66a93ac7972ea1eb7180c45acf4fd53b25cca"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cad65ff22459aa387f543d293f54834c9aac8f76fb7416a7046556df75b567"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3d17a27c567a5e4d18f487368215cb51b43e2499059fd6113b92f7ae1fee48be"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4df167e67b083f96bc277032a526f6186e98662aaa49baea1dfb8ecfe26ce117"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-win32.whl", hash = "sha256:f94d5193b2f2a9595795b83e7978b2bee1c0399da66f2f24d179c388f81fb99c"}, + {file = "grpcio_tools-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:66f527a1e3f063065e29cf6f3e55892434d13a5a51e3b22402e09da9521e98a3"}, + {file = "grpcio_tools-1.66.1.tar.gz", hash = "sha256:5055ffe840ea8f505c30378be02afb4dbecb33480e554debe10b63d6b2f641c3"}, +] + +[package.dependencies] +grpcio = ">=1.66.1" +protobuf = ">=5.26.1,<6.0dev" +setuptools = "*" + [[package]] name = "grpclib" version = "0.4.7" @@ -2354,6 +2472,26 @@ starlette = ["starlette (>=0.19.1)"] starlite = ["starlite (>=1.48)"] tornado = ["tornado (>=5)"] +[[package]] +name = "setuptools" +version = "75.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] + [[package]] name = "simpleaudio" version = "1.0.4" @@ -2650,4 +2788,4 @@ dev = ["headless-kivy", "headless-kivy"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e3bc8f2fb9e8eb1f442cfce506da9b25ab8906f84b4f8070af5351baa330593c" +content-hash = "cd9d570fe0f071e447bc9f9b6ad01c1ef7b86f7ba5abb8cdbebcf2f98542bcce" diff --git a/pyproject.toml b/pyproject.toml index be00135f..fb12fbcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ toml = "^0.10.2" pytest-mock = "^3.14.0" ipython = "^8.23.0" pyfakefs = { git = "https://github.com/pytest-dev/pyfakefs.git" } +grpcio-tools = "^1.66.1" betterproto = { extras = ["compiler"], version = "^2.0.0b7" } [tool.poetry.extras] @@ -89,12 +90,20 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poe.tasks] -lint = "ruff check . --unsafe-fixes" +lint = "ruff check ." +"lint:fix" = "ruff check . --unsafe-fixes --fix" typecheck = "pyright -p pyproject.toml ." test = "pytest --cov=ubo_app" sanity = ["typecheck", "lint", "test"] build-docker-images = "sh -c 'docker buildx build . -f scripts/Dockerfile.dev -t ubo-app-dev && docker buildx build . -f scripts/Dockerfile.test -t ubo-app-test'" +"proto:generate:raw" = "python ubo_app/rpc/generate_proto.py" +"proto:generate" = ["proto:generate:raw", "proto:lint"] +"proto:compile:raw" = "sh -c 'mkdir -p ubo_app/rpc/generated && python -m grpc_tools.protoc -I ubo_app/rpc/proto/ --python_betterproto_opt=typing.310 --python_betterproto_out=ubo_app/rpc/generated/ ubo_app/rpc/proto/store/v1/store.proto'" +"proto:compile" = ["proto:compile:raw", "lint:fix"] +"proto:lint" = "buf format -w ubo_app/rpc/proto/" +"proto" = ["proto:generate", "proto:compile"] + "device:deploy" = "poe deploy-to-device" "device:deploy:deps" = "poe deploy-to-device --deps" "device:deploy:kill" = "poe deploy-to-device --kill" @@ -151,6 +160,14 @@ multiline-quotes = "double" "tests/*" = ["S101", "PLR0913", "PLR0915"] "**/reducer.py" = ["C901", "PLR0912", "PLR0915"] "ubo_app/services/*/ubo_handle.py" = ["TCH004"] +"ubo_app/rpc/generated/*" = [ + "ARG002", + "ASYNC109", + "D", + "ERA001", + "F401", + "RUF009", +] [tool.ruff.format] quote-style = "single" @@ -159,7 +176,7 @@ quote-style = "single" profile = "black" [tool.pyright] -exclude = ["typings"] +exclude = ["typings", "ubo_app/rpc/generated"] [[tool.pyright.executionEnvironments]] root = "ubo_app/services/000-audio" diff --git a/ubo_app/constants.py b/ubo_app/constants.py index ee1cc581..49f66171 100644 --- a/ubo_app/constants.py +++ b/ubo_app/constants.py @@ -28,6 +28,8 @@ ENABLED_SERVICES = os.environ.get('UBO_ENABLED_SERVICES', '') ENABLED_SERVICES = ENABLED_SERVICES.split(',') if ENABLED_SERVICES else [] +DISABLE_GRPC = str_to_bool(os.environ.get('UBO_DISABLE_GRPC', 'False')) == 1 + UPDATE_ASSETS_PATH = Path(f'{INSTALLATION_PATH}/_update/') UPDATE_LOCK_PATH = UPDATE_ASSETS_PATH / 'update_is_ready.lock' diff --git a/ubo_app/main.py b/ubo_app/main.py index ae858134..80cc2c14 100644 --- a/ubo_app/main.py +++ b/ubo_app/main.py @@ -37,13 +37,19 @@ def main() -> None: setup() - from ubo_app.service import start_event_loop_thread + from ubo_app.service import start_event_loop_thread, worker_thread start_event_loop_thread(asyncio.get_event_loop()) + from ubo_app.constants import DISABLE_GRPC, HEIGHT, WIDTH + + if not DISABLE_GRPC: + from ubo_app.rpc.server import serve as grpc_serve + + worker_thread.run_task(grpc_serve()) + import headless_kivy.config - from ubo_app.constants import HEIGHT, WIDTH from ubo_app.display import render_on_display headless_kivy.config.setup_headless_kivy( diff --git a/ubo_app/rpc/generated/__init__.py b/ubo_app/rpc/generated/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ubo_app/rpc/generated/package_info/__init__.py b/ubo_app/rpc/generated/package_info/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ubo_app/rpc/generated/package_info/v1/__init__.py b/ubo_app/rpc/generated/package_info/v1/__init__.py new file mode 100644 index 00000000..3d6e9a0a --- /dev/null +++ b/ubo_app/rpc/generated/package_info/v1/__init__.py @@ -0,0 +1,8 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: package_info/v1/package_info.proto +# plugin: python-betterproto +# This file has been @generated + +from dataclasses import dataclass + +import betterproto diff --git a/ubo_app/rpc/generated/store/__init__.py b/ubo_app/rpc/generated/store/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ubo_app/rpc/generated/store/v1/__init__.py b/ubo_app/rpc/generated/store/v1/__init__.py new file mode 100644 index 00000000..97b524f5 --- /dev/null +++ b/ubo_app/rpc/generated/store/v1/__init__.py @@ -0,0 +1,172 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: store/v1/store.proto +# plugin: python-betterproto +# This file has been @generated + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +import betterproto +import grpclib +import grpclib.server +from betterproto.grpc.grpclib_server import ServiceBase + +if TYPE_CHECKING: + from collections.abc import AsyncIterator + + from betterproto.grpc.grpclib_client import MetadataLike + from grpclib.metadata import Deadline + + from generated.ubo import v1 as __ubo_v1__ + + +@dataclass(eq=False, repr=False) +class DispatchActionRequest(betterproto.Message): + action: '__ubo_v1__.Action' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class DispatchEventRequest(betterproto.Message): + event: '__ubo_v1__.Event' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class DispatchActionResponse(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class DispatchEventResponse(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class SubscribeEventRequest(betterproto.Message): + event: '__ubo_v1__.Event' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class SubscribeEventResponse(betterproto.Message): + event: '__ubo_v1__.Event' = betterproto.message_field(1) + + +class StoreServiceStub(betterproto.ServiceStub): + async def dispatch_action( + self, + dispatch_action_request: 'DispatchActionRequest', + *, + timeout: 'float | None' = None, + deadline: 'Deadline | None' = None, + metadata: 'MetadataLike | None' = None, + ) -> 'DispatchActionResponse': + return await self._unary_unary( + '/store.v1.StoreService/DispatchAction', + dispatch_action_request, + DispatchActionResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def dispatch_event( + self, + dispatch_event_request: 'DispatchEventRequest', + *, + timeout: 'float | None' = None, + deadline: 'Deadline | None' = None, + metadata: 'MetadataLike | None' = None, + ) -> 'DispatchEventResponse': + return await self._unary_unary( + '/store.v1.StoreService/DispatchEvent', + dispatch_event_request, + DispatchEventResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def subscribe_event( + self, + subscribe_event_request: 'SubscribeEventRequest', + *, + timeout: 'float | None' = None, + deadline: 'Deadline | None' = None, + metadata: 'MetadataLike | None' = None, + ) -> 'AsyncIterator[SubscribeEventResponse]': + async for response in self._unary_stream( + '/store.v1.StoreService/SubscribeEvent', + subscribe_event_request, + SubscribeEventResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ): + yield response + + +class StoreServiceBase(ServiceBase): + + async def dispatch_action( + self, dispatch_action_request: 'DispatchActionRequest', + ) -> 'DispatchActionResponse': + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def dispatch_event( + self, dispatch_event_request: 'DispatchEventRequest', + ) -> 'DispatchEventResponse': + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def subscribe_event( + self, subscribe_event_request: 'SubscribeEventRequest', + ) -> 'AsyncIterator[SubscribeEventResponse]': + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + yield SubscribeEventResponse() + + async def __rpc_dispatch_action( + self, + stream: 'grpclib.server.Stream[DispatchActionRequest, DispatchActionResponse]', + ) -> None: + request = await stream.recv_message() + response = await self.dispatch_action(request) + await stream.send_message(response) + + async def __rpc_dispatch_event( + self, + stream: 'grpclib.server.Stream[DispatchEventRequest, DispatchEventResponse]', + ) -> None: + request = await stream.recv_message() + response = await self.dispatch_event(request) + await stream.send_message(response) + + async def __rpc_subscribe_event( + self, + stream: 'grpclib.server.Stream[SubscribeEventRequest, SubscribeEventResponse]', + ) -> None: + request = await stream.recv_message() + await self._call_rpc_handler_server_stream( + self.subscribe_event, + stream, + request, + ) + + def __mapping__(self) -> 'dict[str, grpclib.const.Handler]': + return { + '/store.v1.StoreService/DispatchAction': grpclib.const.Handler( + self.__rpc_dispatch_action, + grpclib.const.Cardinality.UNARY_UNARY, + DispatchActionRequest, + DispatchActionResponse, + ), + '/store.v1.StoreService/DispatchEvent': grpclib.const.Handler( + self.__rpc_dispatch_event, + grpclib.const.Cardinality.UNARY_UNARY, + DispatchEventRequest, + DispatchEventResponse, + ), + '/store.v1.StoreService/SubscribeEvent': grpclib.const.Handler( + self.__rpc_subscribe_event, + grpclib.const.Cardinality.UNARY_STREAM, + SubscribeEventRequest, + SubscribeEventResponse, + ), + } diff --git a/ubo_app/rpc/generated/ubo/__init__.py b/ubo_app/rpc/generated/ubo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ubo_app/rpc/generated/ubo/v1/__init__.py b/ubo_app/rpc/generated/ubo/v1/__init__.py new file mode 100644 index 00000000..dd725265 --- /dev/null +++ b/ubo_app/rpc/generated/ubo/v1/__init__.py @@ -0,0 +1,1946 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: ubo/v1/ubo.proto +# plugin: python-betterproto +# This file has been @generated +import builtins +from dataclasses import dataclass + +import betterproto + + +class WiFiType(betterproto.Enum): + UNSPECIFIED = 0 + WEP = 1 + WPA = 2 + WPA2 = 3 + NOPASS = 4 + + +class ConnectionState(betterproto.Enum): + UNSPECIFIED = 0 + CONNECTED = 1 + CONNECTING = 2 + DISCONNECTED = 3 + UNKNOWN = 4 + + +class GlobalWiFiState(betterproto.Enum): + UNSPECIFIED = 0 + CONNECTED = 1 + DISCONNECTED = 2 + PENDING = 3 + NEEDS_ATTENTION = 4 + UNKNOWN = 5 + + +class Sensor(betterproto.Enum): + UNSPECIFIED = 0 + TEMPERATURE = 1 + LIGHT = 2 + + +class DockerStatus(betterproto.Enum): + UNSPECIFIED = 0 + UNKNOWN = 1 + NOT_INSTALLED = 2 + INSTALLING = 3 + NOT_RUNNING = 4 + RUNNING = 5 + ERROR = 6 + + +class ImageStatus(betterproto.Enum): + UNSPECIFIED = 0 + NOT_AVAILABLE = 1 + FETCHING = 2 + AVAILABLE = 3 + CREATED = 4 + RUNNING = 5 + ERROR = 6 + + +class Key(betterproto.Enum): + UNSPECIFIED = 0 + BACK = 1 + HOME = 2 + UP = 3 + DOWN = 4 + L1 = 5 + L2 = 6 + L3 = 7 + + +class VoiceEngine(betterproto.Enum): + UNSPECIFIED = 0 + PIPER = 1 + PICOVOICE = 2 + + +class GlobalEthernetState(betterproto.Enum): + UNSPECIFIED = 0 + CONNECTED = 1 + DISCONNECTED = 2 + PENDING = 3 + NEEDS_ATTENTION = 4 + UNKNOWN = 5 + + +class Importance(betterproto.Enum): + UNSPECIFIED = 0 + CRITICAL = 1 + HIGH = 2 + MEDIUM = 3 + LOW = 4 + + +class NotificationDisplayType(betterproto.Enum): + UNSPECIFIED = 0 + NOT_SET = 1 + BACKGROUND = 2 + FLASH = 3 + STICKY = 4 + + +class Chime(betterproto.Enum): + UNSPECIFIED = 0 + ADD = 1 + DONE = 2 + FAILURE = 3 + VOLUME_CHANGE = 4 + + +class AudioDevice(betterproto.Enum): + UNSPECIFIED = 0 + INPUT = 1 + OUTPUT = 2 + + +@dataclass(eq=False, repr=False) +class BaseMenu(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + title: str = betterproto.string_field(2) + items: 'list[Item]' = betterproto.message_field(3) + placeholder: 'str | None' = betterproto.string_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class HeadedMenu(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + heading: str = betterproto.string_field(2) + sub_heading: str = betterproto.string_field(3) + title: str = betterproto.string_field(4) + items: 'list[Item]' = betterproto.message_field(5) + placeholder: 'str | None' = betterproto.string_field(6, optional=True) + + +@dataclass(eq=False, repr=False) +class HeadlessMenu(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + title: str = betterproto.string_field(2) + items: 'list[Item]' = betterproto.message_field(3) + placeholder: 'str | None' = betterproto.string_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class Item(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'str | None' = betterproto.string_field(2, optional=True) + label: 'str | None' = betterproto.string_field(3, optional=True) + color: 'str | None' = betterproto.string_field(4, optional=True) + background_color: 'str | None' = betterproto.string_field(5, optional=True) + icon: 'str | None' = betterproto.string_field(6, optional=True) + is_short: 'bool | None' = betterproto.bool_field(7, optional=True) + opacity: 'float | None' = betterproto.float_field(8, optional=True) + progress: 'float | None' = betterproto.float_field(9, optional=True) + + +@dataclass(eq=False, repr=False) +class ActionItem(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'str | None' = betterproto.string_field(2, optional=True) + label: 'str | None' = betterproto.string_field(3, optional=True) + color: 'str | None' = betterproto.string_field(4, optional=True) + background_color: 'str | None' = betterproto.string_field(5, optional=True) + icon: 'str | None' = betterproto.string_field(6, optional=True) + is_short: 'bool | None' = betterproto.bool_field(7, optional=True) + opacity: 'float | None' = betterproto.float_field(8, optional=True) + progress: 'float | None' = betterproto.float_field(9, optional=True) + + +@dataclass(eq=False, repr=False) +class ApplicationItem(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + application: str = betterproto.string_field(2) + key: 'str | None' = betterproto.string_field(3, optional=True) + label: 'str | None' = betterproto.string_field(4, optional=True) + color: 'str | None' = betterproto.string_field(5, optional=True) + background_color: 'str | None' = betterproto.string_field(6, optional=True) + icon: 'str | None' = betterproto.string_field(7, optional=True) + is_short: 'bool | None' = betterproto.bool_field(8, optional=True) + opacity: 'float | None' = betterproto.float_field(9, optional=True) + progress: 'float | None' = betterproto.float_field(10, optional=True) + + +@dataclass(eq=False, repr=False) +class SubMenuItem(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + sub_menu: 'Menu' = betterproto.message_field(2) + key: 'str | None' = betterproto.string_field(3, optional=True) + label: 'str | None' = betterproto.string_field(4, optional=True) + color: 'str | None' = betterproto.string_field(5, optional=True) + background_color: 'str | None' = betterproto.string_field(6, optional=True) + icon: 'str | None' = betterproto.string_field(7, optional=True) + is_short: 'bool | None' = betterproto.bool_field(8, optional=True) + opacity: 'float | None' = betterproto.float_field(9, optional=True) + progress: 'float | None' = betterproto.float_field(10, optional=True) + + +@dataclass(eq=False, repr=False) +class Subscribable(betterproto.Message): + meta_field_package_name_menu: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class Menu(betterproto.Message): + headed_menu: 'HeadedMenu' = betterproto.message_field(1, group='menu') + headless_menu: 'HeadlessMenu' = betterproto.message_field(2, group='menu') + + +@dataclass(eq=False, repr=False) +class ScreenshotEvent(betterproto.Message): + meta_field_package_name_operations: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class SnapshotEvent(betterproto.Message): + meta_field_package_name_operations: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class UboAction(betterproto.Message): + voice_action: 'VoiceAction' = betterproto.message_field(1, group='ubo_action') + ssh_action: 'SshAction' = betterproto.message_field(2, group='ubo_action') + rgb_ring_action: 'RgbRingAction' = betterproto.message_field(3, group='ubo_action') + notifications_action: 'NotificationsAction' = betterproto.message_field( + 4, group='ubo_action', + ) + light_dm_action: 'LightDmAction' = betterproto.message_field(5, group='ubo_action') + ip_action: 'IpAction' = betterproto.message_field(6, group='ubo_action') + display_action: 'DisplayAction' = betterproto.message_field(7, group='ubo_action') + camera_action: 'CameraAction' = betterproto.message_field(8, group='ubo_action') + audio_action: 'AudioAction' = betterproto.message_field(9, group='ubo_action') + docker_action: 'DockerAction' = betterproto.message_field(10, group='ubo_action') + keypad_action: 'KeypadAction' = betterproto.message_field(11, group='ubo_action') + r_pi_connect_action: 'RPiConnectAction' = betterproto.message_field( + 12, group='ubo_action', + ) + sensors_action: 'SensorsAction' = betterproto.message_field(13, group='ubo_action') + users_action: 'UsersAction' = betterproto.message_field(14, group='ubo_action') + vs_code_action: 'VsCodeAction' = betterproto.message_field(15, group='ubo_action') + wi_fi_action: 'WiFiAction' = betterproto.message_field(16, group='ubo_action') + + +@dataclass(eq=False, repr=False) +class UboEvent(betterproto.Message): + users_event: 'UsersEvent' = betterproto.message_field(1, group='ubo_event') + notifications_event: 'NotificationsEvent' = betterproto.message_field( + 2, group='ubo_event', + ) + ip_event: 'IpEvent' = betterproto.message_field(3, group='ubo_event') + display_event: 'DisplayEvent' = betterproto.message_field(4, group='ubo_event') + audio_event: 'AudioEvent' = betterproto.message_field(5, group='ubo_event') + screenshot_event: 'ScreenshotEvent' = betterproto.message_field( + 6, group='ubo_event', + ) + camera_event: 'CameraEvent' = betterproto.message_field(7, group='ubo_event') + keypad_event: 'KeypadEvent' = betterproto.message_field(8, group='ubo_event') + snapshot_event: 'SnapshotEvent' = betterproto.message_field(9, group='ubo_event') + wi_fi_event: 'WiFiEvent' = betterproto.message_field(10, group='ubo_event') + + +@dataclass(eq=False, repr=False) +class DispatchItem(betterproto.Message): + meta_field_package_name_dispatch_action: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + operation: 'DispatchItemOperation' = betterproto.message_field(2) + key: 'str | None' = betterproto.string_field(3, optional=True) + label: 'str | None' = betterproto.string_field(4, optional=True) + color: 'str | None' = betterproto.string_field(5, optional=True) + background_color: 'str | None' = betterproto.string_field(6, optional=True) + icon: 'str | None' = betterproto.string_field(7, optional=True) + is_short: 'bool | None' = betterproto.bool_field(8, optional=True) + opacity: 'float | None' = betterproto.float_field(9, optional=True) + progress: 'float | None' = betterproto.float_field(10, optional=True) + + +@dataclass(eq=False, repr=False) +class DispatchItemOperation(betterproto.Message): + ubo_event: 'Event' = betterproto.message_field(1, group='operation') + ubo_action: 'Action' = betterproto.message_field(2, group='operation') + + +@dataclass(eq=False, repr=False) +class LightDmAction(betterproto.Message): + meta_field_package_name_lightdm: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class LightDmUpdateStateAction(betterproto.Message): + meta_field_package_name_lightdm: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_active: 'bool | None' = betterproto.bool_field(2, optional=True) + is_enabled: 'bool | None' = betterproto.bool_field(3, optional=True) + is_installed: 'bool | None' = betterproto.bool_field(4, optional=True) + is_installing: 'bool | None' = betterproto.bool_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class LightDmClearEnabledStateAction(betterproto.Message): + meta_field_package_name_lightdm: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class LightDmState(betterproto.Message): + meta_field_package_name_lightdm: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_active: 'bool | None' = betterproto.bool_field(2, optional=True) + is_enabled: 'bool | None' = betterproto.bool_field(3, optional=True) + is_installed: 'bool | None' = betterproto.bool_field(4, optional=True) + is_installing: 'bool | None' = betterproto.bool_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class WiFiConnection(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + ssid: str = betterproto.string_field(2) + state: 'ConnectionState | None' = betterproto.enum_field(3, optional=True) + signal_strength: 'int | None' = betterproto.int64_field(4, optional=True) + password: 'str | None' = betterproto.string_field(5, optional=True) + type: 'WiFiType | None' = betterproto.enum_field(6, optional=True) + hidden: 'bool | None' = betterproto.bool_field(7, optional=True) + + +@dataclass(eq=False, repr=False) +class WiFiAction(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class WiFiSetHasVisitedOnboardingAction(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + has_visited_onboarding: bool = betterproto.bool_field(2) + + +@dataclass(eq=False, repr=False) +class WiFiUpdateAction(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + connections: 'list[WiFiConnection]' = betterproto.message_field(2) + state: 'GlobalWiFiState' = betterproto.enum_field(3) + current_connection: 'WiFiConnection' = betterproto.message_field(4) + + +@dataclass(eq=False, repr=False) +class WiFiUpdateRequestAction(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + reset: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class WiFiEvent(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class WiFiUpdateRequestEvent(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class WiFiState(betterproto.Message): + meta_field_package_name_wifi: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + connections: 'list[WiFiConnection]' = betterproto.message_field(2) + state: 'GlobalWiFiState' = betterproto.enum_field(3) + current_connection: 'WiFiConnection' = betterproto.message_field(4) + has_visited_onboarding: 'bool | None' = betterproto.bool_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class SensorsAction(betterproto.Message): + meta_field_package_name_sensors: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class SensorsReportReadingAction(betterproto.Message): + meta_field_package_name_sensors: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + sensor: 'Sensor' = betterproto.enum_field(2) + reading: float = betterproto.float_field(3) + timestamp: int = betterproto.int64_field(4) + + +@dataclass(eq=False, repr=False) +class SensorState(betterproto.Message): + meta_field_package_name_sensors: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + value: 'float | None' = betterproto.float_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class SensorsState(betterproto.Message): + meta_field_package_name_sensors: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + temperature: 'SensorState | None' = betterproto.message_field(2, optional=True) + light: 'SensorState | None' = betterproto.message_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class UsersAction(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class UsersSetUsersAction(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + users: 'list[UserState]' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class UsersCreateUserAction(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class UsersDeleteUserAction(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class UsersResetPasswordAction(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class UsersEvent(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class UsersCreateUserEvent(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class UsersDeleteUserEvent(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class UsersResetPasswordEvent(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class UserState(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + is_removable: bool = betterproto.bool_field(3) + + +@dataclass(eq=False, repr=False) +class UsersState(betterproto.Message): + meta_field_package_name_users: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + users: 'UsersStateUsers | None' = betterproto.message_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class UsersStateUsers(betterproto.Message): + items: 'list[UserState]' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class RPiConnectAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectEvent(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectStartDownloadingAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectDoneDownloadingAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectSetPendingAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectStatus(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + screen_sharing_sessions: int = betterproto.int64_field(2) + remote_shell_sessions: int = betterproto.int64_field(3) + + +@dataclass(eq=False, repr=False) +class RPiConnectSetStatusAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_installed: bool = betterproto.bool_field(2) + is_signed_in: bool = betterproto.bool_field(3) + status: 'RPiConnectStatus' = betterproto.message_field(4) + + +@dataclass(eq=False, repr=False) +class RPiConnectLoginEvent(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RPiConnectUpdateServiceStateAction(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_active: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RPiConnectState(betterproto.Message): + meta_field_package_name_rpi_connect: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_downloading: 'bool | None' = betterproto.bool_field(2, optional=True) + is_active: 'bool | None' = betterproto.bool_field(3, optional=True) + is_installed: 'bool | None' = betterproto.bool_field(4, optional=True) + is_signed_in: 'bool | None' = betterproto.bool_field(5, optional=True) + status: 'RPiConnectStatus | None' = betterproto.message_field(6, optional=True) + + +@dataclass(eq=False, repr=False) +class VsCodeAction(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeEvent(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeStartDownloadingAction(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeDoneDownloadingAction(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeSetPendingAction(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeStatus(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_service_installed: bool = betterproto.bool_field(2) + is_running: bool = betterproto.bool_field(3) + name: str = betterproto.string_field(4) + + +@dataclass(eq=False, repr=False) +class VsCodeSetStatusAction(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_binary_installed: bool = betterproto.bool_field(2) + is_logged_in: bool = betterproto.bool_field(3) + status: 'VsCodeStatus' = betterproto.message_field(4) + + +@dataclass(eq=False, repr=False) +class VsCodeLoginEvent(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeRestartEvent(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VsCodeState(betterproto.Message): + meta_field_package_name_vscode: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_pending: 'bool | None' = betterproto.bool_field(2, optional=True) + is_downloading: 'bool | None' = betterproto.bool_field(3, optional=True) + is_binary_installed: 'bool | None' = betterproto.bool_field(4, optional=True) + is_logged_in: 'bool | None' = betterproto.bool_field(5, optional=True) + status: 'VsCodeStatus | None' = betterproto.message_field(6, optional=True) + + +@dataclass(eq=False, repr=False) +class DockerAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DockerSetStatusAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + status: 'DockerStatus' = betterproto.enum_field(2) + + +@dataclass(eq=False, repr=False) +class DockerStoreUsernameAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + registry: str = betterproto.string_field(2) + username: str = betterproto.string_field(3) + + +@dataclass(eq=False, repr=False) +class DockerRemoveUsernameAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + registry: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class DockerImageAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + image: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class DockerImageSetStatusAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + status: 'ImageStatus' = betterproto.enum_field(2) + ports: 'DockerImageSetStatusActionPorts | None' = betterproto.message_field( + 3, optional=True, + ) + ip: 'str | None' = betterproto.string_field(4, optional=True) + image: str = betterproto.string_field(5) + + +@dataclass(eq=False, repr=False) +class DockerImageSetStatusActionPorts(betterproto.Message): + items: 'list[str]' = betterproto.string_field(1) + + +@dataclass(eq=False, repr=False) +class DockerImageSetDockerIdAction(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + docker_id: str = betterproto.string_field(2) + image: str = betterproto.string_field(3) + + +@dataclass(eq=False, repr=False) +class DockerEvent(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DockerServiceState(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + status: 'DockerStatus | None' = betterproto.enum_field(2, optional=True) + usernames: 'DockerServiceStateUsernamesDict | None' = betterproto.message_field( + 3, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DockerServiceStateUsernamesDict(betterproto.Message): + items: 'dict[str, str]' = betterproto.map_field( + 1, betterproto.TYPE_STRING, betterproto.TYPE_STRING, + ) + + +@dataclass(eq=False, repr=False) +class DockerImageEvent(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + image: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class DockerImageRegisterAppEvent(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + image: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class ImageState(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + status: 'ImageStatus | None' = betterproto.enum_field(3, optional=True) + container_ip: 'str | None' = betterproto.string_field(4, optional=True) + docker_id: 'str | None' = betterproto.string_field(5, optional=True) + ports: 'ImageStatePorts | None' = betterproto.message_field(6, optional=True) + + +@dataclass(eq=False, repr=False) +class ImageStatePorts(betterproto.Message): + items: 'list[str]' = betterproto.string_field(1) + + +@dataclass(eq=False, repr=False) +class DockerState(betterproto.Message): + meta_field_package_name_docker: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + service: 'DockerServiceState' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class DisplayAction(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DisplayEvent(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DisplayPauseAction(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DisplayResumeAction(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class DisplayRenderEvent(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + data: bytes = betterproto.bytes_field(2) + data_hash: int = betterproto.int64_field(3) + rectangle: 'list[int]' = betterproto.int64_field(4) + + +@dataclass(eq=False, repr=False) +class DisplayState(betterproto.Message): + meta_field_package_name_display: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_paused: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadAction(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: 'float | None' = betterproto.float_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadKeyUpAction(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: 'float | None' = betterproto.float_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadKeyDownAction(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: 'float | None' = betterproto.float_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadKeyPressAction(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: 'float | None' = betterproto.float_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadKeyReleaseAction(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: 'float | None' = betterproto.float_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class KeypadEvent(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: float = betterproto.float_field(3) + + +@dataclass(eq=False, repr=False) +class KeypadKeyPressEvent(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: float = betterproto.float_field(3) + + +@dataclass(eq=False, repr=False) +class KeypadKeyReleaseEvent(betterproto.Message): + meta_field_package_name_keypad: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + key: 'Key' = betterproto.enum_field(2) + time: float = betterproto.float_field(3) + + +@dataclass(eq=False, repr=False) +class VoiceAction(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VoiceEvent(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class VoiceUpdateAccessKeyStatus(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_access_key_set: bool = betterproto.bool_field(2) + + +@dataclass(eq=False, repr=False) +class VoiceSetEngineAction(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + engine: 'VoiceEngine' = betterproto.enum_field(2) + + +@dataclass(eq=False, repr=False) +class VoiceReadTextAction(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + text: str = betterproto.string_field(2) + piper_text: 'str | None' = betterproto.string_field(3, optional=True) + picovoice_text: 'str | None' = betterproto.string_field(4, optional=True) + speech_rate: 'float | None' = betterproto.float_field(5, optional=True) + engine: 'VoiceEngine | None' = betterproto.enum_field(6, optional=True) + + +@dataclass(eq=False, repr=False) +class VoiceSynthesizeTextEvent(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + text: str = betterproto.string_field(2) + piper_text: str = betterproto.string_field(3) + picovoice_text: str = betterproto.string_field(4) + speech_rate: 'float | None' = betterproto.float_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class VoiceState(betterproto.Message): + meta_field_package_name_voice: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_access_key_set: 'bool | None' = betterproto.bool_field(2, optional=True) + selected_engine: 'VoiceEngine | None' = betterproto.enum_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RgbRingEvent(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RgbRingSetIsConnectedAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_connected: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingSetIsBusyAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_busy: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingCommandAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RgbRingWaitableCommandAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + wait: 'int | None' = betterproto.int64_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingColorfulCommandAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + color: 'RgbColor | None' = betterproto.message_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingSetEnabledAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + enabled: 'bool | None' = betterproto.bool_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingSetAllAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + color: 'RgbColor | None' = betterproto.message_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingSetBrightnessAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + brightness: 'float | None' = betterproto.float_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingBlankAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class RgbRingRainbowAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + rounds: int = betterproto.int64_field(2) + wait: 'int | None' = betterproto.int64_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingProgressWheelStepAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + color: 'RgbColor | None' = betterproto.message_field(2, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingPulseAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + repetitions: 'int | None' = betterproto.int64_field(2, optional=True) + wait: 'int | None' = betterproto.int64_field(3, optional=True) + color: 'RgbColor | None' = betterproto.message_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingBlinkAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + repetitions: 'int | None' = betterproto.int64_field(2, optional=True) + wait: 'int | None' = betterproto.int64_field(3, optional=True) + color: 'RgbColor | None' = betterproto.message_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingSpinningWheelAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + length: 'int | None' = betterproto.int64_field(2, optional=True) + repetitions: 'int | None' = betterproto.int64_field(3, optional=True) + wait: 'int | None' = betterproto.int64_field(4, optional=True) + color: 'RgbColor | None' = betterproto.message_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingProgressWheelAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + percentage: 'float | None' = betterproto.float_field(2, optional=True) + color: 'RgbColor | None' = betterproto.message_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingFillUptoAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + percentage: 'float | None' = betterproto.float_field(2, optional=True) + wait: 'int | None' = betterproto.int64_field(3, optional=True) + color: 'RgbColor | None' = betterproto.message_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingFillDownfromAction(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + percentage: 'float | None' = betterproto.float_field(2, optional=True) + wait: 'int | None' = betterproto.int64_field(3, optional=True) + color: 'RgbColor | None' = betterproto.message_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class RgbRingCommandEvent(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + command: 'list[str]' = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class RgbRingState(betterproto.Message): + meta_field_package_name_rgb_ring: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_connected: bool = betterproto.bool_field(2) + is_busy: bool = betterproto.bool_field(3) + + +@dataclass(eq=False, repr=False) +class RgbColorElement(betterproto.Message): + float: builtins.float = betterproto.float_field(1, group='rgb_color_element') + int64: int = betterproto.int64_field(2, group='rgb_color_element') + + +@dataclass(eq=False, repr=False) +class RgbColor(betterproto.Message): + items: 'list[RgbColorElement]' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class CameraAction(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class CameraStartViewfinderAction(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + pattern: str = betterproto.string_field(3) + + +@dataclass(eq=False, repr=False) +class CameraEvent(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class CameraStartViewfinderEvent(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + pattern: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class CameraStopViewfinderEvent(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class CameraReportBarcodeAction(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + codes: 'list[str]' = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class CameraBarcodeEvent(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + code: str = betterproto.string_field(3) + group_dict: 'dict[str, str]' = betterproto.map_field( + 4, betterproto.TYPE_STRING, betterproto.TYPE_STRING, + ) + + +@dataclass(eq=False, repr=False) +class InputDescription(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + pattern: str = betterproto.string_field(3) + + +@dataclass(eq=False, repr=False) +class CameraState(betterproto.Message): + meta_field_package_name_camera: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + current: 'InputDescription | None' = betterproto.message_field(2, optional=True) + is_viewfinder_active: bool = betterproto.bool_field(3) + queue: 'list[InputDescription]' = betterproto.message_field(4) + + +@dataclass(eq=False, repr=False) +class IpAction(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class IpEvent(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class IpUpdateInterfacesAction(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + interfaces: 'list[IpNetworkInterface]' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class IpSetIsConnectedAction(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_connected: bool = betterproto.bool_field(2) + + +@dataclass(eq=False, repr=False) +class IpNetworkInterface(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + name: str = betterproto.string_field(2) + ip_addresses: 'list[str]' = betterproto.string_field(3) + + +@dataclass(eq=False, repr=False) +class IpState(betterproto.Message): + meta_field_package_name_ip: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + interfaces: 'list[IpNetworkInterface]' = betterproto.message_field(2) + is_connected: 'bool | None' = betterproto.bool_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class NotificationActionItem(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + background_color: 'str | None' = betterproto.string_field(2, optional=True) + dismiss_notification: 'bool | None' = betterproto.bool_field(3, optional=True) + key: 'str | None' = betterproto.string_field(4, optional=True) + label: 'str | None' = betterproto.string_field(5, optional=True) + color: 'str | None' = betterproto.string_field(6, optional=True) + icon: 'str | None' = betterproto.string_field(7, optional=True) + is_short: 'bool | None' = betterproto.bool_field(8, optional=True) + opacity: 'float | None' = betterproto.float_field(9, optional=True) + progress: 'float | None' = betterproto.float_field(10, optional=True) + + +@dataclass(eq=False, repr=False) +class NotificationDispatchItem(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + operation: 'NotificationDispatchItemOperation' = betterproto.message_field(2) + key: 'str | None' = betterproto.string_field(3, optional=True) + label: 'str | None' = betterproto.string_field(4, optional=True) + color: 'str | None' = betterproto.string_field(5, optional=True) + background_color: 'str | None' = betterproto.string_field(6, optional=True) + icon: 'str | None' = betterproto.string_field(7, optional=True) + is_short: 'bool | None' = betterproto.bool_field(8, optional=True) + opacity: 'float | None' = betterproto.float_field(9, optional=True) + progress: 'float | None' = betterproto.float_field(10, optional=True) + dismiss_notification: 'bool | None' = betterproto.bool_field(11, optional=True) + + +@dataclass(eq=False, repr=False) +class NotificationDispatchItemOperation(betterproto.Message): + ubo_event: 'Event' = betterproto.message_field(1, group='operation') + ubo_action: 'Action' = betterproto.message_field(2, group='operation') + + +@dataclass(eq=False, repr=False) +class NotificationExtraInformation(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + text: str = betterproto.string_field(2) + piper_text: 'str | None' = betterproto.string_field(3, optional=True) + picovoice_text: 'str | None' = betterproto.string_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class Notification(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: 'str | None' = betterproto.string_field(2, optional=True) + title: str = betterproto.string_field(3) + content: str = betterproto.string_field(4) + extra_information: 'NotificationExtraInformation | None' = ( + betterproto.message_field(5, optional=True) + ) + importance: 'Importance | None' = betterproto.enum_field(6, optional=True) + chime: 'Chime | None' = betterproto.enum_field(7, optional=True) + timestamp: 'int | None' = betterproto.int64_field(8, optional=True) + is_read: 'bool | None' = betterproto.bool_field(9, optional=True) + sender: 'str | None' = betterproto.string_field(10, optional=True) + actions: 'NotificationActions | None' = betterproto.message_field(11, optional=True) + icon: 'str | None' = betterproto.string_field(12, optional=True) + color: 'str | None' = betterproto.string_field(13, optional=True) + expiration_timestamp: 'int | None' = betterproto.int64_field(14, optional=True) + display_type: 'NotificationDisplayType | None' = betterproto.enum_field( + 15, optional=True, + ) + flash_time: 'float | None' = betterproto.float_field(16, optional=True) + dismissable: 'bool | None' = betterproto.bool_field(17, optional=True) + dismiss_on_close: 'bool | None' = betterproto.bool_field(18, optional=True) + on_close: 'NotificationOnClose | None' = betterproto.message_field( + 19, optional=True, + ) + blink: 'bool | None' = betterproto.bool_field(20, optional=True) + progress: 'float | None' = betterproto.float_field(21, optional=True) + progress_weight: 'float | None' = betterproto.float_field(22, optional=True) + + +@dataclass(eq=False, repr=False) +class NotificationActionsItem(betterproto.Message): + notification_action_item: 'NotificationActionItem' = betterproto.message_field( + 1, group='actions_item', + ) + notification_dispatch_item: 'NotificationDispatchItem' = betterproto.message_field( + 2, group='actions_item', + ) + + +@dataclass(eq=False, repr=False) +class NotificationActions(betterproto.Message): + items: 'list[NotificationActionsItem]' = betterproto.message_field(1) + + +@dataclass(eq=False, repr=False) +class NotificationOnClose(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class NotificationsAction(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class NotificationsAddAction(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + notification: 'Notification' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class NotificationsClearAction(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + notification: 'Notification' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class NotificationsClearByIdAction(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class NotificationsClearAllAction(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class NotificationsEvent(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class NotificationsClearEvent(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + notification: 'Notification' = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class NotificationsDisplayEvent(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + notification: 'Notification' = betterproto.message_field(2) + index: 'int | None' = betterproto.int64_field(3, optional=True) + count: 'int | None' = betterproto.int64_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class NotificationsState(betterproto.Message): + meta_field_package_name_notifications: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + notifications: 'list[Notification]' = betterproto.message_field(2) + unread_count: int = betterproto.int64_field(3) + progress: 'float | None' = betterproto.float_field(4, optional=True) + + +@dataclass(eq=False, repr=False) +class AudioAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class AudioSetVolumeAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + volume: float = betterproto.float_field(2) + device: 'AudioDevice' = betterproto.enum_field(3) + + +@dataclass(eq=False, repr=False) +class AudioChangeVolumeAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + amount: float = betterproto.float_field(2) + device: 'AudioDevice' = betterproto.enum_field(3) + + +@dataclass(eq=False, repr=False) +class AudioSetMuteStatusAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_mute: bool = betterproto.bool_field(2) + device: 'AudioDevice' = betterproto.enum_field(3) + + +@dataclass(eq=False, repr=False) +class AudioToggleMuteStatusAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + device: 'AudioDevice' = betterproto.enum_field(2) + + +@dataclass(eq=False, repr=False) +class AudioPlayChimeAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + name: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class AudioPlayAudioAction(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: 'str | None' = betterproto.string_field(2, optional=True) + sample: bytes = betterproto.bytes_field(3) + channels: int = betterproto.int64_field(4) + rate: int = betterproto.int64_field(5) + width: int = betterproto.int64_field(6) + + +@dataclass(eq=False, repr=False) +class AudioEvent(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class AudioPlayChimeEvent(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + name: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class AudioPlayAudioEvent(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: 'str | None' = betterproto.string_field(2, optional=True) + sample: bytes = betterproto.bytes_field(3) + channels: int = betterproto.int64_field(4) + rate: int = betterproto.int64_field(5) + width: int = betterproto.int64_field(6) + + +@dataclass(eq=False, repr=False) +class AudioPlaybackDoneEvent(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + id: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class AudioState(betterproto.Message): + meta_field_package_name_audio: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + playback_volume: 'float | None' = betterproto.float_field(2, optional=True) + is_playback_mute: 'bool | None' = betterproto.bool_field(3, optional=True) + capture_volume: 'float | None' = betterproto.float_field(4, optional=True) + is_capture_mute: 'bool | None' = betterproto.bool_field(5, optional=True) + + +@dataclass(eq=False, repr=False) +class SshAction(betterproto.Message): + meta_field_package_name_ssh: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class SshUpdateStateAction(betterproto.Message): + meta_field_package_name_ssh: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_active: 'bool | None' = betterproto.bool_field(2, optional=True) + is_enabled: 'bool | None' = betterproto.bool_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class SshClearEnabledStateAction(betterproto.Message): + meta_field_package_name_ssh: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + + +@dataclass(eq=False, repr=False) +class SshState(betterproto.Message): + meta_field_package_name_ssh: 'str | None' = betterproto.string_field( + 1, optional=True, + ) + is_active: 'bool | None' = betterproto.bool_field(2, optional=True) + is_enabled: 'bool | None' = betterproto.bool_field(3, optional=True) + + +@dataclass(eq=False, repr=False) +class Action(betterproto.Message): + light_dm_action: 'LightDmAction' = betterproto.message_field(1, group='action') + light_dm_update_state_action: 'LightDmUpdateStateAction' = ( + betterproto.message_field(2, group='action') + ) + light_dm_clear_enabled_state_action: 'LightDmClearEnabledStateAction' = ( + betterproto.message_field(3, group='action') + ) + wi_fi_action: 'WiFiAction' = betterproto.message_field(4, group='action') + wi_fi_set_has_visited_onboarding_action: 'WiFiSetHasVisitedOnboardingAction' = ( + betterproto.message_field(5, group='action') + ) + wi_fi_update_action: 'WiFiUpdateAction' = betterproto.message_field( + 6, group='action', + ) + wi_fi_update_request_action: 'WiFiUpdateRequestAction' = betterproto.message_field( + 7, group='action', + ) + sensors_action: 'SensorsAction' = betterproto.message_field(8, group='action') + sensors_report_reading_action: 'SensorsReportReadingAction' = ( + betterproto.message_field(9, group='action') + ) + users_action: 'UsersAction' = betterproto.message_field(10, group='action') + users_set_users_action: 'UsersSetUsersAction' = betterproto.message_field( + 11, group='action', + ) + users_create_user_action: 'UsersCreateUserAction' = betterproto.message_field( + 12, group='action', + ) + users_delete_user_action: 'UsersDeleteUserAction' = betterproto.message_field( + 13, group='action', + ) + users_reset_password_action: 'UsersResetPasswordAction' = betterproto.message_field( + 14, group='action', + ) + r_pi_connect_action: 'RPiConnectAction' = betterproto.message_field( + 15, group='action', + ) + r_pi_connect_start_downloading_action: 'RPiConnectStartDownloadingAction' = ( + betterproto.message_field(16, group='action') + ) + r_pi_connect_done_downloading_action: 'RPiConnectDoneDownloadingAction' = ( + betterproto.message_field(17, group='action') + ) + r_pi_connect_set_pending_action: 'RPiConnectSetPendingAction' = ( + betterproto.message_field(18, group='action') + ) + r_pi_connect_set_status_action: 'RPiConnectSetStatusAction' = ( + betterproto.message_field(19, group='action') + ) + r_pi_connect_update_service_state_action: 'RPiConnectUpdateServiceStateAction' = ( + betterproto.message_field(20, group='action') + ) + vs_code_action: 'VsCodeAction' = betterproto.message_field(21, group='action') + vs_code_start_downloading_action: 'VsCodeStartDownloadingAction' = ( + betterproto.message_field(22, group='action') + ) + vs_code_done_downloading_action: 'VsCodeDoneDownloadingAction' = ( + betterproto.message_field(23, group='action') + ) + vs_code_set_pending_action: 'VsCodeSetPendingAction' = betterproto.message_field( + 24, group='action', + ) + vs_code_set_status_action: 'VsCodeSetStatusAction' = betterproto.message_field( + 25, group='action', + ) + docker_action: 'DockerAction' = betterproto.message_field(26, group='action') + docker_set_status_action: 'DockerSetStatusAction' = betterproto.message_field( + 27, group='action', + ) + docker_store_username_action: 'DockerStoreUsernameAction' = ( + betterproto.message_field(28, group='action') + ) + docker_remove_username_action: 'DockerRemoveUsernameAction' = ( + betterproto.message_field(29, group='action') + ) + docker_image_action: 'DockerImageAction' = betterproto.message_field( + 30, group='action', + ) + docker_image_set_status_action: 'DockerImageSetStatusAction' = ( + betterproto.message_field(31, group='action') + ) + docker_image_set_docker_id_action: 'DockerImageSetDockerIdAction' = ( + betterproto.message_field(32, group='action') + ) + display_action: 'DisplayAction' = betterproto.message_field(33, group='action') + display_pause_action: 'DisplayPauseAction' = betterproto.message_field( + 34, group='action', + ) + display_resume_action: 'DisplayResumeAction' = betterproto.message_field( + 35, group='action', + ) + keypad_action: 'KeypadAction' = betterproto.message_field(36, group='action') + keypad_key_up_action: 'KeypadKeyUpAction' = betterproto.message_field( + 37, group='action', + ) + keypad_key_down_action: 'KeypadKeyDownAction' = betterproto.message_field( + 38, group='action', + ) + keypad_key_press_action: 'KeypadKeyPressAction' = betterproto.message_field( + 39, group='action', + ) + keypad_key_release_action: 'KeypadKeyReleaseAction' = betterproto.message_field( + 40, group='action', + ) + voice_action: 'VoiceAction' = betterproto.message_field(41, group='action') + voice_set_engine_action: 'VoiceSetEngineAction' = betterproto.message_field( + 42, group='action', + ) + voice_read_text_action: 'VoiceReadTextAction' = betterproto.message_field( + 43, group='action', + ) + rgb_ring_action: 'RgbRingAction' = betterproto.message_field(44, group='action') + rgb_ring_set_is_connected_action: 'RgbRingSetIsConnectedAction' = ( + betterproto.message_field(45, group='action') + ) + rgb_ring_set_is_busy_action: 'RgbRingSetIsBusyAction' = betterproto.message_field( + 46, group='action', + ) + rgb_ring_command_action: 'RgbRingCommandAction' = betterproto.message_field( + 47, group='action', + ) + rgb_ring_waitable_command_action: 'RgbRingWaitableCommandAction' = ( + betterproto.message_field(48, group='action') + ) + rgb_ring_colorful_command_action: 'RgbRingColorfulCommandAction' = ( + betterproto.message_field(49, group='action') + ) + rgb_ring_set_enabled_action: 'RgbRingSetEnabledAction' = betterproto.message_field( + 50, group='action', + ) + rgb_ring_set_all_action: 'RgbRingSetAllAction' = betterproto.message_field( + 51, group='action', + ) + rgb_ring_set_brightness_action: 'RgbRingSetBrightnessAction' = ( + betterproto.message_field(52, group='action') + ) + rgb_ring_blank_action: 'RgbRingBlankAction' = betterproto.message_field( + 53, group='action', + ) + rgb_ring_rainbow_action: 'RgbRingRainbowAction' = betterproto.message_field( + 54, group='action', + ) + rgb_ring_progress_wheel_step_action: 'RgbRingProgressWheelStepAction' = ( + betterproto.message_field(55, group='action') + ) + rgb_ring_pulse_action: 'RgbRingPulseAction' = betterproto.message_field( + 56, group='action', + ) + rgb_ring_blink_action: 'RgbRingBlinkAction' = betterproto.message_field( + 57, group='action', + ) + rgb_ring_spinning_wheel_action: 'RgbRingSpinningWheelAction' = ( + betterproto.message_field(58, group='action') + ) + rgb_ring_progress_wheel_action: 'RgbRingProgressWheelAction' = ( + betterproto.message_field(59, group='action') + ) + rgb_ring_fill_upto_action: 'RgbRingFillUptoAction' = betterproto.message_field( + 60, group='action', + ) + rgb_ring_fill_downfrom_action: 'RgbRingFillDownfromAction' = ( + betterproto.message_field(61, group='action') + ) + camera_action: 'CameraAction' = betterproto.message_field(62, group='action') + camera_start_viewfinder_action: 'CameraStartViewfinderAction' = ( + betterproto.message_field(63, group='action') + ) + camera_report_barcode_action: 'CameraReportBarcodeAction' = ( + betterproto.message_field(64, group='action') + ) + ip_action: 'IpAction' = betterproto.message_field(65, group='action') + ip_update_interfaces_action: 'IpUpdateInterfacesAction' = betterproto.message_field( + 66, group='action', + ) + ip_set_is_connected_action: 'IpSetIsConnectedAction' = betterproto.message_field( + 67, group='action', + ) + notifications_action: 'NotificationsAction' = betterproto.message_field( + 68, group='action', + ) + notifications_add_action: 'NotificationsAddAction' = betterproto.message_field( + 69, group='action', + ) + notifications_clear_action: 'NotificationsClearAction' = betterproto.message_field( + 70, group='action', + ) + notifications_clear_by_id_action: 'NotificationsClearByIdAction' = ( + betterproto.message_field(71, group='action') + ) + notifications_clear_all_action: 'NotificationsClearAllAction' = ( + betterproto.message_field(72, group='action') + ) + audio_action: 'AudioAction' = betterproto.message_field(73, group='action') + audio_set_volume_action: 'AudioSetVolumeAction' = betterproto.message_field( + 74, group='action', + ) + audio_change_volume_action: 'AudioChangeVolumeAction' = betterproto.message_field( + 75, group='action', + ) + audio_set_mute_status_action: 'AudioSetMuteStatusAction' = ( + betterproto.message_field(76, group='action') + ) + audio_toggle_mute_status_action: 'AudioToggleMuteStatusAction' = ( + betterproto.message_field(77, group='action') + ) + audio_play_chime_action: 'AudioPlayChimeAction' = betterproto.message_field( + 78, group='action', + ) + audio_play_audio_action: 'AudioPlayAudioAction' = betterproto.message_field( + 79, group='action', + ) + ssh_action: 'SshAction' = betterproto.message_field(80, group='action') + ssh_update_state_action: 'SshUpdateStateAction' = betterproto.message_field( + 81, group='action', + ) + ssh_clear_enabled_state_action: 'SshClearEnabledStateAction' = ( + betterproto.message_field(82, group='action') + ) + + +@dataclass(eq=False, repr=False) +class Event(betterproto.Message): + screenshot_event: 'ScreenshotEvent' = betterproto.message_field(1, group='event') + snapshot_event: 'SnapshotEvent' = betterproto.message_field(2, group='event') + wi_fi_event: 'WiFiEvent' = betterproto.message_field(3, group='event') + wi_fi_update_request_event: 'WiFiUpdateRequestEvent' = betterproto.message_field( + 4, group='event', + ) + users_event: 'UsersEvent' = betterproto.message_field(5, group='event') + users_create_user_event: 'UsersCreateUserEvent' = betterproto.message_field( + 6, group='event', + ) + users_delete_user_event: 'UsersDeleteUserEvent' = betterproto.message_field( + 7, group='event', + ) + users_reset_password_event: 'UsersResetPasswordEvent' = betterproto.message_field( + 8, group='event', + ) + r_pi_connect_event: 'RPiConnectEvent' = betterproto.message_field(9, group='event') + r_pi_connect_login_event: 'RPiConnectLoginEvent' = betterproto.message_field( + 10, group='event', + ) + vs_code_event: 'VsCodeEvent' = betterproto.message_field(11, group='event') + vs_code_login_event: 'VsCodeLoginEvent' = betterproto.message_field( + 12, group='event', + ) + vs_code_restart_event: 'VsCodeRestartEvent' = betterproto.message_field( + 13, group='event', + ) + docker_event: 'DockerEvent' = betterproto.message_field(14, group='event') + docker_image_event: 'DockerImageEvent' = betterproto.message_field( + 15, group='event', + ) + docker_image_register_app_event: 'DockerImageRegisterAppEvent' = ( + betterproto.message_field(16, group='event') + ) + display_event: 'DisplayEvent' = betterproto.message_field(17, group='event') + display_render_event: 'DisplayRenderEvent' = betterproto.message_field( + 18, group='event', + ) + keypad_event: 'KeypadEvent' = betterproto.message_field(19, group='event') + keypad_key_press_event: 'KeypadKeyPressEvent' = betterproto.message_field( + 20, group='event', + ) + keypad_key_release_event: 'KeypadKeyReleaseEvent' = betterproto.message_field( + 21, group='event', + ) + voice_event: 'VoiceEvent' = betterproto.message_field(22, group='event') + voice_synthesize_text_event: 'VoiceSynthesizeTextEvent' = betterproto.message_field( + 23, group='event', + ) + rgb_ring_event: 'RgbRingEvent' = betterproto.message_field(24, group='event') + rgb_ring_command_event: 'RgbRingCommandEvent' = betterproto.message_field( + 25, group='event', + ) + camera_event: 'CameraEvent' = betterproto.message_field(26, group='event') + camera_start_viewfinder_event: 'CameraStartViewfinderEvent' = ( + betterproto.message_field(27, group='event') + ) + camera_stop_viewfinder_event: 'CameraStopViewfinderEvent' = ( + betterproto.message_field(28, group='event') + ) + camera_barcode_event: 'CameraBarcodeEvent' = betterproto.message_field( + 29, group='event', + ) + ip_event: 'IpEvent' = betterproto.message_field(30, group='event') + notifications_event: 'NotificationsEvent' = betterproto.message_field( + 31, group='event', + ) + notifications_clear_event: 'NotificationsClearEvent' = betterproto.message_field( + 32, group='event', + ) + notifications_display_event: 'NotificationsDisplayEvent' = ( + betterproto.message_field(33, group='event') + ) + audio_event: 'AudioEvent' = betterproto.message_field(34, group='event') + audio_play_chime_event: 'AudioPlayChimeEvent' = betterproto.message_field( + 35, group='event', + ) + audio_play_audio_event: 'AudioPlayAudioEvent' = betterproto.message_field( + 36, group='event', + ) + audio_playback_done_event: 'AudioPlaybackDoneEvent' = betterproto.message_field( + 37, group='event', + ) diff --git a/ubo_app/rpc/message_to_object.py b/ubo_app/rpc/message_to_object.py new file mode 100644 index 00000000..bf5b9d76 --- /dev/null +++ b/ubo_app/rpc/message_to_object.py @@ -0,0 +1,115 @@ +# ruff: noqa: SLF001, S101, D100, D103 +from __future__ import annotations + +import enum +import importlib +from datetime import UTC, datetime +from typing import TypeAlias, TypeVar, cast, overload + +import betterproto +import betterproto.casing +from betterproto import Enum +from immutable import Immutable + +ReturnType: TypeAlias = ( + Immutable + | enum.Enum + | int + | float + | str + | bool + | None + | datetime + | list['ReturnType'] +) + +META_FIELD_PREFIX_PACKAGE_NAME = 'meta_field_package_name_' + + +def get_class(message: betterproto.Message) -> type | None: + source_class = type(message) + class_name = source_class.__name__ + + first_field_name = source_class._betterproto.field_name_by_number[1] + if first_field_name.startswith(META_FIELD_PREFIX_PACKAGE_NAME): + package_name = first_field_name[len(META_FIELD_PREFIX_PACKAGE_NAME) :] + else: + return None + + destination_module_path = f'ubo_app.store.services.{package_name}' + destination_module = importlib.import_module(destination_module_path) + + return getattr(destination_module, class_name, None) + + +def reduce_group(message: betterproto.Message) -> betterproto.Message: + assert len(message._group_current) == 1 + attribute = next(iter(message._group_current.values())) + + return getattr(message, attribute) + + +T = TypeVar('T', bound=Immutable) + + +@overload +def rebuild_object( + message: betterproto.Message | list[betterproto.Message], + expected_type: type[T], +) -> T: ... +@overload +def rebuild_object( + message: betterproto.Message | list[betterproto.Message], +) -> ReturnType: ... +def rebuild_object( # noqa: C901 + message: betterproto.Message | list[betterproto.Message], + expected_type: type[T] | None = None, +) -> ReturnType | T: + if isinstance(message, int | float | str | bool | None): + return cast(ReturnType, message) + + if isinstance(message, list): + return [rebuild_object(item) for item in message] + + if hasattr(message, '_group_current') and len(message._group_current) > 0: + return rebuild_object(reduce_group(message)) + + keys = message._betterproto_meta.sorted_field_names + if len(keys) == 1 and keys[0] == 'items': + return [rebuild_object(item) for item in getattr(message, 'items', [])] + + destination_class = get_class(message) + if expected_type and ( + destination_class is None or not issubclass(destination_class, expected_type) + ): + msg = f'Expected {expected_type}, got {destination_class}' + raise ValueError(msg) + + fields = { + betterproto.casing.snake_case(key): datetime.fromtimestamp( + getattr(message, key), + tz=UTC, + ) + if key.endswith('_timestamp') + else rebuild_object(getattr(message, key)) + for key in keys + if not key.startswith('meta_field_') and getattr(message, key) is not None + } + + if len(fields) == 1 and 'list' in fields: + return fields['list'] + + if destination_class is None: + msg = f'Class not found for {message}' + raise ValueError(msg) + + if isinstance(message, Enum) and message.name: + if message.name == 'UNSPECIFIED': + return None + return getattr(destination_class, message.name) + + if isinstance(destination_class, type) and issubclass(destination_class, Immutable): + return destination_class(**fields) + + msg = f'Parsing {message} is not implemented yet' + raise NotImplementedError(msg) diff --git a/ubo_app/rpc/proto/store/v1/store.proto b/ubo_app/rpc/proto/store/v1/store.proto new file mode 100644 index 00000000..1b8fcf67 --- /dev/null +++ b/ubo_app/rpc/proto/store/v1/store.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package store.v1; + +import "ubo/v1/ubo.proto"; + +message DispatchActionRequest { + ubo.v1.Action action = 1; +} + +message DispatchEventRequest { + ubo.v1.Event event = 1; +} + +message DispatchActionResponse {} +message DispatchEventResponse {} + +message SubscribeEventRequest { + ubo.v1.Event event = 1; +} + +message SubscribeEventResponse { + ubo.v1.Event event = 1; +} + +service StoreService { + rpc DispatchAction(DispatchActionRequest) returns (DispatchActionResponse); + rpc DispatchEvent(DispatchEventRequest) returns (DispatchEventResponse); + rpc SubscribeEvent(SubscribeEventRequest) returns (stream SubscribeEventResponse); +} diff --git a/ubo_app/rpc/proto/ubo/v1/ubo.proto b/ubo_app/rpc/proto/ubo/v1/ubo.proto new file mode 100644 index 00000000..9fc6908f --- /dev/null +++ b/ubo_app/rpc/proto/ubo/v1/ubo.proto @@ -0,0 +1,1419 @@ +syntax = "proto3"; + +package ubo.v1; + +import "package_info/v1/package_info.proto"; + +message BaseMenu { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + string title = 2; + repeated Item items = 3; + optional string placeholder = 4; +} + +message HeadedMenu { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + string heading = 2; + string sub_heading = 3; + string title = 4; + repeated Item items = 5; + optional string placeholder = 6; +} + +message HeadlessMenu { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + string title = 2; + repeated Item items = 3; + optional string placeholder = 4; +} + +message Item { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + optional string key = 2; + optional string label = 3; + optional string color = 4; + optional string background_color = 5; + optional string icon = 6; + optional bool is_short = 7; + optional float opacity = 8; + optional float progress = 9; +} + +message ActionItem { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + optional string key = 2; + optional string label = 3; + optional string color = 4; + optional string background_color = 5; + optional string icon = 6; + optional bool is_short = 7; + optional float opacity = 8; + optional float progress = 9; +} + +message ApplicationItem { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + string application = 2; + optional string key = 3; + optional string label = 4; + optional string color = 5; + optional string background_color = 6; + optional string icon = 7; + optional bool is_short = 8; + optional float opacity = 9; + optional float progress = 10; +} + +message SubMenuItem { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; + Menu sub_menu = 2; + optional string key = 3; + optional string label = 4; + optional string color = 5; + optional string background_color = 6; + optional string icon = 7; + optional bool is_short = 8; + optional float opacity = 9; + optional float progress = 10; +} + +message Subscribable { + option (package_info.v1.package_name) = "menu"; + optional string meta_field_package_name_menu = 1; +} + +message Menu { + oneof menu { + HeadedMenu headed_menu = 1; + HeadlessMenu headless_menu = 2; + } +} +message ScreenshotEvent { + option (package_info.v1.package_name) = "operations"; + optional string meta_field_package_name_operations = 1; +} + +message SnapshotEvent { + option (package_info.v1.package_name) = "operations"; + optional string meta_field_package_name_operations = 1; +} + +message UboAction { + oneof ubo_action { + VoiceAction voice_action = 1; + SSHAction ssh_action = 2; + RgbRingAction rgb_ring_action = 3; + NotificationsAction notifications_action = 4; + LightDMAction light_dm_action = 5; + IpAction ip_action = 6; + DisplayAction display_action = 7; + CameraAction camera_action = 8; + AudioAction audio_action = 9; + DockerAction docker_action = 10; + KeypadAction keypad_action = 11; + RPiConnectAction r_pi_connect_action = 12; + SensorsAction sensors_action = 13; + UsersAction users_action = 14; + VSCodeAction vs_code_action = 15; + WiFiAction wi_fi_action = 16; + } +} + +message UboEvent { + oneof ubo_event { + UsersEvent users_event = 1; + NotificationsEvent notifications_event = 2; + IpEvent ip_event = 3; + DisplayEvent display_event = 4; + AudioEvent audio_event = 5; + ScreenshotEvent screenshot_event = 6; + CameraEvent camera_event = 7; + KeypadEvent keypad_event = 8; + SnapshotEvent snapshot_event = 9; + WiFiEvent wi_fi_event = 10; + } +} +message DispatchItem { + option (package_info.v1.package_name) = "dispatch_action"; + optional string meta_field_package_name_dispatch_action = 1; + + message Operation { + oneof operation { + Event ubo_event = 1; + Action ubo_action = 2; + } + } + Operation operation = 2; + optional string key = 3; + optional string label = 4; + optional string color = 5; + optional string background_color = 6; + optional string icon = 7; + optional bool is_short = 8; + optional float opacity = 9; + optional float progress = 10; +} + +message LightDMAction { + option (package_info.v1.package_name) = "lightdm"; + optional string meta_field_package_name_lightdm = 1; +} + +message LightDMUpdateStateAction { + option (package_info.v1.package_name) = "lightdm"; + optional string meta_field_package_name_lightdm = 1; + optional bool is_active = 2; + optional bool is_enabled = 3; + optional bool is_installed = 4; + optional bool is_installing = 5; +} + +message LightDMClearEnabledStateAction { + option (package_info.v1.package_name) = "lightdm"; + optional string meta_field_package_name_lightdm = 1; +} + +message LightDMState { + option (package_info.v1.package_name) = "lightdm"; + optional string meta_field_package_name_lightdm = 1; + optional bool is_active = 2; + optional bool is_enabled = 3; + optional bool is_installed = 4; + optional bool is_installing = 5; +} + +enum WiFiType { + WI_FI_TYPE_UNSPECIFIED = 0; + WI_FI_TYPE_WEP = 1; + WI_FI_TYPE_WPA = 2; + WI_FI_TYPE_WPA2 = 3; + WI_FI_TYPE_NOPASS = 4; +} + +enum ConnectionState { + CONNECTION_STATE_UNSPECIFIED = 0; + CONNECTION_STATE_CONNECTED = 1; + CONNECTION_STATE_CONNECTING = 2; + CONNECTION_STATE_DISCONNECTED = 3; + CONNECTION_STATE_UNKNOWN = 4; +} + +enum GlobalWiFiState { + GLOBAL_WI_FI_STATE_UNSPECIFIED = 0; + GLOBAL_WI_FI_STATE_CONNECTED = 1; + GLOBAL_WI_FI_STATE_DISCONNECTED = 2; + GLOBAL_WI_FI_STATE_PENDING = 3; + GLOBAL_WI_FI_STATE_NEEDS_ATTENTION = 4; + GLOBAL_WI_FI_STATE_UNKNOWN = 5; +} + +message WiFiConnection { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; + string ssid = 2; + optional ConnectionState state = 3; + optional int64 signal_strength = 4; + optional string password = 5; + optional WiFiType type = 6; + optional bool hidden = 7; +} + +message WiFiAction { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; +} + +message WiFiSetHasVisitedOnboardingAction { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; + bool has_visited_onboarding = 2; +} + +message WiFiUpdateAction { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; + repeated WiFiConnection connections = 2; + GlobalWiFiState state = 3; + WiFiConnection current_connection = 4; +} + +message WiFiUpdateRequestAction { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; + optional bool reset = 2; +} + +message WiFiEvent { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; +} + +message WiFiUpdateRequestEvent { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; +} + +message WiFiState { + option (package_info.v1.package_name) = "wifi"; + optional string meta_field_package_name_wifi = 1; + repeated WiFiConnection connections = 2; + GlobalWiFiState state = 3; + WiFiConnection current_connection = 4; + optional bool has_visited_onboarding = 5; +} + +enum Sensor { + SENSOR_UNSPECIFIED = 0; + SENSOR_TEMPERATURE = 1; + SENSOR_LIGHT = 2; +} + +message SensorsAction { + option (package_info.v1.package_name) = "sensors"; + optional string meta_field_package_name_sensors = 1; +} + +message SensorsReportReadingAction { + option (package_info.v1.package_name) = "sensors"; + optional string meta_field_package_name_sensors = 1; + Sensor sensor = 2; + float reading = 3; + int64 timestamp = 4; +} + +message SensorState { + option (package_info.v1.package_name) = "sensors"; + optional string meta_field_package_name_sensors = 1; + optional float value = 2; +} + +message SensorsState { + option (package_info.v1.package_name) = "sensors"; + optional string meta_field_package_name_sensors = 1; + optional SensorState temperature = 2; + optional SensorState light = 3; +} + +message UsersAction { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; +} + +message UsersSetUsersAction { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + repeated UserState users = 2; +} + +message UsersCreateUserAction { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; +} + +message UsersDeleteUserAction { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + string id = 2; +} + +message UsersResetPasswordAction { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + string id = 2; +} + +message UsersEvent { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; +} + +message UsersCreateUserEvent { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; +} + +message UsersDeleteUserEvent { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + string id = 2; +} + +message UsersResetPasswordEvent { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + string id = 2; +} + +message UserState { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + string id = 2; + bool is_removable = 3; +} + +message UsersState { + option (package_info.v1.package_name) = "users"; + optional string meta_field_package_name_users = 1; + + message Users { + repeated UserState items = 1; + } + optional Users users = 2; +} + +message RPiConnectAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectEvent { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectStartDownloadingAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectDoneDownloadingAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectSetPendingAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectStatus { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; + int64 screen_sharing_sessions = 2; + int64 remote_shell_sessions = 3; +} + +message RPiConnectSetStatusAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; + bool is_installed = 2; + bool is_signed_in = 3; + RPiConnectStatus status = 4; +} + +message RPiConnectLoginEvent { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; +} + +message RPiConnectUpdateServiceStateAction { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; + optional bool is_active = 2; +} + +message RPiConnectState { + option (package_info.v1.package_name) = "rpi_connect"; + optional string meta_field_package_name_rpi_connect = 1; + optional bool is_downloading = 2; + optional bool is_active = 3; + optional bool is_installed = 4; + optional bool is_signed_in = 5; + optional RPiConnectStatus status = 6; +} + +message VSCodeAction { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeEvent { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeStartDownloadingAction { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeDoneDownloadingAction { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeSetPendingAction { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeStatus { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; + bool is_service_installed = 2; + bool is_running = 3; + string name = 4; +} + +message VSCodeSetStatusAction { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; + bool is_binary_installed = 2; + bool is_logged_in = 3; + VSCodeStatus status = 4; +} + +message VSCodeLoginEvent { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeRestartEvent { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; +} + +message VSCodeState { + option (package_info.v1.package_name) = "vscode"; + optional string meta_field_package_name_vscode = 1; + optional bool is_pending = 2; + optional bool is_downloading = 3; + optional bool is_binary_installed = 4; + optional bool is_logged_in = 5; + optional VSCodeStatus status = 6; +} + +enum DockerStatus { + DOCKER_STATUS_UNSPECIFIED = 0; + DOCKER_STATUS_UNKNOWN = 1; + DOCKER_STATUS_NOT_INSTALLED = 2; + DOCKER_STATUS_INSTALLING = 3; + DOCKER_STATUS_NOT_RUNNING = 4; + DOCKER_STATUS_RUNNING = 5; + DOCKER_STATUS_ERROR = 6; +} + +enum ImageStatus { + IMAGE_STATUS_UNSPECIFIED = 0; + IMAGE_STATUS_NOT_AVAILABLE = 1; + IMAGE_STATUS_FETCHING = 2; + IMAGE_STATUS_AVAILABLE = 3; + IMAGE_STATUS_CREATED = 4; + IMAGE_STATUS_RUNNING = 5; + IMAGE_STATUS_ERROR = 6; +} + +message DockerAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; +} + +message DockerSetStatusAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + DockerStatus status = 2; +} + +message DockerStoreUsernameAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string registry = 2; + string username = 3; +} + +message DockerRemoveUsernameAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string registry = 2; +} + +message DockerImageAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string image = 2; +} + +message DockerImageSetStatusAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + + message Ports { + repeated string items = 1; + } + ImageStatus status = 2; + optional Ports ports = 3; + optional string ip = 4; + string image = 5; +} + +message DockerImageSetDockerIdAction { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string docker_id = 2; + string image = 3; +} + +message DockerEvent { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; +} + +message DockerServiceState { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + + message UsernamesDict { + map<string, string> items = 1; + } + optional DockerStatus status = 2; + optional UsernamesDict usernames = 3; +} + +message DockerImageEvent { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string image = 2; +} + +message DockerImageRegisterAppEvent { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + string image = 2; +} + +message ImageState { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + + message Ports { + repeated string items = 1; + } + string id = 2; + optional ImageStatus status = 3; + optional string container_ip = 4; + optional string docker_id = 5; + optional Ports ports = 6; +} + +message DockerState { + option (package_info.v1.package_name) = "docker"; + optional string meta_field_package_name_docker = 1; + DockerServiceState service = 2; +} + +message DisplayAction { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; +} + +message DisplayEvent { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; +} + +message DisplayPauseAction { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; +} + +message DisplayResumeAction { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; +} + +message DisplayRenderEvent { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; + bytes data = 2; + int64 data_hash = 3; + repeated int64 rectangle = 4; +} + +message DisplayState { + option (package_info.v1.package_name) = "display"; + optional string meta_field_package_name_display = 1; + optional bool is_paused = 2; +} + +enum Key { + KEY_UNSPECIFIED = 0; + KEY_BACK = 1; + KEY_HOME = 2; + KEY_UP = 3; + KEY_DOWN = 4; + KEY_L1 = 5; + KEY_L2 = 6; + KEY_L3 = 7; +} + +message KeypadAction { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + optional float time = 3; +} + +message KeypadKeyUpAction { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + optional float time = 3; +} + +message KeypadKeyDownAction { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + optional float time = 3; +} + +message KeypadKeyPressAction { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + optional float time = 3; +} + +message KeypadKeyReleaseAction { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + optional float time = 3; +} + +message KeypadEvent { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + float time = 3; +} + +message KeypadKeyPressEvent { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + float time = 3; +} + +message KeypadKeyReleaseEvent { + option (package_info.v1.package_name) = "keypad"; + optional string meta_field_package_name_keypad = 1; + Key key = 2; + float time = 3; +} + +enum VoiceEngine { + VOICE_ENGINE_UNSPECIFIED = 0; + VOICE_ENGINE_PIPER = 1; + VOICE_ENGINE_PICOVOICE = 2; +} + +message VoiceAction { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; +} + +message VoiceEvent { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; +} + +message VoiceUpdateAccessKeyStatus { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; + bool is_access_key_set = 2; +} + +message VoiceSetEngineAction { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; + VoiceEngine engine = 2; +} + +message VoiceReadTextAction { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; + string text = 2; + optional string piper_text = 3; + optional string picovoice_text = 4; + optional float speech_rate = 5; + optional VoiceEngine engine = 6; +} + +message VoiceSynthesizeTextEvent { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; + string text = 2; + string piper_text = 3; + string picovoice_text = 4; + optional float speech_rate = 5; +} + +message VoiceState { + option (package_info.v1.package_name) = "voice"; + optional string meta_field_package_name_voice = 1; + optional bool is_access_key_set = 2; + optional VoiceEngine selected_engine = 3; +} + +enum GlobalEthernetState { + GLOBAL_ETHERNET_STATE_UNSPECIFIED = 0; + GLOBAL_ETHERNET_STATE_CONNECTED = 1; + GLOBAL_ETHERNET_STATE_DISCONNECTED = 2; + GLOBAL_ETHERNET_STATE_PENDING = 3; + GLOBAL_ETHERNET_STATE_NEEDS_ATTENTION = 4; + GLOBAL_ETHERNET_STATE_UNKNOWN = 5; +} + +message RgbRingAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; +} + +message RgbRingEvent { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; +} + +message RgbRingSetIsConnectedAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional bool is_connected = 2; +} + +message RgbRingSetIsBusyAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional bool is_busy = 2; +} + +message RgbRingCommandAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; +} + +message RgbRingWaitableCommandAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional int64 wait = 2; +} + +message RgbRingColorfulCommandAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional RgbColor color = 2; +} + +message RgbRingSetEnabledAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional bool enabled = 2; +} + +message RgbRingSetAllAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional RgbColor color = 2; +} + +message RgbRingSetBrightnessAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional float brightness = 2; +} + +message RgbRingBlankAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; +} + +message RgbRingRainbowAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + int64 rounds = 2; + optional int64 wait = 3; +} + +message RgbRingProgressWheelStepAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional RgbColor color = 2; +} + +message RgbRingPulseAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional int64 repetitions = 2; + optional int64 wait = 3; + optional RgbColor color = 4; +} + +message RgbRingBlinkAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional int64 repetitions = 2; + optional int64 wait = 3; + optional RgbColor color = 4; +} + +message RgbRingSpinningWheelAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional int64 length = 2; + optional int64 repetitions = 3; + optional int64 wait = 4; + optional RgbColor color = 5; +} + +message RgbRingProgressWheelAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional float percentage = 2; + optional RgbColor color = 3; +} + +message RgbRingFillUptoAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional float percentage = 2; + optional int64 wait = 3; + optional RgbColor color = 4; +} + +message RgbRingFillDownfromAction { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + optional float percentage = 2; + optional int64 wait = 3; + optional RgbColor color = 4; +} + +message RgbRingCommandEvent { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + repeated string command = 2; +} + +message RgbRingState { + option (package_info.v1.package_name) = "rgb_ring"; + optional string meta_field_package_name_rgb_ring = 1; + bool is_connected = 2; + bool is_busy = 3; +} + +message RgbColorElement { + oneof rgb_color_element { + float float = 1; + int64 int64 = 2; + } +} + +message RgbColor { + repeated RgbColorElement items = 1; +} +message CameraAction { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; +} + +message CameraStartViewfinderAction { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + string id = 2; + string pattern = 3; +} + +message CameraEvent { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; +} + +message CameraStartViewfinderEvent { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + string pattern = 2; +} + +message CameraStopViewfinderEvent { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + string id = 2; +} + +message CameraReportBarcodeAction { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + repeated string codes = 2; +} + +message CameraBarcodeEvent { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + + string id = 2; + string code = 3; + map<string, string> group_dict = 4; +} + +message InputDescription { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + string id = 2; + string pattern = 3; +} + +message CameraState { + option (package_info.v1.package_name) = "camera"; + optional string meta_field_package_name_camera = 1; + optional InputDescription current = 2; + bool is_viewfinder_active = 3; + repeated InputDescription queue = 4; +} + +message IpAction { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; +} + +message IpEvent { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; +} + +message IpUpdateInterfacesAction { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; + repeated IpNetworkInterface interfaces = 2; +} + +message IpSetIsConnectedAction { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; + bool is_connected = 2; +} + +message IpNetworkInterface { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; + string name = 2; + repeated string ip_addresses = 3; +} + +message IpState { + option (package_info.v1.package_name) = "ip"; + optional string meta_field_package_name_ip = 1; + repeated IpNetworkInterface interfaces = 2; + optional bool is_connected = 3; +} + +enum Importance { + IMPORTANCE_UNSPECIFIED = 0; + IMPORTANCE_CRITICAL = 1; + IMPORTANCE_HIGH = 2; + IMPORTANCE_MEDIUM = 3; + IMPORTANCE_LOW = 4; +} + +enum NotificationDisplayType { + NOTIFICATION_DISPLAY_TYPE_UNSPECIFIED = 0; + NOTIFICATION_DISPLAY_TYPE_NOT_SET = 1; + NOTIFICATION_DISPLAY_TYPE_BACKGROUND = 2; + NOTIFICATION_DISPLAY_TYPE_FLASH = 3; + NOTIFICATION_DISPLAY_TYPE_STICKY = 4; +} + +enum Chime { + CHIME_UNSPECIFIED = 0; + CHIME_ADD = 1; + CHIME_DONE = 2; + CHIME_FAILURE = 3; + CHIME_VOLUME_CHANGE = 4; +} + +message NotificationActionItem { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + optional string background_color = 2; + optional bool dismiss_notification = 3; + optional string key = 4; + optional string label = 5; + optional string color = 6; + optional string icon = 7; + optional bool is_short = 8; + optional float opacity = 9; + optional float progress = 10; +} + +message NotificationDispatchItem { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + + message Operation { + oneof operation { + Event ubo_event = 1; + Action ubo_action = 2; + } + } + Operation operation = 2; + optional string key = 3; + optional string label = 4; + optional string color = 5; + optional string background_color = 6; + optional string icon = 7; + optional bool is_short = 8; + optional float opacity = 9; + optional float progress = 10; + optional bool dismiss_notification = 11; +} + +message NotificationExtraInformation { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + string text = 2; + optional string piper_text = 3; + optional string picovoice_text = 4; +} + +message Notification { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + + message ActionsItem { + oneof actions_item { + NotificationActionItem notification_action_item = 1; + NotificationDispatchItem notification_dispatch_item = 2; + } + } + + message Actions { + repeated ActionsItem items = 1; + } + + message OnClose {} + optional string id = 2; + string title = 3; + string content = 4; + optional NotificationExtraInformation extra_information = 5; + optional Importance importance = 6; + optional Chime chime = 7; + optional int64 timestamp = 8; + optional bool is_read = 9; + optional string sender = 10; + optional Actions actions = 11; + optional string icon = 12; + optional string color = 13; + optional int64 expiration_timestamp = 14; + optional NotificationDisplayType display_type = 15; + optional float flash_time = 16; + optional bool dismissable = 17; + optional bool dismiss_on_close = 18; + optional OnClose on_close = 19; + optional bool blink = 20; + optional float progress = 21; + optional float progress_weight = 22; +} + +message NotificationsAction { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; +} + +message NotificationsAddAction { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + Notification notification = 2; +} + +message NotificationsClearAction { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + Notification notification = 2; +} + +message NotificationsClearByIdAction { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + string id = 2; +} + +message NotificationsClearAllAction { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; +} + +message NotificationsEvent { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; +} + +message NotificationsClearEvent { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + Notification notification = 2; +} + +message NotificationsDisplayEvent { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + Notification notification = 2; + optional int64 index = 3; + optional int64 count = 4; +} + +message NotificationsState { + option (package_info.v1.package_name) = "notifications"; + optional string meta_field_package_name_notifications = 1; + repeated Notification notifications = 2; + int64 unread_count = 3; + optional float progress = 4; +} + +enum AudioDevice { + AUDIO_DEVICE_UNSPECIFIED = 0; + AUDIO_DEVICE_INPUT = 1; + AUDIO_DEVICE_OUTPUT = 2; +} + +message AudioAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; +} + +message AudioSetVolumeAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + float volume = 2; + AudioDevice device = 3; +} + +message AudioChangeVolumeAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + float amount = 2; + AudioDevice device = 3; +} + +message AudioSetMuteStatusAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + bool is_mute = 2; + AudioDevice device = 3; +} + +message AudioToggleMuteStatusAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + AudioDevice device = 2; +} + +message AudioPlayChimeAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + string name = 2; +} + +message AudioPlayAudioAction { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + optional string id = 2; + bytes sample = 3; + int64 channels = 4; + int64 rate = 5; + int64 width = 6; +} + +message AudioEvent { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; +} + +message AudioPlayChimeEvent { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + string name = 2; +} + +message AudioPlayAudioEvent { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + optional string id = 2; + bytes sample = 3; + int64 channels = 4; + int64 rate = 5; + int64 width = 6; +} + +message AudioPlaybackDoneEvent { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + string id = 2; +} + +message AudioState { + option (package_info.v1.package_name) = "audio"; + optional string meta_field_package_name_audio = 1; + optional float playback_volume = 2; + optional bool is_playback_mute = 3; + optional float capture_volume = 4; + optional bool is_capture_mute = 5; +} + +message SSHAction { + option (package_info.v1.package_name) = "ssh"; + optional string meta_field_package_name_ssh = 1; +} + +message SSHUpdateStateAction { + option (package_info.v1.package_name) = "ssh"; + optional string meta_field_package_name_ssh = 1; + optional bool is_active = 2; + optional bool is_enabled = 3; +} + +message SSHClearEnabledStateAction { + option (package_info.v1.package_name) = "ssh"; + optional string meta_field_package_name_ssh = 1; +} + +message SSHState { + option (package_info.v1.package_name) = "ssh"; + optional string meta_field_package_name_ssh = 1; + optional bool is_active = 2; + optional bool is_enabled = 3; +} + +message Action { + oneof action { + LightDMAction light_dm_action = 1; + LightDMUpdateStateAction light_dm_update_state_action = 2; + LightDMClearEnabledStateAction light_dm_clear_enabled_state_action = 3; + WiFiAction wi_fi_action = 4; + WiFiSetHasVisitedOnboardingAction wi_fi_set_has_visited_onboarding_action = 5; + WiFiUpdateAction wi_fi_update_action = 6; + WiFiUpdateRequestAction wi_fi_update_request_action = 7; + SensorsAction sensors_action = 8; + SensorsReportReadingAction sensors_report_reading_action = 9; + UsersAction users_action = 10; + UsersSetUsersAction users_set_users_action = 11; + UsersCreateUserAction users_create_user_action = 12; + UsersDeleteUserAction users_delete_user_action = 13; + UsersResetPasswordAction users_reset_password_action = 14; + RPiConnectAction r_pi_connect_action = 15; + RPiConnectStartDownloadingAction r_pi_connect_start_downloading_action = 16; + RPiConnectDoneDownloadingAction r_pi_connect_done_downloading_action = 17; + RPiConnectSetPendingAction r_pi_connect_set_pending_action = 18; + RPiConnectSetStatusAction r_pi_connect_set_status_action = 19; + RPiConnectUpdateServiceStateAction r_pi_connect_update_service_state_action = 20; + VSCodeAction vs_code_action = 21; + VSCodeStartDownloadingAction vs_code_start_downloading_action = 22; + VSCodeDoneDownloadingAction vs_code_done_downloading_action = 23; + VSCodeSetPendingAction vs_code_set_pending_action = 24; + VSCodeSetStatusAction vs_code_set_status_action = 25; + DockerAction docker_action = 26; + DockerSetStatusAction docker_set_status_action = 27; + DockerStoreUsernameAction docker_store_username_action = 28; + DockerRemoveUsernameAction docker_remove_username_action = 29; + DockerImageAction docker_image_action = 30; + DockerImageSetStatusAction docker_image_set_status_action = 31; + DockerImageSetDockerIdAction docker_image_set_docker_id_action = 32; + DisplayAction display_action = 33; + DisplayPauseAction display_pause_action = 34; + DisplayResumeAction display_resume_action = 35; + KeypadAction keypad_action = 36; + KeypadKeyUpAction keypad_key_up_action = 37; + KeypadKeyDownAction keypad_key_down_action = 38; + KeypadKeyPressAction keypad_key_press_action = 39; + KeypadKeyReleaseAction keypad_key_release_action = 40; + VoiceAction voice_action = 41; + VoiceSetEngineAction voice_set_engine_action = 42; + VoiceReadTextAction voice_read_text_action = 43; + RgbRingAction rgb_ring_action = 44; + RgbRingSetIsConnectedAction rgb_ring_set_is_connected_action = 45; + RgbRingSetIsBusyAction rgb_ring_set_is_busy_action = 46; + RgbRingCommandAction rgb_ring_command_action = 47; + RgbRingWaitableCommandAction rgb_ring_waitable_command_action = 48; + RgbRingColorfulCommandAction rgb_ring_colorful_command_action = 49; + RgbRingSetEnabledAction rgb_ring_set_enabled_action = 50; + RgbRingSetAllAction rgb_ring_set_all_action = 51; + RgbRingSetBrightnessAction rgb_ring_set_brightness_action = 52; + RgbRingBlankAction rgb_ring_blank_action = 53; + RgbRingRainbowAction rgb_ring_rainbow_action = 54; + RgbRingProgressWheelStepAction rgb_ring_progress_wheel_step_action = 55; + RgbRingPulseAction rgb_ring_pulse_action = 56; + RgbRingBlinkAction rgb_ring_blink_action = 57; + RgbRingSpinningWheelAction rgb_ring_spinning_wheel_action = 58; + RgbRingProgressWheelAction rgb_ring_progress_wheel_action = 59; + RgbRingFillUptoAction rgb_ring_fill_upto_action = 60; + RgbRingFillDownfromAction rgb_ring_fill_downfrom_action = 61; + CameraAction camera_action = 62; + CameraStartViewfinderAction camera_start_viewfinder_action = 63; + CameraReportBarcodeAction camera_report_barcode_action = 64; + IpAction ip_action = 65; + IpUpdateInterfacesAction ip_update_interfaces_action = 66; + IpSetIsConnectedAction ip_set_is_connected_action = 67; + NotificationsAction notifications_action = 68; + NotificationsAddAction notifications_add_action = 69; + NotificationsClearAction notifications_clear_action = 70; + NotificationsClearByIdAction notifications_clear_by_id_action = 71; + NotificationsClearAllAction notifications_clear_all_action = 72; + AudioAction audio_action = 73; + AudioSetVolumeAction audio_set_volume_action = 74; + AudioChangeVolumeAction audio_change_volume_action = 75; + AudioSetMuteStatusAction audio_set_mute_status_action = 76; + AudioToggleMuteStatusAction audio_toggle_mute_status_action = 77; + AudioPlayChimeAction audio_play_chime_action = 78; + AudioPlayAudioAction audio_play_audio_action = 79; + SSHAction ssh_action = 80; + SSHUpdateStateAction ssh_update_state_action = 81; + SSHClearEnabledStateAction ssh_clear_enabled_state_action = 82; + } +} + +message Event { + oneof event { + ScreenshotEvent screenshot_event = 1; + SnapshotEvent snapshot_event = 2; + WiFiEvent wi_fi_event = 3; + WiFiUpdateRequestEvent wi_fi_update_request_event = 4; + UsersEvent users_event = 5; + UsersCreateUserEvent users_create_user_event = 6; + UsersDeleteUserEvent users_delete_user_event = 7; + UsersResetPasswordEvent users_reset_password_event = 8; + RPiConnectEvent r_pi_connect_event = 9; + RPiConnectLoginEvent r_pi_connect_login_event = 10; + VSCodeEvent vs_code_event = 11; + VSCodeLoginEvent vs_code_login_event = 12; + VSCodeRestartEvent vs_code_restart_event = 13; + DockerEvent docker_event = 14; + DockerImageEvent docker_image_event = 15; + DockerImageRegisterAppEvent docker_image_register_app_event = 16; + DisplayEvent display_event = 17; + DisplayRenderEvent display_render_event = 18; + KeypadEvent keypad_event = 19; + KeypadKeyPressEvent keypad_key_press_event = 20; + KeypadKeyReleaseEvent keypad_key_release_event = 21; + VoiceEvent voice_event = 22; + VoiceSynthesizeTextEvent voice_synthesize_text_event = 23; + RgbRingEvent rgb_ring_event = 24; + RgbRingCommandEvent rgb_ring_command_event = 25; + CameraEvent camera_event = 26; + CameraStartViewfinderEvent camera_start_viewfinder_event = 27; + CameraStopViewfinderEvent camera_stop_viewfinder_event = 28; + CameraBarcodeEvent camera_barcode_event = 29; + IpEvent ip_event = 30; + NotificationsEvent notifications_event = 31; + NotificationsClearEvent notifications_clear_event = 32; + NotificationsDisplayEvent notifications_display_event = 33; + AudioEvent audio_event = 34; + AudioPlayChimeEvent audio_play_chime_event = 35; + AudioPlayAudioEvent audio_play_audio_event = 36; + AudioPlaybackDoneEvent audio_playback_done_event = 37; + } +} diff --git a/ubo_app/rpc/sample_python_client.py b/ubo_app/rpc/sample_python_client.py new file mode 100644 index 00000000..cddf1ff8 --- /dev/null +++ b/ubo_app/rpc/sample_python_client.py @@ -0,0 +1,132 @@ +"""Client for the remote store.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, overload + +from grpclib.client import Channel + +from ubo_app.rpc.generated.store.v1 import ( + DispatchActionRequest, + DispatchEventRequest, + StoreServiceStub, + SubscribeEventRequest, +) +from ubo_app.rpc.generated.ubo.v1 import ( + Action, + Event, + Key, + KeypadKeyPressAction, + Notification, + NotificationActions, + NotificationActionsItem, + NotificationDispatchItem, + NotificationDispatchItemOperation, + NotificationsAddAction, +) + +if TYPE_CHECKING: + from collections.abc import Callable + +SERVER_HOST = '127.0.0.1' +SERVER_PORT = 50051 + + +class AsyncRemoteStore: + """Async remote store for dispatching operations to a gRPC server.""" + + def __init__( + self: AsyncRemoteStore, + host: str, + port: int, + ) -> None: + """Initialize the async remote store.""" + self.channel = Channel(host=host, port=port) + self.service = StoreServiceStub(self.channel) + + @overload + async def dispatch_async( + self: AsyncRemoteStore, + *, + action: Action, + ) -> None: ... + @overload + async def dispatch_async( + self: AsyncRemoteStore, + *, + event: Event, + ) -> None: ... + async def dispatch_async( + self: AsyncRemoteStore, + *, + action: Action | None = None, + event: Event | None = None, + ) -> None: + """Dispatch an operation to the remote store.""" + return + """Dispatch an operation to the remote store.""" + if action is not None: + await self.service.dispatch_action(DispatchActionRequest(action=action)) + if event is not None: + await self.service.dispatch_event(DispatchEventRequest(event=event)) + + async def subscribe_event( + self: AsyncRemoteStore, + event_type: Event, + callback: Callable[[Event], None], + ) -> None: + """Subscribe to the remote store.""" + async for response in self.service.subscribe_event( + SubscribeEventRequest(event=event_type), + ): + callback(response.event) + + +async def connect() -> None: + """Connect to the gRPC server.""" + store = AsyncRemoteStore(SERVER_HOST, SERVER_PORT) + await store.dispatch_async( + action=Action( + keypad_key_press_action=KeypadKeyPressAction( + key=Key.L1, + time=0.0, + ), + ), + ) + await store.dispatch_async( + action=Action( + notifications_add_action=NotificationsAddAction( + notification=Notification( + title='Hello', + content='World', + actions=NotificationActions( + items=[ + NotificationActionsItem( + notification_dispatch_item=NotificationDispatchItem( + label='custom action', + color='#ff0000', + background_color='#00ff00', + icon='', + operation=NotificationDispatchItemOperation( + ubo_action=Action( + keypad_key_press_action=KeypadKeyPressAction( + key=Key.L1, + time=0.0, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ) + store.channel.close() + + +if __name__ == '__main__': + import asyncio + + asyncio.run(connect()) diff --git a/ubo_app/rpc/server.py b/ubo_app/rpc/server.py new file mode 100644 index 00000000..fec0959e --- /dev/null +++ b/ubo_app/rpc/server.py @@ -0,0 +1,25 @@ +# ruff: noqa: N802 +"""gRPC server for the store service.""" + +from __future__ import annotations + +from grpclib.server import Server + +from ubo_app.logging import logger +from ubo_app.rpc.service import StoreService + +LISTEN_HOST = '127.0.0.1' +LISTEN_PORT = 50051 + + +async def serve() -> None: + """Serve the gRPC server.""" + server = Server([StoreService()]) + + logger.error( + 'Starting gRPC server', + extra={'host': LISTEN_HOST, 'port': LISTEN_PORT}, + ) + await server.start(LISTEN_HOST, LISTEN_PORT) + + await server.wait_closed() diff --git a/ubo_app/rpc/service.py b/ubo_app/rpc/service.py new file mode 100644 index 00000000..5ba6a0ac --- /dev/null +++ b/ubo_app/rpc/service.py @@ -0,0 +1,65 @@ +"""gRPC service that implements the Store service.""" + +from __future__ import annotations + +from typing import cast + +from redux import BaseAction, BaseEvent + +from ubo_app.logging import logger +from ubo_app.rpc.generated.store.v1 import ( + DispatchActionRequest, + DispatchActionResponse, + DispatchEventRequest, + DispatchEventResponse, + StoreServiceBase, +) +from ubo_app.rpc.message_to_object import rebuild_object +from ubo_app.store.main import store +from ubo_app.store.operations import UboAction, UboEvent + + +class StoreService(StoreServiceBase): + """gRPC service class that implements the Store service.""" + + async def dispatch_action( + self: StoreService, + dispatch_action_request: DispatchActionRequest, + ) -> DispatchActionResponse: + """Dispatch an action to the store.""" + logger.info( + 'Received action to be dispatched over gRPC', + extra={ + 'request': dispatch_action_request, + }, + ) + if not dispatch_action_request.action: + return DispatchActionResponse() + try: + action = rebuild_object(dispatch_action_request.action, BaseAction) + except Exception: + logger.exception('Failed to build object from dispatch action request') + else: + store.dispatch(cast(UboAction, action)) + return DispatchActionResponse() + + async def dispatch_event( + self: StoreService, + dispatch_event_request: DispatchEventRequest, + ) -> DispatchEventResponse: + """Dispatch an event to the store.""" + logger.info( + 'Received event to be dispatched over gRPC', + extra={ + 'request': dispatch_event_request, + }, + ) + if not dispatch_event_request.event: + return DispatchEventResponse() + try: + event = rebuild_object(dispatch_event_request.event, BaseEvent) + except Exception: + logger.exception('Failed to build object from dispatch event request') + else: + store.dispatch(cast(UboEvent, event)) + return DispatchEventResponse()