A MagicMirror² module that creates an MQTT device with Home Assistant autodiscovery.
Note: This module was greatly inspired by MMM-Remote-Control.
Example: MagicMirror entities in Home Assistant
When the MagicMirror starts, the first browser client to load the module will establish the MQTT connection and handle all communication with Home Assistant. This ensures only one active MQTT connection per MagicMirror instance, even if multiple browsers are open.
The module uses MQTT autodiscovery to automatically create entities in Home Assistant, allowing you to control your MagicMirror from the Home Assistant dashboard or automations without manual configuration.
- MQTT device integration with Home Assistant via autodiscovery
- Control MagicMirror monitor (on/off)
- Adjust MagicMirror brightness
- Control visibility of individual MagicMirror modules as switches
- Read and set the active MMM-ProfileSwitcher profile as a select entity
- Restart MagicMirror process via Home Assistant
- Execute custom bash commands/scripts via Home Assistant
In your terminal, go to your MagicMirror² Module folder and clone MMM-HomeAssistant:
cd ~/MagicMirror/modules
git clone https://github.com/ambarusa/MMM-HomeAssistant/
cd MMM-HomeAssistant
npm installcd ~/MagicMirror/modules/MMM-HomeAssistant
git pull
npm installTo use this module, add it to the modules array in the config/config.js file:
{
module: 'MMM-HomeAssistant',
config: {
mqttServer: 'mqtt://localhost',
mqttPort: 1883,
username: 'mqtt_username',
password: 'mqtt_password',
deviceName: 'My MagicMirror',
autodiscoveryTopic: 'homeassistant',
monitorControl: true,
brightnessControl: true,
moduleControl: true,
profileControl: true,
profiles: ['Photos', 'Work', 'Weather', 'Media', 'Night'],
monitorStatusCommand: 'xrandr --query | awk \'/Screen/ {print ($8 > 320) ? "true" : "false"}\'',
monitorOnCommand: 'xrandr -d :0 --output HDMI-1 --auto --rotate right',
monitorOffCommand: 'xrandr -d :0 --output HDMI-1 --off',
pm2ProcessName: 'mm',
refreshBrowser: true,
customCommands: [
{
name: 'Update MagicMirror',
command: 'bash ~/MagicMirror/modules/MMM-HomeAssistant/custom_commands/update_MM.sh'
}
]
}
},Using Wayland, the xrandr commands to pull monitor status and control on/off do not work. Replace the following in the 'config/config.js' file to use with wayland.
monitorStatusCommand: 'wlr-randr | awk \'/HDMI-A-2/ {found=1} found && /Enabled:/ {print ($2 == "yes") ? "true" : "false"; exit} END {if(!found) print "false"}\'',
monitorOnCommand: 'wlr-randr --output HDMI-A-2 --on',
monitorOffCommand: 'wlr-randr --output HDMI-A-2 --off',Example config for a Raspberry Pi 4B running MagicMirror server and client with PM2
| Option | Type | Default | Description |
|---|---|---|---|
mqttServer |
string | mqtt://localhost |
MQTT server address (e.g., mqtt://localhost). |
mqttPort |
int | 1883 |
MQTT port. |
username |
string | (none) | (Optional) MQTT username. If omitted, connects anonymously. |
password |
string | (none) | (Optional) MQTT password. |
deviceName |
string | My MagicMirror |
MQTT device name. |
autodiscoveryTopic |
string | homeassistant |
Autodiscovery topic for Home Assistant. |
monitorControl |
boolean | false |
Treat the display as an ON/OFF light entity. |
brightnessControl |
boolean | false |
Treat the display as a light entity with brightness. Enables monitorControl! |
monitorStatusCommand |
string | echo true |
Shell command to check the monitor status; must return true/false or 0/1 for correct operation. |
monitorOnCommand |
string | (none) | Shell command to turn on the monitor. |
monitorOffCommand |
string | (none) | Shell command to turn off the monitor. |
moduleControl |
boolean | true |
Make modules controllable as switch entities. |
profileControl |
boolean | false |
Expose the active MMM-ProfileSwitcher profile as a select entity. Requires profiles. See Profile control. |
profiles |
array | (none) | The profile names to offer, e.g. ["Photos", "Work", "Night"]. Required when profileControl is on. |
pm2ProcessName |
string | (none) | If set, allows MagicMirror to be restarted via Home Assistant. |
refreshBrowser |
boolean | true |
If enabled, will programmatically open and close a browser to refresh MagicMirror clients on other instances. |
customCommands |
array | (none) | Array of custom commands to execute. See Custom Commands section below. |
cleanupStaleEntities |
boolean | true |
Remove Home Assistant entities for modules that no longer exist. See Stale entities. |
Individual modules may also set haEntityId in their own config to pin their
Home Assistant entity name. See Naming module switches.
Entities will appear automatically in Home Assistant if MQTT autodiscovery is enabled. You can control your MagicMirror from the Home Assistant dashboard or automations.
With MMM-ProfileSwitcher installed,
profileControl: true publishes a select entity for the active profile:
select.my_magicmirror_profile
{
module: "MMM-HomeAssistant",
config: {
profileControl: true,
profiles: ["Photos", "Work", "Weather", "Media", "Night"],
}
}The names must match the profile class names MMM-ProfileSwitcher uses, because Home Assistant only accepts options from this list. There is no way to discover them automatically - a profile is only ever a class name on some module.
Selecting an option sends CURRENT_PROFILE, which is the same notification the
on-screen profile buttons send, so both routes behave identically.
The state is read back from MMM-ProfileSwitcher's CHANGED_PROFILE
notification, which it emits for every change including the one it makes at
startup. The entity therefore reflects what the mirror is really showing, even
when the profile was changed by a button on the mirror rather than from Home
Assistant. That is what makes it usable as a trigger in automations.
If profileControl is on and profiles is empty, the entity is skipped and an
error is logged; everything else keeps working.
By default each module gets an entity named after the module, with a numeric suffix when several instances of the same module exist:
switch.my_magicmirror_clock_switch
switch.my_magicmirror_homeassistantsensors_1_switch
switch.my_magicmirror_homeassistantsensors_2_switch
Those generated names depend on how many instances exist and in what order.
Add or remove one module and the suffixes shift, so an entity that used to be
..._2_switch may become ..._3_switch - and the old one is left behind in
Home Assistant. The same happens when a count drops to one, because the suffix
is then dropped entirely.
To pin an entity to a module regardless of ordering, set haEntityId in that
module's own config:
{
module: "MMM-homeassistant-sensors",
position: "top_right",
config: {
haEntityId: "weather_station",
// ... the module's own options
}
}which gives switch.my_magicmirror_weather_station_switch.
The key is read straight from the module's config and ignored by the module itself, so it is safe to add to any module.
Rules:
- The value must be unique across all modules. A duplicate is logged as an error and that module falls back to the generated name.
- It is lowercased and anything outside
a-z,0-9and_becomes_, since the value ends up in both the MQTT topic and the entity id. - Pin all instances of a module type, or none. Pinning one makes its unpinned siblings renumber from 1, which renames their entities.
Discovery configs are published retained, so Home Assistant keeps recreating an
entity even after the module is gone from config.js. On every connect this
module compares what it just published against what is retained for this device
and clears the difference by publishing an empty retained payload, which tells
Home Assistant to drop the entity.
Only this device's per-module switch configs are pruned. The device light and
the button entities are never touched, and nothing is pruned on a run where the
module list was not available - otherwise a startup race could wipe every
entity.
It is attempted once per process, whatever the outcome. The client reconnects on its own, so retrying would turn a single failure into a loop: publish, fail, reconnect, find the same topics, publish again. Worst case is now one error at startup.
Set cleanupStaleEntities: false to turn it off. Worth doing if the log shows
the removals failing on every start. Two causes are known:
- A broker with an ACL on the discovery prefix refuses the empty publishes.
- On at least one arm64 host (Raspberry Pi 4, Debian 13, Node 22, mqtt 5.13)
the write fails with
EFAULTout ofmqtt-packet'suncork. The same publishes from a standalone script on the same machine and broker succeed, so it is something about the client this module holds rather than the platform, but it is not yet understood. x86_64 hosts are unaffected.
You can define custom bash commands or scripts that Home Assistant can trigger. Each command will appear as a button entity in Home Assistant.
The sample script at custom_commands/update_MM.sh performs a full MagicMirror update cycle with logging and safety checks. In short, it:
- Rotates its log every 5 runs and writes to
custom_commands/update_MM.log. - Uses a lock file to prevent concurrent runs.
- Updates the MagicMirror base repo with
git pull, then runsnpm run install-mmif changes were pulled. - Updates every
MMM-*module undermodules/withgit pullandnpm installwhen needed. - Restarts MagicMirror with
pm2 restartif updates were applied (configurable viaPM2_PROCESS_NAME, defaultmm).
Notes:
- The script does a
git reset --hard HEADin the base repo and in each module to avoid conflicts. If you keep local changes, they will be discarded. - You can pass a MagicMirror path as the first argument; otherwise it defaults to
~/MagicMirror. - If
pm2is not installed, the script will skip restart and log a reminder.
Add a customCommands array to your config:
customCommands: [
{
name: 'Update MagicMirror',
command: 'bash ~/MagicMirror/modules/MMM-HomeAssistant/custom_commands/update_MM.sh'
},
{
name: 'Say Hello',
command: 'echo "Hello from MagicMirror!"'
}
]| Property | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name for the command in Home Assistant. Internal name will be automatically converted to lowercase with underscores. |
command |
string | Yes | Bash command or script path to execute. |
- Open your browser's developer console to check for JavaScript errors or warnings.
- Check the MagicMirror logs for errors or warnings (run
npm startnpm run serverorpm2 restart xx; pm2 logs xxfrom your MagicMirror directory and watch the terminal output). - Use MQTT Explorer or a similar tool to easily investigate MQTT messages and topics.
- Optionally, you can temporarily change the
autodiscoveryTopicin your config to something likedebugto see what messages are intended to be sent for Home Assistant autodiscovery. - If the entities still look healthy in Home Assistant but nothing you do there reaches the mirror, check whether the mirror is connected to the broker at all -
ss -tn state established | grep 1883on the mirror. An empty answer means the module is not talking to MQTT, whatever Home Assistant shows, and the mirror will keep serving its own web interface as if nothing were wrong.
npm install- Install devDependencies like ESLint.npm run lint- Run linting and formatter checks.npm run lint:fix- Fix linting and formatter issues.