Skip to content

Commit

Permalink
feat(predefined-action): add tts action to MediaPlayerController
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviml committed Jul 10, 2021
1 parent 9d48e8f commit 501cc8a
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 22 deletions.
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PRERELEASE_NOTE
## :pencil2: Features

- Add `volume_set` predefined action for `MediaPlayerController`. See [here](https://xaviml.github.io/controllerx/advanced/predefined-actions#media-player).
- Add `tts` predefined action for `MediaPlayerController`. See [here](https://xaviml.github.io/controllerx/advanced/predefined-actions#media-player).

<!--
## :hammer: Fixes
Expand Down
1 change: 1 addition & 0 deletions apps/controllerx/cx_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class MediaPlayer:
NEXT_SOURCE = "next_source"
PREVIOUS_SOURCE = "previous_source"
MUTE = "mute"
TTS = "tts"


class Switch:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ def _get_arguments(
f"`{action.__name__}` action is missing some parameters. Parameters available:"
]
for key, param in action_parameters_without_extra.items():
attr_msg = f" {key}: {param.annotation.__name__}"
if hasattr(param.annotation, "__name__"):
attr_msg = f" {key}: {param.annotation.__name__}"
else:
attr_msg = f" {key}:"
if param.default is not inspect.Signature.empty:
attr_msg += f" [default: {param.default}]"
if key in action_args:
Expand Down
21 changes: 20 additions & 1 deletion apps/controllerx/cx_core/type/media_player_controller.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Type
from typing import Any, Dict, Optional, Type

from cx_const import MediaPlayer, PredefinedActionsMapping
from cx_core.controller import action
Expand Down Expand Up @@ -42,6 +42,7 @@ def get_predefined_actions_mapping(self) -> PredefinedActionsMapping:
MediaPlayer.NEXT_SOURCE: (self.change_source_list, (Stepper.UP,)),
MediaPlayer.PREVIOUS_SOURCE: (self.change_source_list, (Stepper.DOWN,)),
MediaPlayer.MUTE: self.volume_mute,
MediaPlayer.TTS: self.tts,
}

@action
Expand Down Expand Up @@ -117,6 +118,24 @@ async def volume_down(self) -> None:
async def volume_mute(self) -> None:
await self.call_service("media_player/volume_mute", entity_id=self.entity.name)

@action
async def tts(
self,
message: str,
service: str = "google_translate_say",
cache: Optional[bool] = None,
language: Optional[str] = None,
options: Optional[Dict[str, Any]] = None,
) -> None:
args: Dict[str, Any] = {"entity_id": self.entity.name, "message": message}
if cache is not None:
args["cache"] = cache
if language is not None:
args["language"] = language
if options is not None:
args["options"] = options
await self.call_service(f"tts.{service}", **args)

@action
async def hold(self, direction: str) -> None: # type: ignore
await self.prepare_volume_change()
Expand Down
31 changes: 16 additions & 15 deletions docs/advanced/predefined-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ When using a [light controller](/controllerx/start/type-configuration#light-cont
| `set_half_brightness` | It sets the brightness to 50% | |
| `set_half_white_value` | It sets the white value to 50% | |
| `set_half_color_temp` | It sets the color temp to 50% | |
| `sync` | It syncs the light(s) to full brightness and white colour or 2700K (370 mireds) | - `brightness`<br>- `color_temp`<br/>- `xy_color` |
| `sync` | It syncs the light(s) to full brightness and white colour or 2700K (370 mireds) | - `brightness`<br>- `color_temp`<br>- `xy_color` |
| `click_brightness_up` | It brights up accordingly with the `manual_steps` attribute | |
| `click_brightness_down` | It brights down accordingly with the `manual_steps` attribute | |
| `click_white_value_up` | It turns the white value up accordingly with the `manual_steps` attribute | |
Expand Down Expand Up @@ -65,20 +65,21 @@ When using a [light controller](/controllerx/start/type-configuration#light-cont

When using a [media player controller](/controllerx/start/type-configuration#media-player-controller) (e.g. `E1743MediaPlayerController`) or `MediaPlayerController`, the following actions can be used as a predefined action:

| value | description | parameters |
| ------------------- | -------------------------------------------------- | -------------------------------------------- |
| `hold_volume_down` | It turns the volume down until `release` is called | |
| `hold_volume_up` | It turns the volume up until `release` is called | |
| `click_volume_down` | It turns the volume down one step | |
| `click_volume_up` | It turns the volume up one step | |
| `volume_set` | It sets the volume to given level | - `volume_level`: volume level (from 0 to 1) |
| `release` | It calls `release` for `hold` actions | |
| `play_pause` | It toggles the play/pause media | |
| `next_track` | It skips the track forward | |
| `previous_track` | It skips the track backward | |
| `next_source` | It changes to the next source | |
| `previous_source` | It changes to the previous source | |
| `mute` | It mutes the media player | |
| value | description | parameters |
| ------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `hold_volume_down` | It turns the volume down until `release` is called | |
| `hold_volume_up` | It turns the volume up until `release` is called | |
| `click_volume_down` | It turns the volume down one step | |
| `click_volume_up` | It turns the volume up one step | |
| `volume_set` | It sets the volume to given level | - `volume_level`: volume level (from 0 to 1) |
| `release` | It calls `release` for `hold` actions | |
| `play_pause` | It toggles the play/pause media | |
| `next_track` | It skips the track forward | |
| `previous_track` | It skips the track backward | |
| `next_source` | It changes to the next source | |
| `previous_source` | It changes to the previous source | |
| `mute` | It mutes the media player | |
| `tts` | Text-to-Speech | - `message`<br>- `service`: the service to call without "tts." (str; default: "google_translate_say")<br>- `cache` (bool; default: None)<br>- `language` (str; default: None)<br>- `options` (dict; default: None) |

## Switch

Expand Down
41 changes: 36 additions & 5 deletions tests/unit_tests/cx_core/type/media_player_controller_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,7 @@ async def test_volume_down(


@pytest.mark.asyncio
async def test_volume_set(
sut: MediaPlayerController, mocker: MockerFixture, monkeypatch: MonkeyPatch
):
monkeypatch.setattr(sut, "get_entity_state", fake_fn(async_=True, to_return=0.5))
sut.feature_support._supported_features = MediaPlayerSupport.VOLUME_SET
async def test_volume_set(sut: MediaPlayerController, mocker: MockerFixture):
called_service_patch = mocker.patch.object(sut, "call_service")

await sut.volume_set(0.8)
Expand All @@ -125,6 +121,41 @@ async def test_volume_set(
)


@pytest.mark.asyncio
async def test_volume_mute(sut: MediaPlayerController, mocker: MockerFixture):
called_service_patch = mocker.patch.object(sut, "call_service")

await sut.volume_mute()

called_service_patch.assert_called_once_with(
"media_player/volume_mute", entity_id=ENTITY_NAME
)


@pytest.mark.asyncio
async def test_tts(
sut: MediaPlayerController, mocker: MockerFixture, monkeypatch: MonkeyPatch
):
called_service_patch = mocker.patch.object(sut, "call_service")

await sut.tts(
"test msg",
service="fake_service",
cache=False,
language="en",
options={"a": "b"},
)

called_service_patch.assert_called_once_with(
"tts.fake_service",
entity_id=ENTITY_NAME,
message="test msg",
cache=False,
language="en",
options={"a": "b"},
)


@pytest.mark.asyncio
async def test_hold(sut: MediaPlayerController, mocker: MockerFixture):
direction = "test_direction"
Expand Down

0 comments on commit 501cc8a

Please sign in to comment.