1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-28 05:33:53 +00:00

Implement Media Player and I2S Media player (#3487)

This commit is contained in:
Jesse Hills
2022-06-02 17:00:17 +12:00
committed by GitHub
parent a922efeafa
commit 6221f6d47d
29 changed files with 1170 additions and 2 deletions

View File

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

View File

@@ -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<enums::MediaPlayerState>(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<enums::EntityCategory>(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<media_player::MediaPlayerCommand>(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<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)

View File

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

View File

@@ -308,6 +308,36 @@ template<> const char *proto_enum_to_string<enums::LockCommand>(enums::LockComma
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::MediaPlayerState>(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>(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<enums::EntityCategory>();
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<enums::EntityCategory>(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<enums::EntityCategory>(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<enums::MediaPlayerState>();
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<enums::MediaPlayerState>(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<enums::MediaPlayerState>(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<enums::MediaPlayerCommand>();
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<enums::MediaPlayerCommand>(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<enums::MediaPlayerCommand>(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

View File

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

View File

@@ -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_<ListEntitiesMediaPlayerResponse>(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_<MediaPlayerStateResponse>(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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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