mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
Introduce base Camera class to support alternative camera implementations (#9285)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
parent
bdd52dbaa4
commit
e49b89a051
@ -87,6 +87,7 @@ esphome/components/bp1658cj/* @Cossid
|
|||||||
esphome/components/bp5758d/* @Cossid
|
esphome/components/bp5758d/* @Cossid
|
||||||
esphome/components/button/* @esphome/core
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/bytebuffer/* @clydebarrow
|
esphome/components/bytebuffer/* @clydebarrow
|
||||||
|
esphome/components/camera/* @DT-art1 @bdraco
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
esphome/components/cap1188/* @mreditor97
|
esphome/components/cap1188/* @mreditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
|
@ -836,7 +836,7 @@ message ListEntitiesCameraResponse {
|
|||||||
option (id) = 43;
|
option (id) = 43;
|
||||||
option (base_class) = "InfoResponseProtoMessage";
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
|
|
||||||
string object_id = 1;
|
string object_id = 1;
|
||||||
fixed32 key = 2;
|
fixed32 key = 2;
|
||||||
@ -851,7 +851,7 @@ message ListEntitiesCameraResponse {
|
|||||||
message CameraImageResponse {
|
message CameraImageResponse {
|
||||||
option (id) = 44;
|
option (id) = 44;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bytes data = 2;
|
bytes data = 2;
|
||||||
@ -860,7 +860,7 @@ message CameraImageResponse {
|
|||||||
message CameraImageRequest {
|
message CameraImageRequest {
|
||||||
option (id) = 45;
|
option (id) = 45;
|
||||||
option (source) = SOURCE_CLIENT;
|
option (source) = SOURCE_CLIENT;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
|
|
||||||
bool single = 1;
|
bool single = 1;
|
||||||
|
@ -38,8 +38,8 @@ static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
|
|||||||
static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
|
static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
|
||||||
|
|
||||||
static const char *const TAG = "api.connection";
|
static const char *const TAG = "api.connection";
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
static const int ESP32_CAMERA_STOP_STREAM = 5000;
|
static const int CAMERA_STOP_STREAM = 5000;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
||||||
@ -58,6 +58,11 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
|
|||||||
#else
|
#else
|
||||||
#error "No frame helper defined"
|
#error "No frame helper defined"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_CAMERA
|
||||||
|
if (camera::Camera::instance() != nullptr) {
|
||||||
|
this->image_reader_ = std::unique_ptr<camera::CameraImageReader>{camera::Camera::instance()->create_image_reader()};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
|
uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
|
||||||
@ -180,10 +185,10 @@ void APIConnection::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
|
if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
|
||||||
uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available());
|
uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_->available());
|
||||||
bool done = this->image_reader_.available() == to_send;
|
bool done = this->image_reader_->available() == to_send;
|
||||||
uint32_t msg_size = 0;
|
uint32_t msg_size = 0;
|
||||||
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
|
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
|
||||||
// partial message size calculated manually since its a special case
|
// partial message size calculated manually since its a special case
|
||||||
@ -193,18 +198,18 @@ void APIConnection::loop() {
|
|||||||
|
|
||||||
auto buffer = this->create_buffer(msg_size);
|
auto buffer = this->create_buffer(msg_size);
|
||||||
// fixed32 key = 1;
|
// fixed32 key = 1;
|
||||||
buffer.encode_fixed32(1, esp32_camera::global_esp32_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;
|
||||||
buffer.encode_bool(3, done);
|
buffer.encode_bool(3, done);
|
||||||
|
|
||||||
bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
|
bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
this->image_reader_.consume_data(to_send);
|
this->image_reader_->consume_data(to_send);
|
||||||
if (done) {
|
if (done) {
|
||||||
this->image_reader_.return_image();
|
this->image_reader_->return_image();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1112,36 +1117,36 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
|
||||||
if (!this->flags_.state_subscription)
|
if (!this->flags_.state_subscription)
|
||||||
return;
|
return;
|
||||||
if (this->image_reader_.available())
|
if (!this->image_reader_)
|
||||||
return;
|
return;
|
||||||
if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
|
if (this->image_reader_->available())
|
||||||
image->was_requested_by(esphome::esp32_camera::IDLE))
|
return;
|
||||||
this->image_reader_.set_image(std::move(image));
|
if (image->was_requested_by(esphome::camera::API_REQUESTER) || image->was_requested_by(esphome::camera::IDLE))
|
||||||
|
this->image_reader_->set_image(std::move(image));
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
|
auto *camera = static_cast<camera::Camera *>(entity);
|
||||||
ListEntitiesCameraResponse msg;
|
ListEntitiesCameraResponse msg;
|
||||||
msg.unique_id = get_default_unique_id("camera", camera);
|
msg.unique_id = get_default_unique_id("camera", camera);
|
||||||
fill_entity_info_base(camera, msg);
|
fill_entity_info_base(camera, msg);
|
||||||
return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||||
if (esp32_camera::global_esp32_camera == nullptr)
|
if (camera::Camera::instance() == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (msg.single)
|
if (msg.single)
|
||||||
esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
|
camera::Camera::instance()->request_image(esphome::camera::API_REQUESTER);
|
||||||
if (msg.stream) {
|
if (msg.stream) {
|
||||||
esp32_camera::global_esp32_camera->start_stream(esphome::esp32_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_camera_stop_stream", CAMERA_STOP_STREAM,
|
||||||
esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
|
[]() { camera::Camera::instance()->stop_stream(esphome::camera::API_REQUESTER); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,8 +60,8 @@ class APIConnection : public APIServerConnection {
|
|||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
|
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
|
void set_camera_state(std::shared_ptr<camera::CameraImage> image);
|
||||||
void camera_image(const CameraImageRequest &msg) override;
|
void camera_image(const CameraImageRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
@ -425,7 +425,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single);
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single);
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
@ -455,8 +455,8 @@ class APIConnection : public APIServerConnection {
|
|||||||
// These contain vectors/pointers internally, so putting them early ensures good alignment
|
// These contain vectors/pointers internally, so putting them early ensures good alignment
|
||||||
InitialStateIterator initial_state_iterator_;
|
InitialStateIterator initial_state_iterator_;
|
||||||
ListEntitiesIterator list_entities_iterator_;
|
ListEntitiesIterator list_entities_iterator_;
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
esp32_camera::CameraImageReader image_reader_;
|
std::unique_ptr<camera::CameraImageReader> image_reader_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Group 3: Strings (12 bytes each on 32-bit, 4-byte aligned)
|
// Group 3: Strings (12 bytes each on 32-bit, 4-byte aligned)
|
||||||
|
@ -2216,7 +2216,7 @@ void ExecuteServiceRequest::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false);
|
ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false);
|
||||||
ProtoSize::add_repeated_message(total_size, 1, this->args);
|
ProtoSize::add_repeated_message(total_size, 1, this->args);
|
||||||
}
|
}
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 5: {
|
case 5: {
|
||||||
|
@ -1273,7 +1273,7 @@ class ExecuteServiceRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
|
class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 43;
|
static constexpr uint16_t MESSAGE_TYPE = 43;
|
||||||
|
@ -1890,7 +1890,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const {
|
|||||||
}
|
}
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
|
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("ListEntitiesCameraResponse {\n");
|
out.append("ListEntitiesCameraResponse {\n");
|
||||||
|
@ -204,7 +204,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
this->on_execute_service_request(msg);
|
this->on_execute_service_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
case 45: {
|
case 45: {
|
||||||
CameraImageRequest msg;
|
CameraImageRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
@ -682,7 +682,7 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
|
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
this->camera_image(msg);
|
this->camera_image(msg);
|
||||||
|
@ -71,7 +71,7 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
|
|
||||||
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
|
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
virtual void on_camera_image_request(const CameraImageRequest &value){};
|
virtual void on_camera_image_request(const CameraImageRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
@ -340,7 +340,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void on_camera_image_request(const CameraImageRequest &msg) override;
|
void on_camera_image_request(const CameraImageRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
|
@ -119,10 +119,9 @@ void APIServer::setup() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
|
if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) {
|
||||||
esp32_camera::global_esp32_camera->add_image_callback(
|
camera::Camera::instance()->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
|
||||||
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->flags_.remove)
|
if (!c->flags_.remove)
|
||||||
c->set_camera_state(image);
|
c->set_camera_state(image);
|
||||||
|
@ -40,8 +40,8 @@ LIST_ENTITIES_HANDLER(lock, lock::Lock, ListEntitiesLockResponse)
|
|||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse)
|
LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
LIST_ENTITIES_HANDLER(camera, esp32_camera::ESP32Camera, ListEntitiesCameraResponse)
|
LIST_ENTITIES_HANDLER(camera, camera::Camera, ListEntitiesCameraResponse)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse)
|
LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse)
|
||||||
|
@ -45,8 +45,8 @@ class ListEntitiesIterator : public ComponentIterator {
|
|||||||
bool on_text_sensor(text_sensor::TextSensor *entity) override;
|
bool on_text_sensor(text_sensor::TextSensor *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_service(UserServiceDescriptor *service) override;
|
bool on_service(UserServiceDescriptor *service) override;
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
bool on_camera(esp32_camera::ESP32Camera *entity) override;
|
bool on_camera(camera::Camera *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool on_climate(climate::Climate *entity) override;
|
bool on_climate(climate::Climate *entity) override;
|
||||||
|
1
esphome/components/camera/__init__.py
Normal file
1
esphome/components/camera/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
CODEOWNERS = ["@DT-art1", "@bdraco"]
|
22
esphome/components/camera/camera.cpp
Normal file
22
esphome/components/camera/camera.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace camera {
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
Camera *Camera::global_camera = nullptr;
|
||||||
|
|
||||||
|
Camera::Camera() {
|
||||||
|
if (global_camera != nullptr) {
|
||||||
|
this->status_set_error("Multiple cameras are configured, but only one is supported.");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_camera = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera *Camera::instance() { return global_camera; }
|
||||||
|
|
||||||
|
} // namespace camera
|
||||||
|
} // namespace esphome
|
80
esphome/components/camera/camera.h
Normal file
80
esphome/components/camera/camera.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace camera {
|
||||||
|
|
||||||
|
/** Different sources for filtering.
|
||||||
|
* IDLE: Camera requests to send an image to the API.
|
||||||
|
* API_REQUESTER: API requests a new image.
|
||||||
|
* WEB_REQUESTER: ESP32 web server request an image. Ignored by API.
|
||||||
|
*/
|
||||||
|
enum CameraRequester : uint8_t { IDLE, API_REQUESTER, WEB_REQUESTER };
|
||||||
|
|
||||||
|
/** Abstract camera image base class.
|
||||||
|
* Encapsulates the JPEG encoded data and it is shared among
|
||||||
|
* all connected clients.
|
||||||
|
*/
|
||||||
|
class CameraImage {
|
||||||
|
public:
|
||||||
|
virtual uint8_t *get_data_buffer() = 0;
|
||||||
|
virtual size_t get_data_length() = 0;
|
||||||
|
virtual bool was_requested_by(CameraRequester requester) const = 0;
|
||||||
|
virtual ~CameraImage() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Abstract image reader base class.
|
||||||
|
* Keeps track of the data offset of the camera image and
|
||||||
|
* how many bytes are remaining to read. When the image
|
||||||
|
* is returned, the shared_ptr is reset and the camera can
|
||||||
|
* reuse the memory of the camera image.
|
||||||
|
*/
|
||||||
|
class CameraImageReader {
|
||||||
|
public:
|
||||||
|
virtual void set_image(std::shared_ptr<CameraImage> image) = 0;
|
||||||
|
virtual size_t available() const = 0;
|
||||||
|
virtual uint8_t *peek_data_buffer() = 0;
|
||||||
|
virtual void consume_data(size_t consumed) = 0;
|
||||||
|
virtual void return_image() = 0;
|
||||||
|
virtual ~CameraImageReader() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Abstract camera base class. Collaborates with API.
|
||||||
|
* 1) API server starts and installs callback (add_image_callback)
|
||||||
|
* which is called by the camera when a new image is available.
|
||||||
|
* 2) New API client connects and creates a new image reader (create_image_reader).
|
||||||
|
* 3) API connection receives protobuf CameraImageRequest and calls request_image.
|
||||||
|
* 3.a) API connection receives protobuf CameraImageRequest and calls start_stream.
|
||||||
|
* 4) Camera implementation provides JPEG data in the CameraImage and calls callback.
|
||||||
|
* 5) API connection sets the image in the image reader.
|
||||||
|
* 6) API connection consumes data from the image reader and returns the image when finished.
|
||||||
|
* 7.a) Camera captures a new image and continues with 4) until start_stream is called.
|
||||||
|
*/
|
||||||
|
class Camera : public EntityBase, public Component {
|
||||||
|
public:
|
||||||
|
Camera();
|
||||||
|
// Camera implementation invokes callback to publish a new image.
|
||||||
|
virtual void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback) = 0;
|
||||||
|
/// 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.
|
||||||
|
virtual void request_image(CameraRequester requester) = 0;
|
||||||
|
// Connection, camera or web server requests a stream of images.
|
||||||
|
virtual void start_stream(CameraRequester requester) = 0;
|
||||||
|
// Connection or web server stops the previously started stream.
|
||||||
|
virtual void stop_stream(CameraRequester requester) = 0;
|
||||||
|
virtual ~Camera() {}
|
||||||
|
/// The singleton instance of the camera implementation.
|
||||||
|
static Camera *instance();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static Camera *global_camera;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camera
|
||||||
|
} // namespace esphome
|
@ -23,7 +23,7 @@ from esphome.core.entity_helpers import setup_entity
|
|||||||
|
|
||||||
DEPENDENCIES = ["esp32"]
|
DEPENDENCIES = ["esp32"]
|
||||||
|
|
||||||
AUTO_LOAD = ["psram"]
|
AUTO_LOAD = ["camera", "psram"]
|
||||||
|
|
||||||
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)
|
||||||
@ -283,6 +283,7 @@ SETTERS = {
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
cg.add_define("USE_CAMERA")
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await setup_entity(var, config, "camera")
|
await setup_entity(var, config, "camera")
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
@ -14,8 +14,6 @@ static const char *const TAG = "esp32_camera";
|
|||||||
|
|
||||||
/* ---------------- public API (derivated) ---------------- */
|
/* ---------------- public API (derivated) ---------------- */
|
||||||
void ESP32Camera::setup() {
|
void ESP32Camera::setup() {
|
||||||
global_esp32_camera = this;
|
|
||||||
|
|
||||||
#ifdef USE_I2C
|
#ifdef USE_I2C
|
||||||
if (this->i2c_bus_ != nullptr) {
|
if (this->i2c_bus_ != nullptr) {
|
||||||
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
|
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
|
||||||
@ -43,7 +41,7 @@ void ESP32Camera::setup() {
|
|||||||
xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task,
|
xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task,
|
||||||
"framebuffer_task", // name
|
"framebuffer_task", // name
|
||||||
1024, // stack size
|
1024, // stack size
|
||||||
nullptr, // task pv params
|
this, // task pv params
|
||||||
1, // priority
|
1, // priority
|
||||||
nullptr, // handle
|
nullptr, // handle
|
||||||
1 // core
|
1 // core
|
||||||
@ -176,7 +174,7 @@ void ESP32Camera::loop() {
|
|||||||
const uint32_t now = App.get_loop_component_start_time();
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) {
|
if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) {
|
||||||
this->last_idle_request_ = now;
|
this->last_idle_request_ = now;
|
||||||
this->request_image(IDLE);
|
this->request_image(camera::IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we should fetch a new image
|
// Check if we should fetch a new image
|
||||||
@ -202,7 +200,7 @@ void ESP32Camera::loop() {
|
|||||||
xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
|
xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->current_image_ = std::make_shared<CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
|
this->current_image_ = std::make_shared<ESP32CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
|
ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
|
||||||
this->new_image_callback_.call(this->current_image_);
|
this->new_image_callback_.call(this->current_image_);
|
||||||
@ -225,8 +223,6 @@ ESP32Camera::ESP32Camera() {
|
|||||||
this->config_.fb_count = 1;
|
this->config_.fb_count = 1;
|
||||||
this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
||||||
this->config_.fb_location = CAMERA_FB_IN_PSRAM;
|
this->config_.fb_location = CAMERA_FB_IN_PSRAM;
|
||||||
|
|
||||||
global_esp32_camera = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- setters ---------------- */
|
/* ---------------- setters ---------------- */
|
||||||
@ -356,7 +352,7 @@ 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<CameraImage>)> &&callback) {
|
void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) {
|
||||||
this->new_image_callback_.add(std::move(callback));
|
this->new_image_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
|
void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
|
||||||
@ -365,15 +361,16 @@ void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
|
|||||||
void ESP32Camera::add_stream_stop_callback(std::function<void()> &&callback) {
|
void ESP32Camera::add_stream_stop_callback(std::function<void()> &&callback) {
|
||||||
this->stream_stop_callback_.add(std::move(callback));
|
this->stream_stop_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
void ESP32Camera::start_stream(CameraRequester requester) {
|
void ESP32Camera::start_stream(camera::CameraRequester requester) {
|
||||||
this->stream_start_callback_.call();
|
this->stream_start_callback_.call();
|
||||||
this->stream_requesters_ |= (1U << requester);
|
this->stream_requesters_ |= (1U << requester);
|
||||||
}
|
}
|
||||||
void ESP32Camera::stop_stream(CameraRequester requester) {
|
void ESP32Camera::stop_stream(camera::CameraRequester requester) {
|
||||||
this->stream_stop_callback_.call();
|
this->stream_stop_callback_.call();
|
||||||
this->stream_requesters_ &= ~(1U << requester);
|
this->stream_requesters_ &= ~(1U << requester);
|
||||||
}
|
}
|
||||||
void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= (1U << requester); }
|
void ESP32Camera::request_image(camera::CameraRequester requester) { this->single_requesters_ |= (1U << requester); }
|
||||||
|
camera::CameraImageReader *ESP32Camera::create_image_reader() { return new ESP32CameraImageReader; }
|
||||||
void ESP32Camera::update_camera_parameters() {
|
void ESP32Camera::update_camera_parameters() {
|
||||||
sensor_t *s = esp_camera_sensor_get();
|
sensor_t *s = esp_camera_sensor_get();
|
||||||
/* update image */
|
/* update image */
|
||||||
@ -402,39 +399,39 @@ void ESP32Camera::update_camera_parameters() {
|
|||||||
bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; }
|
bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; }
|
||||||
bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
|
bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
|
||||||
void ESP32Camera::framebuffer_task(void *pv) {
|
void ESP32Camera::framebuffer_task(void *pv) {
|
||||||
|
ESP32Camera *that = (ESP32Camera *) pv;
|
||||||
while (true) {
|
while (true) {
|
||||||
camera_fb_t *framebuffer = esp_camera_fb_get();
|
camera_fb_t *framebuffer = esp_camera_fb_get();
|
||||||
xQueueSend(global_esp32_camera->framebuffer_get_queue_, &framebuffer, portMAX_DELAY);
|
xQueueSend(that->framebuffer_get_queue_, &framebuffer, portMAX_DELAY);
|
||||||
// return is no-op for config with 1 fb
|
// return is no-op for config with 1 fb
|
||||||
xQueueReceive(global_esp32_camera->framebuffer_return_queue_, &framebuffer, portMAX_DELAY);
|
xQueueReceive(that->framebuffer_return_queue_, &framebuffer, portMAX_DELAY);
|
||||||
esp_camera_fb_return(framebuffer);
|
esp_camera_fb_return(framebuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP32Camera *global_esp32_camera; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
/* ---------------- ESP32CameraImageReader class ----------- */
|
||||||
|
void ESP32CameraImageReader::set_image(std::shared_ptr<camera::CameraImage> image) {
|
||||||
/* ---------------- CameraImageReader class ---------------- */
|
this->image_ = std::static_pointer_cast<ESP32CameraImage>(image);
|
||||||
void CameraImageReader::set_image(std::shared_ptr<CameraImage> image) {
|
|
||||||
this->image_ = std::move(image);
|
|
||||||
this->offset_ = 0;
|
this->offset_ = 0;
|
||||||
}
|
}
|
||||||
size_t CameraImageReader::available() const {
|
size_t ESP32CameraImageReader::available() const {
|
||||||
if (!this->image_)
|
if (!this->image_)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return this->image_->get_data_length() - this->offset_;
|
return this->image_->get_data_length() - this->offset_;
|
||||||
}
|
}
|
||||||
void CameraImageReader::return_image() { this->image_.reset(); }
|
void ESP32CameraImageReader::return_image() { this->image_.reset(); }
|
||||||
void CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; }
|
void ESP32CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; }
|
||||||
uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; }
|
uint8_t *ESP32CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; }
|
||||||
|
|
||||||
/* ---------------- CameraImage class ---------------- */
|
/* ---------------- ESP32CameraImage class ----------- */
|
||||||
CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {}
|
ESP32CameraImage::ESP32CameraImage(camera_fb_t *buffer, uint8_t requesters)
|
||||||
|
: buffer_(buffer), requesters_(requesters) {}
|
||||||
|
|
||||||
camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; }
|
camera_fb_t *ESP32CameraImage::get_raw_buffer() { return this->buffer_; }
|
||||||
uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; }
|
uint8_t *ESP32CameraImage::get_data_buffer() { return this->buffer_->buf; }
|
||||||
size_t CameraImage::get_data_length() { return this->buffer_->len; }
|
size_t ESP32CameraImage::get_data_length() { return this->buffer_->len; }
|
||||||
bool CameraImage::was_requested_by(CameraRequester requester) const {
|
bool ESP32CameraImage::was_requested_by(camera::CameraRequester requester) const {
|
||||||
return (this->requesters_ & (1 << requester)) != 0;
|
return (this->requesters_ & (1 << requester)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <freertos/queue.h>
|
#include <freertos/queue.h>
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/components/camera/camera.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#ifdef USE_I2C
|
#ifdef USE_I2C
|
||||||
@ -19,9 +19,6 @@ namespace esp32_camera {
|
|||||||
|
|
||||||
class ESP32Camera;
|
class ESP32Camera;
|
||||||
|
|
||||||
/* ---------------- enum classes ---------------- */
|
|
||||||
enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER };
|
|
||||||
|
|
||||||
enum ESP32CameraFrameSize {
|
enum ESP32CameraFrameSize {
|
||||||
ESP32_CAMERA_SIZE_160X120, // QQVGA
|
ESP32_CAMERA_SIZE_160X120, // QQVGA
|
||||||
ESP32_CAMERA_SIZE_176X144, // QCIF
|
ESP32_CAMERA_SIZE_176X144, // QCIF
|
||||||
@ -77,13 +74,13 @@ enum ESP32SpecialEffect {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* ---------------- CameraImage class ---------------- */
|
/* ---------------- CameraImage class ---------------- */
|
||||||
class CameraImage {
|
class ESP32CameraImage : public camera::CameraImage {
|
||||||
public:
|
public:
|
||||||
CameraImage(camera_fb_t *buffer, uint8_t requester);
|
ESP32CameraImage(camera_fb_t *buffer, uint8_t requester);
|
||||||
camera_fb_t *get_raw_buffer();
|
camera_fb_t *get_raw_buffer();
|
||||||
uint8_t *get_data_buffer();
|
uint8_t *get_data_buffer() override;
|
||||||
size_t get_data_length();
|
size_t get_data_length() override;
|
||||||
bool was_requested_by(CameraRequester requester) const;
|
bool was_requested_by(camera::CameraRequester requester) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
camera_fb_t *buffer_;
|
camera_fb_t *buffer_;
|
||||||
@ -96,21 +93,21 @@ struct CameraImageData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* ---------------- CameraImageReader class ---------------- */
|
/* ---------------- CameraImageReader class ---------------- */
|
||||||
class CameraImageReader {
|
class ESP32CameraImageReader : public camera::CameraImageReader {
|
||||||
public:
|
public:
|
||||||
void set_image(std::shared_ptr<CameraImage> image);
|
void set_image(std::shared_ptr<camera::CameraImage> image) override;
|
||||||
size_t available() const;
|
size_t available() const override;
|
||||||
uint8_t *peek_data_buffer();
|
uint8_t *peek_data_buffer() override;
|
||||||
void consume_data(size_t consumed);
|
void consume_data(size_t consumed) override;
|
||||||
void return_image();
|
void return_image() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<CameraImage> image_;
|
std::shared_ptr<ESP32CameraImage> image_;
|
||||||
size_t offset_{0};
|
size_t offset_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------------- ESP32Camera class ---------------- */
|
/* ---------------- ESP32Camera class ---------------- */
|
||||||
class ESP32Camera : public EntityBase, public Component {
|
class ESP32Camera : public camera::Camera {
|
||||||
public:
|
public:
|
||||||
ESP32Camera();
|
ESP32Camera();
|
||||||
|
|
||||||
@ -162,14 +159,15 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
/* public API (specific) */
|
/* public API (specific) */
|
||||||
void start_stream(CameraRequester requester);
|
void start_stream(camera::CameraRequester requester) override;
|
||||||
void stop_stream(CameraRequester requester);
|
void stop_stream(camera::CameraRequester requester) override;
|
||||||
void request_image(CameraRequester requester);
|
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<CameraImage>)> &&callback);
|
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_start_callback(std::function<void()> &&callback);
|
||||||
void add_stream_stop_callback(std::function<void()> &&callback);
|
void add_stream_stop_callback(std::function<void()> &&callback);
|
||||||
|
camera::CameraImageReader *create_image_reader() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* internal methods */
|
/* internal methods */
|
||||||
@ -206,12 +204,12 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
uint32_t idle_update_interval_{15000};
|
uint32_t idle_update_interval_{15000};
|
||||||
|
|
||||||
esp_err_t init_error_{ESP_OK};
|
esp_err_t init_error_{ESP_OK};
|
||||||
std::shared_ptr<CameraImage> current_image_;
|
std::shared_ptr<ESP32CameraImage> current_image_;
|
||||||
uint8_t single_requesters_{0};
|
uint8_t single_requesters_{0};
|
||||||
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<CameraImage>)> new_image_callback_{};
|
CallbackManager<void(std::shared_ptr<camera::CameraImage>)> new_image_callback_{};
|
||||||
CallbackManager<void()> stream_start_callback_{};
|
CallbackManager<void()> stream_start_callback_{};
|
||||||
CallbackManager<void()> stream_stop_callback_{};
|
CallbackManager<void()> stream_stop_callback_{};
|
||||||
|
|
||||||
@ -222,13 +220,10 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
#endif // USE_I2C
|
#endif // USE_I2C
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
extern ESP32Camera *global_esp32_camera;
|
|
||||||
|
|
||||||
class ESP32CameraImageTrigger : public Trigger<CameraImageData> {
|
class ESP32CameraImageTrigger : public Trigger<CameraImageData> {
|
||||||
public:
|
public:
|
||||||
explicit ESP32CameraImageTrigger(ESP32Camera *parent) {
|
explicit ESP32CameraImageTrigger(ESP32Camera *parent) {
|
||||||
parent->add_image_callback([this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
parent->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
|
||||||
CameraImageData camera_image_data{};
|
CameraImageData camera_image_data{};
|
||||||
camera_image_data.length = image->get_data_length();
|
camera_image_data.length = image->get_data_length();
|
||||||
camera_image_data.data = image->get_data_buffer();
|
camera_image_data.data = image->get_data_buffer();
|
||||||
|
@ -3,7 +3,8 @@ import esphome.config_validation as cv
|
|||||||
from esphome.const import CONF_ID, CONF_MODE, CONF_PORT
|
from esphome.const import CONF_ID, CONF_MODE, CONF_PORT
|
||||||
|
|
||||||
CODEOWNERS = ["@ayufan"]
|
CODEOWNERS = ["@ayufan"]
|
||||||
DEPENDENCIES = ["esp32_camera", "network"]
|
AUTO_LOAD = ["camera"]
|
||||||
|
DEPENDENCIES = ["network"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server")
|
esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server")
|
||||||
|
@ -40,7 +40,7 @@ CameraWebServer::CameraWebServer() {}
|
|||||||
CameraWebServer::~CameraWebServer() {}
|
CameraWebServer::~CameraWebServer() {}
|
||||||
|
|
||||||
void CameraWebServer::setup() {
|
void CameraWebServer::setup() {
|
||||||
if (!esp32_camera::global_esp32_camera || esp32_camera::global_esp32_camera->is_failed()) {
|
if (!camera::Camera::instance() || camera::Camera::instance()->is_failed()) {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -67,8 +67,8 @@ void CameraWebServer::setup() {
|
|||||||
|
|
||||||
httpd_register_uri_handler(this->httpd_, &uri);
|
httpd_register_uri_handler(this->httpd_, &uri);
|
||||||
|
|
||||||
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
|
camera::Camera::instance()->add_image_callback([this](std::shared_ptr<camera::CameraImage> image) {
|
||||||
if (this->running_ && image->was_requested_by(esp32_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_);
|
||||||
}
|
}
|
||||||
@ -108,8 +108,8 @@ void CameraWebServer::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<esphome::esp32_camera::CameraImage> CameraWebServer::wait_for_image_() {
|
std::shared_ptr<esphome::camera::CameraImage> CameraWebServer::wait_for_image_() {
|
||||||
std::shared_ptr<esphome::esp32_camera::CameraImage> image;
|
std::shared_ptr<esphome::camera::CameraImage> image;
|
||||||
image.swap(this->image_);
|
image.swap(this->image_);
|
||||||
|
|
||||||
if (!image) {
|
if (!image) {
|
||||||
@ -172,7 +172,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;
|
||||||
|
|
||||||
esp32_camera::global_esp32_camera->start_stream(esphome::esp32_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_();
|
||||||
@ -205,7 +205,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_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);
|
||||||
|
|
||||||
@ -215,7 +215,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;
|
||||||
|
|
||||||
esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::WEB_REQUESTER);
|
camera::Camera::instance()->request_image(esphome::camera::WEB_REQUESTER);
|
||||||
|
|
||||||
auto image = this->wait_for_image_();
|
auto image = this->wait_for_image_();
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
#include "esphome/components/esp32_camera/esp32_camera.h"
|
#include "esphome/components/camera/camera.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/preferences.h"
|
#include "esphome/core/preferences.h"
|
||||||
@ -32,7 +32,7 @@ class CameraWebServer : public Component {
|
|||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<esphome::esp32_camera::CameraImage> wait_for_image_();
|
std::shared_ptr<camera::CameraImage> wait_for_image_();
|
||||||
esp_err_t handler_(struct httpd_req *req);
|
esp_err_t handler_(struct httpd_req *req);
|
||||||
esp_err_t streaming_handler_(struct httpd_req *req);
|
esp_err_t streaming_handler_(struct httpd_req *req);
|
||||||
esp_err_t snapshot_handler_(struct httpd_req *req);
|
esp_err_t snapshot_handler_(struct httpd_req *req);
|
||||||
@ -40,7 +40,7 @@ class CameraWebServer : public Component {
|
|||||||
uint16_t port_{0};
|
uint16_t port_{0};
|
||||||
void *httpd_{nullptr};
|
void *httpd_{nullptr};
|
||||||
SemaphoreHandle_t semaphore_;
|
SemaphoreHandle_t semaphore_;
|
||||||
std::shared_ptr<esphome::esp32_camera::CameraImage> image_;
|
std::shared_ptr<camera::CameraImage> image_;
|
||||||
bool running_{false};
|
bool running_{false};
|
||||||
Mode mode_{STREAM};
|
Mode mode_{STREAM};
|
||||||
};
|
};
|
||||||
|
@ -158,16 +158,16 @@ void ComponentIterator::advance() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
case IteratorState::CAMERA:
|
case IteratorState::CAMERA:
|
||||||
if (esp32_camera::global_esp32_camera == nullptr) {
|
if (camera::Camera::instance() == nullptr) {
|
||||||
advance_platform = true;
|
advance_platform = true;
|
||||||
} else {
|
} else {
|
||||||
if (esp32_camera::global_esp32_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(esp32_camera::global_esp32_camera);
|
advance_platform = success = this->on_camera(camera::Camera::instance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -386,8 +386,8 @@ bool ComponentIterator::on_begin() { return true; }
|
|||||||
#ifdef USE_API
|
#ifdef USE_API
|
||||||
bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; }
|
bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; }
|
bool ComponentIterator::on_camera(camera::Camera *camera) { return true; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool ComponentIterator::on_media_player(media_player::MediaPlayer *media_player) { return true; }
|
bool ComponentIterator::on_media_player(media_player::MediaPlayer *media_player) { return true; }
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include "esphome/core/controller.h"
|
#include "esphome/core/controller.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
#include "esphome/components/esp32_camera/esp32_camera.h"
|
#include "esphome/components/camera/camera.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@ -48,8 +48,8 @@ class ComponentIterator {
|
|||||||
#ifdef USE_API
|
#ifdef USE_API
|
||||||
virtual bool on_service(api::UserServiceDescriptor *service);
|
virtual bool on_service(api::UserServiceDescriptor *service);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
virtual bool on_camera(esp32_camera::ESP32Camera *camera);
|
virtual bool on_camera(camera::Camera *camera);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
virtual bool on_climate(climate::Climate *climate) = 0;
|
virtual bool on_climate(climate::Climate *climate) = 0;
|
||||||
@ -125,7 +125,7 @@ class ComponentIterator {
|
|||||||
#ifdef USE_API
|
#ifdef USE_API
|
||||||
SERVICE,
|
SERVICE,
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
CAMERA,
|
CAMERA,
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#define USE_AREAS
|
#define USE_AREAS
|
||||||
#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
|
||||||
@ -144,7 +145,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_ESP32_CAMERA
|
|
||||||
#define USE_I2C
|
#define USE_I2C
|
||||||
#define USE_IMPROV
|
#define USE_IMPROV
|
||||||
#define USE_MICROPHONE
|
#define USE_MICROPHONE
|
||||||
|
18
tests/components/camera/common.yaml
Normal file
18
tests/components/camera/common.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
esphome:
|
||||||
|
includes:
|
||||||
|
- ../../../esphome/components/camera/
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: interface_compile_check
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
using namespace esphome::camera;
|
||||||
|
class MockCamera : public Camera {
|
||||||
|
public:
|
||||||
|
void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback) override {}
|
||||||
|
CameraImageReader *create_image_reader() override { return 0; }
|
||||||
|
void request_image(CameraRequester requester) override {}
|
||||||
|
void start_stream(CameraRequester requester) override {}
|
||||||
|
void stop_stream(CameraRequester requester) override {}
|
||||||
|
};
|
||||||
|
MockCamera* camera = new MockCamera();
|
1
tests/components/camera/test.esp32-ard.yaml
Normal file
1
tests/components/camera/test.esp32-ard.yaml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<<: !include common.yaml
|
1
tests/components/camera/test.esp32-idf.yaml
Normal file
1
tests/components/camera/test.esp32-idf.yaml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<<: !include common.yaml
|
Loading…
x
Reference in New Issue
Block a user