divoom: add pi deploy artifact manifests
Add source-controlled Puppet/Hiera contracts for edge2 Divoom-as-DM-device without replacing the live flowercore-divoom systemd deployment. Add Divoom TV Pi HDMI systemd/Puppet deployment artifacts, LF shell-script guardrails, and focused lint coverage for the additive non-K8s deploy shape. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
44
apps/fc-divoom-tv-pi/README.md
Normal file
44
apps/fc-divoom-tv-pi/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# FlowerCore Divoom TV Pi HDMI
|
||||
|
||||
Source-controlled deploy shape for the native `FlowerCore.Divoom.Tv`
|
||||
Avalonia HDMI renderer on a Raspberry Pi connected to a TV.
|
||||
|
||||
This is a Puppet/systemd appliance bundle, not a Kubernetes application. It
|
||||
mirrors the existing `fc-signage-pi-player` pattern: bluejay-infra carries the
|
||||
systemd units, scripts, Hiera shape, and Puppet profile source that
|
||||
`FlowerCore.Puppet` vendors and installs.
|
||||
|
||||
## Scope
|
||||
|
||||
- Launch the future `FlowerCore.Divoom.Tv` linux-arm64 self-contained payload
|
||||
from `/opt/flowercore/divoom-tv/FlowerCore.Divoom.Tv`.
|
||||
- Prefer `cage` as the Wayland fullscreen compositor, with direct app launch as
|
||||
a fallback for development images.
|
||||
- Restart the app after HDMI hotplug with a 2 second DRM settle delay.
|
||||
- Keep all runtime state local: `/var/lib/fc-divoom-tv` and
|
||||
`/var/log/fc-divoom-tv`.
|
||||
- Avoid CDN/runtime fetches; the app renders the in-house Divoom scene catalog
|
||||
locally.
|
||||
|
||||
## Artifact Map
|
||||
|
||||
| Path | Use |
|
||||
| --- | --- |
|
||||
| `systemd/flowercore-divoom-tv.service` | Fullscreen Avalonia HDMI app service. |
|
||||
| `systemd/flowercore-divoom-tv-hdmi.service` | HDMI hotplug responder service. |
|
||||
| `systemd/99-flowercore-divoom-tv-hdmi.rules` | DRM udev hotplug rule. |
|
||||
| `scripts/flowercore-divoom-tv-prelaunch.sh` | Preflight checks and local directory creation. |
|
||||
| `scripts/flowercore-divoom-tv-launch.sh` | Cage-first fullscreen launcher. |
|
||||
| `scripts/flowercore-divoom-tv-hdmi-respond.sh` | Hotplug settle and restart script. |
|
||||
| `puppet/profile/pi/service/divoom_tv.pp` | Puppet profile shape to vendor into `FlowerCore.Puppet`. |
|
||||
| `hiera/example-divoom-tv-pi.iamworkin.lan.yaml` | Example node Hiera for a Divoom TV Pi. |
|
||||
|
||||
## Rollout Notes
|
||||
|
||||
1. Build `FlowerCore.Divoom.Tv` with `dotnet.exe publish -c Release -r linux-arm64 --self-contained`.
|
||||
2. Stage the payload to `/opt/flowercore/divoom-tv/` through the standard noc1
|
||||
jump path and avoid `/tmp` for unprivileged Pi scratch.
|
||||
3. Vendor the profile and static files into `FlowerCore.Puppet`.
|
||||
4. Run Puppet noop, then apply on the target Pi.
|
||||
5. Prove deployment with `systemctl is-active flowercore-divoom-tv.service`,
|
||||
journal lines showing frames presented, and a visible HDMI display check.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Example node data for a dedicated Pi -> HDMI -> TV Divoom renderer.
|
||||
# Copy into FlowerCore.Puppet data/nodes/<hostname>.iamworkin.lan.yaml only
|
||||
# after the Pi has a static DHCP/DNS entry and the linux-arm64 payload exists.
|
||||
|
||||
facts:
|
||||
role: pi_prototype
|
||||
|
||||
profile::motd::role: 'Divoom TV HDMI Renderer'
|
||||
|
||||
profile::pi::service::divoom_tv::ensure: 'present'
|
||||
profile::pi::service::divoom_tv::service_enabled: true
|
||||
profile::pi::service::divoom_tv::service_ensure: 'running'
|
||||
profile::pi::service::divoom_tv::install_dir: '/opt/flowercore/divoom-tv'
|
||||
profile::pi::service::divoom_tv::state_dir: '/var/lib/fc-divoom-tv'
|
||||
profile::pi::service::divoom_tv::log_dir: '/var/log/fc-divoom-tv'
|
||||
profile::pi::service::divoom_tv::presentation_mode: 'PillarboxSquare'
|
||||
profile::pi::service::divoom_tv::startup_scene: 'bluejay-clock'
|
||||
profile::pi::service::divoom_tv::reduced_motion: false
|
||||
149
apps/fc-divoom-tv-pi/puppet/profile/pi/service/divoom_tv.pp
Normal file
149
apps/fc-divoom-tv-pi/puppet/profile/pi/service/divoom_tv.pp
Normal file
@@ -0,0 +1,149 @@
|
||||
# Drop into FlowerCore.Puppet site-modules/profile/manifests/pi/service/divoom_tv.pp.
|
||||
# Static files come from profile/pi/fc_divoom_tv/ after this bluejay-infra
|
||||
# bundle is vendored into the Puppet control repo.
|
||||
class profile::pi::service::divoom_tv (
|
||||
Enum['present', 'absent'] $ensure = 'present',
|
||||
Boolean $service_enabled = false,
|
||||
Enum['running', 'stopped'] $service_ensure = 'stopped',
|
||||
String $service_name = 'flowercore-divoom-tv',
|
||||
String $user = 'fc-divoom-tv',
|
||||
String $group = 'fc-divoom-tv',
|
||||
String $install_dir = '/opt/flowercore/divoom-tv',
|
||||
String $state_dir = '/var/lib/fc-divoom-tv',
|
||||
String $log_dir = '/var/log/fc-divoom-tv',
|
||||
String $presentation_mode = 'PillarboxSquare',
|
||||
String $startup_scene = 'bluejay-clock',
|
||||
Boolean $reduced_motion = false,
|
||||
) {
|
||||
include profile::workstation::safe_account_exclusion
|
||||
|
||||
$safe_account = $profile::workstation::safe_account_exclusion::safe_account
|
||||
|
||||
if $safe_account {
|
||||
notify { 'fc-divoom-tv safe-account exclusion':
|
||||
message => 'SAFE-ACCOUNT-EXCLUSION: Divoom TV Pi profile refused to apply on operator workstation',
|
||||
}
|
||||
} elsif $ensure == 'absent' {
|
||||
service { $service_name:
|
||||
ensure => stopped,
|
||||
enable => false,
|
||||
}
|
||||
|
||||
file { [
|
||||
"/etc/systemd/system/${service_name}.service",
|
||||
"/etc/systemd/system/${service_name}-hdmi.service",
|
||||
'/etc/udev/rules.d/99-flowercore-divoom-tv-hdmi.rules',
|
||||
'/usr/local/bin/flowercore-divoom-tv-prelaunch.sh',
|
||||
'/usr/local/bin/flowercore-divoom-tv-launch.sh',
|
||||
'/usr/local/bin/flowercore-divoom-tv-hdmi-respond.sh',
|
||||
'/etc/flowercore/divoom-tv.env',
|
||||
]:
|
||||
ensure => absent,
|
||||
}
|
||||
} else {
|
||||
case $facts['os']['family'] {
|
||||
'Debian': {}
|
||||
default: { fail("profile::pi::service::divoom_tv only supports Debian-family OS, got ${facts['os']['family']}") }
|
||||
}
|
||||
|
||||
package { ['cage', 'libgbm1', 'libdrm2', 'libxkbcommon0', 'fonts-dejavu-core']:
|
||||
ensure => installed,
|
||||
}
|
||||
|
||||
group { $group:
|
||||
ensure => present,
|
||||
system => true,
|
||||
}
|
||||
|
||||
user { $user:
|
||||
ensure => present,
|
||||
system => true,
|
||||
gid => $group,
|
||||
home => $state_dir,
|
||||
managehome => false,
|
||||
shell => '/usr/sbin/nologin',
|
||||
require => Group[$group],
|
||||
}
|
||||
|
||||
file { [$install_dir, $state_dir, $log_dir, '/etc/flowercore']:
|
||||
ensure => directory,
|
||||
owner => $user,
|
||||
group => $group,
|
||||
mode => '0755',
|
||||
}
|
||||
|
||||
file { '/etc/flowercore/divoom-tv.env':
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
content => "FC_DIVOOM_TV_PRESENTATION_MODE=${presentation_mode}\nFC_DIVOOM_TV_START_SCENE=${startup_scene}\nFC_DIVOOM_TV_REDUCED_MOTION=${reduced_motion}\n",
|
||||
require => File['/etc/flowercore'],
|
||||
}
|
||||
|
||||
$script_map = {
|
||||
'/usr/local/bin/flowercore-divoom-tv-prelaunch.sh' => 'profile/pi/fc_divoom_tv/flowercore-divoom-tv-prelaunch.sh',
|
||||
'/usr/local/bin/flowercore-divoom-tv-launch.sh' => 'profile/pi/fc_divoom_tv/flowercore-divoom-tv-launch.sh',
|
||||
'/usr/local/bin/flowercore-divoom-tv-hdmi-respond.sh' => 'profile/pi/fc_divoom_tv/flowercore-divoom-tv-hdmi-respond.sh',
|
||||
}
|
||||
|
||||
$script_map.each |$dest, $src| {
|
||||
file { $dest:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0755',
|
||||
source => "puppet:///modules/${src}",
|
||||
}
|
||||
}
|
||||
|
||||
$unit_map = {
|
||||
"/etc/systemd/system/${service_name}.service" => 'profile/pi/fc_divoom_tv/flowercore-divoom-tv.service',
|
||||
"/etc/systemd/system/${service_name}-hdmi.service" => 'profile/pi/fc_divoom_tv/flowercore-divoom-tv-hdmi.service',
|
||||
}
|
||||
|
||||
$unit_map.each |$dest, $src| {
|
||||
file { $dest:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
source => "puppet:///modules/${src}",
|
||||
notify => Exec['fc-divoom-tv-systemd-reload'],
|
||||
}
|
||||
}
|
||||
|
||||
file { '/etc/udev/rules.d/99-flowercore-divoom-tv-hdmi.rules':
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
source => 'puppet:///modules/profile/pi/fc_divoom_tv/99-flowercore-divoom-tv-hdmi.rules',
|
||||
notify => Exec['fc-divoom-tv-udev-reload'],
|
||||
}
|
||||
|
||||
exec { 'fc-divoom-tv-systemd-reload':
|
||||
command => '/usr/bin/systemctl daemon-reload',
|
||||
refreshonly => true,
|
||||
path => ['/usr/bin', '/bin'],
|
||||
}
|
||||
|
||||
exec { 'fc-divoom-tv-udev-reload':
|
||||
command => '/usr/bin/udevadm control --reload-rules',
|
||||
refreshonly => true,
|
||||
path => ['/usr/bin', '/bin'],
|
||||
}
|
||||
|
||||
service { $service_name:
|
||||
ensure => $service_ensure,
|
||||
enable => $service_enabled,
|
||||
require => [
|
||||
File["/etc/systemd/system/${service_name}.service"],
|
||||
File['/etc/flowercore/divoom-tv.env'],
|
||||
File['/usr/local/bin/flowercore-divoom-tv-prelaunch.sh'],
|
||||
File['/usr/local/bin/flowercore-divoom-tv-launch.sh'],
|
||||
Exec['fc-divoom-tv-systemd-reload'],
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sleep 2
|
||||
systemctl restart flowercore-divoom-tv.service
|
||||
25
apps/fc-divoom-tv-pi/scripts/flowercore-divoom-tv-launch.sh
Normal file
25
apps/fc-divoom-tv-pi/scripts/flowercore-divoom-tv-launch.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_BIN="${FC_DIVOOM_TV_BIN:-/opt/flowercore/divoom-tv/FlowerCore.Divoom.Tv}"
|
||||
STATE_DIR="${FC_DIVOOM_TV_STATE_DIR:-/var/lib/fc-divoom-tv}"
|
||||
LOG_DIR="${FC_DIVOOM_TV_LOG_DIR:-/var/log/fc-divoom-tv}"
|
||||
PRESENTATION_MODE="${FC_DIVOOM_TV_PRESENTATION_MODE:-PillarboxSquare}"
|
||||
START_SCENE="${FC_DIVOOM_TV_START_SCENE:-bluejay-clock}"
|
||||
REDUCED_MOTION="${FC_DIVOOM_TV_REDUCED_MOTION:-false}"
|
||||
|
||||
COMMON_ARGS=(
|
||||
"--target=hdmi"
|
||||
"--presentation-mode=${PRESENTATION_MODE}"
|
||||
"--startup-scene=${START_SCENE}"
|
||||
"--reduced-motion=${REDUCED_MOTION}"
|
||||
"--state-dir=${STATE_DIR}"
|
||||
"--log-dir=${LOG_DIR}"
|
||||
)
|
||||
|
||||
if command -v cage >/dev/null 2>&1; then
|
||||
exec cage -- "${APP_BIN}" "${COMMON_ARGS[@]}" "$@"
|
||||
fi
|
||||
|
||||
echo "[$(date -Is)] cage not found; launching FlowerCore.Divoom.Tv directly" >&2
|
||||
exec "${APP_BIN}" "${COMMON_ARGS[@]}" "$@"
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_BIN="${FC_DIVOOM_TV_BIN:-/opt/flowercore/divoom-tv/FlowerCore.Divoom.Tv}"
|
||||
STATE_DIR="${FC_DIVOOM_TV_STATE_DIR:-/var/lib/fc-divoom-tv}"
|
||||
LOG_DIR="${FC_DIVOOM_TV_LOG_DIR:-/var/log/fc-divoom-tv}"
|
||||
|
||||
mkdir -p "${STATE_DIR}" "${LOG_DIR}"
|
||||
|
||||
if [[ ! -x "${APP_BIN}" ]]; then
|
||||
echo "[$(date -Is)] missing executable ${APP_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d /sys/class/drm ]] && ! find /sys/class/drm -maxdepth 1 -name 'card*-HDMI-A-*' -print -quit | grep -q .; then
|
||||
echo "[$(date -Is)] no HDMI connector visible yet; continuing so the app can wait for display" >&2
|
||||
fi
|
||||
|
||||
if command -v cage >/dev/null 2>&1; then
|
||||
echo "[$(date -Is)] cage available for fullscreen Wayland launch"
|
||||
else
|
||||
echo "[$(date -Is)] cage not installed; direct launch fallback will be used" >&2
|
||||
fi
|
||||
@@ -0,0 +1,2 @@
|
||||
# Settle DRM for 2s before restarting the fullscreen Avalonia renderer.
|
||||
SUBSYSTEM=="drm", KERNEL=="card?-HDMI-A-?", ACTION=="change", RUN+="/usr/bin/systemctl start flowercore-divoom-tv-hdmi.service"
|
||||
@@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=FlowerCore Divoom TV HDMI hotplug responder
|
||||
DefaultDependencies=no
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/flowercore-divoom-tv-hdmi-respond.sh
|
||||
40
apps/fc-divoom-tv-pi/systemd/flowercore-divoom-tv.service
Normal file
40
apps/fc-divoom-tv-pi/systemd/flowercore-divoom-tv.service
Normal file
@@ -0,0 +1,40 @@
|
||||
[Unit]
|
||||
Description=FlowerCore Divoom TV HDMI Renderer (Avalonia fullscreen)
|
||||
Documentation=https://github.com/astoltz/FlowerCore.Notes/blob/master/docs/standards/divoom-tv-hdmi-multitarget-render-substrate.md
|
||||
Wants=network-online.target
|
||||
After=network-online.target systemd-user-sessions.service
|
||||
ConditionPathExists=/opt/flowercore/divoom-tv/FlowerCore.Divoom.Tv
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=fc-divoom-tv
|
||||
Group=fc-divoom-tv
|
||||
WorkingDirectory=/opt/flowercore/divoom-tv
|
||||
EnvironmentFile=-/etc/flowercore/divoom-tv.env
|
||||
Environment=DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
Environment=XDG_RUNTIME_DIR=/run/fc-divoom-tv
|
||||
RuntimeDirectory=fc-divoom-tv
|
||||
RuntimeDirectoryMode=0700
|
||||
ExecStartPre=/usr/local/bin/flowercore-divoom-tv-prelaunch.sh
|
||||
ExecStart=/usr/local/bin/flowercore-divoom-tv-launch.sh
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
StartLimitBurst=5
|
||||
StartLimitIntervalSec=300s
|
||||
MemoryMax=2G
|
||||
MemoryHigh=1500M
|
||||
PrivateTmp=true
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/var/lib/fc-divoom-tv /var/log/fc-divoom-tv /run/fc-divoom-tv
|
||||
TTYPath=/dev/tty1
|
||||
StandardInput=tty
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
Reference in New Issue
Block a user