feat: ESPHome Protocol crate #5

merged
opened by sachy.dev targeting main from more-pico-crates
Changed files
+3292 -5
.tangled
workflows
sachy-esphome
+1
.tangled/workflows/test.yml
··· 9 9 - clang 10 10 - cargo 11 11 - rustfmt 12 + - protobuf 12 13 13 14 steps: 14 15 - name: Format check
+728 -4
Cargo.lock
··· 2 2 # It is not intended for manual editing. 3 3 version = 4 4 4 5 + [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.4" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "anyhow" 16 + version = "1.0.100" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 19 + 5 20 [[package]] 6 21 name = "autocfg" 7 22 version = "1.5.0" ··· 20 35 source = "registry+https://github.com/rust-lang/crates.io-index" 21 36 checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 22 37 38 + [[package]] 39 + name = "block-buffer" 40 + version = "0.10.4" 41 + source = "registry+https://github.com/rust-lang/crates.io-index" 42 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 43 + dependencies = [ 44 + "generic-array", 45 + ] 46 + 23 47 [[package]] 24 48 name = "byteorder" 25 49 version = "1.5.0" 26 50 source = "registry+https://github.com/rust-lang/crates.io-index" 27 51 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 28 52 53 + [[package]] 54 + name = "bytes" 55 + version = "1.11.0" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 58 + dependencies = [ 59 + "portable-atomic", 60 + ] 61 + 29 62 [[package]] 30 63 name = "cast" 31 64 version = "0.3.0" ··· 73 106 source = "registry+https://github.com/rust-lang/crates.io-index" 74 107 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 75 108 109 + [[package]] 110 + name = "cpufeatures" 111 + version = "0.2.17" 112 + source = "registry+https://github.com/rust-lang/crates.io-index" 113 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 114 + dependencies = [ 115 + "libc", 116 + ] 117 + 76 118 [[package]] 77 119 name = "critical-section" 78 120 version = "1.2.0" 79 121 source = "registry+https://github.com/rust-lang/crates.io-index" 80 122 checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 81 123 124 + [[package]] 125 + name = "crypto-common" 126 + version = "0.1.7" 127 + source = "registry+https://github.com/rust-lang/crates.io-index" 128 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 129 + dependencies = [ 130 + "generic-array", 131 + "typenum", 132 + ] 133 + 134 + [[package]] 135 + name = "darling" 136 + version = "0.20.11" 137 + source = "registry+https://github.com/rust-lang/crates.io-index" 138 + checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 139 + dependencies = [ 140 + "darling_core", 141 + "darling_macro", 142 + ] 143 + 144 + [[package]] 145 + name = "darling_core" 146 + version = "0.20.11" 147 + source = "registry+https://github.com/rust-lang/crates.io-index" 148 + checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 149 + dependencies = [ 150 + "fnv", 151 + "ident_case", 152 + "proc-macro2", 153 + "quote", 154 + "strsim", 155 + "syn", 156 + ] 157 + 158 + [[package]] 159 + name = "darling_macro" 160 + version = "0.20.11" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 163 + dependencies = [ 164 + "darling_core", 165 + "quote", 166 + "syn", 167 + ] 168 + 82 169 [[package]] 83 170 name = "defmt" 84 171 version = "0.3.100" ··· 117 204 source = "registry+https://github.com/rust-lang/crates.io-index" 118 205 checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" 119 206 dependencies = [ 120 - "thiserror", 207 + "thiserror 2.0.17", 208 + ] 209 + 210 + [[package]] 211 + name = "derive_builder" 212 + version = "0.20.2" 213 + source = "registry+https://github.com/rust-lang/crates.io-index" 214 + checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" 215 + dependencies = [ 216 + "derive_builder_macro", 217 + ] 218 + 219 + [[package]] 220 + name = "derive_builder_core" 221 + version = "0.20.2" 222 + source = "registry+https://github.com/rust-lang/crates.io-index" 223 + checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" 224 + dependencies = [ 225 + "darling", 226 + "proc-macro2", 227 + "quote", 228 + "syn", 229 + ] 230 + 231 + [[package]] 232 + name = "derive_builder_macro" 233 + version = "0.20.2" 234 + source = "registry+https://github.com/rust-lang/crates.io-index" 235 + checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 236 + dependencies = [ 237 + "derive_builder_core", 238 + "syn", 239 + ] 240 + 241 + [[package]] 242 + name = "digest" 243 + version = "0.10.7" 244 + source = "registry+https://github.com/rust-lang/crates.io-index" 245 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 246 + dependencies = [ 247 + "block-buffer", 248 + "crypto-common", 121 249 ] 122 250 123 251 [[package]] ··· 129 257 "litrs", 130 258 ] 131 259 260 + [[package]] 261 + name = "either" 262 + version = "1.15.0" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 265 + 132 266 [[package]] 133 267 name = "embassy-net" 134 268 version = "0.7.1" ··· 273 407 "embedded-nal", 274 408 ] 275 409 410 + [[package]] 411 + name = "equivalent" 412 + version = "1.0.2" 413 + source = "registry+https://github.com/rust-lang/crates.io-index" 414 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 415 + 416 + [[package]] 417 + name = "errno" 418 + version = "0.3.14" 419 + source = "registry+https://github.com/rust-lang/crates.io-index" 420 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 421 + dependencies = [ 422 + "libc", 423 + "windows-sys 0.52.0", 424 + ] 425 + 426 + [[package]] 427 + name = "fastrand" 428 + version = "2.3.0" 429 + source = "registry+https://github.com/rust-lang/crates.io-index" 430 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 431 + 276 432 [[package]] 277 433 name = "find-msvc-tools" 278 434 version = "0.1.5" 279 435 source = "registry+https://github.com/rust-lang/crates.io-index" 280 436 checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 281 437 438 + [[package]] 439 + name = "fixedbitset" 440 + version = "0.5.7" 441 + source = "registry+https://github.com/rust-lang/crates.io-index" 442 + checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" 443 + 444 + [[package]] 445 + name = "fnv" 446 + version = "1.0.7" 447 + source = "registry+https://github.com/rust-lang/crates.io-index" 448 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 449 + 282 450 [[package]] 283 451 name = "futures-core" 284 452 version = "0.3.31" ··· 291 459 source = "registry+https://github.com/rust-lang/crates.io-index" 292 460 checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 293 461 462 + [[package]] 463 + name = "generic-array" 464 + version = "0.14.7" 465 + source = "registry+https://github.com/rust-lang/crates.io-index" 466 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 467 + dependencies = [ 468 + "typenum", 469 + "version_check", 470 + ] 471 + 472 + [[package]] 473 + name = "getrandom" 474 + version = "0.3.4" 475 + source = "registry+https://github.com/rust-lang/crates.io-index" 476 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 477 + dependencies = [ 478 + "cfg-if", 479 + "libc", 480 + "r-efi", 481 + "wasip2", 482 + ] 483 + 294 484 [[package]] 295 485 name = "gpio-cdev" 296 486 version = "0.6.0" ··· 302 492 "nix 0.27.1", 303 493 ] 304 494 495 + [[package]] 496 + name = "handlebars" 497 + version = "6.3.2" 498 + source = "registry+https://github.com/rust-lang/crates.io-index" 499 + checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" 500 + dependencies = [ 501 + "derive_builder", 502 + "log", 503 + "num-order", 504 + "pest", 505 + "pest_derive", 506 + "serde", 507 + "serde_json", 508 + "thiserror 2.0.17", 509 + ] 510 + 305 511 [[package]] 306 512 name = "hash32" 307 513 version = "0.3.1" ··· 311 517 "byteorder", 312 518 ] 313 519 520 + [[package]] 521 + name = "hashbrown" 522 + version = "0.16.1" 523 + source = "registry+https://github.com/rust-lang/crates.io-index" 524 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 525 + 314 526 [[package]] 315 527 name = "heapless" 316 528 version = "0.8.0" ··· 332 544 "stable_deref_trait", 333 545 ] 334 546 547 + [[package]] 548 + name = "heck" 549 + version = "0.5.0" 550 + source = "registry+https://github.com/rust-lang/crates.io-index" 551 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 552 + 553 + [[package]] 554 + name = "home" 555 + version = "0.5.12" 556 + source = "registry+https://github.com/rust-lang/crates.io-index" 557 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 558 + dependencies = [ 559 + "windows-sys 0.61.2", 560 + ] 561 + 335 562 [[package]] 336 563 name = "i2cdev" 337 564 version = "0.6.2" ··· 344 571 "nix 0.26.4", 345 572 ] 346 573 574 + [[package]] 575 + name = "ident_case" 576 + version = "1.0.1" 577 + source = "registry+https://github.com/rust-lang/crates.io-index" 578 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 579 + 580 + [[package]] 581 + name = "indexmap" 582 + version = "2.12.1" 583 + source = "registry+https://github.com/rust-lang/crates.io-index" 584 + checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 585 + dependencies = [ 586 + "equivalent", 587 + "hashbrown", 588 + ] 589 + 347 590 [[package]] 348 591 name = "io-kit-sys" 349 592 version = "0.4.1" ··· 354 597 "mach2", 355 598 ] 356 599 600 + [[package]] 601 + name = "itertools" 602 + version = "0.14.0" 603 + source = "registry+https://github.com/rust-lang/crates.io-index" 604 + checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 605 + dependencies = [ 606 + "either", 607 + ] 608 + 609 + [[package]] 610 + name = "itoa" 611 + version = "1.0.15" 612 + source = "registry+https://github.com/rust-lang/crates.io-index" 613 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 614 + 357 615 [[package]] 358 616 name = "libc" 359 617 version = "0.2.178" 360 618 source = "registry+https://github.com/rust-lang/crates.io-index" 361 619 checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 362 620 621 + [[package]] 622 + name = "libm" 623 + version = "0.2.15" 624 + source = "registry+https://github.com/rust-lang/crates.io-index" 625 + checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 626 + 363 627 [[package]] 364 628 name = "linux-embedded-hal" 365 629 version = "0.4.1" ··· 378 642 "sysfs_gpio", 379 643 ] 380 644 645 + [[package]] 646 + name = "linux-raw-sys" 647 + version = "0.4.15" 648 + source = "registry+https://github.com/rust-lang/crates.io-index" 649 + checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 650 + 651 + [[package]] 652 + name = "linux-raw-sys" 653 + version = "0.11.0" 654 + source = "registry+https://github.com/rust-lang/crates.io-index" 655 + checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 656 + 381 657 [[package]] 382 658 name = "litrs" 383 659 version = "1.0.0" 384 660 source = "registry+https://github.com/rust-lang/crates.io-index" 385 661 checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" 386 662 663 + [[package]] 664 + name = "log" 665 + version = "0.4.29" 666 + source = "registry+https://github.com/rust-lang/crates.io-index" 667 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 668 + 387 669 [[package]] 388 670 name = "mach2" 389 671 version = "0.4.3" ··· 399 681 source = "registry+https://github.com/rust-lang/crates.io-index" 400 682 checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" 401 683 684 + [[package]] 685 + name = "memchr" 686 + version = "2.7.6" 687 + source = "registry+https://github.com/rust-lang/crates.io-index" 688 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 689 + 402 690 [[package]] 403 691 name = "memoffset" 404 692 version = "0.6.5" ··· 417 705 "autocfg", 418 706 ] 419 707 708 + [[package]] 709 + name = "multimap" 710 + version = "0.10.1" 711 + source = "registry+https://github.com/rust-lang/crates.io-index" 712 + checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" 713 + 420 714 [[package]] 421 715 name = "nb" 422 716 version = "0.1.3" ··· 469 763 "libc", 470 764 ] 471 765 766 + [[package]] 767 + name = "num-derive" 768 + version = "0.4.2" 769 + source = "registry+https://github.com/rust-lang/crates.io-index" 770 + checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 771 + dependencies = [ 772 + "proc-macro2", 773 + "quote", 774 + "syn", 775 + ] 776 + 777 + [[package]] 778 + name = "num-modular" 779 + version = "0.6.1" 780 + source = "registry+https://github.com/rust-lang/crates.io-index" 781 + checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" 782 + 783 + [[package]] 784 + name = "num-order" 785 + version = "1.2.0" 786 + source = "registry+https://github.com/rust-lang/crates.io-index" 787 + checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" 788 + dependencies = [ 789 + "num-modular", 790 + ] 791 + 472 792 [[package]] 473 793 name = "num-traits" 474 794 version = "0.2.19" ··· 478 798 "autocfg", 479 799 ] 480 800 801 + [[package]] 802 + name = "once_cell" 803 + version = "1.21.3" 804 + source = "registry+https://github.com/rust-lang/crates.io-index" 805 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 806 + 807 + [[package]] 808 + name = "pest" 809 + version = "2.8.4" 810 + source = "registry+https://github.com/rust-lang/crates.io-index" 811 + checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 812 + dependencies = [ 813 + "memchr", 814 + "ucd-trie", 815 + ] 816 + 817 + [[package]] 818 + name = "pest_derive" 819 + version = "2.8.4" 820 + source = "registry+https://github.com/rust-lang/crates.io-index" 821 + checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 822 + dependencies = [ 823 + "pest", 824 + "pest_generator", 825 + ] 826 + 827 + [[package]] 828 + name = "pest_generator" 829 + version = "2.8.4" 830 + source = "registry+https://github.com/rust-lang/crates.io-index" 831 + checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 832 + dependencies = [ 833 + "pest", 834 + "pest_meta", 835 + "proc-macro2", 836 + "quote", 837 + "syn", 838 + ] 839 + 840 + [[package]] 841 + name = "pest_meta" 842 + version = "2.8.4" 843 + source = "registry+https://github.com/rust-lang/crates.io-index" 844 + checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 845 + dependencies = [ 846 + "pest", 847 + "sha2", 848 + ] 849 + 850 + [[package]] 851 + name = "petgraph" 852 + version = "0.7.1" 853 + source = "registry+https://github.com/rust-lang/crates.io-index" 854 + checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" 855 + dependencies = [ 856 + "fixedbitset", 857 + "indexmap", 858 + ] 859 + 481 860 [[package]] 482 861 name = "pin-utils" 483 862 version = "0.1.0" 484 863 source = "registry+https://github.com/rust-lang/crates.io-index" 485 864 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 486 865 866 + [[package]] 867 + name = "portable-atomic" 868 + version = "1.11.1" 869 + source = "registry+https://github.com/rust-lang/crates.io-index" 870 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 871 + 872 + [[package]] 873 + name = "prettyplease" 874 + version = "0.2.37" 875 + source = "registry+https://github.com/rust-lang/crates.io-index" 876 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 877 + dependencies = [ 878 + "proc-macro2", 879 + "syn", 880 + ] 881 + 487 882 [[package]] 488 883 name = "proc-macro-error-attr2" 489 884 version = "2.0.0" ··· 515 910 "unicode-ident", 516 911 ] 517 912 913 + [[package]] 914 + name = "prost" 915 + version = "0.14.1" 916 + source = "registry+https://github.com/rust-lang/crates.io-index" 917 + checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" 918 + dependencies = [ 919 + "bytes", 920 + "prost-derive", 921 + ] 922 + 923 + [[package]] 924 + name = "prost-build" 925 + version = "0.14.1" 926 + source = "registry+https://github.com/rust-lang/crates.io-index" 927 + checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" 928 + dependencies = [ 929 + "heck", 930 + "itertools", 931 + "log", 932 + "multimap", 933 + "once_cell", 934 + "petgraph", 935 + "prettyplease", 936 + "prost", 937 + "prost-types", 938 + "regex", 939 + "syn", 940 + "tempfile", 941 + ] 942 + 943 + [[package]] 944 + name = "prost-derive" 945 + version = "0.14.1" 946 + source = "registry+https://github.com/rust-lang/crates.io-index" 947 + checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" 948 + dependencies = [ 949 + "anyhow", 950 + "itertools", 951 + "proc-macro2", 952 + "quote", 953 + "syn", 954 + ] 955 + 956 + [[package]] 957 + name = "prost-types" 958 + version = "0.14.1" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" 961 + dependencies = [ 962 + "prost", 963 + ] 964 + 965 + [[package]] 966 + name = "protobuf" 967 + version = "3.7.2" 968 + source = "registry+https://github.com/rust-lang/crates.io-index" 969 + checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" 970 + dependencies = [ 971 + "once_cell", 972 + "protobuf-support", 973 + "thiserror 1.0.69", 974 + ] 975 + 976 + [[package]] 977 + name = "protobuf-parse" 978 + version = "3.7.2" 979 + source = "registry+https://github.com/rust-lang/crates.io-index" 980 + checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" 981 + dependencies = [ 982 + "anyhow", 983 + "indexmap", 984 + "log", 985 + "protobuf", 986 + "protobuf-support", 987 + "tempfile", 988 + "thiserror 1.0.69", 989 + "which", 990 + ] 991 + 992 + [[package]] 993 + name = "protobuf-support" 994 + version = "3.7.2" 995 + source = "registry+https://github.com/rust-lang/crates.io-index" 996 + checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" 997 + dependencies = [ 998 + "thiserror 1.0.69", 999 + ] 1000 + 518 1001 [[package]] 519 1002 name = "quote" 520 1003 version = "1.0.40" ··· 524 1007 "proc-macro2", 525 1008 ] 526 1009 1010 + [[package]] 1011 + name = "r-efi" 1012 + version = "5.3.0" 1013 + source = "registry+https://github.com/rust-lang/crates.io-index" 1014 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1015 + 1016 + [[package]] 1017 + name = "regex" 1018 + version = "1.12.2" 1019 + source = "registry+https://github.com/rust-lang/crates.io-index" 1020 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 1021 + dependencies = [ 1022 + "aho-corasick", 1023 + "memchr", 1024 + "regex-automata", 1025 + "regex-syntax", 1026 + ] 1027 + 1028 + [[package]] 1029 + name = "regex-automata" 1030 + version = "0.4.13" 1031 + source = "registry+https://github.com/rust-lang/crates.io-index" 1032 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 1033 + dependencies = [ 1034 + "aho-corasick", 1035 + "memchr", 1036 + "regex-syntax", 1037 + ] 1038 + 1039 + [[package]] 1040 + name = "regex-syntax" 1041 + version = "0.8.8" 1042 + source = "registry+https://github.com/rust-lang/crates.io-index" 1043 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 1044 + 1045 + [[package]] 1046 + name = "rustix" 1047 + version = "0.38.44" 1048 + source = "registry+https://github.com/rust-lang/crates.io-index" 1049 + checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1050 + dependencies = [ 1051 + "bitflags 2.10.0", 1052 + "errno", 1053 + "libc", 1054 + "linux-raw-sys 0.4.15", 1055 + "windows-sys 0.52.0", 1056 + ] 1057 + 1058 + [[package]] 1059 + name = "rustix" 1060 + version = "1.1.2" 1061 + source = "registry+https://github.com/rust-lang/crates.io-index" 1062 + checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 1063 + dependencies = [ 1064 + "bitflags 2.10.0", 1065 + "errno", 1066 + "libc", 1067 + "linux-raw-sys 0.11.0", 1068 + "windows-sys 0.52.0", 1069 + ] 1070 + 1071 + [[package]] 1072 + name = "ryu" 1073 + version = "1.0.20" 1074 + source = "registry+https://github.com/rust-lang/crates.io-index" 1075 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1076 + 527 1077 [[package]] 528 1078 name = "sachy-battery" 529 1079 version = "0.1.0" ··· 537 1087 "sachy-fmt", 538 1088 ] 539 1089 1090 + [[package]] 1091 + name = "sachy-esphome" 1092 + version = "0.1.0" 1093 + dependencies = [ 1094 + "anyhow", 1095 + "bytes", 1096 + "defmt 1.0.1", 1097 + "handlebars", 1098 + "heck", 1099 + "libm", 1100 + "num-derive", 1101 + "num-traits", 1102 + "prost", 1103 + "prost-build", 1104 + "prost-types", 1105 + "protobuf", 1106 + "protobuf-parse", 1107 + "serde", 1108 + "thiserror 2.0.17", 1109 + ] 1110 + 540 1111 [[package]] 541 1112 name = "sachy-fmt" 542 1113 version = "0.1.0" ··· 576 1147 source = "registry+https://github.com/rust-lang/crates.io-index" 577 1148 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 578 1149 1150 + [[package]] 1151 + name = "serde" 1152 + version = "1.0.228" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 1155 + dependencies = [ 1156 + "serde_core", 1157 + "serde_derive", 1158 + ] 1159 + 1160 + [[package]] 1161 + name = "serde_core" 1162 + version = "1.0.228" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 1165 + dependencies = [ 1166 + "serde_derive", 1167 + ] 1168 + 1169 + [[package]] 1170 + name = "serde_derive" 1171 + version = "1.0.228" 1172 + source = "registry+https://github.com/rust-lang/crates.io-index" 1173 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 1174 + dependencies = [ 1175 + "proc-macro2", 1176 + "quote", 1177 + "syn", 1178 + ] 1179 + 1180 + [[package]] 1181 + name = "serde_json" 1182 + version = "1.0.145" 1183 + source = "registry+https://github.com/rust-lang/crates.io-index" 1184 + checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 1185 + dependencies = [ 1186 + "itoa", 1187 + "memchr", 1188 + "ryu", 1189 + "serde", 1190 + "serde_core", 1191 + ] 1192 + 579 1193 [[package]] 580 1194 name = "serialport" 581 1195 version = "4.8.1" ··· 592 1206 "quote", 593 1207 "scopeguard", 594 1208 "unescaper", 595 - "windows-sys", 1209 + "windows-sys 0.52.0", 1210 + ] 1211 + 1212 + [[package]] 1213 + name = "sha2" 1214 + version = "0.10.9" 1215 + source = "registry+https://github.com/rust-lang/crates.io-index" 1216 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1217 + dependencies = [ 1218 + "cfg-if", 1219 + "cpufeatures", 1220 + "digest", 596 1221 ] 597 1222 598 1223 [[package]] ··· 631 1256 source = "registry+https://github.com/rust-lang/crates.io-index" 632 1257 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 633 1258 1259 + [[package]] 1260 + name = "strsim" 1261 + version = "0.11.1" 1262 + source = "registry+https://github.com/rust-lang/crates.io-index" 1263 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1264 + 634 1265 [[package]] 635 1266 name = "syn" 636 1267 version = "2.0.111" ··· 651 1282 "nix 0.23.2", 652 1283 ] 653 1284 1285 + [[package]] 1286 + name = "tempfile" 1287 + version = "3.23.0" 1288 + source = "registry+https://github.com/rust-lang/crates.io-index" 1289 + checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" 1290 + dependencies = [ 1291 + "fastrand", 1292 + "getrandom", 1293 + "once_cell", 1294 + "rustix 1.1.2", 1295 + "windows-sys 0.52.0", 1296 + ] 1297 + 1298 + [[package]] 1299 + name = "thiserror" 1300 + version = "1.0.69" 1301 + source = "registry+https://github.com/rust-lang/crates.io-index" 1302 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1303 + dependencies = [ 1304 + "thiserror-impl 1.0.69", 1305 + ] 1306 + 654 1307 [[package]] 655 1308 name = "thiserror" 656 1309 version = "2.0.17" 657 1310 source = "registry+https://github.com/rust-lang/crates.io-index" 658 1311 checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 659 1312 dependencies = [ 660 - "thiserror-impl", 1313 + "thiserror-impl 2.0.17", 1314 + ] 1315 + 1316 + [[package]] 1317 + name = "thiserror-impl" 1318 + version = "1.0.69" 1319 + source = "registry+https://github.com/rust-lang/crates.io-index" 1320 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1321 + dependencies = [ 1322 + "proc-macro2", 1323 + "quote", 1324 + "syn", 661 1325 ] 662 1326 663 1327 [[package]] ··· 671 1335 "syn", 672 1336 ] 673 1337 1338 + [[package]] 1339 + name = "typenum" 1340 + version = "1.19.0" 1341 + source = "registry+https://github.com/rust-lang/crates.io-index" 1342 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 1343 + 1344 + [[package]] 1345 + name = "ucd-trie" 1346 + version = "0.1.7" 1347 + source = "registry+https://github.com/rust-lang/crates.io-index" 1348 + checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 1349 + 674 1350 [[package]] 675 1351 name = "unescaper" 676 1352 version = "0.1.6" 677 1353 source = "registry+https://github.com/rust-lang/crates.io-index" 678 1354 checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" 679 1355 dependencies = [ 680 - "thiserror", 1356 + "thiserror 2.0.17", 681 1357 ] 682 1358 683 1359 [[package]] ··· 686 1362 source = "registry+https://github.com/rust-lang/crates.io-index" 687 1363 checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 688 1364 1365 + [[package]] 1366 + name = "version_check" 1367 + version = "0.9.5" 1368 + source = "registry+https://github.com/rust-lang/crates.io-index" 1369 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1370 + 689 1371 [[package]] 690 1372 name = "void" 691 1373 version = "1.0.2" 692 1374 source = "registry+https://github.com/rust-lang/crates.io-index" 693 1375 checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 694 1376 1377 + [[package]] 1378 + name = "wasip2" 1379 + version = "1.0.1+wasi-0.2.4" 1380 + source = "registry+https://github.com/rust-lang/crates.io-index" 1381 + checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 1382 + dependencies = [ 1383 + "wit-bindgen", 1384 + ] 1385 + 1386 + [[package]] 1387 + name = "which" 1388 + version = "4.4.2" 1389 + source = "registry+https://github.com/rust-lang/crates.io-index" 1390 + checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 1391 + dependencies = [ 1392 + "either", 1393 + "home", 1394 + "once_cell", 1395 + "rustix 0.38.44", 1396 + ] 1397 + 1398 + [[package]] 1399 + name = "windows-link" 1400 + version = "0.2.1" 1401 + source = "registry+https://github.com/rust-lang/crates.io-index" 1402 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 1403 + 695 1404 [[package]] 696 1405 name = "windows-sys" 697 1406 version = "0.52.0" ··· 701 1410 "windows-targets", 702 1411 ] 703 1412 1413 + [[package]] 1414 + name = "windows-sys" 1415 + version = "0.61.2" 1416 + source = "registry+https://github.com/rust-lang/crates.io-index" 1417 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 1418 + dependencies = [ 1419 + "windows-link", 1420 + ] 1421 + 704 1422 [[package]] 705 1423 name = "windows-targets" 706 1424 version = "0.52.6" ··· 764 1482 version = "0.52.6" 765 1483 source = "registry+https://github.com/rust-lang/crates.io-index" 766 1484 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1485 + 1486 + [[package]] 1487 + name = "wit-bindgen" 1488 + version = "0.46.0" 1489 + source = "registry+https://github.com/rust-lang/crates.io-index" 1490 + checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+9 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = ["sachy-battery","sachy-bthome", "sachy-fmt", "sachy-fnv", "sachy-shtc3", "sachy-sntp"] 3 + members = [ 4 + "sachy-battery", 5 + "sachy-bthome", 6 + "sachy-esphome", 7 + "sachy-fmt", 8 + "sachy-fnv", 9 + "sachy-shtc3", 10 + "sachy-sntp", 11 + ] 4 12 5 13 [workspace.package] 6 14 authors = ["Sachymetsu <sachymetsu@tutamail.com>"]
+32
sachy-esphome/Cargo.toml
··· 1 + [package] 2 + name = "sachy-esphome" 3 + authors.workspace = true 4 + edition.workspace = true 5 + repository.workspace = true 6 + license.workspace = true 7 + version.workspace = true 8 + rust-version.workspace = true 9 + 10 + [dependencies] 11 + defmt = { workspace = true, features = ["alloc"], optional = true } 12 + bytes = { version = "1", default-features = false, features = ["extra-platforms"] } 13 + libm = { version = "0.2.15", optional = true } 14 + num-derive = "0.4.2" 15 + num-traits = { version = "0.2.19", default-features = false } 16 + prost = { version = "0.14.1", default-features = false, features = ["derive"] } 17 + prost-types = { version = "0.14.1", default-features = false } 18 + thiserror = { version = "2.0.12", default-features = false } 19 + 20 + [build-dependencies] 21 + anyhow = "1.0.98" 22 + handlebars = "6.3.2" 23 + heck = "0.5.0" 24 + prost-build = "0.14.1" 25 + protobuf = "3.7.2" 26 + protobuf-parse = "3.7.2" 27 + serde = { version = "1.0.219", features = ["derive"] } 28 + 29 + [features] 30 + default = [] 31 + libm = ["dep:libm"] 32 + defmt = ["dep:defmt"]
+99
sachy-esphome/build.rs
··· 1 + use heck::ToUpperCamelCase; 2 + use std::{io::BufWriter, path::PathBuf}; 3 + 4 + static MESSAGE_TEMPLATE: &str = include_str!("./templates/message.handlebars"); 5 + 6 + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] 7 + struct ParsedMessageKind { 8 + name: String, 9 + id: u64, 10 + } 11 + 12 + fn main() -> anyhow::Result<()> { 13 + let proto_files = ["protos/api.proto", "protos/api_options.proto"]; 14 + let includes = ["protos"]; 15 + 16 + prost_build::Config::new() 17 + .btree_map(["."]) 18 + .message_attribute( 19 + ".", 20 + "#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]", 21 + ) 22 + .default_package_filename("api") 23 + .compile_protos(&proto_files, &includes)?; 24 + 25 + let valid_sources = 0..=2; 26 + 27 + let mut message_kinds: Vec<ParsedMessageKind> = protobuf_parse::Parser::new() 28 + .inputs(proto_files) 29 + .includes(includes) 30 + .parse_and_typecheck()? 31 + .file_descriptors 32 + .into_iter() 33 + .flat_map(|fd| fd.message_type) 34 + .filter_map(|message| { 35 + if let Some(protobuf::UnknownValueRef::Varint(id)) = message 36 + .options 37 + .get_or_default() 38 + .special_fields 39 + .unknown_fields() 40 + .get(1036) 41 + && let Some(protobuf::UnknownValueRef::Varint(source)) = message 42 + .options 43 + .get_or_default() 44 + .special_fields 45 + .unknown_fields() 46 + .get(1037) 47 + && valid_sources.contains(&source) 48 + { 49 + Some(ParsedMessageKind { 50 + name: sanitize_identifier(message.name().to_upper_camel_case()), 51 + id, 52 + }) 53 + } else { 54 + None 55 + } 56 + }) 57 + .collect(); 58 + 59 + message_kinds.sort(); 60 + 61 + let out_dir = PathBuf::from(std::env::var("OUT_DIR")?).join("api.rs"); 62 + 63 + let api_constants = BufWriter::new(std::fs::OpenOptions::new().append(true).open(out_dir)?); 64 + 65 + handlebars::Handlebars::new().render_template_to_write( 66 + MESSAGE_TEMPLATE, 67 + &message_kinds, 68 + api_constants, 69 + )?; 70 + 71 + Ok(()) 72 + } 73 + 74 + /// From prost-build, as it is in a private module. 75 + fn sanitize_identifier(ident: String) -> String { 76 + // Use a raw identifier if the identifier matches a Rust keyword: 77 + // https://doc.rust-lang.org/reference/keywords.html. 78 + match ident.as_str() { 79 + // 2015 strict keywords. 80 + | "as" | "break" | "const" | "continue" | "else" | "enum" | "false" 81 + | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut" 82 + | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true" 83 + | "type" | "unsafe" | "use" | "where" | "while" 84 + // 2018 strict keywords. 85 + | "dyn" 86 + // 2015 reserved keywords. 87 + | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof" 88 + | "unsized" | "virtual" | "yield" 89 + // 2018 reserved keywords. 90 + | "async" | "await" | "try" 91 + // 2024 reserved keywords. 92 + | "gen" => format!("r#{ident}"), 93 + // the following keywords are not supported as raw identifiers and are therefore suffixed with an underscore. 94 + "_" | "super" | "self" | "Self" | "extern" | "crate" => format!("{ident}_"), 95 + // the following keywords begin with a number and are therefore prefixed with an underscore. 96 + s if s.starts_with(char::is_numeric) => format!("_{ident}"), 97 + _ => ident, 98 + } 99 + }
+2166
sachy-esphome/protos/api.proto
··· 1 + syntax = "proto3"; 2 + 3 + import "api_options.proto"; 4 + 5 + service APIConnection { 6 + rpc hello (HelloRequest) returns (HelloResponse) { 7 + option (needs_setup_connection) = false; 8 + option (needs_authentication) = false; 9 + } 10 + rpc connect (ConnectRequest) returns (ConnectResponse) { 11 + option (needs_setup_connection) = false; 12 + option (needs_authentication) = false; 13 + } 14 + rpc disconnect (DisconnectRequest) returns (DisconnectResponse) { 15 + option (needs_setup_connection) = false; 16 + option (needs_authentication) = false; 17 + } 18 + rpc ping (PingRequest) returns (PingResponse) { 19 + option (needs_setup_connection) = false; 20 + option (needs_authentication) = false; 21 + } 22 + rpc device_info (DeviceInfoRequest) returns (DeviceInfoResponse) { 23 + option (needs_authentication) = false; 24 + } 25 + rpc list_entities (ListEntitiesRequest) returns (void) {} 26 + rpc subscribe_states (SubscribeStatesRequest) returns (void) {} 27 + rpc subscribe_logs (SubscribeLogsRequest) returns (void) {} 28 + rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {} 29 + rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {} 30 + rpc get_time (GetTimeRequest) returns (GetTimeResponse) { 31 + option (needs_authentication) = false; 32 + } 33 + rpc execute_service (ExecuteServiceRequest) returns (void) {} 34 + rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {} 35 + 36 + rpc button_command (ButtonCommandRequest) returns (void) {} 37 + rpc camera_image (CameraImageRequest) returns (void) {} 38 + rpc climate_command (ClimateCommandRequest) returns (void) {} 39 + rpc cover_command (CoverCommandRequest) returns (void) {} 40 + rpc date_command (DateCommandRequest) returns (void) {} 41 + rpc datetime_command (DateTimeCommandRequest) returns (void) {} 42 + rpc fan_command (FanCommandRequest) returns (void) {} 43 + rpc light_command (LightCommandRequest) returns (void) {} 44 + rpc lock_command (LockCommandRequest) returns (void) {} 45 + rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} 46 + rpc number_command (NumberCommandRequest) returns (void) {} 47 + rpc select_command (SelectCommandRequest) returns (void) {} 48 + rpc siren_command (SirenCommandRequest) returns (void) {} 49 + rpc switch_command (SwitchCommandRequest) returns (void) {} 50 + rpc text_command (TextCommandRequest) returns (void) {} 51 + rpc time_command (TimeCommandRequest) returns (void) {} 52 + rpc update_command (UpdateCommandRequest) returns (void) {} 53 + rpc valve_command (ValveCommandRequest) returns (void) {} 54 + 55 + rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} 56 + rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {} 57 + rpc bluetooth_gatt_get_services(BluetoothGATTGetServicesRequest) returns (void) {} 58 + rpc bluetooth_gatt_read(BluetoothGATTReadRequest) returns (void) {} 59 + rpc bluetooth_gatt_write(BluetoothGATTWriteRequest) returns (void) {} 60 + rpc bluetooth_gatt_read_descriptor(BluetoothGATTReadDescriptorRequest) returns (void) {} 61 + rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {} 62 + rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {} 63 + rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {} 64 + rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} 65 + rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {} 66 + 67 + rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} 68 + rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {} 69 + rpc voice_assistant_set_configuration(VoiceAssistantSetConfiguration) returns (void) {} 70 + 71 + rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {} 72 + } 73 + 74 + 75 + // ==================== BASE PACKETS ==================== 76 + 77 + // The Home Assistant protocol is structured as a simple 78 + // TCP socket with short binary messages encoded in the protocol buffers format 79 + // First, a message in this protocol has a specific format: 80 + // * A zero byte. 81 + // * VarInt denoting the size of the message object. (type is not part of this) 82 + // * VarInt denoting the type of message. 83 + // * The message object encoded as a ProtoBuf message 84 + 85 + // The connection is established in 4 steps: 86 + // * First, the client connects to the server and sends a "Hello Request" identifying itself 87 + // * The server responds with a "Hello Response" and selects the protocol version 88 + // * After receiving this message, the client attempts to authenticate itself using 89 + // the password and a "Connect Request" 90 + // * The server responds with a "Connect Response" and notifies of invalid password. 91 + // If anything in this initial process fails, the connection must immediately closed 92 + // by both sides and _no_ disconnection message is to be sent. 93 + 94 + // Message sent at the beginning of each connection 95 + // Can only be sent by the client and only at the beginning of the connection 96 + message HelloRequest { 97 + option (id) = 1; 98 + option (source) = SOURCE_CLIENT; 99 + option (no_delay) = true; 100 + 101 + // Description of client (like User Agent) 102 + // For example "Home Assistant" 103 + // Not strictly necessary to send but nice for debugging 104 + // purposes. 105 + string client_info = 1; 106 + uint32 api_version_major = 2; 107 + uint32 api_version_minor = 3; 108 + } 109 + 110 + // Confirmation of successful connection request. 111 + // Can only be sent by the server and only at the beginning of the connection 112 + message HelloResponse { 113 + option (id) = 2; 114 + option (source) = SOURCE_SERVER; 115 + option (no_delay) = true; 116 + 117 + // The version of the API to use. The _client_ (for example Home Assistant) needs to check 118 + // for compatibility and if necessary adopt to an older API. 119 + // Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_ 120 + // Minor is for breaking changes in individual messages - a mismatch will lead to a warning message 121 + uint32 api_version_major = 1; 122 + uint32 api_version_minor = 2; 123 + 124 + // A string identifying the server (ESP); like client info this may be empty 125 + // and only exists for debugging/logging purposes. 126 + // For example "ESPHome v1.10.0 on ESP8266" 127 + string server_info = 3; 128 + 129 + // The name of the server (App.get_name()) 130 + string name = 4; 131 + } 132 + 133 + // Message sent at the beginning of each connection to authenticate the client 134 + // Can only be sent by the client and only at the beginning of the connection 135 + message ConnectRequest { 136 + option (id) = 3; 137 + option (source) = SOURCE_CLIENT; 138 + option (no_delay) = true; 139 + 140 + // The password to log in with 141 + string password = 1; 142 + } 143 + 144 + // Confirmation of successful connection. After this the connection is available for all traffic. 145 + // Can only be sent by the server and only at the beginning of the connection 146 + message ConnectResponse { 147 + option (id) = 4; 148 + option (source) = SOURCE_SERVER; 149 + option (no_delay) = true; 150 + 151 + bool invalid_password = 1; 152 + } 153 + 154 + // Request to close the connection. 155 + // Can be sent by both the client and server 156 + message DisconnectRequest { 157 + option (id) = 5; 158 + option (source) = SOURCE_BOTH; 159 + option (no_delay) = true; 160 + 161 + // Do not close the connection before the acknowledgement arrives 162 + } 163 + 164 + message DisconnectResponse { 165 + option (id) = 6; 166 + option (source) = SOURCE_BOTH; 167 + option (no_delay) = true; 168 + 169 + // Empty - Both parties are required to close the connection after this 170 + // message has been received. 171 + } 172 + 173 + message PingRequest { 174 + option (id) = 7; 175 + option (source) = SOURCE_BOTH; 176 + // Empty 177 + } 178 + 179 + message PingResponse { 180 + option (id) = 8; 181 + option (source) = SOURCE_BOTH; 182 + // Empty 183 + } 184 + 185 + message DeviceInfoRequest { 186 + option (id) = 9; 187 + option (source) = SOURCE_CLIENT; 188 + // Empty 189 + } 190 + 191 + message AreaInfo { 192 + uint32 area_id = 1; 193 + string name = 2; 194 + } 195 + 196 + message DeviceInfo { 197 + uint32 device_id = 1; 198 + string name = 2; 199 + uint32 area_id = 3; 200 + } 201 + 202 + message DeviceInfoResponse { 203 + option (id) = 10; 204 + option (source) = SOURCE_SERVER; 205 + 206 + bool uses_password = 1; 207 + 208 + // The name of the node, given by "App.set_name()" 209 + string name = 2; 210 + 211 + // The mac address of the device. For example "AC:BC:32:89:0E:A9" 212 + string mac_address = 3; 213 + 214 + // A string describing the ESPHome version. For example "1.10.0" 215 + string esphome_version = 4; 216 + 217 + // A string describing the date of compilation, this is generated by the compiler 218 + // and therefore may not be in the same format all the time. 219 + // If the user isn't using ESPHome, this will also not be set. 220 + string compilation_time = 5; 221 + 222 + // The model of the board. For example NodeMCU 223 + string model = 6; 224 + 225 + bool has_deep_sleep = 7; 226 + 227 + // The esphome project details if set 228 + string project_name = 8; 229 + string project_version = 9; 230 + 231 + uint32 webserver_port = 10; 232 + 233 + uint32 legacy_bluetooth_proxy_version = 11; 234 + uint32 bluetooth_proxy_feature_flags = 15; 235 + 236 + string manufacturer = 12; 237 + 238 + string friendly_name = 13; 239 + 240 + uint32 legacy_voice_assistant_version = 14; 241 + uint32 voice_assistant_feature_flags = 17; 242 + 243 + string suggested_area = 16; 244 + 245 + // The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA" 246 + string bluetooth_mac_address = 18; 247 + 248 + // Supports receiving and saving api encryption key 249 + bool api_encryption_supported = 19; 250 + 251 + repeated DeviceInfo devices = 20; 252 + repeated AreaInfo areas = 21; 253 + 254 + // Top-level area info to phase out suggested_area 255 + AreaInfo area = 22; 256 + } 257 + 258 + message ListEntitiesRequest { 259 + option (id) = 11; 260 + option (source) = SOURCE_CLIENT; 261 + // Empty 262 + } 263 + message ListEntitiesDoneResponse { 264 + option (id) = 19; 265 + option (source) = SOURCE_SERVER; 266 + option (no_delay) = true; 267 + // Empty 268 + } 269 + message SubscribeStatesRequest { 270 + option (id) = 20; 271 + option (source) = SOURCE_CLIENT; 272 + // Empty 273 + } 274 + 275 + // ==================== COMMON ===================== 276 + 277 + enum EntityCategory { 278 + ENTITY_CATEGORY_NONE = 0; 279 + ENTITY_CATEGORY_CONFIG = 1; 280 + ENTITY_CATEGORY_DIAGNOSTIC = 2; 281 + } 282 + 283 + // ==================== BINARY SENSOR ==================== 284 + message ListEntitiesBinarySensorResponse { 285 + option (id) = 12; 286 + option (base_class) = "InfoResponseProtoMessage"; 287 + option (source) = SOURCE_SERVER; 288 + option (ifdef) = "USE_BINARY_SENSOR"; 289 + 290 + string object_id = 1; 291 + fixed32 key = 2; 292 + string name = 3; 293 + string unique_id = 4; 294 + 295 + string device_class = 5; 296 + bool is_status_binary_sensor = 6; 297 + bool disabled_by_default = 7; 298 + string icon = 8; 299 + EntityCategory entity_category = 9; 300 + uint32 device_id = 10; 301 + } 302 + message BinarySensorStateResponse { 303 + option (id) = 21; 304 + option (base_class) = "StateResponseProtoMessage"; 305 + option (source) = SOURCE_SERVER; 306 + option (ifdef) = "USE_BINARY_SENSOR"; 307 + option (no_delay) = true; 308 + 309 + fixed32 key = 1; 310 + bool state = 2; 311 + // If the binary sensor does not have a valid state yet. 312 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 313 + bool missing_state = 3; 314 + uint32 device_id = 4; 315 + } 316 + 317 + // ==================== COVER ==================== 318 + message ListEntitiesCoverResponse { 319 + option (id) = 13; 320 + option (base_class) = "InfoResponseProtoMessage"; 321 + option (source) = SOURCE_SERVER; 322 + option (ifdef) = "USE_COVER"; 323 + 324 + string object_id = 1; 325 + fixed32 key = 2; 326 + string name = 3; 327 + string unique_id = 4; 328 + 329 + bool assumed_state = 5; 330 + bool supports_position = 6; 331 + bool supports_tilt = 7; 332 + string device_class = 8; 333 + bool disabled_by_default = 9; 334 + string icon = 10; 335 + EntityCategory entity_category = 11; 336 + bool supports_stop = 12; 337 + uint32 device_id = 13; 338 + } 339 + 340 + enum LegacyCoverState { 341 + LEGACY_COVER_STATE_OPEN = 0; 342 + LEGACY_COVER_STATE_CLOSED = 1; 343 + } 344 + enum CoverOperation { 345 + COVER_OPERATION_IDLE = 0; 346 + COVER_OPERATION_IS_OPENING = 1; 347 + COVER_OPERATION_IS_CLOSING = 2; 348 + } 349 + message CoverStateResponse { 350 + option (id) = 22; 351 + option (base_class) = "StateResponseProtoMessage"; 352 + option (source) = SOURCE_SERVER; 353 + option (ifdef) = "USE_COVER"; 354 + option (no_delay) = true; 355 + 356 + fixed32 key = 1; 357 + // legacy: state has been removed in 1.13 358 + // clients/servers must still send/accept it until the next protocol change 359 + LegacyCoverState legacy_state = 2; 360 + 361 + float position = 3; 362 + float tilt = 4; 363 + CoverOperation current_operation = 5; 364 + uint32 device_id = 6; 365 + } 366 + 367 + enum LegacyCoverCommand { 368 + LEGACY_COVER_COMMAND_OPEN = 0; 369 + LEGACY_COVER_COMMAND_CLOSE = 1; 370 + LEGACY_COVER_COMMAND_STOP = 2; 371 + } 372 + message CoverCommandRequest { 373 + option (id) = 30; 374 + option (source) = SOURCE_CLIENT; 375 + option (ifdef) = "USE_COVER"; 376 + option (no_delay) = true; 377 + 378 + fixed32 key = 1; 379 + 380 + // legacy: command has been removed in 1.13 381 + // clients/servers must still send/accept it until the next protocol change 382 + bool has_legacy_command = 2; 383 + LegacyCoverCommand legacy_command = 3; 384 + 385 + bool has_position = 4; 386 + float position = 5; 387 + bool has_tilt = 6; 388 + float tilt = 7; 389 + bool stop = 8; 390 + } 391 + 392 + // ==================== FAN ==================== 393 + message ListEntitiesFanResponse { 394 + option (id) = 14; 395 + option (base_class) = "InfoResponseProtoMessage"; 396 + option (source) = SOURCE_SERVER; 397 + option (ifdef) = "USE_FAN"; 398 + 399 + string object_id = 1; 400 + fixed32 key = 2; 401 + string name = 3; 402 + string unique_id = 4; 403 + 404 + bool supports_oscillation = 5; 405 + bool supports_speed = 6; 406 + bool supports_direction = 7; 407 + int32 supported_speed_count = 8; 408 + bool disabled_by_default = 9; 409 + string icon = 10; 410 + EntityCategory entity_category = 11; 411 + repeated string supported_preset_modes = 12; 412 + uint32 device_id = 13; 413 + } 414 + enum FanSpeed { 415 + FAN_SPEED_LOW = 0; 416 + FAN_SPEED_MEDIUM = 1; 417 + FAN_SPEED_HIGH = 2; 418 + } 419 + enum FanDirection { 420 + FAN_DIRECTION_FORWARD = 0; 421 + FAN_DIRECTION_REVERSE = 1; 422 + } 423 + message FanStateResponse { 424 + option (id) = 23; 425 + option (base_class) = "StateResponseProtoMessage"; 426 + option (source) = SOURCE_SERVER; 427 + option (ifdef) = "USE_FAN"; 428 + option (no_delay) = true; 429 + 430 + fixed32 key = 1; 431 + bool state = 2; 432 + bool oscillating = 3; 433 + FanSpeed speed = 4 [deprecated = true]; 434 + FanDirection direction = 5; 435 + int32 speed_level = 6; 436 + string preset_mode = 7; 437 + uint32 device_id = 8; 438 + } 439 + message FanCommandRequest { 440 + option (id) = 31; 441 + option (source) = SOURCE_CLIENT; 442 + option (ifdef) = "USE_FAN"; 443 + option (no_delay) = true; 444 + 445 + fixed32 key = 1; 446 + bool has_state = 2; 447 + bool state = 3; 448 + bool has_speed = 4 [deprecated = true]; 449 + FanSpeed speed = 5 [deprecated = true]; 450 + bool has_oscillating = 6; 451 + bool oscillating = 7; 452 + bool has_direction = 8; 453 + FanDirection direction = 9; 454 + bool has_speed_level = 10; 455 + int32 speed_level = 11; 456 + bool has_preset_mode = 12; 457 + string preset_mode = 13; 458 + } 459 + 460 + // ==================== LIGHT ==================== 461 + enum ColorMode { 462 + COLOR_MODE_UNKNOWN = 0; 463 + COLOR_MODE_ON_OFF = 1; 464 + COLOR_MODE_LEGACY_BRIGHTNESS = 2; 465 + COLOR_MODE_BRIGHTNESS = 3; 466 + COLOR_MODE_WHITE = 7; 467 + COLOR_MODE_COLOR_TEMPERATURE = 11; 468 + COLOR_MODE_COLD_WARM_WHITE = 19; 469 + COLOR_MODE_RGB = 35; 470 + COLOR_MODE_RGB_WHITE = 39; 471 + COLOR_MODE_RGB_COLOR_TEMPERATURE = 47; 472 + COLOR_MODE_RGB_COLD_WARM_WHITE = 51; 473 + } 474 + message ListEntitiesLightResponse { 475 + option (id) = 15; 476 + option (base_class) = "InfoResponseProtoMessage"; 477 + option (source) = SOURCE_SERVER; 478 + option (ifdef) = "USE_LIGHT"; 479 + 480 + string object_id = 1; 481 + fixed32 key = 2; 482 + string name = 3; 483 + string unique_id = 4; 484 + 485 + repeated ColorMode supported_color_modes = 12; 486 + // next four supports_* are for legacy clients, newer clients should use color modes 487 + bool legacy_supports_brightness = 5 [deprecated=true]; 488 + bool legacy_supports_rgb = 6 [deprecated=true]; 489 + bool legacy_supports_white_value = 7 [deprecated=true]; 490 + bool legacy_supports_color_temperature = 8 [deprecated=true]; 491 + float min_mireds = 9; 492 + float max_mireds = 10; 493 + repeated string effects = 11; 494 + bool disabled_by_default = 13; 495 + string icon = 14; 496 + EntityCategory entity_category = 15; 497 + uint32 device_id = 16; 498 + } 499 + message LightStateResponse { 500 + option (id) = 24; 501 + option (base_class) = "StateResponseProtoMessage"; 502 + option (source) = SOURCE_SERVER; 503 + option (ifdef) = "USE_LIGHT"; 504 + option (no_delay) = true; 505 + 506 + fixed32 key = 1; 507 + bool state = 2; 508 + float brightness = 3; 509 + ColorMode color_mode = 11; 510 + float color_brightness = 10; 511 + float red = 4; 512 + float green = 5; 513 + float blue = 6; 514 + float white = 7; 515 + float color_temperature = 8; 516 + float cold_white = 12; 517 + float warm_white = 13; 518 + string effect = 9; 519 + uint32 device_id = 14; 520 + } 521 + message LightCommandRequest { 522 + option (id) = 32; 523 + option (source) = SOURCE_CLIENT; 524 + option (ifdef) = "USE_LIGHT"; 525 + option (no_delay) = true; 526 + 527 + fixed32 key = 1; 528 + bool has_state = 2; 529 + bool state = 3; 530 + bool has_brightness = 4; 531 + float brightness = 5; 532 + bool has_color_mode = 22; 533 + ColorMode color_mode = 23; 534 + bool has_color_brightness = 20; 535 + float color_brightness = 21; 536 + bool has_rgb = 6; 537 + float red = 7; 538 + float green = 8; 539 + float blue = 9; 540 + bool has_white = 10; 541 + float white = 11; 542 + bool has_color_temperature = 12; 543 + float color_temperature = 13; 544 + bool has_cold_white = 24; 545 + float cold_white = 25; 546 + bool has_warm_white = 26; 547 + float warm_white = 27; 548 + bool has_transition_length = 14; 549 + uint32 transition_length = 15; 550 + bool has_flash_length = 16; 551 + uint32 flash_length = 17; 552 + bool has_effect = 18; 553 + string effect = 19; 554 + } 555 + 556 + // ==================== SENSOR ==================== 557 + enum SensorStateClass { 558 + STATE_CLASS_NONE = 0; 559 + STATE_CLASS_MEASUREMENT = 1; 560 + STATE_CLASS_TOTAL_INCREASING = 2; 561 + STATE_CLASS_TOTAL = 3; 562 + } 563 + 564 + enum SensorLastResetType { 565 + LAST_RESET_NONE = 0; 566 + LAST_RESET_NEVER = 1; 567 + LAST_RESET_AUTO = 2; 568 + } 569 + 570 + message ListEntitiesSensorResponse { 571 + option (id) = 16; 572 + option (base_class) = "InfoResponseProtoMessage"; 573 + option (source) = SOURCE_SERVER; 574 + option (ifdef) = "USE_SENSOR"; 575 + 576 + string object_id = 1; 577 + fixed32 key = 2; 578 + string name = 3; 579 + string unique_id = 4; 580 + 581 + string icon = 5; 582 + string unit_of_measurement = 6; 583 + int32 accuracy_decimals = 7; 584 + bool force_update = 8; 585 + string device_class = 9; 586 + SensorStateClass state_class = 10; 587 + // Last reset type removed in 2021.9.0 588 + SensorLastResetType legacy_last_reset_type = 11; 589 + bool disabled_by_default = 12; 590 + EntityCategory entity_category = 13; 591 + uint32 device_id = 14; 592 + } 593 + message SensorStateResponse { 594 + option (id) = 25; 595 + option (base_class) = "StateResponseProtoMessage"; 596 + option (source) = SOURCE_SERVER; 597 + option (ifdef) = "USE_SENSOR"; 598 + option (no_delay) = true; 599 + 600 + fixed32 key = 1; 601 + float state = 2; 602 + // If the sensor does not have a valid state yet. 603 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 604 + bool missing_state = 3; 605 + uint32 device_id = 4; 606 + } 607 + 608 + // ==================== SWITCH ==================== 609 + message ListEntitiesSwitchResponse { 610 + option (id) = 17; 611 + option (base_class) = "InfoResponseProtoMessage"; 612 + option (source) = SOURCE_SERVER; 613 + option (ifdef) = "USE_SWITCH"; 614 + 615 + string object_id = 1; 616 + fixed32 key = 2; 617 + string name = 3; 618 + string unique_id = 4; 619 + 620 + string icon = 5; 621 + bool assumed_state = 6; 622 + bool disabled_by_default = 7; 623 + EntityCategory entity_category = 8; 624 + string device_class = 9; 625 + uint32 device_id = 10; 626 + } 627 + message SwitchStateResponse { 628 + option (id) = 26; 629 + option (base_class) = "StateResponseProtoMessage"; 630 + option (source) = SOURCE_SERVER; 631 + option (ifdef) = "USE_SWITCH"; 632 + option (no_delay) = true; 633 + 634 + fixed32 key = 1; 635 + bool state = 2; 636 + uint32 device_id = 3; 637 + } 638 + message SwitchCommandRequest { 639 + option (id) = 33; 640 + option (source) = SOURCE_CLIENT; 641 + option (ifdef) = "USE_SWITCH"; 642 + option (no_delay) = true; 643 + 644 + fixed32 key = 1; 645 + bool state = 2; 646 + } 647 + 648 + // ==================== TEXT SENSOR ==================== 649 + message ListEntitiesTextSensorResponse { 650 + option (id) = 18; 651 + option (base_class) = "InfoResponseProtoMessage"; 652 + option (source) = SOURCE_SERVER; 653 + option (ifdef) = "USE_TEXT_SENSOR"; 654 + 655 + string object_id = 1; 656 + fixed32 key = 2; 657 + string name = 3; 658 + string unique_id = 4; 659 + 660 + string icon = 5; 661 + bool disabled_by_default = 6; 662 + EntityCategory entity_category = 7; 663 + string device_class = 8; 664 + uint32 device_id = 9; 665 + } 666 + message TextSensorStateResponse { 667 + option (id) = 27; 668 + option (base_class) = "StateResponseProtoMessage"; 669 + option (source) = SOURCE_SERVER; 670 + option (ifdef) = "USE_TEXT_SENSOR"; 671 + option (no_delay) = true; 672 + 673 + fixed32 key = 1; 674 + string state = 2; 675 + // If the text sensor does not have a valid state yet. 676 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 677 + bool missing_state = 3; 678 + uint32 device_id = 4; 679 + } 680 + 681 + // ==================== SUBSCRIBE LOGS ==================== 682 + enum LogLevel { 683 + LOG_LEVEL_NONE = 0; 684 + LOG_LEVEL_ERROR = 1; 685 + LOG_LEVEL_WARN = 2; 686 + LOG_LEVEL_INFO = 3; 687 + LOG_LEVEL_CONFIG = 4; 688 + LOG_LEVEL_DEBUG = 5; 689 + LOG_LEVEL_VERBOSE = 6; 690 + LOG_LEVEL_VERY_VERBOSE = 7; 691 + } 692 + message SubscribeLogsRequest { 693 + option (id) = 28; 694 + option (source) = SOURCE_CLIENT; 695 + LogLevel level = 1; 696 + bool dump_config = 2; 697 + } 698 + message SubscribeLogsResponse { 699 + option (id) = 29; 700 + option (source) = SOURCE_SERVER; 701 + option (log) = false; 702 + option (no_delay) = false; 703 + 704 + LogLevel level = 1; 705 + bytes message = 3; 706 + bool send_failed = 4; 707 + } 708 + 709 + // ==================== NOISE ENCRYPTION ==================== 710 + message NoiseEncryptionSetKeyRequest { 711 + option (id) = 124; 712 + option (source) = SOURCE_CLIENT; 713 + option (ifdef) = "USE_API_NOISE"; 714 + 715 + bytes key = 1; 716 + } 717 + 718 + message NoiseEncryptionSetKeyResponse { 719 + option (id) = 125; 720 + option (source) = SOURCE_SERVER; 721 + option (ifdef) = "USE_API_NOISE"; 722 + 723 + bool success = 1; 724 + } 725 + 726 + // ==================== HOMEASSISTANT.SERVICE ==================== 727 + message SubscribeHomeassistantServicesRequest { 728 + option (id) = 34; 729 + option (source) = SOURCE_CLIENT; 730 + } 731 + 732 + message HomeassistantServiceMap { 733 + string key = 1; 734 + string value = 2; 735 + } 736 + 737 + message HomeassistantServiceResponse { 738 + option (id) = 35; 739 + option (source) = SOURCE_SERVER; 740 + option (no_delay) = true; 741 + 742 + string service = 1; 743 + repeated HomeassistantServiceMap data = 2; 744 + repeated HomeassistantServiceMap data_template = 3; 745 + repeated HomeassistantServiceMap variables = 4; 746 + bool is_event = 5; 747 + } 748 + 749 + // ==================== IMPORT HOME ASSISTANT STATES ==================== 750 + // 1. Client sends SubscribeHomeAssistantStatesRequest 751 + // 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async) 752 + // 3. Client sends HomeAssistantStateResponse for state changes. 753 + message SubscribeHomeAssistantStatesRequest { 754 + option (id) = 38; 755 + option (source) = SOURCE_CLIENT; 756 + } 757 + 758 + message SubscribeHomeAssistantStateResponse { 759 + option (id) = 39; 760 + option (source) = SOURCE_SERVER; 761 + string entity_id = 1; 762 + string attribute = 2; 763 + bool once = 3; 764 + } 765 + 766 + message HomeAssistantStateResponse { 767 + option (id) = 40; 768 + option (source) = SOURCE_CLIENT; 769 + option (no_delay) = true; 770 + 771 + string entity_id = 1; 772 + string state = 2; 773 + string attribute = 3; 774 + } 775 + 776 + // ==================== IMPORT TIME ==================== 777 + message GetTimeRequest { 778 + option (id) = 36; 779 + option (source) = SOURCE_BOTH; 780 + } 781 + 782 + message GetTimeResponse { 783 + option (id) = 37; 784 + option (source) = SOURCE_BOTH; 785 + option (no_delay) = true; 786 + 787 + fixed32 epoch_seconds = 1; 788 + } 789 + 790 + // ==================== USER-DEFINES SERVICES ==================== 791 + enum ServiceArgType { 792 + SERVICE_ARG_TYPE_BOOL = 0; 793 + SERVICE_ARG_TYPE_INT = 1; 794 + SERVICE_ARG_TYPE_FLOAT = 2; 795 + SERVICE_ARG_TYPE_STRING = 3; 796 + SERVICE_ARG_TYPE_BOOL_ARRAY = 4; 797 + SERVICE_ARG_TYPE_INT_ARRAY = 5; 798 + SERVICE_ARG_TYPE_FLOAT_ARRAY = 6; 799 + SERVICE_ARG_TYPE_STRING_ARRAY = 7; 800 + } 801 + message ListEntitiesServicesArgument { 802 + string name = 1; 803 + ServiceArgType type = 2; 804 + } 805 + message ListEntitiesServicesResponse { 806 + option (id) = 41; 807 + option (source) = SOURCE_SERVER; 808 + 809 + string name = 1; 810 + fixed32 key = 2; 811 + repeated ListEntitiesServicesArgument args = 3; 812 + } 813 + message ExecuteServiceArgument { 814 + bool bool_ = 1; 815 + int32 legacy_int = 2; 816 + float float_ = 3; 817 + string string_ = 4; 818 + // ESPHome 1.14 (api v1.3) make int a signed value 819 + sint32 int_ = 5; 820 + repeated bool bool_array = 6 [packed=false]; 821 + repeated sint32 int_array = 7 [packed=false]; 822 + repeated float float_array = 8 [packed=false]; 823 + repeated string string_array = 9; 824 + } 825 + message ExecuteServiceRequest { 826 + option (id) = 42; 827 + option (source) = SOURCE_CLIENT; 828 + option (no_delay) = true; 829 + 830 + fixed32 key = 1; 831 + repeated ExecuteServiceArgument args = 2; 832 + } 833 + 834 + // ==================== CAMERA ==================== 835 + message ListEntitiesCameraResponse { 836 + option (id) = 43; 837 + option (base_class) = "InfoResponseProtoMessage"; 838 + option (source) = SOURCE_SERVER; 839 + option (ifdef) = "USE_CAMERA"; 840 + 841 + string object_id = 1; 842 + fixed32 key = 2; 843 + string name = 3; 844 + string unique_id = 4; 845 + bool disabled_by_default = 5; 846 + string icon = 6; 847 + EntityCategory entity_category = 7; 848 + uint32 device_id = 8; 849 + } 850 + 851 + message CameraImageResponse { 852 + option (id) = 44; 853 + option (source) = SOURCE_SERVER; 854 + option (ifdef) = "USE_CAMERA"; 855 + 856 + fixed32 key = 1; 857 + bytes data = 2; 858 + bool done = 3; 859 + } 860 + message CameraImageRequest { 861 + option (id) = 45; 862 + option (source) = SOURCE_CLIENT; 863 + option (ifdef) = "USE_CAMERA"; 864 + option (no_delay) = true; 865 + 866 + bool single = 1; 867 + bool stream = 2; 868 + } 869 + 870 + // ==================== CLIMATE ==================== 871 + enum ClimateMode { 872 + CLIMATE_MODE_OFF = 0; 873 + CLIMATE_MODE_HEAT_COOL = 1; 874 + CLIMATE_MODE_COOL = 2; 875 + CLIMATE_MODE_HEAT = 3; 876 + CLIMATE_MODE_FAN_ONLY = 4; 877 + CLIMATE_MODE_DRY = 5; 878 + CLIMATE_MODE_AUTO = 6; 879 + } 880 + enum ClimateFanMode { 881 + CLIMATE_FAN_ON = 0; 882 + CLIMATE_FAN_OFF = 1; 883 + CLIMATE_FAN_AUTO = 2; 884 + CLIMATE_FAN_LOW = 3; 885 + CLIMATE_FAN_MEDIUM = 4; 886 + CLIMATE_FAN_HIGH = 5; 887 + CLIMATE_FAN_MIDDLE = 6; 888 + CLIMATE_FAN_FOCUS = 7; 889 + CLIMATE_FAN_DIFFUSE = 8; 890 + CLIMATE_FAN_QUIET = 9; 891 + } 892 + enum ClimateSwingMode { 893 + CLIMATE_SWING_OFF = 0; 894 + CLIMATE_SWING_BOTH = 1; 895 + CLIMATE_SWING_VERTICAL = 2; 896 + CLIMATE_SWING_HORIZONTAL = 3; 897 + } 898 + enum ClimateAction { 899 + CLIMATE_ACTION_OFF = 0; 900 + // values same as mode for readability 901 + CLIMATE_ACTION_COOLING = 2; 902 + CLIMATE_ACTION_HEATING = 3; 903 + CLIMATE_ACTION_IDLE = 4; 904 + CLIMATE_ACTION_DRYING = 5; 905 + CLIMATE_ACTION_FAN = 6; 906 + } 907 + enum ClimatePreset { 908 + CLIMATE_PRESET_NONE = 0; 909 + CLIMATE_PRESET_HOME = 1; 910 + CLIMATE_PRESET_AWAY = 2; 911 + CLIMATE_PRESET_BOOST = 3; 912 + CLIMATE_PRESET_COMFORT = 4; 913 + CLIMATE_PRESET_ECO = 5; 914 + CLIMATE_PRESET_SLEEP = 6; 915 + CLIMATE_PRESET_ACTIVITY = 7; 916 + } 917 + message ListEntitiesClimateResponse { 918 + option (id) = 46; 919 + option (base_class) = "InfoResponseProtoMessage"; 920 + option (source) = SOURCE_SERVER; 921 + option (ifdef) = "USE_CLIMATE"; 922 + 923 + string object_id = 1; 924 + fixed32 key = 2; 925 + string name = 3; 926 + string unique_id = 4; 927 + 928 + bool supports_current_temperature = 5; 929 + bool supports_two_point_target_temperature = 6; 930 + repeated ClimateMode supported_modes = 7; 931 + float visual_min_temperature = 8; 932 + float visual_max_temperature = 9; 933 + float visual_target_temperature_step = 10; 934 + // for older peer versions - in new system this 935 + // is if CLIMATE_PRESET_AWAY exists is supported_presets 936 + bool legacy_supports_away = 11; 937 + bool supports_action = 12; 938 + repeated ClimateFanMode supported_fan_modes = 13; 939 + repeated ClimateSwingMode supported_swing_modes = 14; 940 + repeated string supported_custom_fan_modes = 15; 941 + repeated ClimatePreset supported_presets = 16; 942 + repeated string supported_custom_presets = 17; 943 + bool disabled_by_default = 18; 944 + string icon = 19; 945 + EntityCategory entity_category = 20; 946 + float visual_current_temperature_step = 21; 947 + bool supports_current_humidity = 22; 948 + bool supports_target_humidity = 23; 949 + float visual_min_humidity = 24; 950 + float visual_max_humidity = 25; 951 + uint32 device_id = 26; 952 + } 953 + message ClimateStateResponse { 954 + option (id) = 47; 955 + option (base_class) = "StateResponseProtoMessage"; 956 + option (source) = SOURCE_SERVER; 957 + option (ifdef) = "USE_CLIMATE"; 958 + option (no_delay) = true; 959 + 960 + fixed32 key = 1; 961 + ClimateMode mode = 2; 962 + float current_temperature = 3; 963 + float target_temperature = 4; 964 + float target_temperature_low = 5; 965 + float target_temperature_high = 6; 966 + // For older peers, equal to preset == CLIMATE_PRESET_AWAY 967 + bool unused_legacy_away = 7; 968 + ClimateAction action = 8; 969 + ClimateFanMode fan_mode = 9; 970 + ClimateSwingMode swing_mode = 10; 971 + string custom_fan_mode = 11; 972 + ClimatePreset preset = 12; 973 + string custom_preset = 13; 974 + float current_humidity = 14; 975 + float target_humidity = 15; 976 + uint32 device_id = 16; 977 + } 978 + message ClimateCommandRequest { 979 + option (id) = 48; 980 + option (source) = SOURCE_CLIENT; 981 + option (ifdef) = "USE_CLIMATE"; 982 + option (no_delay) = true; 983 + 984 + fixed32 key = 1; 985 + bool has_mode = 2; 986 + ClimateMode mode = 3; 987 + bool has_target_temperature = 4; 988 + float target_temperature = 5; 989 + bool has_target_temperature_low = 6; 990 + float target_temperature_low = 7; 991 + bool has_target_temperature_high = 8; 992 + float target_temperature_high = 9; 993 + // legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset 994 + bool unused_has_legacy_away = 10; 995 + bool unused_legacy_away = 11; 996 + bool has_fan_mode = 12; 997 + ClimateFanMode fan_mode = 13; 998 + bool has_swing_mode = 14; 999 + ClimateSwingMode swing_mode = 15; 1000 + bool has_custom_fan_mode = 16; 1001 + string custom_fan_mode = 17; 1002 + bool has_preset = 18; 1003 + ClimatePreset preset = 19; 1004 + bool has_custom_preset = 20; 1005 + string custom_preset = 21; 1006 + bool has_target_humidity = 22; 1007 + float target_humidity = 23; 1008 + } 1009 + 1010 + // ==================== NUMBER ==================== 1011 + enum NumberMode { 1012 + NUMBER_MODE_AUTO = 0; 1013 + NUMBER_MODE_BOX = 1; 1014 + NUMBER_MODE_SLIDER = 2; 1015 + } 1016 + message ListEntitiesNumberResponse { 1017 + option (id) = 49; 1018 + option (base_class) = "InfoResponseProtoMessage"; 1019 + option (source) = SOURCE_SERVER; 1020 + option (ifdef) = "USE_NUMBER"; 1021 + 1022 + string object_id = 1; 1023 + fixed32 key = 2; 1024 + string name = 3; 1025 + string unique_id = 4; 1026 + 1027 + string icon = 5; 1028 + float min_value = 6; 1029 + float max_value = 7; 1030 + float step = 8; 1031 + bool disabled_by_default = 9; 1032 + EntityCategory entity_category = 10; 1033 + string unit_of_measurement = 11; 1034 + NumberMode mode = 12; 1035 + string device_class = 13; 1036 + uint32 device_id = 14; 1037 + } 1038 + message NumberStateResponse { 1039 + option (id) = 50; 1040 + option (base_class) = "StateResponseProtoMessage"; 1041 + option (source) = SOURCE_SERVER; 1042 + option (ifdef) = "USE_NUMBER"; 1043 + option (no_delay) = true; 1044 + 1045 + fixed32 key = 1; 1046 + float state = 2; 1047 + // If the number does not have a valid state yet. 1048 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 1049 + bool missing_state = 3; 1050 + uint32 device_id = 4; 1051 + } 1052 + message NumberCommandRequest { 1053 + option (id) = 51; 1054 + option (source) = SOURCE_CLIENT; 1055 + option (ifdef) = "USE_NUMBER"; 1056 + option (no_delay) = true; 1057 + 1058 + fixed32 key = 1; 1059 + float state = 2; 1060 + } 1061 + 1062 + // ==================== SELECT ==================== 1063 + message ListEntitiesSelectResponse { 1064 + option (id) = 52; 1065 + option (base_class) = "InfoResponseProtoMessage"; 1066 + option (source) = SOURCE_SERVER; 1067 + option (ifdef) = "USE_SELECT"; 1068 + 1069 + string object_id = 1; 1070 + fixed32 key = 2; 1071 + string name = 3; 1072 + string unique_id = 4; 1073 + 1074 + string icon = 5; 1075 + repeated string options = 6; 1076 + bool disabled_by_default = 7; 1077 + EntityCategory entity_category = 8; 1078 + uint32 device_id = 9; 1079 + } 1080 + message SelectStateResponse { 1081 + option (id) = 53; 1082 + option (base_class) = "StateResponseProtoMessage"; 1083 + option (source) = SOURCE_SERVER; 1084 + option (ifdef) = "USE_SELECT"; 1085 + option (no_delay) = true; 1086 + 1087 + fixed32 key = 1; 1088 + string state = 2; 1089 + // If the select does not have a valid state yet. 1090 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 1091 + bool missing_state = 3; 1092 + uint32 device_id = 4; 1093 + } 1094 + message SelectCommandRequest { 1095 + option (id) = 54; 1096 + option (source) = SOURCE_CLIENT; 1097 + option (ifdef) = "USE_SELECT"; 1098 + option (no_delay) = true; 1099 + 1100 + fixed32 key = 1; 1101 + string state = 2; 1102 + } 1103 + 1104 + // ==================== SIREN ==================== 1105 + message ListEntitiesSirenResponse { 1106 + option (id) = 55; 1107 + option (base_class) = "InfoResponseProtoMessage"; 1108 + option (source) = SOURCE_SERVER; 1109 + option (ifdef) = "USE_SIREN"; 1110 + 1111 + string object_id = 1; 1112 + fixed32 key = 2; 1113 + string name = 3; 1114 + string unique_id = 4; 1115 + 1116 + string icon = 5; 1117 + bool disabled_by_default = 6; 1118 + repeated string tones = 7; 1119 + bool supports_duration = 8; 1120 + bool supports_volume = 9; 1121 + EntityCategory entity_category = 10; 1122 + uint32 device_id = 11; 1123 + } 1124 + message SirenStateResponse { 1125 + option (id) = 56; 1126 + option (base_class) = "StateResponseProtoMessage"; 1127 + option (source) = SOURCE_SERVER; 1128 + option (ifdef) = "USE_SIREN"; 1129 + option (no_delay) = true; 1130 + 1131 + fixed32 key = 1; 1132 + bool state = 2; 1133 + uint32 device_id = 3; 1134 + } 1135 + message SirenCommandRequest { 1136 + option (id) = 57; 1137 + option (source) = SOURCE_CLIENT; 1138 + option (ifdef) = "USE_SIREN"; 1139 + option (no_delay) = true; 1140 + 1141 + fixed32 key = 1; 1142 + bool has_state = 2; 1143 + bool state = 3; 1144 + bool has_tone = 4; 1145 + string tone = 5; 1146 + bool has_duration = 6; 1147 + uint32 duration = 7; 1148 + bool has_volume = 8; 1149 + float volume = 9; 1150 + } 1151 + 1152 + // ==================== LOCK ==================== 1153 + enum LockState { 1154 + LOCK_STATE_NONE = 0; 1155 + LOCK_STATE_LOCKED = 1; 1156 + LOCK_STATE_UNLOCKED = 2; 1157 + LOCK_STATE_JAMMED = 3; 1158 + LOCK_STATE_LOCKING = 4; 1159 + LOCK_STATE_UNLOCKING = 5; 1160 + } 1161 + enum LockCommand { 1162 + LOCK_UNLOCK = 0; 1163 + LOCK_LOCK = 1; 1164 + LOCK_OPEN = 2; 1165 + } 1166 + message ListEntitiesLockResponse { 1167 + option (id) = 58; 1168 + option (base_class) = "InfoResponseProtoMessage"; 1169 + option (source) = SOURCE_SERVER; 1170 + option (ifdef) = "USE_LOCK"; 1171 + 1172 + string object_id = 1; 1173 + fixed32 key = 2; 1174 + string name = 3; 1175 + string unique_id = 4; 1176 + 1177 + string icon = 5; 1178 + bool disabled_by_default = 6; 1179 + EntityCategory entity_category = 7; 1180 + bool assumed_state = 8; 1181 + 1182 + bool supports_open = 9; 1183 + bool requires_code = 10; 1184 + 1185 + // Not yet implemented: 1186 + string code_format = 11; 1187 + uint32 device_id = 12; 1188 + } 1189 + message LockStateResponse { 1190 + option (id) = 59; 1191 + option (base_class) = "StateResponseProtoMessage"; 1192 + option (source) = SOURCE_SERVER; 1193 + option (ifdef) = "USE_LOCK"; 1194 + option (no_delay) = true; 1195 + fixed32 key = 1; 1196 + LockState state = 2; 1197 + uint32 device_id = 3; 1198 + } 1199 + message LockCommandRequest { 1200 + option (id) = 60; 1201 + option (source) = SOURCE_CLIENT; 1202 + option (ifdef) = "USE_LOCK"; 1203 + option (no_delay) = true; 1204 + fixed32 key = 1; 1205 + LockCommand command = 2; 1206 + 1207 + // Not yet implemented: 1208 + bool has_code = 3; 1209 + string code = 4; 1210 + } 1211 + 1212 + // ==================== BUTTON ==================== 1213 + message ListEntitiesButtonResponse { 1214 + option (id) = 61; 1215 + option (base_class) = "InfoResponseProtoMessage"; 1216 + option (source) = SOURCE_SERVER; 1217 + option (ifdef) = "USE_BUTTON"; 1218 + 1219 + string object_id = 1; 1220 + fixed32 key = 2; 1221 + string name = 3; 1222 + string unique_id = 4; 1223 + 1224 + string icon = 5; 1225 + bool disabled_by_default = 6; 1226 + EntityCategory entity_category = 7; 1227 + string device_class = 8; 1228 + uint32 device_id = 9; 1229 + } 1230 + message ButtonCommandRequest { 1231 + option (id) = 62; 1232 + option (source) = SOURCE_CLIENT; 1233 + option (ifdef) = "USE_BUTTON"; 1234 + option (no_delay) = true; 1235 + 1236 + fixed32 key = 1; 1237 + } 1238 + 1239 + // ==================== MEDIA PLAYER ==================== 1240 + enum MediaPlayerState { 1241 + MEDIA_PLAYER_STATE_NONE = 0; 1242 + MEDIA_PLAYER_STATE_IDLE = 1; 1243 + MEDIA_PLAYER_STATE_PLAYING = 2; 1244 + MEDIA_PLAYER_STATE_PAUSED = 3; 1245 + } 1246 + enum MediaPlayerCommand { 1247 + MEDIA_PLAYER_COMMAND_PLAY = 0; 1248 + MEDIA_PLAYER_COMMAND_PAUSE = 1; 1249 + MEDIA_PLAYER_COMMAND_STOP = 2; 1250 + MEDIA_PLAYER_COMMAND_MUTE = 3; 1251 + MEDIA_PLAYER_COMMAND_UNMUTE = 4; 1252 + } 1253 + enum MediaPlayerFormatPurpose { 1254 + MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0; 1255 + MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1; 1256 + } 1257 + message MediaPlayerSupportedFormat { 1258 + option (ifdef) = "USE_MEDIA_PLAYER"; 1259 + 1260 + string format = 1; 1261 + uint32 sample_rate = 2; 1262 + uint32 num_channels = 3; 1263 + MediaPlayerFormatPurpose purpose = 4; 1264 + uint32 sample_bytes = 5; 1265 + } 1266 + message ListEntitiesMediaPlayerResponse { 1267 + option (id) = 63; 1268 + option (base_class) = "InfoResponseProtoMessage"; 1269 + option (source) = SOURCE_SERVER; 1270 + option (ifdef) = "USE_MEDIA_PLAYER"; 1271 + 1272 + string object_id = 1; 1273 + fixed32 key = 2; 1274 + string name = 3; 1275 + string unique_id = 4; 1276 + 1277 + string icon = 5; 1278 + bool disabled_by_default = 6; 1279 + EntityCategory entity_category = 7; 1280 + 1281 + bool supports_pause = 8; 1282 + 1283 + repeated MediaPlayerSupportedFormat supported_formats = 9; 1284 + 1285 + uint32 device_id = 10; 1286 + } 1287 + message MediaPlayerStateResponse { 1288 + option (id) = 64; 1289 + option (base_class) = "StateResponseProtoMessage"; 1290 + option (source) = SOURCE_SERVER; 1291 + option (ifdef) = "USE_MEDIA_PLAYER"; 1292 + option (no_delay) = true; 1293 + fixed32 key = 1; 1294 + MediaPlayerState state = 2; 1295 + float volume = 3; 1296 + bool muted = 4; 1297 + uint32 device_id = 5; 1298 + } 1299 + message MediaPlayerCommandRequest { 1300 + option (id) = 65; 1301 + option (source) = SOURCE_CLIENT; 1302 + option (ifdef) = "USE_MEDIA_PLAYER"; 1303 + option (no_delay) = true; 1304 + 1305 + fixed32 key = 1; 1306 + 1307 + bool has_command = 2; 1308 + MediaPlayerCommand command = 3; 1309 + 1310 + bool has_volume = 4; 1311 + float volume = 5; 1312 + 1313 + bool has_media_url = 6; 1314 + string media_url = 7; 1315 + 1316 + bool has_announcement = 8; 1317 + bool announcement = 9; 1318 + } 1319 + 1320 + // ==================== BLUETOOTH ==================== 1321 + message SubscribeBluetoothLEAdvertisementsRequest { 1322 + option (id) = 66; 1323 + option (source) = SOURCE_CLIENT; 1324 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1325 + 1326 + uint32 flags = 1; 1327 + } 1328 + 1329 + message BluetoothServiceData { 1330 + string uuid = 1; 1331 + repeated uint32 legacy_data = 2 [deprecated = true]; // Removed in api version 1.7 1332 + bytes data = 3; // Added in api version 1.7 1333 + } 1334 + message BluetoothLEAdvertisementResponse { 1335 + option (id) = 67; 1336 + option (source) = SOURCE_SERVER; 1337 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1338 + option (no_delay) = true; 1339 + 1340 + uint64 address = 1; 1341 + bytes name = 2; 1342 + sint32 rssi = 3; 1343 + 1344 + repeated string service_uuids = 4; 1345 + repeated BluetoothServiceData service_data = 5; 1346 + repeated BluetoothServiceData manufacturer_data = 6; 1347 + 1348 + uint32 address_type = 7; 1349 + } 1350 + 1351 + message BluetoothLERawAdvertisement { 1352 + uint64 address = 1; 1353 + sint32 rssi = 2; 1354 + uint32 address_type = 3; 1355 + 1356 + bytes data = 4; 1357 + } 1358 + 1359 + message BluetoothLERawAdvertisementsResponse { 1360 + option (id) = 93; 1361 + option (source) = SOURCE_SERVER; 1362 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1363 + option (no_delay) = true; 1364 + 1365 + repeated BluetoothLERawAdvertisement advertisements = 1; 1366 + } 1367 + 1368 + enum BluetoothDeviceRequestType { 1369 + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0; 1370 + BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1; 1371 + BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2; 1372 + BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; 1373 + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4; 1374 + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5; 1375 + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6; 1376 + } 1377 + 1378 + message BluetoothDeviceRequest { 1379 + option (id) = 68; 1380 + option (source) = SOURCE_CLIENT; 1381 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1382 + 1383 + uint64 address = 1; 1384 + BluetoothDeviceRequestType request_type = 2; 1385 + bool has_address_type = 3; 1386 + uint32 address_type = 4; 1387 + } 1388 + 1389 + message BluetoothDeviceConnectionResponse { 1390 + option (id) = 69; 1391 + option (source) = SOURCE_SERVER; 1392 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1393 + 1394 + uint64 address = 1; 1395 + bool connected = 2; 1396 + uint32 mtu = 3; 1397 + int32 error = 4; 1398 + } 1399 + 1400 + message BluetoothGATTGetServicesRequest { 1401 + option (id) = 70; 1402 + option (source) = SOURCE_CLIENT; 1403 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1404 + 1405 + uint64 address = 1; 1406 + } 1407 + 1408 + message BluetoothGATTDescriptor { 1409 + repeated uint64 uuid = 1; 1410 + uint32 handle = 2; 1411 + } 1412 + 1413 + message BluetoothGATTCharacteristic { 1414 + repeated uint64 uuid = 1; 1415 + uint32 handle = 2; 1416 + uint32 properties = 3; 1417 + repeated BluetoothGATTDescriptor descriptors = 4; 1418 + } 1419 + 1420 + message BluetoothGATTService { 1421 + repeated uint64 uuid = 1; 1422 + uint32 handle = 2; 1423 + repeated BluetoothGATTCharacteristic characteristics = 3; 1424 + } 1425 + 1426 + message BluetoothGATTGetServicesResponse { 1427 + option (id) = 71; 1428 + option (source) = SOURCE_SERVER; 1429 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1430 + 1431 + uint64 address = 1; 1432 + repeated BluetoothGATTService services = 2; 1433 + } 1434 + 1435 + message BluetoothGATTGetServicesDoneResponse { 1436 + option (id) = 72; 1437 + option (source) = SOURCE_SERVER; 1438 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1439 + 1440 + uint64 address = 1; 1441 + } 1442 + 1443 + message BluetoothGATTReadRequest { 1444 + option (id) = 73; 1445 + option (source) = SOURCE_CLIENT; 1446 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1447 + 1448 + uint64 address = 1; 1449 + uint32 handle = 2; 1450 + } 1451 + 1452 + message BluetoothGATTReadResponse { 1453 + option (id) = 74; 1454 + option (source) = SOURCE_SERVER; 1455 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1456 + 1457 + uint64 address = 1; 1458 + uint32 handle = 2; 1459 + 1460 + bytes data = 3; 1461 + 1462 + } 1463 + 1464 + message BluetoothGATTWriteRequest { 1465 + option (id) = 75; 1466 + option (source) = SOURCE_CLIENT; 1467 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1468 + 1469 + uint64 address = 1; 1470 + uint32 handle = 2; 1471 + bool response = 3; 1472 + 1473 + bytes data = 4; 1474 + } 1475 + 1476 + message BluetoothGATTReadDescriptorRequest { 1477 + option (id) = 76; 1478 + option (source) = SOURCE_CLIENT; 1479 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1480 + 1481 + uint64 address = 1; 1482 + uint32 handle = 2; 1483 + } 1484 + 1485 + message BluetoothGATTWriteDescriptorRequest { 1486 + option (id) = 77; 1487 + option (source) = SOURCE_CLIENT; 1488 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1489 + 1490 + uint64 address = 1; 1491 + uint32 handle = 2; 1492 + 1493 + bytes data = 3; 1494 + } 1495 + 1496 + message BluetoothGATTNotifyRequest { 1497 + option (id) = 78; 1498 + option (source) = SOURCE_CLIENT; 1499 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1500 + 1501 + uint64 address = 1; 1502 + uint32 handle = 2; 1503 + bool enable = 3; 1504 + } 1505 + 1506 + message BluetoothGATTNotifyDataResponse { 1507 + option (id) = 79; 1508 + option (source) = SOURCE_SERVER; 1509 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1510 + 1511 + uint64 address = 1; 1512 + uint32 handle = 2; 1513 + 1514 + bytes data = 3; 1515 + } 1516 + 1517 + message SubscribeBluetoothConnectionsFreeRequest { 1518 + option (id) = 80; 1519 + option (source) = SOURCE_CLIENT; 1520 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1521 + } 1522 + 1523 + message BluetoothConnectionsFreeResponse { 1524 + option (id) = 81; 1525 + option (source) = SOURCE_SERVER; 1526 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1527 + 1528 + uint32 free = 1; 1529 + uint32 limit = 2; 1530 + repeated uint64 allocated = 3; 1531 + } 1532 + 1533 + message BluetoothGATTErrorResponse { 1534 + option (id) = 82; 1535 + option (source) = SOURCE_SERVER; 1536 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1537 + 1538 + uint64 address = 1; 1539 + uint32 handle = 2; 1540 + int32 error = 3; 1541 + } 1542 + 1543 + message BluetoothGATTWriteResponse { 1544 + option (id) = 83; 1545 + option (source) = SOURCE_SERVER; 1546 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1547 + 1548 + uint64 address = 1; 1549 + uint32 handle = 2; 1550 + } 1551 + 1552 + message BluetoothGATTNotifyResponse { 1553 + option (id) = 84; 1554 + option (source) = SOURCE_SERVER; 1555 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1556 + 1557 + uint64 address = 1; 1558 + uint32 handle = 2; 1559 + } 1560 + 1561 + message BluetoothDevicePairingResponse { 1562 + option (id) = 85; 1563 + option (source) = SOURCE_SERVER; 1564 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1565 + 1566 + uint64 address = 1; 1567 + bool paired = 2; 1568 + int32 error = 3; 1569 + } 1570 + 1571 + message BluetoothDeviceUnpairingResponse { 1572 + option (id) = 86; 1573 + option (source) = SOURCE_SERVER; 1574 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1575 + 1576 + uint64 address = 1; 1577 + bool success = 2; 1578 + int32 error = 3; 1579 + } 1580 + 1581 + message UnsubscribeBluetoothLEAdvertisementsRequest { 1582 + option (id) = 87; 1583 + option (source) = SOURCE_CLIENT; 1584 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1585 + } 1586 + 1587 + message BluetoothDeviceClearCacheResponse { 1588 + option (id) = 88; 1589 + option (source) = SOURCE_SERVER; 1590 + option (ifdef) = "USE_BLUETOOTH_PROXY"; 1591 + 1592 + uint64 address = 1; 1593 + bool success = 2; 1594 + int32 error = 3; 1595 + } 1596 + 1597 + enum BluetoothScannerState { 1598 + BLUETOOTH_SCANNER_STATE_IDLE = 0; 1599 + BLUETOOTH_SCANNER_STATE_STARTING = 1; 1600 + BLUETOOTH_SCANNER_STATE_RUNNING = 2; 1601 + BLUETOOTH_SCANNER_STATE_FAILED = 3; 1602 + BLUETOOTH_SCANNER_STATE_STOPPING = 4; 1603 + BLUETOOTH_SCANNER_STATE_STOPPED = 5; 1604 + } 1605 + 1606 + enum BluetoothScannerMode { 1607 + BLUETOOTH_SCANNER_MODE_PASSIVE = 0; 1608 + BLUETOOTH_SCANNER_MODE_ACTIVE = 1; 1609 + } 1610 + 1611 + message BluetoothScannerStateResponse { 1612 + option(id) = 126; 1613 + option(source) = SOURCE_SERVER; 1614 + option(ifdef) = "USE_BLUETOOTH_PROXY"; 1615 + 1616 + BluetoothScannerState state = 1; 1617 + BluetoothScannerMode mode = 2; 1618 + } 1619 + 1620 + message BluetoothScannerSetModeRequest { 1621 + option(id) = 127; 1622 + option(source) = SOURCE_CLIENT; 1623 + option(ifdef) = "USE_BLUETOOTH_PROXY"; 1624 + 1625 + BluetoothScannerMode mode = 1; 1626 + } 1627 + 1628 + // ==================== VOICE ASSISTANT ==================== 1629 + enum VoiceAssistantSubscribeFlag { 1630 + VOICE_ASSISTANT_SUBSCRIBE_NONE = 0; 1631 + VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1; 1632 + } 1633 + 1634 + message SubscribeVoiceAssistantRequest { 1635 + option (id) = 89; 1636 + option (source) = SOURCE_CLIENT; 1637 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1638 + 1639 + bool subscribe = 1; 1640 + uint32 flags = 2; 1641 + } 1642 + 1643 + enum VoiceAssistantRequestFlag { 1644 + VOICE_ASSISTANT_REQUEST_NONE = 0; 1645 + VOICE_ASSISTANT_REQUEST_USE_VAD = 1; 1646 + VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2; 1647 + } 1648 + 1649 + message VoiceAssistantAudioSettings { 1650 + uint32 noise_suppression_level = 1; 1651 + uint32 auto_gain = 2; 1652 + float volume_multiplier = 3; 1653 + } 1654 + 1655 + message VoiceAssistantRequest { 1656 + option (id) = 90; 1657 + option (source) = SOURCE_SERVER; 1658 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1659 + 1660 + bool start = 1; 1661 + string conversation_id = 2; 1662 + uint32 flags = 3; 1663 + VoiceAssistantAudioSettings audio_settings = 4; 1664 + string wake_word_phrase = 5; 1665 + } 1666 + 1667 + message VoiceAssistantResponse { 1668 + option (id) = 91; 1669 + option (source) = SOURCE_CLIENT; 1670 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1671 + 1672 + uint32 port = 1; 1673 + bool error = 2; 1674 + } 1675 + 1676 + enum VoiceAssistantEvent { 1677 + VOICE_ASSISTANT_ERROR = 0; 1678 + VOICE_ASSISTANT_RUN_START = 1; 1679 + VOICE_ASSISTANT_RUN_END = 2; 1680 + VOICE_ASSISTANT_STT_START = 3; 1681 + VOICE_ASSISTANT_STT_END = 4; 1682 + VOICE_ASSISTANT_INTENT_START = 5; 1683 + VOICE_ASSISTANT_INTENT_END = 6; 1684 + VOICE_ASSISTANT_TTS_START = 7; 1685 + VOICE_ASSISTANT_TTS_END = 8; 1686 + VOICE_ASSISTANT_WAKE_WORD_START = 9; 1687 + VOICE_ASSISTANT_WAKE_WORD_END = 10; 1688 + VOICE_ASSISTANT_STT_VAD_START = 11; 1689 + VOICE_ASSISTANT_STT_VAD_END = 12; 1690 + VOICE_ASSISTANT_TTS_STREAM_START = 98; 1691 + VOICE_ASSISTANT_TTS_STREAM_END = 99; 1692 + VOICE_ASSISTANT_INTENT_PROGRESS = 100; 1693 + } 1694 + 1695 + message VoiceAssistantEventData { 1696 + string name = 1; 1697 + string value = 2; 1698 + } 1699 + 1700 + message VoiceAssistantEventResponse { 1701 + option (id) = 92; 1702 + option (source) = SOURCE_CLIENT; 1703 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1704 + 1705 + VoiceAssistantEvent event_type = 1; 1706 + repeated VoiceAssistantEventData data = 2; 1707 + } 1708 + 1709 + message VoiceAssistantAudio { 1710 + option (id) = 106; 1711 + option (source) = SOURCE_BOTH; 1712 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1713 + 1714 + bytes data = 1; 1715 + bool end = 2; 1716 + } 1717 + 1718 + enum VoiceAssistantTimerEvent { 1719 + VOICE_ASSISTANT_TIMER_STARTED = 0; 1720 + VOICE_ASSISTANT_TIMER_UPDATED = 1; 1721 + VOICE_ASSISTANT_TIMER_CANCELLED = 2; 1722 + VOICE_ASSISTANT_TIMER_FINISHED = 3; 1723 + } 1724 + 1725 + message VoiceAssistantTimerEventResponse { 1726 + option (id) = 115; 1727 + option (source) = SOURCE_CLIENT; 1728 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1729 + 1730 + VoiceAssistantTimerEvent event_type = 1; 1731 + string timer_id = 2; 1732 + string name = 3; 1733 + uint32 total_seconds = 4; 1734 + uint32 seconds_left = 5; 1735 + bool is_active = 6; 1736 + } 1737 + 1738 + message VoiceAssistantAnnounceRequest { 1739 + option (id) = 119; 1740 + option (source) = SOURCE_CLIENT; 1741 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1742 + 1743 + string media_id = 1; 1744 + string text = 2; 1745 + string preannounce_media_id = 3; 1746 + bool start_conversation = 4; 1747 + } 1748 + 1749 + message VoiceAssistantAnnounceFinished { 1750 + option (id) = 120; 1751 + option (source) = SOURCE_SERVER; 1752 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1753 + 1754 + bool success = 1; 1755 + } 1756 + 1757 + message VoiceAssistantWakeWord { 1758 + string id = 1; 1759 + string wake_word = 2; 1760 + repeated string trained_languages = 3; 1761 + } 1762 + 1763 + message VoiceAssistantConfigurationRequest { 1764 + option (id) = 121; 1765 + option (source) = SOURCE_CLIENT; 1766 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1767 + } 1768 + 1769 + message VoiceAssistantConfigurationResponse { 1770 + option (id) = 122; 1771 + option (source) = SOURCE_SERVER; 1772 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1773 + 1774 + repeated VoiceAssistantWakeWord available_wake_words = 1; 1775 + repeated string active_wake_words = 2; 1776 + uint32 max_active_wake_words = 3; 1777 + } 1778 + 1779 + message VoiceAssistantSetConfiguration { 1780 + option (id) = 123; 1781 + option (source) = SOURCE_CLIENT; 1782 + option (ifdef) = "USE_VOICE_ASSISTANT"; 1783 + 1784 + repeated string active_wake_words = 1; 1785 + } 1786 + 1787 + // ==================== ALARM CONTROL PANEL ==================== 1788 + enum AlarmControlPanelState { 1789 + ALARM_STATE_DISARMED = 0; 1790 + ALARM_STATE_ARMED_HOME = 1; 1791 + ALARM_STATE_ARMED_AWAY = 2; 1792 + ALARM_STATE_ARMED_NIGHT = 3; 1793 + ALARM_STATE_ARMED_VACATION = 4; 1794 + ALARM_STATE_ARMED_CUSTOM_BYPASS = 5; 1795 + ALARM_STATE_PENDING = 6; 1796 + ALARM_STATE_ARMING = 7; 1797 + ALARM_STATE_DISARMING = 8; 1798 + ALARM_STATE_TRIGGERED = 9; 1799 + } 1800 + 1801 + enum AlarmControlPanelStateCommand { 1802 + ALARM_CONTROL_PANEL_DISARM = 0; 1803 + ALARM_CONTROL_PANEL_ARM_AWAY = 1; 1804 + ALARM_CONTROL_PANEL_ARM_HOME = 2; 1805 + ALARM_CONTROL_PANEL_ARM_NIGHT = 3; 1806 + ALARM_CONTROL_PANEL_ARM_VACATION = 4; 1807 + ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5; 1808 + ALARM_CONTROL_PANEL_TRIGGER = 6; 1809 + } 1810 + 1811 + message ListEntitiesAlarmControlPanelResponse { 1812 + option (id) = 94; 1813 + option (base_class) = "InfoResponseProtoMessage"; 1814 + option (source) = SOURCE_SERVER; 1815 + option (ifdef) = "USE_ALARM_CONTROL_PANEL"; 1816 + 1817 + string object_id = 1; 1818 + fixed32 key = 2; 1819 + string name = 3; 1820 + string unique_id = 4; 1821 + string icon = 5; 1822 + bool disabled_by_default = 6; 1823 + EntityCategory entity_category = 7; 1824 + uint32 supported_features = 8; 1825 + bool requires_code = 9; 1826 + bool requires_code_to_arm = 10; 1827 + uint32 device_id = 11; 1828 + } 1829 + 1830 + message AlarmControlPanelStateResponse { 1831 + option (id) = 95; 1832 + option (base_class) = "StateResponseProtoMessage"; 1833 + option (source) = SOURCE_SERVER; 1834 + option (ifdef) = "USE_ALARM_CONTROL_PANEL"; 1835 + option (no_delay) = true; 1836 + fixed32 key = 1; 1837 + AlarmControlPanelState state = 2; 1838 + uint32 device_id = 3; 1839 + } 1840 + 1841 + message AlarmControlPanelCommandRequest { 1842 + option (id) = 96; 1843 + option (source) = SOURCE_CLIENT; 1844 + option (ifdef) = "USE_ALARM_CONTROL_PANEL"; 1845 + option (no_delay) = true; 1846 + fixed32 key = 1; 1847 + AlarmControlPanelStateCommand command = 2; 1848 + string code = 3; 1849 + } 1850 + 1851 + // ===================== TEXT ===================== 1852 + enum TextMode { 1853 + TEXT_MODE_TEXT = 0; 1854 + TEXT_MODE_PASSWORD = 1; 1855 + } 1856 + message ListEntitiesTextResponse { 1857 + option (id) = 97; 1858 + option (base_class) = "InfoResponseProtoMessage"; 1859 + option (source) = SOURCE_SERVER; 1860 + option (ifdef) = "USE_TEXT"; 1861 + 1862 + string object_id = 1; 1863 + fixed32 key = 2; 1864 + string name = 3; 1865 + string unique_id = 4; 1866 + string icon = 5; 1867 + bool disabled_by_default = 6; 1868 + EntityCategory entity_category = 7; 1869 + 1870 + uint32 min_length = 8; 1871 + uint32 max_length = 9; 1872 + string pattern = 10; 1873 + TextMode mode = 11; 1874 + uint32 device_id = 12; 1875 + } 1876 + message TextStateResponse { 1877 + option (id) = 98; 1878 + option (base_class) = "StateResponseProtoMessage"; 1879 + option (source) = SOURCE_SERVER; 1880 + option (ifdef) = "USE_TEXT"; 1881 + option (no_delay) = true; 1882 + 1883 + fixed32 key = 1; 1884 + string state = 2; 1885 + // If the Text does not have a valid state yet. 1886 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 1887 + bool missing_state = 3; 1888 + uint32 device_id = 4; 1889 + } 1890 + message TextCommandRequest { 1891 + option (id) = 99; 1892 + option (source) = SOURCE_CLIENT; 1893 + option (ifdef) = "USE_TEXT"; 1894 + option (no_delay) = true; 1895 + 1896 + fixed32 key = 1; 1897 + string state = 2; 1898 + } 1899 + 1900 + 1901 + // ==================== DATETIME DATE ==================== 1902 + message ListEntitiesDateResponse { 1903 + option (id) = 100; 1904 + option (base_class) = "InfoResponseProtoMessage"; 1905 + option (source) = SOURCE_SERVER; 1906 + option (ifdef) = "USE_DATETIME_DATE"; 1907 + 1908 + string object_id = 1; 1909 + fixed32 key = 2; 1910 + string name = 3; 1911 + string unique_id = 4; 1912 + 1913 + string icon = 5; 1914 + bool disabled_by_default = 6; 1915 + EntityCategory entity_category = 7; 1916 + uint32 device_id = 8; 1917 + } 1918 + message DateStateResponse { 1919 + option (id) = 101; 1920 + option (base_class) = "StateResponseProtoMessage"; 1921 + option (source) = SOURCE_SERVER; 1922 + option (ifdef) = "USE_DATETIME_DATE"; 1923 + option (no_delay) = true; 1924 + 1925 + fixed32 key = 1; 1926 + // If the date does not have a valid state yet. 1927 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 1928 + bool missing_state = 2; 1929 + uint32 year = 3; 1930 + uint32 month = 4; 1931 + uint32 day = 5; 1932 + uint32 device_id = 6; 1933 + } 1934 + message DateCommandRequest { 1935 + option (id) = 102; 1936 + option (source) = SOURCE_CLIENT; 1937 + option (ifdef) = "USE_DATETIME_DATE"; 1938 + option (no_delay) = true; 1939 + 1940 + fixed32 key = 1; 1941 + uint32 year = 2; 1942 + uint32 month = 3; 1943 + uint32 day = 4; 1944 + } 1945 + 1946 + // ==================== DATETIME TIME ==================== 1947 + message ListEntitiesTimeResponse { 1948 + option (id) = 103; 1949 + option (base_class) = "InfoResponseProtoMessage"; 1950 + option (source) = SOURCE_SERVER; 1951 + option (ifdef) = "USE_DATETIME_TIME"; 1952 + 1953 + string object_id = 1; 1954 + fixed32 key = 2; 1955 + string name = 3; 1956 + string unique_id = 4; 1957 + 1958 + string icon = 5; 1959 + bool disabled_by_default = 6; 1960 + EntityCategory entity_category = 7; 1961 + uint32 device_id = 8; 1962 + } 1963 + message TimeStateResponse { 1964 + option (id) = 104; 1965 + option (base_class) = "StateResponseProtoMessage"; 1966 + option (source) = SOURCE_SERVER; 1967 + option (ifdef) = "USE_DATETIME_TIME"; 1968 + option (no_delay) = true; 1969 + 1970 + fixed32 key = 1; 1971 + // If the time does not have a valid state yet. 1972 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 1973 + bool missing_state = 2; 1974 + uint32 hour = 3; 1975 + uint32 minute = 4; 1976 + uint32 second = 5; 1977 + uint32 device_id = 6; 1978 + } 1979 + message TimeCommandRequest { 1980 + option (id) = 105; 1981 + option (source) = SOURCE_CLIENT; 1982 + option (ifdef) = "USE_DATETIME_TIME"; 1983 + option (no_delay) = true; 1984 + 1985 + fixed32 key = 1; 1986 + uint32 hour = 2; 1987 + uint32 minute = 3; 1988 + uint32 second = 4; 1989 + } 1990 + 1991 + // ==================== EVENT ==================== 1992 + message ListEntitiesEventResponse { 1993 + option (id) = 107; 1994 + option (base_class) = "InfoResponseProtoMessage"; 1995 + option (source) = SOURCE_SERVER; 1996 + option (ifdef) = "USE_EVENT"; 1997 + 1998 + string object_id = 1; 1999 + fixed32 key = 2; 2000 + string name = 3; 2001 + string unique_id = 4; 2002 + 2003 + string icon = 5; 2004 + bool disabled_by_default = 6; 2005 + EntityCategory entity_category = 7; 2006 + string device_class = 8; 2007 + 2008 + repeated string event_types = 9; 2009 + uint32 device_id = 10; 2010 + } 2011 + message EventResponse { 2012 + option (id) = 108; 2013 + option (base_class) = "StateResponseProtoMessage"; 2014 + option (source) = SOURCE_SERVER; 2015 + option (ifdef) = "USE_EVENT"; 2016 + 2017 + fixed32 key = 1; 2018 + string event_type = 2; 2019 + uint32 device_id = 3; 2020 + } 2021 + 2022 + // ==================== VALVE ==================== 2023 + message ListEntitiesValveResponse { 2024 + option (id) = 109; 2025 + option (base_class) = "InfoResponseProtoMessage"; 2026 + option (source) = SOURCE_SERVER; 2027 + option (ifdef) = "USE_VALVE"; 2028 + 2029 + string object_id = 1; 2030 + fixed32 key = 2; 2031 + string name = 3; 2032 + string unique_id = 4; 2033 + 2034 + string icon = 5; 2035 + bool disabled_by_default = 6; 2036 + EntityCategory entity_category = 7; 2037 + string device_class = 8; 2038 + 2039 + bool assumed_state = 9; 2040 + bool supports_position = 10; 2041 + bool supports_stop = 11; 2042 + uint32 device_id = 12; 2043 + } 2044 + 2045 + enum ValveOperation { 2046 + VALVE_OPERATION_IDLE = 0; 2047 + VALVE_OPERATION_IS_OPENING = 1; 2048 + VALVE_OPERATION_IS_CLOSING = 2; 2049 + } 2050 + message ValveStateResponse { 2051 + option (id) = 110; 2052 + option (base_class) = "StateResponseProtoMessage"; 2053 + option (source) = SOURCE_SERVER; 2054 + option (ifdef) = "USE_VALVE"; 2055 + option (no_delay) = true; 2056 + 2057 + fixed32 key = 1; 2058 + float position = 2; 2059 + ValveOperation current_operation = 3; 2060 + uint32 device_id = 4; 2061 + } 2062 + 2063 + message ValveCommandRequest { 2064 + option (id) = 111; 2065 + option (source) = SOURCE_CLIENT; 2066 + option (ifdef) = "USE_VALVE"; 2067 + option (no_delay) = true; 2068 + 2069 + fixed32 key = 1; 2070 + bool has_position = 2; 2071 + float position = 3; 2072 + bool stop = 4; 2073 + } 2074 + 2075 + // ==================== DATETIME DATETIME ==================== 2076 + message ListEntitiesDateTimeResponse { 2077 + option (id) = 112; 2078 + option (base_class) = "InfoResponseProtoMessage"; 2079 + option (source) = SOURCE_SERVER; 2080 + option (ifdef) = "USE_DATETIME_DATETIME"; 2081 + 2082 + string object_id = 1; 2083 + fixed32 key = 2; 2084 + string name = 3; 2085 + string unique_id = 4; 2086 + 2087 + string icon = 5; 2088 + bool disabled_by_default = 6; 2089 + EntityCategory entity_category = 7; 2090 + uint32 device_id = 8; 2091 + } 2092 + message DateTimeStateResponse { 2093 + option (id) = 113; 2094 + option (base_class) = "StateResponseProtoMessage"; 2095 + option (source) = SOURCE_SERVER; 2096 + option (ifdef) = "USE_DATETIME_DATETIME"; 2097 + option (no_delay) = true; 2098 + 2099 + fixed32 key = 1; 2100 + // If the datetime does not have a valid state yet. 2101 + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller 2102 + bool missing_state = 2; 2103 + fixed32 epoch_seconds = 3; 2104 + uint32 device_id = 4; 2105 + } 2106 + message DateTimeCommandRequest { 2107 + option (id) = 114; 2108 + option (source) = SOURCE_CLIENT; 2109 + option (ifdef) = "USE_DATETIME_DATETIME"; 2110 + option (no_delay) = true; 2111 + 2112 + fixed32 key = 1; 2113 + fixed32 epoch_seconds = 2; 2114 + } 2115 + 2116 + // ==================== UPDATE ==================== 2117 + message ListEntitiesUpdateResponse { 2118 + option (id) = 116; 2119 + option (base_class) = "InfoResponseProtoMessage"; 2120 + option (source) = SOURCE_SERVER; 2121 + option (ifdef) = "USE_UPDATE"; 2122 + 2123 + string object_id = 1; 2124 + fixed32 key = 2; 2125 + string name = 3; 2126 + string unique_id = 4; 2127 + 2128 + string icon = 5; 2129 + bool disabled_by_default = 6; 2130 + EntityCategory entity_category = 7; 2131 + string device_class = 8; 2132 + uint32 device_id = 9; 2133 + } 2134 + message UpdateStateResponse { 2135 + option (id) = 117; 2136 + option (base_class) = "StateResponseProtoMessage"; 2137 + option (source) = SOURCE_SERVER; 2138 + option (ifdef) = "USE_UPDATE"; 2139 + option (no_delay) = true; 2140 + 2141 + fixed32 key = 1; 2142 + bool missing_state = 2; 2143 + bool in_progress = 3; 2144 + bool has_progress = 4; 2145 + float progress = 5; 2146 + string current_version = 6; 2147 + string latest_version = 7; 2148 + string title = 8; 2149 + string release_summary = 9; 2150 + string release_url = 10; 2151 + uint32 device_id = 11; 2152 + } 2153 + enum UpdateCommand { 2154 + UPDATE_COMMAND_NONE = 0; 2155 + UPDATE_COMMAND_UPDATE = 1; 2156 + UPDATE_COMMAND_CHECK = 2; 2157 + } 2158 + message UpdateCommandRequest { 2159 + option (id) = 118; 2160 + option (source) = SOURCE_CLIENT; 2161 + option (ifdef) = "USE_UPDATE"; 2162 + option (no_delay) = true; 2163 + 2164 + fixed32 key = 1; 2165 + UpdateCommand command = 2; 2166 + }
+25
sachy-esphome/protos/api_options.proto
··· 1 + syntax = "proto2"; 2 + 3 + import "google/protobuf/descriptor.proto"; 4 + 5 + enum APISourceType { 6 + SOURCE_BOTH = 0; 7 + SOURCE_SERVER = 1; 8 + SOURCE_CLIENT = 2; 9 + } 10 + 11 + message void {} 12 + 13 + extend google.protobuf.MethodOptions { 14 + optional bool needs_setup_connection = 1038 [default=true]; 15 + optional bool needs_authentication = 1039 [default=true]; 16 + } 17 + 18 + extend google.protobuf.MessageOptions { 19 + optional uint32 id = 1036 [default=0]; 20 + optional APISourceType source = 1037 [default=SOURCE_BOTH]; 21 + optional string ifdef = 1038; 22 + optional bool log = 1039 [default=true]; 23 + optional bool no_delay = 1040 [default=false]; 24 + optional string base_class = 1041; 25 + }
+166
sachy-esphome/src/lib.rs
··· 1 + #![no_std] 2 + 3 + use core::ops::{Index, RangeTo}; 4 + 5 + use num_traits::FromPrimitive; 6 + pub use prost; 7 + use prost::{bytes::Buf, bytes::BufMut}; 8 + use thiserror::Error; 9 + 10 + #[allow(deprecated)] 11 + pub mod api { 12 + use prost::Message as ProstMessage; 13 + include!(concat!(env!("OUT_DIR"), "/api.rs")); 14 + } 15 + 16 + pub use api::MessageKind; 17 + 18 + use crate::api::Message; 19 + 20 + #[derive(Error, Debug)] 21 + pub enum EspHomeError { 22 + #[error("First byte of message should be zero")] 23 + InvalidStartByte, 24 + 25 + #[error("Buffer is too small")] 26 + BufferTooSmall, 27 + 28 + #[error("Unable to decode protobuf")] 29 + Decode(prost::DecodeError), 30 + 31 + #[error("Unable to encode protobuf")] 32 + Encode(prost::EncodeError), 33 + 34 + #[error("Unknown message kind: {0}")] 35 + UnknownMessageKind(u64), 36 + } 37 + 38 + #[cfg(feature = "defmt")] 39 + impl defmt::Format for EspHomeError { 40 + fn format(&self, fmt: defmt::Formatter) { 41 + match self { 42 + EspHomeError::InvalidStartByte => defmt::write!(fmt, "InvalidStartByte"), 43 + EspHomeError::BufferTooSmall => defmt::write!(fmt, "BufferTooSmal"), 44 + EspHomeError::Decode(_) => defmt::write!(fmt, "DecodeError"), 45 + EspHomeError::Encode(_) => defmt::write!(fmt, "EncodeError"), 46 + EspHomeError::UnknownMessageKind(msg) => { 47 + defmt::write!(fmt, "UnknownMessageKind({})", msg) 48 + } 49 + } 50 + } 51 + } 52 + 53 + impl From<prost::DecodeError> for EspHomeError { 54 + #[inline] 55 + fn from(value: prost::DecodeError) -> Self { 56 + Self::Decode(value) 57 + } 58 + } 59 + 60 + impl From<prost::EncodeError> for EspHomeError { 61 + #[inline] 62 + fn from(value: prost::EncodeError) -> Self { 63 + Self::Encode(value) 64 + } 65 + } 66 + 67 + pub type EspHomeResult<T> = core::result::Result<T, EspHomeError>; 68 + 69 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 70 + struct EspHomeHeader { 71 + _type: api::MessageKind, 72 + size: u64, 73 + } 74 + 75 + impl EspHomeHeader { 76 + pub fn encode_header(&self, buffer: &mut impl BufMut) -> EspHomeResult<()> { 77 + buffer.put_u8(0); 78 + 79 + try_encode_esp_varint(self.size, buffer)?; 80 + try_encode_esp_varint(self._type as u64, buffer) 81 + } 82 + 83 + pub fn decode(buffer: &mut &[u8]) -> EspHomeResult<Self> { 84 + if buffer.is_empty() { 85 + return Err(EspHomeError::BufferTooSmall); 86 + } 87 + 88 + if buffer.get_u8() != 0 { 89 + return Err(EspHomeError::InvalidStartByte); 90 + } 91 + 92 + let size = prost::encoding::decode_varint(buffer)?; 93 + let kind = prost::encoding::decode_varint(buffer)?; 94 + 95 + Ok(EspHomeHeader { 96 + _type: api::MessageKind::from_u64(kind) 97 + .ok_or(EspHomeError::UnknownMessageKind(kind))?, 98 + size, 99 + }) 100 + } 101 + } 102 + 103 + pub fn decode_esp_request(buffer: &mut &[u8]) -> EspHomeResult<api::Message> { 104 + let header = EspHomeHeader::decode(buffer)?; 105 + 106 + if header.size as usize > buffer.len() { 107 + return Err(EspHomeError::BufferTooSmall); 108 + } 109 + 110 + let body = api::Message::decode(header._type, &mut &buffer[..header.size as usize])?; 111 + 112 + buffer.advance(header.size as usize); 113 + 114 + Ok(body) 115 + } 116 + 117 + pub struct EspEncoder<'any, B> { 118 + buffer: &'any mut B, 119 + written: usize, 120 + } 121 + 122 + impl<'any, B> EspEncoder<'any, B> 123 + where 124 + B: BufMut + Index<RangeTo<usize>, Output = [u8]>, 125 + { 126 + pub fn new(buffer: &'any mut B) -> Self { 127 + Self { buffer, written: 0 } 128 + } 129 + 130 + pub fn encode(mut self, body: Message) -> EspHomeResult<Self> { 131 + let remaining = self.buffer.remaining_mut(); 132 + 133 + let header = EspHomeHeader { 134 + _type: MessageKind::from(&body), 135 + size: body.encoded_len() as u64, 136 + }; 137 + 138 + header.encode_header(self.buffer)?; 139 + 140 + if self.buffer.remaining_mut() < header.size as usize { 141 + return Err(EspHomeError::BufferTooSmall); 142 + } 143 + 144 + body.encode(self.buffer)?; 145 + 146 + self.written += remaining - self.buffer.remaining_mut(); 147 + 148 + Ok(self) 149 + } 150 + 151 + pub fn finish(self) -> &'any [u8] { 152 + &self.buffer[..self.written] 153 + } 154 + } 155 + 156 + fn try_encode_esp_varint(value: u64, buf: &mut impl BufMut) -> EspHomeResult<()> { 157 + let len = prost::encoding::encoded_len_varint(value); 158 + 159 + if buf.remaining_mut() < len { 160 + return Err(EspHomeError::BufferTooSmall); 161 + } 162 + 163 + prost::encoding::encode_varint(value, buf); 164 + 165 + Ok(()) 166 + }
+66
sachy-esphome/templates/message.handlebars
··· 1 + #[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Copy, Clone)] 2 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 3 + #[repr(u64)] 4 + pub enum MessageKind { 5 + {{#each this}} 6 + {{ name }} = {{ id }}, 7 + {{/each}} 8 + } 9 + 10 + #[derive(Debug, Clone)] 11 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 12 + pub enum Message { 13 + {{#each this}} 14 + {{ name }}({{ name }}), 15 + {{/each}} 16 + } 17 + 18 + impl Message { 19 + pub fn decode(message_kind: MessageKind, buffer: &mut &[u8]) -> Result<Self, prost::DecodeError> { 20 + match message_kind { 21 + {{#each this}} 22 + MessageKind::{{ name }} => Ok(Self::{{ name }}({{name}}::decode(buffer)?)), 23 + {{/each}} 24 + } 25 + } 26 + 27 + pub fn encoded_len(&self) -> usize { 28 + match self { 29 + {{#each this}} 30 + Self::{{ name }}(message) => message.encoded_len(), 31 + {{/each}} 32 + } 33 + } 34 + 35 + pub fn encode(&self, buf: &mut impl prost::bytes::BufMut) -> Result<(), prost::EncodeError> { 36 + match self { 37 + {{#each this}} 38 + Self::{{ name }}(message) => message.encode(buf), 39 + {{/each}} 40 + } 41 + } 42 + } 43 + 44 + impl From<&Message> for MessageKind { 45 + fn from(value: &Message) -> Self { 46 + match value { 47 + {{#each this}} 48 + Message::{{ name }}(message) => Self::from(message), 49 + {{/each}} 50 + } 51 + } 52 + } 53 + 54 + {{#each this}} 55 + impl From<&{{ name }}> for MessageKind { 56 + fn from(_value: &{{name}}) -> Self { 57 + Self::{{name}} 58 + } 59 + } 60 + 61 + impl From<{{ name }}> for Message { 62 + fn from(value: {{ name }}) -> Self { 63 + Message::{{ name }}(value) 64 + } 65 + } 66 + {{/each}}