mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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/button/* @esphome/core | ||||
| esphome/components/bytebuffer/* @clydebarrow | ||||
| esphome/components/camera/* @DT-art1 @bdraco | ||||
| esphome/components/canbus/* @danielschramm @mvturnho | ||||
| esphome/components/cap1188/* @mreditor97 | ||||
| esphome/components/captive_portal/* @OttoWinter | ||||
|   | ||||
| @@ -836,7 +836,7 @@ message ListEntitiesCameraResponse { | ||||
|   option (id) = 43; | ||||
|   option (base_class) = "InfoResponseProtoMessage"; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_ESP32_CAMERA"; | ||||
|   option (ifdef) = "USE_CAMERA"; | ||||
|  | ||||
|   string object_id = 1; | ||||
|   fixed32 key = 2; | ||||
| @@ -851,7 +851,7 @@ message ListEntitiesCameraResponse { | ||||
| message CameraImageResponse { | ||||
|   option (id) = 44; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_ESP32_CAMERA"; | ||||
|   option (ifdef) = "USE_CAMERA"; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   bytes data = 2; | ||||
| @@ -860,7 +860,7 @@ message CameraImageResponse { | ||||
| message CameraImageRequest { | ||||
|   option (id) = 45; | ||||
|   option (source) = SOURCE_CLIENT; | ||||
|   option (ifdef) = "USE_ESP32_CAMERA"; | ||||
|   option (ifdef) = "USE_CAMERA"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   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 const char *const TAG = "api.connection"; | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| static const int ESP32_CAMERA_STOP_STREAM = 5000; | ||||
| #ifdef USE_CAMERA | ||||
| static const int CAMERA_STOP_STREAM = 5000; | ||||
| #endif | ||||
|  | ||||
| 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 | ||||
| #error "No frame helper defined" | ||||
| #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(); } | ||||
| @@ -180,10 +185,10 @@ void APIConnection::loop() { | ||||
|     } | ||||
|   } | ||||
|  | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   if (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()); | ||||
|     bool done = this->image_reader_.available() == to_send; | ||||
| #ifdef USE_CAMERA | ||||
|   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()); | ||||
|     bool done = this->image_reader_->available() == to_send; | ||||
|     uint32_t msg_size = 0; | ||||
|     ProtoSize::add_fixed_field<4>(msg_size, 1, true); | ||||
|     // partial message size calculated manually since its a special case | ||||
| @@ -193,18 +198,18 @@ void APIConnection::loop() { | ||||
|  | ||||
|     auto buffer = this->create_buffer(msg_size); | ||||
|     // 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; | ||||
|     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; | ||||
|     buffer.encode_bool(3, done); | ||||
|  | ||||
|     bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE); | ||||
|  | ||||
|     if (success) { | ||||
|       this->image_reader_.consume_data(to_send); | ||||
|       this->image_reader_->consume_data(to_send); | ||||
|       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 | ||||
|  | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { | ||||
| #ifdef USE_CAMERA | ||||
| void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) { | ||||
|   if (!this->flags_.state_subscription) | ||||
|     return; | ||||
|   if (this->image_reader_.available()) | ||||
|   if (!this->image_reader_) | ||||
|     return; | ||||
|   if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) || | ||||
|       image->was_requested_by(esphome::esp32_camera::IDLE)) | ||||
|     this->image_reader_.set_image(std::move(image)); | ||||
|   if (this->image_reader_->available()) | ||||
|     return; | ||||
|   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, | ||||
|                                              bool is_single) { | ||||
|   auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity); | ||||
|   auto *camera = static_cast<camera::Camera *>(entity); | ||||
|   ListEntitiesCameraResponse msg; | ||||
|   msg.unique_id = get_default_unique_id("camera", camera); | ||||
|   fill_entity_info_base(camera, msg); | ||||
|   return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| void APIConnection::camera_image(const CameraImageRequest &msg) { | ||||
|   if (esp32_camera::global_esp32_camera == nullptr) | ||||
|   if (camera::Camera::instance() == nullptr) | ||||
|     return; | ||||
|  | ||||
|   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) { | ||||
|     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, []() { | ||||
|       esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER); | ||||
|     }); | ||||
|     App.scheduler.set_timeout(this->parent_, "api_camera_stop_stream", CAMERA_STOP_STREAM, | ||||
|                               []() { camera::Camera::instance()->stop_stream(esphome::camera::API_REQUESTER); }); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -60,8 +60,8 @@ class APIConnection : public APIServerConnection { | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|   bool send_text_sensor_state(text_sensor::TextSensor *text_sensor); | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image); | ||||
| #ifdef USE_CAMERA | ||||
|   void set_camera_state(std::shared_ptr<camera::CameraImage> image); | ||||
|   void camera_image(const CameraImageRequest &msg) override; | ||||
| #endif | ||||
| #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, | ||||
|                                        bool is_single); | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|   static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, | ||||
|                                        bool is_single); | ||||
| #endif | ||||
| @@ -455,8 +455,8 @@ class APIConnection : public APIServerConnection { | ||||
|   // These contain vectors/pointers internally, so putting them early ensures good alignment | ||||
|   InitialStateIterator initial_state_iterator_; | ||||
|   ListEntitiesIterator list_entities_iterator_; | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   esp32_camera::CameraImageReader image_reader_; | ||||
| #ifdef USE_CAMERA | ||||
|   std::unique_ptr<camera::CameraImageReader> image_reader_; | ||||
| #endif | ||||
|  | ||||
|   // 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_repeated_message(total_size, 1, this->args); | ||||
| } | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
| bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 5: { | ||||
|   | ||||
| @@ -1273,7 +1273,7 @@ class ExecuteServiceRequest : public ProtoMessage { | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||
| }; | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
| class ListEntitiesCameraResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   static constexpr uint16_t MESSAGE_TYPE = 43; | ||||
|   | ||||
| @@ -1890,7 +1890,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const { | ||||
|   } | ||||
|   out.append("}"); | ||||
| } | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
| void ListEntitiesCameraResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   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); | ||||
|       break; | ||||
|     } | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|     case 45: { | ||||
|       CameraImageRequest msg; | ||||
|       msg.decode(msg_data, msg_size); | ||||
| @@ -682,7 +682,7 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest & | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
| void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { | ||||
|   if (this->check_authenticated_()) { | ||||
|     this->camera_image(msg); | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class APIServerConnectionBase : public ProtoService { | ||||
|  | ||||
|   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){}; | ||||
| #endif | ||||
|  | ||||
| @@ -223,7 +223,7 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_BUTTON | ||||
|   virtual void button_command(const ButtonCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|   virtual void camera_image(const CameraImageRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
| @@ -340,7 +340,7 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_BUTTON | ||||
|   void on_button_command_request(const ButtonCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|   void on_camera_image_request(const CameraImageRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
|   | ||||
| @@ -111,10 +111,9 @@ void APIServer::setup() { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { | ||||
|     esp32_camera::global_esp32_camera->add_image_callback( | ||||
|         [this](const std::shared_ptr<esp32_camera::CameraImage> &image) { | ||||
| #ifdef USE_CAMERA | ||||
|   if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) { | ||||
|     camera::Camera::instance()->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) { | ||||
|       for (auto &c : this->clients_) { | ||||
|         if (!c->flags_.remove) | ||||
|           c->set_camera_state(image); | ||||
|   | ||||
| @@ -40,8 +40,8 @@ LIST_ENTITIES_HANDLER(lock, lock::Lock, ListEntitiesLockResponse) | ||||
| #ifdef USE_VALVE | ||||
| LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse) | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| LIST_ENTITIES_HANDLER(camera, esp32_camera::ESP32Camera, ListEntitiesCameraResponse) | ||||
| #ifdef USE_CAMERA | ||||
| LIST_ENTITIES_HANDLER(camera, camera::Camera, ListEntitiesCameraResponse) | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
| LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse) | ||||
|   | ||||
| @@ -45,8 +45,8 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
|   bool on_text_sensor(text_sensor::TextSensor *entity) override; | ||||
| #endif | ||||
|   bool on_service(UserServiceDescriptor *service) override; | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   bool on_camera(esp32_camera::ESP32Camera *entity) override; | ||||
| #ifdef USE_CAMERA | ||||
|   bool on_camera(camera::Camera *entity) override; | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
|   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"] | ||||
|  | ||||
| AUTO_LOAD = ["psram"] | ||||
| AUTO_LOAD = ["camera", "psram"] | ||||
|  | ||||
| esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") | ||||
| ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) | ||||
| @@ -283,6 +283,7 @@ SETTERS = { | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     cg.add_define("USE_CAMERA") | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await setup_entity(var, config, "camera") | ||||
|     await cg.register_component(var, config) | ||||
|   | ||||
| @@ -14,8 +14,6 @@ static const char *const TAG = "esp32_camera"; | ||||
|  | ||||
| /* ---------------- public API (derivated) ---------------- */ | ||||
| void ESP32Camera::setup() { | ||||
|   global_esp32_camera = this; | ||||
|  | ||||
| #ifdef USE_I2C | ||||
|   if (this->i2c_bus_ != nullptr) { | ||||
|     this->config_.sccb_i2c_port = this->i2c_bus_->get_port(); | ||||
| @@ -43,7 +41,7 @@ void ESP32Camera::setup() { | ||||
|   xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task, | ||||
|                           "framebuffer_task",  // name | ||||
|                           1024,                // stack size | ||||
|                           nullptr,             // task pv params | ||||
|                           this,                // task pv params | ||||
|                           1,                   // priority | ||||
|                           nullptr,             // handle | ||||
|                           1                    // core | ||||
| @@ -176,7 +174,7 @@ void ESP32Camera::loop() { | ||||
|   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_) { | ||||
|     this->last_idle_request_ = now; | ||||
|     this->request_image(IDLE); | ||||
|     this->request_image(camera::IDLE); | ||||
|   } | ||||
|  | ||||
|   // Check if we should fetch a new image | ||||
| @@ -202,7 +200,7 @@ void ESP32Camera::loop() { | ||||
|     xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY); | ||||
|     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); | ||||
|   this->new_image_callback_.call(this->current_image_); | ||||
| @@ -225,8 +223,6 @@ ESP32Camera::ESP32Camera() { | ||||
|   this->config_.fb_count = 1; | ||||
|   this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY; | ||||
|   this->config_.fb_location = CAMERA_FB_IN_PSRAM; | ||||
|  | ||||
|   global_esp32_camera = this; | ||||
| } | ||||
|  | ||||
| /* ---------------- setters ---------------- */ | ||||
| @@ -356,7 +352,7 @@ void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) { | ||||
| } | ||||
|  | ||||
| /* ---------------- 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)); | ||||
| } | ||||
| 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) { | ||||
|   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_requesters_ |= (1U << requester); | ||||
| } | ||||
| void ESP32Camera::stop_stream(CameraRequester requester) { | ||||
| void ESP32Camera::stop_stream(camera::CameraRequester requester) { | ||||
|   this->stream_stop_callback_.call(); | ||||
|   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() { | ||||
|   sensor_t *s = esp_camera_sensor_get(); | ||||
|   /* 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::can_return_image_() const { return this->current_image_.use_count() == 1; } | ||||
| void ESP32Camera::framebuffer_task(void *pv) { | ||||
|   ESP32Camera *that = (ESP32Camera *) pv; | ||||
|   while (true) { | ||||
|     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 | ||||
|     xQueueReceive(global_esp32_camera->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); | ||||
|     xQueueReceive(that->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); | ||||
|     esp_camera_fb_return(framebuffer); | ||||
|   } | ||||
| } | ||||
|  | ||||
| ESP32Camera *global_esp32_camera;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|  | ||||
| /* ---------------- CameraImageReader class ---------------- */ | ||||
| void CameraImageReader::set_image(std::shared_ptr<CameraImage> image) { | ||||
|   this->image_ = std::move(image); | ||||
| /* ---------------- ESP32CameraImageReader class ----------- */ | ||||
| void ESP32CameraImageReader::set_image(std::shared_ptr<camera::CameraImage> image) { | ||||
|   this->image_ = std::static_pointer_cast<ESP32CameraImage>(image); | ||||
|   this->offset_ = 0; | ||||
| } | ||||
| size_t CameraImageReader::available() const { | ||||
| size_t ESP32CameraImageReader::available() const { | ||||
|   if (!this->image_) | ||||
|     return 0; | ||||
|  | ||||
|   return this->image_->get_data_length() - this->offset_; | ||||
| } | ||||
| void CameraImageReader::return_image() { this->image_.reset(); } | ||||
| void CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; } | ||||
| uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; } | ||||
| void ESP32CameraImageReader::return_image() { this->image_.reset(); } | ||||
| void ESP32CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; } | ||||
| uint8_t *ESP32CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; } | ||||
|  | ||||
| /* ---------------- CameraImage class ---------------- */ | ||||
| CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} | ||||
| /* ---------------- ESP32CameraImage class ----------- */ | ||||
| ESP32CameraImage::ESP32CameraImage(camera_fb_t *buffer, uint8_t requesters) | ||||
|     : buffer_(buffer), requesters_(requesters) {} | ||||
|  | ||||
| camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; } | ||||
| uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; } | ||||
| size_t CameraImage::get_data_length() { return this->buffer_->len; } | ||||
| bool CameraImage::was_requested_by(CameraRequester requester) const { | ||||
| camera_fb_t *ESP32CameraImage::get_raw_buffer() { return this->buffer_; } | ||||
| uint8_t *ESP32CameraImage::get_data_buffer() { return this->buffer_->buf; } | ||||
| size_t ESP32CameraImage::get_data_length() { return this->buffer_->len; } | ||||
| bool ESP32CameraImage::was_requested_by(camera::CameraRequester requester) const { | ||||
|   return (this->requesters_ & (1 << requester)) != 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include <freertos/queue.h> | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/components/camera/camera.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #ifdef USE_I2C | ||||
| @@ -19,9 +19,6 @@ namespace esp32_camera { | ||||
|  | ||||
| class ESP32Camera; | ||||
|  | ||||
| /* ---------------- enum classes ---------------- */ | ||||
| enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER }; | ||||
|  | ||||
| enum ESP32CameraFrameSize { | ||||
|   ESP32_CAMERA_SIZE_160X120,    // QQVGA | ||||
|   ESP32_CAMERA_SIZE_176X144,    // QCIF | ||||
| @@ -77,13 +74,13 @@ enum ESP32SpecialEffect { | ||||
| }; | ||||
|  | ||||
| /* ---------------- CameraImage class ---------------- */ | ||||
| class CameraImage { | ||||
| class ESP32CameraImage : public camera::CameraImage { | ||||
|  public: | ||||
|   CameraImage(camera_fb_t *buffer, uint8_t requester); | ||||
|   ESP32CameraImage(camera_fb_t *buffer, uint8_t requester); | ||||
|   camera_fb_t *get_raw_buffer(); | ||||
|   uint8_t *get_data_buffer(); | ||||
|   size_t get_data_length(); | ||||
|   bool was_requested_by(CameraRequester requester) const; | ||||
|   uint8_t *get_data_buffer() override; | ||||
|   size_t get_data_length() override; | ||||
|   bool was_requested_by(camera::CameraRequester requester) const override; | ||||
|  | ||||
|  protected: | ||||
|   camera_fb_t *buffer_; | ||||
| @@ -96,21 +93,21 @@ struct CameraImageData { | ||||
| }; | ||||
|  | ||||
| /* ---------------- CameraImageReader class ---------------- */ | ||||
| class CameraImageReader { | ||||
| class ESP32CameraImageReader : public camera::CameraImageReader { | ||||
|  public: | ||||
|   void set_image(std::shared_ptr<CameraImage> image); | ||||
|   size_t available() const; | ||||
|   uint8_t *peek_data_buffer(); | ||||
|   void consume_data(size_t consumed); | ||||
|   void return_image(); | ||||
|   void set_image(std::shared_ptr<camera::CameraImage> image) override; | ||||
|   size_t available() const override; | ||||
|   uint8_t *peek_data_buffer() override; | ||||
|   void consume_data(size_t consumed) override; | ||||
|   void return_image() override; | ||||
|  | ||||
|  protected: | ||||
|   std::shared_ptr<CameraImage> image_; | ||||
|   std::shared_ptr<ESP32CameraImage> image_; | ||||
|   size_t offset_{0}; | ||||
| }; | ||||
|  | ||||
| /* ---------------- ESP32Camera class ---------------- */ | ||||
| class ESP32Camera : public EntityBase, public Component { | ||||
| class ESP32Camera : public camera::Camera { | ||||
|  public: | ||||
|   ESP32Camera(); | ||||
|  | ||||
| @@ -162,14 +159,15 @@ class ESP32Camera : public EntityBase, public Component { | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override; | ||||
|   /* public API (specific) */ | ||||
|   void start_stream(CameraRequester requester); | ||||
|   void stop_stream(CameraRequester requester); | ||||
|   void request_image(CameraRequester requester); | ||||
|   void start_stream(camera::CameraRequester requester) override; | ||||
|   void stop_stream(camera::CameraRequester requester) override; | ||||
|   void request_image(camera::CameraRequester requester) override; | ||||
|   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_stop_callback(std::function<void()> &&callback); | ||||
|   camera::CameraImageReader *create_image_reader() override; | ||||
|  | ||||
|  protected: | ||||
|   /* internal methods */ | ||||
| @@ -206,12 +204,12 @@ class ESP32Camera : public EntityBase, public Component { | ||||
|   uint32_t idle_update_interval_{15000}; | ||||
|  | ||||
|   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 stream_requesters_{0}; | ||||
|   QueueHandle_t framebuffer_get_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_stop_callback_{}; | ||||
|  | ||||
| @@ -222,13 +220,10 @@ class ESP32Camera : public EntityBase, public Component { | ||||
| #endif  // USE_I2C | ||||
| }; | ||||
|  | ||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||
| extern ESP32Camera *global_esp32_camera; | ||||
|  | ||||
| class ESP32CameraImageTrigger : public Trigger<CameraImageData> { | ||||
|  public: | ||||
|   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{}; | ||||
|       camera_image_data.length = image->get_data_length(); | ||||
|       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 | ||||
|  | ||||
| CODEOWNERS = ["@ayufan"] | ||||
| DEPENDENCIES = ["esp32_camera", "network"] | ||||
| AUTO_LOAD = ["camera"] | ||||
| DEPENDENCIES = ["network"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server") | ||||
|   | ||||
| @@ -40,7 +40,7 @@ CameraWebServer::CameraWebServer() {} | ||||
| CameraWebServer::~CameraWebServer() {} | ||||
|  | ||||
| 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(); | ||||
|     return; | ||||
|   } | ||||
| @@ -67,8 +67,8 @@ void CameraWebServer::setup() { | ||||
|  | ||||
|   httpd_register_uri_handler(this->httpd_, &uri); | ||||
|  | ||||
|   esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) { | ||||
|     if (this->running_ && image->was_requested_by(esp32_camera::WEB_REQUESTER)) { | ||||
|   camera::Camera::instance()->add_image_callback([this](std::shared_ptr<camera::CameraImage> image) { | ||||
|     if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) { | ||||
|       this->image_ = std::move(image); | ||||
|       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::esp32_camera::CameraImage> image; | ||||
| std::shared_ptr<esphome::camera::CameraImage> CameraWebServer::wait_for_image_() { | ||||
|   std::shared_ptr<esphome::camera::CameraImage> image; | ||||
|   image.swap(this->image_); | ||||
|  | ||||
|   if (!image) { | ||||
| @@ -172,7 +172,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { | ||||
|   uint32_t last_frame = millis(); | ||||
|   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_) { | ||||
|     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)); | ||||
|   } | ||||
|  | ||||
|   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); | ||||
|  | ||||
| @@ -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 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_(); | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| #include <freertos/FreeRTOS.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/helpers.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| @@ -32,7 +32,7 @@ class CameraWebServer : public Component { | ||||
|   void loop() override; | ||||
|  | ||||
|  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 streaming_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}; | ||||
|   void *httpd_{nullptr}; | ||||
|   SemaphoreHandle_t semaphore_; | ||||
|   std::shared_ptr<esphome::esp32_camera::CameraImage> image_; | ||||
|   std::shared_ptr<camera::CameraImage> image_; | ||||
|   bool running_{false}; | ||||
|   Mode mode_{STREAM}; | ||||
| }; | ||||
|   | ||||
| @@ -158,16 +158,16 @@ void ComponentIterator::advance() { | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|     case IteratorState::CAMERA: | ||||
|       if (esp32_camera::global_esp32_camera == nullptr) { | ||||
|       if (camera::Camera::instance() == nullptr) { | ||||
|         advance_platform = true; | ||||
|       } 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; | ||||
|           break; | ||||
|         } else { | ||||
|           advance_platform = success = this->on_camera(esp32_camera::global_esp32_camera); | ||||
|           advance_platform = success = this->on_camera(camera::Camera::instance()); | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
| @@ -386,8 +386,8 @@ bool ComponentIterator::on_begin() { return true; } | ||||
| #ifdef USE_API | ||||
| bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; } | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; } | ||||
| #ifdef USE_CAMERA | ||||
| bool ComponentIterator::on_camera(camera::Camera *camera) { return true; } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| bool ComponentIterator::on_media_player(media_player::MediaPlayer *media_player) { return true; } | ||||
|   | ||||
| @@ -4,8 +4,8 @@ | ||||
| #include "esphome/core/controller.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #include "esphome/components/esp32_camera/esp32_camera.h" | ||||
| #ifdef USE_CAMERA | ||||
| #include "esphome/components/camera/camera.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -48,8 +48,8 @@ class ComponentIterator { | ||||
| #ifdef USE_API | ||||
|   virtual bool on_service(api::UserServiceDescriptor *service); | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
|   virtual bool on_camera(esp32_camera::ESP32Camera *camera); | ||||
| #ifdef USE_CAMERA | ||||
|   virtual bool on_camera(camera::Camera *camera); | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
|   virtual bool on_climate(climate::Climate *climate) = 0; | ||||
| @@ -125,7 +125,7 @@ class ComponentIterator { | ||||
| #ifdef USE_API | ||||
|     SERVICE, | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| #ifdef USE_CAMERA | ||||
|     CAMERA, | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| #define USE_AREAS | ||||
| #define USE_BINARY_SENSOR | ||||
| #define USE_BUTTON | ||||
| #define USE_CAMERA | ||||
| #define USE_CLIMATE | ||||
| #define USE_COVER | ||||
| #define USE_DATETIME | ||||
| @@ -144,7 +145,6 @@ | ||||
| #define USE_ESP32_BLE | ||||
| #define USE_ESP32_BLE_CLIENT | ||||
| #define USE_ESP32_BLE_SERVER | ||||
| #define USE_ESP32_CAMERA | ||||
| #define USE_I2C | ||||
| #define USE_IMPROV | ||||
| #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