mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'camera-platform' into integration
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
| @@ -111,10 +111,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 | ||||||
		Reference in New Issue
	
	Block a user