Compare commits

...

45 Commits
v0.2.6 ... main

Author SHA1 Message Date
8ac881ffcf Add apt cleanup to reduce image size 2025-07-22 17:27:24 +01:00
b7e48f776e Display git tag on web interface 2025-07-22 17:25:47 +01:00
8e8f51f2d8 no need for x86 2025-07-21 15:11:39 +01:00
751a3fed26 rename to N-AnotterKiosk 2025-07-21 15:04:54 +01:00
8b25aa87db hyperion 2025-07-21 14:12:03 +01:00
e89639703d run hyperion as pi not root 2025-07-21 12:02:06 +01:00
b04d59338d allow rw filesystem 2025-07-19 13:38:38 +01:00
4ba1d6e916 Revert to 1.0.8 and add Hyperion 2025-07-19 09:39:17 +01:00
3581873b41 run commands as sudo 2025-06-09 13:51:05 +01:00
5934a79525 add basic api 2025-06-09 13:47:58 +01:00
28d7d22cd5
Update main.yml
only on tag
2025-06-09 13:47:19 +01:00
81385f2f0e
Update main.yml
remove x86 support
2025-06-09 13:46:31 +01:00
cdd2bc2d02 fixes for all services and reverts 2025-06-04 12:59:05 +01:00
8f4762a32d Merge branch 'main' of https://github.com/karl0ss/AnotterKiosk 2025-06-04 10:34:04 +01:00
17913a5da3 not sure why this hasn't been set on the image 2025-06-04 10:34:02 +01:00
dee5a36fda
Update README.md 2025-06-02 16:22:10 +01:00
94cd861c10 remove the locked / root fs 2025-06-02 15:38:08 +01:00
225cb14d3d Fix executable bit on refresh-screen and setup-refresh-timer 2025-06-02 14:18:50 +01:00
b5bb376cc9 make the changes to system 2025-06-02 12:21:04 +01:00
f5b411ba61 overclock for pi3 only 2025-06-02 12:20:51 +01:00
4069c8d625 enable being able to refresh screen evey x mins 2025-06-02 12:11:07 +01:00
b838cc1acd get HW screen rotation working, overclock rpi, more gpu ram 2025-06-02 12:10:43 +01:00
ca023db6fe add needed keys to screen services 2025-05-31 11:57:55 +01:00
eaa6a09550 Ensure reboot and screen-timer scripts are executable 2025-05-31 11:48:08 +01:00
534d5a6634
Update main.yml
my runner needs sudo
2025-05-31 11:14:38 +01:00
d8986207bd
Update main.yml 2025-04-28 14:55:45 +01:00
5fdb39ea81
Merge pull request #3 from karl0ss/display-power-control
Display power control
2025-04-21 16:31:14 +01:00
7565fb2bdc Merge remote-tracking branch 'origin/main' into display-power-control 2025-04-21 15:29:06 +00:00
8dce7fb3fd support for turning on and off the screen at certain times 2025-04-21 15:26:59 +00:00
df89a6b54e
Merge pull request #2 from karl0ss/scheduled-reboot
logic to schedule reboot based on time in ini file
2025-04-21 16:04:00 +01:00
956ac7bf66 logic to schedule reboot based on time in ini file 2025-04-21 14:33:11 +00:00
ba0da7622c initial ini update for new keys 2025-04-21 14:13:31 +00:00
5046d5249c
Merge branch 'Manawyrm:main' into main 2025-04-21 12:58:53 +01:00
Manawyrm
1c702f90e4
Merge pull request #14 from karl0ss/add_screen_rotation
Add screen rotation
2025-04-11 15:16:05 +02:00
2fb4f35f6b
Update kioskbrowser.ini
update description in ini file
2025-04-11 14:10:57 +01:00
1d7c60ff8b
Merge pull request #1 from karl0ss/add_screen_rotation
Add screen rotation
2025-04-11 12:35:58 +01:00
c76717ddc4
Update kioskbrowser.ini
update description
2025-04-11 12:32:20 +01:00
cf6fcef28a
Update autostart
Add condition to rotate screen if enabled in kioskbrowser.ini
2025-04-11 12:31:28 +01:00
6a159135ac
Update kioskbrowser.ini
add rotate_scree to kioskbrowser.ini
2025-04-11 12:23:34 +01:00
Manawyrm
326a1426c3 kiosk: add support for custom modelines 2024-03-10 16:07:52 +01:00
Manawyrm
09e631a7a9 x86: add xserver-xorg-video-nouveau 2024-03-07 20:29:20 +01:00
Manawyrm
0d0c64a579 x86: use linux kernel from bookworm-backports 2024-03-07 17:55:59 +01:00
Manawyrm
a484f94654 kiosk: better handling of Linux kernel version reporting 2023-07-07 15:04:11 +02:00
Manawyrm
697cac37f3 raspberrypi: run rpi-update to upgrade kernel version 2023-07-07 15:01:17 +02:00
Manawyrm
df1591e1c9 x86: run apt autoremove 2023-07-07 14:40:15 +02:00
25 changed files with 493 additions and 194 deletions

