Implemented review comments.

This commit is contained in:
Dieter Tschanz 2024-11-07 16:56:50 +01:00
parent 761b0bf2dc
commit 1dc0f19854
11 changed files with 228 additions and 198 deletions

View file

@ -41,9 +41,8 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
#endif #endif
#ifdef USE_CAMERA #ifdef USE_CAMERA
if (camera::Camera::global_camera != nullptr) { if (camera::Camera::instance() != nullptr) {
this->image_reader_ = this->image_reader_ = std::unique_ptr<camera::CameraImageReader>{camera::Camera::instance()->create_image_reader()};
std::unique_ptr<camera::CameraImageReader>{camera::Camera::global_camera->create_image_reader()};
} }
#endif #endif
} }
@ -161,7 +160,7 @@ void APIConnection::loop() {
uint32_t to_send = std::min((size_t) 1024, this->image_reader_->available()); uint32_t to_send = std::min((size_t) 1024, this->image_reader_->available());
auto buffer = this->create_buffer(); auto buffer = this->create_buffer();
// fixed32 key = 1; // 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; // bytes data = 2;
buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send); buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
// bool done = 3; // bool done = 3;
@ -1092,16 +1091,16 @@ bool APIConnection::send_camera_info(camera::Camera *camera) {
return this->send_list_entities_camera_response(msg); return this->send_list_entities_camera_response(msg);
} }
void APIConnection::camera_image(const CameraImageRequest &msg) { void APIConnection::camera_image(const CameraImageRequest &msg) {
if (camera::Camera::global_camera == nullptr) if (camera::Camera::instance() == nullptr)
return; return;
if (msg.single) 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) { 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, 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 #endif

View file

@ -81,8 +81,8 @@ void APIServer::setup() {
this->last_connected_ = millis(); this->last_connected_ = millis();
#ifdef USE_CAMERA #ifdef USE_CAMERA
if (camera::Camera::global_camera != nullptr && !camera::Camera::global_camera->is_internal()) { if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) {
camera::Camera::global_camera->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) { camera::Camera::instance()->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (!c->remove_) if (!c->remove_)
c->send_camera_state(image); c->send_camera_state(image);

View file

@ -1,10 +1,63 @@
from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_TRIGGER_ID
CODEOWNERS = ["@DT-art1"] 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): async def to_code(config):
cg.add_define("USE_CAMERA") 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)

View file

@ -3,10 +3,30 @@
namespace esphome { namespace esphome {
namespace camera { namespace camera {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Camera *Camera::global_camera = nullptr; 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<void(std::shared_ptr<CameraImage>)> &&callback) {
this->new_image_callback_.add(std::move(callback));
}
void Camera::add_stream_start_callback(std::function<void()> &&callback) {
this->stream_start_callback_.add(std::move(callback));
}
void Camera::add_stream_stop_callback(std::function<void()> &&callback) {
this->stream_stop_callback_.add(std::move(callback));
}
Camera *Camera::instance() { return global_camera; }
} // namespace camera } // namespace camera
} // namespace esphome } // namespace esphome

View file

@ -54,11 +54,15 @@ class CameraImageReader {
* 6) API connection consumes data from the image reader and returns the image when finished. * 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. * 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: public:
Camera(); Camera();
// Camera implementation invokes callback to publish a new image. // Camera implementation invokes callback to publish a new image.
virtual void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback) = 0; virtual void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback);
// Camera implementation invokes callback when start_stream is called.
virtual void add_stream_start_callback(std::function<void()> &&callback);
// Camera implementation invokes callback when stop_stream is called.
virtual void add_stream_stop_callback(std::function<void()> &&callback);
/// Returns a new camera image reader that keeps track of the JPEG data in the camera image. /// Returns a new camera image reader that keeps track of the JPEG data in the camera image.
virtual CameraImageReader *create_image_reader() = 0; virtual CameraImageReader *create_image_reader() = 0;
// Connection, camera or web server requests one new JPEG image. // 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 void stop_stream(CameraRequester requester) = 0;
virtual ~Camera() {} virtual ~Camera() {}
/// The singleton instance of the camera implementation. /// The singleton instance of the camera implementation.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static Camera *instance();
protected:
CallbackManager<void(std::shared_ptr<camera::CameraImage>)> new_image_callback_{};
CallbackManager<void()> stream_start_callback_{};
CallbackManager<void()> stream_stop_callback_{};
static Camera *global_camera; 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<CameraImageData> {
public:
explicit CameraImageTrigger(Camera *camera) {
camera->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &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 camera
} // namespace esphome } // namespace esphome

View file

@ -1,5 +1,6 @@
from esphome import automation, pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import camera
from esphome.components.esp32 import add_idf_component from esphome.components.esp32 import add_idf_component
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
@ -13,7 +14,6 @@ from esphome.const import (
CONF_RESOLUTION, CONF_RESOLUTION,
CONF_SCL, CONF_SCL,
CONF_SDA, CONF_SDA,
CONF_TRIGGER_ID,
CONF_VSYNC_PIN, CONF_VSYNC_PIN,
) )
from esphome.core import CORE from esphome.core import CORE
@ -25,19 +25,6 @@ AUTO_LOAD = ["psram", "camera"]
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) 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") ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize")
FRAME_SIZES = { FRAME_SIZES = {
"160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, "160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,
@ -142,100 +129,82 @@ CONF_IDLE_FRAMERATE = "idle_framerate"
# frame buffer # frame buffer
CONF_FRAME_BUFFER_COUNT = "frame_buffer_count" 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) camera_range_param = cv.int_range(min=-2, max=2)
CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( CONFIG_SCHEMA = (
{ cv.ENTITY_BASE_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(ESP32Camera), {
# pin assignment cv.GenerateID(): cv.declare_id(ESP32Camera),
cv.Required(CONF_DATA_PINS): cv.All( # pin assignment
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) 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_VSYNC_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( 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.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
cv.frequency, cv.Range(min=8e6, max=20e6) 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_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.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, cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
# image cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( # image
FRAME_SIZES, upper=True 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_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
cv.Optional(CONF_SATURATION, default=0): camera_range_param, cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
ENUM_SPECIAL_EFFECT, upper=True cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
), ENUM_SPECIAL_EFFECT, upper=True
# exposure ),
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( # exposure
ENUM_GAIN_CONTROL_MODE, upper=True 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_AEC2, default=False): cv.boolean,
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
# gains cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( # gains
ENUM_GAIN_CONTROL_MODE, upper=True 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( cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
ENUM_GAIN_CEILING, upper=True 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), # white balance
# test pattern cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, ENUM_WB_MODE, upper=True
# framerates ),
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( # test pattern
cv.framerate, cv.Range(min=0, min_included=False, max=60) cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
), # framerates
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
cv.framerate, cv.Range(min=0, max=1) cv.framerate, cv.Range(min=0, min_included=False, max=60)
), ),
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( cv.framerate, cv.Range(min=0, max=1)
{ ),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
ESP32CameraStreamStartTrigger }
), )
} .extend(cv.COMPONENT_SCHEMA)
), .extend(camera.CONFIG_SCHEMA)
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)
SETTERS = { SETTERS = {
# pin assignment # pin assignment
@ -273,6 +242,7 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await setup_entity(var, config) await setup_entity(var, config)
await cg.register_component(var, config) await cg.register_component(var, config)
await camera.setup_camera(var, config)
for key, setter in SETTERS.items(): for key, setter in SETTERS.items():
if key in config: 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_buffer_count(config[CONF_FRAME_BUFFER_COUNT]))
cg.add(var.set_frame_size(config[CONF_RESOLUTION])) cg.add(var.set_frame_size(config[CONF_RESOLUTION]))
cg.add_define("USE_CAMERA")
if CORE.using_esp_idf: if CORE.using_esp_idf:
add_idf_component( add_idf_component(
name="esp32-camera", name="esp32-camera",
repo="https://github.com/espressif/esp32-camera.git", repo="https://github.com/espressif/esp32-camera.git",
ref="v2.0.9", 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
)

View file

@ -339,17 +339,11 @@ void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) {
} }
/* ---------------- public API (specific) ---------------- */ /* ---------------- public API (specific) ---------------- */
void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) {
this->new_image_callback_.add(std::move(callback));
}
void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
this->stream_start_callback_.add(std::move(callback));
}
void ESP32Camera::add_stream_stop_callback(std::function<void()> &&callback) {
this->stream_stop_callback_.add(std::move(callback));
}
camera::CameraImageReader *ESP32Camera::create_image_reader() { return new ESP32CameraImageReader; } camera::CameraImageReader *ESP32Camera::create_image_reader() { return new ESP32CameraImageReader; }
void ESP32Camera::start_stream(camera::CameraRequester requester) { void ESP32Camera::start_stream(camera::CameraRequester requester) {
if (this->stream_requesters_ & (1U << requester))
return;
this->stream_start_callback_.call(); this->stream_start_callback_.call();
this->stream_requesters_ |= (1U << requester); this->stream_requesters_ |= (1U << requester);
} }

View file

@ -83,11 +83,6 @@ class ESP32CameraImage : public camera::CameraImage {
uint8_t requesters_; uint8_t requesters_;
}; };
struct CameraImageData {
uint8_t *data;
size_t length;
};
/* ---------------- CameraImageReader class ---------------- */ /* ---------------- CameraImageReader class ---------------- */
class ESP32CameraImageReader : public camera::CameraImageReader { class ESP32CameraImageReader : public camera::CameraImageReader {
public: public:
@ -103,7 +98,7 @@ class ESP32CameraImageReader : public camera::CameraImageReader {
}; };
/* ---------------- ESP32Camera class ---------------- */ /* ---------------- ESP32Camera class ---------------- */
class ESP32Camera : public Component, public camera::Camera { class ESP32Camera : public camera::Camera {
public: public:
ESP32Camera(); ESP32Camera();
@ -157,9 +152,6 @@ class ESP32Camera : public Component, public camera::Camera {
void request_image(camera::CameraRequester requester) override; void request_image(camera::CameraRequester requester) override;
void update_camera_parameters(); void update_camera_parameters();
void add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) override;
void add_stream_start_callback(std::function<void()> &&callback);
void add_stream_stop_callback(std::function<void()> &&callback);
camera::CameraImageReader *create_image_reader() override; camera::CameraImageReader *create_image_reader() override;
protected: protected:
@ -202,43 +194,11 @@ class ESP32Camera : public Component, public camera::Camera {
uint8_t stream_requesters_{0}; uint8_t stream_requesters_{0};
QueueHandle_t framebuffer_get_queue_; QueueHandle_t framebuffer_get_queue_;
QueueHandle_t framebuffer_return_queue_; QueueHandle_t framebuffer_return_queue_;
CallbackManager<void(std::shared_ptr<camera::CameraImage>)> new_image_callback_{};
CallbackManager<void()> stream_start_callback_{};
CallbackManager<void()> stream_stop_callback_{};
uint32_t last_idle_request_{0}; uint32_t last_idle_request_{0};
uint32_t last_update_{0}; uint32_t last_update_{0};
}; };
class ESP32CameraImageTrigger : public Trigger<CameraImageData> {
public:
explicit ESP32CameraImageTrigger(ESP32Camera *parent) {
parent->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &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 esp32_camera
} // namespace esphome } // namespace esphome

View file

@ -40,7 +40,7 @@ CameraWebServer::CameraWebServer() {}
CameraWebServer::~CameraWebServer() {} CameraWebServer::~CameraWebServer() {}
void CameraWebServer::setup() { void CameraWebServer::setup() {
if (!camera::Camera::global_camera) { if (!camera::Camera::instance() || camera::Camera::instance()->is_failed()) {
this->mark_failed(); this->mark_failed();
return; return;
} }
@ -67,7 +67,7 @@ void CameraWebServer::setup() {
httpd_register_uri_handler(this->httpd_, &uri); httpd_register_uri_handler(this->httpd_, &uri);
camera::Camera::global_camera->add_image_callback([this](std::shared_ptr<camera::CameraImage> image) { camera::Camera::instance()->add_image_callback([this](std::shared_ptr<camera::CameraImage> image) {
if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) { if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) {
this->image_ = std::move(image); this->image_ = std::move(image);
xSemaphoreGive(this->semaphore_); xSemaphoreGive(this->semaphore_);
@ -170,7 +170,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
uint32_t last_frame = millis(); uint32_t last_frame = millis();
uint32_t frames = 0; 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_) { while (res == ESP_OK && this->running_) {
auto image = this->wait_for_image_(); 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)); 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); 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 CameraWebServer::snapshot_handler_(struct httpd_req *req) {
esp_err_t res = ESP_OK; 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_(); auto image = this->wait_for_image_();

View file

@ -160,14 +160,14 @@ void ComponentIterator::advance() {
#endif #endif
#ifdef USE_CAMERA #ifdef USE_CAMERA
case IteratorState::CAMERA: case IteratorState::CAMERA:
if (camera::Camera::global_camera == nullptr) { if (camera::Camera::instance() == nullptr) {
advance_platform = true; advance_platform = true;
} else { } else {
if (camera::Camera::global_camera->is_internal() && !this->include_internal_) { if (camera::Camera::instance()->is_internal() && !this->include_internal_) {
advance_platform = success = true; advance_platform = success = true;
break; break;
} else { } else {
advance_platform = success = this->on_camera(camera::Camera::global_camera); advance_platform = success = this->on_camera(camera::Camera::instance());
} }
} }
break; break;

View file

@ -21,6 +21,7 @@
#define USE_API_PLAINTEXT #define USE_API_PLAINTEXT
#define USE_BINARY_SENSOR #define USE_BINARY_SENSOR
#define USE_BUTTON #define USE_BUTTON
#define USE_CAMERA
#define USE_CLIMATE #define USE_CLIMATE
#define USE_COVER #define USE_COVER
#define USE_DATETIME #define USE_DATETIME
@ -102,7 +103,6 @@
#define USE_ESP32_BLE #define USE_ESP32_BLE
#define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_CLIENT
#define USE_ESP32_BLE_SERVER #define USE_ESP32_BLE_SERVER
#define USE_CAMERA
#define USE_IMPROV #define USE_IMPROV
#define USE_MICRO_WAKE_WORD_VAD #define USE_MICRO_WAKE_WORD_VAD
#define USE_MICROPHONE #define USE_MICROPHONE