Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| hardware:baby-mirror [2026/07/21 08:22] – Hayden | hardware:baby-mirror [2026/08/25 19:17] (current) – Sparky | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| # Baby Mirror | # Baby Mirror | ||
| - | Donated to us by [/dev/hack](https://devhack.net), thanks to the folks over there! | + | The Baby Mirror is a [MagicMirror²](https://magicmirror.builders/) smart mirror: |
| + | a Raspberry Pi driving a monitor mounted behind one-way glass. It was donated to | ||
| + | DMA by /dev/hack, thanks to the folks over there! | ||
| + | the space, including a feed of community quotes. | ||
| - | It sits on top of the tool chest. There' | + | ## Where it is |
| - | The mirror is a Raspberry Pi with a display driver board. The mirror itself is a monitor. | + | Currently sitting on top of the tool chest. Planned to be wall-mounted above the |
| + | chest. | ||
| - | ## Accessing | + | ## Hardware and software |
| + | |||
| + | - **Board:** Raspberry Pi 3B+ (1 GB RAM) | ||
| + | - **Display: | ||
| + | - **OS:** Debian 13 (Trixie), 64-bit | ||
| + | - **MagicMirror: | ||
| + | - **Networking: | ||
| + | |||
| + | Note that the Pi has been updated since it was donated. It is no longer running | ||
| + | the original /dev/hack image, so do not assume the stock /dev/hack layout or | ||
| + | tooling. Paths and service setup are documented below and were confirmed on the | ||
| + | device on 2026-08-25. | ||
| + | |||
| + | ## Finding the mirror on the network | ||
| + | |||
| + | The mirror is on the DMA **user VLAN** (the `dma.space/ | ||
| + | must be on that network to reach it. Its address comes from DHCP and changes. | ||
| + | |||
| + | **The hostname does not resolve.** `smartmirror.user.dma.computer` will not | ||
| + | resolve even when you are on the user VLAN, because every VLAN's DHCP hands out | ||
| + | public DNS (1.1.1.1 and 1.0.0.1) and the router does not run an internal | ||
| + | resolver. Do not rely on the hostname. mDNS (`smartmirror.local`) did not answer | ||
| + | either when this was last checked. | ||
| + | |||
| + | To find its current address, use one of: | ||
| + | |||
| + | 1. The router' | ||
| + | 2. A sweep of the user VLAN, then look for a host answering SSH with a Debian | ||
| + | | ||
| + | 3. A keyboard on the Pi itself and `hostname -I`. | ||
| + | |||
| + | ## Access | ||
| + | |||
| + | - **SSH user:** `localadmin` | ||
| + | - **Password: | ||
| + | `smartmirror` item. That item lived in a personal vault until 2026-08-24 and | ||
| + | is now shared to the org, so if you cannot find it, check that you are looking | ||
| + | at the org vault. *(Do not paste credentials on this page, the wiki is | ||
| + | public.)* | ||
| + | |||
| + | ``` | ||
| + | ssh localadmin@< | ||
| + | ``` | ||
| + | |||
| + | SSH host key fingerprints (verify on first connect): | ||
| + | |||
| + | ``` | ||
| + | 256 SHA256: | ||
| + | 256 SHA256: | ||
| + | 3072 SHA256: | ||
| + | ``` | ||
| + | |||
| + | All three were confirmed correct on 2026-08-25. | ||
| + | |||
| + | ## Console access | ||
| + | |||
| + | If the mirror is off the network, you can get a local console with a USB | ||
| + | keyboard: | ||
| + | |||
| + | - **Ctrl+Alt+F2** switches to a text console. | ||
| + | - **Ctrl+Alt+F7** switches back to the MagicMirror display. | ||
| + | |||
| + | Two things to know before you rely on this. The console keymap is not US and has | ||
| + | dead keys, so quotes and symbols are hard to type correctly, and `loadkeys` | ||
| + | cannot help because `/ | ||
| + | quotes or a password is much easier over SSH. Also remember to log out of the | ||
| + | console when you are done, since the space is shared. | ||
| + | |||
| + | ## If the mirror is on but shows no data | ||
| + | |||
| + | A module reporting "no internet connection" | ||
| + | dropped off wifi. This happened on 2026-08-25 and the device had been offline | ||
| + | long enough that nobody noticed. | ||
| + | |||
| + | Wifi is managed by **NetworkManager**, | ||
| + | There is no `/ | ||
| + | `wpa_cli` network blocks will not work, because NetworkManager owns the | ||
| + | supplicant. | ||
| + | |||
| + | From a console on the Pi, the fix that worked was simply restarting the service: | ||
| + | |||
| + | ```bash | ||
| + | sudo systemctl restart NetworkManager | ||
| + | ``` | ||
| + | |||
| + | It reconnected and picked up a DHCP lease immediately. If that is not enough: | ||
| + | |||
| + | ```bash | ||
| + | nmcli device status | ||
| + | sudo nmcli device wifi rescan | ||
| + | nmcli device wifi list | ||
| + | sudo nmcli --ask device wifi connect dma.space/ | ||
| + | ``` | ||
| + | |||
| + | `nmcli device wifi connect` can report that the network cannot be found when the | ||
| + | scan cache is stale, so always rescan first. Using `--ask` prompts for the | ||
| + | password instead of taking it on the command line, which avoids both shell | ||
| + | quoting and the console keymap problem. | ||
| + | |||
| + | ## How MagicMirror is managed | ||
| + | |||
| + | MagicMirror runs as a **systemd user service** under the `localadmin` account. | ||
| + | The unit lives at `~/ | ||
| + | boot, and starts `/ | ||
| + | the `/dev/hack` start script are not alternatives to each other; the unit runs | ||
| + | the script. | ||
| + | |||
| + | ```bash | ||
| + | systemctl --user status magicmirror.service | ||
| + | systemctl --user restart magicmirror.service | ||
| + | ``` | ||
| + | |||
| + | **Logs.** `journalctl --user` does not work on this device and reports "No | ||
| + | journal files were found", | ||
| + | either of these instead: | ||
| + | |||
| + | ```bash | ||
| + | systemctl --user status magicmirror.service --no-pager | ||
| + | sudo journalctl _UID=1000 -n 40 --no-pager | ||
| + | ``` | ||
| + | |||
| + | ## Configuration | ||
| + | |||
| + | MagicMirror config lives at: | ||
| + | |||
| + | ``` | ||
| + | / | ||
| + | ``` | ||
| + | |||
| + | Modules are configured in the `modules: [ ... ]` array in that file. Installed | ||
| + | modules live under `/ | ||
| + | |||
| + | **Always back up and validate before restarting.** A malformed `config.js` stops | ||
| + | MagicMirror from starting and leaves the mirror blank. | ||
| + | |||
| + | ```bash | ||
| + | cd / | ||
| + | cp config.js config.js.bak-$(date +%F) | ||
| + | # edit, then check that it parses before restarting: | ||
| + | node -e ' | ||
| + | systemctl --user restart magicmirror.service | ||
| + | ``` | ||
| + | |||
| + | To roll back: | ||
| + | |||
| + | ```bash | ||
| + | cp config.js.bak-< | ||
| + | systemctl --user restart magicmirror.service | ||
| + | ``` | ||
| + | |||
| + | ## Displaying the community quotes feed | ||
| + | |||
| + | Quotes come from `#.offtopic > quotes` in Zulip, published as an Atom feed: | ||
| + | |||
| + | ``` | ||
| + | https:// | ||
| + | ``` | ||
| + | |||
| + | The feed is public and needs no token. Confirm the mirror can reach it before | ||
| + | relying on it, and **test with GET, not HEAD**. The endpoint is GET-only and | ||
| + | `curl -I` returns 405 even when the feed is perfectly healthy: | ||
| + | |||
| + | ```bash | ||
| + | curl -sS -o /dev/null -w ' | ||
| + | ``` | ||
| + | |||
| + | The display uses MagicMirror' | ||
| + | [`newsfeed`](https:// | ||
| + | This is the block currently in `config.js`: | ||
| + | |||
| + | ```js | ||
| + | { | ||
| + | module: " | ||
| + | position: " | ||
| + | config: { | ||
| + | feeds: [ | ||
| + | { | ||
| + | title: "DMA Quotes", | ||
| + | url: " | ||
| + | } | ||
| + | ], | ||
| + | showSourceTitle: | ||
| + | showPublishDate: | ||
| + | showDescription: | ||
| + | } | ||
| + | }, | ||
| + | ``` | ||
| + | |||
| + | `showDescription` is deliberately `false`. Each entry carries the whole quote in | ||
| + | its `< | ||
| + | to show. For the same reason, do not add `ignoreOldItems`: | ||
| + | infrequently and the newest entry is often weeks old, so filtering by age would | ||
| + | empty the bar. | ||
| + | |||
| + | The large centred text on the mirror is the separate `compliments` module, not | ||
| + | the quotes feed. | ||
| + | |||
| + | ## Editing safely | ||
| + | |||
| + | This is a **production device** on the space' | ||
| + | changes, back up `config.js` first, validate that it parses, and restart the | ||
| + | service to apply. If the mirror comes up blank after a change, restore the | ||
| + | backup and restart. | ||
| + | |||
| + | ## Known issues | ||
| + | |||
| + | - The wifi dropout described above has not been tested across a reboot. The | ||
| + | NetworkManager profile should persist, but a silent recurrence is possible. | ||
| + | If the mirror goes dark again, check wifi first. | ||
| - | Poke @hadn69 | ||
| - | TODO make poking hadn69 not required | ||