diff --git a/CODEOWNERS b/CODEOWNERS index 3e82a372ce..d5ce5e6920 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -88,6 +88,7 @@ esphome/components/honeywellabp/* @RubyBailey esphome/components/hrxl_maxsonar_wr/* @netmikey esphome/components/hydreon_rgxx/* @functionpointer esphome/components/i2c/* @esphome/core +esphome/components/i2s_audio/* @jesserockz esphome/components/improv_serial/* @esphome/core esphome/components/ina260/* @MrEditor97 esphome/components/inkbird_ibsth1_mini/* @fkirill @@ -119,6 +120,7 @@ esphome/components/mcp47a1/* @jesserockz esphome/components/mcp9808/* @k7hpn esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core +esphome/components/media_player/* @jesserockz esphome/components/midea/* @dudanov esphome/components/midea_ir/* @dudanov esphome/components/mitsubishi/* @RubyBailey diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index bd39893825..3e9a62f3d8 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -42,6 +42,7 @@ service APIConnection { rpc select_command (SelectCommandRequest) returns (void) {} rpc button_command (ButtonCommandRequest) returns (void) {} rpc lock_command (LockCommandRequest) returns (void) {} + rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} } @@ -991,7 +992,7 @@ message ListEntitiesLockResponse { bool supports_open = 9; bool requires_code = 10; - # Not yet implemented: + // Not yet implemented: string code_format = 11; } message LockStateResponse { @@ -1010,7 +1011,7 @@ message LockCommandRequest { fixed32 key = 1; LockCommand command = 2; - # Not yet implemented: + // Not yet implemented: bool has_code = 3; string code = 4; } @@ -1040,3 +1041,60 @@ message ButtonCommandRequest { fixed32 key = 1; } +// ==================== MEDIA PLAYER ==================== +enum MediaPlayerState { + MEDIA_PLAYER_STATE_NONE = 0; + MEDIA_PLAYER_STATE_IDLE = 1; + MEDIA_PLAYER_STATE_PLAYING = 2; + MEDIA_PLAYER_STATE_PAUSED = 3; +} +enum MediaPlayerCommand { + MEDIA_PLAYER_COMMAND_PLAY = 0; + MEDIA_PLAYER_COMMAND_PAUSE = 1; + MEDIA_PLAYER_COMMAND_STOP = 2; + MEDIA_PLAYER_COMMAND_MUTE = 3; + MEDIA_PLAYER_COMMAND_UNMUTE = 4; +} +message ListEntitiesMediaPlayerResponse { + option (id) = 63; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_MEDIA_PLAYER"; + + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; + bool disabled_by_default = 6; + EntityCategory entity_category = 7; + + bool supports_pause = 8; +} +message MediaPlayerStateResponse { + option (id) = 64; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_MEDIA_PLAYER"; + option (no_delay) = true; + fixed32 key = 1; + MediaPlayerState state = 2; + float volume = 3; + bool muted = 4; +} +message MediaPlayerCommandRequest { + option (id) = 65; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_MEDIA_PLAYER"; + option (no_delay) = true; + + fixed32 key = 1; + + bool has_command = 2; + MediaPlayerCommand command = 3; + + bool has_volume = 4; + float volume = 5; + + bool has_media_url = 6; + string media_url = 7; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 4f399d95d0..9028034c90 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -733,6 +733,52 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { } #endif +#ifdef USE_MEDIA_PLAYER +bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { + if (!this->state_subscription_) + return false; + + MediaPlayerStateResponse resp{}; + resp.key = media_player->get_object_id_hash(); + resp.state = static_cast(media_player->state); + resp.volume = media_player->volume; + resp.muted = media_player->is_muted(); + return this->send_media_player_state_response(resp); +} +bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { + ListEntitiesMediaPlayerResponse msg; + msg.key = media_player->get_object_id_hash(); + msg.object_id = media_player->get_object_id(); + msg.name = media_player->get_name(); + msg.unique_id = get_default_unique_id("media_player", media_player); + msg.icon = media_player->get_icon(); + msg.disabled_by_default = media_player->is_disabled_by_default(); + msg.entity_category = static_cast(media_player->get_entity_category()); + + auto traits = media_player->get_traits(); + msg.supports_pause = traits.get_supports_pause(); + + return this->send_list_entities_media_player_response(msg); +} +void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { + media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key); + if (media_player == nullptr) + return; + + auto call = media_player->make_call(); + if (msg.has_command) { + call.set_command(static_cast(msg.command)); + } + if (msg.has_volume) { + call.set_volume(msg.volume); + } + if (msg.has_media_url) { + call.set_media_url(msg.media_url); + } + call.perform(); +} +#endif + #ifdef USE_ESP32_CAMERA void APIConnection::send_camera_state(std::shared_ptr image) { if (!this->state_subscription_) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 10f0becc54..0787d2f7eb 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -82,6 +82,11 @@ class APIConnection : public APIServerConnection { bool send_lock_state(lock::Lock *a_lock, lock::LockState state); bool send_lock_info(lock::Lock *a_lock); void lock_command(const LockCommandRequest &msg) override; +#endif +#ifdef USE_MEDIA_PLAYER + bool send_media_player_state(media_player::MediaPlayer *media_player); + bool send_media_player_info(media_player::MediaPlayer *media_player); + void media_player_command(const MediaPlayerCommandRequest &msg) override; #endif bool send_log_message(int level, const char *tag, const char *line); void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 5a78587473..70f909c07a 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -308,6 +308,36 @@ template<> const char *proto_enum_to_string(enums::LockComma return "UNKNOWN"; } } +template<> const char *proto_enum_to_string(enums::MediaPlayerState value) { + switch (value) { + case enums::MEDIA_PLAYER_STATE_NONE: + return "MEDIA_PLAYER_STATE_NONE"; + case enums::MEDIA_PLAYER_STATE_IDLE: + return "MEDIA_PLAYER_STATE_IDLE"; + case enums::MEDIA_PLAYER_STATE_PLAYING: + return "MEDIA_PLAYER_STATE_PLAYING"; + case enums::MEDIA_PLAYER_STATE_PAUSED: + return "MEDIA_PLAYER_STATE_PAUSED"; + default: + return "UNKNOWN"; + } +} +template<> const char *proto_enum_to_string(enums::MediaPlayerCommand value) { + switch (value) { + case enums::MEDIA_PLAYER_COMMAND_PLAY: + return "MEDIA_PLAYER_COMMAND_PLAY"; + case enums::MEDIA_PLAYER_COMMAND_PAUSE: + return "MEDIA_PLAYER_COMMAND_PAUSE"; + case enums::MEDIA_PLAYER_COMMAND_STOP: + return "MEDIA_PLAYER_COMMAND_STOP"; + case enums::MEDIA_PLAYER_COMMAND_MUTE: + return "MEDIA_PLAYER_COMMAND_MUTE"; + case enums::MEDIA_PLAYER_COMMAND_UNMUTE: + return "MEDIA_PLAYER_COMMAND_UNMUTE"; + default: + return "UNKNOWN"; + } +} bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { @@ -4574,6 +4604,254 @@ void ButtonCommandRequest::dump_to(std::string &out) const { out.append("}"); } #endif +bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 6: { + this->disabled_by_default = value.as_bool(); + return true; + } + case 7: { + this->entity_category = value.as_enum(); + return true; + } + case 8: { + this->supports_pause = value.as_bool(); + return true; + } + default: + return false; + } +} +bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesMediaPlayerResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); + buffer.encode_bool(6, this->disabled_by_default); + buffer.encode_enum(7, this->entity_category); + buffer.encode_bool(8, this->supports_pause); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("ListEntitiesMediaPlayerResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + + out.append(" disabled_by_default: "); + out.append(YESNO(this->disabled_by_default)); + out.append("\n"); + + out.append(" entity_category: "); + out.append(proto_enum_to_string(this->entity_category)); + out.append("\n"); + + out.append(" supports_pause: "); + out.append(YESNO(this->supports_pause)); + out.append("\n"); + out.append("}"); +} +#endif +bool MediaPlayerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->state = value.as_enum(); + return true; + } + case 4: { + this->muted = value.as_bool(); + return true; + } + default: + return false; + } +} +bool MediaPlayerStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 3: { + this->volume = value.as_float(); + return true; + } + default: + return false; + } +} +void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_enum(2, this->state); + buffer.encode_float(3, this->volume); + buffer.encode_bool(4, this->muted); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void MediaPlayerStateResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("MediaPlayerStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + out.append(proto_enum_to_string(this->state)); + out.append("\n"); + + out.append(" volume: "); + sprintf(buffer, "%g", this->volume); + out.append(buffer); + out.append("\n"); + + out.append(" muted: "); + out.append(YESNO(this->muted)); + out.append("\n"); + out.append("}"); +} +#endif +bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->has_command = value.as_bool(); + return true; + } + case 3: { + this->command = value.as_enum(); + return true; + } + case 4: { + this->has_volume = value.as_bool(); + return true; + } + case 6: { + this->has_media_url = value.as_bool(); + return true; + } + default: + return false; + } +} +bool MediaPlayerCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 7: { + this->media_url = value.as_string(); + return true; + } + default: + return false; + } +} +bool MediaPlayerCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 5: { + this->volume = value.as_float(); + return true; + } + default: + return false; + } +} +void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->has_command); + buffer.encode_enum(3, this->command); + buffer.encode_bool(4, this->has_volume); + buffer.encode_float(5, this->volume); + buffer.encode_bool(6, this->has_media_url); + buffer.encode_string(7, this->media_url); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void MediaPlayerCommandRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("MediaPlayerCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" has_command: "); + out.append(YESNO(this->has_command)); + out.append("\n"); + + out.append(" command: "); + out.append(proto_enum_to_string(this->command)); + out.append("\n"); + + out.append(" has_volume: "); + out.append(YESNO(this->has_volume)); + out.append("\n"); + + out.append(" volume: "); + sprintf(buffer, "%g", this->volume); + out.append(buffer); + out.append("\n"); + + out.append(" has_media_url: "); + out.append(YESNO(this->has_media_url)); + out.append("\n"); + + out.append(" media_url: "); + out.append("'").append(this->media_url).append("'"); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 28c0a7ce88..ec1cdc35ac 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -141,6 +141,19 @@ enum LockCommand : uint32_t { LOCK_LOCK = 1, LOCK_OPEN = 2, }; +enum MediaPlayerState : uint32_t { + MEDIA_PLAYER_STATE_NONE = 0, + MEDIA_PLAYER_STATE_IDLE = 1, + MEDIA_PLAYER_STATE_PLAYING = 2, + MEDIA_PLAYER_STATE_PAUSED = 3, +}; +enum MediaPlayerCommand : uint32_t { + MEDIA_PLAYER_COMMAND_PLAY = 0, + MEDIA_PLAYER_COMMAND_PAUSE = 1, + MEDIA_PLAYER_COMMAND_STOP = 2, + MEDIA_PLAYER_COMMAND_MUTE = 3, + MEDIA_PLAYER_COMMAND_UNMUTE = 4, +}; } // namespace enums @@ -1146,6 +1159,60 @@ class ButtonCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; +class ListEntitiesMediaPlayerResponse : public ProtoMessage { + public: + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + std::string icon{}; + bool disabled_by_default{false}; + enums::EntityCategory entity_category{}; + bool supports_pause{false}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class MediaPlayerStateResponse : public ProtoMessage { + public: + uint32_t key{0}; + enums::MediaPlayerState state{}; + float volume{0.0f}; + bool muted{false}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class MediaPlayerCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; + bool has_command{false}; + enums::MediaPlayerCommand command{}; + bool has_volume{false}; + float volume{0.0f}; + bool has_media_url{false}; + std::string media_url{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index d981a3bf4e..bd146cb54d 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -310,6 +310,24 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit #endif #ifdef USE_BUTTON #endif +#ifdef USE_MEDIA_PLAYER +bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 63); +} +#endif +#ifdef USE_MEDIA_PLAYER +bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 64); +} +#endif +#ifdef USE_MEDIA_PLAYER +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -563,6 +581,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str()); #endif this->on_button_command_request(msg); +#endif + break; + } + case 65: { +#ifdef USE_MEDIA_PLAYER + MediaPlayerCommandRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str()); +#endif + this->on_media_player_command_request(msg); #endif break; } @@ -813,6 +842,19 @@ void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) this->lock_command(msg); } #endif +#ifdef USE_MEDIA_PLAYER +void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->media_player_command(msg); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 5aaf831c91..28ad3fbd15 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -144,6 +144,15 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_BUTTON virtual void on_button_command_request(const ButtonCommandRequest &value){}; +#endif +#ifdef USE_MEDIA_PLAYER + bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg); +#endif +#ifdef USE_MEDIA_PLAYER + bool send_media_player_state_response(const MediaPlayerStateResponse &msg); +#endif +#ifdef USE_MEDIA_PLAYER + virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -192,6 +201,9 @@ class APIServerConnection : public APIServerConnectionBase { #endif #ifdef USE_LOCK virtual void lock_command(const LockCommandRequest &msg) = 0; +#endif +#ifdef USE_MEDIA_PLAYER + virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0; #endif protected: void on_hello_request(const HelloRequest &msg) override; @@ -236,6 +248,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_LOCK void on_lock_command_request(const LockCommandRequest &msg) override; #endif +#ifdef USE_MEDIA_PLAYER + void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override; +#endif }; } // namespace api diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 1f2800f298..8375a82313 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -272,6 +272,15 @@ void APIServer::on_lock_update(lock::Lock *obj) { } #endif +#ifdef USE_MEDIA_PLAYER +void APIServer::on_media_player_update(media_player::MediaPlayer *obj) { + if (obj->is_internal()) + return; + for (auto &c : this->clients_) + c->send_media_player_state(obj); +} +#endif + float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } void APIServer::set_port(uint16_t port) { this->port_ = port; } APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index f03a83fc7b..6997e23cac 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -68,6 +68,9 @@ class APIServer : public Component, public Controller { #endif #ifdef USE_LOCK void on_lock_update(lock::Lock *obj) override; +#endif +#ifdef USE_MEDIA_PLAYER + void on_media_player_update(media_player::MediaPlayer *obj) override; #endif void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 9f55fda617..85d4cd61ef 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -64,5 +64,11 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); } #endif +#ifdef USE_MEDIA_PLAYER +bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) { + return this->client_->send_media_player_info(media_player); +} +#endif + } // namespace api } // namespace esphome diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 51c343eb03..4fbaa509a2 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -51,6 +51,9 @@ class ListEntitiesIterator : public ComponentIterator { #endif #ifdef USE_LOCK bool on_lock(lock::Lock *a_lock) override; +#endif +#ifdef USE_MEDIA_PLAYER + bool on_media_player(media_player::MediaPlayer *media_player) override; #endif bool on_end() override; diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index ba277502c8..1d1ba0245e 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -50,6 +50,11 @@ bool InitialStateIterator::on_select(select::Select *select) { #ifdef USE_LOCK bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } #endif +#ifdef USE_MEDIA_PLAYER +bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) { + return this->client_->send_media_player_state(media_player); +} +#endif InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {} } // namespace api diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index 515e1a2d07..7a7ba697c0 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -48,6 +48,9 @@ class InitialStateIterator : public ComponentIterator { #endif #ifdef USE_LOCK bool on_lock(lock::Lock *a_lock) override; +#endif +#ifdef USE_MEDIA_PLAYER + bool on_media_player(media_player::MediaPlayer *media_player) override; #endif protected: APIConnection *client_; diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/i2s_audio/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/i2s_audio_media_player.cpp new file mode 100644 index 0000000000..0ab3237aeb --- /dev/null +++ b/esphome/components/i2s_audio/i2s_audio_media_player.cpp @@ -0,0 +1,132 @@ +#include "i2s_audio_media_player.h" + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "esphome/core/log.h" + +namespace esphome { +namespace i2s_audio { + +static const char *const TAG = "audio"; + +void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { + if (call.get_media_url().has_value()) { + if (this->audio_->isRunning()) + this->audio_->stopSong(); + this->high_freq_.start(); + this->audio_->connecttohost(call.get_media_url().value().c_str()); + this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; + } + if (call.get_volume().has_value()) { + this->volume = call.get_volume().value(); + this->set_volume_(volume); + this->unmute_(); + } + if (call.get_command().has_value()) { + switch (call.get_command().value()) { + case media_player::MEDIA_PLAYER_COMMAND_PLAY: + if (!this->audio_->isRunning()) + this->audio_->pauseResume(); + this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; + break; + case media_player::MEDIA_PLAYER_COMMAND_PAUSE: + if (this->audio_->isRunning()) + this->audio_->pauseResume(); + this->state = media_player::MEDIA_PLAYER_STATE_PAUSED; + break; + case media_player::MEDIA_PLAYER_COMMAND_STOP: + this->stop_(); + break; + case media_player::MEDIA_PLAYER_COMMAND_MUTE: + this->mute_(); + break; + case media_player::MEDIA_PLAYER_COMMAND_UNMUTE: + this->unmute_(); + break; + } + } + this->publish_state(); +} + +void I2SAudioMediaPlayer::mute_() { + if (this->mute_pin_ != nullptr) { + this->mute_pin_->digital_write(true); + } else { + this->set_volume_(0.0f, false); + } + this->muted_ = true; +} +void I2SAudioMediaPlayer::unmute_() { + if (this->mute_pin_ != nullptr) { + this->mute_pin_->digital_write(false); + } else { + this->set_volume_(this->volume, false); + } + this->muted_ = false; +} +void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) { + this->audio_->setVolume(remap(volume, 0.0f, 1.0f, 0, 21)); + if (publish) + this->volume = volume; +} + +void I2SAudioMediaPlayer::stop_() { + if (this->audio_->isRunning()) + this->audio_->stopSong(); + this->high_freq_.stop(); + this->state = media_player::MEDIA_PLAYER_STATE_IDLE; +} + +void I2SAudioMediaPlayer::setup() { + ESP_LOGCONFIG(TAG, "Setting up Audio..."); + if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) { + this->audio_ = make_unique