Compare commits

..

No commits in common. "main" and "v0.3.1" have entirely different histories.
main ... v0.3.1

24 changed files with 199 additions and 487 deletions

View File

@ -1,16 +1,45 @@
name: CI
on:
push:
tags:
- '*' # Triggers on any tag push
create: { }
pull_request: { }
permissions:
contents: write
jobs:
x86:
runs-on: [ubuntu-latest]
outputs:
pkgfile: ${{ steps.pkgname.outputs.pkgfile }}
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update -qq
sudo apt install -yqq libguestfs-tools qemu-utils qemu-system-x86 ovmf qemu-block-extra qemu-user-static binfmt-support rsync sudo wget xz-utils pigz mount dosfstools libarchive-tools
- name: Build firmware
run: |
./build_x86.sh
- name: Release build artifacts
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
append_body: true
body_path: ./version-info
files: |
./anotterkiosk-*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
raspberrypi:
runs-on: [self-hosted]
runs-on: [self-hosted, hetzner-cax21]
outputs:
pkgfile: ${{ steps.pkgname.outputs.pkgfile }}
@ -26,14 +55,15 @@ jobs:
- name: Build firmware
run: |
sudo ./build_raspberry_pi.sh
./build_raspberry_pi.sh
- name: Release build artifacts
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
append_body: true
body_path: ./version-info
files: |
./n-anotterkiosk-*
./anotterkiosk-*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
raspikiosk.img
raspios.img.xz
work/
.DS_Store

View File

