#pragma once #include #include #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "api_pb2.h" #ifdef USE_API_SERVICES namespace esphome::api { class UserServiceDescriptor { public: virtual ListEntitiesServicesResponse encode_list_service_response() = 0; virtual bool execute_service(const ExecuteServiceRequest &req) = 0; bool is_internal() { return false; } }; template T get_execute_arg_value(const ExecuteServiceArgument &arg); template enums::ServiceArgType to_service_arg_type(); // Base class for YAML-defined services (most common case) // Stores only pointers to string literals in flash - no heap allocation template class UserServiceBase : public UserServiceDescriptor { public: UserServiceBase(const char *name, const std::array &arg_names) : name_(name), arg_names_(arg_names) { this->key_ = fnv1_hash(name); } ListEntitiesServicesResponse encode_list_service_response() override { ListEntitiesServicesResponse msg; msg.set_name(StringRef(this->name_)); msg.key = this->key_; std::array arg_types = {to_service_arg_type()...}; msg.args.init(sizeof...(Ts)); for (size_t i = 0; i < sizeof...(Ts); i++) { auto &arg = msg.args.emplace_back(); arg.type = arg_types[i]; arg.set_name(StringRef(this->arg_names_[i])); } return msg; } bool execute_service(const ExecuteServiceRequest &req) override { if (req.key != this->key_) return false; if (req.args.size() != sizeof...(Ts)) return false; this->execute_(req.args, typename gens::type()); return true; } protected: virtual void execute(Ts... x) = 0; template void execute_(const ArgsContainer &args, seq type) { this->execute((get_execute_arg_value(args[S]))...); } // Pointers to string literals in flash - no heap allocation const char *name_; std::array arg_names_; uint32_t key_{0}; }; // Separate class for custom_api_device services (rare case) // Stores copies of runtime-generated names template class UserServiceDynamic : public UserServiceDescriptor { public: UserServiceDynamic(std::string name, const std::array &arg_names) : name_(std::move(name)), arg_names_(arg_names) { this->key_ = fnv1_hash(this->name_.c_str()); } ListEntitiesServicesResponse encode_list_service_response() override { ListEntitiesServicesResponse msg; msg.set_name(StringRef(this->name_)); msg.key = this->key_; std::array arg_types = {to_service_arg_type()...}; msg.args.init(sizeof...(Ts)); for (size_t i = 0; i < sizeof...(Ts); i++) { auto &arg = msg.args.emplace_back(); arg.type = arg_types[i]; arg.set_name(StringRef(this->arg_names_[i])); } return msg; } bool execute_service(const ExecuteServiceRequest &req) override { if (req.key != this->key_) return false; if (req.args.size() != sizeof...(Ts)) return false; this->execute_(req.args, typename gens::type()); return true; } protected: virtual void execute(Ts... x) = 0; template void execute_(const ArgsContainer &args, seq type) { this->execute((get_execute_arg_value(args[S]))...); } // Heap-allocated strings for runtime-generated names std::string name_; std::array arg_names_; uint32_t key_{0}; }; template class UserServiceTrigger : public UserServiceBase, public Trigger { public: // Constructor for static names (YAML-defined services - used by code generator) UserServiceTrigger(const char *name, const std::array &arg_names) : UserServiceBase(name, arg_names) {} protected: void execute(Ts... x) override { this->trigger(x...); } // NOLINT }; } // namespace esphome::api #endif // USE_API_SERVICES