diff --git a/platypush/app/_app.py b/platypush/app/_app.py
index 589efb66..bf005415 100644
--- a/platypush/app/_app.py
+++ b/platypush/app/_app.py
@@ -274,6 +274,10 @@ class Application:
             backend.stop()
 
         for plugin in runnable_plugins:
+            # This is required because some plugins may redefine the `stop` method.
+            # In that case, at the very least the _should_stop event should be
+            # set to notify the plugin to stop.
+            plugin._should_stop.set()  # pylint: disable=protected-access
             plugin.stop()
 
         for backend in backends:
diff --git a/platypush/backend/music/mpd/__init__.py b/platypush/backend/music/mpd/__init__.py
deleted file mode 100644
index 30ced92f..00000000
--- a/platypush/backend/music/mpd/__init__.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import time
-
-from platypush.backend import Backend
-from platypush.context import get_plugin
-from platypush.message.event.music import (
-    MusicPlayEvent,
-    MusicPauseEvent,
-    MusicStopEvent,
-    NewPlayingTrackEvent,
-    PlaylistChangeEvent,
-    VolumeChangeEvent,
-    PlaybackConsumeModeChangeEvent,
-    PlaybackSingleModeChangeEvent,
-    PlaybackRepeatModeChangeEvent,
-    PlaybackRandomModeChangeEvent,
-)
-
-
-class MusicMpdBackend(Backend):
-    """
-    This backend listens for events on a MPD/Mopidy music server.
-
-    Requires:
-
-        * :class:`platypush.plugins.music.mpd.MusicMpdPlugin` configured
-
-    """
-
-    def __init__(self, server='localhost', port=6600, poll_seconds=3, **kwargs):
-        """
-        :param poll_seconds: Interval between queries to the server (default: 3 seconds)
-        :type poll_seconds: float
-        """
-
-        super().__init__(**kwargs)
-
-        self.server = server
-        self.port = port
-        self.poll_seconds = poll_seconds
-
-    def run(self):
-        super().run()
-
-        last_status = {}
-        last_state = None
-        last_track = None
-        last_playlist = None
-
-        while not self.should_stop():
-            success = False
-            state = None
-            status = None
-            playlist = None
-            track = None
-
-            while not success:
-                try:
-                    plugin = get_plugin('music.mpd')
-                    if not plugin:
-                        raise StopIteration
-
-                    status = plugin.status().output
-                    if not status or status.get('state') is None:
-                        raise StopIteration
-
-                    track = plugin.currentsong().output
-                    state = status['state'].lower()
-                    playlist = status['playlist']
-                    success = True
-                except Exception as e:
-                    self.logger.debug(e)
-                    get_plugin('music.mpd', reload=True)
-                    if not state:
-                        state = last_state
-                    if not playlist:
-                        playlist = last_playlist
-                    if not track:
-                        track = last_track
-                finally:
-                    time.sleep(self.poll_seconds)
-
-            if state != last_state:
-                if state == 'stop':
-                    self.bus.post(
-                        MusicStopEvent(
-                            status=status, track=track, plugin_name='music.mpd'
-                        )
-                    )
-                elif state == 'pause':
-                    self.bus.post(
-                        MusicPauseEvent(
-                            status=status, track=track, plugin_name='music.mpd'
-                        )
-                    )
-                elif state == 'play':
-                    self.bus.post(
-                        MusicPlayEvent(
-                            status=status, track=track, plugin_name='music.mpd'
-                        )
-                    )
-
-            if playlist != last_playlist:
-                if last_playlist:
-                    # XXX plchanges can become heavy with big playlists,
-                    # PlaylistChangeEvent temporarily disabled
-                    # changes = plugin.plchanges(last_playlist).output
-                    # self.bus.post(PlaylistChangeEvent(changes=changes))
-                    self.bus.post(PlaylistChangeEvent(plugin_name='music.mpd'))
-                last_playlist = playlist
-
-            if state == 'play' and track != last_track:
-                self.bus.post(
-                    NewPlayingTrackEvent(
-                        status=status, track=track, plugin_name='music.mpd'
-                    )
-                )
-
-            if last_status.get('volume') != status['volume']:
-                self.bus.post(
-                    VolumeChangeEvent(
-                        volume=int(status['volume']),
-                        status=status,
-                        track=track,
-                        plugin_name='music.mpd',
-                    )
-                )
-
-            if last_status.get('random') != status['random']:
-                self.bus.post(
-                    PlaybackRandomModeChangeEvent(
-                        state=bool(int(status['random'])),
-                        status=status,
-                        track=track,
-                        plugin_name='music.mpd',
-                    )
-                )
-
-            if last_status.get('repeat') != status['repeat']:
-                self.bus.post(
-                    PlaybackRepeatModeChangeEvent(
-                        state=bool(int(status['repeat'])),
-                        status=status,
-                        track=track,
-                        plugin_name='music.mpd',
-                    )
-                )
-
-            if last_status.get('consume') != status['consume']:
-                self.bus.post(
-                    PlaybackConsumeModeChangeEvent(
-                        state=bool(int(status['consume'])),
-                        status=status,
-                        track=track,
-                        plugin_name='music.mpd',
-                    )
-                )
-
-            if last_status.get('single') != status['single']:
-                self.bus.post(
-                    PlaybackSingleModeChangeEvent(
-                        state=bool(int(status['single'])),
-                        status=status,
-                        track=track,
-                        plugin_name='music.mpd',
-                    )
-                )
-
-            last_status = status
-            last_state = state
-            last_track = track
-            time.sleep(self.poll_seconds)
-
-
-# vim:sw=4:ts=4:et:
diff --git a/platypush/backend/music/mpd/manifest.yaml b/platypush/backend/music/mpd/manifest.yaml
deleted file mode 100644
index 5d3328b6..00000000
--- a/platypush/backend/music/mpd/manifest.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-manifest:
-  events:
-    platypush.message.event.music.MusicPauseEvent: if the playback state changed to
-      pause
-    platypush.message.event.music.MusicPlayEvent: if the playback state changed to
-      play
-    platypush.message.event.music.MusicStopEvent: if the playback state changed to
-      stop
-    platypush.message.event.music.NewPlayingTrackEvent: if a new track is being played
-    platypush.message.event.music.PlaylistChangeEvent: if the main playlist has changed
-    platypush.message.event.music.VolumeChangeEvent: if the main volume has changed
-  install:
-    apt:
-      - python3-mpd2
-    dnf:
-      - python-mpd2
-    pacman:
-      - python-mpd2
-    pip:
-      - python-mpd2
-  package: platypush.backend.music.mpd
-  type: backend
diff --git a/platypush/config/config.yaml b/platypush/config/config.yaml
index a5ec7e0a..6af32c3b 100644
--- a/platypush/config/config.yaml
+++ b/platypush/config/config.yaml
@@ -320,6 +320,18 @@ backend.http:
 #     port: 6600
 ###
 
