diff --git a/mautrix_facebook/commands/__init__.py b/mautrix_facebook/commands/__init__.py
index c7420e6ad883145fefe0b64e859ec02eacb2c802..6b11117d3feea72315da4ffe8a640f8712cf7a77 100644
--- a/mautrix_facebook/commands/__init__.py
+++ b/mautrix_facebook/commands/__init__.py
@@ -1,4 +1,4 @@
 from .handler import (CommandProcessor, CommandHandler, CommandEvent, command_handler,
-                      SECTION_AUTH, SECTION_MISC)
-from .auth import login, enter_2fa_code
-from .facebook import search
+                      SECTION_AUTH, SECTION_MISC, SECTION_CONNECTION)
+from .auth import enter_2fa_code
+from . import facebook as _, conn as _
diff --git a/mautrix_facebook/commands/auth.py b/mautrix_facebook/commands/auth.py
index 400c6939f414ef964bc7c485487b1b6addfc663d..bd9f0a4786702be0f6921a18de4d5951ed580419 100644
--- a/mautrix_facebook/commands/auth.py
+++ b/mautrix_facebook/commands/auth.py
@@ -176,10 +176,3 @@ async def logout_matrix(evt: CommandEvent) -> None:
     await puppet.switch_mxid(None, None)
     await evt.reply("Restored the original puppet for your Facebook Messenger account")
 
-
-@command_handler(needs_auth=False, management_only=True, help_section=SECTION_AUTH,
-                 help_text="Mark this room as your bridge notice room")
-async def set_notice_room(evt: CommandEvent) -> None:
-    evt.sender.notice_room = evt.room_id
-    evt.sender.save()
-    await evt.reply("This room has been marked as your bridge notice room")
diff --git a/mautrix_facebook/commands/conn.py b/mautrix_facebook/commands/conn.py
new file mode 100644
index 0000000000000000000000000000000000000000..1070eb727488dc6e634efcd974ce72b54d996e75
--- /dev/null
+++ b/mautrix_facebook/commands/conn.py
@@ -0,0 +1,79 @@
+# mautrix-facebook - A Matrix-Facebook Messenger puppeting bridge
+# Copyright (C) 2020 Tulir Asokan
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+from typing import cast
+
+import fbchat
+
+from . import command_handler, CommandEvent, SECTION_CONNECTION
+
+
+@command_handler(needs_auth=False, management_only=True, help_section=SECTION_CONNECTION,
+                 help_text="Mark this room as your bridge notice room")
+async def set_notice_room(evt: CommandEvent) -> None:
+    evt.sender.notice_room = evt.room_id
+    evt.sender.save()
+    await evt.reply("This room has been marked as your bridge notice room")
+
+
+@command_handler(needs_auth=True, management_only=True, help_section=SECTION_CONNECTION,
+                 help_text="Disconnect from Facebook Messenger")
+async def disconnect(evt: CommandEvent) -> None:
+    if not evt.sender.listener:
+        await evt.reply("You don't have a Messenger MQTT connection")
+        return
+    evt.sender.listener.disconnect()
+
+
+@command_handler(needs_auth=True, management_only=True, help_section=SECTION_CONNECTION,
+                 help_text="Connect to Facebook Messenger")
+async def connect(evt: CommandEvent) -> None:
+    if evt.sender.listen_task and not evt.sender.listen_task.done():
+        await evt.reply("You already have a Messenger MQTT connection")
+        return
+    evt.sender.listen_task = evt.loop.create_task(evt.sender.try_listen())
+
+
+@command_handler(needs_auth=True, management_only=True, help_section=SECTION_CONNECTION,
+                 help_text="Check if you're logged into Facebook Messenger")
+async def ping(evt: CommandEvent) -> None:
+    if not evt.sender.is_logged_in():
+        await evt.reply("You're not logged into Facebook Messenger")
+        return
+    own_info = cast(fbchat.User,
+                    await evt.sender.client.fetch_thread_info([evt.sender.fbid]).__anext__())
+    await evt.reply(f"You're logged in as {own_info.name} (user ID {own_info.id})")
+
+    if not evt.sender.listen_task or evt.sender.listen_task.done():
+        await evt.reply("You don't have a Messenger MQTT connection. Use `connect` to connect.")
+    elif not evt.sender._is_connected:
+        await evt.reply("The Messenger MQTT listener is **disconnected**.")
+    else:
+        await evt.reply("The Messenger MQTT listener is connected.")
+
+
+@command_handler(needs_auth=True, management_only=True, help_section=SECTION_CONNECTION,
+                 help_text="\"Refresh\" the Facebook Messenger page")
+async def refresh(evt: CommandEvent) -> None:
+    if evt.sender.listener:
+        await evt.reply("Disconnecting Messenger MQTT connection")
+        evt.sender.listener.disconnect()
+        await evt.sender.listen_task
+    await evt.reply("Refreshing session")
+    ok = await evt.sender.load_session(_override=True)
+    if ok:
+        await evt.reply("Successfully refreshed session")
+    else:
+        await evt.reply("Failed to refresh session")
diff --git a/mautrix_facebook/commands/handler.py b/mautrix_facebook/commands/handler.py
index 773b4163c34a986f76fd606296b4ea4f94fc989f..12b7e3bfe6f2b2b75d6e86bfa02c2577d71a41e6 100644
--- a/mautrix_facebook/commands/handler.py
+++ b/mautrix_facebook/commands/handler.py
@@ -24,6 +24,7 @@ from .. import user as u, context as c
 HelpCacheKey = NamedTuple('FBHelpCacheKey', is_management=bool, is_admin=bool, is_logged_in=bool)
 
 SECTION_AUTH = HelpSection("Authentication", 10, "")
