Turn any Windows PC into an unattended, self-healing information kiosk that displays dashboards, reports, or web pages full-screen on TVs and monitors - and keeps them running without anyone touching the machine.
Built for warehouses, production halls, offices, and lobbies, but generic enough for any "put a dashboard on a screen and forget about it" use case.
In kiosk mode the system kills explorer.exe, blocks system keys (Alt+Tab, Win,
Ctrl+Alt+Del, Alt+F4), and requires a password to exit. Each PC has its own
configuration of monitors, dashboards, and optional cameras.
- Features
- Requirements
- Quick start
- Portable vs. system runtime
- Run modes
- Configuration
- Hotkeys
- Automatic driver download
- Camera viewer (optional)
- Monitoring server
- Helper tools
- Project structure
- Architecture
- Testing
- Troubleshooting
- Contributing
- License
- Acknowledgments
- Kiosk lockdown - kills the desktop shell, blocks system keys, password to exit.
- Multi-monitor - a different application per monitor (dashboard on one, cameras on another).
- Browser display via Selenium (Firefox, Chrome, Edge) with automatic driver management.
- Portable or system - uses a bundled portable Python/Chrome if present, otherwise the system install.
- Any dashboard - reference a bundled HTML page or point straight at any URL (Grafana, Power BI, an internal app...).
- Self-healing - a watchdog restarts crashed apps and refreshes/reloads pages that fail to load.
- Scheduled restart with an on-screen warning and countdown.
- Network monitoring with optional WiFi fallback.
- Central monitoring server (FastAPI) with a live dashboard of every host.
- GUI tools for editing config, viewing logs, and inspecting system metrics.
- SQLite logging & metrics, designed to be cloud-sync friendly.
- Windows 10 or 11
- Python 3.9+ (3.11+ recommended) - either installed system-wide or bundled as a portable build in
./python - At least one browser: Firefox, Chrome, or Edge
- Browser WebDrivers - downloaded automatically into
bin/on first run (internet required)
Python dependencies (installed from requirements.txt):
| Package | Purpose |
|---|---|
selenium |
Browser automation |
psutil |
System/resource monitoring |
keyboard |
Global hotkeys & system-key blocking |
pynput |
Mouse button detection |
pywin32 |
Windows API (windows, processes, focus) |
webdriver-manager |
Automatic WebDriver downloads |
# 1. Clone
git clone https://github.com/Marak123/KioskModeSystem.git
cd KioskModeSystem
# 2. Install dependencies (system Python)
python -m pip install -r requirements.txt
# ...or, if you use the bundled portable Python:
# setup\pip-install-requirements.bat
# 3. Configure this PC (optional - sensible defaults are created automatically)
copy data\config\example_config.json data\config\%COMPUTERNAME%_config.json
notepad data\config\%COMPUTERNAME%_config.json
# ...or use the graphical editor:
# tools\ConfigEditor.bat
# 4. Test first (does NOT kill explorer)
python main.py --no-kiosk
# 5. Run for real (kiosk mode)
python main.pyThe system automatically loads
data/config/<HOSTNAME>_config.json, where<HOSTNAME>is the result of thehostnamecommand. If that file does not exist, it is created for you and the shareddefault_config.jsonis used.
Default exit password is
admin. Change it before deploying - see Security.
This project is designed to be fully portable - copied onto a USB stick or a cloud-synced folder and run on many machines without installation. It also works fine on a machine with a normal system-wide install. Detection is automatic:
| Component | If bundled in the folder... | Otherwise... |
|---|---|---|
| Python | ./python/python.exe is used |
the system python / py is used |
| Chrome | ./bin/chrome-win/chrome.exe is used |
a system-installed Chrome is used |
Neither the portable Python nor the portable Chrome is shipped in this
repository (they are large and platform-specific). To bundle them, drop a
WinPython build into ./python and a
Chrome for Testing
build into ./bin/chrome-win. See bin/README.md.
python main.py # Full kiosk (kills explorer, blocks keys)
python main.py --no-kiosk # Development mode (does NOT kill explorer)
python main.py --debug # Verbose logging
python main.py --validate # Validate the configuration and exit (code 0/1)| Flag | Description |
|---|---|
--no-kiosk |
Do not kill explorer.exe, do not block system keys |
--debug |
Enable verbose (DEBUG-level) logging |
--validate |
Only validate the configuration, then exit |
You can also launch via run.bat (or run.ps1), which activates the Python
environment and forwards any arguments.
Configuration is JSON, layered per host:
data/config/default_config.json- shared defaults for every host.data/config/<HOSTNAME>_config.json- per-PC overrides, deep-merged on top.
You only need to specify the fields you want to change; everything else falls back to the default.
default = { "network": { "internet_check_enabled": true, "wifi_fallback_enabled": false } }
host = { "network": { "wifi_fallback_enabled": true } }
result = { "network": { "internet_check_enabled": true, "wifi_fallback_enabled": true } }
Each PC can show a different application on each monitor:
{
"monitors": [
{
"monitor_index": 0,
"app_type": "Firefox",
"report_name": "ExampleDashboard",
"refresh_interval_minutes": 15,
"use_selenium": true,
"private_mode": true
},
{
"monitor_index": 1,
"app_type": "HikViewer",
"cameras": ["NVR1:3", "NVR2:20"],
"substream": "sub",
"layout": "2x1"
}
]
}Common fields: monitor_index (0 = primary), app_type (see below).
Browser fields (Firefox, Chrome, Edge):
| Field | Default | Description |
|---|---|---|
report_name |
- | A report registered in reports.py |
report_url |
- | A direct URL (alternative to report_name) |
refresh_interval_minutes |
15 | Page refresh interval |
use_selenium |
true | Drive the browser with Selenium |
private_mode |
true | Private/incognito mode |
Camera fields (HikViewer, iVMS-4200):
| Field | Default | Description |
|---|---|---|
cameras |
- | List of cameras as "RECORDER:CHANNEL" |
substream |
"sub" | Stream type: "main" or "sub" |
layout |
"2x2" | Grid layout: "1x1", "2x2", "3x3", ... |
password |
- | Recorder password |
| Type | Description | Private mode |
|---|---|---|
Firefox |
Default browser for local HTML dashboards | On |
Chrome |
Portable or system Chrome | On |
Edge |
Good for SSO dashboards (e.g. Power BI) | Off (SSO needs cookies) |
HikViewer |
External native camera viewer (see below) | - |
iVMS-4200 |
Hikvision iVMS-4200 desktop client | - |
A browser monitor can display a page in two ways:
report_name- a page registered insrc/config/software/reports.py. The system builds afile:///URL to the matching HTML file inreports/and can validate that it loaded correctly.report_url- any URL, opened directly (a Power BI dashboard, Grafana, an internal web app, anything). If both are set,report_urlwins.
{
"monitors": [
{ "monitor_index": 0, "app_type": "Edge", "report_url": "https://app.example.com/dashboard" },
{ "monitor_index": 1, "app_type": "Firefox", "report_name": "ExampleDashboard" }
]
}Bundled example pages (in reports/, registered in reports.py):
| Report name | File | Description |
|---|---|---|
ExampleDashboard |
example_dashboard.html |
Sample KPI dashboard (demo data) |
SampleReport |
sample_report.html |
Sample list/status report |
Clock |
clock.html |
Fullscreen clock and date |
NotFound |
not_found.html |
Fallback when a report is missing |
These contain only fake demo data - replace them with your own. See
reports/README.md for how to add a dashboard page and how
the validation markers work.
{
"network": {
"internet_check_enabled": true,
"internet_check_interval_seconds": 30,
"server_check_enabled": true,
"wifi_fallback_enabled": true,
"wifi_ssid": "YourWiFiNetwork",
"server_checks": [
{ "host": "192.168.1.50", "port": 8080, "name": "Monitoring Server" }
]
}
}| Field | Description |
|---|---|
internet_check_enabled |
Periodically check connectivity (ping) |
wifi_fallback_enabled |
Switch to WiFi automatically when Ethernet is down |
wifi_ssid |
WiFi network to connect to for the fallback |
server_checks |
Servers to monitor (host + port + name) |
{
"schedule": {
"restart_enabled": true,
"restart_hour": 2,
"restart_minute": 0,
"restart_days": null,
"restart_warning_seconds": 300,
"restart_countdown_seconds": 60
}
}restart_days: null = every day, or a list like [0, 2, 4] (Mon/Wed/Fri).
{
"security": {
"admin_password_hash": "",
"max_login_attempts": 3,
"lockout_time_seconds": 300
}
}The admin password protects exiting the kiosk and restarting the PC. It is stored as a salted SHA-256 hash - never in plain text.
-
Default password:
admin(used whileadmin_password_hashis empty). Change it before deploying. -
Set a new password with the ConfigEditor (Security tab), or generate a hash manually:
python -c "from src.config.software.security import SecuritySettings; print(SecuritySettings.hash_password('your-password'))"then put the result into
admin_password_hash.
Also change
PASSWORD_SALTinsrc/config/software/security.py(and the matching copy intools/src/ConfigEditor.py) for a real deployment.
{
"status": {
"status_dir": null,
"server_url": "http://192.168.1.50:8080",
"report_interval_seconds": 60
},
"monitoring": {
"enable_resource_monitoring": true,
"enable_process_monitoring": true,
"enable_screenshot_on_error": false,
"enable_health_check": true
}
}| Field | Description |
|---|---|
status_dir |
Shared/synced folder to write a JSON status file (optional) |
server_url |
URL of the monitoring server (optional) |
report_interval_seconds |
How often to report status |
Resource metrics are stored in SQLite at data/monitoring/<HOSTNAME>_monit.db.
{
"logging": {
"log_level": "INFO",
"enable_file_logging": true,
"enable_console_logging": true
}
}Logs are written to SQLite at data/logs/<HOSTNAME>.db and can be browsed with
the LogViewer tool. Levels: DEBUG, INFO, WARNING,
ERROR, CRITICAL.
{
"kiosk": {
"kill_explorer": true,
"block_system_keys": true,
"mouse_hold_duration_seconds": 3
}
}| Shortcut | Action | Password |
|---|---|---|
Ctrl+Alt+H |
Exit kiosk mode (shut down) | Yes |
Ctrl+Alt+R |
Restart all applications | No |
Ctrl+Alt+P |
Restart the computer | Yes |
Ctrl+Alt+O |
Switch between applications | No |
| Right mouse button (hold 3s) | Switch between applications | No |
In kiosk mode all other system keys are blocked; the shortcuts above are the only ones that work.
On every startup the system compares the installed browser versions with the
WebDrivers in bin/ and downloads the correct version automatically if they do
not match - so a browser auto-update never breaks the kiosk.
| Browser | Driver source |
|---|---|
| Chrome | Chrome for Testing API (googlechromelabs.github.io) |
| Edge | Microsoft Edge WebDriver |
| Firefox | GitHub Releases (mozilla/geckodriver) |
The HikViewer application type launches an external native camera viewer.
That viewer is a separate project - it is not bundled here:
https://github.com/Marak123/HikVision-KioskMode-Viewer
Build it, place HikvisionViewer.exe in bin/ (or point executable_path at
it in your monitor config), and configure cameras as shown in
Monitors. The iVMS-4200 type launches Hikvision's own desktop
client if you have it installed.
A central FastAPI server for monitoring every KioskMode host, with a live dashboard (Server-Sent Events).
With Docker (recommended):
cd server
docker-compose up -dWithout Docker:
cd server
pip install -r requirements.txt
uvicorn app:app --host 0.0.0.0 --port 8080The dashboard is then at http://<server-ip>:8080. Point each kiosk at it by
setting status.server_url in its configuration.
| Endpoint | Method | Description |
|---|---|---|
/api/status |
POST | Receive a host status |
/api/error |
POST | Receive an error report |
/api/hosts |
GET | All hosts + statuses |
/api/host/{name} |
GET | One host's details |
/api/errors |
GET | Recent errors (filterable) |
/api/metrics/{name} |
GET | Host metric history |
/api/stats |
GET | Overall statistics |
/api/stream |
GET | SSE live updates |
A host is shown offline after 120 seconds without a status.
Standalone GUI tools in tools/:
| Tool | Description |
|---|---|
ConfigEditor.bat |
Graphical editor for per-host configuration |
LogViewer.bat |
Browse/filter/export the SQLite logs |
MonitoringDashboard.bat |
Interactive charts of CPU/RAM/disk/network/processes |
StatusViewer.bat |
View JSON status files from a shared folder |
KioskModeSystem/
main.py # Entry point
SoftwareConfiguration.py # Facade re-exporting the config classes
requirements.txt # Runtime dependencies
requirements-dev.txt # + test dependencies
run.bat / run.ps1 # Launchers (activate env + run main.py)
bin/ # Drivers + optional portable Chrome (not committed)
data/
config/ # JSON configs (default + per-host + example)
logs/ monitoring/ status/ # Generated at runtime
reports/ # HTML dashboard pages (+ example pages)
src/
config/ # Config loading, validation, and definitions
core/ # App base classes, browsers, factory, orchestrator
controllers/ # Selenium + window management
handlers/ # Keyboard, mouse, monitors, kiosk mode, security
services/ # Network, scheduler, status, monitoring, validation
ui/ # On-screen notification overlay
utils/ # Logger, driver manager, dialogs, connectivity
server/ # FastAPI monitoring server (+ Docker)
setup/ # Environment activation + Windows kiosk provisioning
tools/ # GUI helper tools
tests/ # Pytest test suite
Application lifecycle:
main.py
-> KioskOrchestrator.run()
-> load & validate configuration
-> detect monitors
-> launch applications (AppFactory -> Firefox/Chrome/Edge/HikViewer/...)
-> enter kiosk mode (kill explorer, block keys, register hotkeys)
-> start background services (network, scheduler, status, monitoring)
-> watchdog loop (Tkinter event loop)
-> graceful shutdown (Ctrl+Alt+H) -> stop services -> restore explorer.exe
Watchdog (runs on background threads):
| Check | Interval | What it does |
|---|---|---|
| Alive check | 3s | Is the process/window still there? (fast) |
| Health check | 60s | Full Selenium check (page loaded, data fresh) |
| Resolution check | 120s | Reposition windows if a monitor's resolution changed |
New application types are added by subclassing BaseApp (or BrowserApp /
NativeApp) and registering them with AppFactory.register(...).
pip install -r requirements-dev.txt
pytestThe suite covers configuration loading/validation, password hashing, camera
identifier parsing, the reports registry, and the application factory. Tests run
on Windows in CI (see .github/workflows/ci.yml).
The system won't start
- Validate the config:
python main.py --validate - Check the logs:
data/logs/<HOSTNAME>.db(open withtools/LogViewer.bat) - Run in debug mode:
python main.py --no-kiosk --debug
A browser doesn't show the report
- The system validates the page and auto-refreshes/restarts on failure.
- Check that the report's HTML file exists in
reports/. - For
report_url, confirm the URL is reachable from the kiosk PC.
Driver incompatible with the browser
- Drivers are downloaded automatically; a failure usually means no internet.
- Manual download: Chrome,
Edge,
Firefox - place the
.exeinbin/.
explorer.exe didn't come back after a crash
- The system restores it on exit. If not, press
Ctrl+Shift+Esc(Task Manager)File > Run new task >
explorer.exe.
Contributions are welcome! Please read CONTRIBUTING.md and the Code of Conduct.
Released under the MIT License. Copyright (c) 2026 Marak123.
Created by Marak123. Built with the assistance of ChatGPT.