1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-04 11:02:19 +01:00

Introduce base Camera class to support alternative camera implementations (#9285)

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
DT-art1
2025-07-07 05:45:00 +02:00
committed by GitHub
parent bdd52dbaa4
commit e49b89a051
27 changed files with 254 additions and 132 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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: {

View File

@@ -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;

View File

@@ -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");

View File

@@ -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);

View File

@@ -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

View File

@@ -119,15 +119,14 @@ 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) {
for (auto &c : this->clients_) {
if (!c->flags_.remove)
c->set_camera_state(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);
}
});
}
#endif
}

View File

@@ -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)

View File

@@ -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;