+###
+# # Example last.fm scrobbler configuration, to synchronize your music
+# # activities to your Last.fm profile. You'll need to register an application
+# # with your account at https://www.last.fm/api.
+#
+# lastfm:
+#     api_key: <API_KEY>
+#     api_secret: <API_SECRET>
+#     username: <USERNAME>
+#     password: <PASSWORD>
+###
+
 ###
 # # Plugins with empty configuration can also be explicitly enabled by specifying
 # # `enabled: true` or `disabled: false`. An integration with no items will be
@@ -1100,6 +1112,23 @@ backend.http:
 #               source: ${msg["source"]}
 ###
 
+###
+# # The example below is a hook that reacts when a `NewPlayingTrackEvent` event
+# # is received and synchronize the listening activity to the users' Last.fm
+# # profile (it requires the `lastfm` plugin and at least a music plugin
+# # enabled, like `music.mpd`).
+# 
+# event.hook.OnNewMusicActivity:
+#     if:
+#         type: platypush.message.event.music.NewPlayingTrackEvent
+#     then:
+#       - if ${track.get('artist') and track.get('title')}:
+#         - action: lastfm.scrobble
+#           args:
+#             artist: ${track['artist']}
+#             title: ${track['title']}
+##
+
 ###
 # # The example below plays the music on mpd/mopidy when your voice assistant
 # # triggers a speech recognized event with "play the music" content.
