mirror of
https://github.com/esphome/esphome.git
synced 2024-11-15 03:28:12 +01:00
Implemented review comments.
This commit is contained in:
parent
761b0bf2dc
commit
1dc0f19854
11 changed files with 228 additions and 198 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue