Files
Viktor Petersson 10c68b26cc feat(viewer,build,balena): add arm64/Qt6 pi3-64 board and the Rock Pi 4 fleet; keep 32-bit pi3 as legacy (#2985)
* feat(viewer,build): add arm64/Qt6 pi3-64 board; keep 32-bit pi3 as legacy

Revises issue #2906 Phase 2. The original plan (delete the Qt 5 toolchain,
force Pi 2/Pi 3 onto Qt 6) is abandoned: Qt 5 was fixed up on master and
stays. Instead, add a NEW board target `pi3-64` — a 64-bit (arm64) Qt 6
viewer image for Raspberry Pi 3 hardware on a 64-bit OS — as its own image
stream, disk image, and balena fleet. The legacy 32-bit armhf/Qt5 `pi3`
board is left untouched and flagged as legacy/maintenance.

pi3-64 mirrors the existing `pi4-64` path (Qt 6, eglfs_kms; video played
in-process by AnthiasViewer's QtMultimedia pipeline — QMediaPlayer + the
ffmpeg/libavcodec backend with V4L2 HW decode, no external player).
VideoCore IV is H.264-only HW decode. Board selection is by `uname -m`: a
Pi 3 on a 64-bit OS gets `pi3-64`, a 32-bit OS keeps `pi3` (the model
string is identical on both arches).

- image_builder: pi3-64 build params (arm64) + is_qt6; constants.
- Dockerfile.viewer.j2 + start_viewer.sh: pi3-64 shares the pi4-64 eglfs
  KMS path; renamed board-agnostic eglfs-kms-pi4.json -> eglfs-kms.json.
- Detection: install.sh / upgrade_containers.sh (aarch64 Pi 3 -> pi3-64).
- Runtime: media_player force_mpv set (selects MPVMediaPlayer, the
  QtMultimedia D-Bus shim); processing codec grid {'h264'}.
- CI: docker-build matrix + mirror-latest-tags.
- Balena (fleet screenly_ose/anthias-pi3-64, device type raspberrypi3-64):
  disk-image + manual-deploy workflows, balena_ota_deploy.sh,
  balena_fleet_maintenance.py, balena_unpin_devices.py, deploy_to_balena.sh,
  balena-host-config.json.
- Pi Imager: SUPPORTED_BOARDS += pi3-64 (non-maintenance); pi3 stays legacy.
- Docs + tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(website): link the Pi 3 (64-bit) bullet like its siblings

Copilot review: the list is introduced as 'links to the images', so the
new pi3-64 entry should be navigable like the surrounding bullets. Link
the label to the release-images section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(balena): add the Rock Pi 4 fleet (screenly_ose/anthias-rockpi4)

Wires the anthias-rockpi4 balena fleet (device type rockpi-4b-rk3399)
into the OTA deploy + disk-image pipeline. The fleet has no
board-specific image build: it runs the generic arm64 containers, so
bin/balena_ota_deploy.sh / bin/deploy_to_balena.sh map the rockpi4
board to the <short-hash>-arm64 image tags (and strip the /dev/vchiq
mount — no VideoCore on RK3399), and the disk-image preflight verifies
the arm64 images exist.

Root-cause fix for the fleet's codec gate: balena ships no
anthias_host_agent service, so host:board_subtype was never published
and resolve_device_key() stayed 'arm64' — whose HW-decode set is empty,
rejecting every video upload. The model-string → subtype table moves to
the dependency-free anthias_common.device_helper.detect_board_subtype
(single source, imported by host_agent), and
anthias_common.board.get_board_subtype now falls back to reading
/proc/device-tree/model in-container when Redis has no value. The
device tree is kernel-global — the same mechanism get_device_type has
always used for Pi detection — so the rockpi4 fleet resolves its
{h264, hevc} envelope without a host-side daemon, and compose installs
whose host_agent died self-heal too.