diff --git a/platypush/plugins/music/mpd/__init__.py b/platypush/plugins/music/mpd/__init__.py
index 0de6c483..2dd54a16 100644
--- a/platypush/plugins/music/mpd/__init__.py
+++ b/platypush/plugins/music/mpd/__init__.py
@@ -1,13 +1,15 @@
 import re
 import threading
-import time
 from typing import Collection, Optional, Union
 
-from platypush.plugins import action
+from platypush.plugins import RunnablePlugin, action
 from platypush.plugins.music import MusicPlugin
 
+from ._conf import MpdConfig
+from ._listener import MpdListener
 
-class MusicMpdPlugin(MusicPlugin):
+
+class MusicMpdPlugin(MusicPlugin, RunnablePlugin):
     """
     This plugin allows you to interact with an MPD/Mopidy music server.
 
@@ -21,22 +23,29 @@ class MusicMpdPlugin(MusicPlugin):
 
     .. note:: As of Mopidy 3.0 MPD is an optional interface provided by the
         ``mopidy-mpd`` extension. Make sure that you have the extension
-        installed and enabled on your instance to use this plugin with your
-        server.
+        installed and enabled on your instance to use this plugin if you want to
+        use it with Mopidy instead of MPD.
 
     """
 
     _client_lock = threading.RLock()
 
-    def __init__(self, host: str, port: int = 6600):
+    def __init__(
+        self,
+        host: str,
+        port: int = 6600,
+        poll_interval: Optional[float] = 5.0,
+        **kwargs,
+    ):
         """
-        :param host: MPD IP/hostname
-        :param port: MPD port (default: 6600)
+        :param host: MPD IP/hostname.
+        :param port: MPD port (default: 6600).
+        :param poll_interval: Polling interval in seconds. If set, the plugin
+            will poll the MPD server for status updates and trigger change
+            events when required. Default: 5 seconds.
         """
-
-        super().__init__()
-        self.host = host
-        self.port = port
+        super().__init__(poll_interval=poll_interval, **kwargs)
+        self.conf = MpdConfig(host=host, port=port)
         self.client = None
 
     def _connect(self, n_tries: int = 2):
@@ -51,7 +60,7 @@ class MusicMpdPlugin(MusicPlugin):
                 try:
                     n_tries -= 1
                     self.client = mpd.MPDClient()
-                    self.client.connect(self.host, self.port)
+                    self.client.connect(self.conf.host, self.conf.port)
                     return self.client
                 except Exception as e:
                     error = e
@@ -60,7 +69,7 @@ class MusicMpdPlugin(MusicPlugin):
                         e,
                         (': Retrying' if n_tries > 0 else ''),
                     )
-                    time.sleep(0.5)
+                    self.wait_stop(0.5)
 
         self.client = None
         if error:
@@ -83,7 +92,8 @@ class MusicMpdPlugin(MusicPlugin):
                     response = getattr(self.client, method)(*args, **kwargs)
 
                 if return_status:
-                    return self.status().output
+                    return self._status()
+
                 return response
             except Exception as e:
                 error = str(e)
@@ -145,7 +155,7 @@ class MusicMpdPlugin(MusicPlugin):
         return self._exec('play') if status in ('pause', 'stop') else None
 
     @action
-    def stop(self, *_, **__):
+    def stop(self, *_, **__):  # type: ignore
         """Stop playback"""
         return self._exec('stop')
 
