/
/
/
This repo is destined for my server automations and setup.
1---
2# ============================================================================
3# D&D Table Playbook
4# ============================================================================
5#
6# Deploys the projector display path, camera capture path and ceiling wash
7# on the table Pi.
8#
9# PREREQUISITES:
10# - Raspberry Pi OS Bookworm 64-bit **Lite** (not Desktop: kmssink cannot take
11# DRM master while a compositor holds it; and Ubuntu's mainline kernel does
12# not reliably ship the bcm2835-codec nodes this depends on)
13# - Raspberry Pi 4 (the Pi 5 has no hardware H.264 encoder)
14# - SSH access with sudo privileges for ansible_user
15# - MQTT broker accessible on the network
16# - Projector connected over HDMI
17# - Images published to Harbor by Forgejo CI
18# - Camera Module 3 (optional; set dnd_table_camera_enabled=true once fitted)
19#
20# USAGE:
21# ansible-playbook dnd-table.yml
22# ansible-playbook dnd-table.yml --tags wash # ceiling wash only
23# ansible-playbook dnd-table.yml --tags dnd # skip base system roles
24# ansible-playbook dnd-table.yml --tags validation --check
25#
26# ============================================================================
27
28- name: "D&D Table Complete Setup"
29 hosts: dnd-table
30 become: true
31 gather_facts: true
32
33 pre_tasks:
34 - name: Verify table system requirements
35 ansible.builtin.assert:
36 that:
37 - ansible_distribution in ["Debian", "Ubuntu"]
38 - ansible_architecture in ["aarch64", "armv7l"]
39 - ansible_memtotal_mb >= 900
40 - ansible_processor_vcpus >= 2
41 fail_msg: |
42 Table system requirements not met:
43 - Requires Debian or Ubuntu on ARM
44 - Minimum 1GB RAM (found {{ ansible_memtotal_mb }}MB)
45 - Minimum 2 CPU cores (found {{ ansible_processor_vcpus }})
46 success_msg: "Table system requirements validated successfully"
47 tags: always
48
49 # Not fatal here: the codec assertions in the role are the real gate. This
50 # names the likely cause early, because the distribution explains most of
51 # the failures this deployment can have.
52 - name: Warn when not running Raspberry Pi OS
53 ansible.builtin.debug:
54 msg: |
55 WARNING: {{ ansible_distribution }} {{ ansible_distribution_version }} detected.
56
57 The hardware codec nodes (/dev/video10, /dev/video11) come from the
58 bcm2835-codec driver in the downstream Raspberry Pi kernel. Ubuntu's
59 mainline kernel frequently does not expose them, and the role will
60 fail its codec assertions below if so.
61 when: ansible_distribution != "Debian"
62 tags: always
63
64 - name: Display table deployment information
65 ansible.builtin.debug:
66 msg: |
67 ============================================================================
68 D&D Table Installation Starting
69 ============================================================================
70 Target Host: {{ inventory_hostname }}
71 Target IP: {{ ansible_default_ipv4.address }}
72 OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
73 Architecture: {{ ansible_architecture }}
74
75 Display path: {{ 'enabled' if dnd_table_display_enabled else 'disabled' }}
76 {{ dnd_table_width }}x{{ dnd_table_height }}@{{ dnd_table_fps }}, RTP in on :{{ dnd_table_rtp_port }}
77 Camera path: {{ 'enabled' if dnd_table_camera_enabled else 'disabled (no module fitted)' }}
78 -> {{ dnd_table_runner_host }}:{{ dnd_table_camera_rtp_port }}
79 Ceiling wash: {{ 'enabled' if dnd_wash_enabled else 'disabled' }}
80 GPIO r={{ dnd_wash_pin_r }} g={{ dnd_wash_pin_g }} b={{ dnd_wash_pin_b }} w={{ dnd_wash_pin_w }}
81
82 Images:
83 {{ dnd_table_image }}
84 {{ dnd_wash_image }}
85
86 MQTT: {{ mqtt_host }}:{{ mqtt_port }}
87 ============================================================================
88 tags: always
89
90 # ============================================================================
91 # ROLE EXECUTION ORDER
92 # ============================================================================
93
94 roles:
95 # 1. SYSTEM SETUP - Basic system configuration and packages
96 - role: system
97 tags: [system, setup]
98
99 # 2. DOCKER INSTALLATION - Install Docker using geerlingguy.docker
100 - role: geerlingguy.docker
101 tags: [docker, setup]
102
103 # 3. USER MANAGEMENT - Create user with proper groups
104 - role: user
105 tags: [user, setup]
106
107 # 4. DOCKER FRAMEWORK - Setup Docker directory structure
108 - role: docker-framework
109 tags: [docker, framework]
110
111 # 5. SECURITY HARDENING - Apply security settings
112 - role: geerlingguy.security
113 tags: [security, hardening]
114
115 # 6. TABLE - Projector, camera and ceiling wash
116 - role: dnd-table
117 tags: [dnd, table, wash]
118
119 # 7. MONITORING - System monitoring
120 - role: monitoring
121 tags: [monitoring, glances]
122
123 # ============================================================================
124 # POST-INSTALLATION TASKS
125 # ============================================================================
126
127 post_tasks:
128 - name: Verify Docker containers are running
129 ansible.builtin.command: docker ps --format "table {% raw %}{{.Names}}\t{{.Status}}{% endraw %}"
130 register: docker_status
131 changed_when: false
132 tags: [verification, docker]
133
134 - name: Display deployment completion summary
135 ansible.builtin.debug:
136 msg: |
137 ============================================================================
138 D&D Table Installation Complete
139 ============================================================================
140
141 Host: {{ inventory_hostname }}
142 IP Address: {{ ansible_default_ipv4.address }}
143 OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
144
145 Docker Containers:
146 {{ docker_status.stdout | default('Skipped') | indent(10) }}
147
148 Verify the display path -- from the runner:
149 gst-launch-1.0 videotestsrc pattern=smpte is-live=true \
150 ! video/x-raw,width={{ dnd_table_width }},height={{ dnd_table_height }},framerate={{ dnd_table_fps }}/1 \
151 ! timeoverlay ! videoconvert \
152 ! x264enc tune=zerolatency speed-preset=ultrafast key-int-max={{ dnd_table_fps }} \
153 ! h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 \
154 ! udpsink host={{ ansible_default_ipv4.address }} port={{ dnd_table_rtp_port }} sync=false
155 {% if dnd_wash_enabled %}
156
157 Verify the ceiling wash:
158 mosquitto_pub -h {{ mqtt_host }} -t {{ dnd_wash_base_topic }}/set -m '{"scene":"torchlight"}'
159 mosquitto_sub -h {{ mqtt_host }} -t '{{ dnd_wash_base_topic }}/#' -v
160 {% endif %}
161
162 Useful commands:
163 docker logs -f dnd-table
164 docker logs -f dnd-wash
165 ============================================================================
166 tags: always
167