syntax = "proto3";

import "api_options.proto";

service APIConnection {
  rpc hello (HelloRequest) returns (HelloResponse) {
    option (needs_setup_connection) = false;
    option (needs_authentication) = false;
  }
  // REMOVED in ESPHome 2026.1.0: rpc authenticate (AuthenticationRequest) returns (AuthenticationResponse)
  rpc disconnect (DisconnectRequest) returns (DisconnectResponse) {
    option (needs_setup_connection) = false;
    option (needs_authentication) = false;
  }
  rpc ping (PingRequest) returns (PingResponse) {
    option (needs_setup_connection) = false;
    option (needs_authentication) = false;
  }
  rpc device_info (DeviceInfoRequest) returns (DeviceInfoResponse) {
    option (needs_authentication) = false;
  }
  rpc list_entities (ListEntitiesRequest) returns (void) {}
  rpc subscribe_states (SubscribeStatesRequest) returns (void) {}
  rpc subscribe_logs (SubscribeLogsRequest) returns (void) {}
  rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {}
  rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {}
  rpc execute_service (ExecuteServiceRequest) returns (void) {}
  rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {}

  rpc button_command (ButtonCommandRequest) returns (void) {}
  rpc camera_image (CameraImageRequest) returns (void) {}
  rpc climate_command (ClimateCommandRequest) returns (void) {}
  rpc cover_command (CoverCommandRequest) returns (void) {}
  rpc date_command (DateCommandRequest) returns (void) {}
  rpc datetime_command (DateTimeCommandRequest) returns (void) {}
  rpc fan_command (FanCommandRequest) returns (void) {}
  rpc light_command (LightCommandRequest) returns (void) {}
  rpc lock_command (LockCommandRequest) returns (void) {}
  rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
  rpc number_command (NumberCommandRequest) returns (void) {}
  rpc select_command (SelectCommandRequest) returns (void) {}
  rpc siren_command (SirenCommandRequest) returns (void) {}
  rpc switch_command (SwitchCommandRequest) returns (void) {}
  rpc text_command (TextCommandRequest) returns (void) {}
  rpc time_command (TimeCommandRequest) returns (void) {}
  rpc update_command (UpdateCommandRequest) returns (void) {}
  rpc valve_command (ValveCommandRequest) returns (void) {}
  rpc water_heater_command (WaterHeaterCommandRequest) returns (void) {}

  rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
  rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
  rpc bluetooth_gatt_get_services(BluetoothGATTGetServicesRequest) returns (void) {}
  rpc bluetooth_gatt_read(BluetoothGATTReadRequest) returns (void) {}
  rpc bluetooth_gatt_write(BluetoothGATTWriteRequest) returns (void) {}
  rpc bluetooth_gatt_read_descriptor(BluetoothGATTReadDescriptorRequest) returns (void) {}
  rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
  rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
  rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
  rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
  rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {}
  rpc bluetooth_set_connection_params(BluetoothSetConnectionParamsRequest) returns (BluetoothSetConnectionParamsResponse) {}

  rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
  rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
  rpc voice_assistant_set_configuration(VoiceAssistantSetConfiguration) returns (void) {}

  rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {}

  rpc zwave_proxy_frame(ZWaveProxyFrame) returns (void) {}
  rpc zwave_proxy_request(ZWaveProxyRequest) returns (void) {}

  rpc infrared_rf_transmit_raw_timings(InfraredRFTransmitRawTimingsRequest) returns (void) {}

  rpc serial_proxy_configure(SerialProxyConfigureRequest) returns (void) {}
  rpc serial_proxy_write(SerialProxyWriteRequest) returns (void) {}
  rpc serial_proxy_set_modem_pins(SerialProxySetModemPinsRequest) returns (void) {}
  rpc serial_proxy_get_modem_pins(SerialProxyGetModemPinsRequest) returns (void) {}
  rpc serial_proxy_request(SerialProxyRequest) returns (void) {}
}


// ==================== BASE PACKETS ====================

// The Home Assistant protocol is structured as a simple
// TCP socket with short binary messages encoded in the protocol buffers format
// First, a message in this protocol has a specific format:
//  * A zero byte.
//  * VarInt denoting the size of the message object. (type is not part of this)
//  * VarInt denoting the type of message.
//  * The message object encoded as a ProtoBuf message

// The connection is established in 2 steps:
//  * First, the client connects to the server and sends a "Hello Request" identifying itself
//  * The server responds with a "Hello Response" and the connection is authenticated
// If anything in this initial process fails, the connection must immediately closed
// by both sides and _no_ disconnection message is to be sent.
// Note: Password authentication via AuthenticationRequest/AuthenticationResponse (message IDs 3, 4)
// was removed in ESPHome 2026.1.0. Those message IDs are reserved and should not be reused.

// Message sent at the beginning of each connection
// Can only be sent by the client and only at the beginning of the connection
message HelloRequest {
  option (id) = 1;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;

  // Description of client (like User Agent)
  // For example "Home Assistant"
  // Not strictly necessary to send but nice for debugging
  // purposes.
  string client_info = 1;
  uint32 api_version_major = 2;
  uint32 api_version_minor = 3;
}

// Confirmation of successful connection request.
// Can only be sent by the server and only at the beginning of the connection
message HelloResponse {
  option (id) = 2;
  option (source) = SOURCE_SERVER;
  option (no_delay) = true;

  // The version of the API to use. The _client_ (for example Home Assistant) needs to check
  // for compatibility and if necessary adopt to an older API.
  // Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_
  // Minor is for breaking changes in individual messages - a mismatch will lead to a warning message
  uint32 api_version_major = 1;
  uint32 api_version_minor = 2;

  // A string identifying the server (ESP); like client info this may be empty
  // and only exists for debugging/logging purposes.
  // Currently set to ESPHOME_VERSION string literal.
  string server_info = 3 [(max_data_length) = 32, (force) = true];

  // The name of the server (App.get_name() - device hostname)
  // max_data_length matches ESPHOME_DEVICE_NAME_MAX_LEN (validated by validate_hostname)
  string name = 4 [(max_data_length) = 31, (force) = true];
}

// DEPRECATED in ESPHome 2026.1.0 - Password authentication is no longer supported.
// These messages are kept for protocol documentation but are not processed by the server.
// Use noise encryption instead: https://esphome.io/components/api/#configuration-variables
message AuthenticationRequest {
  option (id) = 3;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;
  option deprecated = true;

  string password = 1;
}

message AuthenticationResponse {
  option (id) = 4;
  option (source) = SOURCE_SERVER;
  option (no_delay) = true;
  option deprecated = true;

  bool invalid_password = 1;
}

// Request to close the connection.
// Can be sent by both the client and server
message DisconnectRequest {
  option (id) = 5;
  option (source) = SOURCE_BOTH;
  option (no_delay) = true;

  // Do not close the connection before the acknowledgement arrives
}

message DisconnectResponse {
  option (id) = 6;
  option (source) = SOURCE_BOTH;
  option (no_delay) = true;

  // Empty - Both parties are required to close the connection after this
  // message has been received.
}

message PingRequest {
  option (id) = 7;
  option (source) = SOURCE_BOTH;
  // Empty
}

message PingResponse {
  option (id) = 8;
  option (source) = SOURCE_BOTH;
  // Empty
}

message DeviceInfoRequest {
  option (id) = 9;
  option (source) = SOURCE_CLIENT;
  // Empty
}

message AreaInfo {
  uint32 area_id = 1;
  // max_data_length matches core/config.FRIENDLY_NAME_MAX_LEN via AREA_SCHEMA
  string name = 2 [(max_data_length) = 120, (force) = true];
}

message DeviceInfo {
  uint32 device_id = 1;
  // max_data_length matches core/config.FRIENDLY_NAME_MAX_LEN via DEVICE_SCHEMA
  string name = 2 [(max_data_length) = 120, (force) = true];
  uint32 area_id = 3;
}

enum SerialProxyPortType {
  SERIAL_PROXY_PORT_TYPE_TTL = 0;
  SERIAL_PROXY_PORT_TYPE_RS232 = 1;
  SERIAL_PROXY_PORT_TYPE_RS485 = 2;
}

message SerialProxyInfo {
  string name = 1;                    // Human-readable port name
  SerialProxyPortType port_type = 2;  // Port type (RS232, RS485)
}

// DeviceInfoResponse max_data_length values:
//   name = 31 (ESPHOME_DEVICE_NAME_MAX_LEN, validated by validate_hostname)
//   friendly_name = 120 (core/config.FRIENDLY_NAME_MAX_LEN)
//   mac_address/bluetooth_mac_address = 17 (MAC_ADDRESS_PRETTY_BUFFER_SIZE - 1, constexpr)
//   esphome_version = 32 (ESPHOME_VERSION string literal)
//   compilation_time = 25 (Application::BUILD_TIME_STR_SIZE - 1, constexpr)
//   manufacturer = 20 (longest hardcoded literal: "Nordic Semiconductor")
//   model = 127 (core/config.BOARD_MAX_LENGTH, validated in platform schemas)
//   project_name/project_version = 127 (core/config.PROJECT_MAX_LENGTH)
//   suggested_area = 120 (core/config.FRIENDLY_NAME_MAX_LEN via AREA_SCHEMA)
message DeviceInfoResponse {
  option (id) = 10;
  option (source) = SOURCE_SERVER;

  // Deprecated in ESPHome 2026.1.0, but kept for backward compatibility
  // with older ESPHome versions that still send this field.
  bool uses_password = 1 [deprecated = true];

  // The name of the node, given by "App.set_name()" - device hostname
  string name = 2 [(max_data_length) = 31, (force) = true];

  // The mac address of the device. For example "AC:BC:32:89:0E:A9"
  string mac_address = 3 [(max_data_length) = 17, (force) = true];

  // A string describing the ESPHome version. For example "1.10.0"
  string esphome_version = 4 [(max_data_length) = 32, (force) = true];

  // A string describing the date of compilation, this is generated by the compiler
  // and therefore may not be in the same format all the time.
  // If the user isn't using ESPHome, this will also not be set.
  string compilation_time = 5 [(max_data_length) = 25, (force) = true];

  // The model of the board. For example NodeMCU
  // max_data_length matches core/config.BOARD_MAX_LENGTH (validated in platform schemas)
  string model = 6 [(max_data_length) = 127, (force) = true];

  bool has_deep_sleep = 7 [(field_ifdef) = "USE_DEEP_SLEEP"];

  // The esphome project details if set
  // max_data_length matches core/config.PROJECT_MAX_LENGTH
  string project_name = 8 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME"];
  string project_version = 9 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME"];

  uint32 webserver_port = 10 [(field_ifdef) = "USE_WEBSERVER"];

  // Deprecated in API version 1.9
  uint32 legacy_bluetooth_proxy_version = 11 [deprecated=true, (field_ifdef) = "USE_BLUETOOTH_PROXY"];
  uint32 bluetooth_proxy_feature_flags = 15 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];

  string manufacturer = 12 [(max_data_length) = 20, (force) = true];

  string friendly_name = 13 [(max_data_length) = 120, (force) = true];

  // Deprecated in API version 1.10
  uint32 legacy_voice_assistant_version = 14 [deprecated=true, (field_ifdef) = "USE_VOICE_ASSISTANT"];
  uint32 voice_assistant_feature_flags = 17 [(field_ifdef) = "USE_VOICE_ASSISTANT"];

  string suggested_area = 16 [(max_data_length) = 120, (force) = true, (field_ifdef) = "USE_AREAS"];

  // The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA"
  string bluetooth_mac_address = 18 [(max_data_length) = 17, (force) = true, (field_ifdef) = "USE_BLUETOOTH_PROXY"];

  // Supports receiving and saving api encryption key
  bool api_encryption_supported = 19 [(field_ifdef) = "USE_API_NOISE"];

  repeated DeviceInfo devices = 20 [(field_ifdef) = "USE_DEVICES", (fixed_array_size_define) = "ESPHOME_DEVICE_COUNT"];
  repeated AreaInfo areas = 21 [(field_ifdef) = "USE_AREAS", (fixed_array_size_define) = "ESPHOME_AREA_COUNT"];

  // Top-level area info to phase out suggested_area
  AreaInfo area = 22 [(field_ifdef) = "USE_AREAS"];

  // Indicates if Z-Wave proxy support is available and features supported
  uint32 zwave_proxy_feature_flags = 23 [(field_ifdef) = "USE_ZWAVE_PROXY"];
  uint32 zwave_home_id = 24 [(field_ifdef) = "USE_ZWAVE_PROXY"];

  // Serial proxy instance metadata
  repeated SerialProxyInfo serial_proxies = 25 [(field_ifdef) = "USE_SERIAL_PROXY", (fixed_array_size_define) = "SERIAL_PROXY_COUNT"];
}

message ListEntitiesRequest {
  option (id) = 11;
  option (source) = SOURCE_CLIENT;
  // Empty
}
message ListEntitiesDoneResponse {
  option (id) = 19;
  option (source) = SOURCE_SERVER;
  option (no_delay) = true;
  // Empty
}
message SubscribeStatesRequest {
  option (id) = 20;
  option (source) = SOURCE_CLIENT;
  // Empty
}

// ==================== COMMON =====================

enum EntityCategory {
  ENTITY_CATEGORY_NONE = 0;
  ENTITY_CATEGORY_CONFIG = 1;
  ENTITY_CATEGORY_DIAGNOSTIC = 2;
}

// Entity field max_data_length values match Python validation constants:
//   name/object_id = 120 (config_validation.NAME_MAX_LENGTH)
//   icon = 63 (core/config.ICON_MAX_LENGTH)
//   device_class = 47 (core/config.DEVICE_CLASS_MAX_LENGTH)
//   unit_of_measurement = 63 (core/config.UNIT_OF_MEASUREMENT_MAX_LENGTH)

// ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse {
  option (id) = 12;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BINARY_SENSOR";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string device_class = 5 [(max_data_length) = 47];
  bool is_status_binary_sensor = 6;
  bool disabled_by_default = 7;
  string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 9;
  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message BinarySensorStateResponse {
  option (id) = 21;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BINARY_SENSOR";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  // If the binary sensor does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== COVER ====================
message ListEntitiesCoverResponse {
  option (id) = 13;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_COVER";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  bool assumed_state = 5;
  bool supports_position = 6;
  bool supports_tilt = 7;
  string device_class = 8 [(max_data_length) = 47];
  bool disabled_by_default = 9;
  string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 11;
  bool supports_stop = 12;
  uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}

// Deprecated in API version 1.1
enum LegacyCoverState {
  option deprecated = true;
  LEGACY_COVER_STATE_OPEN = 0;
  LEGACY_COVER_STATE_CLOSED = 1;
}
enum CoverOperation {
  COVER_OPERATION_IDLE = 0;
  COVER_OPERATION_IS_OPENING = 1;
  COVER_OPERATION_IS_CLOSING = 2;
}
message CoverStateResponse {
  option (id) = 22;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_COVER";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  // legacy: state has been removed in 1.13
  // clients/servers must still send/accept it until the next protocol change
  // Deprecated in API version 1.1
  LegacyCoverState legacy_state = 2 [deprecated=true];

  float position = 3;
  float tilt = 4;
  CoverOperation current_operation = 5;
  uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}

// Deprecated in API version 1.1
enum LegacyCoverCommand {
  option deprecated = true;
  LEGACY_COVER_COMMAND_OPEN = 0;
  LEGACY_COVER_COMMAND_CLOSE = 1;
  LEGACY_COVER_COMMAND_STOP = 2;
}
message CoverCommandRequest {
  option (id) = 30;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_COVER";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];

  // legacy: command has been removed in 1.13
  // clients/servers must still send/accept it until the next protocol change
  // Deprecated in API version 1.1
  bool has_legacy_command = 2 [deprecated=true];
  // Deprecated in API version 1.1
  LegacyCoverCommand legacy_command = 3 [deprecated=true];

  bool has_position = 4;
  float position = 5;
  bool has_tilt = 6;
  float tilt = 7;
  bool stop = 8;
  uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== FAN ====================
message ListEntitiesFanResponse {
  option (id) = 14;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_FAN";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  bool supports_oscillation = 5;
  bool supports_speed = 6;
  bool supports_direction = 7;
  int32 supported_speed_count = 8;
  bool disabled_by_default = 9;
  string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 11;
  repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "std::vector<const char *>"];
  uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}
// Deprecated in API version 1.6 - only used in deprecated fields
enum FanSpeed {
  option deprecated = true;
  FAN_SPEED_LOW = 0;
  FAN_SPEED_MEDIUM = 1;
  FAN_SPEED_HIGH = 2;
}
enum FanDirection {
  FAN_DIRECTION_FORWARD = 0;
  FAN_DIRECTION_REVERSE = 1;
}
message FanStateResponse {
  option (id) = 23;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_FAN";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  bool oscillating = 3;
  // Deprecated in API version 1.6
  FanSpeed speed = 4 [deprecated=true];
  FanDirection direction = 5;
  int32 speed_level = 6;
  string preset_mode = 7;
  uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message FanCommandRequest {
  option (id) = 31;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_FAN";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool has_state = 2;
  bool state = 3;
  // Deprecated in API version 1.6
  bool has_speed = 4 [deprecated=true];
  // Deprecated in API version 1.6
  FanSpeed speed = 5 [deprecated=true];
  bool has_oscillating = 6;
  bool oscillating = 7;
  bool has_direction = 8;
  FanDirection direction = 9;
  bool has_speed_level = 10;
  int32 speed_level = 11;
  bool has_preset_mode = 12;
  string preset_mode = 13;
  uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== LIGHT ====================
enum ColorMode {
  COLOR_MODE_UNKNOWN = 0;
  COLOR_MODE_ON_OFF = 1;
  COLOR_MODE_LEGACY_BRIGHTNESS = 2;
  COLOR_MODE_BRIGHTNESS = 3;
  COLOR_MODE_WHITE = 7;
  COLOR_MODE_COLOR_TEMPERATURE = 11;
  COLOR_MODE_COLD_WARM_WHITE = 19;
  COLOR_MODE_RGB = 35;
  COLOR_MODE_RGB_WHITE = 39;
  COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
  COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
}
message ListEntitiesLightResponse {
  option (id) = 15;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_LIGHT";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"];
  // next four supports_* are for legacy clients, newer clients should use color modes
  // Deprecated in API version 1.6
  bool legacy_supports_brightness = 5 [deprecated=true];
  // Deprecated in API version 1.6
  bool legacy_supports_rgb = 6 [deprecated=true];
  // Deprecated in API version 1.6
  bool legacy_supports_white_value = 7 [deprecated=true];
  // Deprecated in API version 1.6
  bool legacy_supports_color_temperature = 8 [deprecated=true];
  float min_mireds = 9;
  float max_mireds = 10;
  repeated string effects = 11 [(container_pointer_no_template) = "FixedVector<const char *>"];
  bool disabled_by_default = 13;
  string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 15;
  uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
}
message LightStateResponse {
  option (id) = 24;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_LIGHT";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  float brightness = 3;
  ColorMode color_mode = 11;
  float color_brightness = 10;
  float red = 4;
  float green = 5;
  float blue = 6;
  float white = 7;
  float color_temperature = 8;
  float cold_white = 12;
  float warm_white = 13;
  string effect = 9;
  uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message LightCommandRequest {
  option (id) = 32;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_LIGHT";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool has_state = 2;
  bool state = 3;
  bool has_brightness = 4;
  float brightness = 5;
  bool has_color_mode = 22;
  ColorMode color_mode = 23;
  bool has_color_brightness = 20;
  float color_brightness = 21;
  bool has_rgb = 6;
  float red = 7;
  float green = 8;
  float blue = 9;
  bool has_white = 10;
  float white = 11;
  bool has_color_temperature = 12;
  float color_temperature = 13;
  bool has_cold_white = 24;
  float cold_white = 25;
  bool has_warm_white = 26;
  float warm_white = 27;
  bool has_transition_length = 14;
  uint32 transition_length = 15;
  bool has_flash_length = 16;
  uint32 flash_length = 17;
  bool has_effect = 18;
  string effect = 19;
  uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== SENSOR ====================
enum SensorStateClass {
  STATE_CLASS_NONE = 0;
  STATE_CLASS_MEASUREMENT = 1;
  STATE_CLASS_TOTAL_INCREASING = 2;
  STATE_CLASS_TOTAL = 3;
  STATE_CLASS_MEASUREMENT_ANGLE = 4;
}

// Deprecated in API version 1.5
enum SensorLastResetType {
  option deprecated = true;
  LAST_RESET_NONE = 0;
  LAST_RESET_NEVER = 1;
  LAST_RESET_AUTO = 2;
}

message ListEntitiesSensorResponse {
  option (id) = 16;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SENSOR";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  string unit_of_measurement = 6 [(max_data_length) = 63];
  int32 accuracy_decimals = 7;
  bool force_update = 8;
  string device_class = 9 [(max_data_length) = 47];
  SensorStateClass state_class = 10;
  // Last reset type removed in 2021.9.0
  // Deprecated in API version 1.5
  SensorLastResetType legacy_last_reset_type = 11 [deprecated=true];
  bool disabled_by_default = 12;
  EntityCategory entity_category = 13;
  uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message SensorStateResponse {
  option (id) = 25;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SENSOR";
  option (no_delay) = true;
  option (speed_optimized) = true;

  fixed32 key = 1 [(force) = true];
  float state = 2;
  // If the sensor does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== SWITCH ====================
message ListEntitiesSwitchResponse {
  option (id) = 17;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SWITCH";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool assumed_state = 6;
  bool disabled_by_default = 7;
  EntityCategory entity_category = 8;
  string device_class = 9 [(max_data_length) = 47];
  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message SwitchStateResponse {
  option (id) = 26;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SWITCH";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message SwitchCommandRequest {
  option (id) = 33;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SWITCH";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== TEXT SENSOR ====================
message ListEntitiesTextSensorResponse {
  option (id) = 18;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_TEXT_SENSOR";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  string device_class = 8 [(max_data_length) = 47];
  uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message TextSensorStateResponse {
  option (id) = 27;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_TEXT_SENSOR";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  string state = 2;
  // If the text sensor does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== SUBSCRIBE LOGS ====================
enum LogLevel {
  LOG_LEVEL_NONE = 0;
  LOG_LEVEL_ERROR = 1;
  LOG_LEVEL_WARN = 2;
  LOG_LEVEL_INFO = 3;
  LOG_LEVEL_CONFIG = 4;
  LOG_LEVEL_DEBUG = 5;
  LOG_LEVEL_VERBOSE = 6;
  LOG_LEVEL_VERY_VERBOSE = 7;
}
message SubscribeLogsRequest {
  option (id) = 28;
  option (source) = SOURCE_CLIENT;
  LogLevel level = 1;
  bool dump_config = 2;
}
message SubscribeLogsResponse {
  option (id) = 29;
  option (source) = SOURCE_SERVER;
  option (log) = false;
  option (no_delay) = false;
  option (speed_optimized) = true;

  LogLevel level = 1 [(force) = true];
  bytes message = 3 [(force) = true];
}

// ==================== NOISE ENCRYPTION ====================
message NoiseEncryptionSetKeyRequest {
  option (id) = 124;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_API_NOISE";

  bytes key = 1;
}

message NoiseEncryptionSetKeyResponse {
  option (id) = 125;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_API_NOISE";

  bool success = 1;
}

// ==================== HOMEASSISTANT.SERVICE ====================
message SubscribeHomeassistantServicesRequest {
  option (id) = 34;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES";
}

message HomeassistantServiceMap {
  string key = 1;
  string value = 2;
}

message HomeassistantActionRequest {
  option (id) = 35;
  option (source) = SOURCE_SERVER;
  option (no_delay) = true;
  option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES";

  string service = 1;
  repeated HomeassistantServiceMap data = 2 [(fixed_vector) = true];
  repeated HomeassistantServiceMap data_template = 3 [(fixed_vector) = true];
  repeated HomeassistantServiceMap variables = 4 [(fixed_vector) = true];
  bool is_event = 5;
  uint32 call_id = 6 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES"];
  bool wants_response = 7 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"];
  string response_template = 8 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"];
}

// Message sent by Home Assistant to ESPHome with service call response data
message HomeassistantActionResponse {
  option (id) = 130;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;
  option (ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES";

  uint32 call_id = 1; // Matches the call_id from HomeassistantActionRequest
  bool success = 2; // Whether the service call succeeded
  string error_message = 3; // Error message if success = false
  bytes response_data = 4 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"];
}

// ==================== IMPORT HOME ASSISTANT STATES ====================
// 1. Client sends SubscribeHomeAssistantStatesRequest
// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async)
// 3. Client sends HomeAssistantStateResponse for state changes.
message SubscribeHomeAssistantStatesRequest {
  option (id) = 38;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
}

message SubscribeHomeAssistantStateResponse {
  option (id) = 39;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
  string entity_id = 1;
  string attribute = 2;
  bool once = 3;
}

message HomeAssistantStateResponse {
  option (id) = 40;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;
  option (ifdef) = "USE_API_HOMEASSISTANT_STATES";

  string entity_id = 1;
  string state = 2;
  string attribute = 3;
}

// ==================== IMPORT TIME ====================
message GetTimeRequest {
  option (id) = 36;
  option (source) = SOURCE_SERVER;
}

enum DSTRuleType {
  DST_RULE_TYPE_NONE = 0;
  DST_RULE_TYPE_MONTH_WEEK_DAY = 1;
  DST_RULE_TYPE_JULIAN_NO_LEAP = 2;
  DST_RULE_TYPE_DAY_OF_YEAR = 3;
}

message DSTRule {
  option (source) = SOURCE_CLIENT;

  sint32 time_seconds = 1;
  uint32 day = 2;
  DSTRuleType type = 3;
  uint32 month = 4;
  uint32 week = 5;
  uint32 day_of_week = 6;
}

message ParsedTimezone {
  option (source) = SOURCE_CLIENT;

  sint32 std_offset_seconds = 1;
  sint32 dst_offset_seconds = 2;
  DSTRule dst_start = 3;
  DSTRule dst_end = 4;
}

message GetTimeResponse {
  option (id) = 37;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;

  fixed32 epoch_seconds = 1;
  string timezone = 2;
  ParsedTimezone parsed_timezone = 3;
}

// ==================== USER-DEFINES SERVICES ====================
enum ServiceArgType {
  SERVICE_ARG_TYPE_BOOL = 0;
  SERVICE_ARG_TYPE_INT = 1;
  SERVICE_ARG_TYPE_FLOAT = 2;
  SERVICE_ARG_TYPE_STRING = 3;
  SERVICE_ARG_TYPE_BOOL_ARRAY = 4;
  SERVICE_ARG_TYPE_INT_ARRAY = 5;
  SERVICE_ARG_TYPE_FLOAT_ARRAY = 6;
  SERVICE_ARG_TYPE_STRING_ARRAY = 7;
}
enum SupportsResponseType {
  SUPPORTS_RESPONSE_NONE = 0;
  SUPPORTS_RESPONSE_OPTIONAL = 1;
  SUPPORTS_RESPONSE_ONLY = 2;
  // Status-only response - reports success/error without data payload
  // Value is higher to avoid conflicts with future Home Assistant values
  SUPPORTS_RESPONSE_STATUS = 100;
}
message ListEntitiesServicesArgument {
  option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";
  string name = 1;
  ServiceArgType type = 2;
}
message ListEntitiesServicesResponse {
  option (id) = 41;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";

  string name = 1;
  fixed32 key = 2 [(force) = true];
  repeated ListEntitiesServicesArgument args = 3 [(fixed_vector) = true];
  SupportsResponseType supports_response = 4;
}
message ExecuteServiceArgument {
  option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";
  bool bool_ = 1;
  int32 legacy_int = 2;
  float float_ = 3;
  string string_ = 4;
  // ESPHome 1.14 (api v1.3) make int a signed value
  sint32 int_ = 5;
  repeated bool bool_array = 6 [packed=false, (fixed_vector) = true];
  repeated sint32 int_array = 7 [packed=false, (fixed_vector) = true];
  repeated float float_array = 8 [packed=false, (fixed_vector) = true];
  repeated string string_array = 9 [(fixed_vector) = true];
}
message ExecuteServiceRequest {
  option (id) = 42;
  option (source) = SOURCE_CLIENT;
  option (no_delay) = true;
  option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";

  fixed32 key = 1 [(force) = true];
  repeated ExecuteServiceArgument args = 2 [(fixed_vector) = true];
  uint32 call_id = 3 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES"];
  bool return_response = 4 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES"];
}

// Message sent by ESPHome to Home Assistant with service execution response data
message ExecuteServiceResponse {
  option (id) = 131;
  option (source) = SOURCE_SERVER;
  option (no_delay) = true;
  option (ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES";

  uint32 call_id = 1; // Matches the call_id from ExecuteServiceRequest
  bool success = 2; // Whether the service execution succeeded
  string error_message = 3; // Error message if success = false
  bytes response_data = 4 [(pointer_to_buffer) = true, (field_ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES_JSON"];
}

// ==================== CAMERA ====================
message ListEntitiesCameraResponse {
  option (id) = 43;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_CAMERA";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id
  bool disabled_by_default = 5;
  string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 7;
  uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}

message CameraImageResponse {
  option (id) = 44;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_CAMERA";

  fixed32 key = 1 [(force) = true];
  bytes data = 2;
  bool done = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message CameraImageRequest {
  option (id) = 45;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_CAMERA";
  option (no_delay) = true;

  bool single = 1;
  bool stream = 2;
}

// ==================== CLIMATE ====================
enum ClimateMode {
  CLIMATE_MODE_OFF = 0;
  CLIMATE_MODE_HEAT_COOL = 1;
  CLIMATE_MODE_COOL = 2;
  CLIMATE_MODE_HEAT = 3;
  CLIMATE_MODE_FAN_ONLY = 4;
  CLIMATE_MODE_DRY = 5;
  CLIMATE_MODE_AUTO = 6;
}
enum ClimateFanMode {
  CLIMATE_FAN_ON = 0;
  CLIMATE_FAN_OFF = 1;
  CLIMATE_FAN_AUTO = 2;
  CLIMATE_FAN_LOW = 3;
  CLIMATE_FAN_MEDIUM = 4;
  CLIMATE_FAN_HIGH = 5;
  CLIMATE_FAN_MIDDLE = 6;
  CLIMATE_FAN_FOCUS = 7;
  CLIMATE_FAN_DIFFUSE = 8;
  CLIMATE_FAN_QUIET = 9;
}
enum ClimateSwingMode {
  CLIMATE_SWING_OFF = 0;
  CLIMATE_SWING_BOTH = 1;
  CLIMATE_SWING_VERTICAL = 2;
  CLIMATE_SWING_HORIZONTAL = 3;
}
enum ClimateAction {
  CLIMATE_ACTION_OFF = 0;
  // values same as mode for readability
  CLIMATE_ACTION_COOLING = 2;
  CLIMATE_ACTION_HEATING = 3;
  CLIMATE_ACTION_IDLE = 4;
  CLIMATE_ACTION_DRYING = 5;
  CLIMATE_ACTION_FAN = 6;
  CLIMATE_ACTION_DEFROSTING = 7;
}
enum ClimatePreset {
  CLIMATE_PRESET_NONE = 0;
  CLIMATE_PRESET_HOME = 1;
  CLIMATE_PRESET_AWAY = 2;
  CLIMATE_PRESET_BOOST = 3;
  CLIMATE_PRESET_COMFORT = 4;
  CLIMATE_PRESET_ECO = 5;
  CLIMATE_PRESET_SLEEP = 6;
  CLIMATE_PRESET_ACTIVITY = 7;
}
message ListEntitiesClimateResponse {
  option (id) = 46;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_CLIMATE";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  bool supports_current_temperature = 5; // Deprecated: use feature_flags
  bool supports_two_point_target_temperature = 6; // Deprecated: use feature_flags
  repeated ClimateMode supported_modes = 7 [(container_pointer_no_template) = "climate::ClimateModeMask"];
  float visual_min_temperature = 8;
  float visual_max_temperature = 9;
  float visual_target_temperature_step = 10;
  // for older peer versions - in new system this
  // is if CLIMATE_PRESET_AWAY exists is supported_presets
  // Deprecated in API version 1.5
  bool legacy_supports_away = 11 [deprecated=true];
  bool supports_action = 12; // Deprecated: use feature_flags
  repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer_no_template) = "climate::ClimateFanModeMask"];
  repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer_no_template) = "climate::ClimateSwingModeMask"];
  repeated string supported_custom_fan_modes = 15 [(container_pointer_no_template) = "std::vector<const char *>"];
  repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"];
  repeated string supported_custom_presets = 17 [(container_pointer_no_template) = "std::vector<const char *>"];
  bool disabled_by_default = 18;
  string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  EntityCategory entity_category = 20;
  float visual_current_temperature_step = 21;
  bool supports_current_humidity = 22; // Deprecated: use feature_flags
  bool supports_target_humidity = 23; // Deprecated: use feature_flags
  float visual_min_humidity = 24;
  float visual_max_humidity = 25;
  uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"];
  uint32 feature_flags = 27;
}
message ClimateStateResponse {
  option (id) = 47;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_CLIMATE";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  ClimateMode mode = 2;
  float current_temperature = 3;
  float target_temperature = 4;
  float target_temperature_low = 5;
  float target_temperature_high = 6;
  // For older peers, equal to preset == CLIMATE_PRESET_AWAY
  // Deprecated in API version 1.5
  bool unused_legacy_away = 7 [deprecated=true];
  ClimateAction action = 8;
  ClimateFanMode fan_mode = 9;
  ClimateSwingMode swing_mode = 10;
  string custom_fan_mode = 11;
  ClimatePreset preset = 12;
  string custom_preset = 13;
  float current_humidity = 14;
  float target_humidity = 15;
  uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
}
message ClimateCommandRequest {
  option (id) = 48;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_CLIMATE";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool has_mode = 2;
  ClimateMode mode = 3;
  bool has_target_temperature = 4;
  float target_temperature = 5;
  bool has_target_temperature_low = 6;
  float target_temperature_low = 7;
  bool has_target_temperature_high = 8;
  float target_temperature_high = 9;
  // legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
  // Deprecated in API version 1.5
  bool unused_has_legacy_away = 10 [deprecated=true];
  // Deprecated in API version 1.5
  bool unused_legacy_away = 11 [deprecated=true];
  bool has_fan_mode = 12;
  ClimateFanMode fan_mode = 13;
  bool has_swing_mode = 14;
  ClimateSwingMode swing_mode = 15;
  bool has_custom_fan_mode = 16;
  string custom_fan_mode = 17;
  bool has_preset = 18;
  ClimatePreset preset = 19;
  bool has_custom_preset = 20;
  string custom_preset = 21;
  bool has_target_humidity = 22;
  float target_humidity = 23;
  uint32 device_id = 24 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== WATER_HEATER ====================
enum WaterHeaterMode {
  WATER_HEATER_MODE_OFF = 0;
  WATER_HEATER_MODE_ECO = 1;
  WATER_HEATER_MODE_ELECTRIC = 2;
  WATER_HEATER_MODE_PERFORMANCE = 3;
  WATER_HEATER_MODE_HIGH_DEMAND = 4;
  WATER_HEATER_MODE_HEAT_PUMP = 5;
  WATER_HEATER_MODE_GAS = 6;
}

message ListEntitiesWaterHeaterResponse {
  option (id) = 132;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_WATER_HEATER";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 5;
  EntityCategory entity_category = 6;
  uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
  float min_temperature = 8;
  float max_temperature = 9;
  float target_temperature_step = 10;
  repeated WaterHeaterMode supported_modes = 11 [(container_pointer_no_template) = "water_heater::WaterHeaterModeMask"];
  // Bitmask of WaterHeaterFeature flags
  uint32 supported_features = 12;
}

message WaterHeaterStateResponse {
  option (id) = 133;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_WATER_HEATER";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  float current_temperature = 2;
  float target_temperature = 3;
  WaterHeaterMode mode = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
  // Bitmask of current state flags (bit 0 = away, bit 1 = on)
  uint32 state = 6;
  float target_temperature_low = 7;
  float target_temperature_high = 8;
}

// Bitmask for WaterHeaterCommandRequest.has_fields
enum WaterHeaterCommandHasField {
  WATER_HEATER_COMMAND_HAS_NONE = 0;
  WATER_HEATER_COMMAND_HAS_MODE = 1;
  WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE = 2;
  WATER_HEATER_COMMAND_HAS_STATE = 4 [deprecated=true];
  WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW = 8;
  WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH = 16;
  WATER_HEATER_COMMAND_HAS_ON_STATE = 32;
  WATER_HEATER_COMMAND_HAS_AWAY_STATE = 64;
}

message WaterHeaterCommandRequest {
  option (id) = 134;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_WATER_HEATER";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  // Bitmask of which fields are set (see WaterHeaterCommandHasField)
  uint32 has_fields = 2;
  WaterHeaterMode mode = 3;
  float target_temperature = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
  // State flags bitmask (bit 0 = away, bit 1 = on)
  uint32 state = 6;
  float target_temperature_low = 7;
  float target_temperature_high = 8;
}

// ==================== NUMBER ====================
enum NumberMode {
  NUMBER_MODE_AUTO = 0;
  NUMBER_MODE_BOX = 1;
  NUMBER_MODE_SLIDER = 2;
}
message ListEntitiesNumberResponse {
  option (id) = 49;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_NUMBER";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  float min_value = 6;
  float max_value = 7;
  float step = 8;
  bool disabled_by_default = 9;
  EntityCategory entity_category = 10;
  string unit_of_measurement = 11 [(max_data_length) = 63];
  NumberMode mode = 12;
  string device_class = 13 [(max_data_length) = 47];
  uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message NumberStateResponse {
  option (id) = 50;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_NUMBER";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  float state = 2;
  // If the number does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message NumberCommandRequest {
  option (id) = 51;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_NUMBER";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  float state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== SELECT ====================
message ListEntitiesSelectResponse {
  option (id) = 52;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SELECT";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  repeated string options = 6 [(container_pointer_no_template) = "FixedVector<const char *>"];
  bool disabled_by_default = 7;
  EntityCategory entity_category = 8;
  uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message SelectStateResponse {
  option (id) = 53;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SELECT";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  string state = 2;
  // If the select does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message SelectCommandRequest {
  option (id) = 54;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SELECT";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  string state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== SIREN ====================
message ListEntitiesSirenResponse {
  option (id) = 55;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SIREN";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  repeated string tones = 7 [(container_pointer_no_template) = "FixedVector<const char *>"];
  bool supports_duration = 8;
  bool supports_volume = 9;
  EntityCategory entity_category = 10;
  uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}
message SirenStateResponse {
  option (id) = 56;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SIREN";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message SirenCommandRequest {
  option (id) = 57;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SIREN";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool has_state = 2;
  bool state = 3;
  bool has_tone = 4;
  string tone = 5;
  bool has_duration = 6;
  uint32 duration = 7;
  bool has_volume = 8;
  float volume = 9;
  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== LOCK ====================
enum LockState {
  LOCK_STATE_NONE = 0;
  LOCK_STATE_LOCKED = 1;
  LOCK_STATE_UNLOCKED = 2;
  LOCK_STATE_JAMMED = 3;
  LOCK_STATE_LOCKING = 4;
  LOCK_STATE_UNLOCKING = 5;
}
enum LockCommand  {
  LOCK_UNLOCK = 0;
  LOCK_LOCK = 1;
  LOCK_OPEN = 2;
}
message ListEntitiesLockResponse {
  option (id) = 58;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_LOCK";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  bool assumed_state = 8;

  bool supports_open = 9;
  bool requires_code = 10;

  // Not yet implemented:
  string code_format = 11;
  uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}
message LockStateResponse {
  option (id) = 59;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_LOCK";
  option (no_delay) = true;
  fixed32 key = 1 [(force) = true];
  LockState state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message LockCommandRequest {
  option (id) = 60;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_LOCK";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";
  fixed32 key = 1 [(force) = true];
  LockCommand command = 2;

  // Not yet implemented:
  bool has_code = 3;
  string code = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
  option (id) = 61;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BUTTON";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  string device_class = 8 [(max_data_length) = 47];
  uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message ButtonCommandRequest {
  option (id) = 62;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BUTTON";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  uint32 device_id = 2 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== MEDIA PLAYER ====================
enum MediaPlayerState {
  MEDIA_PLAYER_STATE_NONE = 0;
  MEDIA_PLAYER_STATE_IDLE = 1;
  MEDIA_PLAYER_STATE_PLAYING = 2;
  MEDIA_PLAYER_STATE_PAUSED = 3;
  MEDIA_PLAYER_STATE_ANNOUNCING = 4;
  MEDIA_PLAYER_STATE_OFF = 5;
  MEDIA_PLAYER_STATE_ON = 6;
}
enum MediaPlayerCommand {
  MEDIA_PLAYER_COMMAND_PLAY = 0;
  MEDIA_PLAYER_COMMAND_PAUSE = 1;
  MEDIA_PLAYER_COMMAND_STOP = 2;
  MEDIA_PLAYER_COMMAND_MUTE = 3;
  MEDIA_PLAYER_COMMAND_UNMUTE = 4;
  MEDIA_PLAYER_COMMAND_TOGGLE = 5;
  MEDIA_PLAYER_COMMAND_VOLUME_UP = 6;
  MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7;
  MEDIA_PLAYER_COMMAND_ENQUEUE = 8;
  MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9;
  MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10;
  MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11;
  MEDIA_PLAYER_COMMAND_TURN_ON = 12;
  MEDIA_PLAYER_COMMAND_TURN_OFF = 13;
}
enum MediaPlayerFormatPurpose {
  MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;
  MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1;
}
message MediaPlayerSupportedFormat {
  option (ifdef) = "USE_MEDIA_PLAYER";

  string format = 1;
  uint32 sample_rate = 2;
  uint32 num_channels = 3;
  MediaPlayerFormatPurpose purpose = 4;
  uint32 sample_bytes = 5;
}
message ListEntitiesMediaPlayerResponse {
  option (id) = 63;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_MEDIA_PLAYER";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;

  bool supports_pause = 8;

  repeated MediaPlayerSupportedFormat supported_formats = 9;

  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];

  uint32 feature_flags = 11;
}
message MediaPlayerStateResponse {
  option (id) = 64;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_MEDIA_PLAYER";
  option (no_delay) = true;
  fixed32 key = 1 [(force) = true];
  MediaPlayerState state = 2;
  float volume = 3;
  bool muted = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
message MediaPlayerCommandRequest {
  option (id) = 65;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_MEDIA_PLAYER";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];

  bool has_command = 2;
  MediaPlayerCommand command = 3;

  bool has_volume = 4;
  float volume = 5;

  bool has_media_url = 6;
  string media_url = 7;

  bool has_announcement = 8;
  bool announcement = 9;
  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== BLUETOOTH ====================
message SubscribeBluetoothLEAdvertisementsRequest {
  option (id) = 66;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint32 flags = 1;
}

// Deprecated - only used by deprecated BluetoothLEAdvertisementResponse
message BluetoothServiceData {
  option deprecated = true;
  string uuid = 1;
  // Deprecated in API version 1.7
  repeated uint32 legacy_data = 2 [deprecated=true];  // Removed in api version 1.7
  bytes data = 3;  // Added in api version 1.7
}
// Removed in ESPHome 2025.8.0 - use BluetoothLERawAdvertisementsResponse instead
message BluetoothLEAdvertisementResponse {
  option deprecated = true;
  option (id) = 67;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";
  option (no_delay) = true;

  uint64 address = 1;
  bytes name = 2;
  sint32 rssi = 3;

  repeated string service_uuids = 4;
  repeated BluetoothServiceData service_data = 5;
  repeated BluetoothServiceData manufacturer_data = 6;

  uint32 address_type = 7;
}

message BluetoothLERawAdvertisement {
  option (inline_encode) = true;
  uint64 address = 1 [(force) = true];
  sint32 rssi = 2 [(force) = true];
  uint32 address_type = 3 [(max_value) = 4];

  bytes data = 4 [(fixed_array_size) = 62, (force) = true];
}

message BluetoothLERawAdvertisementsResponse {
  option (id) = 93;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";
  option (no_delay) = true;
  option (speed_optimized) = true;

  repeated BluetoothLERawAdvertisement advertisements = 1 [(fixed_array_with_length_define) = "BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE"];
}

enum BluetoothDeviceRequestType {
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0 [deprecated = true]; // V1 removed, use V3 variants
  BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;
  BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2;
  BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4;
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5;
  BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6;
}

message BluetoothDeviceRequest {
  option (id) = 68;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  BluetoothDeviceRequestType request_type = 2;
  bool has_address_type = 3;  // Deprecated, should be removed in 2027.8 - https://github.com/esphome/esphome/pull/10318
  uint32 address_type = 4;
}

message BluetoothDeviceConnectionResponse {
  option (id) = 69;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  bool connected = 2;
  uint32 mtu = 3;
  int32 error = 4;
}

message BluetoothGATTGetServicesRequest {
  option (id) = 70;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
}

message BluetoothGATTDescriptor {
  repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
  uint32 handle = 2;

  // New field for efficient UUID (v1.12+)
  // Only one of uuid or short_uuid will be set.
  // short_uuid is used for both 16-bit and 32-bit UUIDs with v1.12+ clients.
  // 128-bit UUIDs always use the uuid field for backwards compatibility.
  uint32 short_uuid = 3;  // 16-bit or 32-bit UUID
}

message BluetoothGATTCharacteristic {
  repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
  uint32 handle = 2;
  uint32 properties = 3;
  repeated BluetoothGATTDescriptor descriptors = 4 [(fixed_vector) = true];

  // New field for efficient UUID (v1.12+)
  // Only one of uuid or short_uuid will be set.
  // short_uuid is used for both 16-bit and 32-bit UUIDs with v1.12+ clients.
  // 128-bit UUIDs always use the uuid field for backwards compatibility.
  uint32 short_uuid = 5;  // 16-bit or 32-bit UUID
}

message BluetoothGATTService {
  repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
  uint32 handle = 2;
  repeated BluetoothGATTCharacteristic characteristics = 3 [(fixed_vector) = true];

  // New field for efficient UUID (v1.12+)
  // Only one of uuid or short_uuid will be set.
  // short_uuid is used for both 16-bit and 32-bit UUIDs with v1.12+ clients.
  // 128-bit UUIDs always use the uuid field for backwards compatibility.
  uint32 short_uuid = 4;  // 16-bit or 32-bit UUID
}

message BluetoothGATTGetServicesResponse {
  option (id) = 71;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  repeated BluetoothGATTService services = 2;
}

message BluetoothGATTGetServicesDoneResponse {
  option (id) = 72;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
}

message BluetoothGATTReadRequest {
  option (id) = 73;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
}

message BluetoothGATTReadResponse {
  option (id) = 74;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;

  bytes data = 3;

}

message BluetoothGATTWriteRequest {
  option (id) = 75;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
  bool response = 3;

  bytes data = 4;
}

message BluetoothGATTReadDescriptorRequest {
  option (id) = 76;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
}

message BluetoothGATTWriteDescriptorRequest {
  option (id) = 77;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;

  bytes data = 3;
}

message BluetoothGATTNotifyRequest {
  option (id) = 78;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
  bool enable = 3;
}

message BluetoothGATTNotifyDataResponse {
  option (id) = 79;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;

  bytes data = 3;
}

message SubscribeBluetoothConnectionsFreeRequest {
  option (id) = 80;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";
}

message BluetoothConnectionsFreeResponse {
  option (id) = 81;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint32 free = 1;
  uint32 limit = 2;
  repeated uint64 allocated = 3 [
    (fixed_array_size_define) = "BLUETOOTH_PROXY_MAX_CONNECTIONS",
    (fixed_array_skip_zero) = true
  ];
}

message BluetoothGATTErrorResponse {
  option (id) = 82;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
  int32 error = 3;
}

message BluetoothGATTWriteResponse {
  option (id) = 83;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
}

message BluetoothGATTNotifyResponse {
  option (id) = 84;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 handle = 2;
}

message BluetoothDevicePairingResponse {
  option (id) = 85;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  bool paired = 2;
  int32 error = 3;
}

message BluetoothDeviceUnpairingResponse {
  option (id) = 86;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  bool success = 2;
  int32 error = 3;
}

message UnsubscribeBluetoothLEAdvertisementsRequest {
  option (id) = 87;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";
}

message BluetoothDeviceClearCacheResponse {
  option (id) = 88;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  bool success = 2;
  int32 error = 3;
}

enum BluetoothScannerState {
  BLUETOOTH_SCANNER_STATE_IDLE = 0;
  BLUETOOTH_SCANNER_STATE_STARTING = 1;
  BLUETOOTH_SCANNER_STATE_RUNNING = 2;
  BLUETOOTH_SCANNER_STATE_FAILED = 3;
  BLUETOOTH_SCANNER_STATE_STOPPING = 4;
  BLUETOOTH_SCANNER_STATE_STOPPED = 5;
}

enum BluetoothScannerMode {
  BLUETOOTH_SCANNER_MODE_PASSIVE = 0;
  BLUETOOTH_SCANNER_MODE_ACTIVE = 1;
}

message BluetoothScannerStateResponse {
  option(id) = 126;
  option(source) = SOURCE_SERVER;
  option(ifdef) = "USE_BLUETOOTH_PROXY";

  BluetoothScannerState state = 1;
  BluetoothScannerMode mode = 2;
  BluetoothScannerMode configured_mode = 3;
}

message BluetoothScannerSetModeRequest {
  option(id) = 127;
  option(source) = SOURCE_CLIENT;
  option(ifdef) = "USE_BLUETOOTH_PROXY";

  BluetoothScannerMode mode = 1;
}

// ==================== VOICE ASSISTANT ====================
enum VoiceAssistantSubscribeFlag {
  VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
  VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
}

message SubscribeVoiceAssistantRequest {
  option (id) = 89;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  bool subscribe = 1;
  uint32 flags = 2;
}

enum VoiceAssistantRequestFlag {
  VOICE_ASSISTANT_REQUEST_NONE = 0;
  VOICE_ASSISTANT_REQUEST_USE_VAD = 1;
  VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2;
}

message VoiceAssistantAudioSettings {
  uint32 noise_suppression_level = 1;
  uint32 auto_gain = 2;
  float volume_multiplier = 3;
}

message VoiceAssistantRequest {
  option (id) = 90;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  bool start = 1;
  string conversation_id = 2;
  uint32 flags = 3;
  VoiceAssistantAudioSettings audio_settings = 4;
  string wake_word_phrase = 5;
}

message VoiceAssistantResponse {
  option (id) = 91;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  uint32 port = 1;
  bool error = 2;
}

enum VoiceAssistantEvent {
  VOICE_ASSISTANT_ERROR = 0;
  VOICE_ASSISTANT_RUN_START = 1;
  VOICE_ASSISTANT_RUN_END = 2;
  VOICE_ASSISTANT_STT_START = 3;
  VOICE_ASSISTANT_STT_END = 4;
  VOICE_ASSISTANT_INTENT_START = 5;
  VOICE_ASSISTANT_INTENT_END = 6;
  VOICE_ASSISTANT_TTS_START = 7;
  VOICE_ASSISTANT_TTS_END = 8;
  VOICE_ASSISTANT_WAKE_WORD_START = 9;
  VOICE_ASSISTANT_WAKE_WORD_END = 10;
  VOICE_ASSISTANT_STT_VAD_START = 11;
  VOICE_ASSISTANT_STT_VAD_END = 12;
  VOICE_ASSISTANT_TTS_STREAM_START = 98;
  VOICE_ASSISTANT_TTS_STREAM_END = 99;
  VOICE_ASSISTANT_INTENT_PROGRESS = 100;
}

message VoiceAssistantEventData {
  string name = 1;
  string value = 2;
}

message VoiceAssistantEventResponse {
  option (id) = 92;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  VoiceAssistantEvent event_type = 1;
  repeated VoiceAssistantEventData data = 2;
}

message VoiceAssistantAudio {
  option (id) = 106;
  option (source) = SOURCE_BOTH;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  bytes data = 1 [(pointer_to_buffer) = true];
  bool end = 2;
}

enum VoiceAssistantTimerEvent {
  VOICE_ASSISTANT_TIMER_STARTED = 0;
  VOICE_ASSISTANT_TIMER_UPDATED = 1;
  VOICE_ASSISTANT_TIMER_CANCELLED = 2;
  VOICE_ASSISTANT_TIMER_FINISHED = 3;
}

message VoiceAssistantTimerEventResponse {
  option (id) = 115;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  VoiceAssistantTimerEvent event_type = 1;
  string timer_id = 2;
  string name = 3;
  uint32 total_seconds = 4;
  uint32 seconds_left = 5;
  bool is_active = 6;
}

message VoiceAssistantAnnounceRequest {
  option (id) = 119;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  string media_id = 1;
  string text = 2;
  string preannounce_media_id = 3;
  bool start_conversation = 4;
}

message VoiceAssistantAnnounceFinished {
  option (id) = 120;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  bool success = 1;
}

message VoiceAssistantWakeWord {
  string id = 1;
  string wake_word = 2;
  repeated string trained_languages = 3;
}

message VoiceAssistantExternalWakeWord {
  string id = 1;
  string wake_word = 2;
  repeated string trained_languages = 3;
  string model_type = 4;
  uint32 model_size = 5;
  string model_hash = 6;
  string url = 7;
}

message VoiceAssistantConfigurationRequest {
  option (id) = 121;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  repeated VoiceAssistantExternalWakeWord external_wake_words = 1;
}

message VoiceAssistantConfigurationResponse {
  option (id) = 122;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  repeated VoiceAssistantWakeWord available_wake_words = 1;
  repeated string active_wake_words = 2 [(container_pointer) = "std::vector"];
  uint32 max_active_wake_words = 3;
}

message VoiceAssistantSetConfiguration {
  option (id) = 123;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VOICE_ASSISTANT";

  repeated string active_wake_words = 1;
}

// ==================== ALARM CONTROL PANEL ====================
enum AlarmControlPanelState {
  ALARM_STATE_DISARMED = 0;
  ALARM_STATE_ARMED_HOME = 1;
  ALARM_STATE_ARMED_AWAY = 2;
  ALARM_STATE_ARMED_NIGHT = 3;
  ALARM_STATE_ARMED_VACATION = 4;
  ALARM_STATE_ARMED_CUSTOM_BYPASS = 5;
  ALARM_STATE_PENDING = 6;
  ALARM_STATE_ARMING = 7;
  ALARM_STATE_DISARMING = 8;
  ALARM_STATE_TRIGGERED = 9;
}

enum AlarmControlPanelStateCommand {
  ALARM_CONTROL_PANEL_DISARM = 0;
  ALARM_CONTROL_PANEL_ARM_AWAY = 1;
  ALARM_CONTROL_PANEL_ARM_HOME = 2;
  ALARM_CONTROL_PANEL_ARM_NIGHT = 3;
  ALARM_CONTROL_PANEL_ARM_VACATION = 4;
  ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5;
  ALARM_CONTROL_PANEL_TRIGGER = 6;
}

message ListEntitiesAlarmControlPanelResponse {
  option (id) = 94;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_ALARM_CONTROL_PANEL";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id
  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  uint32 supported_features = 8;
  bool requires_code = 9;
  bool requires_code_to_arm = 10;
  uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}

message AlarmControlPanelStateResponse {
  option (id) = 95;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_ALARM_CONTROL_PANEL";
  option (no_delay) = true;
  fixed32 key = 1 [(force) = true];
  AlarmControlPanelState state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

message AlarmControlPanelCommandRequest {
  option (id) = 96;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_ALARM_CONTROL_PANEL";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";
  fixed32 key = 1 [(force) = true];
  AlarmControlPanelStateCommand command = 2;
  string code = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}

// ===================== TEXT =====================
enum TextMode {
  TEXT_MODE_TEXT = 0;
  TEXT_MODE_PASSWORD = 1;
}
message ListEntitiesTextResponse {
  option (id) = 97;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_TEXT";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id
  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;

  uint32 min_length = 8;
  uint32 max_length = 9;
  string pattern = 10;
  TextMode mode = 11;
  uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}
message TextStateResponse {
  option (id) = 98;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_TEXT";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  string state = 2;
  // If the Text does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message TextCommandRequest {
  option (id) = 99;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_TEXT";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  string state = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}


// ==================== DATETIME DATE ====================
message ListEntitiesDateResponse {
  option (id) = 100;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_DATE";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message DateStateResponse {
  option (id) = 101;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_DATE";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  // If the date does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 2;
  uint32 year = 3;
  uint32 month = 4;
  uint32 day = 5;
  uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}
message DateCommandRequest {
  option (id) = 102;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_DATETIME_DATE";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  uint32 year = 2;
  uint32 month = 3;
  uint32 day = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== DATETIME TIME ====================
message ListEntitiesTimeResponse {
  option (id) = 103;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_TIME";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message TimeStateResponse {
  option (id) = 104;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_TIME";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  // If the time does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 2;
  uint32 hour = 3;
  uint32 minute = 4;
  uint32 second = 5;
  uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}
message TimeCommandRequest {
  option (id) = 105;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_DATETIME_TIME";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  uint32 hour = 2;
  uint32 minute = 3;
  uint32 second = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== EVENT ====================
message ListEntitiesEventResponse {
  option (id) = 107;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_EVENT";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  string device_class = 8 [(max_data_length) = 47];

  repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector<const char *>"];
  uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message EventResponse {
  option (id) = 108;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_EVENT";

  fixed32 key = 1 [(force) = true];
  string event_type = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== VALVE ====================
message ListEntitiesValveResponse {
  option (id) = 109;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_VALVE";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  string device_class = 8 [(max_data_length) = 47];

  bool assumed_state = 9;
  bool supports_position = 10;
  bool supports_stop = 11;
  uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}

enum ValveOperation {
  VALVE_OPERATION_IDLE = 0;
  VALVE_OPERATION_IS_OPENING = 1;
  VALVE_OPERATION_IS_CLOSING = 2;
}
message ValveStateResponse {
  option (id) = 110;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_VALVE";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  float position = 2;
  ValveOperation current_operation = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}

message ValveCommandRequest {
  option (id) = 111;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_VALVE";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  bool has_position = 2;
  float position = 3;
  bool stop = 4;
  uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== DATETIME DATETIME ====================
message ListEntitiesDateTimeResponse {
  option (id) = 112;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_DATETIME";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message DateTimeStateResponse {
  option (id) = 113;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_DATETIME_DATETIME";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  // If the datetime does not have a valid state yet.
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
  bool missing_state = 2;
  fixed32 epoch_seconds = 3;
  uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message DateTimeCommandRequest {
  option (id) = 114;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_DATETIME_DATETIME";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  fixed32 epoch_seconds = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== UPDATE ====================
message ListEntitiesUpdateResponse {
  option (id) = 116;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_UPDATE";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  reserved 4; // Deprecated: was string unique_id

  string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 6;
  EntityCategory entity_category = 7;
  string device_class = 8 [(max_data_length) = 47];
  uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message UpdateStateResponse {
  option (id) = 117;
  option (base_class) = "StateResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_UPDATE";
  option (no_delay) = true;

  fixed32 key = 1 [(force) = true];
  bool missing_state = 2;
  bool in_progress = 3;
  bool has_progress = 4;
  float progress = 5;
  string current_version = 6;
  string latest_version = 7;
  string title = 8;
  string release_summary = 9;
  string release_url = 10;
  uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}
enum UpdateCommand {
  UPDATE_COMMAND_NONE = 0;
  UPDATE_COMMAND_UPDATE = 1;
  UPDATE_COMMAND_CHECK = 2;
}
message UpdateCommandRequest {
  option (id) = 118;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_UPDATE";
  option (no_delay) = true;
  option (base_class) = "CommandProtoMessage";

  fixed32 key = 1 [(force) = true];
  UpdateCommand command = 2;
  uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

// ==================== Z-WAVE ====================

message ZWaveProxyFrame {
  option (id) = 128;
  option (source) = SOURCE_BOTH;
  option (ifdef) = "USE_ZWAVE_PROXY";
  option (no_delay) = true;

  bytes data = 1;
}

enum ZWaveProxyRequestType {
  ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0;
  ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1;
  ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2;
}
message ZWaveProxyRequest {
  option (id) = 129;
  option (source) = SOURCE_BOTH;
  option (ifdef) = "USE_ZWAVE_PROXY";

  ZWaveProxyRequestType type = 1;
  bytes data = 2;
}

// ==================== INFRARED ====================
// Note: Feature and capability flag enums are defined in
// esphome/components/infrared/infrared.h

// Listing of infrared instances
message ListEntitiesInfraredResponse {
  option (id) = 135;
  option (base_class) = "InfoResponseProtoMessage";
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_INFRARED";

  string object_id = 1 [(max_data_length) = 120, (force) = true];
  fixed32 key = 2 [(force) = true];
  string name = 3 [(max_data_length) = 120, (force) = true];
  string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
  bool disabled_by_default = 5;
  EntityCategory entity_category = 6;
  uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
  uint32 capabilities = 8;  // Bitfield of InfraredCapabilityFlags
  uint32 receiver_frequency = 9;  // Demodulation frequency of the IR receiver in Hz (0 = unspecified)
}

// Command to transmit infrared/RF data using raw timings
message InfraredRFTransmitRawTimingsRequest {
  option (id) = 136;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_IR_RF";

  uint32 device_id = 1 [(field_ifdef) = "USE_DEVICES"];
  fixed32 key = 2 [(force) = true];                                       // Key identifying the transmitter instance
  uint32 carrier_frequency = 3;                          // Carrier frequency in Hz
  uint32 repeat_count = 4;                               // Number of times to transmit (1 = once, 2 = twice, etc.)
  repeated sint32 timings = 5 [packed = true, (packed_buffer) = true];  // Raw timings in microseconds (zigzag-encoded): positive = mark (LED/TX on), negative = space (LED/TX off)
}

// Event message for received infrared/RF data
message InfraredRFReceiveEvent {
  option (id) = 137;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_IR_RF";
  option (no_delay) = true;

  uint32 device_id = 1 [(field_ifdef) = "USE_DEVICES"];
  fixed32 key = 2 [(force) = true];                                    // Key identifying the receiver instance
  repeated sint32 timings = 3 [packed = true, (container_pointer_no_template) = "std::vector<int32_t>"];  // Raw timings in microseconds (zigzag-encoded): alternating mark/space periods
}

// ==================== SERIAL PROXY ====================

enum SerialProxyParity {
  SERIAL_PROXY_PARITY_NONE = 0;
  SERIAL_PROXY_PARITY_EVEN = 1;
  SERIAL_PROXY_PARITY_ODD = 2;
}

// Configure UART parameters for a serial proxy instance
message SerialProxyConfigureRequest {
  option (id) = 138;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;          // Instance index (0-based)
  uint32 baudrate = 2;          // Baud rate in bits per second
  bool flow_control = 3;        // Enable hardware flow control
  SerialProxyParity parity = 4; // Parity setting
  uint32 stop_bits = 5;         // Number of stop bits (1 or 2)
  uint32 data_size = 6;         // Number of data bits (5-8)
}

// Data received from a serial device, forwarded to clients
message SerialProxyDataReceived {
  option (id) = 139;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SERIAL_PROXY";
  option (no_delay) = true;

  uint32 instance = 1;          // Instance index (0-based)
  bytes data = 2;               // Raw data received from the serial device
}

// Write data to a serial device
message SerialProxyWriteRequest {
  option (id) = 140;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SERIAL_PROXY";
  option (no_delay) = true;

  uint32 instance = 1;          // Instance index (0-based)
  bytes data = 2;               // Raw data to write to the serial device
}

// Set modem control pin states (RTS and DTR)
message SerialProxySetModemPinsRequest {
  option (id) = 141;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;          // Instance index (0-based)
  uint32 line_states = 2;       // Bitmask of SerialProxyLineStateFlags
}

// Request current modem control pin states
message SerialProxyGetModemPinsRequest {
  option (id) = 142;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;          // Instance index (0-based)
}

// Response with current modem control pin states
message SerialProxyGetModemPinsResponse {
  option (id) = 143;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;          // Instance index (0-based)
  uint32 line_states = 2;       // Bitmask of SerialProxyLineStateFlags
}

enum SerialProxyRequestType {
  SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE = 0;   // Subscribe to receive data from this serial proxy instance
  SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1; // Unsubscribe from this serial proxy instance
  SERIAL_PROXY_REQUEST_TYPE_FLUSH = 2;       // Flush the serial port (block until all TX data is sent)
}

enum SerialProxyStatus {
  SERIAL_PROXY_STATUS_OK = 0;              // Completed successfully; TX drain confirmed
  SERIAL_PROXY_STATUS_ASSUMED_SUCCESS = 1; // Platform cannot confirm TX drain; success assumed
  SERIAL_PROXY_STATUS_ERROR = 2;           // Driver or hardware error
  SERIAL_PROXY_STATUS_TIMEOUT = 3;         // Timed out before TX completed
  SERIAL_PROXY_STATUS_NOT_SUPPORTED = 4;   // Request type not supported by this instance
}

// Generic request message for simple serial proxy operations
message SerialProxyRequest {
  option (id) = 144;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;                  // Instance index (0-based)
  SerialProxyRequestType type = 2;      // Request type
}

// Response to a SerialProxyRequest (e.g. flush completion or failure)
message SerialProxyRequestResponse {
  option (id) = 147;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_SERIAL_PROXY";

  uint32 instance = 1;                  // Instance index (0-based)
  SerialProxyRequestType type = 2;      // Which request type this responds to
  SerialProxyStatus status = 3;         // Result status
  string error_message = 4;            // Additional detail on failure (optional)
}

// ==================== BLUETOOTH CONNECTION PARAMS ====================
message BluetoothSetConnectionParamsRequest {
  option (id) = 145;
  option (source) = SOURCE_CLIENT;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  uint32 min_interval = 2;  // units of 1.25ms
  uint32 max_interval = 3;  // units of 1.25ms
  uint32 latency = 4;
  uint32 timeout = 5;       // units of 10ms
}

message BluetoothSetConnectionParamsResponse {
  option (id) = 146;
  option (source) = SOURCE_SERVER;
  option (ifdef) = "USE_BLUETOOTH_PROXY";

  uint64 address = 1;
  int32 error = 2;
}
