""" This module integrates Mopidy music playback with ntfy notifications. It listens for music control commands sent via ntfy and executes them on the Mopidy server. """ import json import logging from collections import namedtuple from enum import Enum from platypush import Config, hook, run # Listen for ntfy message notifications from platypush.message.event.ntfy import NotificationEvent # NOTE: The topic used to send music commands to Mopidy via ntfy. # It must match the one configured in your ntfy mobile app. music_commands_topic = "music-commands-" music_plugin = "music.mopidy" # Or "music.mpd" if using MPD logger = logging.getLogger("music_integrations") ClassCommand = namedtuple("ClassCommand", ["name", "action"]) class MusicCommands(Enum): """ Enum representing music control commands and their corresponding actions. """ PLAY = ClassCommand("play", f"{music_plugin}.play") PAUSE = ClassCommand("pause", f"{music_plugin}.pause") STOP = ClassCommand("stop", f"{music_plugin}.stop") NEXT = ClassCommand("next", f"{music_plugin}.next") PREVIOUS = ClassCommand("previous", f"{music_plugin}.previous") TOGGLE = ClassCommand("toggle", f"{music_plugin}.toggle") supported_commands = {cmd.value.name: cmd.value.action for cmd in MusicCommands} @hook(NotificationEvent, topic=music_commands_topic) def on_music_command(event: NotificationEvent): """ Handles incoming music commands from ntfy notifications. """ try: payload = json.loads(event.message) except Exception as e: logger.warning("Invalid music command payload: %s", e) return # Ignore the command if it's not for this device. # This is useful if multiple devices share the same ntfy topic. device_id = payload.get("device_id") if device_id and device_id != Config.get_device_id(): return command = next( (cmd for cmd in MusicCommands if cmd.value.name == payload.get("command")), None ) if not command: logger.warning( "Unsupported music command: %s. Supported commands: %s", payload.get("command"), list(supported_commands.keys()), ) return # Handle the TOGGLE case depending on current playback state if command == MusicCommands.TOGGLE: status = run("music.mopidy.status") if status.get("state") == "play": action = MusicCommands.PAUSE.value.action else: action = MusicCommands.PLAY.value.action else: action = command.value.action logger.info( "Executing music command on %s@%s: %s", music_plugin, device_id, command.value.name, ) run(action)