Good evening to all.
Today was an interesting day - caused by an issue posted by Stefano ( papinist )
He opened issue # 10 . with a friendly, well-argued suggestion: expose the mirror screen to Home Assistant as a proper switch — something you can both see (is the screen on?) and control (turn it on/off) as a native HA entity.
The nice realisation while designing it was that this needs almost nothing new. PSC already speaks MQTT — it already subscribes to a presence topic so that MQTT occupancy sensors can drive the screen. So Home Assistant is simply another presence source, exactly like a radar or mmWave sensor publishing occupancy. The only piece genuinely missing was the reverse direction: a topic the mirror writes, carrying its own screen state. No extra module, no shell scripts, no IPC — just publish one more thing and let HA subscribe to it.
The design
The result is an optional homeAssistant config block. Enable it and PSC opens a dedicated MQTT connection (independent of mode, so it works even in pure PIR setups), reusing the broker and credentials you already configured:
homeAssistant: {
enabled: false, // opt-in, default off → no surprise
discovery: true, // publish the HA auto-discovery config
discoveryPrefix: "homeassistant",// must match your HA MQTT discovery prefix
objectId: "magicmirror_screen", // technical id → topic paths + HA unique_id
name: "MagicMirror Screen" // friendly name shown in HA
}
A few decisions worth flagging, because each is a small opinion:
It’s a switch, not a button — so ON latches. ON sets a held presence signal: the screen stays on for as long as the switch is on. OFF releases it, and the mirror returns to standby through your normal counterTimeout — the same graceful countdown a PIR walk-away uses. There’s deliberately no separate “instant off”; if you want a snappy off, lower counterTimeout.
It never reaches into the module’s own logic. The integration adds exactly one OR-term to the presence evaluation — haPresence alongside the existing PIR / MQTT / touch sources — and it sits below the cron windows in precedence. The scheduler still wins: an ON during a cronIgnoreWindows is swallowed, an OFF during a cronAlwaysOnWindows is overridden. Timer, dimming and window logic are untouched.
The switch is a strict mirror of the real screen. The state topic always publishes the actual screen state, whatever caused the change — PIR, schedule, touch or HA — and it re-publishes after every command. So when the schedule rejects or overrides a command, the switch snaps back to reality rather than lying: HA can never show “on” while the screen is off, or vice versa. A command is a request; the fact is whatever the mirror actually does.
Auto-discovery + availability, so setup is effortless. On connect PSC publishes a retained Home Assistant MQTT-Discovery config and an availability topic backed by a Last-Will. HA creates the entity automatically and marks it unavailable if the mirror drops off the broker.
The topics, all derived from objectId:
homeassistant/switch/magicmirror_screen/config (retained) → discovery
magicmirror/magicmirror_screen/set → command (HA → mirror: ON/OFF)
magicmirror/magicmirror_screen/state (retained) → state (mirror → HA: ON/OFF)
magicmirror/magicmirror_screen/availability (retained) → online/offline (LWT)
How the auto-creation works
The mirror and Home Assistant never talk to each other directly — the broker is the only meeting point, and the discoveryPrefix (homeassistant) is the agreed channel. HA’s MQTT integration subscribes to homeassistant/# and listens for anyone announcing themselves there. Because our config is published retained, the broker hands it to HA the instant HA connects — even if HA boots hours later. HA reads the topic path plus the JSON body and builds switch.magicmirror_screen, wired to the command/state/availability topics. It’s the same mechanism Tasmota, Zigbee2MQTT and ESPHome use — the mirror just follows the convention, so it’s picked up like any other device.
An example automation (doorbell → wake → graceful standby)
automation:
- alias: "Doorbell wakes the MagicMirror"
trigger:
- platform: state
entity_id: binary_sensor.video_doorbell
to: "on"
action:
- service: switch.turn_on
target: { entity_id: switch.magicmirror_screen }
- delay: "00:00:30" # keep the camera visible while you look
- service: switch.turn_off
target: { entity_id: switch.magicmirror_screen }
Turn it on when the doorbell rings, keep it visible for half a minute, then hand back to PSC’s timer for the standby — or drop the delay/off and turn it off from another trigger, whatever fits your flow.
A note on testing
I don’t run Home Assistant here, so I verified the whole MQTT surface with MQTT Explorer playing HA’s role — publishing ON/OFF to the command topic and watching the discovery, state and availability topics. The entire round-trip is observable that way: ON holds, OFF starts the countdown, the state tracks the real screen (including snapping back when the schedule overrides a command), the discovery config is well-formed, availability goes online on start. The one thing that needs a real instance — does HA actually auto-create the entity from that config — is being confirmed by papinist, who has the setup for it.
Update / install
If you’re already on the module:
cd ~/MagicMirror/modules/MMM-PresenceScreenControl
git pull
No dependency changes this time, so no npm install is needed — just restart MagicMirror. Everything is backwards-compatible and opt-in: homeAssistant.enabled defaults to false, and the rest is additive, so an update touches nothing in your existing setup until you switch it on.
A genuine thank-you to papinist for the friendly, well-reasoned suggestion and the pleasantconversation that shaped it. Exactly the kind of exchange that makes maintaining a module worthwhile.
Hope you find it useful.
Warmest regards,
Ralf