@@ -185,6 +195,9 @@ class MusicMpdPlugin(MusicPlugin):
 
         :param vol: Volume value (range: 0-100).
         """
+        self.logger.warning(
+            'music.mpd.setvol is deprecated, use music.mpd.set_volume instead'
+        )
         return self.set_volume(vol)
 
     @action
@@ -404,6 +417,9 @@ class MusicMpdPlugin(MusicPlugin):
         :param value: Seek position in seconds, or delta string (e.g. '+15' or
             '-15') to indicate a seek relative to the current position
         """
+        self.logger.warning(
+            'music.mpd.seekcur is deprecated, use music.mpd.seek instead'
+        )
         return self.seek(value)
 
     @action
@@ -472,6 +488,24 @@ class MusicMpdPlugin(MusicPlugin):
         """
         return self._status()
 
+    def _current_track(self):
+        track = self._exec('currentsong', return_status=False)
+        if not isinstance(track, dict):
+            return None
+
+        if 'title' in track and (
+            'artist' not in track
+            or not track['artist']
+            or re.search('^https?://', track['file'])
+            or re.search('^tunein:', track['file'])
+        ):
+            m = re.match(r'^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
+            if m and m.group(1) and m.group(2):
+                track['artist'] = m.group(1)
+                track['title'] = m.group(2)
+
+        return track
+
     @action
     def currentsong(self):
         """
@@ -500,23 +534,7 @@ class MusicMpdPlugin(MusicPlugin):
                 "x-albumuri": "spotify:album:6q5KhDhf9BZkoob7uAnq19"
             }
         """
-
-        track = self._exec('currentsong', return_status=False)
-        if not isinstance(track, dict):
-            return None
-
-        if 'title' in track and (
-            'artist' not in track
-            or not track['artist']
-            or re.search('^https?://', track['file'])
-            or re.search('^tunein:', track['file'])
-        ):
-            m = re.match(r'^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
-            if m and m.group(1) and m.group(2):
-                track['artist'] = m.group(1)
-                track['title'] = m.group(2)
-
-        return track
+        return self._current_track()
 
     @action
     def playlistinfo(self):
@@ -589,6 +607,9 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.playlists`.
         """
+        self.logger.warning(
+            'music.mpd.listplaylists is deprecated, use music.mpd.get_playlists instead'
+        )
         return self.get_playlists()
 
     @action
@@ -611,6 +632,9 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.playlist`.
         """
+        self.logger.warning(
+            'music.mpd.listplaylist is deprecated, use music.mpd.get_playlist instead'
+        )
         return self._exec('listplaylist', name, return_status=False)
 
     @action
@@ -618,10 +642,15 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.playlist` with ``with_tracks=True``.
         """
+        self.logger.warning(
+            'music.mpd.listplaylistinfo is deprecated, use music.mpd.get_playlist instead'
+        )
         return self.get_playlist(name, with_tracks=True)
 
     @action
-    def add_to_playlist(self, playlist: str, resources: Union[str, Collection[str]]):
+    def add_to_playlist(
+        self, playlist: str, resources: Union[str, Collection[str]], **_
+    ):
         """
         Add one or multiple resources to a playlist.
 
@@ -640,6 +669,9 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.add_to_playlist`.
         """
+        self.logger.warning(
+            'music.mpd.playlistadd is deprecated, use music.mpd.add_to_playlist instead'
+        )
         return self.add_to_playlist(name, uri)
 
     @action
@@ -677,6 +709,9 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.remove_from_playlist`.
         """
+        self.logger.warning(
+            'music.mpd.playlistdelete is deprecated, use music.mpd.remove_from_playlist instead'
+        )
         return self.remove_from_playlist(name, pos)
 
     @action
@@ -684,6 +719,9 @@ class MusicMpdPlugin(MusicPlugin):
         """
         Deprecated alias for :meth:`.playlist_move`.
         """
+        self.logger.warning(
+            'music.mpd.playlistmove is deprecated, use music.mpd.playlist_move instead'
+        )
         return self.playlist_move(name, from_pos=from_pos, to_pos=to_pos)
 
     @action
@@ -816,7 +854,9 @@ class MusicMpdPlugin(MusicPlugin):
         )
 
     @action
-    def searchadd(self, filter: dict, *args, **kwargs):
+    def searchadd(
+        self, filter: dict, *args, **kwargs  # pylint: disable=redefined-builtin
+    ):
         """
         Free search by filter and add the results to the current playlist.
 
