mirror of
https://github.com/esphome/aioesphomeapi.git
synced 2024-12-21 16:37:41 +01:00
Add media player entities (#214)
This commit is contained in:
parent
616e5c6621
commit
fcd529bb32
@ -43,6 +43,7 @@ service APIConnection {
|
||||
rpc siren_command (SirenCommandRequest) returns (void) {}
|
||||
rpc button_command (ButtonCommandRequest) returns (void) {}
|
||||
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||
}
|
||||
|
||||
|
||||
@ -1018,7 +1019,7 @@ message ListEntitiesLockResponse {
|
||||
bool disabled_by_default = 6;
|
||||
EntityCategory entity_category = 7;
|
||||
bool assumed_state = 8;
|
||||
|
||||
|
||||
bool supports_open = 9;
|
||||
bool requires_code = 10;
|
||||
string code_format = 11;
|
||||
@ -1066,3 +1067,63 @@ 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;
|
||||
bool supports_volume = 9;
|
||||
bool supports_mute = 10;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -42,6 +42,7 @@ from .api_pb2 import ( # type: ignore
|
||||
ListEntitiesFanResponse,
|
||||
ListEntitiesLightResponse,
|
||||
ListEntitiesLockResponse,
|
||||
ListEntitiesMediaPlayerResponse,
|
||||
ListEntitiesNumberResponse,
|
||||
ListEntitiesRequest,
|
||||
ListEntitiesSelectResponse,
|
||||
@ -52,6 +53,8 @@ from .api_pb2 import ( # type: ignore
|
||||
ListEntitiesTextSensorResponse,
|
||||
LockCommandRequest,
|
||||
LockStateResponse,
|
||||
MediaPlayerCommandRequest,
|
||||
MediaPlayerStateResponse,
|
||||
NumberCommandRequest,
|
||||
NumberStateResponse,
|
||||
SelectCommandRequest,
|
||||
@ -102,6 +105,9 @@ from .model import (
|
||||
LockEntityState,
|
||||
LockInfo,
|
||||
LogLevel,
|
||||
MediaPlayerCommand,
|
||||
MediaPlayerEntityState,
|
||||
MediaPlayerInfo,
|
||||
NumberInfo,
|
||||
NumberState,
|
||||
SelectInfo,
|
||||
@ -265,6 +271,7 @@ class APIClient:
|
||||
ListEntitiesCameraResponse: CameraInfo,
|
||||
ListEntitiesClimateResponse: ClimateInfo,
|
||||
ListEntitiesLockResponse: LockInfo,
|
||||
ListEntitiesMediaPlayerResponse: MediaPlayerInfo,
|
||||
}
|
||||
|
||||
def do_append(msg: message.Message) -> bool:
|
||||
@ -309,6 +316,7 @@ class APIClient:
|
||||
TextSensorStateResponse: TextSensorState,
|
||||
ClimateStateResponse: ClimateState,
|
||||
LockStateResponse: LockEntityState,
|
||||
MediaPlayerStateResponse: MediaPlayerEntityState,
|
||||
}
|
||||
|
||||
image_stream: Dict[int, bytes] = {}
|
||||
@ -655,6 +663,30 @@ class APIClient:
|
||||
assert self._connection is not None
|
||||
await self._connection.send_message(req)
|
||||
|
||||
async def media_player_command(
|
||||
self,
|
||||
key: int,
|
||||
*,
|
||||
command: Optional[MediaPlayerCommand] = None,
|
||||
volume: Optional[float] = None,
|
||||
media_url: Optional[str] = None,
|
||||
) -> None:
|
||||
self._check_authenticated()
|
||||
|
||||
req = MediaPlayerCommandRequest()
|
||||
req.key = key
|
||||
if command is not None:
|
||||
req.command = command
|
||||
req.has_command = True
|
||||
if volume is not None:
|
||||
req.volume = volume
|
||||
req.has_volume = True
|
||||
if media_url is not None:
|
||||
req.media_url = media_url
|
||||
req.has_media_url = True
|
||||
assert self._connection is not None
|
||||
await self._connection.send_message(req)
|
||||
|
||||
async def execute_service(
|
||||
self, service: UserService, data: ExecuteServiceDataType
|
||||
) -> None:
|
||||
|
@ -33,6 +33,7 @@ from .api_pb2 import ( # type: ignore
|
||||
ListEntitiesFanResponse,
|
||||
ListEntitiesLightResponse,
|
||||
ListEntitiesLockResponse,
|
||||
ListEntitiesMediaPlayerResponse,
|
||||
ListEntitiesNumberResponse,
|
||||
ListEntitiesRequest,
|
||||
ListEntitiesSelectResponse,
|
||||
@ -43,6 +44,8 @@ from .api_pb2 import ( # type: ignore
|
||||
ListEntitiesTextSensorResponse,
|
||||
LockCommandRequest,
|
||||
LockStateResponse,
|
||||
MediaPlayerCommandRequest,
|
||||
MediaPlayerStateResponse,
|
||||
NumberCommandRequest,
|
||||
NumberStateResponse,
|
||||
PingRequest,
|
||||
@ -183,4 +186,7 @@ MESSAGE_TYPE_TO_PROTO = {
|
||||
60: LockCommandRequest,
|
||||
61: ListEntitiesButtonResponse,
|
||||
62: ButtonCommandRequest,
|
||||
63: ListEntitiesMediaPlayerResponse,
|
||||
64: MediaPlayerStateResponse,
|
||||
65: MediaPlayerCommandRequest,
|
||||
}
|
||||
|
@ -634,6 +634,40 @@ class LockEntityState(EntityState):
|
||||
)
|
||||
|
||||
|
||||
# ==================== MEDIA PLAYER ====================
|
||||
class MediaPlayerState(APIIntEnum):
|
||||
NONE = 0
|
||||
IDLE = 1
|
||||
PLAYING = 2
|
||||
PAUSED = 3
|
||||
|
||||
|
||||
class MediaPlayerCommand(APIIntEnum):
|
||||
PLAY = 0
|
||||
PAUSE = 1
|
||||
STOP = 2
|
||||
MUTE = 3
|
||||
UNMUTE = 4
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MediaPlayerInfo(EntityInfo):
|
||||
supports_pause: bool = False
|
||||
supports_volume: bool = False
|
||||
supports_mute: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MediaPlayerEntityState(EntityState):
|
||||
state: Optional[MediaPlayerState] = converter_field(
|
||||
default=MediaPlayerState.NONE, converter=MediaPlayerState.convert
|
||||
)
|
||||
volume: float = converter_field(
|
||||
default=0.0, converter=fix_float_single_double_conversion
|
||||
)
|
||||
muted: bool = False
|
||||
|
||||
|
||||
# ==================== INFO MAP ====================
|
||||
|
||||
COMPONENT_TYPE_TO_INFO: Dict[str, Type[EntityInfo]] = {
|
||||
@ -651,6 +685,7 @@ COMPONENT_TYPE_TO_INFO: Dict[str, Type[EntityInfo]] = {
|
||||
"siren": SirenInfo,
|
||||
"button": ButtonInfo,
|
||||
"lock": LockInfo,
|
||||
"media_player": MediaPlayerInfo,
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ from aioesphomeapi.api_pb2 import (
|
||||
ListEntitiesDoneResponse,
|
||||
ListEntitiesServicesResponse,
|
||||
LockCommandRequest,
|
||||
MediaPlayerCommandRequest,
|
||||
NumberCommandRequest,
|
||||
SelectCommandRequest,
|
||||
SwitchCommandRequest,
|
||||
@ -35,6 +36,7 @@ from aioesphomeapi.model import (
|
||||
FanSpeed,
|
||||
LegacyCoverCommand,
|
||||
LockCommand,
|
||||
MediaPlayerCommand,
|
||||
UserService,
|
||||
UserServiceArg,
|
||||
UserServiceArgType,
|
||||
@ -392,6 +394,31 @@ async def test_select_command(auth_client, cmd, req):
|
||||
send.assert_called_once_with(SelectCommandRequest(**req))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"cmd, req",
|
||||
[
|
||||
(
|
||||
dict(key=1, command=MediaPlayerCommand.MUTE),
|
||||
dict(key=1, has_command=True, command=MediaPlayerCommand.MUTE),
|
||||
),
|
||||
(
|
||||
dict(key=1, volume=1.0),
|
||||
dict(key=1, has_volume=True, volume=1.0),
|
||||
),
|
||||
(
|
||||
dict(key=1, media_url="http://example.com"),
|
||||
dict(key=1, has_media_url=True, media_url="http://example.com"),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_media_player_command(auth_client, cmd, req):
|
||||
send = patch_send(auth_client)
|
||||
|
||||
await auth_client.media_player_command(**cmd)
|
||||
send.assert_called_once_with(MediaPlayerCommandRequest(**req))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_service(auth_client):
|
||||
send = patch_send(auth_client)
|
||||
|
@ -19,6 +19,7 @@ from aioesphomeapi.api_pb2 import (
|
||||
ListEntitiesFanResponse,
|
||||
ListEntitiesLightResponse,
|
||||
ListEntitiesLockResponse,
|
||||
ListEntitiesMediaPlayerResponse,
|
||||
ListEntitiesNumberResponse,
|
||||
ListEntitiesSelectResponse,
|
||||
ListEntitiesSensorResponse,
|
||||
@ -27,6 +28,7 @@ from aioesphomeapi.api_pb2 import (
|
||||
ListEntitiesSwitchResponse,
|
||||
ListEntitiesTextSensorResponse,
|
||||
LockStateResponse,
|
||||
MediaPlayerStateResponse,
|
||||
NumberStateResponse,
|
||||
SelectStateResponse,
|
||||
SensorStateResponse,
|
||||
@ -55,6 +57,8 @@ from aioesphomeapi.model import (
|
||||
LightState,
|
||||
LockEntityState,
|
||||
LockInfo,
|
||||
MediaPlayerEntityState,
|
||||
MediaPlayerInfo,
|
||||
NumberInfo,
|
||||
NumberState,
|
||||
SelectInfo,
|
||||
@ -220,6 +224,8 @@ def test_api_version_ord():
|
||||
(ButtonInfo, ListEntitiesButtonResponse),
|
||||
(LockInfo, ListEntitiesLockResponse),
|
||||
(LockEntityState, LockStateResponse),
|
||||
(MediaPlayerInfo, ListEntitiesMediaPlayerResponse),
|
||||
(MediaPlayerEntityState, MediaPlayerStateResponse),
|
||||
],
|
||||
)
|
||||
def test_basic_pb_conversions(model, pb):
|
||||
|
Loading…
Reference in New Issue
Block a user