- build-balena-disk-image.yaml: rockpi4 in both matrices, fleet +
  rockpi-4b-rk3399 image cases, arm64 images in the preflight check.
- deploy-balena-manual.yaml: rockpi4 board option.
- balena-host-config.json: rockpi4 declared {} (config.txt is
  RPi-only; the reconcile hard-fails on a missing key).
- balena_fleet_maintenance.py / balena_unpin_devices.py: fleet added.
- tests: get_board_subtype Redis-first + device-tree-fallback order;
  detect_board_subtype patch targets follow the move.
- docs: board-enablement, balena-fleet-host-config,
  installation-options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 07:49:12 +02:00
..

Anthias Website

This is the source code for anthias.screenly.io, built with Hugo and Tailwind CSS v4.

Local Development

The site needs the Tailwind output and self-hosted fonts to be generated before Hugo serves anything. Run the bun pipeline once, then start Hugo (or use bun run dev to do both with watchers):

cd website
bun install
bun run css:build      # generates assets/styles/style.css and static/fonts/
hugo server

Or, with hot reload on CSS + templates:

cd website
bun run dev            # bun css:watch + hugo server in parallel

The site will be available at http://localhost:1313.

Skipping the bun step will load the page with broken styling and 404s for /fonts/...woff2 — the font files come from the npm package @fontsource/plus-jakarta-sans and are materialized at build time, not committed. Icons are handled the same way: bun run icons:install copies a curated subset of Font Awesome SVGs from @fortawesome/fontawesome-free into assets/images/icons/ (gitignored). Both run automatically as part of css:build / css:watch.

Home-page screenshot slider

The hero on / is a slider fed by PNGs in assets/images/screenshots/. The directory is gitignored — captures are produced by the marketing-screenshots.yaml workflow and pulled into the build at deploy time. To preview the real slider locally:

bun run screenshots:fetch     # latest successful master run
bun run dev

The fetch script needs the gh CLI authenticated; it targets Screenly/Anthias by default so the upstream artifact is reachable even from a fork clone. Pass --ref <branch> to pull from a different branch's most recent run, or --repo <owner>/<repo> to point at a different repo. Without screenshots, Hugo still builds — the slider section simply omits.

Build

cd website
bun run build          # bun css:build && hugo

Output goes to website/public/.

Project Structure

website/
├── assets/                       # Hugo Pipes inputs
│   ├── docs/                     # Doc images / d2 diagram
│   ├── fonts/                    # Generated by bun fonts:install (gitignored)
│   ├── images/                   # Site images, logos, hero
│   └── styles/                   # Built Tailwind + chroma stylesheets
├── content/
│   ├── _index.md
│   ├── api.md                    # /api/ — OpenAPI rendered server-side
│   ├── docs/                     # /docs/* (rendered via layouts/docs/)
│   ├── faq.md                    # /faq/ — driven by data/faq.yaml
│   ├── features.md
│   └── get-started.md
├── data/
│   ├── faq.yaml                  # FAQ entries grouped by section
│   └── openapi.yaml              # drf-spectacular dump for the /api/ page
├── layouts/
│   ├── _default/
│   │   ├── _markup/              # Goldmark render hooks (image, blockquote)
│   │   ├── api.html              # /api/ template
│   │   ├── baseof.html           # Base HTML
│   │   ├── faq.html              # /faq/ template
│   │   └── ...
│   ├── docs/                     # /docs/ list and single layouts
│   ├── index.html                # Home page
│   └── partials/                 # navbar, footer
├── scripts/
│   └── install-fonts.ts          # Materializes Plus Jakarta Sans from npm
├── src/
│   └── main.css                  # Tailwind entrypoint
├── static/
│   ├── fonts/                    # Generated by bun fonts:install (gitignored)
│   └── robots.txt
├── hugo.toml                     # Hugo configuration
├── package.json                  # Bun scripts: dev / css:build / fonts:install
└── public/                       # Build output (gitignored)