@ -1,25 +1,17 @@
N-AnotterKiosk (Not-AnotterKiosk)
=================================
### I have hacked this about alot from the main branch, mainly Raspberry Pi changes
- Added scheduled screen on/off
- Added scheduled chrome page refresh
- Rpi3 Overclock settings
- Disabled KMS driver for HW screen rotation (screen rotated portrait by default)
AnotterKiosk
=============================
### Overview
Another kiosk browser OS? Yes, this one is a little bit opinionated :)
The author ran several similar setups in production for years and has seen a lot of problems and strange failure modes.
This project aims to solve a lot of those (at least for the author), it might also be useful for others :)
#### Key features
- [Images built via CI](https://github.com/Manawyrm/N-AnotterKiosk/blob/main/.github/workflows/main.yml)
- [Images built via CI](https://github.com/Manawyrm/AnotterKiosk/blob/main/.github/workflows/main.yml)
- WiFi connection support
- Raspberry Pi (Arm64) compatibility
- PC (x86) compatibility
- [USB flash drive, USB SSD, etc. compatible](#how-to-use)
- aarch64 mode for Raspberry Pis (_significant_ performance improvements over armv7/32bit ARM)
- Read-only filesystem handling (no more broken SD cards)
@ -30,47 +22,39 @@ This project aims to solve a lot of those (at least for the author), it might al
- SSH support
- VNC support
- SSH tunneling support (for remote-access without port-forwarding, etc.)
- Basic API for Rpi Actions
- Hyperion-NG support for ambilight
#### Planned features:
- Raspberry Pi PXE/network boot support
- Network connectivity watchdog (configurable ping, etc. timeout)
- Automatic reboot at specified time
#### Security considerations:
- Autossh does not check SSH host keys. This is okay-ish as long as the target server only allows tunneling, nothing else.
- nginx/PHP are allowed to use sudo/NOPASSWD (because it needs to query the VideoCore, manage service, etc.), more priviledge seperation would be nice
- due to the skeleton mechanism, the system has some ... creative permissions. some cleanup required.
### How-To Use
Like any other Raspberry Pi image: download the current .img file from the [Releases](https://github.com/Manawyrm/N-AnotterKiosk/releases) page and flash it to a storage device of your choice.
Like any other Raspberry Pi image: download the current .img file from the [Releases](https://github.com/Manawyrm/AnotterKiosk/releases) page and flash it to a storage device of your choice.
SD cards, USB flash drives, USB SSDs, SATA SSDs, NVMe SSDs are all good options.
You can use a tool like the [Raspberry Pi Imager](https://www.raspberrypi.com/software/), [BalenaEtcher](https://etcher.balena.io/), [Win32DiskImager](https://sourceforge.net/projects/win32diskimager/) or plain "dd" on \*nix-like systems.
When using the latter two, make sure to extract the .gz compression first (using a tool like 7zip).
After flashing, re-plug the storage device and open the FAT32 partition.
Open the [`kioskbrowser.ini`](https://github.com/Manawyrm/N-AnotterKiosk/blob/main/kiosk_skeleton/boot/kioskbrowser.ini) file in a text editor and change everything to your needs.
Open the [`kioskbrowser.ini`](https://github.com/Manawyrm/AnotterKiosk/blob/main/kiosk_skeleton/boot/kioskbrowser.ini) file in a text editor and change everything to your needs.
More complex WiFi setups (like WPA2-Enterprise) can be configured by creating a wpa_supplicant.conf.
Adding your own SSH keys can be done by creating a authorized_keys file.
If you want to use the autossh tunneling features, copy an SSH private key as either "id_rsa" or "id_ed25519".
### HTTP watchdog functionality
Browsers are complex, networks are unstable and software can be buggy.
In order to get the highest reliability possible, self-hosted websites can be modified to include a heartbeat/watchdog functionality.
This works by requesting a certain http-endpoint from the website at some interval.
If your page is being reloaded often (like with a <meta refresh=-header), you can just load the heartbeat-URL as an image:
```html
<img src="http://localhost/heartbeat.php" style="display: none;">
```
If your page stays on one page for a long time (or is just a single-page application), you might want to use AJAX requests to send a heartbeat:
```html
<script>
const req = new XMLHttpRequest();
@ -83,51 +67,6 @@ setInterval(function() {
Whenever the heartbeat stops (for whatever reason), the device will first restart the X11 environment (browser, window manager, etc.) and later (if it hasn't recovered) the whole system by rebooting.
### API
Lightweight HTTP API for controlling and monitoring a Raspberry Pi-based kiosk system. It exposes several endpoints that allow you to query system status, control the display, refresh the screen, and reboot the device — all protected by an API key.
API key will be loaded from `/boot/kioskbrowser.ini`
```ini
[api]
key = "My Key"
```
#### Endpoints
All requests must include a key query parameter matching the API key from the INI file.
`GET /script.php?action=status&key=YOUR_API_KEY`
Returns system status:
```json
{
"temperature": "temp=48.0'C",
"voltage": "volt=1.2000V",
"throttled": "throttled=0x0",
"heartbeat": "2025-06-09 14:33:12"
}
```
`GET /script.php?action=screen_off&key=YOUR_API_KEY`
Turns off the screen.
`GET /script.php?action=screen_on&key=YOUR_API_KEY`
Turns on the screen.
`GET /script.php?action=screen_refresh&key=YOUR_API_KEY`
Starts the screen-refresh.service to refresh the screen.
`GET /script.php?action=reboot&key=YOUR_API_KEY`
Reboots the Raspberry Pi.
### Hyperion-NG
The kiosk now supports Hyperion-NG for ambilight control.
Manage Hyperion via its web interface, which is available on port 8090.
### Inspiration / Other Kiosk-OSes:
- https://github.com/jareware/chilipie-kiosk/
- https://github.com/guysoft/FullPageOS

View File

@ -7,10 +7,10 @@ SCRIPT_DIR="$(dirname "$(realpath "$0")")"
BUILD_DIR="${SCRIPT_DIR}/work/root/"
# cleanup any previous build attempts
sudo umount -fl "${BUILD_DIR}" || true
sudo losetup -D /dev/loop0 || true
sudo rm -rf "${BUILD_DIR}" || true
sudo mkdir -p "${BUILD_DIR}"
umount -fl "${BUILD_DIR}" || true
losetup -D /dev/loop0 || true
rm -rf "${BUILD_DIR}" || true
mkdir -p "${BUILD_DIR}"
# download a modern RaspiOS build
if [ ! -f raspios.img.xz ]
@ -51,7 +51,7 @@ sed -i 's/vfat defaults/vfat ro,defaults/g' "${BUILD_DIR}/etc/fstab"
sed -i 's/ext4 defaults/ext4 ro,defaults/g' "${BUILD_DIR}/etc/fstab"
# Include git repo version info
echo -n "N-AnotterKiosk Raspberry Pi version: " > "${BUILD_DIR}/version-info"
echo -n "AnotterKiosk Raspberry Pi version: " > "${BUILD_DIR}/version-info"
git describe --abbrev=4 --dirty --always --tags >> "${BUILD_DIR}/version-info"
# Mount system partitions (from the build host)
@ -84,5 +84,5 @@ sudo umount "${BUILD_DIR}"
sudo losetup -D /dev/loop0
tag=$(git describe --abbrev=4 --dirty --always --tags)
mv raspikiosk.img n-anotterkiosk-${tag}-arm64-raspberrypi.img
pigz -4 n-anotterkiosk-${tag}-arm64-raspberrypi.img
mv raspikiosk.img anotterkiosk-${tag}-arm64-raspberrypi.img
pigz -4 anotterkiosk-${tag}-arm64-raspberrypi.img

90
build_x86.sh Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash
# *sigh*, some docker containers don't seem to have sbin in their PATH
export PATH=$PATH:/usr/sbin
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
BUILD_DIR="${SCRIPT_DIR}/work/root/"
# cleanup any previous build attempts
umount -fl "${BUILD_DIR}" || true
rm -rf "${BUILD_DIR}" || true
mkdir -p "${BUILD_DIR}"
rm x86kiosk.img || true
truncate -s 10G x86kiosk.img
PARTLAYOUT=$(cat <<-END
label: gpt
label-id: 3BC7D7CD-4BF8-4E92-AAEB-2ACD5F8D05AA
device: x86kiosk.img
unit: sectors
first-lba: 34
last-lba: 20971486
sector-size: 512
x86kiosk.img1 : start= 2048, size= 2095105, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=9C99F1BB-11A8-4BB5-82C2-555D7A38F85C, name="EFI system partition"
x86kiosk.img2 : start= 2099200, size= 18870272, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=93A9AB2C-BC29-4C6C-B6DD-1B4EDDED9A1E, name="Linux filesystem"
END
)
echo "${PARTLAYOUT}" | sfdisk x86kiosk.img
# Setup loop device for x86 image (with partition scanning)
ld=$(sudo losetup -P --show -f x86kiosk.img)
# Create filesystems
sudo mkfs.ext4 "${ld}p2"
sudo mkfs.fat -F 32 "${ld}p1"
# Mount partitions
sudo mount "${ld}p2" "${BUILD_DIR}"
sudo mkdir "${BUILD_DIR}/boot"
sudo mount "${ld}p1" "${BUILD_DIR}/boot"
# Debootstrap debian
sudo debootstrap --include=linux-image-amd64,grub-efi,sudo --arch amd64 bookworm "${BUILD_DIR}" http://deb.debian.org/debian/
# Copy the skeleton files
sudo rsync -a "${SCRIPT_DIR}/x86_skeleton/." "${BUILD_DIR}"
sudo rsync -a "${SCRIPT_DIR}/kiosk_skeleton/." "${BUILD_DIR}/kiosk_skeleton"
# Create fstab
fat_uuid=$(lsblk -no UUID "${ld}p1")
ext_uuid=$(lsblk -no UUID "${ld}p2")
echo "UUID=${fat_uuid} /boot vfat ro,defaults 0 2" | sudo tee "${BUILD_DIR}/etc/fstab"
echo "UUID=${ext_uuid} / ext4 ro,defaults,noatime 0 1" | sudo tee -a "${BUILD_DIR}/etc/fstab"
# Include git repo version info
echo -n "AnotterKiosk x86 version: " > "${BUILD_DIR}/version-info"
git describe --abbrev=4 --dirty --always --tags >> "${BUILD_DIR}/version-info"
# Mount system partitions (from the build host)
sudo mount proc -t proc -o nosuid,noexec,nodev "${BUILD_DIR}/proc/"
sudo mount sys -t sysfs -o nosuid,noexec,nodev,ro "${BUILD_DIR}/sys/"
sudo mount devpts -t devtmpfs -o mode=0755,nosuid "${BUILD_DIR}/dev/"
# and then actually install everything.
sudo chroot "${BUILD_DIR}" /setup.sh
sudo chroot "${BUILD_DIR}" /kiosk_skeleton/build.sh
sudo rm -r "${BUILD_DIR}/kiosk_skeleton"
cp "${BUILD_DIR}/version-info" version-info
sudo umount -fl "${BUILD_DIR}/proc"
sudo umount -fl "${BUILD_DIR}/sys"
sudo umount -fl "${BUILD_DIR}/dev"
sudo umount "${BUILD_DIR}/proc"
sudo umount "${BUILD_DIR}/sys"
sudo umount "${BUILD_DIR}/dev"
sudo umount "${BUILD_DIR}/boot"
sudo umount "${BUILD_DIR}"
sudo losetup -D "${ld}"
tag=$(git describe --abbrev=4 --dirty --always --tags)
mv x86kiosk.img anotterkiosk-${tag}-x86.img
pigz -4 anotterkiosk-${tag}-x86.img

View File

@ -2,27 +2,9 @@
[general]
hostname = "kioskpi"
[reboot]
; can be used to set an automatic reboot on a specific time (time in 24 horus format)
enabled=0
reboot_time = 04:00
[screen]
; can be used to force 1080p on 4k screens or workaround broken EDID communication
;force_resolution = "1920x1080"
; force a custom modelines (for specialty diplays like embedded monitors, car screens, etc.)
;custom_modeline = "40.141 1024 1032 1064 1104 600 604 612 618 +HSync -VSync"
; set screen rotation to be used (normal, left, right, inverted)
;rotate_screen = "normal"
; configure screen to power on/off as specific time of day (time format in 24 hours)
;screen_off_time=23:00
;screen_on_time=07:00
; configure chrome to refresh the page every x minutes
;refresh_screen_every_x_min=15
[api]
; apikey to be sent with commands to /api.php
key = "MyKey"
[wifi]
; If you need more complex WiFi settings (like WPA2-Enterprise, hidden SSIDs, etc.)
@ -34,7 +16,7 @@ ssid="My WiFi"
psk="My Passphrase"
[browser]
url="https://kittenlabs.de/"
url="https://tbspace.de/"
; clear the browser cache every 10 minutes
cache_clear_interval=600
@ -55,7 +37,7 @@ enabled=0
; autossh can be used to keep a connection to a specified SSH server up-and-running to allow for remote access
; without the need for port forwarding, public IPv4 addressing, dynamic DNS, etc.
enabled=0
args = "-p 22 -R 1234:127.0.0.1:22 tunnel@example.com"
args = "-p 22 -R 1234:127.0.0.1:22 tunnel@mydomain.de"
; security warning: ensure sshd_config "GatewayPorts" is set to "clientspecified" or "no".
; GatewayPorts=yes will cause the kioskpi to be globally bound (0.0.0.0, regardless of the bind-address specified above) and be reachable from the internet!
; This might be a huge risk.

View File

@ -78,23 +78,6 @@ systemctl enable ntpdate
systemctl enable lightdm
systemctl enable nginx
systemctl enable ssh
systemctl enable kiosk-sechedule-screen.service
systemctl enable schedule-reboot.service
systemctl enable setup-refresh-timer.service
systemctl enable hyperiond
# Install Hyperion
curl -sSL https://apt.hyperion-project.org/hyperion.pub.key | gpg --dearmor -o /usr/share/keyrings/hyperion.pub.gpg
echo "deb [signed-by=/usr/share/keyrings/hyperion.pub.gpg] https://apt.hyperion-project.org/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hyperion.list
apt update
apt install -y hyperion
# Run Hyperion as the 'pi' user
mkdir -p /etc/systemd/system/hyperiond.service.d
echo -e "[Service]\nUser=pi\nGroup=pi" > /etc/systemd/system/hyperiond.service.d/override.conf
chown -R pi:pi /var/lib/hyperion || true
chown -R pi:pi /etc/hyperion
usermod -a -G video pi
# generate a version info/build info file
echo -n "Chromium version: " >> /version-info
@ -103,7 +86,3 @@ dpkg --list | grep "ii chromium " >> /version-info
echo -n "Linux kernel version: " >> /version-info
ls /lib/modules/ | sort -r | head -n 1 >> /version-info
echo >> /version-info
# Clean up apt cache and remove unused packages
apt-get clean
apt-get autoremove -y

View File

@ -1,2 +1,2 @@
N-AnotterKiosk \n \l
AnotterKiosk \n \l

View File

@ -1,3 +1,4 @@
Welcome to N-AnotterKiosk!
Welcome to AnotterKiosk!
Run "mount -o remount,rw /" to make the root filesystem writeable.
FAT32 / config partition is located in /boot.

View File

@ -1,11 +0,0 @@
# /etc/systemd/system/kiosk-sechedule-screen.service
[Unit]
Description=Schedule Screen On/Off Timers
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/bin/schedule-screen-timers
[Install]
WantedBy=multi-user.target

View File

@ -1,10 +0,0 @@
[Unit]
Description=Schedule Reboot from kioskbrowser.ini
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/bin/schedule-reboot
[Install]
WantedBy=multi-user.target

View File

@ -1,11 +0,0 @@
[Unit]
Description=Initial screen refresh timer setup
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/setup-refresh-timer
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

View File

@ -24,24 +24,6 @@ then
xrandr --output "${MONITOR}" --mode "${RESOLUTION}"
fi
# set a custom modeline (if specified)
CUSTOM_MODELINE=$(get-ini /boot/kioskbrowser.ini screen custom_modeline)
if [ -n "${CUSTOM_MODELINE}" ]
then
MONITOR=$(xrandr -q | grep " connected" | awk '{ print $1; }')
xrandr --newmode "custom" ${CUSTOM_MODELINE}
xrandr --addmode "${MONITOR}" "custom"
xrandr --output "${MONITOR}" --mode "custom"
fi
# set a screen rotation (if specified)
ROTATE_SCREEN=$(get-ini /boot/kioskbrowser.ini screen rotate_screen)
if [ -n "${ROTATE_SCREEN}" ]
then
MONITOR=$(xrandr -q | grep " connected" | awk '{ print $1; }')
xrandr --output "${MONITOR}" --rotate ${ROTATE_SCREEN}
fi
# start chromium
URL=$(get-ini /boot/kioskbrowser.ini browser url)
chromium --start-fullscreen \

View File

@ -1,4 +0,0 @@
#!/bin/bash
export DISPLAY=:0
/usr/bin/xdotool key F5

View File

@ -1,64 +0,0 @@
#!/bin/bash
INI_FILE="/boot/kioskbrowser.ini"
remount_root() {
local mode=$1
echo "Remounting root filesystem as $mode..."
mount -o remount,"$mode" / || {
echo "Failed to remount root as $mode"
exit 1
}
}
REBOOT_ENABLED=$(awk -F '=' '/^\[reboot\]/ { in_reboot=1; next }
in_reboot && /^\[/ { in_reboot=0 }
in_reboot && $1 ~ /enabled/ { gsub(/ /, "", $2); print $2 }' "$INI_FILE")
REBOOT_TIME=$(awk -F '=' '/^\[reboot\]/ { in_reboot=1; next }
in_reboot && /^\[/ { in_reboot=0 }
in_reboot && $1 ~ /reboot_time/ { gsub(/ /, "", $2); print $2 }' "$INI_FILE")
if [[ "$REBOOT_ENABLED" -eq 1 ]] && [[ "$REBOOT_TIME" =~ ^[0-2][0-9]:[0-5][0-9]$ ]]; then
echo "Scheduling reboot for $REBOOT_TIME..."
remount_root rw
TARGET_TIME=$(date -d "$REBOOT_TIME" +%s)
NOW=$(date +%s)
if [ "$TARGET_TIME" -le "$NOW" ]; then
TARGET_TIME=$(date -d "tomorrow $REBOOT_TIME" +%s)
fi
TARGET_ISO=$(date -d "@$TARGET_TIME" --iso-8601=seconds)
REBOOT_UNIT="/etc/systemd/system/reboot-at.timer"
cat <<EOF > "$REBOOT_UNIT"
[Unit]
Description=One-off reboot timer
[Timer]
OnCalendar=$TARGET_ISO
Persistent=false
[Install]
WantedBy=timers.target
EOF
cat <<EOF > /etc/systemd/system/reboot-at.service
[Unit]
Description=Scheduled Reboot
[Service]
Type=oneshot
ExecStart=/sbin/reboot
EOF
systemctl daemon-reload
systemctl enable --now reboot-at.timer
remount_root ro
else
echo "Reboot not scheduled (disabled or invalid time)"
fi

View File

@ -1,93 +0,0 @@
#!/bin/bash
INI_FILE="/boot/kioskbrowser.ini"
SYSTEMD_DIR="/etc/systemd/system"
# Function to safely remount root FS
remount_root() {
local mode=$1
echo "Remounting root filesystem as $mode..."
mount -o remount,"$mode" / || {
echo "Failed to remount root as $mode"
exit 1
}
}
get_ini_value() {
local section=$1 key=$2
awk -F '=' -v sec="$section" -v k="$key" '
$0 ~ /^\[.*\]/ { in_section = ($0 == "[" sec "]") }
in_section && $1 ~ "^"k"$" {
gsub(/^[ \t]+|[ \t]+$/, "", $2)
print $2
exit
}' "$INI_FILE"
}
create_recurring_timer() {
local action=$1
local time=$2
local value
if [[ "$action" == "on" ]]; then
value=1
elif [[ "$action" == "off" ]]; then
value=0
else
echo "Invalid action: $action"
return 1
fi
local name="screen-${action}"
echo "Setting daily screen ${action} at ${time}"
cat <<EOF > "$SYSTEMD_DIR/${name}.timer"
[Unit]
Description=Daily screen ${action} timer
[Timer]
OnCalendar=*-*-* ${time}:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
cat <<EOF > "$SYSTEMD_DIR/${name}.service"
[Unit]
Description=Turn screen ${action}
[Service]
Type=oneshot
ExecStart=/usr/bin/vcgencmd display_power ${value}
User=pi
EOF
systemctl daemon-reload
systemctl enable --now "${name}.timer"
}
cleanup_screen_timers() {
for action in on off; do
systemctl disable --now screen-${action}.timer 2>/dev/null
rm -f "$SYSTEMD_DIR/screen-${action}.timer" "$SYSTEMD_DIR/screen-${action}.service"
done
systemctl daemon-reload
}
remount_root rw
# === MAIN ===
SCREEN_ON=$(get_ini_value screen screen_on_time)
SCREEN_OFF=$(get_ini_value screen screen_off_time)
cleanup_screen_timers
[[ "$SCREEN_ON" =~ ^[0-2][0-9]:[0-5][0-9]$ ]] && create_recurring_timer on "$SCREEN_ON"
[[ "$SCREEN_OFF" =~ ^[0-2][0-9]:[0-5][0-9]$ ]] && create_recurring_timer off "$SCREEN_OFF"
remount_root ro

View File

@ -1,65 +0,0 @@
#!/bin/bash
INI_FILE="/boot/kioskbrowser.ini"
REFRESH_INTERVAL=$(awk -F '=' '/^\[screen\]/ { in_screen=1; next }
in_screen && /^\[/ { in_screen=0 }
in_screen && $1 ~ /refresh_screen_every_x_min/ { gsub(/ /, "", $2); print $2 }' "$INI_FILE")
# Function to safely remount root FS
remount_root() {
local mode=$1
echo "Remounting root filesystem as $mode..."
mount -o remount,"$mode" / || {
echo "Failed to remount root as $mode"
exit 1
}
}
# Safely check if REFRESH_INTERVAL is a positive integer
if [[ "$REFRESH_INTERVAL" =~ ^[0-9]+$ ]] && (( REFRESH_INTERVAL > 0 )); then
echo "Setting up screen refresh every $REFRESH_INTERVAL minutes..."
SERVICE_UNIT="/etc/systemd/system/screen-refresh.service"
TIMER_UNIT="/etc/systemd/system/screen-refresh.timer"
# Remount as read-write
remount_root rw
# Write unit files
cat <<EOF | tee "$SERVICE_UNIT" > /dev/null
[Unit]
Description=Refresh Screen
After=graphical.target
[Service]
Type=oneshot
User=pi
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority
ExecStart=/usr/bin/refresh-screen
EOF
cat <<EOF | tee "$TIMER_UNIT" > /dev/null
[Unit]
Description=Run screen refresh every $REFRESH_INTERVAL minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=${REFRESH_INTERVAL}min
Persistent=false
[Install]
WantedBy=timers.target
EOF
# Reload and enable timer
systemctl daemon-reload
systemctl enable --now screen-refresh.timer
# Remount as read-only
remount_root ro
echo "Screen refresh timer setup complete."
else
echo "Invalid or missing refresh interval"
fi

View File

@ -1,60 +0,0 @@
<?php
header('Content-Type: application/json');
// Load API key from INI file
$iniFile = '/boot/kioskbrowser.ini';
if (!file_exists($iniFile)) {
http_response_code(500);
echo json_encode(["error" => "INI file not found"]);
exit;
}
$config = parse_ini_file($iniFile, true);
$API_KEY = trim($config['api']['key'], "\"'"); // Remove any surrounding quotes
// API key check
if (!isset($_GET['key']) || $_GET['key'] !== $API_KEY) {
http_response_code(403);
echo json_encode(["error" => "Forbidden"]);
exit;
}
// Get action
$action = $_GET['action'] ?? '';
switch ($action) {
case 'status':
echo json_encode([
'temperature' => trim(shell_exec("sudo vcgencmd measure_temp")),
'voltage' => trim(shell_exec("sudo vcgencmd measure_volts")),
'throttled' => trim(shell_exec("sudo vcgencmd get_throttled")),
'heartbeat' => date("Y-m-d H:i:s", filemtime("/dev/shm/heartbeat")),
]);
break;
case 'screen_off':
shell_exec("sudo vcgencmd display_power 0");
echo json_encode(["message" => "Screen turned off"]);
break;
case 'screen_on':
shell_exec("sudo vcgencmd display_power 1");
echo json_encode(["message" => "Screen turned on"]);
break;
case 'screen_refresh':
shell_exec("sudo systemctl start screen-refresh.service");
echo json_encode(["message" => "Screen refreshed"]);
break;
case 'reboot':
shell_exec("sudo reboot");
echo json_encode(["message" => "Rebooting"]);
break;
default:
http_response_code(400);
echo json_encode(["error" => "Invalid action"]);
break;
}
?>

View File

@ -1,5 +1,4 @@
<h1>Kioskbrowser</h1>
<h2>Version: <?php echo exec('git describe --tags --abbrev=0'); ?></h2>
CPU temperature: <br>
<?php passthru("sudo vcgencmd measure_temp"); ?>

View File

@ -1 +1 @@
console=serial0,115200 console=tty1 root=PARTUUID=544c6228-02 rootfstype=ext4 rw rootwait logo.nologo consoleblank=0 loglevel=0 quiet
console=serial0,115200 console=tty1 root=PARTUUID=544c6228-02 rootfstype=ext4 ro rootwait logo.nologo consoleblank=0 loglevel=0 quiet

View File

@ -28,14 +28,11 @@
enable_uart=1
disable_splash=1
dtparam=audio=on
gpu_mem=256
gpu_mem=128
# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
#max_framebuffers=2
# Rotate the screen
display_hdmi_rotate=1
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Run in 64-bit mode
arm_64bit=1
@ -49,15 +46,10 @@ disable_overscan=1
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[pi3]
arm_freq=1350
core_freq=500
gpu_freq=500
over_voltage=4
[all]
[pi4]
# Run as fast as firmware / board allows
arm_boost=1
[all]
avoid_warnings=1

View File

@ -0,0 +1,10 @@
deb http://deb.debian.org/debian bookworm main
deb-src http://deb.debian.org/debian bookworm main
deb http://deb.debian.org/debian-security/ bookworm-security main
deb-src http://deb.debian.org/debian-security/ bookworm-security main
deb http://deb.debian.org/debian bookworm-updates main
deb-src http://deb.debian.org/debian bookworm-updates main
deb http://deb.debian.org/debian bookworm-backports main

View File

@ -0,0 +1 @@
GRUB_DISTRIBUTOR="AnotterKiosk"

26
x86_skeleton/setup.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
apt update
# make sure we have all updates installed (from the -updates and -security repos)
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" -f -y dist-upgrade
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt install -f -y -t bookworm-backports linux-image-amd64
# to remove old kernel versions
apt --purge autoremove
apt install -y polkitd locales zstd dhcpcd wpasupplicant xserver-xorg-video-nouveau
locale-gen en_US.UTF-8
apt install -y firmware-amd-graphics firmware-iwlwifi firmware-brcm80211 firmware-atheros firmware-misc-nonfree firmware-realtek firmware-ath9k-htc
echo "grub-efi-amd64 grub2/force_efi_extra_removable boolean true" | debconf-set-selections
update-grub
grub-install --target=x86_64-efi --efi-directory=/boot --removable --bootloader-id=AnotterKiosk
useradd -U -m -s /bin/bash -u 1000 -G audio,video,users,input,adm,dialout,plugdev,render pi
systemctl enable dhcpcd
rm /etc/resolv.conf
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 2001:4860:4860::8888" >> /etc/resolv.conf
echo "nameserver 8.8.4.4" >> /etc/resolv.conf
echo "nameserver 2001:4860:4860::8844" >> /etc/resolv.conf