diff --git a/README.md b/README.md index 4a2112d..98228ef 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A Home Assistant custom integration that allows you to control a remote PC's med - Seek support with smooth timeline updates - Displays current track info (title, artist, album, artwork) - Real-time updates via WebSocket (with HTTP polling fallback) +- **Turn on/off/toggle support** - Execute custom actions (e.g., lock screen on turn off) - **Script buttons** - Execute pre-defined scripts (shutdown, restart, lock, sleep, etc.) - Configurable via Home Assistant UI @@ -69,6 +70,31 @@ A full-featured media player entity with: - Volume control and mute - Seek functionality - Current track information +- Turn on/off/toggle actions (execute server-side callbacks) + +### Turn On/Off/Toggle + +The media player supports `media_player.turn_on`, `media_player.turn_off`, and `media_player.toggle` actions. These execute optional callbacks configured on the Media Server (e.g., lock screen on turn off). + +Configure callbacks in Media Server's `config.yaml`: + +```yaml +callbacks: + on_turn_on: + command: "echo PC turned on" + timeout: 10 + shell: true + + on_turn_off: + command: "rundll32.exe user32.dll,LockWorkStation" + timeout: 5 + shell: true + + on_toggle: + command: "echo Toggle triggered" + timeout: 10 + shell: true +``` ### Script Button Entities diff --git a/custom_components/remote_media_player/api_client.py b/custom_components/remote_media_player/api_client.py index 7dbcfc0..01cb88d 100644 --- a/custom_components/remote_media_player/api_client.py +++ b/custom_components/remote_media_player/api_client.py @@ -22,6 +22,9 @@ from .const import ( API_VOLUME, API_MUTE, API_SEEK, + API_TURN_ON, + API_TURN_OFF, + API_TOGGLE, API_SCRIPTS_LIST, API_SCRIPTS_EXECUTE, ) @@ -245,6 +248,30 @@ class MediaServerClient: """ return await self._request("POST", API_SEEK, {"position": position}) + async def turn_on(self) -> dict[str, Any]: + """Send turn on command. + + Returns: + Response data + """ + return await self._request("POST", API_TURN_ON) + + async def turn_off(self) -> dict[str, Any]: + """Send turn off command. + + Returns: + Response data + """ + return await self._request("POST", API_TURN_OFF) + + async def toggle(self) -> dict[str, Any]: + """Send toggle command. + + Returns: + Response data + """ + return await self._request("POST", API_TOGGLE) + async def list_scripts(self) -> list[dict[str, Any]]: """List available scripts on the server. diff --git a/custom_components/remote_media_player/const.py b/custom_components/remote_media_player/const.py index c9dafd5..19a38b6 100644 --- a/custom_components/remote_media_player/const.py +++ b/custom_components/remote_media_player/const.py @@ -28,6 +28,9 @@ API_PREVIOUS = "/api/media/previous" API_VOLUME = "/api/media/volume" API_MUTE = "/api/media/mute" API_SEEK = "/api/media/seek" +API_TURN_ON = "/api/media/turn_on" +API_TURN_OFF = "/api/media/turn_off" +API_TOGGLE = "/api/media/toggle" API_SCRIPTS_LIST = "/api/scripts/list" API_SCRIPTS_EXECUTE = "/api/scripts/execute" API_WEBSOCKET = "/api/media/ws" diff --git a/custom_components/remote_media_player/media_player.py b/custom_components/remote_media_player/media_player.py index 319afb7..350f15b 100644 --- a/custom_components/remote_media_player/media_player.py +++ b/custom_components/remote_media_player/media_player.py @@ -285,6 +285,8 @@ class RemoteMediaPlayerEntity(CoordinatorEntity[MediaPlayerCoordinator], MediaPl | MediaPlayerEntityFeature.PREVIOUS_TRACK | MediaPlayerEntityFeature.NEXT_TRACK | MediaPlayerEntityFeature.SEEK + | MediaPlayerEntityFeature.TURN_ON + | MediaPlayerEntityFeature.TURN_OFF ) @property @@ -450,3 +452,24 @@ class RemoteMediaPlayerEntity(CoordinatorEntity[MediaPlayerCoordinator], MediaPl await self.coordinator.async_request_refresh() except MediaServerError as err: _LOGGER.error("Failed to seek: %s", err) + + async def async_turn_on(self) -> None: + """Send turn on command.""" + try: + await self.coordinator.client.turn_on() + except MediaServerError as err: + _LOGGER.error("Failed to turn on: %s", err) + + async def async_turn_off(self) -> None: + """Send turn off command.""" + try: + await self.coordinator.client.turn_off() + except MediaServerError as err: + _LOGGER.error("Failed to turn off: %s", err) + + async def async_toggle(self) -> None: + """Send toggle command.""" + try: + await self.coordinator.client.toggle() + except MediaServerError as err: + _LOGGER.error("Failed to toggle: %s", err)