From 50a2d16d9d6dc5ef6122b4881c956e4fd60eaccf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 7 Jun 2026 08:13:17 -0400 Subject: [PATCH] sandbox: add the generic EntityQuery request/response RPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fire-and-forget call_service channel can command an entity but can't ask it a server-side question that has no SupportsResponse service to ride. Add one generic EntityQuery RPC for those, mirroring the call_service path end to end (proto -> codec registry -> bridge sender + error translation -> sandbox handler -> proxy helper): - proto: EntityQuery {sandbox_entity_id, method, args, context_id} and EntityQueryResult {result} (the return wrapped as {"value": ...} so scalar/list/None are all representable). Gencode regenerated into both _pb2 mirrors; drift guard passes. - MSG_ENTITY_QUERY constant + REGISTRY entry added to both protocol/messages mirrors. - SandboxBridge.async_entity_query builds the request, remembers the context before the id is reduced to a wire value, translates remote/closed errors through the existing paths, and unwraps {"value": ...}. - EntryRunner._handle_entity_query resolves the entity on the private hass, invokes the named method with the decoded kwargs, and serialises the return through the as_dict-aware JSON encoder; raised HA/voluptuous errors propagate as channel error frames so main rebuilds the same shape. - SandboxProxyEntity._entity_query is the proxy-side companion to _call_service. No proxy op is wired onto it yet — that is the next phase. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/sandbox/_proto/sandbox_pb2.py | 130 +++++++++--------- .../components/sandbox/_proto/sandbox_pb2.pyi | 18 +++ homeassistant/components/sandbox/bridge.py | 38 +++++ .../components/sandbox/entity/__init__.py | 17 +++ homeassistant/components/sandbox/messages.py | 1 + homeassistant/components/sandbox/protocol.py | 9 ++ .../hass_client/_proto/sandbox_pb2.py | 130 +++++++++--------- .../hass_client/_proto/sandbox_pb2.pyi | 18 +++ .../hass_client/hass_client/entry_runner.py | 46 ++++++- sandbox/hass_client/hass_client/messages.py | 1 + sandbox/hass_client/hass_client/protocol.py | 2 + sandbox/proto/sandbox.proto | 21 +++ 12 files changed, 304 insertions(+), 127 deletions(-) diff --git a/homeassistant/components/sandbox/_proto/sandbox_pb2.py b/homeassistant/components/sandbox/_proto/sandbox_pb2.py index 5047c3c31667..e93de4f2b5c5 100644 --- a/homeassistant/components/sandbox/_proto/sandbox_pb2.py +++ b/homeassistant/components/sandbox/_proto/sandbox_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rsandbox.proto\x12\x07sandbox\x1a\x1cgoogle/protobuf/struct.proto\"c\n\x05\x46rame\x12\n\n\x02id\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x11\n\x07request\x18\x03 \x01(\x0cH\x00\x12%\n\x08response\x18\x04 \x01(\x0b\x32\x11.sandbox.ResponseH\x00\x42\x06\n\x04\x62ody\"E\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0e\n\x06result\x18\x02 \x01(\x0c\x12\x1d\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x0e.sandbox.Error\"`\n\x05\x45rror\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12&\n\x07invalid\x18\x03 \x03(\x0b\x32\x15.sandbox.InvalidError\x12\x10\n\x08multiple\x18\x04 \x01(\x08\"-\n\x0cInvalidError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\"(\n\nDevicePair\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xcc\x03\n\nDeviceInfo\x12(\n\x0bidentifiers\x18\x01 \x03(\x0b\x32\x13.sandbox.DevicePair\x12(\n\x0b\x63onnections\x18\x02 \x03(\x0b\x32\x13.sandbox.DevicePair\x12,\n\nvia_device\x18\x03 \x01(\x0b\x32\x13.sandbox.DevicePairH\x00\x88\x01\x01\x12\x12\n\nentry_type\x18\x04 \x01(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x14\n\x0cmanufacturer\x18\x06 \x01(\t\x12\r\n\x05model\x18\x07 \x01(\t\x12\x10\n\x08model_id\x18\x08 \x01(\t\x12\x12\n\nsw_version\x18\t \x01(\t\x12\x12\n\nhw_version\x18\n \x01(\t\x12\x15\n\rserial_number\x18\x0b \x01(\t\x12\x16\n\x0esuggested_area\x18\x0c \x01(\t\x12\x19\n\x11\x63onfiguration_url\x18\r \x01(\t\x12\x14\n\x0c\x64\x65\x66\x61ult_name\x18\x0e \x01(\t\x12\x1c\n\x14\x64\x65\x66\x61ult_manufacturer\x18\x0f \x01(\t\x12\x15\n\rdefault_model\x18\x10 \x01(\t\x12\x17\n\x0ftranslation_key\x18\x11 \x01(\tB\r\n\x0b_via_device\"h\n\x11IntegrationSource\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0b\n\x03ref\x18\x03 \x01(\t\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x0e\n\x06\x64omain\x18\x05 \x01(\t\x12\x0e\n\x06subdir\x18\x06 \x01(\t\"\xa4\x02\n\nEntrySetup\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12%\n\x04\x64\x61ta\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x16\n\tunique_id\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07version\x18\x08 \x01(\x05\x12\x15\n\rminor_version\x18\t \x01(\x05\x12\x36\n\x12integration_source\x18\n \x01(\x0b\x32\x1a.sandbox.IntegrationSourceB\x0c\n\n_unique_id\">\n\x10\x45ntrySetupResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x1f\n\x0b\x45ntryUnload\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\"\x1f\n\x11\x45ntryUnloadResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\xc7\x01\n\x0b\x43\x61llService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\'\n\x06target\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\x0cservice_data\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0freturn_response\x18\x06 \x01(\x08\x42\r\n\x0b_context_id\"8\n\x0fServiceResponse\x12%\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"Q\n\x11\x43\x61llServiceResult\x12/\n\x08response\x18\x01 \x01(\x0b\x32\x18.sandbox.ServiceResponseH\x00\x88\x01\x01\x42\x0b\n\t_response\"4\n\x0fGetTranslations\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x0f\n\x07\x64omains\x18\x02 \x03(\t\"S\n\x15GetTranslationsResult\x12\x10\n\x08language\x18\x01 \x01(\t\x12(\n\x07strings\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\n\n\x08Shutdown\"u\n\x0eShutdownResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x10\n\x08unloaded\x18\x02 \x01(\x05\x12\x33\n\rrestore_state\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x10\n\x0e_restore_state\"\x06\n\x04Ping\"\x1a\n\nPingResult\x12\x0c\n\x04pong\x18\x01 \x01(\t\"\x07\n\x05Ready\"l\n\x08\x46lowInit\x12\x0f\n\x07handler\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\"H\n\x08\x46lowStep\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\x12+\n\nuser_input\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1c\n\tFlowAbort\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\"\x11\n\x0f\x46lowAbortResult\"\xa9\x05\n\nFlowResult\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x14\n\x07\x66low_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07handler\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07step_id\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06reason\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x12\n\x05title\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tlast_step\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07preview\x18\t \x01(\tH\x07\x88\x01\x01\x12\x14\n\x07version\x18\n \x01(\x05H\x08\x88\x01\x01\x12\x1a\n\rminor_version\x18\x0b \x01(\x05H\t\x88\x01\x01\x12%\n\x04\x64\x61ta\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\r \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65rrors\x18\x0e \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x39\n\x18\x64\x65scription_placeholders\x18\x0f \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07\x63ontext\x18\x10 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0b\x64\x61ta_schema\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12\x17\n\x0fhas_data_schema\x18\x12 \x01(\x08\x42\n\n\x08_flow_idB\n\n\x08_handlerB\n\n\x08_step_idB\t\n\x07_reasonB\x08\n\x06_titleB\x0e\n\x0c_descriptionB\x0c\n\n_last_stepB\n\n\x08_previewB\n\n\x08_versionB\x10\n\x0e_minor_version\"\x8a\x03\n\nEntityInfo\x12\x39\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1f.sandbox.EntityInfo.DescriptionH\x00\x88\x01\x01\x12-\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x13.sandbox.DeviceInfoH\x01\x88\x01\x01\x1a\xf1\x01\n\x0b\x44\x65scription\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04icon\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x65ntity_category\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65vice_class\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1a\n\x12supported_features\x18\x05 \x01(\x05\x12\x1c\n\x0ftranslation_key\x18\x06 \x01(\tH\x04\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_iconB\x12\n\x10_entity_categoryB\x0f\n\r_device_classB\x12\n\x10_translation_keyB\x0e\n\x0c_descriptionB\x0e\n\x0c_device_info\"\x88\x01\n\x0cInitialState\x12\x12\n\x05state\x18\x01 \x01(\tH\x00\x88\x01\x01\x12-\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructB\x08\n\x06_state\"\xda\x01\n\x11\x45ntityDescription\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\x19\n\x11sandbox_entity_id\x18\x03 \x01(\t\x12\x16\n\tunique_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0fhas_entity_name\x18\x05 \x01(\x08\x12!\n\x04info\x18\x06 \x01(\x0b\x32\x13.sandbox.EntityInfo\x12&\n\x07initial\x18\x07 \x01(\x0b\x32\x15.sandbox.InitialStateB\x0c\n\n_unique_id\")\n\x14RegisterEntityResult\x12\x11\n\tentity_id\x18\x01 \x01(\t\"-\n\x10UnregisterEntity\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\"$\n\x16UnregisterEntityResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x9c\x01\n\x0cStateChanged\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x12\n\x05state\x18\x02 \x01(\tH\x00\x88\x01\x01\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_stateB\r\n\x0b_context_id\"y\n\x0fRegisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x19\n\x11supports_response\x18\x03 \x01(\t\x12*\n\x06schema\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.ListValue\"6\n\x15RegisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x11\n\tinstalled\x18\x02 \x01(\x08\"4\n\x11UnregisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\"6\n\x17UnregisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07removed\x18\x02 \x01(\x08\"t\n\tFireEvent\x12\x12\n\nevent_type\x18\x01 \x01(\t\x12+\n\nevent_data\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"\x18\n\tStoreLoad\x12\x0b\n\x03key\x18\x01 \x01(\t\"F\n\x0fStoreLoadResult\x12*\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x07\n\x05_data\"?\n\tStoreSave\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1d\n\x0fStoreSaveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x1a\n\x0bStoreRemove\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1f\n\x11StoreRemoveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rsandbox.proto\x12\x07sandbox\x1a\x1cgoogle/protobuf/struct.proto\"c\n\x05\x46rame\x12\n\n\x02id\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x11\n\x07request\x18\x03 \x01(\x0cH\x00\x12%\n\x08response\x18\x04 \x01(\x0b\x32\x11.sandbox.ResponseH\x00\x42\x06\n\x04\x62ody\"E\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0e\n\x06result\x18\x02 \x01(\x0c\x12\x1d\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x0e.sandbox.Error\"`\n\x05\x45rror\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12&\n\x07invalid\x18\x03 \x03(\x0b\x32\x15.sandbox.InvalidError\x12\x10\n\x08multiple\x18\x04 \x01(\x08\"-\n\x0cInvalidError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\"(\n\nDevicePair\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xcc\x03\n\nDeviceInfo\x12(\n\x0bidentifiers\x18\x01 \x03(\x0b\x32\x13.sandbox.DevicePair\x12(\n\x0b\x63onnections\x18\x02 \x03(\x0b\x32\x13.sandbox.DevicePair\x12,\n\nvia_device\x18\x03 \x01(\x0b\x32\x13.sandbox.DevicePairH\x00\x88\x01\x01\x12\x12\n\nentry_type\x18\x04 \x01(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x14\n\x0cmanufacturer\x18\x06 \x01(\t\x12\r\n\x05model\x18\x07 \x01(\t\x12\x10\n\x08model_id\x18\x08 \x01(\t\x12\x12\n\nsw_version\x18\t \x01(\t\x12\x12\n\nhw_version\x18\n \x01(\t\x12\x15\n\rserial_number\x18\x0b \x01(\t\x12\x16\n\x0esuggested_area\x18\x0c \x01(\t\x12\x19\n\x11\x63onfiguration_url\x18\r \x01(\t\x12\x14\n\x0c\x64\x65\x66\x61ult_name\x18\x0e \x01(\t\x12\x1c\n\x14\x64\x65\x66\x61ult_manufacturer\x18\x0f \x01(\t\x12\x15\n\rdefault_model\x18\x10 \x01(\t\x12\x17\n\x0ftranslation_key\x18\x11 \x01(\tB\r\n\x0b_via_device\"h\n\x11IntegrationSource\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0b\n\x03ref\x18\x03 \x01(\t\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x0e\n\x06\x64omain\x18\x05 \x01(\t\x12\x0e\n\x06subdir\x18\x06 \x01(\t\"\xa4\x02\n\nEntrySetup\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12%\n\x04\x64\x61ta\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x16\n\tunique_id\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07version\x18\x08 \x01(\x05\x12\x15\n\rminor_version\x18\t \x01(\x05\x12\x36\n\x12integration_source\x18\n \x01(\x0b\x32\x1a.sandbox.IntegrationSourceB\x0c\n\n_unique_id\">\n\x10\x45ntrySetupResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x1f\n\x0b\x45ntryUnload\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\"\x1f\n\x11\x45ntryUnloadResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\xc7\x01\n\x0b\x43\x61llService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\'\n\x06target\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\x0cservice_data\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0freturn_response\x18\x06 \x01(\x08\x42\r\n\x0b_context_id\"8\n\x0fServiceResponse\x12%\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"Q\n\x11\x43\x61llServiceResult\x12/\n\x08response\x18\x01 \x01(\x0b\x32\x18.sandbox.ServiceResponseH\x00\x88\x01\x01\x42\x0b\n\t_response\"\x87\x01\n\x0b\x45ntityQuery\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x0e\n\x06method\x18\x02 \x01(\t\x12%\n\x04\x61rgs\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"<\n\x11\x45ntityQueryResult\x12\'\n\x06result\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"4\n\x0fGetTranslations\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x0f\n\x07\x64omains\x18\x02 \x03(\t\"S\n\x15GetTranslationsResult\x12\x10\n\x08language\x18\x01 \x01(\t\x12(\n\x07strings\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\n\n\x08Shutdown\"u\n\x0eShutdownResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x10\n\x08unloaded\x18\x02 \x01(\x05\x12\x33\n\rrestore_state\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x10\n\x0e_restore_state\"\x06\n\x04Ping\"\x1a\n\nPingResult\x12\x0c\n\x04pong\x18\x01 \x01(\t\"\x07\n\x05Ready\"l\n\x08\x46lowInit\x12\x0f\n\x07handler\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\"H\n\x08\x46lowStep\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\x12+\n\nuser_input\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1c\n\tFlowAbort\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\"\x11\n\x0f\x46lowAbortResult\"\xa9\x05\n\nFlowResult\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x14\n\x07\x66low_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07handler\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07step_id\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06reason\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x12\n\x05title\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tlast_step\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07preview\x18\t \x01(\tH\x07\x88\x01\x01\x12\x14\n\x07version\x18\n \x01(\x05H\x08\x88\x01\x01\x12\x1a\n\rminor_version\x18\x0b \x01(\x05H\t\x88\x01\x01\x12%\n\x04\x64\x61ta\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\r \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65rrors\x18\x0e \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x39\n\x18\x64\x65scription_placeholders\x18\x0f \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07\x63ontext\x18\x10 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0b\x64\x61ta_schema\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12\x17\n\x0fhas_data_schema\x18\x12 \x01(\x08\x42\n\n\x08_flow_idB\n\n\x08_handlerB\n\n\x08_step_idB\t\n\x07_reasonB\x08\n\x06_titleB\x0e\n\x0c_descriptionB\x0c\n\n_last_stepB\n\n\x08_previewB\n\n\x08_versionB\x10\n\x0e_minor_version\"\x8a\x03\n\nEntityInfo\x12\x39\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1f.sandbox.EntityInfo.DescriptionH\x00\x88\x01\x01\x12-\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x13.sandbox.DeviceInfoH\x01\x88\x01\x01\x1a\xf1\x01\n\x0b\x44\x65scription\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04icon\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x65ntity_category\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65vice_class\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1a\n\x12supported_features\x18\x05 \x01(\x05\x12\x1c\n\x0ftranslation_key\x18\x06 \x01(\tH\x04\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_iconB\x12\n\x10_entity_categoryB\x0f\n\r_device_classB\x12\n\x10_translation_keyB\x0e\n\x0c_descriptionB\x0e\n\x0c_device_info\"\x88\x01\n\x0cInitialState\x12\x12\n\x05state\x18\x01 \x01(\tH\x00\x88\x01\x01\x12-\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructB\x08\n\x06_state\"\xda\x01\n\x11\x45ntityDescription\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\x19\n\x11sandbox_entity_id\x18\x03 \x01(\t\x12\x16\n\tunique_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0fhas_entity_name\x18\x05 \x01(\x08\x12!\n\x04info\x18\x06 \x01(\x0b\x32\x13.sandbox.EntityInfo\x12&\n\x07initial\x18\x07 \x01(\x0b\x32\x15.sandbox.InitialStateB\x0c\n\n_unique_id\")\n\x14RegisterEntityResult\x12\x11\n\tentity_id\x18\x01 \x01(\t\"-\n\x10UnregisterEntity\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\"$\n\x16UnregisterEntityResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x9c\x01\n\x0cStateChanged\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x12\n\x05state\x18\x02 \x01(\tH\x00\x88\x01\x01\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_stateB\r\n\x0b_context_id\"y\n\x0fRegisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x19\n\x11supports_response\x18\x03 \x01(\t\x12*\n\x06schema\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.ListValue\"6\n\x15RegisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x11\n\tinstalled\x18\x02 \x01(\x08\"4\n\x11UnregisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\"6\n\x17UnregisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07removed\x18\x02 \x01(\x08\"t\n\tFireEvent\x12\x12\n\nevent_type\x18\x01 \x01(\t\x12+\n\nevent_data\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"\x18\n\tStoreLoad\x12\x0b\n\x03key\x18\x01 \x01(\t\"F\n\x0fStoreLoadResult\x12*\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x07\n\x05_data\"?\n\tStoreSave\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1d\n\x0fStoreSaveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x1a\n\x0bStoreRemove\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1f\n\x11StoreRemoveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -60,66 +60,70 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_SERVICERESPONSE']._serialized_end=1667 _globals['_CALLSERVICERESULT']._serialized_start=1669 _globals['_CALLSERVICERESULT']._serialized_end=1750 - _globals['_GETTRANSLATIONS']._serialized_start=1752 - _globals['_GETTRANSLATIONS']._serialized_end=1804 - _globals['_GETTRANSLATIONSRESULT']._serialized_start=1806 - _globals['_GETTRANSLATIONSRESULT']._serialized_end=1889 - _globals['_SHUTDOWN']._serialized_start=1891 - _globals['_SHUTDOWN']._serialized_end=1901 - _globals['_SHUTDOWNRESULT']._serialized_start=1903 - _globals['_SHUTDOWNRESULT']._serialized_end=2020 - _globals['_PING']._serialized_start=2022 - _globals['_PING']._serialized_end=2028 - _globals['_PINGRESULT']._serialized_start=2030 - _globals['_PINGRESULT']._serialized_end=2056 - _globals['_READY']._serialized_start=2058 - _globals['_READY']._serialized_end=2065 - _globals['_FLOWINIT']._serialized_start=2067 - _globals['_FLOWINIT']._serialized_end=2175 - _globals['_FLOWSTEP']._serialized_start=2177 - _globals['_FLOWSTEP']._serialized_end=2249 - _globals['_FLOWABORT']._serialized_start=2251 - _globals['_FLOWABORT']._serialized_end=2279 - _globals['_FLOWABORTRESULT']._serialized_start=2281 - _globals['_FLOWABORTRESULT']._serialized_end=2298 - _globals['_FLOWRESULT']._serialized_start=2301 - _globals['_FLOWRESULT']._serialized_end=2982 - _globals['_ENTITYINFO']._serialized_start=2985 - _globals['_ENTITYINFO']._serialized_end=3379 - _globals['_ENTITYINFO_DESCRIPTION']._serialized_start=3106 - _globals['_ENTITYINFO_DESCRIPTION']._serialized_end=3347 - _globals['_INITIALSTATE']._serialized_start=3382 - _globals['_INITIALSTATE']._serialized_end=3518 - _globals['_ENTITYDESCRIPTION']._serialized_start=3521 - _globals['_ENTITYDESCRIPTION']._serialized_end=3739 - _globals['_REGISTERENTITYRESULT']._serialized_start=3741 - _globals['_REGISTERENTITYRESULT']._serialized_end=3782 - _globals['_UNREGISTERENTITY']._serialized_start=3784 - _globals['_UNREGISTERENTITY']._serialized_end=3829 - _globals['_UNREGISTERENTITYRESULT']._serialized_start=3831 - _globals['_UNREGISTERENTITYRESULT']._serialized_end=3867 - _globals['_STATECHANGED']._serialized_start=3870 - _globals['_STATECHANGED']._serialized_end=4026 - _globals['_REGISTERSERVICE']._serialized_start=4028 - _globals['_REGISTERSERVICE']._serialized_end=4149 - _globals['_REGISTERSERVICERESULT']._serialized_start=4151 - _globals['_REGISTERSERVICERESULT']._serialized_end=4205 - _globals['_UNREGISTERSERVICE']._serialized_start=4207 - _globals['_UNREGISTERSERVICE']._serialized_end=4259 - _globals['_UNREGISTERSERVICERESULT']._serialized_start=4261 - _globals['_UNREGISTERSERVICERESULT']._serialized_end=4315 - _globals['_FIREEVENT']._serialized_start=4317 - _globals['_FIREEVENT']._serialized_end=4433 - _globals['_STORELOAD']._serialized_start=4435 - _globals['_STORELOAD']._serialized_end=4459 - _globals['_STORELOADRESULT']._serialized_start=4461 - _globals['_STORELOADRESULT']._serialized_end=4531 - _globals['_STORESAVE']._serialized_start=4533 - _globals['_STORESAVE']._serialized_end=4596 - _globals['_STORESAVERESULT']._serialized_start=4598 - _globals['_STORESAVERESULT']._serialized_end=4627 - _globals['_STOREREMOVE']._serialized_start=4629 - _globals['_STOREREMOVE']._serialized_end=4655 - _globals['_STOREREMOVERESULT']._serialized_start=4657 - _globals['_STOREREMOVERESULT']._serialized_end=4688 + _globals['_ENTITYQUERY']._serialized_start=1753 + _globals['_ENTITYQUERY']._serialized_end=1888 + _globals['_ENTITYQUERYRESULT']._serialized_start=1890 + _globals['_ENTITYQUERYRESULT']._serialized_end=1950 + _globals['_GETTRANSLATIONS']._serialized_start=1952 + _globals['_GETTRANSLATIONS']._serialized_end=2004 + _globals['_GETTRANSLATIONSRESULT']._serialized_start=2006 + _globals['_GETTRANSLATIONSRESULT']._serialized_end=2089 + _globals['_SHUTDOWN']._serialized_start=2091 + _globals['_SHUTDOWN']._serialized_end=2101 + _globals['_SHUTDOWNRESULT']._serialized_start=2103 + _globals['_SHUTDOWNRESULT']._serialized_end=2220 + _globals['_PING']._serialized_start=2222 + _globals['_PING']._serialized_end=2228 + _globals['_PINGRESULT']._serialized_start=2230 + _globals['_PINGRESULT']._serialized_end=2256 + _globals['_READY']._serialized_start=2258 + _globals['_READY']._serialized_end=2265 + _globals['_FLOWINIT']._serialized_start=2267 + _globals['_FLOWINIT']._serialized_end=2375 + _globals['_FLOWSTEP']._serialized_start=2377 + _globals['_FLOWSTEP']._serialized_end=2449 + _globals['_FLOWABORT']._serialized_start=2451 + _globals['_FLOWABORT']._serialized_end=2479 + _globals['_FLOWABORTRESULT']._serialized_start=2481 + _globals['_FLOWABORTRESULT']._serialized_end=2498 + _globals['_FLOWRESULT']._serialized_start=2501 + _globals['_FLOWRESULT']._serialized_end=3182 + _globals['_ENTITYINFO']._serialized_start=3185 + _globals['_ENTITYINFO']._serialized_end=3579 + _globals['_ENTITYINFO_DESCRIPTION']._serialized_start=3306 + _globals['_ENTITYINFO_DESCRIPTION']._serialized_end=3547 + _globals['_INITIALSTATE']._serialized_start=3582 + _globals['_INITIALSTATE']._serialized_end=3718 + _globals['_ENTITYDESCRIPTION']._serialized_start=3721 + _globals['_ENTITYDESCRIPTION']._serialized_end=3939 + _globals['_REGISTERENTITYRESULT']._serialized_start=3941 + _globals['_REGISTERENTITYRESULT']._serialized_end=3982 + _globals['_UNREGISTERENTITY']._serialized_start=3984 + _globals['_UNREGISTERENTITY']._serialized_end=4029 + _globals['_UNREGISTERENTITYRESULT']._serialized_start=4031 + _globals['_UNREGISTERENTITYRESULT']._serialized_end=4067 + _globals['_STATECHANGED']._serialized_start=4070 + _globals['_STATECHANGED']._serialized_end=4226 + _globals['_REGISTERSERVICE']._serialized_start=4228 + _globals['_REGISTERSERVICE']._serialized_end=4349 + _globals['_REGISTERSERVICERESULT']._serialized_start=4351 + _globals['_REGISTERSERVICERESULT']._serialized_end=4405 + _globals['_UNREGISTERSERVICE']._serialized_start=4407 + _globals['_UNREGISTERSERVICE']._serialized_end=4459 + _globals['_UNREGISTERSERVICERESULT']._serialized_start=4461 + _globals['_UNREGISTERSERVICERESULT']._serialized_end=4515 + _globals['_FIREEVENT']._serialized_start=4517 + _globals['_FIREEVENT']._serialized_end=4633 + _globals['_STORELOAD']._serialized_start=4635 + _globals['_STORELOAD']._serialized_end=4659 + _globals['_STORELOADRESULT']._serialized_start=4661 + _globals['_STORELOADRESULT']._serialized_end=4731 + _globals['_STORESAVE']._serialized_start=4733 + _globals['_STORESAVE']._serialized_end=4796 + _globals['_STORESAVERESULT']._serialized_start=4798 + _globals['_STORESAVERESULT']._serialized_end=4827 + _globals['_STOREREMOVE']._serialized_start=4829 + _globals['_STOREREMOVE']._serialized_end=4855 + _globals['_STOREREMOVERESULT']._serialized_start=4857 + _globals['_STOREREMOVERESULT']._serialized_end=4888 # @@protoc_insertion_point(module_scope) diff --git a/homeassistant/components/sandbox/_proto/sandbox_pb2.pyi b/homeassistant/components/sandbox/_proto/sandbox_pb2.pyi index 56a7742da87e..1101c5b54192 100644 --- a/homeassistant/components/sandbox/_proto/sandbox_pb2.pyi +++ b/homeassistant/components/sandbox/_proto/sandbox_pb2.pyi @@ -183,6 +183,24 @@ class CallServiceResult(_message.Message): response: ServiceResponse def __init__(self, response: _Optional[_Union[ServiceResponse, _Mapping]] = ...) -> None: ... +class EntityQuery(_message.Message): + __slots__ = ("sandbox_entity_id", "method", "args", "context_id") + SANDBOX_ENTITY_ID_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + ARGS_FIELD_NUMBER: _ClassVar[int] + CONTEXT_ID_FIELD_NUMBER: _ClassVar[int] + sandbox_entity_id: str + method: str + args: _struct_pb2.Struct + context_id: str + def __init__(self, sandbox_entity_id: _Optional[str] = ..., method: _Optional[str] = ..., args: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context_id: _Optional[str] = ...) -> None: ... + +class EntityQueryResult(_message.Message): + __slots__ = ("result",) + RESULT_FIELD_NUMBER: _ClassVar[int] + result: _struct_pb2.Struct + def __init__(self, result: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... + class GetTranslations(_message.Message): __slots__ = ("language", "domains") LANGUAGE_FIELD_NUMBER: _ClassVar[int] diff --git a/homeassistant/components/sandbox/bridge.py b/homeassistant/components/sandbox/bridge.py index 71108fbb1b52..7c186f3e11f0 100644 --- a/homeassistant/components/sandbox/bridge.py +++ b/homeassistant/components/sandbox/bridge.py @@ -65,6 +65,7 @@ from .const import UNIQUE_ID_SEPARATOR from .messages import dict_to_struct, listvalue_to_list, struct_to_dict from .protocol import ( MSG_CALL_SERVICE, + MSG_ENTITY_QUERY, MSG_FIRE_EVENT, MSG_REGISTER_ENTITY, MSG_REGISTER_SERVICE, @@ -243,6 +244,43 @@ class SandboxBridge: return_response=return_response, ) + async def async_entity_query( + self, + *, + sandbox_entity_id: str, + method: str, + args: dict[str, Any], + context: Context | None = None, + ) -> Any: + """Forward one server-side entity query to the sandbox as a single RPC. + + The companion to :meth:`async_call_service` for the query-shaped entity + APIs that have no ``SupportsResponse`` service to ride (media search, + update release notes, vacuum segments, the WS-only calendar event + edits). ``method`` names the real entity method; ``args`` are its + kwargs. Like a service call the ``context`` is remembered before its id + is reduced to a bare wire value, errors translate through the same + :func:`_translate_remote_error` / ``ChannelClosedError`` paths, and the + wrapped ``{"value": …}`` return is unwrapped. + """ + self._remember_context(context) + request = pb.EntityQuery( + sandbox_entity_id=sandbox_entity_id, + method=method, + args=dict_to_struct(args), + ) + if context is not None: + request.context_id = context.id + try: + result = await self.channel.call(MSG_ENTITY_QUERY, request) + except ChannelRemoteError as err: + raise _translate_remote_error(err) from err + except ChannelClosedError as err: + raise HomeAssistantError( + f"Sandbox {self.group!r} channel closed mid-query" + ) from err + return struct_to_dict(result.result).get("value") + async def _raw_call_service( self, *, diff --git a/homeassistant/components/sandbox/entity/__init__.py b/homeassistant/components/sandbox/entity/__init__.py index 0f1de0180a9f..d8e1db8a3124 100644 --- a/homeassistant/components/sandbox/entity/__init__.py +++ b/homeassistant/components/sandbox/entity/__init__.py @@ -197,6 +197,23 @@ class SandboxProxyEntity(Entity): return struct_to_dict(result.response.data) return {} + async def _entity_query(self, method: str, **args: Any) -> Any: + """Forward a server-side entity query to the sandbox. + + The request/response companion to :meth:`_call_service` for the + query-shaped entity APIs that have no ``SupportsResponse`` service to + ride. ``method`` names the real entity method to invoke on the sandbox + side; ``args`` are its kwargs. Returns the deserialised return value + (``None`` for mutations). ``self._context`` is forwarded so attribution + survives exactly as it does for a service call. + """ + return await self._bridge.async_entity_query( + sandbox_entity_id=self.description.sandbox_entity_id, + method=method, + args=args, + context=self._context, + ) + # Lazy import to avoid a circular dependency at module import time # (bridge imports build_proxy → entity imports proxies → proxies import diff --git a/homeassistant/components/sandbox/messages.py b/homeassistant/components/sandbox/messages.py index 797bf2c9738a..5d2cc4dd2bab 100644 --- a/homeassistant/components/sandbox/messages.py +++ b/homeassistant/components/sandbox/messages.py @@ -38,6 +38,7 @@ REGISTRY: dict[str, tuple[type[Message], type[Message] | None]] = { "sandbox/entry_setup": (pb.EntrySetup, pb.EntrySetupResult), "sandbox/entry_unload": (pb.EntryUnload, pb.EntryUnloadResult), "sandbox/call_service": (pb.CallService, pb.CallServiceResult), + "sandbox/entity_query": (pb.EntityQuery, pb.EntityQueryResult), "sandbox/get_translations": (pb.GetTranslations, pb.GetTranslationsResult), "sandbox/shutdown": (pb.Shutdown, pb.ShutdownResult), "sandbox/ping": (pb.Ping, pb.PingResult), diff --git a/homeassistant/components/sandbox/protocol.py b/homeassistant/components/sandbox/protocol.py index a17ba24c0141..0e7726f48b88 100644 --- a/homeassistant/components/sandbox/protocol.py +++ b/homeassistant/components/sandbox/protocol.py @@ -32,6 +32,13 @@ Main → Sandbox calls: the main→sandbox service mirroring path). Payload mirrors a ``ServiceCall``: ``(domain, service, target, service_data, context, return_response)``. Returns either ``None`` or a service-response dict. +* ``sandbox/entity_query`` — generic request/response RPC for the + server-side entity queries with no ``SupportsResponse`` service to ride + (media search, update release notes, vacuum segments, the WS-only calendar + event edits). Payload ``{sandbox_entity_id, method, args, context_id}``; + the sandbox resolves the entity, invokes ``method`` with ``args`` as kwargs, + and returns the serialised result wrapped as ``{"value": }``. + Ops that map to a ``SupportsResponse`` service use ``call_service`` instead. * ``sandbox/get_translations`` — pull a sandboxed integration's frontend translation strings. Payload ``{language, domains: [str]}`` (main batches every owned custom domain of one group into a single request). Response @@ -100,6 +107,7 @@ MSG_READY: Final = "sandbox/ready" MSG_ENTRY_SETUP: Final = "sandbox/entry_setup" MSG_ENTRY_UNLOAD: Final = "sandbox/entry_unload" MSG_CALL_SERVICE: Final = "sandbox/call_service" +MSG_ENTITY_QUERY: Final = "sandbox/entity_query" MSG_GET_TRANSLATIONS: Final = "sandbox/get_translations" MSG_SHUTDOWN: Final = "sandbox/shutdown" @@ -117,6 +125,7 @@ MSG_STORE_REMOVE: Final = "sandbox/store_remove" __all__ = [ "MSG_CALL_SERVICE", + "MSG_ENTITY_QUERY", "MSG_ENTRY_SETUP", "MSG_ENTRY_UNLOAD", "MSG_FIRE_EVENT", diff --git a/sandbox/hass_client/hass_client/_proto/sandbox_pb2.py b/sandbox/hass_client/hass_client/_proto/sandbox_pb2.py index 5047c3c31667..e93de4f2b5c5 100644 --- a/sandbox/hass_client/hass_client/_proto/sandbox_pb2.py +++ b/sandbox/hass_client/hass_client/_proto/sandbox_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rsandbox.proto\x12\x07sandbox\x1a\x1cgoogle/protobuf/struct.proto\"c\n\x05\x46rame\x12\n\n\x02id\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x11\n\x07request\x18\x03 \x01(\x0cH\x00\x12%\n\x08response\x18\x04 \x01(\x0b\x32\x11.sandbox.ResponseH\x00\x42\x06\n\x04\x62ody\"E\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0e\n\x06result\x18\x02 \x01(\x0c\x12\x1d\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x0e.sandbox.Error\"`\n\x05\x45rror\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12&\n\x07invalid\x18\x03 \x03(\x0b\x32\x15.sandbox.InvalidError\x12\x10\n\x08multiple\x18\x04 \x01(\x08\"-\n\x0cInvalidError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\"(\n\nDevicePair\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xcc\x03\n\nDeviceInfo\x12(\n\x0bidentifiers\x18\x01 \x03(\x0b\x32\x13.sandbox.DevicePair\x12(\n\x0b\x63onnections\x18\x02 \x03(\x0b\x32\x13.sandbox.DevicePair\x12,\n\nvia_device\x18\x03 \x01(\x0b\x32\x13.sandbox.DevicePairH\x00\x88\x01\x01\x12\x12\n\nentry_type\x18\x04 \x01(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x14\n\x0cmanufacturer\x18\x06 \x01(\t\x12\r\n\x05model\x18\x07 \x01(\t\x12\x10\n\x08model_id\x18\x08 \x01(\t\x12\x12\n\nsw_version\x18\t \x01(\t\x12\x12\n\nhw_version\x18\n \x01(\t\x12\x15\n\rserial_number\x18\x0b \x01(\t\x12\x16\n\x0esuggested_area\x18\x0c \x01(\t\x12\x19\n\x11\x63onfiguration_url\x18\r \x01(\t\x12\x14\n\x0c\x64\x65\x66\x61ult_name\x18\x0e \x01(\t\x12\x1c\n\x14\x64\x65\x66\x61ult_manufacturer\x18\x0f \x01(\t\x12\x15\n\rdefault_model\x18\x10 \x01(\t\x12\x17\n\x0ftranslation_key\x18\x11 \x01(\tB\r\n\x0b_via_device\"h\n\x11IntegrationSource\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0b\n\x03ref\x18\x03 \x01(\t\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x0e\n\x06\x64omain\x18\x05 \x01(\t\x12\x0e\n\x06subdir\x18\x06 \x01(\t\"\xa4\x02\n\nEntrySetup\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12%\n\x04\x64\x61ta\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x16\n\tunique_id\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07version\x18\x08 \x01(\x05\x12\x15\n\rminor_version\x18\t \x01(\x05\x12\x36\n\x12integration_source\x18\n \x01(\x0b\x32\x1a.sandbox.IntegrationSourceB\x0c\n\n_unique_id\">\n\x10\x45ntrySetupResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x1f\n\x0b\x45ntryUnload\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\"\x1f\n\x11\x45ntryUnloadResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\xc7\x01\n\x0b\x43\x61llService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\'\n\x06target\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\x0cservice_data\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0freturn_response\x18\x06 \x01(\x08\x42\r\n\x0b_context_id\"8\n\x0fServiceResponse\x12%\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"Q\n\x11\x43\x61llServiceResult\x12/\n\x08response\x18\x01 \x01(\x0b\x32\x18.sandbox.ServiceResponseH\x00\x88\x01\x01\x42\x0b\n\t_response\"4\n\x0fGetTranslations\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x0f\n\x07\x64omains\x18\x02 \x03(\t\"S\n\x15GetTranslationsResult\x12\x10\n\x08language\x18\x01 \x01(\t\x12(\n\x07strings\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\n\n\x08Shutdown\"u\n\x0eShutdownResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x10\n\x08unloaded\x18\x02 \x01(\x05\x12\x33\n\rrestore_state\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x10\n\x0e_restore_state\"\x06\n\x04Ping\"\x1a\n\nPingResult\x12\x0c\n\x04pong\x18\x01 \x01(\t\"\x07\n\x05Ready\"l\n\x08\x46lowInit\x12\x0f\n\x07handler\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\"H\n\x08\x46lowStep\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\x12+\n\nuser_input\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1c\n\tFlowAbort\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\"\x11\n\x0f\x46lowAbortResult\"\xa9\x05\n\nFlowResult\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x14\n\x07\x66low_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07handler\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07step_id\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06reason\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x12\n\x05title\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tlast_step\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07preview\x18\t \x01(\tH\x07\x88\x01\x01\x12\x14\n\x07version\x18\n \x01(\x05H\x08\x88\x01\x01\x12\x1a\n\rminor_version\x18\x0b \x01(\x05H\t\x88\x01\x01\x12%\n\x04\x64\x61ta\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\r \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65rrors\x18\x0e \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x39\n\x18\x64\x65scription_placeholders\x18\x0f \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07\x63ontext\x18\x10 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0b\x64\x61ta_schema\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12\x17\n\x0fhas_data_schema\x18\x12 \x01(\x08\x42\n\n\x08_flow_idB\n\n\x08_handlerB\n\n\x08_step_idB\t\n\x07_reasonB\x08\n\x06_titleB\x0e\n\x0c_descriptionB\x0c\n\n_last_stepB\n\n\x08_previewB\n\n\x08_versionB\x10\n\x0e_minor_version\"\x8a\x03\n\nEntityInfo\x12\x39\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1f.sandbox.EntityInfo.DescriptionH\x00\x88\x01\x01\x12-\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x13.sandbox.DeviceInfoH\x01\x88\x01\x01\x1a\xf1\x01\n\x0b\x44\x65scription\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04icon\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x65ntity_category\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65vice_class\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1a\n\x12supported_features\x18\x05 \x01(\x05\x12\x1c\n\x0ftranslation_key\x18\x06 \x01(\tH\x04\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_iconB\x12\n\x10_entity_categoryB\x0f\n\r_device_classB\x12\n\x10_translation_keyB\x0e\n\x0c_descriptionB\x0e\n\x0c_device_info\"\x88\x01\n\x0cInitialState\x12\x12\n\x05state\x18\x01 \x01(\tH\x00\x88\x01\x01\x12-\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructB\x08\n\x06_state\"\xda\x01\n\x11\x45ntityDescription\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\x19\n\x11sandbox_entity_id\x18\x03 \x01(\t\x12\x16\n\tunique_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0fhas_entity_name\x18\x05 \x01(\x08\x12!\n\x04info\x18\x06 \x01(\x0b\x32\x13.sandbox.EntityInfo\x12&\n\x07initial\x18\x07 \x01(\x0b\x32\x15.sandbox.InitialStateB\x0c\n\n_unique_id\")\n\x14RegisterEntityResult\x12\x11\n\tentity_id\x18\x01 \x01(\t\"-\n\x10UnregisterEntity\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\"$\n\x16UnregisterEntityResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x9c\x01\n\x0cStateChanged\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x12\n\x05state\x18\x02 \x01(\tH\x00\x88\x01\x01\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_stateB\r\n\x0b_context_id\"y\n\x0fRegisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x19\n\x11supports_response\x18\x03 \x01(\t\x12*\n\x06schema\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.ListValue\"6\n\x15RegisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x11\n\tinstalled\x18\x02 \x01(\x08\"4\n\x11UnregisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\"6\n\x17UnregisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07removed\x18\x02 \x01(\x08\"t\n\tFireEvent\x12\x12\n\nevent_type\x18\x01 \x01(\t\x12+\n\nevent_data\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"\x18\n\tStoreLoad\x12\x0b\n\x03key\x18\x01 \x01(\t\"F\n\x0fStoreLoadResult\x12*\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x07\n\x05_data\"?\n\tStoreSave\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1d\n\x0fStoreSaveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x1a\n\x0bStoreRemove\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1f\n\x11StoreRemoveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rsandbox.proto\x12\x07sandbox\x1a\x1cgoogle/protobuf/struct.proto\"c\n\x05\x46rame\x12\n\n\x02id\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x11\n\x07request\x18\x03 \x01(\x0cH\x00\x12%\n\x08response\x18\x04 \x01(\x0b\x32\x11.sandbox.ResponseH\x00\x42\x06\n\x04\x62ody\"E\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0e\n\x06result\x18\x02 \x01(\x0c\x12\x1d\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x0e.sandbox.Error\"`\n\x05\x45rror\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12&\n\x07invalid\x18\x03 \x03(\x0b\x32\x15.sandbox.InvalidError\x12\x10\n\x08multiple\x18\x04 \x01(\x08\"-\n\x0cInvalidError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\"(\n\nDevicePair\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xcc\x03\n\nDeviceInfo\x12(\n\x0bidentifiers\x18\x01 \x03(\x0b\x32\x13.sandbox.DevicePair\x12(\n\x0b\x63onnections\x18\x02 \x03(\x0b\x32\x13.sandbox.DevicePair\x12,\n\nvia_device\x18\x03 \x01(\x0b\x32\x13.sandbox.DevicePairH\x00\x88\x01\x01\x12\x12\n\nentry_type\x18\x04 \x01(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x14\n\x0cmanufacturer\x18\x06 \x01(\t\x12\r\n\x05model\x18\x07 \x01(\t\x12\x10\n\x08model_id\x18\x08 \x01(\t\x12\x12\n\nsw_version\x18\t \x01(\t\x12\x12\n\nhw_version\x18\n \x01(\t\x12\x15\n\rserial_number\x18\x0b \x01(\t\x12\x16\n\x0esuggested_area\x18\x0c \x01(\t\x12\x19\n\x11\x63onfiguration_url\x18\r \x01(\t\x12\x14\n\x0c\x64\x65\x66\x61ult_name\x18\x0e \x01(\t\x12\x1c\n\x14\x64\x65\x66\x61ult_manufacturer\x18\x0f \x01(\t\x12\x15\n\rdefault_model\x18\x10 \x01(\t\x12\x17\n\x0ftranslation_key\x18\x11 \x01(\tB\r\n\x0b_via_device\"h\n\x11IntegrationSource\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0b\n\x03ref\x18\x03 \x01(\t\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x0e\n\x06\x64omain\x18\x05 \x01(\t\x12\x0e\n\x06subdir\x18\x06 \x01(\t\"\xa4\x02\n\nEntrySetup\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12%\n\x04\x64\x61ta\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x16\n\tunique_id\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07version\x18\x08 \x01(\x05\x12\x15\n\rminor_version\x18\t \x01(\x05\x12\x36\n\x12integration_source\x18\n \x01(\x0b\x32\x1a.sandbox.IntegrationSourceB\x0c\n\n_unique_id\">\n\x10\x45ntrySetupResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x1f\n\x0b\x45ntryUnload\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\"\x1f\n\x11\x45ntryUnloadResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\xc7\x01\n\x0b\x43\x61llService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\'\n\x06target\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\x0cservice_data\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0freturn_response\x18\x06 \x01(\x08\x42\r\n\x0b_context_id\"8\n\x0fServiceResponse\x12%\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"Q\n\x11\x43\x61llServiceResult\x12/\n\x08response\x18\x01 \x01(\x0b\x32\x18.sandbox.ServiceResponseH\x00\x88\x01\x01\x42\x0b\n\t_response\"\x87\x01\n\x0b\x45ntityQuery\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x0e\n\x06method\x18\x02 \x01(\t\x12%\n\x04\x61rgs\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"<\n\x11\x45ntityQueryResult\x12\'\n\x06result\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"4\n\x0fGetTranslations\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x0f\n\x07\x64omains\x18\x02 \x03(\t\"S\n\x15GetTranslationsResult\x12\x10\n\x08language\x18\x01 \x01(\t\x12(\n\x07strings\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\n\n\x08Shutdown\"u\n\x0eShutdownResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x10\n\x08unloaded\x18\x02 \x01(\x05\x12\x33\n\rrestore_state\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x10\n\x0e_restore_state\"\x06\n\x04Ping\"\x1a\n\nPingResult\x12\x0c\n\x04pong\x18\x01 \x01(\t\"\x07\n\x05Ready\"l\n\x08\x46lowInit\x12\x0f\n\x07handler\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\"H\n\x08\x46lowStep\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\x12+\n\nuser_input\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1c\n\tFlowAbort\x12\x0f\n\x07\x66low_id\x18\x01 \x01(\t\"\x11\n\x0f\x46lowAbortResult\"\xa9\x05\n\nFlowResult\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x14\n\x07\x66low_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07handler\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07step_id\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06reason\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x12\n\x05title\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tlast_step\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07preview\x18\t \x01(\tH\x07\x88\x01\x01\x12\x14\n\x07version\x18\n \x01(\x05H\x08\x88\x01\x01\x12\x1a\n\rminor_version\x18\x0b \x01(\x05H\t\x88\x01\x01\x12%\n\x04\x64\x61ta\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07options\x18\r \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65rrors\x18\x0e \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x39\n\x18\x64\x65scription_placeholders\x18\x0f \x01(\x0b\x32\x17.google.protobuf.Struct\x12(\n\x07\x63ontext\x18\x10 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0b\x64\x61ta_schema\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12\x17\n\x0fhas_data_schema\x18\x12 \x01(\x08\x42\n\n\x08_flow_idB\n\n\x08_handlerB\n\n\x08_step_idB\t\n\x07_reasonB\x08\n\x06_titleB\x0e\n\x0c_descriptionB\x0c\n\n_last_stepB\n\n\x08_previewB\n\n\x08_versionB\x10\n\x0e_minor_version\"\x8a\x03\n\nEntityInfo\x12\x39\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1f.sandbox.EntityInfo.DescriptionH\x00\x88\x01\x01\x12-\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x13.sandbox.DeviceInfoH\x01\x88\x01\x01\x1a\xf1\x01\n\x0b\x44\x65scription\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04icon\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x65ntity_category\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65vice_class\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1a\n\x12supported_features\x18\x05 \x01(\x05\x12\x1c\n\x0ftranslation_key\x18\x06 \x01(\tH\x04\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_iconB\x12\n\x10_entity_categoryB\x0f\n\r_device_classB\x12\n\x10_translation_keyB\x0e\n\x0c_descriptionB\x0e\n\x0c_device_info\"\x88\x01\n\x0cInitialState\x12\x12\n\x05state\x18\x01 \x01(\tH\x00\x88\x01\x01\x12-\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructB\x08\n\x06_state\"\xda\x01\n\x11\x45ntityDescription\x12\x10\n\x08\x65ntry_id\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\x19\n\x11sandbox_entity_id\x18\x03 \x01(\t\x12\x16\n\tunique_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0fhas_entity_name\x18\x05 \x01(\x08\x12!\n\x04info\x18\x06 \x01(\x0b\x32\x13.sandbox.EntityInfo\x12&\n\x07initial\x18\x07 \x01(\x0b\x32\x15.sandbox.InitialStateB\x0c\n\n_unique_id\")\n\x14RegisterEntityResult\x12\x11\n\tentity_id\x18\x01 \x01(\t\"-\n\x10UnregisterEntity\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\"$\n\x16UnregisterEntityResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x9c\x01\n\x0cStateChanged\x12\x19\n\x11sandbox_entity_id\x18\x01 \x01(\t\x12\x12\n\x05state\x18\x02 \x01(\tH\x00\x88\x01\x01\x12+\n\nattributes\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_stateB\r\n\x0b_context_id\"y\n\x0fRegisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x19\n\x11supports_response\x18\x03 \x01(\t\x12*\n\x06schema\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.ListValue\"6\n\x15RegisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x11\n\tinstalled\x18\x02 \x01(\x08\"4\n\x11UnregisterService\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\"6\n\x17UnregisterServiceResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07removed\x18\x02 \x01(\x08\"t\n\tFireEvent\x12\x12\n\nevent_type\x18\x01 \x01(\t\x12+\n\nevent_data\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x17\n\ncontext_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_context_id\"\x18\n\tStoreLoad\x12\x0b\n\x03key\x18\x01 \x01(\t\"F\n\x0fStoreLoadResult\x12*\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\x07\n\x05_data\"?\n\tStoreSave\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x1d\n\x0fStoreSaveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\"\x1a\n\x0bStoreRemove\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1f\n\x11StoreRemoveResult\x12\n\n\x02ok\x18\x01 \x01(\x08\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -60,66 +60,70 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_SERVICERESPONSE']._serialized_end=1667 _globals['_CALLSERVICERESULT']._serialized_start=1669 _globals['_CALLSERVICERESULT']._serialized_end=1750 - _globals['_GETTRANSLATIONS']._serialized_start=1752 - _globals['_GETTRANSLATIONS']._serialized_end=1804 - _globals['_GETTRANSLATIONSRESULT']._serialized_start=1806 - _globals['_GETTRANSLATIONSRESULT']._serialized_end=1889 - _globals['_SHUTDOWN']._serialized_start=1891 - _globals['_SHUTDOWN']._serialized_end=1901 - _globals['_SHUTDOWNRESULT']._serialized_start=1903 - _globals['_SHUTDOWNRESULT']._serialized_end=2020 - _globals['_PING']._serialized_start=2022 - _globals['_PING']._serialized_end=2028 - _globals['_PINGRESULT']._serialized_start=2030 - _globals['_PINGRESULT']._serialized_end=2056 - _globals['_READY']._serialized_start=2058 - _globals['_READY']._serialized_end=2065 - _globals['_FLOWINIT']._serialized_start=2067 - _globals['_FLOWINIT']._serialized_end=2175 - _globals['_FLOWSTEP']._serialized_start=2177 - _globals['_FLOWSTEP']._serialized_end=2249 - _globals['_FLOWABORT']._serialized_start=2251 - _globals['_FLOWABORT']._serialized_end=2279 - _globals['_FLOWABORTRESULT']._serialized_start=2281 - _globals['_FLOWABORTRESULT']._serialized_end=2298 - _globals['_FLOWRESULT']._serialized_start=2301 - _globals['_FLOWRESULT']._serialized_end=2982 - _globals['_ENTITYINFO']._serialized_start=2985 - _globals['_ENTITYINFO']._serialized_end=3379 - _globals['_ENTITYINFO_DESCRIPTION']._serialized_start=3106 - _globals['_ENTITYINFO_DESCRIPTION']._serialized_end=3347 - _globals['_INITIALSTATE']._serialized_start=3382 - _globals['_INITIALSTATE']._serialized_end=3518 - _globals['_ENTITYDESCRIPTION']._serialized_start=3521 - _globals['_ENTITYDESCRIPTION']._serialized_end=3739 - _globals['_REGISTERENTITYRESULT']._serialized_start=3741 - _globals['_REGISTERENTITYRESULT']._serialized_end=3782 - _globals['_UNREGISTERENTITY']._serialized_start=3784 - _globals['_UNREGISTERENTITY']._serialized_end=3829 - _globals['_UNREGISTERENTITYRESULT']._serialized_start=3831 - _globals['_UNREGISTERENTITYRESULT']._serialized_end=3867 - _globals['_STATECHANGED']._serialized_start=3870 - _globals['_STATECHANGED']._serialized_end=4026 - _globals['_REGISTERSERVICE']._serialized_start=4028 - _globals['_REGISTERSERVICE']._serialized_end=4149 - _globals['_REGISTERSERVICERESULT']._serialized_start=4151 - _globals['_REGISTERSERVICERESULT']._serialized_end=4205 - _globals['_UNREGISTERSERVICE']._serialized_start=4207 - _globals['_UNREGISTERSERVICE']._serialized_end=4259 - _globals['_UNREGISTERSERVICERESULT']._serialized_start=4261 - _globals['_UNREGISTERSERVICERESULT']._serialized_end=4315 - _globals['_FIREEVENT']._serialized_start=4317 - _globals['_FIREEVENT']._serialized_end=4433 - _globals['_STORELOAD']._serialized_start=4435 - _globals['_STORELOAD']._serialized_end=4459 - _globals['_STORELOADRESULT']._serialized_start=4461 - _globals['_STORELOADRESULT']._serialized_end=4531 - _globals['_STORESAVE']._serialized_start=4533 - _globals['_STORESAVE']._serialized_end=4596 - _globals['_STORESAVERESULT']._serialized_start=4598 - _globals['_STORESAVERESULT']._serialized_end=4627 - _globals['_STOREREMOVE']._serialized_start=4629 - _globals['_STOREREMOVE']._serialized_end=4655 - _globals['_STOREREMOVERESULT']._serialized_start=4657 - _globals['_STOREREMOVERESULT']._serialized_end=4688 + _globals['_ENTITYQUERY']._serialized_start=1753 + _globals['_ENTITYQUERY']._serialized_end=1888 + _globals['_ENTITYQUERYRESULT']._serialized_start=1890 + _globals['_ENTITYQUERYRESULT']._serialized_end=1950 + _globals['_GETTRANSLATIONS']._serialized_start=1952 + _globals['_GETTRANSLATIONS']._serialized_end=2004 + _globals['_GETTRANSLATIONSRESULT']._serialized_start=2006 + _globals['_GETTRANSLATIONSRESULT']._serialized_end=2089 + _globals['_SHUTDOWN']._serialized_start=2091 + _globals['_SHUTDOWN']._serialized_end=2101 + _globals['_SHUTDOWNRESULT']._serialized_start=2103 + _globals['_SHUTDOWNRESULT']._serialized_end=2220 + _globals['_PING']._serialized_start=2222 + _globals['_PING']._serialized_end=2228 + _globals['_PINGRESULT']._serialized_start=2230 + _globals['_PINGRESULT']._serialized_end=2256 + _globals['_READY']._serialized_start=2258 + _globals['_READY']._serialized_end=2265 + _globals['_FLOWINIT']._serialized_start=2267 + _globals['_FLOWINIT']._serialized_end=2375 + _globals['_FLOWSTEP']._serialized_start=2377 + _globals['_FLOWSTEP']._serialized_end=2449 + _globals['_FLOWABORT']._serialized_start=2451 + _globals['_FLOWABORT']._serialized_end=2479 + _globals['_FLOWABORTRESULT']._serialized_start=2481 + _globals['_FLOWABORTRESULT']._serialized_end=2498 + _globals['_FLOWRESULT']._serialized_start=2501 + _globals['_FLOWRESULT']._serialized_end=3182 + _globals['_ENTITYINFO']._serialized_start=3185 + _globals['_ENTITYINFO']._serialized_end=3579 + _globals['_ENTITYINFO_DESCRIPTION']._serialized_start=3306 + _globals['_ENTITYINFO_DESCRIPTION']._serialized_end=3547 + _globals['_INITIALSTATE']._serialized_start=3582 + _globals['_INITIALSTATE']._serialized_end=3718 + _globals['_ENTITYDESCRIPTION']._serialized_start=3721 + _globals['_ENTITYDESCRIPTION']._serialized_end=3939 + _globals['_REGISTERENTITYRESULT']._serialized_start=3941 + _globals['_REGISTERENTITYRESULT']._serialized_end=3982 + _globals['_UNREGISTERENTITY']._serialized_start=3984 + _globals['_UNREGISTERENTITY']._serialized_end=4029 + _globals['_UNREGISTERENTITYRESULT']._serialized_start=4031 + _globals['_UNREGISTERENTITYRESULT']._serialized_end=4067 + _globals['_STATECHANGED']._serialized_start=4070 + _globals['_STATECHANGED']._serialized_end=4226 + _globals['_REGISTERSERVICE']._serialized_start=4228 + _globals['_REGISTERSERVICE']._serialized_end=4349 + _globals['_REGISTERSERVICERESULT']._serialized_start=4351 + _globals['_REGISTERSERVICERESULT']._serialized_end=4405 + _globals['_UNREGISTERSERVICE']._serialized_start=4407 + _globals['_UNREGISTERSERVICE']._serialized_end=4459 + _globals['_UNREGISTERSERVICERESULT']._serialized_start=4461 + _globals['_UNREGISTERSERVICERESULT']._serialized_end=4515 + _globals['_FIREEVENT']._serialized_start=4517 + _globals['_FIREEVENT']._serialized_end=4633 + _globals['_STORELOAD']._serialized_start=4635 + _globals['_STORELOAD']._serialized_end=4659 + _globals['_STORELOADRESULT']._serialized_start=4661 + _globals['_STORELOADRESULT']._serialized_end=4731 + _globals['_STORESAVE']._serialized_start=4733 + _globals['_STORESAVE']._serialized_end=4796 + _globals['_STORESAVERESULT']._serialized_start=4798 + _globals['_STORESAVERESULT']._serialized_end=4827 + _globals['_STOREREMOVE']._serialized_start=4829 + _globals['_STOREREMOVE']._serialized_end=4855 + _globals['_STOREREMOVERESULT']._serialized_start=4857 + _globals['_STOREREMOVERESULT']._serialized_end=4888 # @@protoc_insertion_point(module_scope) diff --git a/sandbox/hass_client/hass_client/_proto/sandbox_pb2.pyi b/sandbox/hass_client/hass_client/_proto/sandbox_pb2.pyi index 56a7742da87e..1101c5b54192 100644 --- a/sandbox/hass_client/hass_client/_proto/sandbox_pb2.pyi +++ b/sandbox/hass_client/hass_client/_proto/sandbox_pb2.pyi @@ -183,6 +183,24 @@ class CallServiceResult(_message.Message): response: ServiceResponse def __init__(self, response: _Optional[_Union[ServiceResponse, _Mapping]] = ...) -> None: ... +class EntityQuery(_message.Message): + __slots__ = ("sandbox_entity_id", "method", "args", "context_id") + SANDBOX_ENTITY_ID_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + ARGS_FIELD_NUMBER: _ClassVar[int] + CONTEXT_ID_FIELD_NUMBER: _ClassVar[int] + sandbox_entity_id: str + method: str + args: _struct_pb2.Struct + context_id: str + def __init__(self, sandbox_entity_id: _Optional[str] = ..., method: _Optional[str] = ..., args: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context_id: _Optional[str] = ...) -> None: ... + +class EntityQueryResult(_message.Message): + __slots__ = ("result",) + RESULT_FIELD_NUMBER: _ClassVar[int] + result: _struct_pb2.Struct + def __init__(self, result: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... + class GetTranslations(_message.Message): __slots__ = ("language", "domains") LANGUAGE_FIELD_NUMBER: _ClassVar[int] diff --git a/sandbox/hass_client/hass_client/entry_runner.py b/sandbox/hass_client/hass_client/entry_runner.py index f468f83e6f64..d382ea686211 100644 --- a/sandbox/hass_client/hass_client/entry_runner.py +++ b/sandbox/hass_client/hass_client/entry_runner.py @@ -14,6 +14,9 @@ from typing import Any from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import DATA_INSTANCES from homeassistant.helpers.json import json_bytes from homeassistant.util.json import json_loads @@ -21,7 +24,12 @@ from ._proto import sandbox_pb2 as pb from .approved_domains import ApprovedDomains from .channel import Channel from .messages import dict_to_struct, struct_to_dict -from .protocol import MSG_CALL_SERVICE, MSG_ENTRY_SETUP, MSG_ENTRY_UNLOAD +from .protocol import ( + MSG_CALL_SERVICE, + MSG_ENTITY_QUERY, + MSG_ENTRY_SETUP, + MSG_ENTRY_UNLOAD, +) from .sources import FetchPrimitive, SandboxSourceError, async_ensure_integration_source _LOGGER = logging.getLogger(__name__) @@ -53,6 +61,7 @@ class EntryRunner: channel.register(MSG_ENTRY_SETUP, self._handle_entry_setup) channel.register(MSG_ENTRY_UNLOAD, self._handle_entry_unload) channel.register(MSG_CALL_SERVICE, self._handle_call_service) + channel.register(MSG_ENTITY_QUERY, self._handle_entity_query) async def _handle_entry_setup(self, msg: pb.EntrySetup) -> pb.EntrySetupResult: """Build a :class:`ConfigEntry`, register it, and call async_setup.""" @@ -153,6 +162,41 @@ class EntryRunner: ) return pb.CallServiceResult() + async def _handle_entity_query(self, msg: pb.EntityQuery) -> pb.EntityQueryResult: + """Invoke a server-side entity method and return its serialised result. + + Resolves the entity on the private hass by ``sandbox_entity_id``, + ``getattr``s the named method, and awaits it with the decoded kwargs. + The return is wrapped as ``{"value": …}`` and run through the same + ``as_dict``-aware JSON encoder used for service responses, so rich + types (``SearchMedia``, ``BrowseMedia``, ``Segment`` dataclasses) + cross verbatim. A raised exception (``ServiceValidationError`` / + ``BrowseError`` / ``SearchError`` / ``HomeAssistantError`` / + ``vol.Invalid``) propagates as a channel error frame, exactly like + ``call_service``, so main rebuilds the same error shape. + """ + entity = _resolve_entity(self.hass, msg.sandbox_entity_id) + method = getattr(entity, msg.method, None) + if not callable(method): + raise HomeAssistantError( + f"entity_query: {msg.sandbox_entity_id!r} has no method" + f" {msg.method!r}" + ) + value = await method(**struct_to_dict(msg.args)) + result = pb.EntityQueryResult() + result.result.CopyFrom(dict_to_struct(_json_safe({"value": value}))) + return result + + +def _resolve_entity(hass: HomeAssistant, entity_id: str) -> Entity: + """Return the live entity object for ``entity_id`` or raise.""" + domain = entity_id.split(".", 1)[0] + component = hass.data.get(DATA_INSTANCES, {}).get(domain) + entity = component.get_entity(entity_id) if component is not None else None + if entity is None: + raise HomeAssistantError(f"entity_query: unknown entity_id {entity_id!r}") + return entity + def _json_safe(result: Any) -> dict[str, Any]: """Coerce a service response into a plain JSON-safe dict. diff --git a/sandbox/hass_client/hass_client/messages.py b/sandbox/hass_client/hass_client/messages.py index 797bf2c9738a..5d2cc4dd2bab 100644 --- a/sandbox/hass_client/hass_client/messages.py +++ b/sandbox/hass_client/hass_client/messages.py @@ -38,6 +38,7 @@ REGISTRY: dict[str, tuple[type[Message], type[Message] | None]] = { "sandbox/entry_setup": (pb.EntrySetup, pb.EntrySetupResult), "sandbox/entry_unload": (pb.EntryUnload, pb.EntryUnloadResult), "sandbox/call_service": (pb.CallService, pb.CallServiceResult), + "sandbox/entity_query": (pb.EntityQuery, pb.EntityQueryResult), "sandbox/get_translations": (pb.GetTranslations, pb.GetTranslationsResult), "sandbox/shutdown": (pb.Shutdown, pb.ShutdownResult), "sandbox/ping": (pb.Ping, pb.PingResult), diff --git a/sandbox/hass_client/hass_client/protocol.py b/sandbox/hass_client/hass_client/protocol.py index c1db12b16b2c..bf8dd918d452 100644 --- a/sandbox/hass_client/hass_client/protocol.py +++ b/sandbox/hass_client/hass_client/protocol.py @@ -18,6 +18,7 @@ MSG_READY: Final = "sandbox/ready" MSG_ENTRY_SETUP: Final = "sandbox/entry_setup" MSG_ENTRY_UNLOAD: Final = "sandbox/entry_unload" MSG_CALL_SERVICE: Final = "sandbox/call_service" +MSG_ENTITY_QUERY: Final = "sandbox/entity_query" MSG_GET_TRANSLATIONS: Final = "sandbox/get_translations" MSG_SHUTDOWN: Final = "sandbox/shutdown" @@ -35,6 +36,7 @@ MSG_STORE_REMOVE: Final = "sandbox/store_remove" __all__ = [ "MSG_CALL_SERVICE", + "MSG_ENTITY_QUERY", "MSG_ENTRY_SETUP", "MSG_ENTRY_UNLOAD", "MSG_FIRE_EVENT", diff --git a/sandbox/proto/sandbox.proto b/sandbox/proto/sandbox.proto index 4c42669dd83a..8b3c2c941b0c 100644 --- a/sandbox/proto/sandbox.proto +++ b/sandbox/proto/sandbox.proto @@ -153,6 +153,27 @@ message CallServiceResult { optional ServiceResponse response = 1; // unset when no response returned } +// --- entity_query (main -> sandbox) --------------------------------------- + +// Generic request/response RPC for the server-side entity queries that have +// NO SupportsResponse service to ride (media search, release notes, vacuum +// segments, the WS-only calendar event edits). Main names the entity + method +// and passes the kwargs; the sandbox invokes the real entity method and +// returns the serialised result. Ops that DO map to a SupportsResponse +// service use call_service instead — see docs/query-shaped-rpcs.md. +message EntityQuery { + string sandbox_entity_id = 1; + string method = 2; // e.g. "async_search_media" + google.protobuf.Struct args = 3; // kwargs, dynamic + optional string context_id = 4; // same wire-safe id rule as CallService +} + +message EntityQueryResult { + // Wrapped as {"value": } so scalar / list / None returns are all + // representable (a Struct's top level must be an object). + google.protobuf.Struct result = 1; +} + // --- get_translations (main -> sandbox) ----------------------------------- // Pull a sandboxed integration's frontend translation strings. Main issues