From 1dc0f198541c88110cea826242d081b39843683b Mon Sep 17 00:00:00 2001 From: Dieter Tschanz Date: Thu, 7 Nov 2024 16:56:50 +0100 Subject: [PATCH] Implemented review comments. --- esphome/components/api/api_connection.cpp | 15 +- esphome/components/api/api_server.cpp | 4 +- esphome/components/camera/__init__.py | 55 ++++- esphome/components/camera/camera.cpp | 24 ++- esphome/components/camera/camera.h | 56 ++++- esphome/components/esp32_camera/__init__.py | 200 +++++++----------- .../components/esp32_camera/esp32_camera.cpp | 12 +- .../components/esp32_camera/esp32_camera.h | 42 +--- .../camera_web_server.cpp | 10 +- esphome/core/component_iterator.cpp | 6 +- esphome/core/defines.h | 2 +- 11 files changed, 228 insertions(+), 198 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index a8c7b0f817..314c536a86 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -41,9 +41,8 @@ APIConnection::APIConnection(std::unique_ptr sock, APIServer *pa #endif #ifdef USE_CAMERA - if (camera::Camera::global_camera != nullptr) { - this->image_reader_ = - std::unique_ptr{camera::Camera::global_camera->create_image_reader()}; + if (camera::Camera::instance() != nullptr) { + this->image_reader_ = std::unique_ptr{camera::Camera::instance()->create_image_reader()}; } #endif } @@ -161,7 +160,7 @@ void APIConnection::loop() { uint32_t to_send = std::min((size_t) 1024, this->image_reader_->available()); auto buffer = this->create_buffer(); // fixed32 key = 1; - buffer.encode_fixed32(1, camera::Camera::global_camera->get_object_id_hash()); + buffer.encode_fixed32(1, camera::Camera::instance()->get_object_id_hash()); // bytes data = 2; buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send); // bool done = 3; @@ -1092,16 +1091,16 @@ bool APIConnection::send_camera_info(camera::Camera *camera) { return this->send_list_entities_camera_response(msg); } void APIConnection::camera_image(const CameraImageRequest &msg) { - if (camera::Camera::global_camera == nullptr) + if (camera::Camera::instance() == nullptr) return; if (msg.single) - camera::Camera::global_camera->request_image(esphome::camera::API_REQUESTER); + camera::Camera::instance()->request_image(esphome::camera::API_REQUESTER); if (msg.stream) { - camera::Camera::global_camera->start_stream(esphome::camera::API_REQUESTER); + camera::Camera::instance()->start_stream(esphome::camera::API_REQUESTER); App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, - []() { camera::Camera::global_camera->stop_stream(esphome::camera::API_REQUESTER); }); + []() { camera::Camera::instance()->stop_stream(esphome::camera::API_REQUESTER); }); } } #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 309473bbaa..c47ff61c3c 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -81,8 +81,8 @@ void APIServer::setup() { this->last_connected_ = millis(); #ifdef USE_CAMERA - if (camera::Camera::global_camera != nullptr && !camera::Camera::global_camera->is_internal()) { - camera::Camera::global_camera->add_image_callback([this](const std::shared_ptr &image) { + if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) { + camera::Camera::instance()->add_image_callback([this](const std::shared_ptr &image) { for (auto &c : this->clients_) { if (!c->remove_) c->send_camera_state(image); diff --git a/esphome/components/camera/__init__.py b/esphome/components/camera/__init__.py index 268594e49e..34442c9b91 100644 --- a/esphome/components/camera/__init__.py +++ b/esphome/components/camera/__init__.py @@ -1,10 +1,63 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_TRIGGER_ID CODEOWNERS = ["@DT-art1"] -CONFIG_SCHEMA = cv.Schema({}) +CONF_ON_STREAM_START = "on_stream_start" +CONF_ON_STREAM_STOP = "on_stream_stop" +CONF_ON_IMAGE = "on_image" + +camera_ns = cg.esphome_ns.namespace("camera") +Camera = camera_ns.class_("Camera", cg.Component, cg.EntityBase) +CameraImageData = camera_ns.struct("CameraImageData") +CameraImageTrigger = camera_ns.class_( + "CameraImageTrigger", automation.Trigger.template() +) +CameraStreamStartTrigger = camera_ns.class_( + "CameraStreamStartTrigger", + automation.Trigger.template(), +) +CameraStreamStopTrigger = camera_ns.class_( + "CameraStreamStopTrigger", + automation.Trigger.template(), +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CameraStreamStartTrigger), + } + ), + cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CameraStreamStopTrigger), + } + ), + cv.Optional(CONF_ON_IMAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CameraImageTrigger), + } + ), + } +) async def to_code(config): cg.add_define("USE_CAMERA") + + +async def setup_camera(var, config): + for conf in config.get(CONF_ON_STREAM_START, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_STREAM_STOP, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_IMAGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(CameraImageData, "image")], conf) diff --git a/esphome/components/camera/camera.cpp b/esphome/components/camera/camera.cpp index 1e2f4397f7..8b4f6f76d1 100644 --- a/esphome/components/camera/camera.cpp +++ b/esphome/components/camera/camera.cpp @@ -3,10 +3,30 @@ namespace esphome { namespace camera { -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) Camera *Camera::global_camera = nullptr; -Camera::Camera() { global_camera = this; } +Camera::Camera() { + if (global_camera != nullptr) { + mark_failed(); + return; + } + + global_camera = this; +} + +void Camera::add_image_callback(std::function)> &&callback) { + this->new_image_callback_.add(std::move(callback)); +} + +void Camera::add_stream_start_callback(std::function &&callback) { + this->stream_start_callback_.add(std::move(callback)); +} + +void Camera::add_stream_stop_callback(std::function &&callback) { + this->stream_stop_callback_.add(std::move(callback)); +} + +Camera *Camera::instance() { return global_camera; } } // namespace camera } // namespace esphome diff --git a/esphome/components/camera/camera.h b/esphome/components/camera/camera.h index 08ae637eb4..5a4217d8cc 100644 --- a/esphome/components/camera/camera.h +++ b/esphome/components/camera/camera.h @@ -54,11 +54,15 @@ class CameraImageReader { * 6) API connection consumes data from the image reader and returns the image when finished. * 7.a) Camera caputes new image and continues with 4) until start_stream is called. */ -class Camera : public EntityBase { +class Camera : public Component, public EntityBase { public: Camera(); // Camera implementation invokes callback to publish a new image. - virtual void add_image_callback(std::function)> &&callback) = 0; + virtual void add_image_callback(std::function)> &&callback); + // Camera implementation invokes callback when start_stream is called. + virtual void add_stream_start_callback(std::function &&callback); + // Camera implementation invokes callback when stop_stream is called. + virtual void add_stream_stop_callback(std::function &&callback); /// Returns a new camera image reader that keeps track of the JPEG data in the camera image. virtual CameraImageReader *create_image_reader() = 0; // Connection, camera or web server requests one new JPEG image. @@ -69,9 +73,55 @@ class Camera : public EntityBase { virtual void stop_stream(CameraRequester requester) = 0; virtual ~Camera() {} /// The singleton instance of the camera implementation. - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + static Camera *instance(); + + protected: + CallbackManager)> new_image_callback_{}; + CallbackManager stream_start_callback_{}; + CallbackManager stream_stop_callback_{}; static Camera *global_camera; }; +/** Struct that encapsulates the image data for the CameraImageTrigger */ +struct CameraImageData { + uint8_t *data; + size_t length; +}; + +/** Class that installs a camera callback which is triggered + * every time a new image is captured. + */ +class CameraImageTrigger : public Trigger { + public: + explicit CameraImageTrigger(Camera *camera) { + camera->add_image_callback([this](const std::shared_ptr &image) { + CameraImageData camera_image_data{}; + camera_image_data.length = image->get_data_length(); + camera_image_data.data = image->get_data_buffer(); + this->trigger(camera_image_data); + }); + } +}; + +/** Class that installs a camera callback which is triggered + * every time a new image stream is requested. + */ +class CameraStreamStartTrigger : public Trigger<> { + public: + explicit CameraStreamStartTrigger(Camera *camera) { + camera->add_stream_start_callback([this]() { this->trigger(); }); + } +}; + +/** Class that installs a camera callback which is triggered + * every time a image stream is stopped. + */ +class CameraStreamStopTrigger : public Trigger<> { + public: + explicit CameraStreamStopTrigger(Camera *camera) { + camera->add_stream_stop_callback([this]() { this->trigger(); }); + } +}; + } // namespace camera } // namespace esphome diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 2f96d3c051..4275e6ffd9 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -1,5 +1,6 @@ -from esphome import automation, pins +from esphome import pins import esphome.codegen as cg +from esphome.components import camera from esphome.components.esp32 import add_idf_component import esphome.config_validation as cv from esphome.const import ( @@ -13,7 +14,6 @@ from esphome.const import ( CONF_RESOLUTION, CONF_SCL, CONF_SDA, - CONF_TRIGGER_ID, CONF_VSYNC_PIN, ) from esphome.core import CORE @@ -25,19 +25,6 @@ AUTO_LOAD = ["psram", "camera"] esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) -ESP32CameraImageData = esp32_camera_ns.struct("CameraImageData") -# Triggers -ESP32CameraImageTrigger = esp32_camera_ns.class_( - "ESP32CameraImageTrigger", automation.Trigger.template() -) -ESP32CameraStreamStartTrigger = esp32_camera_ns.class_( - "ESP32CameraStreamStartTrigger", - automation.Trigger.template(), -) -ESP32CameraStreamStopTrigger = esp32_camera_ns.class_( - "ESP32CameraStreamStopTrigger", - automation.Trigger.template(), -) ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize") FRAME_SIZES = { "160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, @@ -142,100 +129,82 @@ CONF_IDLE_FRAMERATE = "idle_framerate" # frame buffer CONF_FRAME_BUFFER_COUNT = "frame_buffer_count" -# stream trigger -CONF_ON_STREAM_START = "on_stream_start" -CONF_ON_STREAM_STOP = "on_stream_stop" -CONF_ON_IMAGE = "on_image" - camera_range_param = cv.int_range(min=-2, max=2) -CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(ESP32Camera), - # pin assignment - cv.Required(CONF_DATA_PINS): cv.All( - [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) - ), - cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( - { - cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, - cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( - cv.frequency, cv.Range(min=8e6, max=20e6) - ), - } - ), - cv.Required(CONF_I2C_PINS): cv.Schema( - { - cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, - cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, - } - ), - cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, - # image - cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( - FRAME_SIZES, upper=True - ), - cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), - cv.Optional(CONF_CONTRAST, default=0): camera_range_param, - cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, - cv.Optional(CONF_SATURATION, default=0): camera_range_param, - cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, - cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, - cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( - ENUM_SPECIAL_EFFECT, upper=True - ), - # exposure - cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( - ENUM_GAIN_CONTROL_MODE, upper=True - ), - cv.Optional(CONF_AEC2, default=False): cv.boolean, - cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, - cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), - # gains - cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( - ENUM_GAIN_CONTROL_MODE, upper=True - ), - cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), - cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( - ENUM_GAIN_CEILING, upper=True - ), - # white balance - cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True), - # test pattern - cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, - # framerates - cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( - cv.framerate, cv.Range(min=0, min_included=False, max=60) - ), - cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( - cv.framerate, cv.Range(min=0, max=1) - ), - cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), - cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32CameraStreamStartTrigger - ), - } - ), - cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32CameraStreamStopTrigger - ), - } - ), - cv.Optional(CONF_ON_IMAGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger), - } - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.ENTITY_BASE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ESP32Camera), + # pin assignment + cv.Required(CONF_DATA_PINS): cv.All( + [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) + ), + cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( + { + cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, + cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( + cv.frequency, cv.Range(min=8e6, max=20e6) + ), + } + ), + cv.Required(CONF_I2C_PINS): cv.Schema( + { + cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, + cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, + } + ), + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, + # image + cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( + FRAME_SIZES, upper=True + ), + cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), + cv.Optional(CONF_CONTRAST, default=0): camera_range_param, + cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, + cv.Optional(CONF_SATURATION, default=0): camera_range_param, + cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, + cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, + cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( + ENUM_SPECIAL_EFFECT, upper=True + ), + # exposure + cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), + cv.Optional(CONF_AEC2, default=False): cv.boolean, + cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, + cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), + # gains + cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), + cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), + cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( + ENUM_GAIN_CEILING, upper=True + ), + # white balance + cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum( + ENUM_WB_MODE, upper=True + ), + # test pattern + cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, + # framerates + cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( + cv.framerate, cv.Range(min=0, min_included=False, max=60) + ), + cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( + cv.framerate, cv.Range(min=0, max=1) + ), + cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(camera.CONFIG_SCHEMA) +) SETTERS = { # pin assignment @@ -273,6 +242,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await setup_entity(var, config) await cg.register_component(var, config) + await camera.setup_camera(var, config) for key, setter in SETTERS.items(): if key in config: @@ -290,25 +260,9 @@ async def to_code(config): cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT])) cg.add(var.set_frame_size(config[CONF_RESOLUTION])) - cg.add_define("USE_CAMERA") - if CORE.using_esp_idf: add_idf_component( name="esp32-camera", repo="https://github.com/espressif/esp32-camera.git", ref="v2.0.9", ) - - for conf in config.get(CONF_ON_STREAM_START, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - - for conf in config.get(CONF_ON_STREAM_STOP, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - - for conf in config.get(CONF_ON_IMAGE, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation( - trigger, [(ESP32CameraImageData, "image")], conf - ) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 5e44ea95eb..010dc3fbd8 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -339,17 +339,11 @@ void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) { } /* ---------------- public API (specific) ---------------- */ -void ESP32Camera::add_image_callback(std::function)> &&callback) { - this->new_image_callback_.add(std::move(callback)); -} -void ESP32Camera::add_stream_start_callback(std::function &&callback) { - this->stream_start_callback_.add(std::move(callback)); -} -void ESP32Camera::add_stream_stop_callback(std::function &&callback) { - this->stream_stop_callback_.add(std::move(callback)); -} camera::CameraImageReader *ESP32Camera::create_image_reader() { return new ESP32CameraImageReader; } void ESP32Camera::start_stream(camera::CameraRequester requester) { + if (this->stream_requesters_ & (1U << requester)) + return; + this->stream_start_callback_.call(); this->stream_requesters_ |= (1U << requester); } diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 9c0f947572..1a02b759e0 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -83,11 +83,6 @@ class ESP32CameraImage : public camera::CameraImage { uint8_t requesters_; }; -struct CameraImageData { - uint8_t *data; - size_t length; -}; - /* ---------------- CameraImageReader class ---------------- */ class ESP32CameraImageReader : public camera::CameraImageReader { public: @@ -103,7 +98,7 @@ class ESP32CameraImageReader : public camera::CameraImageReader { }; /* ---------------- ESP32Camera class ---------------- */ -class ESP32Camera : public Component, public camera::Camera { +class ESP32Camera : public camera::Camera { public: ESP32Camera(); @@ -157,9 +152,6 @@ class ESP32Camera : public Component, public camera::Camera { void request_image(camera::CameraRequester requester) override; void update_camera_parameters(); - void add_image_callback(std::function)> &&callback) override; - void add_stream_start_callback(std::function &&callback); - void add_stream_stop_callback(std::function &&callback); camera::CameraImageReader *create_image_reader() override; protected: @@ -202,43 +194,11 @@ class ESP32Camera : public Component, public camera::Camera { uint8_t stream_requesters_{0}; QueueHandle_t framebuffer_get_queue_; QueueHandle_t framebuffer_return_queue_; - CallbackManager)> new_image_callback_{}; - CallbackManager stream_start_callback_{}; - CallbackManager stream_stop_callback_{}; uint32_t last_idle_request_{0}; uint32_t last_update_{0}; }; -class ESP32CameraImageTrigger : public Trigger { - public: - explicit ESP32CameraImageTrigger(ESP32Camera *parent) { - parent->add_image_callback([this](const std::shared_ptr &image) { - CameraImageData camera_image_data{}; - camera_image_data.length = image->get_data_length(); - camera_image_data.data = image->get_data_buffer(); - this->trigger(camera_image_data); - }); - } -}; - -class ESP32CameraStreamStartTrigger : public Trigger<> { - public: - explicit ESP32CameraStreamStartTrigger(ESP32Camera *parent) { - parent->add_stream_start_callback([this]() { this->trigger(); }); - } - - protected: -}; -class ESP32CameraStreamStopTrigger : public Trigger<> { - public: - explicit ESP32CameraStreamStopTrigger(ESP32Camera *parent) { - parent->add_stream_stop_callback([this]() { this->trigger(); }); - } - - protected: -}; - } // namespace esp32_camera } // namespace esphome diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index 547a5c5361..0f8c92b733 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -40,7 +40,7 @@ CameraWebServer::CameraWebServer() {} CameraWebServer::~CameraWebServer() {} void CameraWebServer::setup() { - if (!camera::Camera::global_camera) { + if (!camera::Camera::instance() || camera::Camera::instance()->is_failed()) { this->mark_failed(); return; } @@ -67,7 +67,7 @@ void CameraWebServer::setup() { httpd_register_uri_handler(this->httpd_, &uri); - camera::Camera::global_camera->add_image_callback([this](std::shared_ptr image) { + camera::Camera::instance()->add_image_callback([this](std::shared_ptr image) { if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) { this->image_ = std::move(image); xSemaphoreGive(this->semaphore_); @@ -170,7 +170,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { uint32_t last_frame = millis(); uint32_t frames = 0; - camera::Camera::global_camera->start_stream(esphome::camera::WEB_REQUESTER); + camera::Camera::instance()->start_stream(esphome::camera::WEB_REQUESTER); while (res == ESP_OK && this->running_) { auto image = this->wait_for_image_(); @@ -203,7 +203,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR)); } - camera::Camera::global_camera->stop_stream(esphome::camera::WEB_REQUESTER); + camera::Camera::instance()->stop_stream(esphome::camera::WEB_REQUESTER); ESP_LOGI(TAG, "STREAM: closed. Frames: %" PRIu32, frames); @@ -213,7 +213,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { esp_err_t CameraWebServer::snapshot_handler_(struct httpd_req *req) { esp_err_t res = ESP_OK; - camera::Camera::global_camera->request_image(esphome::camera::WEB_REQUESTER); + camera::Camera::instance()->request_image(esphome::camera::WEB_REQUESTER); auto image = this->wait_for_image_(); diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index c68daa04e5..6dcf0fd9e1 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -160,14 +160,14 @@ void ComponentIterator::advance() { #endif #ifdef USE_CAMERA case IteratorState::CAMERA: - if (camera::Camera::global_camera == nullptr) { + if (camera::Camera::instance() == nullptr) { advance_platform = true; } else { - if (camera::Camera::global_camera->is_internal() && !this->include_internal_) { + if (camera::Camera::instance()->is_internal() && !this->include_internal_) { advance_platform = success = true; break; } else { - advance_platform = success = this->on_camera(camera::Camera::global_camera); + advance_platform = success = this->on_camera(camera::Camera::instance()); } } break; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1655a54c00..17bc84ad10 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -21,6 +21,7 @@ #define USE_API_PLAINTEXT #define USE_BINARY_SENSOR #define USE_BUTTON +#define USE_CAMERA #define USE_CLIMATE #define USE_COVER #define USE_DATETIME @@ -102,7 +103,6 @@ #define USE_ESP32_BLE #define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_SERVER -#define USE_CAMERA #define USE_IMPROV #define USE_MICRO_WAKE_WORD_VAD #define USE_MICROPHONE