View File

@ -1,45 +1,16 @@
name: CI
on:
create: { }
pull_request: { }
push:
tags:
- '*' # Triggers on any tag push
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, hetzner-cax21]
runs-on: [self-hosted]
outputs:
pkgfile: ${{ steps.pkgname.outputs.pkgfile }}
@ -55,15 +26,14 @@ jobs:
- name: Build firmware
run: |
./build_raspberry_pi.sh
sudo ./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: |
./anotterkiosk-*
./n-anotterkiosk-*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

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

View File

@ -1,17 +1,25 @@
AnotterKiosk
=============================
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)
### 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/AnotterKiosk/blob/main/.github/workflows/main.yml)
- [Images built via CI](https://github.com/Manawyrm/N-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)
@ -22,39 +30,47 @@ 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/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/N-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/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/N-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();
@ -67,6 +83,51 @@ 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
umount -fl "${BUILD_DIR}" || true
losetup -D /dev/loop0 || true
rm -rf "${BUILD_DIR}" || true
mkdir -p "${BUILD_DIR}"
sudo umount -fl "${BUILD_DIR}" || true
sudo losetup -D /dev/loop0 || true
sudo rm -rf "${BUILD_DIR}" || true
sudo 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 "AnotterKiosk Raspberry Pi version: " > "${BUILD_DIR}/version-info"
echo -n "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 anotterkiosk-${tag}-arm64-raspberrypi.img
pigz -4 anotterkiosk-${tag}-arm64-raspberrypi.img
mv raspikiosk.img n-anotterkiosk-${tag}-arm64-raspberrypi.img
pigz -4 n-anotterkiosk-${tag}-arm64-raspberrypi.img

View File

@ -1,90 +0,0 @@
#!/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,9 +2,27 @@
[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.)
@ -16,7 +34,7 @@ ssid="My WiFi"
psk="My Passphrase"
[browser]
url="https://tbspace.de/"
url="https://kittenlabs.de/"
; clear the browser cache every 10 minutes
cache_clear_interval=600
@ -37,7 +55,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@mydomain.de"
args = "-p 22 -R 1234:127.0.0.1:22 tunnel@example.com"
; 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,11 +78,32 @@ 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
dpkg --list | grep "ii chromium " >> /version-info
echo -n "Linux kernel version: " >> /version-info
ls /lib/modules/ >> /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 @@
AnotterKiosk \n \l
N-AnotterKiosk \n \l

View File

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

View File

@ -0,0 +1,11 @@
# /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

@ -0,0 +1,10 @@
[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

@ -0,0 +1,11 @@
[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,6 +24,24 @@ 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

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

View File

@ -0,0 +1,64 @@
#!/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

@ -0,0 +1,93 @@
#!/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

@ -0,0 +1,65 @@
#!/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

@ -0,0 +1,60 @@
<?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,4 +1,5 @@
<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 ro rootwait logo.nologo consoleblank=0 loglevel=0 quiet
console=serial0,115200 console=tty1 root=PARTUUID=544c6228-02 rootfstype=ext4 rw rootwait logo.nologo consoleblank=0 loglevel=0 quiet

View File

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

View File

@ -7,3 +7,8 @@ echo "deb http://deb.debian.org/debian bookworm-updates main contrib non-free" >
apt update
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" -f -y dist-upgrade
# This step is a bit risky, as the current kernel in https://github.com/raspberrypi/rpi-firmware might
# be less tested as the currently shipping kernel in the Raspberry Pi images.
apt install -y rpi-update
SKIP_CHECK_PARTITION=1 SKIP_WARNING=1 rpi-update

View File

@ -1,8 +0,0 @@
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

View File

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

View File

@ -1,22 +0,0 @@
#!/bin/bash
apt update
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" -f -y dist-upgrade
apt install -y polkitd locales zstd dhcpcd wpasupplicant
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