@@ -828,5 +868,16 @@ class MusicMpdPlugin(MusicPlugin):
             'searchadd', *filter_list, *args, return_status=False, **kwargs
         )
 
+    def main(self):
+        listener = None
+        if self.poll_interval is not None:
+            listener = MpdListener(self)
+            listener.start()
+
+        self.wait_stop()
+
+        if listener:
+            listener.join()
+
 
 # vim:sw=4:ts=4:et:
diff --git a/platypush/plugins/music/mpd/_conf.py b/platypush/plugins/music/mpd/_conf.py
new file mode 100644
index 00000000..313aa542
--- /dev/null
+++ b/platypush/plugins/music/mpd/_conf.py
@@ -0,0 +1,12 @@
+from dataclasses import dataclass
+
+
+@dataclass
+class MpdConfig:
+    """
+    MPD configuration
+    """
+
+    host: str = 'localhost'
+    port: int = 6600
+    password: str = ''
diff --git a/platypush/plugins/music/mpd/_listener.py b/platypush/plugins/music/mpd/_listener.py
new file mode 100644
index 00000000..0c0ea621
--- /dev/null
+++ b/platypush/plugins/music/mpd/_listener.py
@@ -0,0 +1,163 @@
+from dataclasses import dataclass, field
+from threading import Thread
+from typing import Optional
+
+from platypush.context import get_bus
+from platypush.message.event.music import (
+    MusicPlayEvent,
+    MusicPauseEvent,
+    MusicStopEvent,
+    NewPlayingTrackEvent,
+    PlaylistChangeEvent,
+    VolumeChangeEvent,
+    PlaybackConsumeModeChangeEvent,
+    PlaybackSingleModeChangeEvent,
+    PlaybackRepeatModeChangeEvent,
+    PlaybackRandomModeChangeEvent,
+)
+from platypush.plugins.music import MusicPlugin
+
+
+@dataclass
+class MpdStatus:
+    """
+    Data class for the MPD status.
+    """
+
+    state: Optional[str] = None
+    playlist: Optional[int] = None
+    volume: Optional[int] = None
+    random: Optional[bool] = None
+    repeat: Optional[bool] = None
+    consume: Optional[bool] = None
+    single: Optional[bool] = None
+    track: dict = field(default_factory=dict)
+
+
+class MpdListener(Thread):
+    """
+    Thread that listens/polls for MPD events and posts them to the bus.
+    """
+
+    def __init__(self, plugin: MusicPlugin, *_, **__):
+        from . import MusicMpdPlugin
+
+        super().__init__(name='platypush:mpd:listener')
+        assert isinstance(plugin, MusicMpdPlugin)
+        self.plugin: MusicMpdPlugin = plugin
+        self._status = MpdStatus()
+
+    @property
+    def logger(self):
+        return self.plugin.logger
+
+    @property
+    def bus(self):
+        return get_bus()
+
+    def wait_stop(self, timeout=None):
+        self.plugin.wait_stop(timeout=timeout)
+
+    def _process_events(self, status: dict, track: Optional[dict] = None):
+        state = status.get('state', '').lower()
+        evt_args = {'status': status, 'track': track, 'plugin_name': 'music.mpd'}
+
+        if state != self._status.state:
+            if state == 'stop':
+                self.bus.post(MusicStopEvent(**evt_args))
+            elif state == 'pause':
+                self.bus.post(MusicPauseEvent(**evt_args))
+            elif state == 'play':
+                self.bus.post(MusicPlayEvent(**evt_args))
+
+        if status.get('playlist') != self._status.playlist and self._status.playlist:
+            # XXX plchanges can become heavy with big playlists,
+            # PlaylistChangeEvent temporarily disabled
+            # changes = plugin.plchanges(last_playlist).output
+            # self.bus.post(PlaylistChangeEvent(changes=changes))
+            self.bus.post(PlaylistChangeEvent(plugin_name='music.mpd'))
+
+        if state == 'play' and track != self._status.track:
+            self.bus.post(NewPlayingTrackEvent(**evt_args))
+
+        if (
+            status.get('volume') is not None
+            and status.get('volume') != self._status.volume
+        ):
+            self.bus.post(VolumeChangeEvent(volume=int(status['volume']), **evt_args))
+
+        if (
+            status.get('random') is not None
+            and status.get('random') != self._status.random
+        ):
+            self.bus.post(
+                PlaybackRandomModeChangeEvent(
+                    state=bool(int(status['random'])), **evt_args
+                )
+            )
+
+        if (
+            status.get('repeat') is not None
+            and status.get('repeat') != self._status.repeat
+        ):
+            self.bus.post(
+                PlaybackRepeatModeChangeEvent(
+                    state=bool(int(status['repeat'])), **evt_args
+                )
+            )
+
+        if (
+            status.get('consume') is not None
+            and status.get('consume') != self._status.consume
+        ):
+            self.bus.post(
+                PlaybackConsumeModeChangeEvent(
+                    state=bool(int(status['consume'])), **evt_args
+                )
+            )
+
+        if (
+            status.get('single') is not None
+            and status.get('single') != self._status.single
+        ):
+            self.bus.post(
+                PlaybackSingleModeChangeEvent(
+                    state=bool(int(status['single'])), **evt_args
+                )
+            )
+
+    def _update_status(self, status: dict, track: Optional[dict] = None):
+        self._status = MpdStatus(
+            state=status.get('state', '').lower(),
+            playlist=status.get('playlist'),
+            volume=status.get('volume'),
+            random=status.get('random'),
+            repeat=status.get('repeat'),
+            consume=status.get('consume'),
+            single=status.get('single'),
+            track=track or {},
+        )
+
+    def run(self):
+        super().run()
+
+        while not self.plugin.should_stop():
+            try:
+                status = self.plugin._status()  # pylint: disable=protected-access
+                assert status and status.get('state'), 'No status returned'
+                if not (status and status.get('state')):
+                    self.wait_stop(self.plugin.poll_interval)
+                    break
+
+                track = self.plugin._current_track()  # pylint: disable=protected-access
+                self._process_events(status, track)
+                self._update_status(status, track)
+            except Exception as e:
+                self.logger.warning(
+                    'Could not retrieve the latest status: %s', e, exc_info=True
+                )
+            finally:
+                self.wait_stop(self.plugin.poll_interval)
+
+
+# vim:sw=4:ts=4:et:
diff --git a/platypush/plugins/music/mpd/manifest.yaml b/platypush/plugins/music/mpd/manifest.yaml
index 8e9f5a7d..fafe723a 100644
--- a/platypush/plugins/music/mpd/manifest.yaml
+++ b/platypush/plugins/music/mpd/manifest.yaml
@@ -1,5 +1,11 @@
 manifest:
-  events: {}
+  events:
+    - platypush.message.event.music.MusicPauseEvent
+    - platypush.message.event.music.MusicPlayEvent
+    - platypush.message.event.music.MusicStopEvent
+    - platypush.message.event.music.NewPlayingTrackEvent
+    - platypush.message.event.music.PlaylistChangeEvent
+    - platypush.message.event.music.VolumeChangeEvent
   install:
     apt:
       - python3-mpd