Files
ansible/roles/pi_kiosk/tasks/main.yml
Martin Bauer 831a1b50a4 Run the kiosk on Chromium, and take the stray media server off musicdolphin
Chromium: same page, same machine, idle, Firefox 24.2% of a core against Chromium's
11.4%. The build comes from archive.raspberrypi.com (a "+rpt" version) and is patched
for this board's V3D GPU; Debian ships a much older chromium, so the archive matters.
The whole userland here is 32-bit armhf on a 64-bit kernel, which Firefox handles worse
than Chromium does. --use-gl=egl, --enable-gpu-rasterization, --ignore-gpu-blocklist
and --enable-zero-copy are what keep rasterization on the GPU: Chromium's blocklist
does not recognise this driver and silently falls back to software without them.
pi_kiosk_browser: firefox still works, as the fallback if an update ever regresses.

Debug mode also gains xdotool, scrot and --remote-debugging-port=9222 (on 127.0.0.1),
because firing XTEST key events at a window lands about half the time and a benchmark
you cannot verify the state of is worse than none.

pi_squeezeserver gains an absent path, and mediapis.yml derives its state from
mediapi_has_squeezeserver, which defaults to false for the whole group. false means
"actively remove", not "skip": musicdolphin has been running a Logitech Media Server
that no playbook installs and nothing talks to - every squeezelite in the fleet points
at pi_squeezelite_squeezeserver (192.168.178.80, the server) - while costing ~30 MB
resident and ~47 MB of swap on a 2 GB Pi that also drives the kiosk. The absent path
stops it, purges the package, unregisters it from sysdweb, drops the port 80 -> 9000
redirect and deletes its database and logs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:13:16 +02:00

146 lines
5.5 KiB
YAML

---
- name: Packages
ansible.builtin.apt:
name:
- xserver-xorg
- xinit
- x11-xserver-utils
# Without a window manager, a browser's request to go full-screen (which is all
# --kiosk does) has nobody to answer it, and it settles for its default window
# size in the corner of the screen. Openbox answers it, and nothing else.
- openbox
- unclutter
cache_valid_time: 7200
- name: Browser
# Chromium comes from archive.raspberrypi.com, not Debian: that build carries the
# "+rpt" suffix and Raspberry Pi's own patches for the V3D GPU on this board. Debian
# also ships a much older `chromium`, so pinning the wrong archive here would quietly
# undo the reason for choosing it.
ansible.builtin.apt:
name: "{{ 'chromium' if pi_kiosk_browser == 'chromium' else 'firefox-esr' }}"
cache_valid_time: 7200
notify: Reload systemd and restart getty
- name: Debugging tools for the attached screen
# Only pulled in for pi_kiosk_mode=debug; apt does not take them away again when the
# host goes back to kiosk mode, which is fine - they are inert and small.
ansible.builtin.apt:
name:
- xterm # a terminal that is ours, whatever x-terminal-emulator points at
- x11-utils # xwininfo, xprop: is the window actually the size of the screen?
- mesa-utils # glxinfo, glxgears: is anything accelerated, or is this llvmpipe?
- xdotool # drive the UI from ssh, so a measurement can be taken on a real
# screen rather than an idle one
- scrot # and see what that screen actually shows, over ssh
cache_valid_time: 7200
when: pi_kiosk_mode == 'debug'
- name: Create kiosk user
ansible.builtin.user:
name: "{{ pi_kiosk_user }}"
groups: [video, input, tty]
shell: /bin/bash
create_home: true
- name: Own the directories X and Firefox write into
# Xorg runs as root here (Debian's wrapper, allowed_users=console) with HOME set to
# the kiosk user's, so it creates ~/.cache itself - root-owned and 0700 - the first
# time it writes a mesa shader cache. Firefox then cannot create ~/.cache/mozilla and
# gives up with "Your Firefox profile cannot be loaded. It may be missing or
# inaccessible". Creating these up front, owned by the user, means root only ever
# adds subdirectories to a directory the user already owns.
ansible.builtin.file:
path: "/home/{{ pi_kiosk_user }}/{{ item }}"
state: directory
owner: "{{ pi_kiosk_user }}"
group: "{{ pi_kiosk_user }}"
mode: "0700"
loop:
- .cache
- .mozilla
- name: Autologin the kiosk user on tty1
ansible.builtin.file:
path: /etc/systemd/system/getty@tty1.service.d
state: directory
mode: "0755"
- name: Install getty autologin override
ansible.builtin.template:
src: autologin.conf.j2
dest: /etc/systemd/system/getty@tty1.service.d/autologin.conf
mode: "0644"
notify: Reload systemd and restart getty
- name: Enable the autologin getty on tty1
# Enabling it is the part that survives a reboot. These images do not enable
# getty@tty1 themselves - getty.target pulls in getty-static.service and nothing
# else - so without this the drop-in above is installed, the handler starts the unit
# once, and the kiosk then silently stops coming up after the next reboot.
ansible.builtin.systemd:
name: "getty@tty1"
enabled: true
state: started
daemon_reload: true
- name: Install .bash_profile (starts X on tty1 login)
ansible.builtin.template:
src: bash_profile.j2
dest: "/home/{{ pi_kiosk_user }}/.bash_profile"
owner: "{{ pi_kiosk_user }}"
group: "{{ pi_kiosk_user }}"
mode: "0644"
notify: Reload systemd and restart getty
- name: Install .xinitrc (starts Openbox, then the browser)
ansible.builtin.template:
src: xinitrc.j2
dest: "/home/{{ pi_kiosk_user }}/.xinitrc"
owner: "{{ pi_kiosk_user }}"
group: "{{ pi_kiosk_user }}"
mode: "0755"
notify: Reload systemd and restart getty
- name: Ensure the browser policy directory exists
ansible.builtin.file:
path: "{{ '/etc/chromium/policies/managed' if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr' }}"
state: directory
mode: "0755"
# The packages normally create these; a backstop in case a layout changes
- name: Install the browser's enterprise policy
# Everything that is a setting rather than a startup flag: no sign-in, no sync, no
# metrics, no password manager, no translate bar. The flags themselves are in
# .xinitrc, where they have to be.
ansible.builtin.copy:
src: "{{ 'chromium-policies.json' if pi_kiosk_browser == 'chromium' else 'policies.json' }}"
dest: >-
{{ '/etc/chromium/policies/managed/kiosk.json'
if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr/policies.json' }}
mode: "0644"
notify: Reload systemd and restart getty
- name: Ensure the Openbox config directories exist
# Both levels explicitly, and owned by the user: a file: task creates missing parents
# as root, and a root-owned ~/.config is the same trap that broke the Firefox profile
# above.
ansible.builtin.file:
path: "/home/{{ pi_kiosk_user }}/{{ item }}"
state: directory
owner: "{{ pi_kiosk_user }}"
group: "{{ pi_kiosk_user }}"
mode: "0755"
loop:
- .config
- .config/openbox
- name: Install the Openbox root menu
ansible.builtin.template:
src: openbox-menu.xml.j2
dest: "/home/{{ pi_kiosk_user }}/.config/openbox/menu.xml"
owner: "{{ pi_kiosk_user }}"
group: "{{ pi_kiosk_user }}"
mode: "0644"
notify: Reload systemd and restart getty