mirror of
https://github.com/karl0ss/AnotterKiosk.git
synced 2025-07-28 03:00:16 +01:00
Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
8ac881ffcf | |||
b7e48f776e | |||
8e8f51f2d8 | |||
751a3fed26 | |||
8b25aa87db | |||
e89639703d | |||
b04d59338d | |||
4ba1d6e916 | |||
3581873b41 | |||
5934a79525 | |||
28d7d22cd5 | |||
81385f2f0e | |||
cdd2bc2d02 | |||
8f4762a32d | |||
17913a5da3 | |||
dee5a36fda | |||
94cd861c10 | |||
225cb14d3d | |||
b5bb376cc9 | |||
f5b411ba61 | |||
4069c8d625 | |||
b838cc1acd | |||
ca023db6fe | |||
eaa6a09550 | |||
534d5a6634 | |||
d8986207bd | |||
5fdb39ea81 | |||
7565fb2bdc | |||
8dce7fb3fd | |||
df89a6b54e | |||
956ac7bf66 | |||
ba0da7622c | |||
5046d5249c | |||
![]() |
1c702f90e4 | ||
2fb4f35f6b | |||
1d7c60ff8b | |||
c76717ddc4 | |||
cf6fcef28a | |||
6a159135ac | |||
![]() |
326a1426c3 | ||
![]() |
09e631a7a9 |
44
.github/workflows/main.yml
vendored
44
.github/workflows/main.yml
vendored
@ -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 }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
raspikiosk.img
|
||||
raspios.img.xz
|
||||
work/
|
||||
.DS_Store
|
||||
|
97
README.md
97
README.md
@ -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 :)
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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/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.
|
||||
|
||||
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.
|
||||
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
|
||||
- https://github.com/guysoft/FullPageOS
|
||||
|
@ -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
|
90
build_x86.sh
90
build_x86.sh
@ -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
|
@ -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.
|
||||
|
@ -78,6 +78,23 @@ 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
|
||||
@ -86,3 +103,7 @@ 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
|
||||
|
@ -1,2 +1,2 @@
|
||||
AnotterKiosk \n \l
|
||||
N-AnotterKiosk \n \l
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
10
kiosk_skeleton/etc/systemd/system/schedule-reboot.service
Normal file
10
kiosk_skeleton/etc/systemd/system/schedule-reboot.service
Normal 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
|
@ -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
|
@ -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 \
|
||||
|
4
kiosk_skeleton/usr/bin/refresh-screen
Executable file
4
kiosk_skeleton/usr/bin/refresh-screen
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
export DISPLAY=:0
|
||||
/usr/bin/xdotool key F5
|
64
kiosk_skeleton/usr/bin/schedule-reboot
Executable file
64
kiosk_skeleton/usr/bin/schedule-reboot
Executable 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
|
93
kiosk_skeleton/usr/bin/schedule-screen-timers
Executable file
93
kiosk_skeleton/usr/bin/schedule-screen-timers
Executable 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
|
65
kiosk_skeleton/usr/bin/setup-refresh-timer
Executable file
65
kiosk_skeleton/usr/bin/setup-refresh-timer
Executable 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
|
60
kiosk_skeleton/var/www/html/api.php
Normal file
60
kiosk_skeleton/var/www/html/api.php
Normal 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;
|
||||
}
|
||||
?>
|
@ -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"); ?>
|
||||
|
@ -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
|
@ -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
|
@ -1,10 +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
|
||||
|
||||
deb http://deb.debian.org/debian bookworm-backports main
|
@ -1 +0,0 @@
|
||||
GRUB_DISTRIBUTOR="AnotterKiosk"
|
@ -1,26 +0,0 @@
|
||||
#!/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
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user