+SECTION_CONNECTION = HelpSection("Connection management", 15, "")
 SECTION_CREATING_PORTALS = HelpSection("Creating portals", 20, "")
 SECTION_PORTAL_MANAGEMENT = HelpSection("Portal management", 30, "")
 SECTION_MISC = HelpSection("Miscellaneous", 40, "")
diff --git a/mautrix_facebook/user.py b/mautrix_facebook/user.py
index 37fc149093eb83501babd6c90b77db1f87f4a91a..d8e8e55570a77d9f7d87c2843feadc71c00898bd 100644
--- a/mautrix_facebook/user.py
+++ b/mautrix_facebook/user.py
@@ -50,6 +50,7 @@ class User(BaseUser):
     is_admin: bool
     permission_level: str
     _is_logged_in: Optional[bool]
+    _is_connected: Optional[bool]
     _session_data: Optional[Dict[str, str]]
     _db_instance: Optional[DBUser]
 
@@ -66,6 +67,7 @@ class User(BaseUser):
         self.command_status = None
         self.is_whitelisted, self.is_admin, self.permission_level = config.get_permissions(mxid)
         self._is_logged_in = None
+        self._is_connected = None
         self._session_data = session
         self._db_instance = db_instance
         self._community_id = None
@@ -142,8 +144,8 @@ class User(BaseUser):
 
         return None
 
-    async def load_session(self) -> bool:
-        if self._is_logged_in:
+    async def load_session(self, _override: bool = False) -> bool:
+        if self._is_logged_in and not _override:
             return True
         elif not self._session_data:
             return False
@@ -156,6 +158,7 @@ class User(BaseUser):
             self.log.info("Loaded session successfully")
             self.session = session
             self.client = fbchat.Client(session=self.session)
+            self._is_logged_in = True
             if self.listen_task:
                 self.listen_task.cancel()
             self.listen_task = self.loop.create_task(self.try_listen())
@@ -183,8 +186,10 @@ class User(BaseUser):
                 ok = False
         self._session_data = None
         self._is_logged_in = False
+        self._is_connected = None
         self.client = None
         self.session = None
+        self.listener = None
         self.save(_update_session_data=False)
         return ok
 
@@ -327,6 +332,7 @@ class User(BaseUser):
         try:
             await self.listen()
         except Exception:
+            self._is_connected = False
             await self.send_bridge_notice("Fatal error in listener (see logs for more info)")
             self.log.exception("Fatal error in listener")
             try:
@@ -335,7 +341,8 @@ class User(BaseUser):
                 self.log.debug("Error disconnecting listener after error", exc_info=True)
 
     async def listen(self) -> None:
-        self.listener = fbchat.Listener(session=self.session, chat_on=True, foreground=False)
+        if not self.listener:
+            self.listener = fbchat.Listener(session=self.session, chat_on=True, foreground=False)
         handlers: Dict[Type[fbchat.Event], Callable[[Any], Awaitable[None]]] = {
             fbchat.MessageEvent: self.on_message,
             fbchat.MessageReplyEvent: self.on_message,
@@ -362,11 +369,15 @@ class User(BaseUser):
                     await handler(event)
                 except Exception:
                     self.log.exception("Failed to handle facebook event")
+        self._is_connected = False
+        await self.send_bridge_notice("Facebook Messenger connection closed without error")
 
     async def on_connect(self, evt: fbchat.Connect) -> None:
+        self._is_connected = True
         await self.send_bridge_notice("Connected to Facebook Messenger")
 
     async def on_disconnect(self, evt: fbchat.Disconnect) -> None:
+        self._is_connected = False
         await self.send_bridge_notice(f"Disconnected from Facebook Messenger: {evt.reason}")
 
     def stop_listening(self) -> None: