diff --git a/esphome/__main__.py b/esphome/__main__.py index f166cfcd87..c32ebf301b 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -11,7 +11,7 @@ from esphome import const, writer, yaml_util import esphome.codegen as cg from esphome.config import iter_components, read_config, strip_default_ids from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \ - CONF_PASSWORD, CONF_PORT + CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.helpers import color, indent from esphome.py_compat import IS_PY2, safe_input @@ -163,6 +163,7 @@ def compile_program(args, config): def upload_using_esptool(config, port): path = CORE.firmware_bin cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', + '--baud', config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 115200), '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: diff --git a/esphome/api/api.proto b/esphome/api/api.proto deleted file mode 100644 index 37cb792959..0000000000 --- a/esphome/api/api.proto +++ /dev/null @@ -1,330 +0,0 @@ -syntax = "proto3"; - -// 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: -// * 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 4 steps: -// * First, the client connects to the server and sends a "Hello Request" identifying itself -// * The server responds with a "Hello Response" and selects the protocol version -// * After receiving this message, the client attempts to authenticate itself using -// the password and a "Connect Request" -// * The server responds with a "Connect Response" and notifies of invalid password. -// If anything in this initial process fails, the connection must immediately closed -// by both sides and _no_ disconnection message is to be sent. - -// 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 { - // Description of client (like User Agent) - // For example "Home Assistant" - // Not strictly necessary to send but nice for debugging - // purposes. - string client_info = 1; -} - -// Confirmation of successful connection request. -// Can only be sent by the server and only at the beginning of the connection -message HelloResponse { - // 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. - // For example "ESPHome v1.10.0 on ESP8266" - string server_info = 3; -} - -// Message sent at the beginning of each connection to authenticate the client -// Can only be sent by the client and only at the beginning of the connection -message ConnectRequest { - // The password to log in with - string password = 1; -} - -// Confirmation of successful connection. After this the connection is available for all traffic. -// Can only be sent by the server and only at the beginning of the connection -message ConnectResponse { - bool invalid_password = 1; -} - -// Request to close the connection. -// Can be sent by both the client and server -message DisconnectRequest { - // Do not close the connection before the acknowledgement arrives -} - -message DisconnectResponse { - // Empty - Both parties are required to close the connection after this - // message has been received. -} - -message PingRequest { - // Empty -} - -message PingResponse { - // Empty -} - -message DeviceInfoRequest { - // Empty -} - -message DeviceInfoResponse { - bool uses_password = 1; - - // The name of the node, given by "App.set_name()" - string name = 2; - - // The mac address of the device. For example "AC:BC:32:89:0E:A9" - string mac_address = 3; - - // A string describing the ESPHome version. For example "1.10.0" - string esphome_core_version = 4; - - // 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; - - // The model of the board. For example NodeMCU - string model = 6; - - bool has_deep_sleep = 7; -} - -message ListEntitiesRequest { - // Empty -} - -message ListEntitiesBinarySensorResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - string device_class = 5; - bool is_status_binary_sensor = 6; -} -message ListEntitiesCoverResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - bool is_optimistic = 5; -} -message ListEntitiesFanResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - bool supports_oscillation = 5; - bool supports_speed = 6; -} -message ListEntitiesLightResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - bool supports_brightness = 5; - bool supports_rgb = 6; - bool supports_white_value = 7; - bool supports_color_temperature = 8; - float min_mireds = 9; - float max_mireds = 10; - repeated string effects = 11; -} -message ListEntitiesSensorResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - string icon = 5; - string unit_of_measurement = 6; - int32 accuracy_decimals = 7; -} -message ListEntitiesSwitchResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - string icon = 5; - bool optimistic = 6; -} -message ListEntitiesTextSensorResponse { - string object_id = 1; - fixed32 key = 2; - string name = 3; - string unique_id = 4; - - string icon = 5; -} -message ListEntitiesDoneResponse { - // Empty -} - -message SubscribeStatesRequest { - // Empty -} -message BinarySensorStateResponse { - fixed32 key = 1; - bool state = 2; -} -message CoverStateResponse { - fixed32 key = 1; - enum CoverState { - OPEN = 0; - CLOSED = 1; - } - CoverState state = 2; -} -enum FanSpeed { - LOW = 0; - MEDIUM = 1; - HIGH = 2; -} -message FanStateResponse { - fixed32 key = 1; - bool state = 2; - bool oscillating = 3; - FanSpeed speed = 4; -} -message LightStateResponse { - fixed32 key = 1; - bool state = 2; - float brightness = 3; - float red = 4; - float green = 5; - float blue = 6; - float white = 7; - float color_temperature = 8; - string effect = 9; -} -message SensorStateResponse { - fixed32 key = 1; - float state = 2; -} -message SwitchStateResponse { - fixed32 key = 1; - bool state = 2; -} -message TextSensorStateResponse { - fixed32 key = 1; - string state = 2; -} - -message CoverCommandRequest { - fixed32 key = 1; - enum CoverCommand { - OPEN = 0; - CLOSE = 1; - STOP = 2; - } - bool has_state = 2; - CoverCommand command = 3; -} -message FanCommandRequest { - fixed32 key = 1; - bool has_state = 2; - bool state = 3; - bool has_speed = 4; - FanSpeed speed = 5; - bool has_oscillating = 6; - bool oscillating = 7; -} -message LightCommandRequest { - fixed32 key = 1; - bool has_state = 2; - bool state = 3; - bool has_brightness = 4; - float brightness = 5; - 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_transition_length = 14; - uint32 transition_length = 15; - bool has_flash_length = 16; - uint32 flash_length = 17; - bool has_effect = 18; - string effect = 19; -} -message SwitchCommandRequest { - fixed32 key = 1; - bool state = 2; -} - -enum LogLevel { - NONE = 0; - ERROR = 1; - WARN = 2; - INFO = 3; - DEBUG = 4; - VERBOSE = 5; - VERY_VERBOSE = 6; -} - -message SubscribeLogsRequest { - LogLevel level = 1; - bool dump_config = 2; -} - -message SubscribeLogsResponse { - LogLevel level = 1; - string tag = 2; - string message = 3; - bool send_failed = 4; -} - -message SubscribeServiceCallsRequest { - -} - -message ServiceCallResponse { - string service = 1; - map data = 2; - map data_template = 3; - map variables = 4; -} - -// 1. Client sends SubscribeHomeAssistantStatesRequest -// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async) -// 3. Client sends HomeAssistantStateResponse for state changes. -message SubscribeHomeAssistantStatesRequest { - -} - -message SubscribeHomeAssistantStateResponse { - string entity_id = 1; -} - -message HomeAssistantStateResponse { - string entity_id = 1; - string state = 2; -} - -message GetTimeRequest { - -} - -message GetTimeResponse { - fixed32 epoch_seconds = 1; -} - diff --git a/esphome/api/client.py b/esphome/api/client.py index cdafcc1d74..34e3b4f3a2 100644 --- a/esphome/api/client.py +++ b/esphome/api/client.py @@ -14,7 +14,7 @@ import esphome.api.api_pb2 as pb from esphome.const import CONF_PASSWORD, CONF_PORT from esphome.core import EsphomeError from esphome.helpers import resolve_ip_address, indent, color -from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte, format_bytes +from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte from esphome.util import safe_print _LOGGER = logging.getLogger(__name__) @@ -247,7 +247,7 @@ class APIClient(threading.Thread): if self._socket is None: raise APIConnectionError("Socket closed") - _LOGGER.debug("Write: %s", format_bytes(data)) + # _LOGGER.debug("Write: %s", format_bytes(data)) with self._socket_write_lock: try: self._socket.sendall(data) @@ -275,7 +275,7 @@ class APIClient(threading.Thread): req += encoded self._write(req) - def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=1): + def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5): event = threading.Event() responses = [] @@ -296,7 +296,7 @@ class APIClient(threading.Thread): raise APIConnectionError("Timeout while waiting for message response!") return responses - def _send_message_await_response(self, send_msg, response_type, timeout=1): + def _send_message_await_response(self, send_msg, response_type, timeout=5): def is_response(msg): return isinstance(msg, response_type) @@ -327,7 +327,7 @@ class APIClient(threading.Thread): if not self._authenticated: raise APIConnectionError("Must login first!") - def subscribe_logs(self, on_log, log_level=None, dump_config=False): + def subscribe_logs(self, on_log, log_level=7, dump_config=False): self._check_authenticated() def on_msg(msg): @@ -336,8 +336,7 @@ class APIClient(threading.Thread): self._message_handlers.append(on_msg) req = pb.SubscribeLogsRequest(dump_config=dump_config) - if log_level is not None: - req.level = log_level + req.level = log_level self._send_message(req) def _recv(self, amount): diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 5c15c68db7..ff825693ba 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \ - CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID + CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT from esphome.core import CORE, coroutine_with_priority DEPENDENCIES = ['network'] @@ -11,24 +11,19 @@ DEPENDENCIES = ['network'] api_ns = cg.esphome_ns.namespace('api') APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller) HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action) -KeyValuePair = api_ns.class_('KeyValuePair') -TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair') APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition) -UserService = api_ns.class_('UserService', automation.Trigger) -ServiceTypeArgument = api_ns.class_('ServiceTypeArgument') -ServiceArgType = api_ns.enum('ServiceArgType') -SERVICE_ARG_TYPES = { - 'bool': ServiceArgType.SERVICE_ARG_TYPE_BOOL, - 'int': ServiceArgType.SERVICE_ARG_TYPE_INT, - 'float': ServiceArgType.SERVICE_ARG_TYPE_FLOAT, - 'string': ServiceArgType.SERVICE_ARG_TYPE_STRING, -} +UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger) +ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument') SERVICE_ARG_NATIVE_TYPES = { 'bool': bool, 'int': cg.int32, 'float': float, 'string': cg.std_string, + 'bool[]': cg.std_vector.template(bool), + 'int[]': cg.std_vector.template(cg.int32), + 'float[]': cg.std_vector.template(float), + 'string[]': cg.std_vector.template(cg.std_string), } CONFIG_SCHEMA = cv.Schema({ @@ -37,10 +32,10 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_PASSWORD, default=''): cv.string_strict, cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds, cv.Optional(CONF_SERVICES): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserService), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), cv.Required(CONF_SERVICE): cv.valid_name, cv.Optional(CONF_VARIABLES, default={}): cv.Schema({ - cv.validate_id_name: cv.one_of(*SERVICE_ARG_TYPES, lower=True), + cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True), }), }), }).extend(cv.COMPONENT_SCHEMA) @@ -58,37 +53,34 @@ def to_code(config): for conf in config.get(CONF_SERVICES, []): template_args = [] func_args = [] - service_type_args = [] + service_arg_names = [] for name, var_ in conf[CONF_VARIABLES].items(): native = SERVICE_ARG_NATIVE_TYPES[var_] template_args.append(native) func_args.append((native, name)) - service_type_args.append(ServiceTypeArgument(name, SERVICE_ARG_TYPES[var_])) + service_arg_names.append(name) templ = cg.TemplateArguments(*template_args) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ, - conf[CONF_SERVICE], service_type_args) + conf[CONF_SERVICE], service_arg_names) cg.add(var.register_user_service(trigger)) yield automation.build_automation(trigger, func_args, conf) cg.add_define('USE_API') + cg.add_global(api_ns.using) if CORE.is_esp32: cg.add_library('AsyncTCP', '1.0.3') elif CORE.is_esp8266: cg.add_library('ESPAsyncTCP', '1.2.0') +KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string)}) + HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ cv.GenerateID(): cv.use_id(APIServer), - cv.Required(CONF_SERVICE): cv.string, - cv.Optional(CONF_DATA): cv.Schema({ - cv.string: cv.string, - }), - cv.Optional(CONF_DATA_TEMPLATE): cv.Schema({ - cv.string: cv.string, - }), - cv.Optional(CONF_VARIABLES): cv.Schema({ - cv.string: cv.returning_lambda, - }), + cv.Required(CONF_SERVICE): cv.templatable(cv.string), + cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, }) @@ -96,20 +88,54 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ HOMEASSISTANT_SERVICE_ACTION_SCHEMA) def homeassistant_service_to_code(config, action_id, template_arg, args): serv = yield cg.get_variable(config[CONF_ID]) - var = cg.new_Pvariable(action_id, template_arg, serv) - cg.add(var.set_service(config[CONF_SERVICE])) - if CONF_DATA in config: - datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA].items()] - cg.add(var.set_data(datas)) - if CONF_DATA_TEMPLATE in config: - datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA_TEMPLATE].items()] - cg.add(var.set_data_template(datas)) - if CONF_VARIABLES in config: - datas = [] - for key, value in config[CONF_VARIABLES].items(): - value_ = yield cg.process_lambda(value, []) - datas.append(TemplatableKeyValuePair(key, value_)) - cg.add(var.set_variables(datas)) + var = cg.new_Pvariable(action_id, template_arg, serv, False) + templ = yield cg.templatable(config[CONF_SERVICE], args, None) + cg.add(var.set_service(templ)) + for key, value in config[CONF_DATA].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_data(key, templ)) + for key, value in config[CONF_DATA_TEMPLATE].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_data_template(key, templ)) + for key, value in config[CONF_VARIABLES].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_variable(key, templ)) + yield var + + +def validate_homeassistant_event(value): + value = cv.string(value) + if not value.startswith(u'esphome.'): + raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with " + "esphome. For example 'esphome.xyz'") + return value + + +HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.use_id(APIServer), + cv.Required(CONF_EVENT): validate_homeassistant_event, + cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, +}) + + +@automation.register_action('homeassistant.event', HomeAssistantServiceCallAction, + HOMEASSISTANT_EVENT_ACTION_SCHEMA) +def homeassistant_event_to_code(config, action_id, template_arg, args): + serv = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, serv, True) + templ = yield cg.templatable(config[CONF_EVENT], args, None) + cg.add(var.set_service(templ)) + for key, value in config[CONF_DATA].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_data(key, templ)) + for key, value in config[CONF_DATA_TEMPLATE].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_data_template(key, templ)) + for key, value in config[CONF_VARIABLES].items(): + templ = yield cg.templatable(value, args, None) + cg.add(var.add_variable(key, templ)) yield var diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 4d6f2ac9a7..c776a96f86 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1,5 +1,45 @@ syntax = "proto3"; +import "api_options.proto"; + +service APIConnection { + rpc hello (HelloRequest) returns (HelloResponse) { + option (needs_setup_connection) = false; + option (needs_authentication) = false; + } + rpc connect (ConnectRequest) returns (ConnectResponse) { + option (needs_setup_connection) = false; + option (needs_authentication) = false; + } + 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 get_time (GetTimeRequest) returns (GetTimeResponse) { + option (needs_authentication) = false; + } + rpc execute_service (ExecuteServiceRequest) returns (void) {} + + rpc cover_command (CoverCommandRequest) returns (void) {} + rpc fan_command (FanCommandRequest) returns (void) {} + rpc light_command (LightCommandRequest) returns (void) {} + rpc switch_command (SwitchCommandRequest) returns (void) {} + rpc camera_image (CameraImageRequest) returns (void) {} + rpc climate_command (ClimateCommandRequest) returns (void) {} +} + // ==================== BASE PACKETS ==================== @@ -21,8 +61,11 @@ syntax = "proto3"; // Message sent at the beginning of each connection // Can only be sent by the client and only at the beginning of the connection -// ID: 1 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 @@ -32,8 +75,11 @@ message HelloRequest { // Confirmation of successful connection request. // Can only be sent by the server and only at the beginning of the connection -// ID: 2 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_ @@ -49,49 +95,66 @@ message HelloResponse { // Message sent at the beginning of each connection to authenticate the client // Can only be sent by the client and only at the beginning of the connection -// ID: 3 message ConnectRequest { + option (id) = 3; + option (source) = SOURCE_CLIENT; + option (no_delay) = true; + // The password to log in with string password = 1; } // Confirmation of successful connection. After this the connection is available for all traffic. // Can only be sent by the server and only at the beginning of the connection -// ID: 4 message ConnectResponse { + option (id) = 4; + option (source) = SOURCE_SERVER; + option (no_delay) = true; + bool invalid_password = 1; } // Request to close the connection. // Can be sent by both the client and server -// ID: 5 message DisconnectRequest { + option (id) = 5; + option (source) = SOURCE_BOTH; + option (no_delay) = true; + // Do not close the connection before the acknowledgement arrives } -// ID: 6 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. } -// ID: 7 message PingRequest { + option (id) = 7; + option (source) = SOURCE_BOTH; // Empty } -// ID: 8 message PingResponse { + option (id) = 8; + option (source) = SOURCE_BOTH; // Empty } -// ID: 9 message DeviceInfoRequest { + option (id) = 9; + option (source) = SOURCE_CLIENT; // Empty } -// ID: 10 message DeviceInfoResponse { + option (id) = 10; + option (source) = SOURCE_SERVER; + bool uses_password = 1; // The name of the node, given by "App.set_name()" @@ -101,7 +164,7 @@ message DeviceInfoResponse { string mac_address = 3; // A string describing the ESPHome version. For example "1.10.0" - string esphome_core_version = 4; + string esphome_version = 4; // 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. @@ -114,22 +177,29 @@ message DeviceInfoResponse { bool has_deep_sleep = 7; } -// ID: 11 message ListEntitiesRequest { + option (id) = 11; + option (source) = SOURCE_CLIENT; // Empty } -// ID: 19 message ListEntitiesDoneResponse { + option (id) = 19; + option (source) = SOURCE_SERVER; + option (no_delay) = true; // Empty } -// ID: 20 message SubscribeStatesRequest { + option (id) = 20; + option (source) = SOURCE_CLIENT; // Empty } // ==================== BINARY SENSOR ==================== -// ID: 12 message ListEntitiesBinarySensorResponse { + option (id) = 12; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BINARY_SENSOR"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -138,15 +208,22 @@ message ListEntitiesBinarySensorResponse { string device_class = 5; bool is_status_binary_sensor = 6; } -// ID: 21 message BinarySensorStateResponse { + option (id) = 21; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BINARY_SENSOR"; + option (no_delay) = true; + fixed32 key = 1; bool state = 2; } // ==================== COVER ==================== -// ID: 13 message ListEntitiesCoverResponse { + option (id) = 13; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_COVER"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -157,38 +234,47 @@ message ListEntitiesCoverResponse { bool supports_tilt = 7; string device_class = 8; } -// ID: 22 -message CoverStateResponse { - fixed32 key = 1; +enum LegacyCoverState { + 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 (source) = SOURCE_SERVER; + option (ifdef) = "USE_COVER"; + option (no_delay) = true; + + fixed32 key = 1; // legacy: state has been removed in 1.13 // clients/servers must still send/accept it until the next protocol change - enum LegacyCoverState { - OPEN = 0; - CLOSED = 1; - } LegacyCoverState legacy_state = 2; float position = 3; float tilt = 4; - enum CoverOperation { - IDLE = 0; - IS_OPENING = 1; - IS_CLOSING = 2; - } CoverOperation current_operation = 5; } -// ID: 30 + +enum LegacyCoverCommand { + 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; + fixed32 key = 1; // legacy: command has been removed in 1.13 // clients/servers must still send/accept it until the next protocol change - enum LegacyCoverCommand { - OPEN = 0; - CLOSE = 1; - STOP = 2; - } bool has_legacy_command = 2; LegacyCoverCommand legacy_command = 3; @@ -200,8 +286,11 @@ message CoverCommandRequest { } // ==================== FAN ==================== -// ID: 14 message ListEntitiesFanResponse { + option (id) = 14; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_FAN"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -211,19 +300,27 @@ message ListEntitiesFanResponse { bool supports_speed = 6; } enum FanSpeed { - LOW = 0; - MEDIUM = 1; - HIGH = 2; + FAN_SPEED_LOW = 0; + FAN_SPEED_MEDIUM = 1; + FAN_SPEED_HIGH = 2; } -// ID: 23 message FanStateResponse { + option (id) = 23; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_FAN"; + option (no_delay) = true; + fixed32 key = 1; bool state = 2; bool oscillating = 3; FanSpeed speed = 4; } -// ID: 31 message FanCommandRequest { + option (id) = 31; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_FAN"; + option (no_delay) = true; + fixed32 key = 1; bool has_state = 2; bool state = 3; @@ -234,8 +331,11 @@ message FanCommandRequest { } // ==================== LIGHT ==================== -// ID: 15 message ListEntitiesLightResponse { + option (id) = 15; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_LIGHT"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -249,8 +349,12 @@ message ListEntitiesLightResponse { float max_mireds = 10; repeated string effects = 11; } -// ID: 24 message LightStateResponse { + option (id) = 24; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_LIGHT"; + option (no_delay) = true; + fixed32 key = 1; bool state = 2; float brightness = 3; @@ -261,8 +365,12 @@ message LightStateResponse { float color_temperature = 8; string effect = 9; } -// ID: 32 message LightCommandRequest { + option (id) = 32; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_LIGHT"; + option (no_delay) = true; + fixed32 key = 1; bool has_state = 2; bool state = 3; @@ -285,8 +393,11 @@ message LightCommandRequest { } // ==================== SENSOR ==================== -// ID: 16 message ListEntitiesSensorResponse { + option (id) = 16; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_SENSOR"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -296,15 +407,22 @@ message ListEntitiesSensorResponse { string unit_of_measurement = 6; int32 accuracy_decimals = 7; } -// ID: 25 message SensorStateResponse { + option (id) = 25; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_SENSOR"; + option (no_delay) = true; + fixed32 key = 1; float state = 2; } // ==================== SWITCH ==================== -// ID: 17 message ListEntitiesSwitchResponse { + option (id) = 17; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_SWITCH"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -313,20 +431,31 @@ message ListEntitiesSwitchResponse { string icon = 5; bool assumed_state = 6; } -// ID: 26 message SwitchStateResponse { + option (id) = 26; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_SWITCH"; + option (no_delay) = true; + fixed32 key = 1; bool state = 2; } -// ID: 33 message SwitchCommandRequest { + option (id) = 33; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_SWITCH"; + option (no_delay) = true; + fixed32 key = 1; bool state = 2; } // ==================== TEXT SENSOR ==================== -// ID: 18 message ListEntitiesTextSensorResponse { + option (id) = 18; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_TEXT_SENSOR"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -334,29 +463,38 @@ message ListEntitiesTextSensorResponse { string icon = 5; } -// ID: 27 message TextSensorStateResponse { + option (id) = 27; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_TEXT_SENSOR"; + option (no_delay) = true; + fixed32 key = 1; string state = 2; } // ==================== SUBSCRIBE LOGS ==================== enum LogLevel { - NONE = 0; - ERROR = 1; - WARN = 2; - INFO = 3; - DEBUG = 4; - VERBOSE = 5; - VERY_VERBOSE = 6; + LOG_LEVEL_NONE = 0; + LOG_LEVEL_ERROR = 1; + LOG_LEVEL_WARN = 2; + LOG_LEVEL_INFO = 3; + LOG_LEVEL_DEBUG = 4; + LOG_LEVEL_VERBOSE = 5; + LOG_LEVEL_VERY_VERBOSE = 6; } -// ID: 28 message SubscribeLogsRequest { + option (id) = 28; + option (source) = SOURCE_CLIENT; LogLevel level = 1; bool dump_config = 2; } -// ID: 29 message SubscribeLogsResponse { + option (id) = 29; + option (source) = SOURCE_SERVER; + option (log) = false; + option (no_delay) = false; + LogLevel level = 1; string tag = 2; string message = 3; @@ -364,109 +502,153 @@ message SubscribeLogsResponse { } // ==================== HOMEASSISTANT.SERVICE ==================== -// ID: 34 -message SubscribeServiceCallsRequest { - +message SubscribeHomeassistantServicesRequest { + option (id) = 34; + option (source) = SOURCE_CLIENT; } -// ID: 35 -message ServiceCallResponse { +message HomeassistantServiceMap { + string key = 1; + string value = 2; +} + +message HomeassistantServiceResponse { + option (id) = 35; + option (source) = SOURCE_SERVER; + option (no_delay) = true; + string service = 1; - map data = 2; - map data_template = 3; - map variables = 4; + repeated HomeassistantServiceMap data = 2; + repeated HomeassistantServiceMap data_template = 3; + repeated HomeassistantServiceMap variables = 4; + bool is_event = 5; } // ==================== IMPORT HOME ASSISTANT STATES ==================== // 1. Client sends SubscribeHomeAssistantStatesRequest // 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async) // 3. Client sends HomeAssistantStateResponse for state changes. -// ID: 38 message SubscribeHomeAssistantStatesRequest { - + option (id) = 38; + option (source) = SOURCE_CLIENT; } -// ID: 39 message SubscribeHomeAssistantStateResponse { + option (id) = 39; + option (source) = SOURCE_SERVER; string entity_id = 1; } -// ID: 40 message HomeAssistantStateResponse { + option (id) = 40; + option (source) = SOURCE_CLIENT; + option (no_delay) = true; + string entity_id = 1; string state = 2; } // ==================== IMPORT TIME ==================== -// ID: 36 message GetTimeRequest { - + option (id) = 36; + option (source) = SOURCE_BOTH; } -// ID: 37 message GetTimeResponse { + option (id) = 37; + option (source) = SOURCE_BOTH; + option (no_delay) = true; + fixed32 epoch_seconds = 1; } // ==================== 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; +} message ListEntitiesServicesArgument { string name = 1; - enum Type { - BOOL = 0; - INT = 1; - FLOAT = 2; - STRING = 3; - } - Type type = 2; + ServiceArgType type = 2; } -// ID: 41 message ListEntitiesServicesResponse { + option (id) = 41; + option (source) = SOURCE_SERVER; + string name = 1; fixed32 key = 2; repeated ListEntitiesServicesArgument args = 3; } message ExecuteServiceArgument { bool bool_ = 1; - int32 int_ = 2; + 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]; + repeated sint32 int_array = 7 [packed=false]; + repeated float float_array = 8 [packed=false]; + repeated string string_array = 9; } -// ID: 42 message ExecuteServiceRequest { + option (id) = 42; + option (source) = SOURCE_CLIENT; + option (no_delay) = true; + fixed32 key = 1; repeated ExecuteServiceArgument args = 2; } // ==================== CAMERA ==================== -// ID: 43 message ListEntitiesCameraResponse { + option (id) = 43; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_ESP32_CAMERA"; + string object_id = 1; fixed32 key = 2; string name = 3; string unique_id = 4; } -// ID: 44 message CameraImageResponse { + option (id) = 44; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_ESP32_CAMERA"; + fixed32 key = 1; bytes data = 2; bool done = 3; } -// ID: 45 message CameraImageRequest { + option (id) = 45; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_ESP32_CAMERA"; + option (no_delay) = true; + bool single = 1; bool stream = 2; } // ==================== CLIMATE ==================== enum ClimateMode { - OFF = 0; - AUTO = 1; - COOL = 2; - HEAT = 3; + CLIMATE_MODE_OFF = 0; + CLIMATE_MODE_AUTO = 1; + CLIMATE_MODE_COOL = 2; + CLIMATE_MODE_HEAT = 3; } -// ID: 46 message ListEntitiesClimateResponse { + option (id) = 46; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_CLIMATE"; + string object_id = 1; fixed32 key = 2; string name = 3; @@ -480,8 +662,12 @@ message ListEntitiesClimateResponse { float visual_temperature_step = 10; bool supports_away = 11; } -// ID: 47 message ClimateStateResponse { + option (id) = 47; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_CLIMATE"; + option (no_delay) = true; + fixed32 key = 1; ClimateMode mode = 2; float current_temperature = 3; @@ -490,8 +676,12 @@ message ClimateStateResponse { float target_temperature_high = 6; bool away = 7; } -// ID: 48 message ClimateCommandRequest { + option (id) = 48; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_CLIMATE"; + option (no_delay) = true; + fixed32 key = 1; bool has_mode = 2; ClimateMode mode = 3; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp new file mode 100644 index 0000000000..e949f0249a --- /dev/null +++ b/esphome/components/api/api_connection.cpp @@ -0,0 +1,670 @@ +#include "api_connection.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" +#include "esphome/core/version.h" + +#ifdef USE_DEEP_SLEEP +#include "esphome/components/deep_sleep/deep_sleep_component.h" +#endif +#ifdef USE_HOMEASSISTANT_TIME +#include "esphome/components/homeassistant/time/homeassistant_time.h" +#endif + +namespace esphome { +namespace api { + +static const char *TAG = "api.connection"; + +APIConnection::APIConnection(AsyncClient *client, APIServer *parent) + : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { + this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this); + this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this); + this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); }, + this); + this->client_->onData([](void *s, AsyncClient *c, void *buf, + size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast(buf), len); }, + this); + + this->send_buffer_.reserve(64); + this->recv_buffer_.reserve(32); + this->client_info_ = this->client_->remoteIP().toString().c_str(); + this->last_traffic_ = millis(); +} +APIConnection::~APIConnection() { delete this->client_; } +void APIConnection::on_error_(int8_t error) { this->remove_ = true; } +void APIConnection::on_disconnect_() { this->remove_ = true; } +void APIConnection::on_timeout_(uint32_t time) { this->on_fatal_error(); } +void APIConnection::on_data_(uint8_t *buf, size_t len) { + if (len == 0 || buf == nullptr) + return; + this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len); +} +void APIConnection::parse_recv_buffer_() { + if (this->recv_buffer_.empty() || this->remove_) + return; + + while (!this->recv_buffer_.empty()) { + if (this->recv_buffer_[0] != 0x00) { + ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str()); + this->on_fatal_error(); + return; + } + uint32_t i = 1; + const uint32_t size = this->recv_buffer_.size(); + uint32_t consumed; + auto msg_size_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed); + if (!msg_size_varint.has_value()) + // not enough data there yet + return; + i += consumed; + uint32_t msg_size = msg_size_varint->as_uint32(); + + auto msg_type_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed); + if (!msg_type_varint.has_value()) + // not enough data there yet + return; + i += consumed; + uint32_t msg_type = msg_type_varint->as_uint32(); + + if (size - i < msg_size) + // message body not fully received + return; + + uint8_t *msg = &this->recv_buffer_[i]; + this->read_message(msg_size, msg_type, msg); + if (this->remove_) + return; + // pop front + uint32_t total = i + msg_size; + this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total); + this->last_traffic_ = millis(); + } +} + +void APIConnection::disconnect_client() { + this->client_->close(); + this->remove_ = true; +} + +void APIConnection::loop() { + if (this->remove_) + return; + + if (this->next_close_) { + this->disconnect_client(); + return; + } + + if (!network_is_connected()) { + // when network is disconnected force disconnect immediately + // don't wait for timeout + this->on_fatal_error(); + return; + } + if (this->client_->disconnected()) { + // failsafe for disconnect logic + this->on_disconnect_(); + return; + } + this->parse_recv_buffer_(); + + this->list_entities_iterator_.advance(); + this->initial_state_iterator_.advance(); + + const uint32_t keepalive = 60000; + if (this->sent_ping_) { + // Disconnect if not responded within 2.5*keepalive + if (millis() - this->last_traffic_ > (keepalive * 5) / 2) { + ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str()); + this->disconnect_client(); + } + } else if (millis() - this->last_traffic_ > keepalive) { + this->sent_ping_ = true; + this->send_ping_request(PingRequest()); + } + +#ifdef USE_ESP32_CAMERA + if (this->image_reader_.available()) { + uint32_t space = this->client_->space(); + // reserve 15 bytes for metadata, and at least 64 bytes of data + if (space >= 15 + 64) { + uint32_t to_send = std::min(space - 15, this->image_reader_.available()); + auto buffer = this->create_buffer(); + // fixed32 key = 1; + buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash()); + // bytes data = 2; + buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send); + // bool done = 3; + bool done = this->image_reader_.available() == to_send; + buffer.encode_bool(3, done); + this->set_nodelay(false); + bool success = this->send_buffer(buffer, 44); + + if (success) { + this->image_reader_.consume_data(to_send); + } + if (success && done) { + this->image_reader_.return_image(); + } + } + } +#endif +} + +std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) { + return App.get_name() + component_type + nameable->get_object_id(); +} + +#ifdef USE_BINARY_SENSOR +bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { + if (!this->state_subscription_) + return false; + + BinarySensorStateResponse resp; + resp.key = binary_sensor->get_object_id_hash(); + resp.state = state; + return this->send_binary_sensor_state_response(resp); +} +bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { + ListEntitiesBinarySensorResponse msg; + msg.object_id = binary_sensor->get_object_id(); + msg.key = binary_sensor->get_object_id_hash(); + msg.name = binary_sensor->get_name(); + msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor); + msg.device_class = binary_sensor->get_device_class(); + msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); + return this->send_list_entities_binary_sensor_response(msg); +} +#endif + +#ifdef USE_COVER +bool APIConnection::send_cover_state(cover::Cover *cover) { + if (!this->state_subscription_) + return false; + + auto traits = cover->get_traits(); + CoverStateResponse resp{}; + resp.key = cover->get_object_id_hash(); + resp.legacy_state = (cover->position == cover::COVER_OPEN) ? LEGACY_COVER_STATE_OPEN : LEGACY_COVER_STATE_CLOSED; + resp.position = cover->position; + if (traits.get_supports_tilt()) + resp.tilt = cover->tilt; + resp.current_operation = static_cast(cover->current_operation); + return this->send_cover_state_response(resp); +} +bool APIConnection::send_cover_info(cover::Cover *cover) { + auto traits = cover->get_traits(); + ListEntitiesCoverResponse msg; + msg.key = cover->get_object_id_hash(); + msg.object_id = cover->get_object_id(); + msg.name = cover->get_name(); + msg.unique_id = get_default_unique_id("cover", cover); + msg.assumed_state = traits.get_is_assumed_state(); + msg.supports_position = traits.get_supports_position(); + msg.supports_tilt = traits.get_supports_tilt(); + msg.device_class = cover->get_device_class(); + return this->send_list_entities_cover_response(msg); +} +void APIConnection::cover_command(const CoverCommandRequest &msg) { + cover::Cover *cover = App.get_cover_by_key(msg.key); + if (cover == nullptr) + return; + + auto call = cover->make_call(); + if (msg.has_legacy_command) { + switch (msg.legacy_command) { + case LEGACY_COVER_COMMAND_OPEN: + call.set_command_open(); + break; + case LEGACY_COVER_COMMAND_CLOSE: + call.set_command_close(); + break; + case LEGACY_COVER_COMMAND_STOP: + call.set_command_stop(); + break; + } + } + if (msg.has_position) + call.set_position(msg.position); + if (msg.has_tilt) + call.set_tilt(msg.tilt); + if (msg.stop) + call.set_command_stop(); + call.perform(); +} +#endif + +#ifdef USE_FAN +bool APIConnection::send_fan_state(fan::FanState *fan) { + if (!this->state_subscription_) + return false; + + auto traits = fan->get_traits(); + FanStateResponse resp{}; + resp.key = fan->get_object_id_hash(); + resp.state = fan->state; + if (traits.supports_oscillation()) + resp.oscillating = fan->oscillating; + if (traits.supports_speed()) + resp.speed = static_cast(fan->speed); + return this->send_fan_state_response(resp); +} +bool APIConnection::send_fan_info(fan::FanState *fan) { + auto traits = fan->get_traits(); + ListEntitiesFanResponse msg; + msg.key = fan->get_object_id_hash(); + msg.object_id = fan->get_object_id(); + msg.name = fan->get_name(); + msg.unique_id = get_default_unique_id("fan", fan); + msg.supports_oscillation = traits.supports_oscillation(); + msg.supports_speed = traits.supports_speed(); + return this->send_list_entities_fan_response(msg); +} +void APIConnection::fan_command(const FanCommandRequest &msg) { + fan::FanState *fan = App.get_fan_by_key(msg.key); + if (fan == nullptr) + return; + + auto call = fan->make_call(); + if (msg.has_state) + call.set_state(msg.state); + if (msg.has_oscillating) + call.set_oscillating(msg.oscillating); + if (msg.has_speed) + call.set_speed(static_cast(msg.speed)); + call.perform(); +} +#endif + +#ifdef USE_LIGHT +bool APIConnection::send_light_state(light::LightState *light) { + if (!this->state_subscription_) + return false; + + auto traits = light->get_traits(); + auto values = light->remote_values; + LightStateResponse resp{}; + + resp.key = light->get_object_id_hash(); + resp.state = values.is_on(); + if (traits.get_supports_brightness()) + resp.brightness = values.get_brightness(); + if (traits.get_supports_rgb()) { + resp.red = values.get_red(); + resp.green = values.get_green(); + resp.blue = values.get_blue(); + } + if (traits.get_supports_rgb_white_value()) + resp.white = values.get_white(); + if (traits.get_supports_color_temperature()) + resp.color_temperature = values.get_color_temperature(); + if (light->supports_effects()) + resp.effect = light->get_effect_name(); + return this->send_light_state_response(resp); +} +bool APIConnection::send_light_info(light::LightState *light) { + auto traits = light->get_traits(); + ListEntitiesLightResponse msg; + msg.key = light->get_object_id_hash(); + msg.object_id = light->get_object_id(); + msg.name = light->get_name(); + msg.unique_id = get_default_unique_id("light", light); + msg.supports_brightness = traits.get_supports_brightness(); + msg.supports_rgb = traits.get_supports_rgb(); + msg.supports_white_value = traits.get_supports_rgb_white_value(); + msg.supports_color_temperature = traits.get_supports_color_temperature(); + if (msg.supports_color_temperature) { + msg.min_mireds = traits.get_min_mireds(); + msg.max_mireds = traits.get_max_mireds(); + } + if (light->supports_effects()) { + for (auto *effect : light->get_effects()) + msg.effects.push_back(effect->get_name()); + } + return this->send_list_entities_light_response(msg); +} +void APIConnection::light_command(const LightCommandRequest &msg) { + light::LightState *light = App.get_light_by_key(msg.key); + if (light == nullptr) + return; + + auto call = light->make_call(); + if (msg.has_state) + call.set_state(msg.state); + if (msg.has_brightness) + call.set_brightness(msg.brightness); + if (msg.has_rgb) { + call.set_red(msg.red); + call.set_green(msg.green); + call.set_blue(msg.blue); + } + if (msg.has_white) + call.set_white(msg.white); + if (msg.has_color_temperature) + call.set_color_temperature(msg.color_temperature); + if (msg.has_transition_length) + call.set_transition_length(msg.transition_length); + if (msg.has_flash_length) + call.set_flash_length(msg.flash_length); + if (msg.has_effect) + call.set_effect(msg.effect); + call.perform(); +} +#endif + +#ifdef USE_SENSOR +bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { + if (!this->state_subscription_) + return false; + + SensorStateResponse resp{}; + resp.key = sensor->get_object_id_hash(); + resp.state = state; + return this->send_sensor_state_response(resp); +} +bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { + ListEntitiesSensorResponse msg; + msg.key = sensor->get_object_id_hash(); + msg.object_id = sensor->get_object_id(); + msg.name = sensor->get_name(); + msg.unique_id = sensor->unique_id(); + if (msg.unique_id.empty()) + msg.unique_id = get_default_unique_id("sensor", sensor); + msg.icon = sensor->get_icon(); + msg.unit_of_measurement = sensor->get_unit_of_measurement(); + msg.accuracy_decimals = sensor->get_accuracy_decimals(); + return this->send_list_entities_sensor_response(msg); +} +#endif + +#ifdef USE_SWITCH +bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) { + if (!this->state_subscription_) + return false; + + SwitchStateResponse resp{}; + resp.key = a_switch->get_object_id_hash(); + resp.state = state; + return this->send_switch_state_response(resp); +} +bool APIConnection::send_switch_info(switch_::Switch *a_switch) { + ListEntitiesSwitchResponse msg; + msg.key = a_switch->get_object_id_hash(); + msg.object_id = a_switch->get_object_id(); + msg.name = a_switch->get_name(); + msg.unique_id = get_default_unique_id("switch", a_switch); + msg.icon = a_switch->get_icon(); + msg.assumed_state = a_switch->assumed_state(); + return this->send_list_entities_switch_response(msg); +} +void APIConnection::switch_command(const SwitchCommandRequest &msg) { + switch_::Switch *a_switch = App.get_switch_by_key(msg.key); + if (a_switch == nullptr) + return; + + if (msg.state) + a_switch->turn_on(); + else + a_switch->turn_off(); +} +#endif + +#ifdef USE_TEXT_SENSOR +bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) { + if (!this->state_subscription_) + return false; + + TextSensorStateResponse resp{}; + resp.key = text_sensor->get_object_id_hash(); + resp.state = std::move(state); + return this->send_text_sensor_state_response(resp); +} +bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { + ListEntitiesTextSensorResponse msg; + msg.key = text_sensor->get_object_id_hash(); + msg.object_id = text_sensor->get_object_id(); + msg.name = text_sensor->get_name(); + msg.unique_id = text_sensor->unique_id(); + if (msg.unique_id.empty()) + msg.unique_id = get_default_unique_id("text_sensor", text_sensor); + msg.icon = text_sensor->get_icon(); + return this->send_list_entities_text_sensor_response(msg); +} +#endif + +#ifdef USE_CLIMATE +bool APIConnection::send_climate_state(climate::Climate *climate) { + if (!this->state_subscription_) + return false; + + auto traits = climate->get_traits(); + ClimateStateResponse resp{}; + resp.key = climate->get_object_id_hash(); + resp.mode = static_cast(climate->mode); + if (traits.get_supports_current_temperature()) + resp.current_temperature = climate->current_temperature; + if (traits.get_supports_two_point_target_temperature()) { + resp.target_temperature_low = climate->target_temperature_low; + resp.target_temperature_high = climate->target_temperature_high; + } else { + resp.target_temperature = climate->target_temperature; + } + if (traits.get_supports_away()) + resp.away = climate->away; + return this->send_climate_state_response(resp); +} +bool APIConnection::send_climate_info(climate::Climate *climate) { + auto traits = climate->get_traits(); + ListEntitiesClimateResponse msg; + msg.key = climate->get_object_id_hash(); + msg.object_id = climate->get_object_id(); + msg.name = climate->get_name(); + msg.unique_id = get_default_unique_id("climate", climate); + msg.supports_current_temperature = traits.get_supports_current_temperature(); + msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); + for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, + climate::CLIMATE_MODE_HEAT}) { + if (traits.supports_mode(mode)) + msg.supported_modes.push_back(static_cast(mode)); + } + msg.visual_min_temperature = traits.get_visual_min_temperature(); + msg.visual_max_temperature = traits.get_visual_max_temperature(); + msg.visual_temperature_step = traits.get_visual_temperature_step(); + msg.supports_away = traits.get_supports_away(); + return this->send_list_entities_climate_response(msg); +} +void APIConnection::climate_command(const ClimateCommandRequest &msg) { + climate::Climate *climate = App.get_climate_by_key(msg.key); + if (climate == nullptr) + return; + + auto call = climate->make_call(); + if (msg.has_mode) + call.set_mode(static_cast(msg.mode)); + if (msg.has_target_temperature) + call.set_target_temperature(msg.target_temperature); + if (msg.has_target_temperature_low) + call.set_target_temperature_low(msg.target_temperature_low); + if (msg.has_target_temperature_high) + call.set_target_temperature_high(msg.target_temperature_high); + if (msg.has_away) + call.set_away(msg.away); + call.perform(); +} +#endif + +#ifdef USE_ESP32_CAMERA +void APIConnection::send_camera_state(std::shared_ptr image) { + if (!this->state_subscription_) + return; + if (this->image_reader_.available()) + return; + this->image_reader_.set_image(image); +} +bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { + ListEntitiesCameraResponse msg; + msg.key = camera->get_object_id_hash(); + msg.object_id = camera->get_object_id(); + msg.name = camera->get_name(); + msg.unique_id = get_default_unique_id("camera", camera); + return this->send_list_entities_camera_response(msg); +} +void APIConnection::camera_image(const CameraImageRequest &msg) { + if (esp32_camera::global_esp32_camera == nullptr) + return; + + if (msg.single) + esp32_camera::global_esp32_camera->request_image(); + if (msg.stream) + esp32_camera::global_esp32_camera->request_stream(); +} +#endif + +#ifdef USE_HOMEASSISTANT_TIME +void APIConnection::on_get_time_response(const GetTimeResponse &value) { + if (homeassistant::global_homeassistant_time != nullptr) + homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds); +} +#endif + +bool APIConnection::send_log_message(int level, const char *tag, const char *line) { + if (this->log_subscription_ < level) + return false; + + this->set_nodelay(false); + + // Send raw so that we don't copy too much + auto buffer = this->create_buffer(); + // LogLevel level = 1; + buffer.encode_uint32(1, static_cast(level)); + // string tag = 2; + // buffer.encode_string(2, tag, strlen(tag)); + // string message = 3; + buffer.encode_string(3, line, strlen(line)); + // SubscribeLogsResponse - 29 + bool success = this->send_buffer(buffer, 29); + if (!success) { + buffer = this->create_buffer(); + // bool send_failed = 4; + buffer.encode_bool(4, true); + return this->send_buffer(buffer, 29); + } else { + return true; + } +} + +HelloResponse APIConnection::hello(const HelloRequest &msg) { + this->client_info_ = msg.client_info + " (" + this->client_->remoteIP().toString().c_str(); + this->client_info_ += ")"; + ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str()); + + HelloResponse resp; + resp.api_version_major = 1; + resp.api_version_minor = 3; + resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; + this->connection_state_ = ConnectionState::CONNECTED; + return resp; +} +ConnectResponse APIConnection::connect(const ConnectRequest &msg) { + bool correct = this->parent_->check_password(msg.password); + + ConnectResponse resp; + // bool invalid_password = 1; + resp.invalid_password = !correct; + if (correct) { + ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str()); + this->connection_state_ = ConnectionState::AUTHENTICATED; + +#ifdef USE_HOMEASSISTANT_TIME + if (homeassistant::global_homeassistant_time != nullptr) { + this->send_time_request(); + } +#endif + } + return resp; +} +DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { + DeviceInfoResponse resp{}; + resp.uses_password = this->parent_->uses_password(); + resp.name = App.get_name(); + resp.mac_address = get_mac_address_pretty(); + resp.esphome_version = ESPHOME_VERSION; + resp.compilation_time = App.get_compilation_time(); +#ifdef ARDUINO_BOARD + resp.model = ARDUINO_BOARD; +#endif +#ifdef USE_DEEP_SLEEP + resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; +#endif + return resp; +} +void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) { + for (auto &it : this->parent_->get_state_subs()) + if (it.entity_id == msg.entity_id) + it.callback(msg.state); +} +void APIConnection::execute_service(const ExecuteServiceRequest &msg) { + bool found = false; + for (auto *service : this->parent_->get_user_services()) { + if (service->execute_service(msg)) { + found = true; + } + } + if (!found) { + ESP_LOGV(TAG, "Could not find matching service!"); + } +} +void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) { + for (auto &it : this->parent_->get_state_subs()) { + SubscribeHomeAssistantStateResponse resp; + resp.entity_id = it.entity_id; + if (!this->send_subscribe_home_assistant_state_response(resp)) { + this->on_fatal_error(); + return; + } + } +} +bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { + if (this->remove_) + return false; + + std::vector header; + header.push_back(0x00); + ProtoVarInt(buffer.get_buffer()->size()).encode(header); + ProtoVarInt(message_type).encode(header); + + size_t needed_space = buffer.get_buffer()->size() + header.size(); + + if (needed_space > this->client_->space()) { + delay(0); + if (needed_space > this->client_->space()) { + // SubscribeLogsResponse + if (message_type != 29) { + ESP_LOGV(TAG, "Cannot send message because of TCP buffer space"); + } + delay(0); + return false; + } + } + + this->client_->add(reinterpret_cast(header.data()), header.size()); + this->client_->add(reinterpret_cast(buffer.get_buffer()->data()), buffer.get_buffer()->size()); + bool ret = this->client_->send(); + return ret; +} +void APIConnection::on_unauthenticated_access() { + ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str()); + this->on_fatal_error(); +} +void APIConnection::on_no_setup_connection() { + ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str()); + this->on_fatal_error(); +} +void APIConnection::on_fatal_error() { + ESP_LOGV(TAG, "Error: Disconnecting %s", this->client_info_.c_str()); + this->client_->close(); + this->remove_ = true; +} + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h new file mode 100644 index 0000000000..f9c7ffa28d --- /dev/null +++ b/esphome/components/api/api_connection.h @@ -0,0 +1,178 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/application.h" +#include "api_pb2.h" +#include "api_pb2_service.h" +#include "api_server.h" + +namespace esphome { +namespace api { + +class APIConnection : public APIServerConnection { + public: + APIConnection(AsyncClient *client, APIServer *parent); + virtual ~APIConnection(); + + void disconnect_client(); + void loop(); + + bool send_list_info_done() { + ListEntitiesDoneResponse resp; + return this->send_list_entities_done_response(resp); + } +#ifdef USE_BINARY_SENSOR + bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state); + bool send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor); +#endif +#ifdef USE_COVER + bool send_cover_state(cover::Cover *cover); + bool send_cover_info(cover::Cover *cover); + void cover_command(const CoverCommandRequest &msg) override; +#endif +#ifdef USE_FAN + bool send_fan_state(fan::FanState *fan); + bool send_fan_info(fan::FanState *fan); + void fan_command(const FanCommandRequest &msg) override; +#endif +#ifdef USE_LIGHT + bool send_light_state(light::LightState *light); + bool send_light_info(light::LightState *light); + void light_command(const LightCommandRequest &msg) override; +#endif +#ifdef USE_SENSOR + bool send_sensor_state(sensor::Sensor *sensor, float state); + bool send_sensor_info(sensor::Sensor *sensor); +#endif +#ifdef USE_SWITCH + bool send_switch_state(switch_::Switch *a_switch, bool state); + bool send_switch_info(switch_::Switch *a_switch); + void switch_command(const SwitchCommandRequest &msg) override; +#endif +#ifdef USE_TEXT_SENSOR + bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); + bool send_text_sensor_info(text_sensor::TextSensor *text_sensor); +#endif +#ifdef USE_ESP32_CAMERA + void send_camera_state(std::shared_ptr image); + bool send_camera_info(esp32_camera::ESP32Camera *camera); + void camera_image(const CameraImageRequest &msg) override; +#endif +#ifdef USE_CLIMATE + bool send_climate_state(climate::Climate *climate); + bool send_climate_info(climate::Climate *climate); + void climate_command(const ClimateCommandRequest &msg) override; +#endif + bool send_log_message(int level, const char *tag, const char *line); + void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { + if (!this->service_call_subscription_) + return; + this->send_homeassistant_service_response(call); + } +#ifdef USE_HOMEASSISTANT_TIME + void send_time_request() { + GetTimeRequest req; + this->send_get_time_request(req); + } +#endif + + void on_disconnect_response(const DisconnectResponse &value) override { + // we initiated disconnect_client + this->next_close_ = true; + } + void on_ping_response(const PingResponse &value) override { + // we initiated ping + this->sent_ping_ = false; + } + void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override; +#ifdef USE_HOMEASSISTANT_TIME + void on_get_time_response(const GetTimeResponse &value) override; +#endif + HelloResponse hello(const HelloRequest &msg) override; + ConnectResponse connect(const ConnectRequest &msg) override; + DisconnectResponse disconnect(const DisconnectRequest &msg) override { + // remote initiated disconnect_client + this->next_close_ = true; + DisconnectResponse resp; + return resp; + } + PingResponse ping(const PingRequest &msg) override { return {}; } + DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override; + void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); } + void subscribe_states(const SubscribeStatesRequest &msg) override { + this->state_subscription_ = true; + this->initial_state_iterator_.begin(); + } + void subscribe_logs(const SubscribeLogsRequest &msg) override { + this->log_subscription_ = msg.level; + if (msg.dump_config) + App.schedule_dump_config(); + } + void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override { + this->service_call_subscription_ = true; + } + void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override; + GetTimeResponse get_time(const GetTimeRequest &msg) override { + // TODO + return {}; + } + void execute_service(const ExecuteServiceRequest &msg) override; + bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } + bool is_connection_setup() override { + return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated(); + } + void on_fatal_error() override; + void on_unauthenticated_access() override; + void on_no_setup_connection() override; + ProtoWriteBuffer create_buffer() override { + this->send_buffer_.clear(); + return {&this->send_buffer_}; + } + bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override; + + protected: + friend APIServer; + + void on_error_(int8_t error); + void on_disconnect_(); + void on_timeout_(uint32_t time); + void on_data_(uint8_t *buf, size_t len); + void parse_recv_buffer_(); + void set_nodelay(bool nodelay) override { + if (nodelay == this->current_nodelay_) + return; + this->client_->setNoDelay(nodelay); + this->current_nodelay_ = nodelay; + } + + enum class ConnectionState { + WAITING_FOR_HELLO, + CONNECTED, + AUTHENTICATED, + } connection_state_{ConnectionState::WAITING_FOR_HELLO}; + + bool remove_{false}; + + std::vector send_buffer_; + std::vector recv_buffer_; + + std::string client_info_; +#ifdef USE_ESP32_CAMERA + esp32_camera::CameraImageReader image_reader_; +#endif + + bool state_subscription_{false}; + int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; + uint32_t last_traffic_; + bool sent_ping_{false}; + bool service_call_subscription_{false}; + bool current_nodelay_{false}; + bool next_close_{false}; + AsyncClient *client_; + APIServer *parent_; + InitialStateIterator initial_state_iterator_; + ListEntitiesIterator list_entities_iterator_; +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_message.h b/esphome/components/api/api_message.h deleted file mode 100644 index 26c24073df..0000000000 --- a/esphome/components/api/api_message.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "util.h" - -namespace esphome { -namespace api { - -enum class APIMessageType { - HELLO_REQUEST = 1, - HELLO_RESPONSE = 2, - CONNECT_REQUEST = 3, - CONNECT_RESPONSE = 4, - DISCONNECT_REQUEST = 5, - DISCONNECT_RESPONSE = 6, - PING_REQUEST = 7, - PING_RESPONSE = 8, - DEVICE_INFO_REQUEST = 9, - DEVICE_INFO_RESPONSE = 10, - - LIST_ENTITIES_REQUEST = 11, - LIST_ENTITIES_BINARY_SENSOR_RESPONSE = 12, - LIST_ENTITIES_COVER_RESPONSE = 13, - LIST_ENTITIES_FAN_RESPONSE = 14, - LIST_ENTITIES_LIGHT_RESPONSE = 15, - LIST_ENTITIES_SENSOR_RESPONSE = 16, - LIST_ENTITIES_SWITCH_RESPONSE = 17, - LIST_ENTITIES_TEXT_SENSOR_RESPONSE = 18, - LIST_ENTITIES_SERVICE_RESPONSE = 41, - LIST_ENTITIES_CAMERA_RESPONSE = 43, - LIST_ENTITIES_CLIMATE_RESPONSE = 46, - LIST_ENTITIES_DONE_RESPONSE = 19, - - SUBSCRIBE_STATES_REQUEST = 20, - BINARY_SENSOR_STATE_RESPONSE = 21, - COVER_STATE_RESPONSE = 22, - FAN_STATE_RESPONSE = 23, - LIGHT_STATE_RESPONSE = 24, - SENSOR_STATE_RESPONSE = 25, - SWITCH_STATE_RESPONSE = 26, - TEXT_SENSOR_STATE_RESPONSE = 27, - CAMERA_IMAGE_RESPONSE = 44, - CLIMATE_STATE_RESPONSE = 47, - - SUBSCRIBE_LOGS_REQUEST = 28, - SUBSCRIBE_LOGS_RESPONSE = 29, - - COVER_COMMAND_REQUEST = 30, - FAN_COMMAND_REQUEST = 31, - LIGHT_COMMAND_REQUEST = 32, - SWITCH_COMMAND_REQUEST = 33, - CAMERA_IMAGE_REQUEST = 45, - CLIMATE_COMMAND_REQUEST = 48, - - SUBSCRIBE_SERVICE_CALLS_REQUEST = 34, - SERVICE_CALL_RESPONSE = 35, - GET_TIME_REQUEST = 36, - GET_TIME_RESPONSE = 37, - - SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST = 38, - SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE = 39, - HOME_ASSISTANT_STATE_RESPONSE = 40, - - EXECUTE_SERVICE_REQUEST = 42, -}; - -class APIMessage { - public: - void decode(const uint8_t *buffer, size_t length); - virtual bool decode_varint(uint32_t field_id, uint32_t value); - virtual bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len); - virtual bool decode_32bit(uint32_t field_id, uint32_t value); - virtual APIMessageType message_type() const = 0; - - virtual void encode(APIBuffer &buffer); -}; - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto new file mode 100644 index 0000000000..feaf39ba15 --- /dev/null +++ b/esphome/components/api/api_options.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; +import "google/protobuf/descriptor.proto"; + + +enum APISourceType { + SOURCE_BOTH = 0; + SOURCE_SERVER = 1; + SOURCE_CLIENT = 2; +} + +message void {} + +extend google.protobuf.MethodOptions { + optional bool needs_setup_connection = 1038 [default=true]; + optional bool needs_authentication = 1039 [default=true]; +} + +extend google.protobuf.MessageOptions { + optional uint32 id = 1036 [default=0]; + optional APISourceType source = 1037 [default=SOURCE_BOTH]; + optional string ifdef = 1038; + optional bool log = 1039 [default=true]; + optional bool no_delay = 1040 [default=false]; +} diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp new file mode 100644 index 0000000000..65a0531ea4 --- /dev/null +++ b/esphome/components/api/api_pb2.cpp @@ -0,0 +1,2719 @@ +#include "api_pb2.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace api { + +template<> const char *proto_enum_to_string(LegacyCoverState value) { + switch (value) { + case LEGACY_COVER_STATE_OPEN: + return "LEGACY_COVER_STATE_OPEN"; + case LEGACY_COVER_STATE_CLOSED: + return "LEGACY_COVER_STATE_CLOSED"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(CoverOperation value) { + switch (value) { + case COVER_OPERATION_IDLE: + return "COVER_OPERATION_IDLE"; + case COVER_OPERATION_IS_OPENING: + return "COVER_OPERATION_IS_OPENING"; + case COVER_OPERATION_IS_CLOSING: + return "COVER_OPERATION_IS_CLOSING"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(LegacyCoverCommand value) { + switch (value) { + case LEGACY_COVER_COMMAND_OPEN: + return "LEGACY_COVER_COMMAND_OPEN"; + case LEGACY_COVER_COMMAND_CLOSE: + return "LEGACY_COVER_COMMAND_CLOSE"; + case LEGACY_COVER_COMMAND_STOP: + return "LEGACY_COVER_COMMAND_STOP"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(FanSpeed value) { + switch (value) { + case FAN_SPEED_LOW: + return "FAN_SPEED_LOW"; + case FAN_SPEED_MEDIUM: + return "FAN_SPEED_MEDIUM"; + case FAN_SPEED_HIGH: + return "FAN_SPEED_HIGH"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(LogLevel value) { + switch (value) { + case LOG_LEVEL_NONE: + return "LOG_LEVEL_NONE"; + case LOG_LEVEL_ERROR: + return "LOG_LEVEL_ERROR"; + case LOG_LEVEL_WARN: + return "LOG_LEVEL_WARN"; + case LOG_LEVEL_INFO: + return "LOG_LEVEL_INFO"; + case LOG_LEVEL_DEBUG: + return "LOG_LEVEL_DEBUG"; + case LOG_LEVEL_VERBOSE: + return "LOG_LEVEL_VERBOSE"; + case LOG_LEVEL_VERY_VERBOSE: + return "LOG_LEVEL_VERY_VERBOSE"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(ServiceArgType value) { + switch (value) { + case SERVICE_ARG_TYPE_BOOL: + return "SERVICE_ARG_TYPE_BOOL"; + case SERVICE_ARG_TYPE_INT: + return "SERVICE_ARG_TYPE_INT"; + case SERVICE_ARG_TYPE_FLOAT: + return "SERVICE_ARG_TYPE_FLOAT"; + case SERVICE_ARG_TYPE_STRING: + return "SERVICE_ARG_TYPE_STRING"; + case SERVICE_ARG_TYPE_BOOL_ARRAY: + return "SERVICE_ARG_TYPE_BOOL_ARRAY"; + case SERVICE_ARG_TYPE_INT_ARRAY: + return "SERVICE_ARG_TYPE_INT_ARRAY"; + case SERVICE_ARG_TYPE_FLOAT_ARRAY: + return "SERVICE_ARG_TYPE_FLOAT_ARRAY"; + case SERVICE_ARG_TYPE_STRING_ARRAY: + return "SERVICE_ARG_TYPE_STRING_ARRAY"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(ClimateMode value) { + switch (value) { + case CLIMATE_MODE_OFF: + return "CLIMATE_MODE_OFF"; + case CLIMATE_MODE_AUTO: + return "CLIMATE_MODE_AUTO"; + case CLIMATE_MODE_COOL: + return "CLIMATE_MODE_COOL"; + case CLIMATE_MODE_HEAT: + return "CLIMATE_MODE_HEAT"; + default: + return "UNKNOWN"; + } +} +bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->client_info = value.as_string(); + return true; + } + default: + return false; + } +} +void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); } +void HelloRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("HelloRequest {\n"); + out.append(" client_info: "); + out.append("'").append(this->client_info).append("'"); + out.append("\n"); + out.append("}"); +} +bool HelloResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->api_version_major = value.as_uint32(); + return true; + } + case 2: { + this->api_version_minor = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool HelloResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 3: { + this->server_info = value.as_string(); + return true; + } + default: + return false; + } +} +void HelloResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->api_version_major); + buffer.encode_uint32(2, this->api_version_minor); + buffer.encode_string(3, this->server_info); +} +void HelloResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("HelloResponse {\n"); + out.append(" api_version_major: "); + sprintf(buffer, "%u", this->api_version_major); + out.append(buffer); + out.append("\n"); + + out.append(" api_version_minor: "); + sprintf(buffer, "%u", this->api_version_minor); + out.append(buffer); + out.append("\n"); + + out.append(" server_info: "); + out.append("'").append(this->server_info).append("'"); + out.append("\n"); + out.append("}"); +} +bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->password = value.as_string(); + return true; + } + default: + return false; + } +} +void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); } +void ConnectRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("ConnectRequest {\n"); + out.append(" password: "); + out.append("'").append(this->password).append("'"); + out.append("\n"); + out.append("}"); +} +bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->invalid_password = value.as_bool(); + return true; + } + default: + return false; + } +} +void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); } +void ConnectResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ConnectResponse {\n"); + out.append(" invalid_password: "); + out.append(YESNO(this->invalid_password)); + out.append("\n"); + out.append("}"); +} +void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {} +void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); } +void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {} +void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); } +void PingRequest::encode(ProtoWriteBuffer buffer) const {} +void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); } +void PingResponse::encode(ProtoWriteBuffer buffer) const {} +void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); } +void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {} +void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } +bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->uses_password = value.as_bool(); + return true; + } + case 7: { + this->has_deep_sleep = value.as_bool(); + return true; + } + default: + return false; + } +} +bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->name = value.as_string(); + return true; + } + case 3: { + this->mac_address = value.as_string(); + return true; + } + case 4: { + this->esphome_version = value.as_string(); + return true; + } + case 5: { + this->compilation_time = value.as_string(); + return true; + } + case 6: { + this->model = value.as_string(); + return true; + } + default: + return false; + } +} +void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_bool(1, this->uses_password); + buffer.encode_string(2, this->name); + buffer.encode_string(3, this->mac_address); + buffer.encode_string(4, this->esphome_version); + buffer.encode_string(5, this->compilation_time); + buffer.encode_string(6, this->model); + buffer.encode_bool(7, this->has_deep_sleep); +} +void DeviceInfoResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("DeviceInfoResponse {\n"); + out.append(" uses_password: "); + out.append(YESNO(this->uses_password)); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" mac_address: "); + out.append("'").append(this->mac_address).append("'"); + out.append("\n"); + + out.append(" esphome_version: "); + out.append("'").append(this->esphome_version).append("'"); + out.append("\n"); + + out.append(" compilation_time: "); + out.append("'").append(this->compilation_time).append("'"); + out.append("\n"); + + out.append(" model: "); + out.append("'").append(this->model).append("'"); + out.append("\n"); + + out.append(" has_deep_sleep: "); + out.append(YESNO(this->has_deep_sleep)); + out.append("\n"); + out.append("}"); +} +void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {} +void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); } +void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {} +void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); } +void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {} +void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); } +bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 6: { + this->is_status_binary_sensor = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->device_class = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesBinarySensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->device_class); + buffer.encode_bool(6, this->is_status_binary_sensor); +} +void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesBinarySensorResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" device_class: "); + out.append("'").append(this->device_class).append("'"); + out.append("\n"); + + out.append(" is_status_binary_sensor: "); + out.append(YESNO(this->is_status_binary_sensor)); + out.append("\n"); + out.append("}"); +} +bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->state); +} +void BinarySensorStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("BinarySensorStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 5: { + this->assumed_state = value.as_bool(); + return true; + } + case 6: { + this->supports_position = value.as_bool(); + return true; + } + case 7: { + this->supports_tilt = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 8: { + this->device_class = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesCoverResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_bool(5, this->assumed_state); + buffer.encode_bool(6, this->supports_position); + buffer.encode_bool(7, this->supports_tilt); + buffer.encode_string(8, this->device_class); +} +void ListEntitiesCoverResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesCoverResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" assumed_state: "); + out.append(YESNO(this->assumed_state)); + out.append("\n"); + + out.append(" supports_position: "); + out.append(YESNO(this->supports_position)); + out.append("\n"); + + out.append(" supports_tilt: "); + out.append(YESNO(this->supports_tilt)); + out.append("\n"); + + out.append(" device_class: "); + out.append("'").append(this->device_class).append("'"); + out.append("\n"); + out.append("}"); +} +bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->legacy_state = value.as_enum(); + return true; + } + case 5: { + this->current_operation = value.as_enum(); + return true; + } + default: + return false; + } +} +bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 3: { + this->position = value.as_float(); + return true; + } + case 4: { + this->tilt = value.as_float(); + return true; + } + default: + return false; + } +} +void CoverStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_enum(2, this->legacy_state); + buffer.encode_float(3, this->position); + buffer.encode_float(4, this->tilt); + buffer.encode_enum(5, this->current_operation); +} +void CoverStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("CoverStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" legacy_state: "); + out.append(proto_enum_to_string(this->legacy_state)); + out.append("\n"); + + out.append(" position: "); + sprintf(buffer, "%g", this->position); + out.append(buffer); + out.append("\n"); + + out.append(" tilt: "); + sprintf(buffer, "%g", this->tilt); + out.append(buffer); + out.append("\n"); + + out.append(" current_operation: "); + out.append(proto_enum_to_string(this->current_operation)); + out.append("\n"); + out.append("}"); +} +bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->has_legacy_command = value.as_bool(); + return true; + } + case 3: { + this->legacy_command = value.as_enum(); + return true; + } + case 4: { + this->has_position = value.as_bool(); + return true; + } + case 6: { + this->has_tilt = value.as_bool(); + return true; + } + case 8: { + this->stop = value.as_bool(); + return true; + } + default: + return false; + } +} +bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 5: { + this->position = value.as_float(); + return true; + } + case 7: { + this->tilt = value.as_float(); + return true; + } + default: + return false; + } +} +void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->has_legacy_command); + buffer.encode_enum(3, this->legacy_command); + buffer.encode_bool(4, this->has_position); + buffer.encode_float(5, this->position); + buffer.encode_bool(6, this->has_tilt); + buffer.encode_float(7, this->tilt); + buffer.encode_bool(8, this->stop); +} +void CoverCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("CoverCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" has_legacy_command: "); + out.append(YESNO(this->has_legacy_command)); + out.append("\n"); + + out.append(" legacy_command: "); + out.append(proto_enum_to_string(this->legacy_command)); + out.append("\n"); + + out.append(" has_position: "); + out.append(YESNO(this->has_position)); + out.append("\n"); + + out.append(" position: "); + sprintf(buffer, "%g", this->position); + out.append(buffer); + out.append("\n"); + + out.append(" has_tilt: "); + out.append(YESNO(this->has_tilt)); + out.append("\n"); + + out.append(" tilt: "); + sprintf(buffer, "%g", this->tilt); + out.append(buffer); + out.append("\n"); + + out.append(" stop: "); + out.append(YESNO(this->stop)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 5: { + this->supports_oscillation = value.as_bool(); + return true; + } + case 6: { + this->supports_speed = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesFanResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_bool(5, this->supports_oscillation); + buffer.encode_bool(6, this->supports_speed); +} +void ListEntitiesFanResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesFanResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" supports_oscillation: "); + out.append(YESNO(this->supports_oscillation)); + out.append("\n"); + + out.append(" supports_speed: "); + out.append(YESNO(this->supports_speed)); + out.append("\n"); + out.append("}"); +} +bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_bool(); + return true; + } + case 3: { + this->oscillating = value.as_bool(); + return true; + } + case 4: { + this->speed = value.as_enum(); + return true; + } + default: + return false; + } +} +bool FanStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void FanStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->state); + buffer.encode_bool(3, this->oscillating); + buffer.encode_enum(4, this->speed); +} +void FanStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("FanStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + + out.append(" oscillating: "); + out.append(YESNO(this->oscillating)); + out.append("\n"); + + out.append(" speed: "); + out.append(proto_enum_to_string(this->speed)); + out.append("\n"); + out.append("}"); +} +bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->has_state = value.as_bool(); + return true; + } + case 3: { + this->state = value.as_bool(); + return true; + } + case 4: { + this->has_speed = value.as_bool(); + return true; + } + case 5: { + this->speed = value.as_enum(); + return true; + } + case 6: { + this->has_oscillating = value.as_bool(); + return true; + } + case 7: { + this->oscillating = value.as_bool(); + return true; + } + default: + return false; + } +} +bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->has_state); + buffer.encode_bool(3, this->state); + buffer.encode_bool(4, this->has_speed); + buffer.encode_enum(5, this->speed); + buffer.encode_bool(6, this->has_oscillating); + buffer.encode_bool(7, this->oscillating); +} +void FanCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("FanCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" has_state: "); + out.append(YESNO(this->has_state)); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + + out.append(" has_speed: "); + out.append(YESNO(this->has_speed)); + out.append("\n"); + + out.append(" speed: "); + out.append(proto_enum_to_string(this->speed)); + out.append("\n"); + + out.append(" has_oscillating: "); + out.append(YESNO(this->has_oscillating)); + out.append("\n"); + + out.append(" oscillating: "); + out.append(YESNO(this->oscillating)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 5: { + this->supports_brightness = value.as_bool(); + return true; + } + case 6: { + this->supports_rgb = value.as_bool(); + return true; + } + case 7: { + this->supports_white_value = value.as_bool(); + return true; + } + case 8: { + this->supports_color_temperature = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 11: { + this->effects.push_back(value.as_string()); + return true; + } + default: + return false; + } +} +bool ListEntitiesLightResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + case 9: { + this->min_mireds = value.as_float(); + return true; + } + case 10: { + this->max_mireds = value.as_float(); + return true; + } + default: + return false; + } +} +void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_bool(5, this->supports_brightness); + buffer.encode_bool(6, this->supports_rgb); + buffer.encode_bool(7, this->supports_white_value); + buffer.encode_bool(8, this->supports_color_temperature); + buffer.encode_float(9, this->min_mireds); + buffer.encode_float(10, this->max_mireds); + for (auto &it : this->effects) { + buffer.encode_string(11, it, true); + } +} +void ListEntitiesLightResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesLightResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" supports_brightness: "); + out.append(YESNO(this->supports_brightness)); + out.append("\n"); + + out.append(" supports_rgb: "); + out.append(YESNO(this->supports_rgb)); + out.append("\n"); + + out.append(" supports_white_value: "); + out.append(YESNO(this->supports_white_value)); + out.append("\n"); + + out.append(" supports_color_temperature: "); + out.append(YESNO(this->supports_color_temperature)); + out.append("\n"); + + out.append(" min_mireds: "); + sprintf(buffer, "%g", this->min_mireds); + out.append(buffer); + out.append("\n"); + + out.append(" max_mireds: "); + sprintf(buffer, "%g", this->max_mireds); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->effects) { + out.append(" effects: "); + out.append("'").append(it).append("'"); + out.append("\n"); + } + out.append("}"); +} +bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool LightStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 9: { + this->effect = value.as_string(); + return true; + } + default: + return false; + } +} +bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 3: { + this->brightness = value.as_float(); + return true; + } + case 4: { + this->red = value.as_float(); + return true; + } + case 5: { + this->green = value.as_float(); + return true; + } + case 6: { + this->blue = value.as_float(); + return true; + } + case 7: { + this->white = value.as_float(); + return true; + } + case 8: { + this->color_temperature = value.as_float(); + return true; + } + default: + return false; + } +} +void LightStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->state); + buffer.encode_float(3, this->brightness); + buffer.encode_float(4, this->red); + buffer.encode_float(5, this->green); + buffer.encode_float(6, this->blue); + buffer.encode_float(7, this->white); + buffer.encode_float(8, this->color_temperature); + buffer.encode_string(9, this->effect); +} +void LightStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("LightStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + + out.append(" brightness: "); + sprintf(buffer, "%g", this->brightness); + out.append(buffer); + out.append("\n"); + + out.append(" red: "); + sprintf(buffer, "%g", this->red); + out.append(buffer); + out.append("\n"); + + out.append(" green: "); + sprintf(buffer, "%g", this->green); + out.append(buffer); + out.append("\n"); + + out.append(" blue: "); + sprintf(buffer, "%g", this->blue); + out.append(buffer); + out.append("\n"); + + out.append(" white: "); + sprintf(buffer, "%g", this->white); + out.append(buffer); + out.append("\n"); + + out.append(" color_temperature: "); + sprintf(buffer, "%g", this->color_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" effect: "); + out.append("'").append(this->effect).append("'"); + out.append("\n"); + out.append("}"); +} +bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->has_state = value.as_bool(); + return true; + } + case 3: { + this->state = value.as_bool(); + return true; + } + case 4: { + this->has_brightness = value.as_bool(); + return true; + } + case 6: { + this->has_rgb = value.as_bool(); + return true; + } + case 10: { + this->has_white = value.as_bool(); + return true; + } + case 12: { + this->has_color_temperature = value.as_bool(); + return true; + } + case 14: { + this->has_transition_length = value.as_bool(); + return true; + } + case 15: { + this->transition_length = value.as_uint32(); + return true; + } + case 16: { + this->has_flash_length = value.as_bool(); + return true; + } + case 17: { + this->flash_length = value.as_uint32(); + return true; + } + case 18: { + this->has_effect = value.as_bool(); + return true; + } + default: + return false; + } +} +bool LightCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 19: { + this->effect = value.as_string(); + return true; + } + default: + return false; + } +} +bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 5: { + this->brightness = value.as_float(); + return true; + } + case 7: { + this->red = value.as_float(); + return true; + } + case 8: { + this->green = value.as_float(); + return true; + } + case 9: { + this->blue = value.as_float(); + return true; + } + case 11: { + this->white = value.as_float(); + return true; + } + case 13: { + this->color_temperature = value.as_float(); + return true; + } + default: + return false; + } +} +void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->has_state); + buffer.encode_bool(3, this->state); + buffer.encode_bool(4, this->has_brightness); + buffer.encode_float(5, this->brightness); + buffer.encode_bool(6, this->has_rgb); + buffer.encode_float(7, this->red); + buffer.encode_float(8, this->green); + buffer.encode_float(9, this->blue); + buffer.encode_bool(10, this->has_white); + buffer.encode_float(11, this->white); + buffer.encode_bool(12, this->has_color_temperature); + buffer.encode_float(13, this->color_temperature); + buffer.encode_bool(14, this->has_transition_length); + buffer.encode_uint32(15, this->transition_length); + buffer.encode_bool(16, this->has_flash_length); + buffer.encode_uint32(17, this->flash_length); + buffer.encode_bool(18, this->has_effect); + buffer.encode_string(19, this->effect); +} +void LightCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("LightCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" has_state: "); + out.append(YESNO(this->has_state)); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + + out.append(" has_brightness: "); + out.append(YESNO(this->has_brightness)); + out.append("\n"); + + out.append(" brightness: "); + sprintf(buffer, "%g", this->brightness); + out.append(buffer); + out.append("\n"); + + out.append(" has_rgb: "); + out.append(YESNO(this->has_rgb)); + out.append("\n"); + + out.append(" red: "); + sprintf(buffer, "%g", this->red); + out.append(buffer); + out.append("\n"); + + out.append(" green: "); + sprintf(buffer, "%g", this->green); + out.append(buffer); + out.append("\n"); + + out.append(" blue: "); + sprintf(buffer, "%g", this->blue); + out.append(buffer); + out.append("\n"); + + out.append(" has_white: "); + out.append(YESNO(this->has_white)); + out.append("\n"); + + out.append(" white: "); + sprintf(buffer, "%g", this->white); + out.append(buffer); + out.append("\n"); + + out.append(" has_color_temperature: "); + out.append(YESNO(this->has_color_temperature)); + out.append("\n"); + + out.append(" color_temperature: "); + sprintf(buffer, "%g", this->color_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" has_transition_length: "); + out.append(YESNO(this->has_transition_length)); + out.append("\n"); + + out.append(" transition_length: "); + sprintf(buffer, "%u", this->transition_length); + out.append(buffer); + out.append("\n"); + + out.append(" has_flash_length: "); + out.append(YESNO(this->has_flash_length)); + out.append("\n"); + + out.append(" flash_length: "); + sprintf(buffer, "%u", this->flash_length); + out.append(buffer); + out.append("\n"); + + out.append(" has_effect: "); + out.append(YESNO(this->has_effect)); + out.append("\n"); + + out.append(" effect: "); + out.append("'").append(this->effect).append("'"); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 7: { + this->accuracy_decimals = value.as_int32(); + return true; + } + default: + return false; + } +} +bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + case 6: { + this->unit_of_measurement = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesSensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); + buffer.encode_string(6, this->unit_of_measurement); + buffer.encode_int32(7, this->accuracy_decimals); +} +void ListEntitiesSensorResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesSensorResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + + out.append(" unit_of_measurement: "); + out.append("'").append(this->unit_of_measurement).append("'"); + out.append("\n"); + + out.append(" accuracy_decimals: "); + sprintf(buffer, "%d", this->accuracy_decimals); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 2: { + this->state = value.as_float(); + return true; + } + default: + return false; + } +} +void SensorStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_float(2, this->state); +} +void SensorStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("SensorStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + sprintf(buffer, "%g", this->state); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 6: { + this->assumed_state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesSwitchResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); + buffer.encode_bool(6, this->assumed_state); +} +void ListEntitiesSwitchResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesSwitchResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + + out.append(" assumed_state: "); + out.append(YESNO(this->assumed_state)); + out.append("\n"); + out.append("}"); +} +bool SwitchStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool SwitchStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->state); +} +void SwitchStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("SwitchStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + out.append("}"); +} +bool SwitchCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->state); +} +void SwitchCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("SwitchCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(YESNO(this->state)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesTextSensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); +} +void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesTextSensorResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + out.append("}"); +} +bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->state = value.as_string(); + return true; + } + default: + return false; + } +} +bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_string(2, this->state); +} +void TextSensorStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("TextSensorStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append("'").append(this->state).append("'"); + out.append("\n"); + out.append("}"); +} +bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->level = value.as_enum(); + return true; + } + case 2: { + this->dump_config = value.as_bool(); + return true; + } + default: + return false; + } +} +void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_enum(1, this->level); + buffer.encode_bool(2, this->dump_config); +} +void SubscribeLogsRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("SubscribeLogsRequest {\n"); + out.append(" level: "); + out.append(proto_enum_to_string(this->level)); + out.append("\n"); + + out.append(" dump_config: "); + out.append(YESNO(this->dump_config)); + out.append("\n"); + out.append("}"); +} +bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->level = value.as_enum(); + return true; + } + case 4: { + this->send_failed = value.as_bool(); + return true; + } + default: + return false; + } +} +bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->tag = value.as_string(); + return true; + } + case 3: { + this->message = value.as_string(); + return true; + } + default: + return false; + } +} +void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_enum(1, this->level); + buffer.encode_string(2, this->tag); + buffer.encode_string(3, this->message); + buffer.encode_bool(4, this->send_failed); +} +void SubscribeLogsResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("SubscribeLogsResponse {\n"); + out.append(" level: "); + out.append(proto_enum_to_string(this->level)); + out.append("\n"); + + out.append(" tag: "); + out.append("'").append(this->tag).append("'"); + out.append("\n"); + + out.append(" message: "); + out.append("'").append(this->message).append("'"); + out.append("\n"); + + out.append(" send_failed: "); + out.append(YESNO(this->send_failed)); + out.append("\n"); + out.append("}"); +} +void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {} +void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const { + out.append("SubscribeHomeassistantServicesRequest {}"); +} +bool HomeassistantServiceMap::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->key = value.as_string(); + return true; + } + case 2: { + this->value = value.as_string(); + return true; + } + default: + return false; + } +} +void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->key); + buffer.encode_string(2, this->value); +} +void HomeassistantServiceMap::dump_to(std::string &out) const { + char buffer[64]; + out.append("HomeassistantServiceMap {\n"); + out.append(" key: "); + out.append("'").append(this->key).append("'"); + out.append("\n"); + + out.append(" value: "); + out.append("'").append(this->value).append("'"); + out.append("\n"); + out.append("}"); +} +bool HomeassistantServiceResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 5: { + this->is_event = value.as_bool(); + return true; + } + default: + return false; + } +} +bool HomeassistantServiceResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->service = value.as_string(); + return true; + } + case 2: { + this->data.push_back(value.as_message()); + return true; + } + case 3: { + this->data_template.push_back(value.as_message()); + return true; + } + case 4: { + this->variables.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->service); + for (auto &it : this->data) { + buffer.encode_message(2, it, true); + } + for (auto &it : this->data_template) { + buffer.encode_message(3, it, true); + } + for (auto &it : this->variables) { + buffer.encode_message(4, it, true); + } + buffer.encode_bool(5, this->is_event); +} +void HomeassistantServiceResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("HomeassistantServiceResponse {\n"); + out.append(" service: "); + out.append("'").append(this->service).append("'"); + out.append("\n"); + + for (const auto &it : this->data) { + out.append(" data: "); + it.dump_to(out); + out.append("\n"); + } + + for (const auto &it : this->data_template) { + out.append(" data_template: "); + it.dump_to(out); + out.append("\n"); + } + + for (const auto &it : this->variables) { + out.append(" variables: "); + it.dump_to(out); + out.append("\n"); + } + + out.append(" is_event: "); + out.append(YESNO(this->is_event)); + out.append("\n"); + out.append("}"); +} +void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {} +void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { + out.append("SubscribeHomeAssistantStatesRequest {}"); +} +bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->entity_id = value.as_string(); + return true; + } + default: + return false; + } +} +void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->entity_id); +} +void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("SubscribeHomeAssistantStateResponse {\n"); + out.append(" entity_id: "); + out.append("'").append(this->entity_id).append("'"); + out.append("\n"); + out.append("}"); +} +bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->entity_id = value.as_string(); + return true; + } + case 2: { + this->state = value.as_string(); + return true; + } + default: + return false; + } +} +void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->entity_id); + buffer.encode_string(2, this->state); +} +void HomeAssistantStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("HomeAssistantStateResponse {\n"); + out.append(" entity_id: "); + out.append("'").append(this->entity_id).append("'"); + out.append("\n"); + + out.append(" state: "); + out.append("'").append(this->state).append("'"); + out.append("\n"); + out.append("}"); +} +void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {} +void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); } +bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->epoch_seconds = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); } +void GetTimeResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("GetTimeResponse {\n"); + out.append(" epoch_seconds: "); + sprintf(buffer, "%u", this->epoch_seconds); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->type = value.as_enum(); + return true; + } + default: + return false; + } +} +bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->name = value.as_string(); + return true; + } + default: + return false; + } +} +void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->name); + buffer.encode_enum(2, this->type); +} +void ListEntitiesServicesArgument::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesServicesArgument {\n"); + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" type: "); + out.append(proto_enum_to_string(this->type)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->name = value.as_string(); + return true; + } + case 3: { + this->args.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +bool ListEntitiesServicesResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->name); + buffer.encode_fixed32(2, this->key); + for (auto &it : this->args) { + buffer.encode_message(3, it, true); + } +} +void ListEntitiesServicesResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesServicesResponse {\n"); + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->args) { + out.append(" args: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +bool ExecuteServiceArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->bool_ = value.as_bool(); + return true; + } + case 2: { + this->legacy_int = value.as_int32(); + return true; + } + case 5: { + this->int_ = value.as_sint32(); + return true; + } + case 6: { + this->bool_array.push_back(value.as_bool()); + return true; + } + case 7: { + this->int_array.push_back(value.as_sint32()); + return true; + } + default: + return false; + } +} +bool ExecuteServiceArgument::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 4: { + this->string_ = value.as_string(); + return true; + } + case 9: { + this->string_array.push_back(value.as_string()); + return true; + } + default: + return false; + } +} +bool ExecuteServiceArgument::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 3: { + this->float_ = value.as_float(); + return true; + } + case 8: { + this->float_array.push_back(value.as_float()); + return true; + } + default: + return false; + } +} +void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const { + buffer.encode_bool(1, this->bool_); + buffer.encode_int32(2, this->legacy_int); + buffer.encode_float(3, this->float_); + buffer.encode_string(4, this->string_); + buffer.encode_sint32(5, this->int_); + for (auto it : this->bool_array) { + buffer.encode_bool(6, it, true); + } + for (auto &it : this->int_array) { + buffer.encode_sint32(7, it, true); + } + for (auto &it : this->float_array) { + buffer.encode_float(8, it, true); + } + for (auto &it : this->string_array) { + buffer.encode_string(9, it, true); + } +} +void ExecuteServiceArgument::dump_to(std::string &out) const { + char buffer[64]; + out.append("ExecuteServiceArgument {\n"); + out.append(" bool_: "); + out.append(YESNO(this->bool_)); + out.append("\n"); + + out.append(" legacy_int: "); + sprintf(buffer, "%d", this->legacy_int); + out.append(buffer); + out.append("\n"); + + out.append(" float_: "); + sprintf(buffer, "%g", this->float_); + out.append(buffer); + out.append("\n"); + + out.append(" string_: "); + out.append("'").append(this->string_).append("'"); + out.append("\n"); + + out.append(" int_: "); + sprintf(buffer, "%d", this->int_); + out.append(buffer); + out.append("\n"); + + for (const auto it : this->bool_array) { + out.append(" bool_array: "); + out.append(YESNO(it)); + out.append("\n"); + } + + for (const auto &it : this->int_array) { + out.append(" int_array: "); + sprintf(buffer, "%d", it); + out.append(buffer); + out.append("\n"); + } + + for (const auto &it : this->float_array) { + out.append(" float_array: "); + sprintf(buffer, "%g", it); + out.append(buffer); + out.append("\n"); + } + + for (const auto &it : this->string_array) { + out.append(" string_array: "); + out.append("'").append(it).append("'"); + out.append("\n"); + } + out.append("}"); +} +bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->args.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + for (auto &it : this->args) { + buffer.encode_message(2, it, true); + } +} +void ExecuteServiceRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("ExecuteServiceRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->args) { + out.append(" args: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesCameraResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); +} +void ListEntitiesCameraResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesCameraResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + out.append("}"); +} +bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->done = value.as_bool(); + return true; + } + default: + return false; + } +} +bool CameraImageResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->data = value.as_string(); + return true; + } + default: + return false; + } +} +bool CameraImageResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void CameraImageResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_string(2, this->data); + buffer.encode_bool(3, this->done); +} +void CameraImageResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("CameraImageResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); + + out.append(" done: "); + out.append(YESNO(this->done)); + out.append("\n"); + out.append("}"); +} +bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->single = value.as_bool(); + return true; + } + case 2: { + this->stream = value.as_bool(); + return true; + } + default: + return false; + } +} +void CameraImageRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_bool(1, this->single); + buffer.encode_bool(2, this->stream); +} +void CameraImageRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("CameraImageRequest {\n"); + out.append(" single: "); + out.append(YESNO(this->single)); + out.append("\n"); + + out.append(" stream: "); + out.append(YESNO(this->stream)); + out.append("\n"); + out.append("}"); +} +bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 5: { + this->supports_current_temperature = value.as_bool(); + return true; + } + case 6: { + this->supports_two_point_target_temperature = value.as_bool(); + return true; + } + case 7: { + this->supported_modes.push_back(value.as_enum()); + return true; + } + case 11: { + this->supports_away = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesClimateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + case 8: { + this->visual_min_temperature = value.as_float(); + return true; + } + case 9: { + this->visual_max_temperature = value.as_float(); + return true; + } + case 10: { + this->visual_temperature_step = value.as_float(); + return true; + } + default: + return false; + } +} +void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_bool(5, this->supports_current_temperature); + buffer.encode_bool(6, this->supports_two_point_target_temperature); + for (auto &it : this->supported_modes) { + buffer.encode_enum(7, it, true); + } + buffer.encode_float(8, this->visual_min_temperature); + buffer.encode_float(9, this->visual_max_temperature); + buffer.encode_float(10, this->visual_temperature_step); + buffer.encode_bool(11, this->supports_away); +} +void ListEntitiesClimateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesClimateResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" supports_current_temperature: "); + out.append(YESNO(this->supports_current_temperature)); + out.append("\n"); + + out.append(" supports_two_point_target_temperature: "); + out.append(YESNO(this->supports_two_point_target_temperature)); + out.append("\n"); + + for (const auto &it : this->supported_modes) { + out.append(" supported_modes: "); + out.append(proto_enum_to_string(it)); + out.append("\n"); + } + + out.append(" visual_min_temperature: "); + sprintf(buffer, "%g", this->visual_min_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" visual_max_temperature: "); + sprintf(buffer, "%g", this->visual_max_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" visual_temperature_step: "); + sprintf(buffer, "%g", this->visual_temperature_step); + out.append(buffer); + out.append("\n"); + + out.append(" supports_away: "); + out.append(YESNO(this->supports_away)); + out.append("\n"); + out.append("}"); +} +bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->mode = value.as_enum(); + return true; + } + case 7: { + this->away = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 3: { + this->current_temperature = value.as_float(); + return true; + } + case 4: { + this->target_temperature = value.as_float(); + return true; + } + case 5: { + this->target_temperature_low = value.as_float(); + return true; + } + case 6: { + this->target_temperature_high = value.as_float(); + return true; + } + default: + return false; + } +} +void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_enum(2, this->mode); + buffer.encode_float(3, this->current_temperature); + buffer.encode_float(4, this->target_temperature); + buffer.encode_float(5, this->target_temperature_low); + buffer.encode_float(6, this->target_temperature_high); + buffer.encode_bool(7, this->away); +} +void ClimateStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ClimateStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" mode: "); + out.append(proto_enum_to_string(this->mode)); + out.append("\n"); + + out.append(" current_temperature: "); + sprintf(buffer, "%g", this->current_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" target_temperature: "); + sprintf(buffer, "%g", this->target_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" target_temperature_low: "); + sprintf(buffer, "%g", this->target_temperature_low); + out.append(buffer); + out.append("\n"); + + out.append(" target_temperature_high: "); + sprintf(buffer, "%g", this->target_temperature_high); + out.append(buffer); + out.append("\n"); + + out.append(" away: "); + out.append(YESNO(this->away)); + out.append("\n"); + out.append("}"); +} +bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->has_mode = value.as_bool(); + return true; + } + case 3: { + this->mode = value.as_enum(); + return true; + } + case 4: { + this->has_target_temperature = value.as_bool(); + return true; + } + case 6: { + this->has_target_temperature_low = value.as_bool(); + return true; + } + case 8: { + this->has_target_temperature_high = value.as_bool(); + return true; + } + case 10: { + this->has_away = value.as_bool(); + return true; + } + case 11: { + this->away = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 5: { + this->target_temperature = value.as_float(); + return true; + } + case 7: { + this->target_temperature_low = value.as_float(); + return true; + } + case 9: { + this->target_temperature_high = value.as_float(); + return true; + } + default: + return false; + } +} +void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->has_mode); + buffer.encode_enum(3, this->mode); + buffer.encode_bool(4, this->has_target_temperature); + buffer.encode_float(5, this->target_temperature); + buffer.encode_bool(6, this->has_target_temperature_low); + buffer.encode_float(7, this->target_temperature_low); + buffer.encode_bool(8, this->has_target_temperature_high); + buffer.encode_float(9, this->target_temperature_high); + buffer.encode_bool(10, this->has_away); + buffer.encode_bool(11, this->away); +} +void ClimateCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("ClimateCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" has_mode: "); + out.append(YESNO(this->has_mode)); + out.append("\n"); + + out.append(" mode: "); + out.append(proto_enum_to_string(this->mode)); + out.append("\n"); + + out.append(" has_target_temperature: "); + out.append(YESNO(this->has_target_temperature)); + out.append("\n"); + + out.append(" target_temperature: "); + sprintf(buffer, "%g", this->target_temperature); + out.append(buffer); + out.append("\n"); + + out.append(" has_target_temperature_low: "); + out.append(YESNO(this->has_target_temperature_low)); + out.append("\n"); + + out.append(" target_temperature_low: "); + sprintf(buffer, "%g", this->target_temperature_low); + out.append(buffer); + out.append("\n"); + + out.append(" has_target_temperature_high: "); + out.append(YESNO(this->has_target_temperature_high)); + out.append("\n"); + + out.append(" target_temperature_high: "); + sprintf(buffer, "%g", this->target_temperature_high); + out.append(buffer); + out.append("\n"); + + out.append(" has_away: "); + out.append(YESNO(this->has_away)); + out.append("\n"); + + out.append(" away: "); + out.append(YESNO(this->away)); + out.append("\n"); + out.append("}"); +} + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h new file mode 100644 index 0000000000..6836bb847d --- /dev/null +++ b/esphome/components/api/api_pb2.h @@ -0,0 +1,687 @@ +#pragma once + +#include "proto.h" + +namespace esphome { +namespace api { + +enum LegacyCoverState : uint32_t { + LEGACY_COVER_STATE_OPEN = 0, + LEGACY_COVER_STATE_CLOSED = 1, +}; +enum CoverOperation : uint32_t { + COVER_OPERATION_IDLE = 0, + COVER_OPERATION_IS_OPENING = 1, + COVER_OPERATION_IS_CLOSING = 2, +}; +enum LegacyCoverCommand : uint32_t { + LEGACY_COVER_COMMAND_OPEN = 0, + LEGACY_COVER_COMMAND_CLOSE = 1, + LEGACY_COVER_COMMAND_STOP = 2, +}; +enum FanSpeed : uint32_t { + FAN_SPEED_LOW = 0, + FAN_SPEED_MEDIUM = 1, + FAN_SPEED_HIGH = 2, +}; +enum LogLevel : uint32_t { + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR = 1, + LOG_LEVEL_WARN = 2, + LOG_LEVEL_INFO = 3, + LOG_LEVEL_DEBUG = 4, + LOG_LEVEL_VERBOSE = 5, + LOG_LEVEL_VERY_VERBOSE = 6, +}; +enum ServiceArgType : uint32_t { + 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 ClimateMode : uint32_t { + CLIMATE_MODE_OFF = 0, + CLIMATE_MODE_AUTO = 1, + CLIMATE_MODE_COOL = 2, + CLIMATE_MODE_HEAT = 3, +}; +class HelloRequest : public ProtoMessage { + public: + std::string client_info{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class HelloResponse : public ProtoMessage { + public: + uint32_t api_version_major{0}; // NOLINT + uint32_t api_version_minor{0}; // NOLINT + std::string server_info{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ConnectRequest : public ProtoMessage { + public: + std::string password{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class ConnectResponse : public ProtoMessage { + public: + bool invalid_password{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class DisconnectRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class DisconnectResponse : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class PingRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class PingResponse : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class DeviceInfoRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class DeviceInfoResponse : public ProtoMessage { + public: + bool uses_password{false}; // NOLINT + std::string name{}; // NOLINT + std::string mac_address{}; // NOLINT + std::string esphome_version{}; // NOLINT + std::string compilation_time{}; // NOLINT + std::string model{}; // NOLINT + bool has_deep_sleep{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class ListEntitiesDoneResponse : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class SubscribeStatesRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class ListEntitiesBinarySensorResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + std::string device_class{}; // NOLINT + bool is_status_binary_sensor{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BinarySensorStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool state{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesCoverResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + bool assumed_state{false}; // NOLINT + bool supports_position{false}; // NOLINT + bool supports_tilt{false}; // NOLINT + std::string device_class{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class CoverStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + LegacyCoverState legacy_state{}; // NOLINT + float position{0.0f}; // NOLINT + float tilt{0.0f}; // NOLINT + CoverOperation current_operation{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class CoverCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool has_legacy_command{false}; // NOLINT + LegacyCoverCommand legacy_command{}; // NOLINT + bool has_position{false}; // NOLINT + float position{0.0f}; // NOLINT + bool has_tilt{false}; // NOLINT + float tilt{0.0f}; // NOLINT + bool stop{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesFanResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + bool supports_oscillation{false}; // NOLINT + bool supports_speed{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class FanStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool state{false}; // NOLINT + bool oscillating{false}; // NOLINT + FanSpeed speed{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class FanCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool has_state{false}; // NOLINT + bool state{false}; // NOLINT + bool has_speed{false}; // NOLINT + FanSpeed speed{}; // NOLINT + bool has_oscillating{false}; // NOLINT + bool oscillating{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesLightResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + bool supports_brightness{false}; // NOLINT + bool supports_rgb{false}; // NOLINT + bool supports_white_value{false}; // NOLINT + bool supports_color_temperature{false}; // NOLINT + float min_mireds{0.0f}; // NOLINT + float max_mireds{0.0f}; // NOLINT + std::vector effects{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class LightStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool state{false}; // NOLINT + float brightness{0.0f}; // NOLINT + float red{0.0f}; // NOLINT + float green{0.0f}; // NOLINT + float blue{0.0f}; // NOLINT + float white{0.0f}; // NOLINT + float color_temperature{0.0f}; // NOLINT + std::string effect{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class LightCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool has_state{false}; // NOLINT + bool state{false}; // NOLINT + bool has_brightness{false}; // NOLINT + float brightness{0.0f}; // NOLINT + bool has_rgb{false}; // NOLINT + float red{0.0f}; // NOLINT + float green{0.0f}; // NOLINT + float blue{0.0f}; // NOLINT + bool has_white{false}; // NOLINT + float white{0.0f}; // NOLINT + bool has_color_temperature{false}; // NOLINT + float color_temperature{0.0f}; // NOLINT + bool has_transition_length{false}; // NOLINT + uint32_t transition_length{0}; // NOLINT + bool has_flash_length{false}; // NOLINT + uint32_t flash_length{0}; // NOLINT + bool has_effect{false}; // NOLINT + std::string effect{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesSensorResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + std::string icon{}; // NOLINT + std::string unit_of_measurement{}; // NOLINT + int32_t accuracy_decimals{0}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SensorStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + float state{0.0f}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; +}; +class ListEntitiesSwitchResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + std::string icon{}; // NOLINT + bool assumed_state{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SwitchStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool state{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SwitchCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool state{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesTextSensorResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + std::string icon{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class TextSensorStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + std::string state{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class SubscribeLogsRequest : public ProtoMessage { + public: + LogLevel level{}; // NOLINT + bool dump_config{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SubscribeLogsResponse : public ProtoMessage { + public: + LogLevel level{}; // NOLINT + std::string tag{}; // NOLINT + std::string message{}; // NOLINT + bool send_failed{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SubscribeHomeassistantServicesRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class HomeassistantServiceMap : public ProtoMessage { + public: + std::string key{}; // NOLINT + std::string value{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class HomeassistantServiceResponse : public ProtoMessage { + public: + std::string service{}; // NOLINT + std::vector data{}; // NOLINT + std::vector data_template{}; // NOLINT + std::vector variables{}; // NOLINT + bool is_event{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SubscribeHomeAssistantStatesRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class SubscribeHomeAssistantStateResponse : public ProtoMessage { + public: + std::string entity_id{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class HomeAssistantStateResponse : public ProtoMessage { + public: + std::string entity_id{}; // NOLINT + std::string state{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class GetTimeRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: +}; +class GetTimeResponse : public ProtoMessage { + public: + uint32_t epoch_seconds{0}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; +}; +class ListEntitiesServicesArgument : public ProtoMessage { + public: + std::string name{}; // NOLINT + ServiceArgType type{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesServicesResponse : public ProtoMessage { + public: + std::string name{}; // NOLINT + uint32_t key{0}; // NOLINT + std::vector args{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class ExecuteServiceArgument : public ProtoMessage { + public: + bool bool_{false}; // NOLINT + int32_t legacy_int{0}; // NOLINT + float float_{0.0f}; // NOLINT + std::string string_{}; // NOLINT + int32_t int_{0}; // NOLINT + std::vector bool_array{}; // NOLINT + std::vector int_array{}; // NOLINT + std::vector float_array{}; // NOLINT + std::vector string_array{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ExecuteServiceRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + std::vector args{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class ListEntitiesCameraResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class CameraImageResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + std::string data{}; // NOLINT + bool done{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class CameraImageRequest : public ProtoMessage { + public: + bool single{false}; // NOLINT + bool stream{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ListEntitiesClimateResponse : public ProtoMessage { + public: + std::string object_id{}; // NOLINT + uint32_t key{0}; // NOLINT + std::string name{}; // NOLINT + std::string unique_id{}; // NOLINT + bool supports_current_temperature{false}; // NOLINT + bool supports_two_point_target_temperature{false}; // NOLINT + std::vector supported_modes{}; // NOLINT + float visual_min_temperature{0.0f}; // NOLINT + float visual_max_temperature{0.0f}; // NOLINT + float visual_temperature_step{0.0f}; // NOLINT + bool supports_away{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ClimateStateResponse : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + ClimateMode mode{}; // NOLINT + float current_temperature{0.0f}; // NOLINT + float target_temperature{0.0f}; // NOLINT + float target_temperature_low{0.0f}; // NOLINT + float target_temperature_high{0.0f}; // NOLINT + bool away{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class ClimateCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; // NOLINT + bool has_mode{false}; // NOLINT + ClimateMode mode{}; // NOLINT + bool has_target_temperature{false}; // NOLINT + float target_temperature{0.0f}; // NOLINT + bool has_target_temperature_low{false}; // NOLINT + float target_temperature_low{0.0f}; // NOLINT + bool has_target_temperature_high{false}; // NOLINT + float target_temperature_high{0.0f}; // NOLINT + bool has_away{false}; // NOLINT + bool away{false}; // NOLINT + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp new file mode 100644 index 0000000000..13e123c10f --- /dev/null +++ b/esphome/components/api/api_pb2_service.cpp @@ -0,0 +1,582 @@ +#include "api_pb2_service.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace api { + +static const char *TAG = "api.service"; + +bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { + ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 2); +} +bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) { + ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 4); +} +bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) { + ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 5); +} +bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) { + ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 6); +} +bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) { + ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 7); +} +bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) { + ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 8); +} +bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) { + ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 10); +} +bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 19); +} +#ifdef USE_BINARY_SENSOR +bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 12); +} +#endif +#ifdef USE_BINARY_SENSOR +bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) { + ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 21); +} +#endif +#ifdef USE_COVER +bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 13); +} +#endif +#ifdef USE_COVER +bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) { + ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 22); +} +#endif +#ifdef USE_COVER +#endif +#ifdef USE_FAN +bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 14); +} +#endif +#ifdef USE_FAN +bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) { + ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 23); +} +#endif +#ifdef USE_FAN +#endif +#ifdef USE_LIGHT +bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 15); +} +#endif +#ifdef USE_LIGHT +bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) { + ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 24); +} +#endif +#ifdef USE_LIGHT +#endif +#ifdef USE_SENSOR +bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 16); +} +#endif +#ifdef USE_SENSOR +bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) { + ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 25); +} +#endif +#ifdef USE_SWITCH +bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 17); +} +#endif +#ifdef USE_SWITCH +bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) { + ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 26); +} +#endif +#ifdef USE_SWITCH +#endif +#ifdef USE_TEXT_SENSOR +bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 18); +} +#endif +#ifdef USE_TEXT_SENSOR +bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) { + ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 27); +} +#endif +bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { + this->set_nodelay(false); + return this->send_message_(msg, 29); +} +bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) { + ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 35); +} +bool APIServerConnectionBase::send_subscribe_home_assistant_state_response( + const SubscribeHomeAssistantStateResponse &msg) { + ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 39); +} +bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) { + ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 36); +} +bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) { + ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 37); +} +bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 41); +} +#ifdef USE_ESP32_CAMERA +bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 43); +} +#endif +#ifdef USE_ESP32_CAMERA +bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) { + ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 44); +} +#endif +#ifdef USE_ESP32_CAMERA +#endif +#ifdef USE_CLIMATE +bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str()); + this->set_nodelay(false); + return this->send_message_(msg, 46); +} +#endif +#ifdef USE_CLIMATE +bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) { + ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str()); + this->set_nodelay(true); + return this->send_message_(msg, 47); +} +#endif +#ifdef USE_CLIMATE +#endif +bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { + switch (msg_type) { + case 1: { + HelloRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str()); + this->on_hello_request(msg); + break; + } + case 3: { + ConnectRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str()); + this->on_connect_request(msg); + break; + } + case 5: { + DisconnectRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str()); + this->on_disconnect_request(msg); + break; + } + case 6: { + DisconnectResponse msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str()); + this->on_disconnect_response(msg); + break; + } + case 7: { + PingRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str()); + this->on_ping_request(msg); + break; + } + case 8: { + PingResponse msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str()); + this->on_ping_response(msg); + break; + } + case 9: { + DeviceInfoRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str()); + this->on_device_info_request(msg); + break; + } + case 11: { + ListEntitiesRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str()); + this->on_list_entities_request(msg); + break; + } + case 20: { + SubscribeStatesRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str()); + this->on_subscribe_states_request(msg); + break; + } + case 28: { + SubscribeLogsRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str()); + this->on_subscribe_logs_request(msg); + break; + } + case 30: { +#ifdef USE_COVER + CoverCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str()); + this->on_cover_command_request(msg); +#endif + break; + } + case 31: { +#ifdef USE_FAN + FanCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str()); + this->on_fan_command_request(msg); +#endif + break; + } + case 32: { +#ifdef USE_LIGHT + LightCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str()); + this->on_light_command_request(msg); +#endif + break; + } + case 33: { +#ifdef USE_SWITCH + SwitchCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str()); + this->on_switch_command_request(msg); +#endif + break; + } + case 34: { + SubscribeHomeassistantServicesRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str()); + this->on_subscribe_homeassistant_services_request(msg); + break; + } + case 36: { + GetTimeRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str()); + this->on_get_time_request(msg); + break; + } + case 37: { + GetTimeResponse msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str()); + this->on_get_time_response(msg); + break; + } + case 38: { + SubscribeHomeAssistantStatesRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str()); + this->on_subscribe_home_assistant_states_request(msg); + break; + } + case 40: { + HomeAssistantStateResponse msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str()); + this->on_home_assistant_state_response(msg); + break; + } + case 42: { + ExecuteServiceRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str()); + this->on_execute_service_request(msg); + break; + } + case 45: { +#ifdef USE_ESP32_CAMERA + CameraImageRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str()); + this->on_camera_image_request(msg); +#endif + break; + } + case 48: { +#ifdef USE_CLIMATE + ClimateCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str()); + this->on_climate_command_request(msg); +#endif + break; + } + default: + return false; + } + return true; +} + +void APIServerConnection::on_hello_request(const HelloRequest &msg) { + HelloResponse ret = this->hello(msg); + if (!this->send_hello_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_connect_request(const ConnectRequest &msg) { + ConnectResponse ret = this->connect(msg); + if (!this->send_connect_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) { + DisconnectResponse ret = this->disconnect(msg); + if (!this->send_disconnect_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_ping_request(const PingRequest &msg) { + PingResponse ret = this->ping(msg); + if (!this->send_ping_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + DeviceInfoResponse ret = this->device_info(msg); + if (!this->send_device_info_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->list_entities(msg); +} +void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_states(msg); +} +void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_logs(msg); +} +void APIServerConnection::on_subscribe_homeassistant_services_request( + const SubscribeHomeassistantServicesRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_homeassistant_services(msg); +} +void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_home_assistant_states(msg); +} +void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + GetTimeResponse ret = this->get_time(msg); + if (!this->send_get_time_response(ret)) { + this->on_fatal_error(); + } +} +void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->execute_service(msg); +} +#ifdef USE_COVER +void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->cover_command(msg); +} +#endif +#ifdef USE_FAN +void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->fan_command(msg); +} +#endif +#ifdef USE_LIGHT +void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->light_command(msg); +} +#endif +#ifdef USE_SWITCH +void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->switch_command(msg); +} +#endif +#ifdef USE_ESP32_CAMERA +void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->camera_image(msg); +} +#endif +#ifdef USE_CLIMATE +void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->climate_command(msg); +} +#endif + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h new file mode 100644 index 0000000000..16662811fe --- /dev/null +++ b/esphome/components/api/api_pb2_service.h @@ -0,0 +1,183 @@ +#pragma once + +#include "api_pb2.h" +#include "esphome/core/defines.h" + +namespace esphome { +namespace api { + +class APIServerConnectionBase : public ProtoService { + public: + virtual void on_hello_request(const HelloRequest &value){}; + bool send_hello_response(const HelloResponse &msg); + virtual void on_connect_request(const ConnectRequest &value){}; + bool send_connect_response(const ConnectResponse &msg); + bool send_disconnect_request(const DisconnectRequest &msg); + virtual void on_disconnect_request(const DisconnectRequest &value){}; + bool send_disconnect_response(const DisconnectResponse &msg); + virtual void on_disconnect_response(const DisconnectResponse &value){}; + bool send_ping_request(const PingRequest &msg); + virtual void on_ping_request(const PingRequest &value){}; + bool send_ping_response(const PingResponse &msg); + virtual void on_ping_response(const PingResponse &value){}; + virtual void on_device_info_request(const DeviceInfoRequest &value){}; + bool send_device_info_response(const DeviceInfoResponse &msg); + virtual void on_list_entities_request(const ListEntitiesRequest &value){}; + bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg); + virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){}; +#ifdef USE_BINARY_SENSOR + bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg); +#endif +#ifdef USE_BINARY_SENSOR + bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg); +#endif +#ifdef USE_COVER + bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg); +#endif +#ifdef USE_COVER + bool send_cover_state_response(const CoverStateResponse &msg); +#endif +#ifdef USE_COVER + virtual void on_cover_command_request(const CoverCommandRequest &value){}; +#endif +#ifdef USE_FAN + bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg); +#endif +#ifdef USE_FAN + bool send_fan_state_response(const FanStateResponse &msg); +#endif +#ifdef USE_FAN + virtual void on_fan_command_request(const FanCommandRequest &value){}; +#endif +#ifdef USE_LIGHT + bool send_list_entities_light_response(const ListEntitiesLightResponse &msg); +#endif +#ifdef USE_LIGHT + bool send_light_state_response(const LightStateResponse &msg); +#endif +#ifdef USE_LIGHT + virtual void on_light_command_request(const LightCommandRequest &value){}; +#endif +#ifdef USE_SENSOR + bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg); +#endif +#ifdef USE_SENSOR + bool send_sensor_state_response(const SensorStateResponse &msg); +#endif +#ifdef USE_SWITCH + bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg); +#endif +#ifdef USE_SWITCH + bool send_switch_state_response(const SwitchStateResponse &msg); +#endif +#ifdef USE_SWITCH + virtual void on_switch_command_request(const SwitchCommandRequest &value){}; +#endif +#ifdef USE_TEXT_SENSOR + bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg); +#endif +#ifdef USE_TEXT_SENSOR + bool send_text_sensor_state_response(const TextSensorStateResponse &msg); +#endif + virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){}; + bool send_subscribe_logs_response(const SubscribeLogsResponse &msg); + virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; + bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg); + virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){}; + bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg); + virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){}; + bool send_get_time_request(const GetTimeRequest &msg); + virtual void on_get_time_request(const GetTimeRequest &value){}; + bool send_get_time_response(const GetTimeResponse &msg); + virtual void on_get_time_response(const GetTimeResponse &value){}; + bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg); + virtual void on_execute_service_request(const ExecuteServiceRequest &value){}; +#ifdef USE_ESP32_CAMERA + bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg); +#endif +#ifdef USE_ESP32_CAMERA + bool send_camera_image_response(const CameraImageResponse &msg); +#endif +#ifdef USE_ESP32_CAMERA + virtual void on_camera_image_request(const CameraImageRequest &value){}; +#endif +#ifdef USE_CLIMATE + bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg); +#endif +#ifdef USE_CLIMATE + bool send_climate_state_response(const ClimateStateResponse &msg); +#endif +#ifdef USE_CLIMATE + virtual void on_climate_command_request(const ClimateCommandRequest &value){}; +#endif + protected: + bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; +}; + +class APIServerConnection : public APIServerConnectionBase { + public: + virtual HelloResponse hello(const HelloRequest &msg) = 0; + virtual ConnectResponse connect(const ConnectRequest &msg) = 0; + virtual DisconnectResponse disconnect(const DisconnectRequest &msg) = 0; + virtual PingResponse ping(const PingRequest &msg) = 0; + virtual DeviceInfoResponse device_info(const DeviceInfoRequest &msg) = 0; + virtual void list_entities(const ListEntitiesRequest &msg) = 0; + virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0; + virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0; + virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; + virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; + virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; + virtual void execute_service(const ExecuteServiceRequest &msg) = 0; +#ifdef USE_COVER + virtual void cover_command(const CoverCommandRequest &msg) = 0; +#endif +#ifdef USE_FAN + virtual void fan_command(const FanCommandRequest &msg) = 0; +#endif +#ifdef USE_LIGHT + virtual void light_command(const LightCommandRequest &msg) = 0; +#endif +#ifdef USE_SWITCH + virtual void switch_command(const SwitchCommandRequest &msg) = 0; +#endif +#ifdef USE_ESP32_CAMERA + virtual void camera_image(const CameraImageRequest &msg) = 0; +#endif +#ifdef USE_CLIMATE + virtual void climate_command(const ClimateCommandRequest &msg) = 0; +#endif + protected: + void on_hello_request(const HelloRequest &msg) override; + void on_connect_request(const ConnectRequest &msg) override; + void on_disconnect_request(const DisconnectRequest &msg) override; + void on_ping_request(const PingRequest &msg) override; + void on_device_info_request(const DeviceInfoRequest &msg) override; + void on_list_entities_request(const ListEntitiesRequest &msg) override; + void on_subscribe_states_request(const SubscribeStatesRequest &msg) override; + void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override; + void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override; + void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; + void on_get_time_request(const GetTimeRequest &msg) override; + void on_execute_service_request(const ExecuteServiceRequest &msg) override; +#ifdef USE_COVER + void on_cover_command_request(const CoverCommandRequest &msg) override; +#endif +#ifdef USE_FAN + void on_fan_command_request(const FanCommandRequest &msg) override; +#endif +#ifdef USE_LIGHT + void on_light_command_request(const LightCommandRequest &msg) override; +#endif +#ifdef USE_SWITCH + void on_switch_command_request(const SwitchCommandRequest &msg) override; +#endif +#ifdef USE_ESP32_CAMERA + void on_camera_image_request(const CameraImageRequest &msg) override; +#endif +#ifdef USE_CLIMATE + void on_climate_command_request(const ClimateCommandRequest &msg) override; +#endif +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index cde6f1e875..25ae9a98a3 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -1,19 +1,11 @@ -#include - #include "api_server.h" -#include "basic_messages.h" +#include "api_connection.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/util.h" #include "esphome/core/defines.h" #include "esphome/core/version.h" -#ifdef USE_DEEP_SLEEP -#include "esphome/components/deep_sleep/deep_sleep_component.h" -#endif -#ifdef USE_HOMEASSISTANT_TIME -#include "esphome/components/homeassistant/time/homeassistant_time.h" -#endif #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" #endif @@ -210,9 +202,9 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; } APIServer *global_api_server = nullptr; void APIServer::set_password(const std::string &password) { this->password_ = password; } -void APIServer::send_service_call(ServiceCallResponse &call) { +void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { for (auto *client : this->clients_) { - client->send_service_call(call); + client->send_homeassistant_service_call(call); } } APIServer::APIServer() { global_api_server = this; } @@ -238,965 +230,10 @@ void APIServer::request_time() { bool APIServer::is_connected() const { return !this->clients_.empty(); } void APIServer::on_shutdown() { for (auto *c : this->clients_) { - c->send_disconnect_request(); + c->send_disconnect_request(DisconnectRequest()); } delay(10); } -// APIConnection -APIConnection::APIConnection(AsyncClient *client, APIServer *parent) - : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { - this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this); - this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this); - this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); }, - this); - this->client_->onData([](void *s, AsyncClient *c, void *buf, - size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast(buf), len); }, - this); - - this->send_buffer_.reserve(64); - this->recv_buffer_.reserve(32); - this->client_info_ = this->client_->remoteIP().toString().c_str(); - this->last_traffic_ = millis(); -} -APIConnection::~APIConnection() { delete this->client_; } -void APIConnection::on_error_(int8_t error) { - // disconnect will also be called, nothing to do here - this->remove_ = true; -} -void APIConnection::on_disconnect_() { - // delete self, generally unsafe but not in this case. - this->remove_ = true; -} -void APIConnection::on_timeout_(uint32_t time) { this->disconnect_client(); } -void APIConnection::on_data_(uint8_t *buf, size_t len) { - if (len == 0 || buf == nullptr) - return; - - this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len); - // TODO: On ESP32, use queue to notify main thread of new data -} -void APIConnection::parse_recv_buffer_() { - if (this->recv_buffer_.empty() || this->remove_) - return; - - while (!this->recv_buffer_.empty()) { - if (this->recv_buffer_[0] != 0x00) { - ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str()); - this->fatal_error_(); - return; - } - uint32_t i = 1; - const uint32_t size = this->recv_buffer_.size(); - uint32_t msg_size = 0; - while (i < size) { - const uint8_t dat = this->recv_buffer_[i]; - msg_size |= (dat & 0x7F); - // consume - i += 1; - if ((dat & 0x80) == 0x00) { - break; - } else { - msg_size <<= 7; - } - } - if (i == size) - // not enough data there yet - return; - - uint32_t msg_type = 0; - bool msg_type_done = false; - while (i < size) { - const uint8_t dat = this->recv_buffer_[i]; - msg_type |= (dat & 0x7F); - // consume - i += 1; - if ((dat & 0x80) == 0x00) { - msg_type_done = true; - break; - } else { - msg_type <<= 7; - } - } - if (!msg_type_done) - // not enough data there yet - return; - - if (size - i < msg_size) - // message body not fully received - return; - - // ESP_LOGVV(TAG, "RECV Message: Size=%u Type=%u", msg_size, msg_type); - - if (!this->valid_rx_message_type_(msg_type)) { - ESP_LOGE(TAG, "Not a valid message type: %u", msg_type); - this->fatal_error_(); - return; - } - - uint8_t *msg = &this->recv_buffer_[i]; - this->read_message_(msg_size, msg_type, msg); - if (this->remove_) - return; - // pop front - uint32_t total = i + msg_size; - this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total); - } -} -void APIConnection::read_message_(uint32_t size, uint32_t type, uint8_t *msg) { - this->last_traffic_ = millis(); - - switch (static_cast(type)) { - case APIMessageType::HELLO_REQUEST: { - HelloRequest req; - req.decode(msg, size); - this->on_hello_request_(req); - break; - } - case APIMessageType::HELLO_RESPONSE: { - // Invalid - break; - } - case APIMessageType::CONNECT_REQUEST: { - ConnectRequest req; - req.decode(msg, size); - this->on_connect_request_(req); - break; - } - case APIMessageType::CONNECT_RESPONSE: - // Invalid - break; - case APIMessageType::DISCONNECT_REQUEST: { - DisconnectRequest req; - req.decode(msg, size); - this->on_disconnect_request_(req); - break; - } - case APIMessageType::DISCONNECT_RESPONSE: { - DisconnectResponse req; - req.decode(msg, size); - this->on_disconnect_response_(req); - break; - } - case APIMessageType::PING_REQUEST: { - PingRequest req; - req.decode(msg, size); - this->on_ping_request_(req); - break; - } - case APIMessageType::PING_RESPONSE: { - PingResponse req; - req.decode(msg, size); - this->on_ping_response_(req); - break; - } - case APIMessageType::DEVICE_INFO_REQUEST: { - DeviceInfoRequest req; - req.decode(msg, size); - this->on_device_info_request_(req); - break; - } - case APIMessageType::DEVICE_INFO_RESPONSE: { - // Invalid - break; - } - case APIMessageType::LIST_ENTITIES_REQUEST: { - ListEntitiesRequest req; - req.decode(msg, size); - this->on_list_entities_request_(req); - break; - } - case APIMessageType::LIST_ENTITIES_BINARY_SENSOR_RESPONSE: - case APIMessageType::LIST_ENTITIES_COVER_RESPONSE: - case APIMessageType::LIST_ENTITIES_FAN_RESPONSE: - case APIMessageType::LIST_ENTITIES_LIGHT_RESPONSE: - case APIMessageType::LIST_ENTITIES_SENSOR_RESPONSE: - case APIMessageType::LIST_ENTITIES_SWITCH_RESPONSE: - case APIMessageType::LIST_ENTITIES_TEXT_SENSOR_RESPONSE: - case APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE: - case APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE: - case APIMessageType::LIST_ENTITIES_CLIMATE_RESPONSE: - case APIMessageType::LIST_ENTITIES_DONE_RESPONSE: - // Invalid - break; - case APIMessageType::SUBSCRIBE_STATES_REQUEST: { - SubscribeStatesRequest req; - req.decode(msg, size); - this->on_subscribe_states_request_(req); - break; - } - case APIMessageType::BINARY_SENSOR_STATE_RESPONSE: - case APIMessageType::COVER_STATE_RESPONSE: - case APIMessageType::FAN_STATE_RESPONSE: - case APIMessageType::LIGHT_STATE_RESPONSE: - case APIMessageType::SENSOR_STATE_RESPONSE: - case APIMessageType::SWITCH_STATE_RESPONSE: - case APIMessageType::TEXT_SENSOR_STATE_RESPONSE: - case APIMessageType::CAMERA_IMAGE_RESPONSE: - case APIMessageType::CLIMATE_STATE_RESPONSE: - // Invalid - break; - case APIMessageType::SUBSCRIBE_LOGS_REQUEST: { - SubscribeLogsRequest req; - req.decode(msg, size); - this->on_subscribe_logs_request_(req); - break; - } - case APIMessageType ::SUBSCRIBE_LOGS_RESPONSE: - // Invalid - break; - case APIMessageType::COVER_COMMAND_REQUEST: { -#ifdef USE_COVER - CoverCommandRequest req; - req.decode(msg, size); - this->on_cover_command_request_(req); -#endif - break; - } - case APIMessageType::FAN_COMMAND_REQUEST: { -#ifdef USE_FAN - FanCommandRequest req; - req.decode(msg, size); - this->on_fan_command_request_(req); -#endif - break; - } - case APIMessageType::LIGHT_COMMAND_REQUEST: { -#ifdef USE_LIGHT - LightCommandRequest req; - req.decode(msg, size); - this->on_light_command_request_(req); -#endif - break; - } - case APIMessageType::SWITCH_COMMAND_REQUEST: { -#ifdef USE_SWITCH - SwitchCommandRequest req; - req.decode(msg, size); - this->on_switch_command_request_(req); -#endif - break; - } - case APIMessageType::CLIMATE_COMMAND_REQUEST: { -#ifdef USE_CLIMATE - ClimateCommandRequest req; - req.decode(msg, size); - this->on_climate_command_request_(req); -#endif - break; - } - case APIMessageType::SUBSCRIBE_SERVICE_CALLS_REQUEST: { - SubscribeServiceCallsRequest req; - req.decode(msg, size); - this->on_subscribe_service_calls_request_(req); - break; - } - case APIMessageType::SERVICE_CALL_RESPONSE: - // Invalid - break; - case APIMessageType::GET_TIME_REQUEST: - // Invalid - break; - case APIMessageType::GET_TIME_RESPONSE: { -#ifdef USE_HOMEASSISTANT_TIME - homeassistant::GetTimeResponse req; - req.decode(msg, size); -#endif - break; - } - case APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST: { - SubscribeHomeAssistantStatesRequest req; - req.decode(msg, size); - this->on_subscribe_home_assistant_states_request_(req); - break; - } - case APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE: - // Invalid - break; - case APIMessageType::HOME_ASSISTANT_STATE_RESPONSE: { - HomeAssistantStateResponse req; - req.decode(msg, size); - this->on_home_assistant_state_response_(req); - break; - } - case APIMessageType::EXECUTE_SERVICE_REQUEST: { - ExecuteServiceRequest req; - req.decode(msg, size); - this->on_execute_service_(req); - break; - } - case APIMessageType::CAMERA_IMAGE_REQUEST: { -#ifdef USE_ESP32_CAMERA - CameraImageRequest req; - req.decode(msg, size); - this->on_camera_image_request_(req); -#endif - break; - } - } -} -void APIConnection::on_hello_request_(const HelloRequest &req) { - ESP_LOGVV(TAG, "on_hello_request_(client_info='%s')", req.get_client_info().c_str()); - this->client_info_ = req.get_client_info() + " (" + this->client_->remoteIP().toString().c_str(); - this->client_info_ += ")"; - ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str()); - - auto buffer = this->get_buffer(); - // uint32 api_version_major = 1; -> 1 - buffer.encode_uint32(1, 1); - // uint32 api_version_minor = 2; -> 1 - buffer.encode_uint32(2, 1); - - // string server_info = 3; - buffer.encode_string(3, App.get_name() + " (esphome v" ESPHOME_VERSION ")"); - bool success = this->send_buffer(APIMessageType::HELLO_RESPONSE); - if (!success) { - this->fatal_error_(); - return; - } - - this->connection_state_ = ConnectionState::WAITING_FOR_CONNECT; -} -void APIConnection::on_connect_request_(const ConnectRequest &req) { - ESP_LOGVV(TAG, "on_connect_request_(password='%s')", req.get_password().c_str()); - bool correct = this->parent_->check_password(req.get_password()); - auto buffer = this->get_buffer(); - // bool invalid_password = 1; - buffer.encode_bool(1, !correct); - bool success = this->send_buffer(APIMessageType::CONNECT_RESPONSE); - if (!success) { - this->fatal_error_(); - return; - } - - if (correct) { - ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str()); - this->connection_state_ = ConnectionState::CONNECTED; - -#ifdef USE_HOMEASSISTANT_TIME - if (homeassistant::global_homeassistant_time != nullptr) { - this->send_time_request(); - } -#endif - } -} -void APIConnection::on_disconnect_request_(const DisconnectRequest &req) { - ESP_LOGVV(TAG, "on_disconnect_request_"); - // remote initiated disconnect_client - if (!this->send_empty_message(APIMessageType::DISCONNECT_RESPONSE)) { - this->fatal_error_(); - return; - } - this->disconnect_client(); -} -void APIConnection::on_disconnect_response_(const DisconnectResponse &req) { - ESP_LOGVV(TAG, "on_disconnect_response_"); - // we initiated disconnect_client - this->disconnect_client(); -} -void APIConnection::on_ping_request_(const PingRequest &req) { - ESP_LOGVV(TAG, "on_ping_request_"); - PingResponse resp; - this->send_message(resp); -} -void APIConnection::on_ping_response_(const PingResponse &req) { - ESP_LOGVV(TAG, "on_ping_response_"); - // we initiated ping - this->sent_ping_ = false; -} -void APIConnection::on_device_info_request_(const DeviceInfoRequest &req) { - ESP_LOGVV(TAG, "on_device_info_request_"); - auto buffer = this->get_buffer(); - // bool uses_password = 1; - buffer.encode_bool(1, this->parent_->uses_password()); - // string name = 2; - buffer.encode_string(2, App.get_name()); - // string mac_address = 3; - buffer.encode_string(3, get_mac_address_pretty()); - // string esphome_version = 4; - buffer.encode_string(4, ESPHOME_VERSION); - // string compilation_time = 5; - buffer.encode_string(5, App.get_compilation_time()); -#ifdef ARDUINO_BOARD - // string model = 6; - buffer.encode_string(6, ARDUINO_BOARD); -#endif -#ifdef USE_DEEP_SLEEP - // bool has_deep_sleep = 7; - buffer.encode_bool(7, deep_sleep::global_has_deep_sleep); -#endif - this->send_buffer(APIMessageType::DEVICE_INFO_RESPONSE); -} -void APIConnection::on_list_entities_request_(const ListEntitiesRequest &req) { - ESP_LOGVV(TAG, "on_list_entities_request_"); - this->list_entities_iterator_.begin(); -} -void APIConnection::on_subscribe_states_request_(const SubscribeStatesRequest &req) { - ESP_LOGVV(TAG, "on_subscribe_states_request_"); - this->state_subscription_ = true; - this->initial_state_iterator_.begin(); -} -void APIConnection::on_subscribe_logs_request_(const SubscribeLogsRequest &req) { - ESP_LOGVV(TAG, "on_subscribe_logs_request_"); - this->log_subscription_ = req.get_level(); - if (req.get_dump_config()) { - App.schedule_dump_config(); - } -} - -void APIConnection::fatal_error_() { - this->client_->close(); - this->remove_ = true; -} -bool APIConnection::valid_rx_message_type_(uint32_t type) { - switch (static_cast(type)) { - case APIMessageType::HELLO_RESPONSE: - case APIMessageType::CONNECT_RESPONSE: - return false; - case APIMessageType::HELLO_REQUEST: - return this->connection_state_ == ConnectionState::WAITING_FOR_HELLO; - case APIMessageType::CONNECT_REQUEST: - return this->connection_state_ == ConnectionState::WAITING_FOR_CONNECT; - case APIMessageType::PING_REQUEST: - case APIMessageType::PING_RESPONSE: - case APIMessageType::DISCONNECT_REQUEST: - case APIMessageType::DISCONNECT_RESPONSE: - case APIMessageType::DEVICE_INFO_REQUEST: - if (this->connection_state_ == ConnectionState::WAITING_FOR_CONNECT) - return true; - default: - return this->connection_state_ == ConnectionState::CONNECTED; - } -} -bool APIConnection::send_message(APIMessage &msg) { - this->send_buffer_.clear(); - APIBuffer buf(&this->send_buffer_); - msg.encode(buf); - return this->send_buffer(msg.message_type()); -} -bool APIConnection::send_empty_message(APIMessageType type) { - this->send_buffer_.clear(); - return this->send_buffer(type); -} - -void APIConnection::disconnect_client() { - this->client_->close(); - this->remove_ = true; -} -void encode_varint(uint8_t *dat, uint8_t *len, uint32_t value) { - if (value <= 0x7F) { - *dat = value; - (*len)++; - return; - } - - while (value) { - uint8_t temp = value & 0x7F; - value >>= 7; - if (value) { - *dat = temp | 0x80; - } else { - *dat = temp; - } - dat++; - (*len)++; - } -} - -bool APIConnection::send_buffer(APIMessageType type) { - uint8_t header[20]; - header[0] = 0x00; - uint8_t header_len = 1; - encode_varint(header + header_len, &header_len, this->send_buffer_.size()); - encode_varint(header + header_len, &header_len, static_cast(type)); - - size_t needed_space = this->send_buffer_.size() + header_len; - - if (needed_space > this->client_->space()) { - delay(0); - if (needed_space > this->client_->space()) { - if (type != APIMessageType::SUBSCRIBE_LOGS_RESPONSE) { - ESP_LOGV(TAG, "Cannot send message because of TCP buffer space"); - } - delay(0); - return false; - } - } - - // char buffer[512]; - // uint32_t offset = 0; - // for (int j = 0; j < header_len; j++) { - // offset += snprintf(buffer + offset, 512 - offset, "0x%02X ", header[j]); - // } - // offset += snprintf(buffer + offset, 512 - offset, "| "); - // for (auto &it : this->send_buffer_) { - // int i = snprintf(buffer + offset, 512 - offset, "0x%02X ", it); - // if (i <= 0) - // break; - // offset += i; - // } - // ESP_LOGVV(TAG, "SEND %s", buffer); - - this->client_->add(reinterpret_cast(header), header_len); - this->client_->add(reinterpret_cast(this->send_buffer_.data()), this->send_buffer_.size()); - return this->client_->send(); -} - -void APIConnection::loop() { - if (!network_is_connected()) { - // when network is disconnected force disconnect immediately - // don't wait for timeout - this->fatal_error_(); - return; - } - if (this->client_->disconnected()) { - // failsafe for disconnect logic - this->on_disconnect_(); - return; - } - this->parse_recv_buffer_(); - - this->list_entities_iterator_.advance(); - this->initial_state_iterator_.advance(); - - const uint32_t keepalive = 60000; - if (this->sent_ping_) { - if (millis() - this->last_traffic_ > (keepalive * 3) / 2) { - ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str()); - this->disconnect_client(); - } - } else if (millis() - this->last_traffic_ > keepalive) { - this->sent_ping_ = true; - this->send_ping_request(); - } - -#ifdef USE_ESP32_CAMERA - if (this->image_reader_.available()) { - uint32_t space = this->client_->space(); - // reserve 15 bytes for metadata, and at least 64 bytes of data - if (space >= 15 + 64) { - uint32_t to_send = std::min(space - 15, this->image_reader_.available()); - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash()); - // bytes data = 2; - buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send); - // bool done = 3; - bool done = this->image_reader_.available() == to_send; - buffer.encode_bool(3, done); - bool success = this->send_buffer(APIMessageType::CAMERA_IMAGE_RESPONSE); - if (success) { - this->image_reader_.consume_data(to_send); - } - if (success && done) { - this->image_reader_.return_image(); - } - } - } -#endif -} - -#ifdef USE_BINARY_SENSOR -bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, binary_sensor->get_object_id_hash()); - // bool state = 2; - buffer.encode_bool(2, state); - return this->send_buffer(APIMessageType::BINARY_SENSOR_STATE_RESPONSE); -} -#endif - -#ifdef USE_COVER -bool APIConnection::send_cover_state(cover::Cover *cover) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - auto traits = cover->get_traits(); - // fixed32 key = 1; - buffer.encode_fixed32(1, cover->get_object_id_hash()); - // enum LegacyCoverState { - // OPEN = 0; - // CLOSED = 1; - // } - // LegacyCoverState legacy_state = 2; - uint32_t state = (cover->position == cover::COVER_OPEN) ? 0 : 1; - buffer.encode_uint32(2, state); - // float position = 3; - buffer.encode_float(3, cover->position); - if (traits.get_supports_tilt()) { - // float tilt = 4; - buffer.encode_float(4, cover->tilt); - } - // enum CoverCurrentOperation { - // IDLE = 0; - // IS_OPENING = 1; - // IS_CLOSING = 2; - // } - // CoverCurrentOperation current_operation = 5; - buffer.encode_uint32(5, cover->current_operation); - return this->send_buffer(APIMessageType::COVER_STATE_RESPONSE); -} -#endif - -#ifdef USE_FAN -bool APIConnection::send_fan_state(fan::FanState *fan) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, fan->get_object_id_hash()); - // bool state = 2; - buffer.encode_bool(2, fan->state); - // bool oscillating = 3; - if (fan->get_traits().supports_oscillation()) { - buffer.encode_bool(3, fan->oscillating); - } - // enum FanSpeed { - // LOW = 0; - // MEDIUM = 1; - // HIGH = 2; - // } - // FanSpeed speed = 4; - if (fan->get_traits().supports_speed()) { - buffer.encode_uint32(4, fan->speed); - } - return this->send_buffer(APIMessageType::FAN_STATE_RESPONSE); -} -#endif - -#ifdef USE_LIGHT -bool APIConnection::send_light_state(light::LightState *light) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - auto traits = light->get_traits(); - auto values = light->remote_values; - - // fixed32 key = 1; - buffer.encode_fixed32(1, light->get_object_id_hash()); - // bool state = 2; - buffer.encode_bool(2, values.get_state() != 0.0f); - // float brightness = 3; - if (traits.get_supports_brightness()) { - buffer.encode_float(3, values.get_brightness()); - } - if (traits.get_supports_rgb()) { - // float red = 4; - buffer.encode_float(4, values.get_red()); - // float green = 5; - buffer.encode_float(5, values.get_green()); - // float blue = 6; - buffer.encode_float(6, values.get_blue()); - } - // float white = 7; - if (traits.get_supports_rgb_white_value()) { - buffer.encode_float(7, values.get_white()); - } - // float color_temperature = 8; - if (traits.get_supports_color_temperature()) { - buffer.encode_float(8, values.get_color_temperature()); - } - // string effect = 9; - if (light->supports_effects()) { - buffer.encode_string(9, light->get_effect_name()); - } - return this->send_buffer(APIMessageType::LIGHT_STATE_RESPONSE); -} -#endif - -#ifdef USE_SENSOR -bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, sensor->get_object_id_hash()); - // float state = 2; - buffer.encode_float(2, state); - return this->send_buffer(APIMessageType::SENSOR_STATE_RESPONSE); -} -#endif - -#ifdef USE_SWITCH -bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, a_switch->get_object_id_hash()); - // bool state = 2; - buffer.encode_bool(2, state); - return this->send_buffer(APIMessageType::SWITCH_STATE_RESPONSE); -} -#endif - -#ifdef USE_TEXT_SENSOR -bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - // fixed32 key = 1; - buffer.encode_fixed32(1, text_sensor->get_object_id_hash()); - // string state = 2; - buffer.encode_string(2, state); - return this->send_buffer(APIMessageType::TEXT_SENSOR_STATE_RESPONSE); -} -#endif - -#ifdef USE_CLIMATE -bool APIConnection::send_climate_state(climate::Climate *climate) { - if (!this->state_subscription_) - return false; - - auto buffer = this->get_buffer(); - auto traits = climate->get_traits(); - // fixed32 key = 1; - buffer.encode_fixed32(1, climate->get_object_id_hash()); - // ClimateMode mode = 2; - buffer.encode_uint32(2, static_cast(climate->mode)); - // float current_temperature = 3; - if (traits.get_supports_current_temperature()) { - buffer.encode_float(3, climate->current_temperature); - } - if (traits.get_supports_two_point_target_temperature()) { - // float target_temperature_low = 5; - buffer.encode_float(5, climate->target_temperature_low); - // float target_temperature_high = 6; - buffer.encode_float(6, climate->target_temperature_high); - } else { - // float target_temperature = 4; - buffer.encode_float(4, climate->target_temperature); - } - // bool away = 7; - if (traits.get_supports_away()) { - buffer.encode_bool(7, climate->away); - } - return this->send_buffer(APIMessageType::CLIMATE_STATE_RESPONSE); -} -#endif - -bool APIConnection::send_log_message(int level, const char *tag, const char *line) { - if (this->log_subscription_ < level) - return false; - - auto buffer = this->get_buffer(); - // LogLevel level = 1; - buffer.encode_uint32(1, static_cast(level)); - // string tag = 2; - // buffer.encode_string(2, tag, strlen(tag)); - // string message = 3; - buffer.encode_string(3, line, strlen(line)); - bool success = this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE); - - if (!success) { - buffer = this->get_buffer(); - // bool send_failed = 4; - buffer.encode_bool(4, true); - return this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE); - } else { - return true; - } -} -bool APIConnection::send_disconnect_request() { - DisconnectRequest req; - return this->send_message(req); -} -bool APIConnection::send_ping_request() { - ESP_LOGVV(TAG, "Sending ping..."); - PingRequest req; - return this->send_message(req); -} - -#ifdef USE_COVER -void APIConnection::on_cover_command_request_(const CoverCommandRequest &req) { - ESP_LOGVV(TAG, "on_cover_command_request_"); - cover::Cover *cover = App.get_cover_by_key(req.get_key()); - if (cover == nullptr) - return; - - auto call = cover->make_call(); - if (req.get_legacy_command().has_value()) { - auto cmd = *req.get_legacy_command(); - switch (cmd) { - case LEGACY_COVER_COMMAND_OPEN: - call.set_command_open(); - break; - case LEGACY_COVER_COMMAND_CLOSE: - call.set_command_close(); - break; - case LEGACY_COVER_COMMAND_STOP: - call.set_command_stop(); - break; - } - } - if (req.get_position().has_value()) { - auto pos = *req.get_position(); - call.set_position(pos); - } - if (req.get_tilt().has_value()) { - auto tilt = *req.get_tilt(); - call.set_tilt(tilt); - } - if (req.get_stop()) { - call.set_command_stop(); - } - call.perform(); -} -#endif - -#ifdef USE_FAN -void APIConnection::on_fan_command_request_(const FanCommandRequest &req) { - ESP_LOGVV(TAG, "on_fan_command_request_"); - fan::FanState *fan = App.get_fan_by_key(req.get_key()); - if (fan == nullptr) - return; - - auto call = fan->make_call(); - call.set_state(req.get_state()); - call.set_oscillating(req.get_oscillating()); - call.set_speed(req.get_speed()); - call.perform(); -} -#endif - -#ifdef USE_LIGHT -void APIConnection::on_light_command_request_(const LightCommandRequest &req) { - ESP_LOGVV(TAG, "on_light_command_request_"); - light::LightState *light = App.get_light_by_key(req.get_key()); - if (light == nullptr) - return; - - auto call = light->make_call(); - call.set_state(req.get_state()); - call.set_brightness(req.get_brightness()); - call.set_red(req.get_red()); - call.set_green(req.get_green()); - call.set_blue(req.get_blue()); - call.set_white(req.get_white()); - call.set_color_temperature(req.get_color_temperature()); - call.set_transition_length(req.get_transition_length()); - call.set_flash_length(req.get_flash_length()); - call.set_effect(req.get_effect()); - call.perform(); -} -#endif - -#ifdef USE_SWITCH -void APIConnection::on_switch_command_request_(const SwitchCommandRequest &req) { - ESP_LOGVV(TAG, "on_switch_command_request_"); - switch_::Switch *a_switch = App.get_switch_by_key(req.get_key()); - if (a_switch == nullptr || a_switch->is_internal()) - return; - - if (req.get_state()) { - a_switch->turn_on(); - } else { - a_switch->turn_off(); - } -} -#endif - -#ifdef USE_CLIMATE -void APIConnection::on_climate_command_request_(const ClimateCommandRequest &req) { - ESP_LOGVV(TAG, "on_climate_command_request_"); - climate::Climate *climate = App.get_climate_by_key(req.get_key()); - if (climate == nullptr) - return; - - auto call = climate->make_call(); - if (req.get_mode().has_value()) - call.set_mode(*req.get_mode()); - if (req.get_target_temperature().has_value()) - call.set_target_temperature(*req.get_target_temperature()); - if (req.get_target_temperature_low().has_value()) - call.set_target_temperature_low(*req.get_target_temperature_low()); - if (req.get_target_temperature_high().has_value()) - call.set_target_temperature_high(*req.get_target_temperature_high()); - if (req.get_away().has_value()) - call.set_away(*req.get_away()); - call.perform(); -} -#endif - -void APIConnection::on_subscribe_service_calls_request_(const SubscribeServiceCallsRequest &req) { - this->service_call_subscription_ = true; -} -void APIConnection::send_service_call(ServiceCallResponse &call) { - if (!this->service_call_subscription_) - return; - - this->send_message(call); -} -void APIConnection::on_subscribe_home_assistant_states_request_(const SubscribeHomeAssistantStatesRequest &req) { - for (auto &it : this->parent_->get_state_subs()) { - auto buffer = this->get_buffer(); - // string entity_id = 1; - buffer.encode_string(1, it.entity_id); - this->send_buffer(APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE); - } -} -void APIConnection::on_home_assistant_state_response_(const HomeAssistantStateResponse &req) { - for (auto &it : this->parent_->get_state_subs()) { - if (it.entity_id == req.get_entity_id()) { - it.callback(req.get_state()); - } - } -} -void APIConnection::on_execute_service_(const ExecuteServiceRequest &req) { - ESP_LOGVV(TAG, "on_execute_service_"); - bool found = false; - for (auto *service : this->parent_->get_user_services()) { - if (service->execute_service(req)) { - found = true; - } - } - if (!found) { - ESP_LOGV(TAG, "Could not find matching service!"); - } -} - -APIBuffer APIConnection::get_buffer() { - this->send_buffer_.clear(); - return {&this->send_buffer_}; -} -#ifdef USE_HOMEASSISTANT_TIME -void APIConnection::send_time_request() { this->send_empty_message(APIMessageType::GET_TIME_REQUEST); } -#endif - -#ifdef USE_ESP32_CAMERA -void APIConnection::send_camera_state(std::shared_ptr image) { - if (!this->state_subscription_) - return; - if (this->image_reader_.available()) - return; - this->image_reader_.set_image(image); -} -#endif - -#ifdef USE_ESP32_CAMERA -void APIConnection::on_camera_image_request_(const CameraImageRequest &req) { - if (esp32_camera::global_esp32_camera == nullptr) - return; - - ESP_LOGV(TAG, "on_camera_image_request_ stream=%s single=%s", YESNO(req.get_stream()), YESNO(req.get_single())); - if (req.get_single()) { - esp32_camera::global_esp32_camera->request_image(); - } - if (req.get_stream()) { - esp32_camera::global_esp32_camera->request_stream(); - } -} -#endif - } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index be75d87264..db826c55c2 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -4,14 +4,12 @@ #include "esphome/core/controller.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" +#include "api_pb2.h" +#include "api_pb2_service.h" #include "util.h" -#include "api_message.h" -#include "basic_messages.h" #include "list_entities.h" #include "subscribe_state.h" -#include "subscribe_logs.h" -#include "command_messages.h" -#include "service_call_message.h" +#include "homeassistant_service.h" #include "user_services.h" #ifdef ARDUINO_ARCH_ESP32 @@ -24,130 +22,6 @@ namespace esphome { namespace api { -class APIServer; - -class APIConnection { - public: - APIConnection(AsyncClient *client, APIServer *parent); - ~APIConnection(); - - void disconnect_client(); - APIBuffer get_buffer(); - bool send_buffer(APIMessageType type); - bool send_message(APIMessage &msg); - bool send_empty_message(APIMessageType type); - void loop(); - -#ifdef USE_BINARY_SENSOR - bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state); -#endif -#ifdef USE_COVER - bool send_cover_state(cover::Cover *cover); -#endif -#ifdef USE_FAN - bool send_fan_state(fan::FanState *fan); -#endif -#ifdef USE_LIGHT - bool send_light_state(light::LightState *light); -#endif -#ifdef USE_SENSOR - bool send_sensor_state(sensor::Sensor *sensor, float state); -#endif -#ifdef USE_SWITCH - bool send_switch_state(switch_::Switch *a_switch, bool state); -#endif -#ifdef USE_TEXT_SENSOR - bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); -#endif -#ifdef USE_ESP32_CAMERA - void send_camera_state(std::shared_ptr image); -#endif -#ifdef USE_CLIMATE - bool send_climate_state(climate::Climate *climate); -#endif - bool send_log_message(int level, const char *tag, const char *line); - bool send_disconnect_request(); - bool send_ping_request(); - void send_service_call(ServiceCallResponse &call); -#ifdef USE_HOMEASSISTANT_TIME - void send_time_request(); -#endif - - protected: - friend APIServer; - - void on_error_(int8_t error); - void on_disconnect_(); - void on_timeout_(uint32_t time); - void on_data_(uint8_t *buf, size_t len); - void fatal_error_(); - bool valid_rx_message_type_(uint32_t msg_type); - void read_message_(uint32_t size, uint32_t type, uint8_t *msg); - void parse_recv_buffer_(); - - // request types - void on_hello_request_(const HelloRequest &req); - void on_connect_request_(const ConnectRequest &req); - void on_disconnect_request_(const DisconnectRequest &req); - void on_disconnect_response_(const DisconnectResponse &req); - void on_ping_request_(const PingRequest &req); - void on_ping_response_(const PingResponse &req); - void on_device_info_request_(const DeviceInfoRequest &req); - void on_list_entities_request_(const ListEntitiesRequest &req); - void on_subscribe_states_request_(const SubscribeStatesRequest &req); - void on_subscribe_logs_request_(const SubscribeLogsRequest &req); -#ifdef USE_COVER - void on_cover_command_request_(const CoverCommandRequest &req); -#endif -#ifdef USE_FAN - void on_fan_command_request_(const FanCommandRequest &req); -#endif -#ifdef USE_LIGHT - void on_light_command_request_(const LightCommandRequest &req); -#endif -#ifdef USE_SWITCH - void on_switch_command_request_(const SwitchCommandRequest &req); -#endif -#ifdef USE_CLIMATE - void on_climate_command_request_(const ClimateCommandRequest &req); -#endif - void on_subscribe_service_calls_request_(const SubscribeServiceCallsRequest &req); - void on_subscribe_home_assistant_states_request_(const SubscribeHomeAssistantStatesRequest &req); - void on_home_assistant_state_response_(const HomeAssistantStateResponse &req); - void on_execute_service_(const ExecuteServiceRequest &req); -#ifdef USE_ESP32_CAMERA - void on_camera_image_request_(const CameraImageRequest &req); -#endif - - enum class ConnectionState { - WAITING_FOR_HELLO, - WAITING_FOR_CONNECT, - CONNECTED, - } connection_state_{ConnectionState::WAITING_FOR_HELLO}; - - bool remove_{false}; - - std::vector send_buffer_; - std::vector recv_buffer_; - - std::string client_info_; -#ifdef USE_ESP32_CAMERA - esp32_camera::CameraImageReader image_reader_; -#endif - - bool state_subscription_{false}; - int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; - uint32_t last_traffic_; - bool sent_ping_{false}; - bool service_call_subscription_{false}; - AsyncClient *client_; - APIServer *parent_; - InitialStateIterator initial_state_iterator_; - ListEntitiesIterator list_entities_iterator_; -}; - -template class HomeAssistantServiceCallAction; - class APIServer : public Component, public Controller { public: APIServer(); @@ -187,7 +61,7 @@ class APIServer : public Component, public Controller { #ifdef USE_CLIMATE void on_climate_update(climate::Climate *obj) override; #endif - void send_service_call(ServiceCallResponse &call); + void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #ifdef USE_HOMEASSISTANT_TIME void request_time(); @@ -217,22 +91,6 @@ class APIServer : public Component, public Controller { extern APIServer *global_api_server; -template class HomeAssistantServiceCallAction : public Action { - public: - explicit HomeAssistantServiceCallAction(APIServer *parent) : parent_(parent) {} - void set_service(const std::string &service) { this->resp_.set_service(service); } - void set_data(const std::vector &data) { this->resp_.set_data(data); } - void set_data_template(const std::vector &data_template) { - this->resp_.set_data_template(data_template); - } - void set_variables(const std::vector &variables) { this->resp_.set_variables(variables); } - void play(Ts... x) override { this->parent_->send_service_call(this->resp_); } - - protected: - APIServer *parent_; - ServiceCallResponse resp_; -}; - template class APIConnectedCondition : public Condition { public: bool check(Ts... x) override { return global_api_server->is_connected(); } diff --git a/esphome/components/api/basic_messages.cpp b/esphome/components/api/basic_messages.cpp deleted file mode 100644 index 71e0045c05..0000000000 --- a/esphome/components/api/basic_messages.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "basic_messages.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace api { - -// Hello -bool HelloRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 1: // string client_info = 1; - this->client_info_ = as_string(value, len); - return true; - default: - return false; - } -} -const std::string &HelloRequest::get_client_info() const { return this->client_info_; } -void HelloRequest::set_client_info(const std::string &client_info) { this->client_info_ = client_info; } -APIMessageType HelloRequest::message_type() const { return APIMessageType::HELLO_REQUEST; } - -// Connect -bool ConnectRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 1: // string password = 1; - this->password_ = as_string(value, len); - return true; - default: - return false; - } -} -const std::string &ConnectRequest::get_password() const { return this->password_; } -void ConnectRequest::set_password(const std::string &password) { this->password_ = password; } -APIMessageType ConnectRequest::message_type() const { return APIMessageType::CONNECT_REQUEST; } - -APIMessageType DeviceInfoRequest::message_type() const { return APIMessageType::DEVICE_INFO_REQUEST; } -APIMessageType DisconnectRequest::message_type() const { return APIMessageType::DISCONNECT_REQUEST; } -bool DisconnectRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 1: // string reason = 1; - this->reason_ = as_string(value, len); - return true; - default: - return false; - } -} -const std::string &DisconnectRequest::get_reason() const { return this->reason_; } -void DisconnectRequest::set_reason(const std::string &reason) { this->reason_ = reason; } -void DisconnectRequest::encode(APIBuffer &buffer) { - // string reason = 1; - buffer.encode_string(1, this->reason_); -} -APIMessageType DisconnectResponse::message_type() const { return APIMessageType::DISCONNECT_RESPONSE; } -APIMessageType PingRequest::message_type() const { return APIMessageType::PING_REQUEST; } -APIMessageType PingResponse::message_type() const { return APIMessageType::PING_RESPONSE; } - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/basic_messages.h b/esphome/components/api/basic_messages.h deleted file mode 100644 index 213a053514..0000000000 --- a/esphome/components/api/basic_messages.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "api_message.h" - -namespace esphome { -namespace api { - -class HelloRequest : public APIMessage { - public: - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - const std::string &get_client_info() const; - void set_client_info(const std::string &client_info); - APIMessageType message_type() const override; - - protected: - std::string client_info_; -}; - -class ConnectRequest : public APIMessage { - public: - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - const std::string &get_password() const; - void set_password(const std::string &password); - APIMessageType message_type() const override; - - protected: - std::string password_; -}; - -class DeviceInfoRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -class DisconnectRequest : public APIMessage { - public: - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - void encode(APIBuffer &buffer) override; - APIMessageType message_type() const override; - const std::string &get_reason() const; - void set_reason(const std::string &reason); - - protected: - std::string reason_; -}; - -class DisconnectResponse : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -class PingRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -class PingResponse : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/command_messages.cpp b/esphome/components/api/command_messages.cpp deleted file mode 100644 index 240bc05cb7..0000000000 --- a/esphome/components/api/command_messages.cpp +++ /dev/null @@ -1,417 +0,0 @@ -#include "command_messages.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace api { - -#ifdef USE_COVER -bool CoverCommandRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 2: - // bool has_legacy_command = 2; - this->has_legacy_command_ = value; - return true; - case 3: - // enum LegacyCoverCommand { - // OPEN = 0; - // CLOSE = 1; - // STOP = 2; - // } - // LegacyCoverCommand legacy_command_ = 3; - this->legacy_command_ = static_cast(value); - return true; - case 4: - // bool has_position = 4; - this->has_position_ = value; - return true; - case 6: - // bool has_tilt = 6; - this->has_tilt_ = value; - return true; - case 8: - // bool stop = 8; - this->stop_ = value; - default: - return false; - } -} -bool CoverCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 key = 1; - this->key_ = value; - return true; - case 5: - // float position = 5; - this->position_ = as_float(value); - return true; - case 7: - // float tilt = 7; - this->tilt_ = as_float(value); - return true; - default: - return false; - } -} -APIMessageType CoverCommandRequest::message_type() const { return APIMessageType ::COVER_COMMAND_REQUEST; } -uint32_t CoverCommandRequest::get_key() const { return this->key_; } -optional CoverCommandRequest::get_legacy_command() const { - if (!this->has_legacy_command_) - return {}; - return this->legacy_command_; -} -optional CoverCommandRequest::get_position() const { - if (!this->has_position_) - return {}; - return this->position_; -} -optional CoverCommandRequest::get_tilt() const { - if (!this->has_tilt_) - return {}; - return this->tilt_; -} -#endif - -#ifdef USE_FAN -bool FanCommandRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 2: - // bool has_state = 2; - this->has_state_ = value; - return true; - case 3: - // bool state = 3; - this->state_ = value; - return true; - case 4: - // bool has_speed = 4; - this->has_speed_ = value; - return true; - case 5: - // FanSpeed speed = 5; - this->speed_ = static_cast(value); - return true; - case 6: - // bool has_oscillating = 6; - this->has_oscillating_ = value; - return true; - case 7: - // bool oscillating = 7; - this->oscillating_ = value; - return true; - default: - return false; - } -} -bool FanCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 key = 1; - this->key_ = value; - return true; - default: - return false; - } -} -APIMessageType FanCommandRequest::message_type() const { return APIMessageType::FAN_COMMAND_REQUEST; } -uint32_t FanCommandRequest::get_key() const { return this->key_; } -optional FanCommandRequest::get_state() const { - if (!this->has_state_) - return {}; - return this->state_; -} -optional FanCommandRequest::get_speed() const { - if (!this->has_speed_) - return {}; - return this->speed_; -} -optional FanCommandRequest::get_oscillating() const { - if (!this->has_oscillating_) - return {}; - return this->oscillating_; -} -#endif - -#ifdef USE_LIGHT -bool LightCommandRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 2: - // bool has_state = 2; - this->has_state_ = value; - return true; - case 3: - // bool state = 3; - this->state_ = value; - return true; - case 4: - // bool has_brightness = 4; - this->has_brightness_ = value; - return true; - case 6: - // bool has_rgb = 6; - this->has_rgb_ = value; - return true; - case 10: - // bool has_white = 10; - this->has_white_ = value; - return true; - case 12: - // bool has_color_temperature = 12; - this->has_color_temperature_ = value; - return true; - case 14: - // bool has_transition_length = 14; - this->has_transition_length_ = value; - return true; - case 15: - // uint32 transition_length = 15; - this->transition_length_ = value; - return true; - case 16: - // bool has_flash_length = 16; - this->has_flash_length_ = value; - return true; - case 17: - // uint32 flash_length = 17; - this->flash_length_ = value; - return true; - case 18: - // bool has_effect = 18; - this->has_effect_ = value; - return true; - default: - return false; - } -} -bool LightCommandRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 19: - // string effect = 19; - this->effect_ = as_string(value, len); - return true; - default: - return false; - } -} -bool LightCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 key = 1; - this->key_ = value; - return true; - case 5: - // float brightness = 5; - this->brightness_ = as_float(value); - return true; - case 7: - // float red = 7; - this->red_ = as_float(value); - return true; - case 8: - // float green = 8; - this->green_ = as_float(value); - return true; - case 9: - // float blue = 9; - this->blue_ = as_float(value); - return true; - case 11: - // float white = 11; - this->white_ = as_float(value); - return true; - case 13: - // float color_temperature = 13; - this->color_temperature_ = as_float(value); - return true; - default: - return false; - } -} -APIMessageType LightCommandRequest::message_type() const { return APIMessageType::LIGHT_COMMAND_REQUEST; } -uint32_t LightCommandRequest::get_key() const { return this->key_; } -optional LightCommandRequest::get_state() const { - if (!this->has_state_) - return {}; - return this->state_; -} -optional LightCommandRequest::get_brightness() const { - if (!this->has_brightness_) - return {}; - return this->brightness_; -} -optional LightCommandRequest::get_red() const { - if (!this->has_rgb_) - return {}; - return this->red_; -} -optional LightCommandRequest::get_green() const { - if (!this->has_rgb_) - return {}; - return this->green_; -} -optional LightCommandRequest::get_blue() const { - if (!this->has_rgb_) - return {}; - return this->blue_; -} -optional LightCommandRequest::get_white() const { - if (!this->has_white_) - return {}; - return this->white_; -} -optional LightCommandRequest::get_color_temperature() const { - if (!this->has_color_temperature_) - return {}; - return this->color_temperature_; -} -optional LightCommandRequest::get_transition_length() const { - if (!this->has_transition_length_) - return {}; - return this->transition_length_; -} -optional LightCommandRequest::get_flash_length() const { - if (!this->has_flash_length_) - return {}; - return this->flash_length_; -} -optional LightCommandRequest::get_effect() const { - if (!this->has_effect_) - return {}; - return this->effect_; -} -#endif - -#ifdef USE_SWITCH -bool SwitchCommandRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 2: - // bool state = 2; - this->state_ = value; - return true; - default: - return false; - } -} -bool SwitchCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 key = 1; - this->key_ = value; - return true; - default: - return false; - } -} -APIMessageType SwitchCommandRequest::message_type() const { return APIMessageType::SWITCH_COMMAND_REQUEST; } -uint32_t SwitchCommandRequest::get_key() const { return this->key_; } -bool SwitchCommandRequest::get_state() const { return this->state_; } -#endif - -#ifdef USE_ESP32_CAMERA -bool CameraImageRequest::get_single() const { return this->single_; } -bool CameraImageRequest::get_stream() const { return this->stream_; } -bool CameraImageRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // bool single = 1; - this->single_ = value; - return true; - case 2: - // bool stream = 2; - this->stream_ = value; - return true; - default: - return false; - } -} -APIMessageType CameraImageRequest::message_type() const { return APIMessageType::CAMERA_IMAGE_REQUEST; } -#endif - -#ifdef USE_CLIMATE -bool ClimateCommandRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 2: - // bool has_mode = 2; - this->has_mode_ = value; - return true; - case 3: - // ClimateMode mode = 3; - this->mode_ = static_cast(value); - return true; - case 4: - // bool has_target_temperature = 4; - this->has_target_temperature_ = value; - return true; - case 6: - // bool has_target_temperature_low = 6; - this->has_target_temperature_low_ = value; - return true; - case 8: - // bool has_target_temperature_high = 8; - this->has_target_temperature_high_ = value; - return true; - case 10: - // bool has_away = 10; - this->has_away_ = value; - return true; - case 11: - // bool away = 11; - this->away_ = value; - return true; - default: - return false; - } -} -bool ClimateCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 key = 1; - this->key_ = value; - return true; - case 5: - // float target_temperature = 5; - this->target_temperature_ = as_float(value); - return true; - case 7: - // float target_temperature_low = 7; - this->target_temperature_low_ = as_float(value); - return true; - case 9: - // float target_temperature_high = 9; - this->target_temperature_high_ = as_float(value); - return true; - default: - return false; - } -} -APIMessageType ClimateCommandRequest::message_type() const { return APIMessageType::CLIMATE_COMMAND_REQUEST; } -uint32_t ClimateCommandRequest::get_key() const { return this->key_; } -optional ClimateCommandRequest::get_mode() const { - if (!this->has_mode_) - return {}; - return this->mode_; -} -optional ClimateCommandRequest::get_target_temperature() const { - if (!this->has_target_temperature_) - return {}; - return this->target_temperature_; -} -optional ClimateCommandRequest::get_target_temperature_low() const { - if (!this->has_target_temperature_low_) - return {}; - return this->target_temperature_low_; -} -optional ClimateCommandRequest::get_target_temperature_high() const { - if (!this->has_target_temperature_high_) - return {}; - return this->target_temperature_high_; -} -optional ClimateCommandRequest::get_away() const { - if (!this->has_away_) - return {}; - return this->away_; -} -#endif - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/command_messages.h b/esphome/components/api/command_messages.h deleted file mode 100644 index 09ca9cb126..0000000000 --- a/esphome/components/api/command_messages.h +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/core/defines.h" -#include "api_message.h" - -namespace esphome { -namespace api { - -#ifdef USE_COVER -enum LegacyCoverCommand { - LEGACY_COVER_COMMAND_OPEN = 0, - LEGACY_COVER_COMMAND_CLOSE = 1, - LEGACY_COVER_COMMAND_STOP = 2, -}; - -class CoverCommandRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_key() const; - optional get_legacy_command() const; - optional get_position() const; - optional get_tilt() const; - bool get_stop() const { return this->stop_; } - - protected: - uint32_t key_{0}; - bool has_legacy_command_{false}; - LegacyCoverCommand legacy_command_{LEGACY_COVER_COMMAND_OPEN}; - bool has_position_{false}; - float position_{0.0f}; - bool has_tilt_{false}; - float tilt_{0.0f}; - bool stop_{false}; -}; -#endif - -#ifdef USE_FAN -class FanCommandRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_key() const; - optional get_state() const; - optional get_speed() const; - optional get_oscillating() const; - - protected: - uint32_t key_{0}; - bool has_state_{false}; - bool state_{false}; - bool has_speed_{false}; - fan::FanSpeed speed_{fan::FAN_SPEED_LOW}; - bool has_oscillating_{false}; - bool oscillating_{false}; -}; -#endif - -#ifdef USE_LIGHT -class LightCommandRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_key() const; - optional get_state() const; - optional get_brightness() const; - optional get_red() const; - optional get_green() const; - optional get_blue() const; - optional get_white() const; - optional get_color_temperature() const; - optional get_transition_length() const; - optional get_flash_length() const; - optional get_effect() const; - - protected: - uint32_t key_{0}; - bool has_state_{false}; - bool state_{false}; - bool has_brightness_{false}; - float brightness_{0.0f}; - bool has_rgb_{false}; - float red_{0.0f}; - float green_{0.0f}; - float blue_{0.0f}; - bool has_white_{false}; - float white_{0.0f}; - bool has_color_temperature_{false}; - float color_temperature_{0.0f}; - bool has_transition_length_{false}; - uint32_t transition_length_{0}; - bool has_flash_length_{false}; - uint32_t flash_length_{0}; - bool has_effect_{false}; - std::string effect_{}; -}; -#endif - -#ifdef USE_SWITCH -class SwitchCommandRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_key() const; - bool get_state() const; - - protected: - uint32_t key_{0}; - bool state_{false}; -}; -#endif - -#ifdef USE_ESP32_CAMERA -class CameraImageRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool get_single() const; - bool get_stream() const; - APIMessageType message_type() const override; - - protected: - bool single_{false}; - bool stream_{false}; -}; -#endif - -#ifdef USE_CLIMATE -class ClimateCommandRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_key() const; - optional get_mode() const; - optional get_target_temperature() const; - optional get_target_temperature_low() const; - optional get_target_temperature_high() const; - optional get_away() const; - - protected: - uint32_t key_{0}; - bool has_mode_{false}; - climate::ClimateMode mode_{climate::CLIMATE_MODE_OFF}; - bool has_target_temperature_{false}; - float target_temperature_{0.0f}; - bool has_target_temperature_low_{false}; - float target_temperature_low_{0.0f}; - bool has_target_temperature_high_{false}; - float target_temperature_high_{0.0f}; - bool has_away_{false}; - bool away_{false}; -}; -#endif - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h new file mode 100644 index 0000000000..aac91244b6 --- /dev/null +++ b/esphome/components/api/custom_api_device.h @@ -0,0 +1,214 @@ +#pragma once + +#include +#include "user_services.h" +#include "api_server.h" + +namespace esphome { +namespace api { + +template class CustomAPIDeviceService : public UserServiceBase { + public: + CustomAPIDeviceService(const std::string &name, const std::array &arg_names, T *obj, + void (T::*callback)(Ts...)) + : UserServiceBase(name, arg_names), obj_(obj), callback_(callback) {} + + protected: + void execute(Ts... x) override { (this->obj_->*this->callback_)(x...); } // NOLINT + + T *obj_; + void (T::*callback_)(Ts...); +}; + +class CustomAPIDevice { + public: + /// Return if a client (such as Home Assistant) is connected to the native API. + bool is_connected() const { return global_api_server->is_connected(); } + + /** Register a custom native API service that will show up in Home Assistant. + * + * Usage: + * + * ```cpp + * void setup() override { + * register_service(&CustomNativeAPI::on_start_washer_cycle, "start_washer_cycle", + * {"cycle_length"}); + * } + * + * void on_start_washer_cycle(int cycle_length) { + * // Start washer cycle. + * } + * ``` + * + * @tparam T The class type creating the service, automatically deduced from the function pointer. + * @tparam Ts The argument types for the service, automatically deduced from the function arguments. + * @param callback The member function to call when the service is triggered. + * @param name The name of the service to register. + * @param arg_names The name of the arguments for the service, must match the arguments of the function. + */ + template + void register_service(void (T::*callback)(Ts...), const std::string &name, + const std::array &arg_names) { + auto *service = new CustomAPIDeviceService(name, arg_names, (T *) this, callback); + global_api_server->register_user_service(service); + } + + /** Register a custom native API service that will show up in Home Assistant. + * + * Usage: + * + * ```cpp + * void setup() override { + * register_service(&CustomNativeAPI::on_hello_world, "hello_world"); + * } + * + * void on_hello_world() { + * // Hello World service called. + * } + * ``` + * + * @tparam T The class type creating the service, automatically deduced from the function pointer. + * @param callback The member function to call when the service is triggered. + * @param name The name of the arguments for the service, must match the arguments of the function. + */ + template void register_service(void (T::*callback)(), const std::string &name) { + auto *service = new CustomAPIDeviceService(name, {}, (T *) this, callback); + global_api_server->register_user_service(service); + } + + /** Subscribe to the state of an entity from Home Assistant. + * + * Usage: + * + * ```cpp + * void setup() override { + * subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast"); + * } + * + * void on_state_changed(std::string state) { + * // State of sensor.weather_forecast is `state` + * } + * ``` + * + * @tparam T The class type creating the service, automatically deduced from the function pointer. + * @param callback The member function to call when the entity state changes. + * @param entity_id The entity_id to track. + */ + template + void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) { + auto f = std::bind(callback, (T *) this, std::placeholders::_1); + global_api_server->subscribe_home_assistant_state(entity_id, f); + } + + /** Subscribe to the state of an entity from Home Assistant. + * + * Usage: + * + * ```cpp + * void setup() override { + * subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast"); + * } + * + * void on_state_changed(std::string entity_id, std::string state) { + * // State of `entity_id` is `state` + * } + * ``` + * + * @tparam T The class type creating the service, automatically deduced from the function pointer. + * @param callback The member function to call when the entity state changes. + * @param entity_id The entity_id to track. + */ + template + void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) { + auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1); + global_api_server->subscribe_home_assistant_state(entity_id, f); + } + + /** Call a Home Assistant service from ESPHome. + * + * Usage: + * + * ```cpp + * call_homeassistant_service("homeassistant.restart"); + * ``` + * + * @param service_name The service to call. + */ + void call_homeassistant_service(const std::string &service_name) { + HomeassistantServiceResponse resp; + resp.service = service_name; + global_api_server->send_homeassistant_service_call(resp); + } + + /** Call a Home Assistant service from ESPHome. + * + * Usage: + * + * ```cpp + * call_homeassistant_service("light.turn_on", { + * {"entity_id", "light.my_light"}, + * {"brightness", "127"}, + * }); + * ``` + * + * @param service_name The service to call. + * @param data The data for the service call, mapping from string to string. + */ + void call_homeassistant_service(const std::string &service_name, const std::map &data) { + HomeassistantServiceResponse resp; + resp.service = service_name; + for (auto &it : data) { + HomeassistantServiceMap kv; + kv.key = it.first; + kv.value = it.second; + resp.data.push_back(kv); + } + global_api_server->send_homeassistant_service_call(resp); + } + + /** Fire an ESPHome event in Home Assistant. + * + * Usage: + * + * ```cpp + * fire_homeassistant_event("esphome.something_happened"); + * ``` + * + * @param event_name The event to fire. + */ + void fire_homeassistant_event(const std::string &event_name) { + HomeassistantServiceResponse resp; + resp.service = event_name; + resp.is_event = true; + global_api_server->send_homeassistant_service_call(resp); + } + + /** Fire an ESPHome event in Home Assistant. + * + * Usage: + * + * ```cpp + * fire_homeassistant_event("esphome.something_happened", { + * {"my_value", "500"}, + * }); + * ``` + * + * @param event_name The event to fire. + * @param data The data for the event, mapping from string to string. + */ + void fire_homeassistant_event(const std::string &service_name, const std::map &data) { + HomeassistantServiceResponse resp; + resp.service = service_name; + resp.is_event = true; + for (auto &it : data) { + HomeassistantServiceMap kv; + kv.key = it.first; + kv.value = it.second; + resp.data.push_back(kv); + } + global_api_server->send_homeassistant_service_call(resp); + } +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h new file mode 100644 index 0000000000..d68dac3b61 --- /dev/null +++ b/esphome/components/api/homeassistant_service.h @@ -0,0 +1,66 @@ +#pragma once + +#include "esphome/core/helpers.h" +#include "esphome/core/automation.h" +#include "api_pb2.h" +#include "api_server.h" + +namespace esphome { +namespace api { + +template class TemplatableKeyValuePair { + public: + template TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {} + std::string key; + TemplatableStringValue value; +}; + +template class HomeAssistantServiceCallAction : public Action { + public: + explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {} + + TEMPLATABLE_STRING_VALUE(service); + template void add_data(std::string key, T value) { + this->data_.push_back(TemplatableKeyValuePair(key, value)); + } + template void add_data_template(std::string key, T value) { + this->data_template_.push_back(TemplatableKeyValuePair(key, value)); + } + template void add_variable(std::string key, T value) { + this->variables_.push_back(TemplatableKeyValuePair(key, value)); + } + void play(Ts... x) override { + HomeassistantServiceResponse resp; + resp.service = this->service_.value(x...); + resp.is_event = this->is_event_; + for (auto &it : this->data_) { + HomeassistantServiceMap kv; + kv.key = it.key; + kv.value = it.value.value(x...); + resp.data.push_back(kv); + } + for (auto &it : this->data_template_) { + HomeassistantServiceMap kv; + kv.key = it.key; + kv.value = it.value.value(x...); + resp.data_template.push_back(kv); + } + for (auto &it : this->variables_) { + HomeassistantServiceMap kv; + kv.key = it.key; + kv.value = it.value.value(x...); + resp.variables.push_back(kv); + } + this->parent_->send_homeassistant_service_call(resp); + } + + protected: + APIServer *parent_; + bool is_event_; + std::vector> data_; + std::vector> data_template_; + std::vector> variables_; +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index ae63d70448..d4245136ae 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -2,189 +2,54 @@ #include "esphome/core/util.h" #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "api_connection.h" namespace esphome { namespace api { -std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) { - return App.get_name() + component_type + nameable->get_object_id(); -} - #ifdef USE_BINARY_SENSOR bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(binary_sensor); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("binary_sensor", binary_sensor)); - // string device_class = 5; - buffer.encode_string(5, binary_sensor->get_device_class()); - // bool is_status_binary_sensor = 6; - buffer.encode_bool(6, binary_sensor->is_status_binary_sensor()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_BINARY_SENSOR_RESPONSE); + return this->client_->send_binary_sensor_info(binary_sensor); } #endif #ifdef USE_COVER -bool ListEntitiesIterator::on_cover(cover::Cover *cover) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(cover); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("cover", cover)); - auto traits = cover->get_traits(); - - // bool assumed_state = 5; - buffer.encode_bool(5, traits.get_is_assumed_state()); - // bool supports_position = 6; - buffer.encode_bool(6, traits.get_supports_position()); - // bool supports_tilt = 7; - buffer.encode_bool(7, traits.get_supports_tilt()); - // string device_class = 8; - buffer.encode_string(8, cover->get_device_class()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_COVER_RESPONSE); -} +bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); } #endif #ifdef USE_FAN -bool ListEntitiesIterator::on_fan(fan::FanState *fan) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(fan); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("fan", fan)); - // bool supports_oscillation = 5; - buffer.encode_bool(5, fan->get_traits().supports_oscillation()); - // bool supports_speed = 6; - buffer.encode_bool(6, fan->get_traits().supports_speed()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_FAN_RESPONSE); -} +bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); } #endif #ifdef USE_LIGHT -bool ListEntitiesIterator::on_light(light::LightState *light) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(light); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("light", light)); - // bool supports_brightness = 5; - auto traits = light->get_traits(); - buffer.encode_bool(5, traits.get_supports_brightness()); - // bool supports_rgb = 6; - buffer.encode_bool(6, traits.get_supports_rgb()); - // bool supports_white_value = 7; - buffer.encode_bool(7, traits.get_supports_rgb_white_value()); - // bool supports_color_temperature = 8; - buffer.encode_bool(8, traits.get_supports_color_temperature()); - if (traits.get_supports_color_temperature()) { - // float min_mireds = 9; - buffer.encode_float(9, traits.get_min_mireds()); - // float max_mireds = 10; - buffer.encode_float(10, traits.get_max_mireds()); - } - // repeated string effects = 11; - if (light->supports_effects()) { - buffer.encode_string(11, "None"); - for (auto *effect : light->get_effects()) { - buffer.encode_string(11, effect->get_name()); - } - } - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_LIGHT_RESPONSE); -} +bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); } #endif #ifdef USE_SENSOR -bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(sensor); - // string unique_id = 4; - std::string unique_id = sensor->unique_id(); - if (unique_id.empty()) - unique_id = get_default_unique_id("sensor", sensor); - buffer.encode_string(4, unique_id); - // string icon = 5; - buffer.encode_string(5, sensor->get_icon()); - // string unit_of_measurement = 6; - buffer.encode_string(6, sensor->get_unit_of_measurement()); - // int32 accuracy_decimals = 7; - buffer.encode_int32(7, sensor->get_accuracy_decimals()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SENSOR_RESPONSE); -} +bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_info(sensor); } #endif #ifdef USE_SWITCH -bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(a_switch); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("switch", a_switch)); - // string icon = 5; - buffer.encode_string(5, a_switch->get_icon()); - // bool assumed_state = 6; - buffer.encode_bool(6, a_switch->assumed_state()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SWITCH_RESPONSE); -} +bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); } #endif #ifdef USE_TEXT_SENSOR bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(text_sensor); - // string unique_id = 4; - std::string unique_id = text_sensor->unique_id(); - if (unique_id.empty()) - unique_id = get_default_unique_id("text_sensor", text_sensor); - buffer.encode_string(4, unique_id); - // string icon = 5; - buffer.encode_string(5, text_sensor->get_icon()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_TEXT_SENSOR_RESPONSE); + return this->client_->send_text_sensor_info(text_sensor); } #endif -bool ListEntitiesIterator::on_end() { - return this->client_->send_empty_message(APIMessageType::LIST_ENTITIES_DONE_RESPONSE); -} +bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client) : ComponentIterator(server), client_(client) {} bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { - auto buffer = this->client_->get_buffer(); - service->encode_list_service_response(buffer); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE); + auto resp = service->encode_list_service_response(); + return this->client_->send_list_entities_services_response(resp); } #ifdef USE_ESP32_CAMERA bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(camera); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("camera", camera)); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE); + return this->client_->send_camera_info(camera); } #endif #ifdef USE_CLIMATE -bool ListEntitiesIterator::on_climate(climate::Climate *climate) { - auto buffer = this->client_->get_buffer(); - buffer.encode_nameable(climate); - // string unique_id = 4; - buffer.encode_string(4, get_default_unique_id("climate", climate)); - - auto traits = climate->get_traits(); - // bool supports_current_temperature = 5; - buffer.encode_bool(5, traits.get_supports_current_temperature()); - // bool supports_two_point_target_temperature = 6; - buffer.encode_bool(6, traits.get_supports_two_point_target_temperature()); - // repeated ClimateMode supported_modes = 7; - for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, - climate::CLIMATE_MODE_HEAT}) { - if (traits.supports_mode(mode)) - buffer.encode_uint32(7, mode, true); - } - - // float visual_min_temperature = 8; - buffer.encode_float(8, traits.get_visual_min_temperature()); - // float visual_max_temperature = 9; - buffer.encode_float(9, traits.get_visual_max_temperature()); - // float visual_temperature_step = 10; - buffer.encode_float(10, traits.get_visual_temperature_step()); - // bool supports_away = 11; - buffer.encode_bool(11, traits.get_supports_away()); - return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_CLIMATE_RESPONSE); -} +bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); } #endif -APIMessageType ListEntitiesRequest::message_type() const { return APIMessageType::LIST_ENTITIES_REQUEST; } - } // namespace api } // namespace esphome diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index c1c93e91f8..6b10a72fdf 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -2,16 +2,11 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "api_message.h" +#include "util.h" namespace esphome { namespace api { -class ListEntitiesRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - class APIConnection; class ListEntitiesIterator : public ComponentIterator { diff --git a/esphome/components/api/api_message.cpp b/esphome/components/api/proto.cpp similarity index 64% rename from esphome/components/api/api_message.cpp rename to esphome/components/api/proto.cpp index 157ddba2b5..3d2f669f54 100644 --- a/esphome/components/api/api_message.cpp +++ b/esphome/components/api/proto.cpp @@ -1,61 +1,59 @@ -#include "api_message.h" +#include "proto.h" +#include "util.h" #include "esphome/core/log.h" namespace esphome { namespace api { -static const char *TAG = "api.message"; +static const char *TAG = "api.proto"; -bool APIMessage::decode_varint(uint32_t field_id, uint32_t value) { return false; } -bool APIMessage::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { return false; } -bool APIMessage::decode_32bit(uint32_t field_id, uint32_t value) { return false; } -void APIMessage::encode(APIBuffer &buffer) {} -void APIMessage::decode(const uint8_t *buffer, size_t length) { +void ProtoMessage::decode(const uint8_t *buffer, size_t length) { uint32_t i = 0; bool error = false; while (i < length) { uint32_t consumed; - auto res = proto_decode_varuint32(&buffer[i], length - i, &consumed); + auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { ESP_LOGV(TAG, "Invalid field start at %u", i); break; } - uint32_t field_type = (*res) & 0b111; - uint32_t field_id = (*res) >> 3; + uint32_t field_type = (res->as_uint32()) & 0b111; + uint32_t field_id = (res->as_uint32()) >> 3; i += consumed; switch (field_type) { case 0: { // VarInt - res = proto_decode_varuint32(&buffer[i], length - i, &consumed); + res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { ESP_LOGV(TAG, "Invalid VarInt at %u", i); error = true; break; } if (!this->decode_varint(field_id, *res)) { - ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, *res); + ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32()); } i += consumed; break; } case 2: { // Length-delimited - res = proto_decode_varuint32(&buffer[i], length - i, &consumed); + res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { ESP_LOGV(TAG, "Invalid Length Delimited at %u", i); error = true; break; } + uint32_t field_length = res->as_uint32(); i += consumed; - if (*res > length - i) { + if (field_length > length - i) { ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i); error = true; break; } - if (!this->decode_length_delimited(field_id, &buffer[i], *res)) { + if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) { ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id); } - i += *res; + i += field_length; break; } case 5: { // 32-bit @@ -66,7 +64,7 @@ void APIMessage::decode(const uint8_t *buffer, size_t length) { } uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) | (uint32_t(buffer[i + 3]) << 24); - if (!this->decode_32bit(field_id, val)) { + if (!this->decode_32bit(field_id, Proto32Bit(val))) { ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val); } i += 4; @@ -83,5 +81,11 @@ void APIMessage::decode(const uint8_t *buffer, size_t length) { } } +std::string ProtoMessage::dump() const { + std::string out; + this->dump_to(out); + return out; +} + } // namespace api } // namespace esphome diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h new file mode 100644 index 0000000000..b0160bf240 --- /dev/null +++ b/esphome/components/api/proto.h @@ -0,0 +1,279 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace api { + +/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit +class ProtoVarInt { + public: + ProtoVarInt() : value_(0) {} + explicit ProtoVarInt(uint64_t value) : value_(value) {} + + static optional parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) { + if (consumed != nullptr) + *consumed = 0; + + if (len == 0) + return {}; + + uint64_t result = 0; + uint8_t bitpos = 0; + + for (uint32_t i = 0; i < len; i++) { + uint8_t val = buffer[i]; + result |= uint64_t(val & 0x7F) << uint64_t(bitpos); + bitpos += 7; + if ((val & 0x80) == 0) { + if (consumed != nullptr) + *consumed = i + 1; + return ProtoVarInt(result); + } + } + + return {}; + } + + uint32_t as_uint32() const { return this->value_; } + uint64_t as_uint64() const { return this->value_; } + bool as_bool() const { return this->value_; } + template T as_enum() const { return static_cast(this->as_uint32()); } + int32_t as_int32() const { + // Not ZigZag encoded + return static_cast(this->as_int64()); + } + int64_t as_int64() const { + // Not ZigZag encoded + return static_cast(this->value_); + } + int32_t as_sint32() const { + // with ZigZag encoding + if (this->value_ & 1) + return static_cast(~(this->value_ >> 1)); + else + return static_cast(this->value_ >> 1); + } + int64_t as_sint64() const { + // with ZigZag encoding + if (this->value_ & 1) + return static_cast(~(this->value_ >> 1)); + else + return static_cast(this->value_ >> 1); + } + void encode(std::vector &out) { + uint32_t val = this->value_; + if (val <= 0x7F) { + out.push_back(val); + return; + } + while (val) { + uint8_t temp = val & 0x7F; + val >>= 7; + if (val) { + out.push_back(temp | 0x80); + } else { + out.push_back(temp); + } + } + } + + protected: + uint64_t value_; +}; + +class ProtoLengthDelimited { + public: + explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {} + std::string as_string() const { return std::string(reinterpret_cast(this->value_), this->length_); } + template C as_message() const { + auto msg = C(); + msg.decode(this->value_, this->length_); + return msg; + } + + protected: + const uint8_t *const value_; + const size_t length_; +}; + +class Proto32Bit { + public: + explicit Proto32Bit(uint32_t value) : value_(value) {} + uint32_t as_fixed32() const { return this->value_; } + int32_t as_sfixed32() const { return static_cast(this->value_); } + float as_float() const { + union { + uint32_t raw; + float value; + } s{}; + s.raw = this->value_; + return s.value; + } + + protected: + const uint32_t value_; +}; + +class Proto64Bit { + public: + explicit Proto64Bit(uint64_t value) : value_(value) {} + uint64_t as_fixed64() const { return this->value_; } + int64_t as_sfixed64() const { return static_cast(this->value_); } + double as_double() const { + union { + uint64_t raw; + double value; + } s{}; + s.raw = this->value_; + return s.value; + } + + protected: + const uint64_t value_; +}; + +class ProtoWriteBuffer { + public: + ProtoWriteBuffer(std::vector *buffer) : buffer_(buffer) {} + void write(uint8_t value) { this->buffer_->push_back(value); } + void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); } + void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); } + void encode_field_raw(uint32_t field_id, uint32_t type) { + uint32_t val = (field_id << 3) | (type & 0b111); + this->encode_varint_raw(val); + } + void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) { + if (len == 0 && !force) + return; + + this->encode_field_raw(field_id, 2); + this->encode_varint_raw(len); + auto *data = reinterpret_cast(string); + for (size_t i = 0; i < len; i++) + this->write(data[i]); + } + void encode_string(uint32_t field_id, const std::string &value, bool force = false) { + this->encode_string(field_id, value.data(), value.size()); + } + void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) { + this->encode_string(field_id, reinterpret_cast(data), len, force); + } + void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) { + if (value == 0 && !force) + return; + this->encode_field_raw(field_id, 0); + this->encode_varint_raw(value); + } + void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) { + if (value == 0 && !force) + return; + this->encode_field_raw(field_id, 0); + this->encode_varint_raw(ProtoVarInt(value)); + } + void encode_bool(uint32_t field_id, bool value, bool force = false) { + if (!value && !force) + return; + this->encode_field_raw(field_id, 0); + this->write(0x01); + } + void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) { + if (value == 0 && !force) + return; + + this->encode_field_raw(field_id, 5); + this->write((value >> 0) & 0xFF); + this->write((value >> 8) & 0xFF); + this->write((value >> 16) & 0xFF); + this->write((value >> 24) & 0xFF); + } + template void encode_enum(uint32_t field_id, T value, bool force = false) { + this->encode_uint32(field_id, static_cast(value), force); + } + void encode_float(uint32_t field_id, float value, bool force = false) { + if (value == 0.0f && !force) + return; + + union { + float value; + uint32_t raw; + } val{}; + val.value = value; + this->encode_fixed32(field_id, val.raw); + } + void encode_int32(uint32_t field_id, int32_t value, bool force = false) { + if (value < 0) { + // negative int32 is always 10 byte long + this->encode_int64(field_id, value, force); + return; + } + this->encode_uint32(field_id, static_cast(value), force); + } + void encode_int64(uint32_t field_id, int64_t value, bool force = false) { + this->encode_uint64(field_id, static_cast(value), force); + } + void encode_sint32(uint32_t field_id, int32_t value, bool force = false) { + uint32_t uvalue; + if (value < 0) + uvalue = ~(value << 1); + else + uvalue = value << 1; + this->encode_uint32(field_id, uvalue, force); + } + template void encode_message(uint32_t field_id, const C &value, bool force = false) { + this->encode_field_raw(field_id, 2); + size_t begin = this->buffer_->size(); + + value.encode(*this); + + const uint32_t nested_length = this->buffer_->size() - begin; + // add size varint + std::vector var; + ProtoVarInt(nested_length).encode(var); + this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end()); + } + std::vector *get_buffer() const { return buffer_; } + + protected: + std::vector *buffer_; +}; + +class ProtoMessage { + public: + virtual void encode(ProtoWriteBuffer buffer) const = 0; + void decode(const uint8_t *buffer, size_t length); + std::string dump() const; + virtual void dump_to(std::string &out) const = 0; + + protected: + virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; } + virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; } + virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } + virtual bool decode_64bit(uint32_t field_id, Proto64Bit value) { return false; } +}; + +template const char *proto_enum_to_string(T value); + +class ProtoService { + public: + protected: + virtual bool is_authenticated() = 0; + virtual bool is_connection_setup() = 0; + virtual void on_fatal_error() = 0; + virtual void on_unauthenticated_access() = 0; + virtual void on_no_setup_connection() = 0; + virtual ProtoWriteBuffer create_buffer() = 0; + virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0; + virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; + virtual void set_nodelay(bool nodelay) = 0; + + template bool send_message_(const C &msg, uint32_t message_type) { + auto buffer = this->create_buffer(); + msg.encode(buffer); + return this->send_buffer(buffer, message_type); + } +}; + +} // namespace api +} // namespace esphome diff --git a/esphome/components/api/service_call_message.cpp b/esphome/components/api/service_call_message.cpp deleted file mode 100644 index f4c5741c94..0000000000 --- a/esphome/components/api/service_call_message.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "service_call_message.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace api { - -APIMessageType SubscribeServiceCallsRequest::message_type() const { - return APIMessageType::SUBSCRIBE_SERVICE_CALLS_REQUEST; -} - -APIMessageType ServiceCallResponse::message_type() const { return APIMessageType::SERVICE_CALL_RESPONSE; } -void ServiceCallResponse::encode(APIBuffer &buffer) { - // string service = 1; - buffer.encode_string(1, this->service_); - // map data = 2; - for (auto &it : this->data_) { - auto nested = buffer.begin_nested(2); - buffer.encode_string(1, it.key); - buffer.encode_string(2, it.value); - buffer.end_nested(nested); - } - // map data_template = 3; - for (auto &it : this->data_template_) { - auto nested = buffer.begin_nested(3); - buffer.encode_string(1, it.key); - buffer.encode_string(2, it.value); - buffer.end_nested(nested); - } - // map variables = 4; - for (auto &it : this->variables_) { - auto nested = buffer.begin_nested(4); - buffer.encode_string(1, it.key); - buffer.encode_string(2, it.value()); - buffer.end_nested(nested); - } -} -void ServiceCallResponse::set_service(const std::string &service) { this->service_ = service; } -void ServiceCallResponse::set_data(const std::vector &data) { this->data_ = data; } -void ServiceCallResponse::set_data_template(const std::vector &data_template) { - this->data_template_ = data_template; -} -void ServiceCallResponse::set_variables(const std::vector &variables) { - this->variables_ = variables; -} - -KeyValuePair::KeyValuePair(const std::string &key, const std::string &value) : key(key), value(value) {} - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/service_call_message.h b/esphome/components/api/service_call_message.h deleted file mode 100644 index bb9d17a110..0000000000 --- a/esphome/components/api/service_call_message.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "esphome/core/helpers.h" -#include "esphome/core/automation.h" -#include "api_message.h" - -namespace esphome { -namespace api { - -class SubscribeServiceCallsRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -class KeyValuePair { - public: - KeyValuePair(const std::string &key, const std::string &value); - - std::string key; - std::string value; -}; - -class TemplatableKeyValuePair { - public: - template TemplatableKeyValuePair(std::string key, T func); - - std::string key; - std::function value; -}; -template TemplatableKeyValuePair::TemplatableKeyValuePair(std::string key, T func) : key(key) { - this->value = [func]() -> std::string { return to_string(func()); }; -} - -class ServiceCallResponse : public APIMessage { - public: - APIMessageType message_type() const override; - - void encode(APIBuffer &buffer) override; - - void set_service(const std::string &service); - void set_data(const std::vector &data); - void set_data_template(const std::vector &data_template); - void set_variables(const std::vector &variables); - - protected: - std::string service_; - std::vector data_; - std::vector data_template_; - std::vector variables_; -}; - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/subscribe_logs.cpp b/esphome/components/api/subscribe_logs.cpp deleted file mode 100644 index f7f5be9fad..0000000000 --- a/esphome/components/api/subscribe_logs.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "subscribe_logs.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace api { - -APIMessageType SubscribeLogsRequest::message_type() const { return APIMessageType::SUBSCRIBE_LOGS_REQUEST; } -bool SubscribeLogsRequest::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: // LogLevel level = 1; - this->level_ = value; - return true; - case 2: // bool dump_config = 2; - this->dump_config_ = value; - return true; - default: - return false; - } -} -uint32_t SubscribeLogsRequest::get_level() const { return this->level_; } -void SubscribeLogsRequest::set_level(uint32_t level) { this->level_ = level; } -bool SubscribeLogsRequest::get_dump_config() const { return this->dump_config_; } -void SubscribeLogsRequest::set_dump_config(bool dump_config) { this->dump_config_ = dump_config; } - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/subscribe_logs.h b/esphome/components/api/subscribe_logs.h deleted file mode 100644 index f9b0db75b1..0000000000 --- a/esphome/components/api/subscribe_logs.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "api_message.h" - -namespace esphome { -namespace api { - -class SubscribeLogsRequest : public APIMessage { - public: - bool decode_varint(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - uint32_t get_level() const; - void set_level(uint32_t level); - bool get_dump_config() const; - void set_dump_config(bool dump_config); - - protected: - uint32_t level_{6}; - bool dump_config_{false}; -}; - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 8f312d5421..50d674bee2 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -1,4 +1,5 @@ #include "subscribe_state.h" +#include "api_connection.h" #include "esphome/core/log.h" namespace esphome { @@ -48,30 +49,5 @@ bool InitialStateIterator::on_climate(climate::Climate *climate) { return this-> InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) : ComponentIterator(server), client_(client) {} -APIMessageType SubscribeStatesRequest::message_type() const { return APIMessageType::SUBSCRIBE_STATES_REQUEST; } - -bool HomeAssistantStateResponse::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 1: - // string entity_id = 1; - this->entity_id_ = as_string(value, len); - return true; - case 2: - // string state = 2; - this->state_ = as_string(value, len); - return true; - default: - return false; - } -} -APIMessageType HomeAssistantStateResponse::message_type() const { - return APIMessageType::HOME_ASSISTANT_STATE_RESPONSE; -} -const std::string &HomeAssistantStateResponse::get_entity_id() const { return this->entity_id_; } -const std::string &HomeAssistantStateResponse::get_state() const { return this->state_; } -APIMessageType SubscribeHomeAssistantStatesRequest::message_type() const { - return APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST; -} - } // namespace api } // namespace esphome diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index 43b919f2e8..51b9c695e4 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -4,16 +4,10 @@ #include "esphome/core/controller.h" #include "esphome/core/defines.h" #include "util.h" -#include "api_message.h" namespace esphome { namespace api { -class SubscribeStatesRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - class APIConnection; class InitialStateIterator : public ComponentIterator { @@ -47,23 +41,6 @@ class InitialStateIterator : public ComponentIterator { APIConnection *client_; }; -class SubscribeHomeAssistantStatesRequest : public APIMessage { - public: - APIMessageType message_type() const override; -}; - -class HomeAssistantStateResponse : public APIMessage { - public: - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - APIMessageType message_type() const override; - const std::string &get_entity_id() const; - const std::string &get_state() const; - - protected: - std::string entity_id_; - std::string state_; -}; - } // namespace api } // namespace esphome diff --git a/esphome/components/api/user_services.cpp b/esphome/components/api/user_services.cpp index e1dd8932f8..9e2560d3c8 100644 --- a/esphome/components/api/user_services.cpp +++ b/esphome/components/api/user_services.cpp @@ -4,71 +4,35 @@ namespace esphome { namespace api { -template<> bool ExecuteServiceArgument::get_value() { return this->value_bool_; } -template<> int ExecuteServiceArgument::get_value() { return this->value_int_; } -template<> float ExecuteServiceArgument::get_value() { return this->value_float_; } -template<> std::string ExecuteServiceArgument::get_value() { return this->value_string_; } - -APIMessageType ExecuteServiceArgument::message_type() const { return APIMessageType::EXECUTE_SERVICE_REQUEST; } -bool ExecuteServiceArgument::decode_varint(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: // bool bool_ = 1; - this->value_bool_ = value; - return true; - case 2: // int32 int_ = 2; - this->value_int_ = value; - return true; - default: - return false; - } +template<> bool get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.bool_; } +template<> int get_execute_arg_value(const ExecuteServiceArgument &arg) { + if (arg.legacy_int != 0) + return arg.legacy_int; + return arg.int_; } -bool ExecuteServiceArgument::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 3: // float float_ = 3; - this->value_float_ = as_float(value); - return true; - default: - return false; - } +template<> float get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.float_; } +template<> std::string get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.string_; } +template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { + return arg.bool_array; } -bool ExecuteServiceArgument::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 4: // string string_ = 4; - this->value_string_ = as_string(value, len); - return true; - default: - return false; - } +template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { + return arg.int_array; +} +template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { + return arg.float_array; +} +template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { + return arg.string_array; } -bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: // fixed32 key = 1; - this->key_ = value; - return true; - default: - return false; - } -} -bool ExecuteServiceRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { - switch (field_id) { - case 2: { // repeated ExecuteServiceArgument args = 2; - ExecuteServiceArgument arg; - arg.decode(value, len); - this->args_.push_back(arg); - return true; - } - default: - return false; - } -} -APIMessageType ExecuteServiceRequest::message_type() const { return APIMessageType::EXECUTE_SERVICE_REQUEST; } -const std::vector &ExecuteServiceRequest::get_args() const { return this->args_; } -uint32_t ExecuteServiceRequest::get_key() const { return this->key_; } - -ServiceTypeArgument::ServiceTypeArgument(const std::string &name, ServiceArgType type) : name_(name), type_(type) {} -const std::string &ServiceTypeArgument::get_name() const { return this->name_; } -ServiceArgType ServiceTypeArgument::get_type() const { return this->type_; } +template<> ServiceArgType to_service_arg_type() { return SERVICE_ARG_TYPE_BOOL; } +template<> ServiceArgType to_service_arg_type() { return SERVICE_ARG_TYPE_INT; } +template<> ServiceArgType to_service_arg_type() { return SERVICE_ARG_TYPE_FLOAT; } +template<> ServiceArgType to_service_arg_type() { return SERVICE_ARG_TYPE_STRING; } +template<> ServiceArgType to_service_arg_type>() { return SERVICE_ARG_TYPE_BOOL_ARRAY; } +template<> ServiceArgType to_service_arg_type>() { return SERVICE_ARG_TYPE_INT_ARRAY; } +template<> ServiceArgType to_service_arg_type>() { return SERVICE_ARG_TYPE_FLOAT_ARRAY; } +template<> ServiceArgType to_service_arg_type>() { return SERVICE_ARG_TYPE_STRING_ARRAY; } } // namespace api } // namespace esphome diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 92e7a2ca0e..dcc13a528d 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -2,124 +2,71 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" -#include "api_message.h" +#include "api_pb2.h" namespace esphome { namespace api { -enum ServiceArgType { - SERVICE_ARG_TYPE_BOOL = 0, - SERVICE_ARG_TYPE_INT = 1, - SERVICE_ARG_TYPE_FLOAT = 2, - SERVICE_ARG_TYPE_STRING = 3, -}; - -class ServiceTypeArgument { - public: - ServiceTypeArgument(const std::string &name, ServiceArgType type); - const std::string &get_name() const; - ServiceArgType get_type() const; - - protected: - std::string name_; - ServiceArgType type_; -}; - -class ExecuteServiceArgument : public APIMessage { - public: - APIMessageType message_type() const override; - template T get_value(); - - bool decode_varint(uint32_t field_id, uint32_t value) override; - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - - protected: - bool value_bool_{false}; - int value_int_{0}; - float value_float_{0.0f}; - std::string value_string_{}; -}; - -class ExecuteServiceRequest : public APIMessage { - public: - bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override; - bool decode_32bit(uint32_t field_id, uint32_t value) override; - APIMessageType message_type() const override; - - uint32_t get_key() const; - const std::vector &get_args() const; - - protected: - uint32_t key_; - std::vector args_; -}; - class UserServiceDescriptor { public: - virtual void encode_list_service_response(APIBuffer &buffer) = 0; + virtual ListEntitiesServicesResponse encode_list_service_response() = 0; virtual bool execute_service(const ExecuteServiceRequest &req) = 0; }; -template class UserService : public UserServiceDescriptor, public Trigger { +template T get_execute_arg_value(const ExecuteServiceArgument &arg); + +template ServiceArgType to_service_arg_type(); + +template class UserServiceBase : public UserServiceDescriptor { public: - UserService(const std::string &name, const std::array &args); + UserServiceBase(const std::string &name, const std::array &arg_names) + : name_(name), arg_names_(arg_names) { + this->key_ = fnv1_hash(this->name_); + } - void encode_list_service_response(APIBuffer &buffer) override; + ListEntitiesServicesResponse encode_list_service_response() override { + ListEntitiesServicesResponse msg; + msg.name = this->name_; + msg.key = this->key_; + std::array arg_types = {to_service_arg_type()...}; + for (int i = 0; i < sizeof...(Ts); i++) { + ListEntitiesServicesArgument arg; + arg.type = arg_types[i]; + arg.name = this->arg_names_[i]; + msg.args.push_back(arg); + } + return msg; + } - bool execute_service(const ExecuteServiceRequest &req) override; + bool execute_service(const ExecuteServiceRequest &req) override { + if (req.key != this->key_) + return false; + if (req.args.size() != this->arg_names_.size()) + return false; + this->execute_(req.args, typename gens::type()); + return true; + } protected: - template void execute_(std::vector args, seq); + virtual void execute(Ts... x) = 0; + template void execute_(std::vector args, seq) { + this->execute((get_execute_arg_value(args[S]))...); + } std::string name_; uint32_t key_{0}; - std::array args_; + std::array arg_names_; }; -template -template -void UserService::execute_(std::vector args, seq) { - this->trigger((args[S].get_value())...); -} -template void UserService::encode_list_service_response(APIBuffer &buffer) { - // string name = 1; - buffer.encode_string(1, this->name_); - // fixed32 key = 2; - buffer.encode_fixed32(2, this->key_); +template class UserServiceTrigger : public UserServiceBase, public Trigger { + public: + UserServiceTrigger(const std::string &name, const std::array &arg_names) + : UserServiceBase(name, arg_names) {} - // repeated ListServicesArgument args = 3; - for (auto &arg : this->args_) { - auto nested = buffer.begin_nested(3); - // string name = 1; - buffer.encode_string(1, arg.get_name()); - // Type type = 2; - buffer.encode_int32(2, arg.get_type()); - buffer.end_nested(nested); - } -} -template bool UserService::execute_service(const ExecuteServiceRequest &req) { - if (req.get_key() != this->key_) - return false; - - if (req.get_args().size() != this->args_.size()) { - return false; - } - - this->execute_(req.get_args(), typename gens::type()); - return true; -} -template -UserService::UserService(const std::string &name, const std::array &args) - : name_(name), args_(args) { - this->key_ = fnv1_hash(this->name_); -} - -template<> bool ExecuteServiceArgument::get_value(); -template<> int ExecuteServiceArgument::get_value(); -template<> float ExecuteServiceArgument::get_value(); -template<> std::string ExecuteServiceArgument::get_value(); + protected: + void execute(Ts... x) override { this->trigger(x...); } // NOLINT +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/util.cpp b/esphome/components/api/util.cpp index 9b8f91ea7d..f929db5d6a 100644 --- a/esphome/components/api/util.cpp +++ b/esphome/components/api/util.cpp @@ -7,166 +7,6 @@ namespace esphome { namespace api { -APIBuffer::APIBuffer(std::vector *buffer) : buffer_(buffer) {} -size_t APIBuffer::get_length() const { return this->buffer_->size(); } -void APIBuffer::write(uint8_t value) { this->buffer_->push_back(value); } -void APIBuffer::encode_uint32(uint32_t field, uint32_t value, bool force) { - if (value == 0 && !force) - return; - - this->encode_field_raw(field, 0); - this->encode_varint_raw(value); -} -void APIBuffer::encode_int32(uint32_t field, int32_t value, bool force) { - this->encode_uint32(field, static_cast(value), force); -} -void APIBuffer::encode_bool(uint32_t field, bool value, bool force) { - if (!value && !force) - return; - - this->encode_field_raw(field, 0); - this->write(0x01); -} -void APIBuffer::encode_string(uint32_t field, const std::string &value) { - this->encode_string(field, value.data(), value.size()); -} -void APIBuffer::encode_bytes(uint32_t field, const uint8_t *data, size_t len) { - this->encode_string(field, reinterpret_cast(data), len); -} -void APIBuffer::encode_string(uint32_t field, const char *string, size_t len) { - if (len == 0) - return; - - this->encode_field_raw(field, 2); - this->encode_varint_raw(len); - const uint8_t *data = reinterpret_cast(string); - for (size_t i = 0; i < len; i++) { - this->write(data[i]); - } -} -void APIBuffer::encode_fixed32(uint32_t field, uint32_t value, bool force) { - if (value == 0 && !force) - return; - - this->encode_field_raw(field, 5); - this->write((value >> 0) & 0xFF); - this->write((value >> 8) & 0xFF); - this->write((value >> 16) & 0xFF); - this->write((value >> 24) & 0xFF); -} -void APIBuffer::encode_float(uint32_t field, float value, bool force) { - if (value == 0.0f && !force) - return; - - union { - float value_f; - uint32_t value_raw; - } val; - val.value_f = value; - this->encode_fixed32(field, val.value_raw); -} -void APIBuffer::encode_field_raw(uint32_t field, uint32_t type) { - uint32_t val = (field << 3) | (type & 0b111); - this->encode_varint_raw(val); -} -void APIBuffer::encode_varint_raw(uint32_t value) { - if (value <= 0x7F) { - this->write(value); - return; - } - - while (value) { - uint8_t temp = value & 0x7F; - value >>= 7; - if (value) { - this->write(temp | 0x80); - } else { - this->write(temp); - } - } -} -void APIBuffer::encode_sint32(uint32_t field, int32_t value, bool force) { - if (value < 0) - this->encode_uint32(field, ~(uint32_t(value) << 1), force); - else - this->encode_uint32(field, uint32_t(value) << 1, force); -} -void APIBuffer::encode_nameable(Nameable *nameable) { - // string object_id = 1; - this->encode_string(1, nameable->get_object_id()); - // fixed32 key = 2; - this->encode_fixed32(2, nameable->get_object_id_hash()); - // string name = 3; - this->encode_string(3, nameable->get_name()); -} -size_t APIBuffer::begin_nested(uint32_t field) { - this->encode_field_raw(field, 2); - return this->buffer_->size(); -} -void APIBuffer::end_nested(size_t begin_index) { - const uint32_t nested_length = this->buffer_->size() - begin_index; - // add varint - std::vector var; - uint32_t val = nested_length; - if (val <= 0x7F) { - var.push_back(val); - } else { - while (val) { - uint8_t temp = val & 0x7F; - val >>= 7; - if (val) { - var.push_back(temp | 0x80); - } else { - var.push_back(temp); - } - } - } - this->buffer_->insert(this->buffer_->begin() + begin_index, var.begin(), var.end()); -} - -optional proto_decode_varuint32(const uint8_t *buf, size_t len, uint32_t *consumed) { - if (len == 0) - return {}; - - uint32_t result = 0; - uint8_t bitpos = 0; - - for (uint32_t i = 0; i < len; i++) { - uint8_t val = buf[i]; - result |= uint32_t(val & 0x7F) << bitpos; - bitpos += 7; - if ((val & 0x80) == 0) { - if (consumed != nullptr) { - *consumed = i + 1; - } - return result; - } - } - - return {}; -} - -std::string as_string(const uint8_t *value, size_t len) { - return std::string(reinterpret_cast(value), len); -} - -int32_t as_sint32(uint32_t val) { - if (val & 1) - return uint32_t(~(val >> 1)); - else - return uint32_t(val >> 1); -} - -float as_float(uint32_t val) { - static_assert(sizeof(uint32_t) == sizeof(float), "float must be 32bit long"); - union { - uint32_t raw; - float value; - } x; - x.raw = val; - return x.value; -} - ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {} void ComponentIterator::begin() { this->state_ = IteratorState::BEGIN; diff --git a/esphome/components/api/util.h b/esphome/components/api/util.h index 6478dec07a..5a29a48cbe 100644 --- a/esphome/components/api/util.h +++ b/esphome/components/api/util.h @@ -10,40 +10,6 @@ namespace esphome { namespace api { -class APIBuffer { - public: - APIBuffer(std::vector *buffer); - - size_t get_length() const; - void write(uint8_t value); - - void encode_int32(uint32_t field, int32_t value, bool force = false); - void encode_uint32(uint32_t field, uint32_t value, bool force = false); - void encode_sint32(uint32_t field, int32_t value, bool force = false); - void encode_bool(uint32_t field, bool value, bool force = false); - void encode_string(uint32_t field, const std::string &value); - void encode_string(uint32_t field, const char *string, size_t len); - void encode_bytes(uint32_t field, const uint8_t *data, size_t len); - void encode_fixed32(uint32_t field, uint32_t value, bool force = false); - void encode_float(uint32_t field, float value, bool force = false); - void encode_nameable(Nameable *nameable); - - size_t begin_nested(uint32_t field); - void end_nested(size_t begin_index); - - void encode_field_raw(uint32_t field, uint32_t type); - void encode_varint_raw(uint32_t value); - - protected: - std::vector *buffer_; -}; - -optional proto_decode_varuint32(const uint8_t *buf, size_t len, uint32_t *consumed = nullptr); - -std::string as_string(const uint8_t *value, size_t len); -int32_t as_sint32(uint32_t val); -float as_float(uint32_t val); - class APIServer; class UserServiceDescriptor; diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index b21fd4c0ce..e9d97690fb 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -22,19 +22,5 @@ void HomeassistantTime::setup() { HomeassistantTime *global_homeassistant_time = nullptr; -bool GetTimeResponse::decode_32bit(uint32_t field_id, uint32_t value) { - switch (field_id) { - case 1: - // fixed32 epoch_seconds = 1; - if (global_homeassistant_time != nullptr) { - global_homeassistant_time->set_epoch_time(value); - } - return true; - default: - return false; - } -} -api::APIMessageType GetTimeResponse::message_type() const { return api::APIMessageType::GET_TIME_RESPONSE; } - } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/homeassistant/time/homeassistant_time.h b/esphome/components/homeassistant/time/homeassistant_time.h index 43937c6f13..8ab09d1185 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.h +++ b/esphome/components/homeassistant/time/homeassistant_time.h @@ -17,11 +17,5 @@ class HomeassistantTime : public time::RealTimeClock { extern HomeassistantTime *global_homeassistant_time; -class GetTimeResponse : public api::APIMessage { - public: - bool decode_32bit(uint32_t field_id, uint32_t value) override; - api::APIMessageType message_type() const override; -}; - } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index c374568149..5c983be002 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -38,6 +38,7 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ def to_code(config): + cg.add_global(uart_ns.using) var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) diff --git a/esphome/const.py b/esphome/const.py index cfddd3ae7f..0fd67c6161 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -136,6 +136,7 @@ CONF_ENTITY_ID = 'entity_id' CONF_ESP8266_RESTORE_FROM_FLASH = 'esp8266_restore_from_flash' CONF_ESPHOME = 'esphome' CONF_ESPHOME_CORE_VERSION = 'esphome_core_version' +CONF_EVENT = 'event' CONF_EXPIRE_AFTER = 'expire_after' CONF_EXTERNAL_VCC = 'external_vcc' CONF_FALLING_EDGE = 'falling_edge' diff --git a/esphome/core/automation.h b/esphome/core/automation.h index ceed28e5b8..cbe96a749e 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -17,6 +17,15 @@ namespace esphome { #define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name) +#define TEMPLATABLE_STRING_VALUE_(name) \ + protected: \ + TemplatableStringValue name##_{}; \ +\ + public: \ + template void set_##name(V name) { this->name##_ = name; } + +#define TEMPLATABLE_STRING_VALUE(name) TEMPLATABLE_STRING_VALUE_(name) + /** Base class for all automation conditions. * * @tparam Ts The template parameters to pass when executing. diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 01586a606f..5670d136a3 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -243,6 +243,18 @@ template class TemplatableValue { std::function f_; }; +template class TemplatableStringValue : public TemplatableValue { + public: + TemplatableStringValue() : TemplatableValue() {} + + template::value, int> = 0> + TemplatableStringValue(F value) : TemplatableValue(value) {} + + template::value, int> = 0> + TemplatableStringValue(F f) + : TemplatableValue([f](X... x) -> std::string { return to_string(f(x...)); }) {} +}; + void delay_microseconds_accurate(uint32_t usec); template class Deduplicator { diff --git a/esphome/wizard.py b/esphome/wizard.py index c1a1c92ca1..5d290a3592 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -54,9 +54,9 @@ wifi: ssid: "{ssid}" password: "{psk}" - # Enable fallback network (captive portal) + # Enable fallback hotspot (captive portal) in case wifi connection fails ap: - ssid: "{name} Fallback AP" + ssid: "{fallback_name}" password: "{fallback_psk}" captive_portal: @@ -75,6 +75,7 @@ def sanitize_double_quotes(value): def wizard_file(**kwargs): letters = string.ascii_letters + string.digits + kwargs['fallback_name'] = "{} Fallback Hotspot".format(kwargs['name'].replace('_', ' ').title()) kwargs['fallback_psk'] = ''.join(random.choice(letters) for _ in range(12)) config = BASE_CONFIG.format(**kwargs) diff --git a/script/api_protobuf/api_options_pb2.py b/script/api_protobuf/api_options_pb2.py new file mode 100644 index 0000000000..52cbbde678 --- /dev/null +++ b/script/api_protobuf/api_options_pb2.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: api_options.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='api_options.proto', + package='', + syntax='proto2', + serialized_options=None, + serialized_pb=_b('\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto\"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse') + , + dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,]) + +_APISOURCETYPE = _descriptor.EnumDescriptor( + name='APISourceType', + full_name='APISourceType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='SOURCE_BOTH', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SOURCE_SERVER', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SOURCE_CLIENT', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=63, + serialized_end=133, +) +_sym_db.RegisterEnumDescriptor(_APISOURCETYPE) + +APISourceType = enum_type_wrapper.EnumTypeWrapper(_APISOURCETYPE) +SOURCE_BOTH = 0 +SOURCE_SERVER = 1 +SOURCE_CLIENT = 2 + +NEEDS_SETUP_CONNECTION_FIELD_NUMBER = 1038 +needs_setup_connection = _descriptor.FieldDescriptor( + name='needs_setup_connection', full_name='needs_setup_connection', index=0, + number=1038, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +NEEDS_AUTHENTICATION_FIELD_NUMBER = 1039 +needs_authentication = _descriptor.FieldDescriptor( + name='needs_authentication', full_name='needs_authentication', index=1, + number=1039, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +ID_FIELD_NUMBER = 1036 +id = _descriptor.FieldDescriptor( + name='id', full_name='id', index=2, + number=1036, type=13, cpp_type=3, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +SOURCE_FIELD_NUMBER = 1037 +source = _descriptor.FieldDescriptor( + name='source', full_name='source', index=3, + number=1037, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +IFDEF_FIELD_NUMBER = 1038 +ifdef = _descriptor.FieldDescriptor( + name='ifdef', full_name='ifdef', index=4, + number=1038, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +LOG_FIELD_NUMBER = 1039 +log = _descriptor.FieldDescriptor( + name='log', full_name='log', index=5, + number=1039, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) +NO_DELAY_FIELD_NUMBER = 1040 +no_delay = _descriptor.FieldDescriptor( + name='no_delay', full_name='no_delay', index=6, + number=1040, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR) + + +_VOID = _descriptor.Descriptor( + name='void', + full_name='void', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=55, + serialized_end=61, +) + +DESCRIPTOR.message_types_by_name['void'] = _VOID +DESCRIPTOR.enum_types_by_name['APISourceType'] = _APISOURCETYPE +DESCRIPTOR.extensions_by_name['needs_setup_connection'] = needs_setup_connection +DESCRIPTOR.extensions_by_name['needs_authentication'] = needs_authentication +DESCRIPTOR.extensions_by_name['id'] = id +DESCRIPTOR.extensions_by_name['source'] = source +DESCRIPTOR.extensions_by_name['ifdef'] = ifdef +DESCRIPTOR.extensions_by_name['log'] = log +DESCRIPTOR.extensions_by_name['no_delay'] = no_delay +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +void = _reflection.GeneratedProtocolMessageType('void', (_message.Message,), dict( + DESCRIPTOR = _VOID, + __module__ = 'api_options_pb2' + # @@protoc_insertion_point(class_scope:void) + )) +_sym_db.RegisterMessage(void) + +google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(needs_setup_connection) +google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(needs_authentication) +google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(id) +source.enum_type = _APISOURCETYPE +google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(source) +google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(ifdef) +google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(log) +google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(no_delay) + +# @@protoc_insertion_point(module_scope) diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py new file mode 100644 index 0000000000..e6189cd386 --- /dev/null +++ b/script/api_protobuf/api_protobuf.py @@ -0,0 +1,866 @@ +"""Python 3 script to automatically generate C++ classes for ESPHome's native API. + +It's pretty crappy spaghetti code, but it works. +""" + +import re +from pathlib import Path +from textwrap import dedent +from subprocess import call + +# Generate with +# protoc --python_out=script/api_protobuf -I esphome/components/api/ api_options.proto +import api_options_pb2 as pb +import google.protobuf.descriptor_pb2 as descriptor + +cwd = Path(__file__).parent +root = cwd.parent.parent / 'esphome' / 'components' / 'api' +prot = cwd / 'api.protoc' +call(['protoc', '-o', prot, '-I', root, 'api.proto']) +content = prot.read_bytes() + +d = descriptor.FileDescriptorSet.FromString(content) + + +def indent_list(text, padding=u' '): + return [padding + line for line in text.splitlines()] + + +def indent(text, padding=u' '): + return u'\n'.join(indent_list(text, padding)) + + +def camel_to_snake(name): + # https://stackoverflow.com/a/1176023 + s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + + +class TypeInfo(): + def __init__(self, field): + self._field = field + + @property + def default_value(self): + return '' + + @property + def name(self): + return self._field.name + + @property + def arg_name(self): + return self.name + + @property + def field_name(self): + return self.name + + @property + def number(self): + return self._field.number + + @property + def repeated(self): + return self._field.label == 3 + + @property + def cpp_type(self): + raise NotImplementedError + + @property + def reference_type(self): + return f'{self.cpp_type} ' + + @property + def const_reference_type(self): + return f'{self.cpp_type} ' + + @property + def public_content(self) -> str: + return [self.class_member] + + @property + def protected_content(self) -> str: + return [] + + @property + def class_member(self) -> str: + return f'{self.cpp_type} {self.field_name}{{{self.default_value}}}; // NOLINT' + + @property + def decode_varint_content(self) -> str: + content = self.decode_varint + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name} = {content}; + return true; + }}''') + + decode_varint = None + + @property + def decode_length_content(self) -> str: + content = self.decode_length + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name} = {content}; + return true; + }}''') + + decode_length = None + + @property + def decode_32bit_content(self) -> str: + content = self.decode_32bit + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name} = {content}; + return true; + }}''') + + decode_32bit = None + + @property + def decode_64bit_content(self) -> str: + content = self.decode_64bit + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name} = {content}; + return true; + }}''') + + decode_64bit = None + + @property + def encode_content(self): + return f'buffer.{self.encode_func}({self.number}, this->{self.field_name});' + + encode_func = None + + @property + def dump_content(self): + o = f'out.append(" {self.name}: ");\n' + o += self.dump(f'this->{self.field_name}') + '\n' + o += f'out.append("\\n");\n' + return o + + dump = None + + +TYPE_INFO = {} + + +def register_type(name): + def func(value): + TYPE_INFO[name] = value + return value + + return func + + +@register_type(1) +class DoubleType(TypeInfo): + cpp_type = 'double' + default_value = '0.0' + decode_64bit = 'value.as_double()' + encode_func = 'encode_double' + + def dump(self, name): + o = f'sprintf(buffer, "%g", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(2) +class FloatType(TypeInfo): + cpp_type = 'float' + default_value = '0.0f' + decode_32bit = 'value.as_float()' + encode_func = 'encode_float' + + def dump(self, name): + o = f'sprintf(buffer, "%g", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(3) +class Int64Type(TypeInfo): + cpp_type = 'int64_t' + default_value = '0' + decode_varint = 'value.as_int64()' + encode_func = 'encode_int64' + + def dump(self, name): + o = f'sprintf(buffer, "%ll", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(4) +class UInt64Type(TypeInfo): + cpp_type = 'uint64_t' + default_value = '0' + decode_varint = 'value.as_uint64()' + encode_func = 'encode_uint64' + + def dump(self, name): + o = f'sprintf(buffer, "%ull", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(5) +class Int32Type(TypeInfo): + cpp_type = 'int32_t' + default_value = '0' + decode_varint = 'value.as_int32()' + encode_func = 'encode_int32' + + def dump(self, name): + o = f'sprintf(buffer, "%d", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(6) +class Fixed64Type(TypeInfo): + cpp_type = 'uint64_t' + default_value = '0' + decode_64bit = 'value.as_fixed64()' + encode_func = 'encode_fixed64' + + def dump(self, name): + o = f'sprintf(buffer, "%ull", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(7) +class Fixed32Type(TypeInfo): + cpp_type = 'uint32_t' + default_value = '0' + decode_32bit = 'value.as_fixed32()' + encode_func = 'encode_fixed32' + + def dump(self, name): + o = f'sprintf(buffer, "%u", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(8) +class BoolType(TypeInfo): + cpp_type = 'bool' + default_value = 'false' + decode_varint = 'value.as_bool()' + encode_func = 'encode_bool' + + def dump(self, name): + o = f'out.append(YESNO({name}));' + return o + + +@register_type(9) +class StringType(TypeInfo): + cpp_type = 'std::string' + default_value = '' + reference_type = 'std::string &' + const_reference_type = 'const std::string &' + decode_length = 'value.as_string()' + encode_func = 'encode_string' + + def dump(self, name): + o = f'out.append("\'").append({name}).append("\'");' + return o + + +@register_type(11) +class MessageType(TypeInfo): + @property + def cpp_type(self): + return self._field.type_name[1:] + + default_value = '' + + @property + def reference_type(self): + return f'{self.cpp_type} &' + + @property + def const_reference_type(self): + return f'const {self.cpp_type} &' + + @property + def encode_func(self): + return f'encode_message<{self.cpp_type}>' + + @property + def decode_length(self): + return f'value.as_message<{self.cpp_type}>()' + + def dump(self, name): + o = f'{name}.dump_to(out);' + return o + + +@register_type(12) +class BytesType(TypeInfo): + cpp_type = 'std::string' + default_value = '' + reference_type = 'std::string &' + const_reference_type = 'const std::string &' + decode_length = 'value.as_string()' + encode_func = 'encode_string' + + def dump(self, name): + o = f'out.append("\'").append({name}).append("\'");' + return o + + +@register_type(13) +class UInt32Type(TypeInfo): + cpp_type = 'uint32_t' + default_value = '0' + decode_varint = 'value.as_uint32()' + encode_func = 'encode_uint32' + + def dump(self, name): + o = f'sprintf(buffer, "%u", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(14) +class EnumType(TypeInfo): + @property + def cpp_type(self): + return self._field.type_name[1:] + + @property + def decode_varint(self): + return f'value.as_enum<{self.cpp_type}>()' + + default_value = '' + + @property + def encode_func(self): + return f'encode_enum<{self.cpp_type}>' + + def dump(self, name): + o = f'out.append(proto_enum_to_string<{self.cpp_type}>({name}));' + return o + + +@register_type(15) +class SFixed32Type(TypeInfo): + cpp_type = 'int32_t' + default_value = '0' + decode_32bit = 'value.as_sfixed32()' + encode_func = 'encode_sfixed32' + + def dump(self, name): + o = f'sprintf(buffer, "%d", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(16) +class SFixed64Type(TypeInfo): + cpp_type = 'int64_t' + default_value = '0' + decode_64bit = 'value.as_sfixed64()' + encode_func = 'encode_sfixed64' + + def dump(self, name): + o = f'sprintf(buffer, "%ll", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(17) +class SInt32Type(TypeInfo): + cpp_type = 'int32_t' + default_value = '0' + decode_varint = 'value.as_sint32()' + encode_func = 'encode_sint32' + + def dump(self, name): + o = f'sprintf(buffer, "%d", {name});\n' + o += f'out.append(buffer);' + return o + + +@register_type(18) +class SInt64Type(TypeInfo): + cpp_type = 'int64_t' + default_value = '0' + decode_varint = 'value.as_sint64()' + encode_func = 'encode_sin64' + + def dump(self): + o = f'sprintf(buffer, "%ll", {name});\n' + o += f'out.append(buffer);' + return o + + +class RepeatedTypeInfo(TypeInfo): + def __init__(self, field): + super(RepeatedTypeInfo, self).__init__(field) + self._ti = TYPE_INFO[field.type](field) + + @property + def cpp_type(self): + return f'std::vector<{self._ti.cpp_type}>' + + @property + def reference_type(self): + return f'{self.cpp_type} &' + + @property + def const_reference_type(self): + return f'const {self.cpp_type} &' + + @property + def decode_varint_content(self) -> str: + content = self._ti.decode_varint + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name}.push_back({content}); + return true; + }}''') + + @property + def decode_length_content(self) -> str: + content = self._ti.decode_length + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name}.push_back({content}); + return true; + }}''') + + @property + def decode_32bit_content(self) -> str: + content = self._ti.decode_32bit + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name}.push_back({content}); + return true; + }}''') + + @property + def decode_64bit_content(self) -> str: + content = self._ti.decode_64bit + if content is None: + return None + return dedent(f'''\ + case {self.number}: {{ + this->{self.field_name}.push_back({content}); + return true; + }}''') + + @property + def _ti_is_bool(self): + # std::vector is specialized for bool, reference does not work + return isinstance(self._ti, BoolType) + + @property + def encode_content(self): + return f"""\ + for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{ + buffer.{self._ti.encode_func}({self.number}, it, true); + }}""" + + @property + def dump_content(self): + o = f'for (const auto {"" if self._ti_is_bool else "&"}it : this->{self.field_name}) {{\n' + o += f' out.append(" {self.name}: ");\n' + o += indent(self._ti.dump('it')) + '\n' + o += f' out.append("\\n");\n' + o += f'}}\n' + return o + + +def build_enum_type(desc): + out = f"enum {desc.name} : uint32_t {{\n" + for v in desc.value: + out += f' {v.name} = {v.number},\n' + out += '};\n' + + cpp = f"template<>\n" + cpp += f"const char *proto_enum_to_string<{desc.name}>({desc.name} value) {{\n" + cpp += f" switch (value) {{\n" + for v in desc.value: + cpp += f' case {v.name}: return "{v.name}";\n' + cpp += f' default: return "UNKNOWN";\n' + cpp += f' }}\n' + cpp += f'}}\n' + + return out, cpp + + +def build_message_type(desc): + public_content = [] + protected_content = [] + decode_varint = [] + decode_length = [] + decode_32bit = [] + decode_64bit = [] + encode = [] + dump = [] + + for field in desc.field: + if field.label == 3: + ti = RepeatedTypeInfo(field) + else: + ti = TYPE_INFO[field.type](field) + protected_content.extend(ti.protected_content) + public_content.extend(ti.public_content) + encode.append(ti.encode_content) + + if ti.decode_varint_content: + decode_varint.append(ti.decode_varint_content) + if ti.decode_length_content: + decode_length.append(ti.decode_length_content) + if ti.decode_32bit_content: + decode_32bit.append(ti.decode_32bit_content) + if ti.decode_64bit_content: + decode_64bit.append(ti.decode_64bit_content) + if ti.dump_content: + dump.append(ti.dump_content) + + cpp = '' + if decode_varint: + decode_varint.append('default:\n return false;') + o = f'bool {desc.name}::decode_varint(uint32_t field_id, ProtoVarInt value) {{\n' + o += ' switch (field_id) {\n' + o += indent("\n".join(decode_varint), ' ') + '\n' + o += ' }\n' + o += '}\n' + cpp += o + prot = 'bool decode_varint(uint32_t field_id, ProtoVarInt value) override;' + protected_content.insert(0, prot) + if decode_length: + decode_length.append('default:\n return false;') + o = f'bool {desc.name}::decode_length(uint32_t field_id, ProtoLengthDelimited value) {{\n' + o += ' switch (field_id) {\n' + o += indent("\n".join(decode_length), ' ') + '\n' + o += ' }\n' + o += '}\n' + cpp += o + prot = 'bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;' + protected_content.insert(0, prot) + if decode_32bit: + decode_32bit.append('default:\n return false;') + o = f'bool {desc.name}::decode_32bit(uint32_t field_id, Proto32Bit value) {{\n' + o += ' switch (field_id) {\n' + o += indent("\n".join(decode_32bit), ' ') + '\n' + o += ' }\n' + o += '}\n' + cpp += o + prot = 'bool decode_32bit(uint32_t field_id, Proto32Bit value) override;' + protected_content.insert(0, prot) + if decode_64bit: + decode_64bit.append('default:\n return false;') + o = f'bool {desc.name}::decode_64bit(uint32_t field_id, Proto64bit value) {{\n' + o += ' switch (field_id) {\n' + o += indent("\n".join(decode_64bit), ' ') + '\n' + o += ' }\n' + o += '}\n' + cpp += o + prot = 'bool decode_64bit(uint32_t field_id, Proto64bit value) override;' + protected_content.insert(0, prot) + + o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{\n" + o += indent('\n'.join(encode)) + '\n' + o += '}\n' + cpp += o + prot = 'void encode(ProtoWriteBuffer buffer) const override;' + public_content.append(prot) + + o = f"void {desc.name}::dump_to(std::string &out) const {{\n" + if dump: + o += f" char buffer[64];\n" + o += f' out.append("{desc.name} {{\\n");\n' + o += indent('\n'.join(dump)) + '\n' + o += f' out.append("}}");\n' + else: + o += f' out.append("{desc.name} {{}}");\n' + o += '}\n' + cpp += o + prot = 'void dump_to(std::string &out) const override;' + public_content.append(prot) + + out = f"class {desc.name} : public ProtoMessage {{\n" + out += ' public:\n' + out += indent('\n'.join(public_content)) + '\n' + out += ' protected:\n' + out += indent('\n'.join(protected_content)) + '\n' + out += "};\n" + return out, cpp + + +file = d.file[0] +content = '''\ +#pragma once + +#include "proto.h" + +namespace esphome { +namespace api { + +''' + +cpp = '''\ +#include "api_pb2.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace api { + +''' + +for enum in file.enum_type: + s, c = build_enum_type(enum) + content += s + cpp += c + +mt = file.message_type + +for m in mt: + s, c = build_message_type(m) + content += s + cpp += c + +content += '''\ + +} // namespace api +} // namespace esphome +''' +cpp += '''\ + +} // namespace api +} // namespace esphome +''' + +with open(root / 'api_pb2.h', 'w') as f: + f.write(content) + +with open(root / 'api_pb2.cpp', 'w') as f: + f.write(cpp) + +SOURCE_BOTH = 0 +SOURCE_SERVER = 1 +SOURCE_CLIENT = 2 + +RECEIVE_CASES = {} + +class_name = 'APIServerConnectionBase' + +ifdefs = {} + + +def get_opt(desc, opt, default=None): + if not desc.options.HasExtension(opt): + return default + return desc.options.Extensions[opt] + + +def build_service_message_type(mt): + snake = camel_to_snake(mt.name) + id_ = get_opt(mt, pb.id) + if id_ is None: + return None + + source = get_opt(mt, pb.source, 0) + + ifdef = get_opt(mt, pb.ifdef) + log = get_opt(mt, pb.log, True) + nodelay = get_opt(mt, pb.no_delay, False) + hout = '' + cout = '' + + if ifdef is not None: + ifdefs[str(mt.name)] = ifdef + hout += f'#ifdef {ifdef}\n' + cout += f'#ifdef {ifdef}\n' + + if source in (SOURCE_BOTH, SOURCE_SERVER): + # Generate send + func = f'send_{snake}' + hout += f'bool {func}(const {mt.name} &msg);\n' + cout += f'bool {class_name}::{func}(const {mt.name} &msg) {{\n' + if log: + cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' + cout += f' this->set_nodelay({str(nodelay).lower()});\n' + cout += f' return this->send_message_<{mt.name}>(msg, {id_});\n' + cout += f'}}\n' + if source in (SOURCE_BOTH, SOURCE_CLIENT): + # Generate receive + func = f'on_{snake}' + hout += f'virtual void {func}(const {mt.name} &value){{}};\n' + case = '' + if ifdef is not None: + case += f'#ifdef {ifdef}\n' + case += f'{mt.name} msg;\n' + case += f'msg.decode(msg_data, msg_size);\n' + if log: + case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' + case += f'this->{func}(msg);\n' + if ifdef is not None: + case += f'#endif\n' + case += 'break;' + RECEIVE_CASES[id_] = case + + if ifdef is not None: + hout += f'#endif\n' + cout += f'#endif\n' + + return hout, cout + + +hpp = '''\ +#pragma once + +#include "api_pb2.h" +#include "esphome/core/defines.h" + +namespace esphome { +namespace api { + +''' + +cpp = '''\ +#include "api_pb2_service.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace api { + +static const char *TAG = "api.service"; + +''' + +hpp += f'class {class_name} : public ProtoService {{\n' +hpp += ' public:\n' + +for mt in file.message_type: + obj = build_service_message_type(mt) + if obj is None: + continue + hout, cout = obj + hpp += indent(hout) + '\n' + cpp += cout + +cases = list(RECEIVE_CASES.items()) +cases.sort() +hpp += ' protected:\n' +hpp += f' bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n' +out = f'bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n' +out += f' switch(msg_type) {{\n' +for i, case in cases: + c = f'case {i}: {{\n' + c += indent(case) + '\n' + c += f'}}' + out += indent(c, ' ') + '\n' +out += ' default: \n' +out += ' return false;\n' +out += ' }\n' +out += ' return true;\n' +out += '}\n' +cpp += out +hpp += '};\n' + +serv = file.service[0] +class_name = 'APIServerConnection' +hpp += '\n' +hpp += f'class {class_name} : public {class_name}Base {{\n' +hpp += ' public:\n' +hpp_protected = '' +cpp += '\n' + +m = serv.method[0] +for m in serv.method: + func = m.name + inp = m.input_type[1:] + ret = m.output_type[1:] + is_void = ret == 'void' + snake = camel_to_snake(inp) + on_func = f'on_{snake}' + needs_conn = get_opt(m, pb.needs_setup_connection, True) + needs_auth = get_opt(m, pb.needs_authentication, True) + + ifdef = ifdefs.get(inp, None) + + if ifdef is not None: + hpp += f'#ifdef {ifdef}\n' + hpp_protected += f'#ifdef {ifdef}\n' + cpp += f'#ifdef {ifdef}\n' + + hpp_protected += f' void {on_func}(const {inp} &msg) override;\n' + hpp += f' virtual {ret} {func}(const {inp} &msg) = 0;\n' + cpp += f'void {class_name}::{on_func}(const {inp} &msg) {{\n' + body = '' + if needs_conn: + body += 'if (!this->is_connection_setup()) {\n' + body += ' this->on_no_setup_connection();\n' + body += ' return;\n' + body += '}\n' + if needs_auth: + body += 'if (!this->is_authenticated()) {\n' + body += ' this->on_unauthenticated_access();\n' + body += ' return;\n' + body += '}\n' + + if is_void: + body += f'this->{func}(msg);\n' + else: + body += f'{ret} ret = this->{func}(msg);\n' + ret_snake = camel_to_snake(ret) + body += f'if (!this->send_{ret_snake}(ret)) {{\n' + body += f' this->on_fatal_error();\n' + body += '}\n' + cpp += indent(body) + '\n' + '}\n' + + if ifdef is not None: + hpp += f'#endif\n' + hpp_protected += f'#endif\n' + cpp += f'#endif\n' + +hpp += ' protected:\n' +hpp += hpp_protected +hpp += '};\n' + +hpp += '''\ + +} // namespace api +} // namespace esphome +''' +cpp += '''\ + +} // namespace api +} // namespace esphome +''' + +with open(root / 'api_pb2_service.h', 'w') as f: + f.write(hpp) + +with open(root / 'api_pb2_service.cpp', 'w') as f: + f.write(cpp) + +prot.unlink() diff --git a/script/ci-custom.py b/script/ci-custom.py index d5cbf05174..7033c9721d 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -28,6 +28,7 @@ EXECUTABLE_BIT = { s[3].strip(): int(s[0]) for s in lines } files = [s[3].strip() for s in lines] +files = list(filter(os.path.exists, files)) files.sort() file_types = ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt', '.ico', '.svg', diff --git a/tests/custom.h b/tests/custom.h index 2635084406..278e300785 100644 --- a/tests/custom.h +++ b/tests/custom.h @@ -41,3 +41,32 @@ class CustomFloatOutput : public FloatOutput, public Component { protected: void write_state(float state) override { ESP_LOGD("custom_output", "Setting %f", state); } }; + +class CustomNativeAPI : public Component, public CustomAPIDevice { + public: + void setup() override { + register_service(&CustomNativeAPI::on_hello_world, "hello_world"); + register_service(&CustomNativeAPI::on_start_dryer, "start_dryer", {"value"}); + register_service(&CustomNativeAPI::on_many_values, "many_values", {"bool", "int", "float", "str1", "str2"}); + subscribe_homeassistant_state(&CustomNativeAPI::on_light_state, "light.my_light"); + } + + void on_hello_world() { ESP_LOGD("custom_api", "Hello World from native API service!"); } + void on_start_dryer(int value) { ESP_LOGD("custom_api", "Value is %d", value); } + void on_many_values(bool a_bool, int a_int, float a_float, std::string str1, std::string str2) { + ESP_LOGD("custom_api", "Bool: %s", ONOFF(a_bool)); + ESP_LOGD("custom_api", "Int: %d", a_int); + ESP_LOGD("custom_api", "Float: %f", a_float); + ESP_LOGD("custom_api", "String1: %s", str1.c_str()); + ESP_LOGD("custom_api", "String2: %s", str2.c_str()); + ESP_LOGD("custom_api", "Connected: %s", YESNO(is_connected())); + + call_homeassistant_service("light.turn_on", { + {"entity_id", "light.my_light"}, + {"brightness", "127"}, + }); + // no args + call_homeassistant_service("homeassistant.restart"); + } + void on_light_state(std::string state) { ESP_LOGD("custom_api", "Got state %s", state.c_str()); } +}; diff --git a/tests/test3.yaml b/tests/test3.yaml index a37cc47f4a..41bae760d8 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -40,6 +40,24 @@ api: - stepper.set_target: id: my_stepper2 target: !lambda 'return int_;' + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + format: 'Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)' + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() wifi: ssid: 'MySSID'