King Kiosk User Guide¶
Your Complete Guide to the World's First Programmable Display Runtime™
Version 2.3 | Beta Tester Edition
Table of Contents¶
- Welcome to King Kiosk
- What is King Kiosk?
- Why King Kiosk is Different
- Platform Availability
- How Widget Windows Work
- Getting Started
- Initial Setup
- Privacy & Telemetry
- Android Kiosk Mode (Lockdown)
- iOS/iPadOS Kiosk Mode (Guided Access)
- macOS Kiosk Mode (Best-Effort)
- Connecting to Your MQTT Server
- Understanding Your Device ID
- Home Assistant Auto-Discovery
- The MQTT Architecture
- How King Kiosk Communicates
- Message Addresses (Topics) Explained
- Command and Response Flow
- Secure Command Signing (Optional)
- The Windowing System
- Understanding Tiles and Windows
- Controlling Windows via Touch and Mouse
- Controlling Windows via MQTT
- Window Layouts and Arrangements
- Saving and Managing Screen States
- What is a Screen State?
- Saving Your Layout
- Loading Saved Layouts
- Exporting and Importing for Backup
- Remote Feature Server Provisioning
- Scheduling Screen States
- Fleet Layout Replication
- System Commands
- Device Control
- Volume and Brightness
- Notifications and Alerts
- Visual Effects
- Text-to-Speech
- Speech-to-Text
- Screenshots and Screen Capture
- Background Settings
- Screensaver
- AI Integration
- Batch Commands and Scripting
- Widget Reference
- Web Browsers
- Remote Browser (Apple TV & iOS)
- Clock Widget
- Weather Widget
- Media Player
- YouTube Player
- Image Carousel
- Gauges and Thermostats
- Charts
- Maps
- Canvas (Visual Diagrams)
- Animated Text
- MQTT Image
- MQTT Button
- Calendar (with Bidirectional Sync)
- Timers and Stopwatch
- Alarmo (Security System)
- DLNA Player
- PDF Viewer
- Games
- LED Light Panel
- RSS Feed Reader
- Building Your Own Widgets
- Why Build Custom Widgets?
- Quick Start
- Adding Your Widget
- Connecting Your Widget to King Kiosk
- Receiving Commands
- Sending Data Back From Your Widget
- Persistent Storage
- Platform Considerations
- Complete Example: Smart Thermostat
- Native Widget Alternatives
- Canvas: Declarative Native Graphics
- MQTT Image: External Rendering
- Dynamic Native Widgets
- Home Assistant Integration
- Creating Custom Sensors
- Controlling King Kiosk from Home Assistant
- Tips and Best Practices
- Performance Optimization
- Network Considerations
- Troubleshooting Common Issues
- Apple TV Experience
- A Revolutionary Window System for Television
- Controlling with the Siri Remote
- Understanding Focus Navigation
- Move Mode: Rearranging Your Layout
- Interactive Mode: Deep Widget Control
- MQTT Control for Apple TV
- Feature Server
- What is the Feature Server?
- Intercom & Broadcast
- Remote Browser
- RTSP Camera Export
- Home Assistant MQTT Bridge
- Detection, Recognition & Motion
- Overview
- On-Device vs Feature Server Detection
- Person & Object Detection
- Facial Recognition
- Face Training (Enrollment)
- Motion Detection
- Camera Snapshots (Motion-Triggered)
- MQTT Topics Reference
- Home Assistant Integration
- Automation Examples
- Quick Reference Card
- Remote Browser: Full Web on Your TV
1. Welcome to King Kiosk¶
What is King Kiosk?¶
King Kiosk is the world's first Programmable Display Runtime™ — it turns any screen into a remotely programmable display surface. It's not digital signage. It's not a kiosk. It's not a dashboard. It's all of those things and none of them, because you decide what it becomes by sending it instructions.
Whether you're managing a single tablet on your kitchen wall or orchestrating hundreds of displays across multiple locations, King Kiosk gives you a composable, MQTT-driven runtime that renders whatever you need: dashboards, media, cameras, gauges, web content, intercoms — all in real-time, all remotely controllable, from anywhere in the world.
That's King Kiosk. A Programmable Display Runtime™.
Why King Kiosk is Different¶
Existing categories are dead ends. Digital signage loops slideshows. Kiosk software locks to one app. Dashboard tools can't manage displays remotely. Smart home panels lack windowing. You've always had to choose a category. King Kiosk created a new one: the Programmable Display Runtime™.
Here's what sets us apart:
True Multi-Window Compositing King Kiosk lets you compose multiple widgets simultaneously on any screen. Show a clock, weather, calendar, and live camera feed all at once, each in its own resizable, independently controllable tile.
Real-Time Remote Control Through MQTT (a lightweight messaging system - think of it as a walkie-talkie for your devices), you can control every aspect of your displays instantly. Change content, adjust layouts, send notifications, trigger alerts - all in real-time from anywhere.
Home Assistant Native King Kiosk speaks the language of smart homes. It auto-discovers itself to Home Assistant, publishes sensor data, and responds to automations. Your displays become part of your smart home ecosystem.
AI-Powered Voice Control Connect to Home Assistant's conversation agents, Anthropic's Claude, OpenAI, Google Gemini, or even local Ollama instances. Your displays can listen, understand, and respond.
Cross-Platform Consistency The same powerful experience on Android, iOS, macOS, Windows, Linux, and even Apple TV. One interface, one command structure, everywhere.
Secure by Design Optional command signing ensures only authorized commands reach your displays - like a secret handshake that verifies messages are genuine. Perfect for enterprise deployments.
Platform Availability¶
King Kiosk runs on: - Android - Phones, tablets, and dedicated kiosk hardware - iOS - iPhones and iPads - macOS - Mac desktops and laptops - Windows - Desktop and kiosk PCs - Linux - Desktop and embedded (including Raspberry Pi 4 & 5) - tvOS - Apple TV - Web - Browser-based deployment (limited features)
Feature Server note: On tvOS, Linux, and Raspberry Pi, the Web Browser, YouTube Player, and Custom Widget SDK require the Feature Server (KingKiosk Core) to provide server-side rendering via Remote Browser. All other widgets run natively on every platform.
How Widget Windows Work¶
Every widget in King Kiosk lives inside a window, sometimes called a tile. That window is more than a frame around the widget. It is a shared layer that gives all widgets the same core layout behavior.
All widgets share the same window background system
You can give any widget window one of four background styles:
- transparent
- color
- gradient
- image
You can also control the background opacity separately from the whole window opacity:
- opacity fades the entire window, including the widget itself
- background_opacity only fades the background layer behind the widget
This makes a big difference when you want to save space visually. For example, you can keep gauge text sharp and readable while making only the panel behind it translucent or fully transparent.
If you do not want to think about all of those settings separately, use appearance_mode.
transparentmeans "make the window and the widget's base panel transparent"translucentmeans "give me a glassy semi-transparent panel behind the widget"opaquemeans "give me a solid panel"
This is the simple option. You can still drop down to surface.background_* or widget-specific background_* settings later if you want exact control.
All widgets support proportion-aware resizing
In floating mode, the built-in resize handle keeps the window's shape by default instead of stretching it freely. For most native King Kiosk widgets, the content inside also scales as one unit. That means the whole design stays in proportion, including text, icons, labels, and gauge readouts.
A small exception for browser and video style widgets
Browser, PDF, video, remote browser, and call-style widgets still use the shared window background system, and their windows still keep their proportions by default. But full content scaling is not forced on those widget types because embedded platform views can render badly when transformed. In other words, the window still behaves consistently, but King Kiosk avoids breaking the content technology inside it.
Why this matters for future widgets
These behaviors now live in the shared window layer instead of being reimplemented widget by widget. That means new widgets inherit the same background and scaling rules automatically unless they intentionally opt into something special.
2. Getting Started¶
Initial Setup¶
The first time you launch King Kiosk, the app walks you through a short setup process before you reach the main screen.
Step 1: Accept the Terms of Service¶
The first screen you'll see is the Terms of Service. Take a moment to read through the terms, then tap "I Accept the Terms of Service" at the bottom to continue. You only need to do this once — the app remembers your acceptance for future launches.
Step 2: Set Up Your Connection (MQTT Wizard)¶
After accepting the Terms of Service, King Kiosk opens a five-step setup wizard that helps you connect your device to an MQTT broker. MQTT is the messaging system that lets you control your displays remotely. The wizard walks you through:
-
Name your display — Give your device a friendly name like "living-room-display" or "lobby-screen-1". This name identifies your device on the network and is used in MQTT topic paths. The app suggests a name based on your device's hostname, but you can change it to whatever you like.
-
Connect to your broker — Enter your MQTT broker address (for example,
broker.emqx.io) and port. If your broker uses SSL/TLS, turn on the SSL toggle and the port will automatically switch to 8883. You can also allow self-signed certificates if your setup requires it. -
Authentication — If your MQTT broker requires a username and password, enter them here. If your broker doesn't require authentication, just leave these fields blank and move on.
-
Final touches & connection test — Choose whether to enable Home Assistant Discovery (on by default), which automatically registers your device with Home Assistant. You can also enable Reconnect on startup (also on by default) so the app automatically reconnects to your broker each time it launches. When you're ready, tap "Connect & Finish" and the app will test the connection. If it succeeds, you'll see a green confirmation.
-
Crash Reports & Diagnostics — You're asked whether to opt in to anonymous crash reports and diagnostics. This is off by default — no data is sent unless you explicitly enable it. No personal data, MQTT message content, or usage analytics are ever collected. You can change this choice at any time in Settings → App Settings → Crash Reports & Diagnostics. See Privacy & Telemetry for full details.
Not ready to connect? You can tap "Skip for now" at the bottom of any wizard step to go straight to the main screen. You can always set up your MQTT connection later in Settings.
After Setup¶
Once you're on the main screen, here are a couple of things to do next:
- Access Settings — Tap or click the lock icon to unlock your display. You'll be prompted to enter a PIN — the default PIN is 1234.
- Change Your PIN — For security, go to Settings → Security and change the default PIN to something unique for your deployment.
Privacy & Telemetry¶
KingKiosk can optionally share anonymous crash reports and error diagnostics to help improve the app. This feature is off by default and requires your explicit opt-in — either during the setup wizard (step 5) or later in settings.
What is collected (when enabled): - Anonymous crash reports (stack traces, device model, OS version) - Error diagnostics that help identify and fix bugs
What is never collected: - Personal data of any kind - MQTT message content or topics - Usage analytics or browsing history - Screenshots, media, or widget content
How to change your preference:
- In the app: Go to Settings → App Settings → Crash Reports & Diagnostics and toggle the switch on or off.
- Remotely via MQTT: Send a
set_telemetrycommand tokingkiosk/{device_id}/system/cmd: To query the current state, send{"command": "get_telemetry"}and check the response onkingkiosk/{device_id}/system/response.
You can change this setting at any time. Disabling it stops all telemetry immediately.
Android Kiosk Mode (Lockdown)¶
On Android, King Kiosk can lock a device so it stays dedicated to the display—great for wall tablets, public screens, and smart-home dashboards.
There are two practical levels of kiosk lockdown on Android:
- Standard kiosk (no Device Owner): easy to enable in Settings, but a determined user can often escape depending on the device/OEM.
- Full kiosk (Device Owner): strongest lockdown; King Kiosk can enforce itself as HOME and apply device-owner kiosk policies.
Standard Setup (No Device Owner)¶
In Settings -> App Settings -> Kiosk Mode: 1. Enable kiosk mode and approve the requested permissions (Device Admin / overlays / battery optimization prompts). 2. When prompted, set King Kiosk as the Home/Launcher app and choose Always.
What to expect: the device stays in King Kiosk most of the time, but some devices still allow exit via certain system gestures/shortcuts.
Full Lockdown (Device Owner Setup)¶
Device Owner setup is intended for dedicated kiosk devices and usually requires a freshly reset device. This is a more advanced setup typically done by IT administrators.
- Factory reset the device.
- Before adding any Google account, connect the device to a computer via USB and run a special command using Android development tools. (If you're not familiar with this process, consult your IT administrator or search for "Android Device Owner provisioning" tutorials.)
- Launch King Kiosk and enable Kiosk Mode.
What to expect: The strongest lockdown available - users cannot exit the app using normal means. To recover from a misconfiguration, you may need physical access to the device, and in worst cases, a factory reset.
iOS/iPadOS Kiosk Mode (Guided Access)¶
iOS and iPadOS are much more locked down than Android. For “true kiosk mode” you typically use:
- Guided Access (recommended for personal devices): enabled by the user in iOS Settings, then started from the device.
- Single App Mode (enterprise/education): requires a supervised device and device management software (MDM - Mobile Device Management, used by businesses and schools).
In King Kiosk, enabling Kiosk Mode on iOS is best-effort: - The app can hide system UI and lock orientation. - The app can attempt to start Guided Access if it’s enabled, otherwise it will show instructions.
To enable Guided Access: 1. On the iPhone/iPad, go to Settings -> Accessibility -> Guided Access and turn it on. 2. Open King Kiosk. 3. Triple‑click the Side/Home button and choose Guided Access, then tap Start.
macOS Kiosk Mode (Best-Effort)¶
macOS does not support a fully locked-down kiosk mode without device management and system policy.
King Kiosk can still provide a solid “kiosk-like” experience on macOS: - Fullscreen + Always on Top - Hide Dock and menu bar (best-effort) - Reduce app switching and some system UI access (best-effort)
For true kiosks on macOS, use a dedicated macOS user account with parental controls or restrictions, and/or device management software that limits system access.
Connecting to Your MQTT Server¶
MQTT is the communication backbone of King Kiosk - it's how your displays receive commands and send back status updates. Think of an MQTT server (also called a "broker") as a central message hub that routes commands to your devices.
You'll need access to an MQTT server - this could be: - A local server like Mosquitto running on a Raspberry Pi or home computer - Your Home Assistant's built-in MQTT server - A cloud MQTT service like HiveMQ or CloudMQTT
In Settings, navigate to the MQTT section and enter: - Server Address: The IP address or web address of your MQTT server (e.g., 192.168.1.100 or mqtt.example.com) - Port: Usually 1883 for standard connections, 8883 for encrypted connections - Username & Password: If your server requires login credentials - Enable SSL: For encrypted, secure connections
Once connected, King Kiosk automatically listens for commands and begins sending status updates.
Understanding Your Device ID¶
Every King Kiosk installation has a unique device ID. This ID is used in all MQTT topics to ensure commands reach the right display. Your device ID is based on your configured device name, sanitized for MQTT compatibility.
For example, if you name your device "Living Room Display", your device ID might be living_room_display or similar.
You can find your device ID in Settings under the Device Information section.
Home Assistant Auto-Discovery¶
If you use Home Assistant, King Kiosk can automatically appear as a device in your smart home dashboard. When enabled, King Kiosk announces itself using Home Assistant's MQTT Discovery protocol.
To enable auto-discovery: 1. Ensure your Home Assistant is using the same MQTT broker as King Kiosk 2. In King Kiosk Settings, navigate to MQTT > Home Assistant Discovery 3. Toggle the feature on
Once connected, your display will appear in Home Assistant with: - Sensors for device status, battery level, screen state, and more - Buttons for common actions like refresh, screenshot, and restart - Camera entity that displays periodic screenshots of your display
This means you can monitor and control your King Kiosk displays directly from the Home Assistant UI, include them in automations, and view their status on your HA dashboards - all without any manual YAML configuration.
3. The MQTT Architecture¶
How King Kiosk Communicates¶
King Kiosk uses an elegant two-level communication model:
System Commands control the device as a whole - things like volume, brightness, creating windows, taking screenshots, and managing layouts.
Element Commands control individual widgets - like telling a specific clock to switch from analog to digital, or a carousel to advance to the next slide.
This separation means you can manage displays at whatever level of detail you need.
Message Addresses (Topics) Explained¶
Every MQTT message has a "topic" - think of it as the mailing address that tells the system where to deliver the message. King Kiosk uses a consistent, easy-to-follow address structure:
Sending Commands TO King Kiosk:
kingkiosk/{your-device-id}/system/cmd -> System-wide commands
kingkiosk/{your-device-id}/element/{element_id}/cmd -> Element-specific commands
kingkiosk/fleet/{fleet_id}/cmd -> Fleet-wide system commands
kingkiosk/fleet/{fleet_id}/element/{element_id}/cmd -> Fleet-wide element commands
kingkiosk/{your-device-id}/window/{window-id}/command -> Window-controller commands (legacy compatibility; prefer `system/cmd`)
For fleet element topics, the {element_id} in the topic path is the target (topic-addressed routing).
Receiving Responses FROM King Kiosk:
kingkiosk/{your-device-id}/system/response -> System command results
kingkiosk/{your-device-id}/element/{element_id}/response -> Element command results
kingkiosk/{your-device-id}/element/{element_id}/state -> Element current state
kingkiosk/{your-device-id}/element/{element_id}/event -> Element events
kingkiosk/{your-device-id}/info -> Device capabilities
kingkiosk/{your-device-id}/status -> Online/offline status
Command and Response Flow¶
Commands are sent as structured text messages in a format called JSON (JavaScript Object Notation) - basically name-value pairs wrapped in curly braces. Here's the basic format:
For example, to set the volume to 50%:
King Kiosk responds with a JSON result:
{
"success": true,
"command": "set_volume",
"volume": 0.5,
"timestamp": "2024-12-19T10:30:00.000Z"
}
Tracking Your Commands¶
If you send many commands, responses can arrive out of order. To make it easy to match a response to the command that triggered it, you can include a correlation_id field (a tracking number of your choice) in the request. King Kiosk will include that same value in the response.
Why it helps: - Lets your automations match responses to the right command - Helps avoid confusion when multiple devices or automations are active at once - Makes troubleshooting easier because you can search by a single ID
How to use it:
- Pick any short, unique ID (for example ui-42 or req-2024-12-19-01)
- Include it in your command, and the response will have the same ID
- If you don't include a tracking ID, everything still works - you'll just match responses by timing or content
Example with correlation_id:
Secure Command Signing (Optional)¶
For deployments requiring extra security, King Kiosk supports signed commands. When enabled, commands must include a special code that proves they came from an authorized source - like a digital wax seal that verifies authenticity.
Signed commands wrap your regular command in a protective envelope:
The verification code ("sig") is calculated using a secret password known only to you and your devices. This prevents unauthorized people from sending commands to your displays.
4. The Windowing System¶
Understanding Tiles and Windows¶
King Kiosk's windowing system is what truly sets it apart. Instead of a single full-screen view, you can create multiple "tiles" - each containing a different widget.
Window Properties:
- Position (x, y) - Where on screen the window appears
- Size (width, height) - How large the window is
- Movement Animation (animate) - MQTT move/update commands animate by default; set "animate": false to snap instantly
- Opacity - Transparency level (0.0 = invisible, 1.0 = fully visible)
- Window Border (showBorder) - The shared thin white outline around a window; off by default and only shown when you set showBorder: true
- Background Surface - Every widget window supports transparent, color, gradient, or image
- Background Opacity - Separate from the overall window opacity
- Preserve Aspect Ratio - On by default when you drag the resize handle in floating mode
- Scale Content Together - Most native widgets scale the whole design, so text and graphics stay in proportion
- Stacking Order (Z-Index) - Which window appears on top when they overlap (higher numbers appear on top, like stacking papers)
Setting Opacity When Creating Windows:
Most widget creation commands accept an opacity parameter:
Every widget window also supports a shared window surface configuration. This is the preferred way to set the outer window background consistently across all widgets:
{
"command": "create_gauge",
"window_id": "transparent-gauge",
"gauge_id": "transparent-gauge",
"surface": {
"background_mode": "transparent",
"background_opacity": 0.6,
"preserve_aspect_ratio": true,
"scale_content": true
}
}
Background modes include transparent, color, gradient, and image.
Use surface when you want a consistent outer window background across widget types. Some widgets, such as Clock, Carousel, Canvas, Animated Text, and RSS, also have their own internal background settings. Those still work, but they style the widget itself rather than the shared outer window.
The same shared window layer also controls the thin white border around tiles. Every windowed widget accepts showBorder (or show_border) and the default is off:
{
"command": "open_clock",
"window_id": "kitchen-timer",
"showBorder": true,
"x": 100,
"y": 100,
"width": 380,
"height": 120
}
You can also change that border later without recreating the widget:
If you want one plain-English shortcut instead, use appearance_mode:
{
"command": "alarmo_widget",
"window_id": "front-door-alarm",
"appearance_mode": "opaque",
"opacity": 1.0,
"x": 120,
"y": 80,
"width": 420,
"height": 720
}
Think of it this way:
- appearance_mode chooses the overall look
- surface is for exact outer-window control
- widget-specific background_* settings are only for that widget's own interior
- opacity still fades everything, including text and buttons
Controlling Z-Index (Window Stacking Order):
New windows are automatically placed on top. To change stacking order for existing windows, use the bring_to_front and send_to_back commands:
The z-index is automatically managed - bring_to_front assigns the highest z-index, while send_to_back assigns the lowest. Window stacking order is preserved when you save and load screen states.
Windows can be in different states: - Normal - Regular display - Minimized - Hidden but not closed - Maximized - Full screen
Controlling Windows via Touch and Mouse¶
King Kiosk windows respond to both touch and mouse input, giving you intuitive direct control regardless of your device type.
Touch Controls (Tablets, Phones, Touch Screens): - Tap and drag the title bar - Move the window - Drag the edges or corners - Resize the window - Double-tap the title bar - Toggle maximize - Swipe down on title bar - Minimize - Tap the X button - Close the window
Mouse Controls (Desktop, Laptop): - Click and drag the title bar - Move the window - Drag the edges or corners - Resize the window - Double-click the title bar - Toggle maximize - Click the minimize button - Minimize the window - Click the X button - Close the window - Right-click the title bar - Access window context menu - Mouse wheel - Scroll content within the window
Apple TV Remote: - D-pad - Navigate between windows using spatial focus - Select button - Interact with focused window - Play/Pause - Enter Move Mode to reposition windows - Menu button - Exit modes, go back, close menus
See Section 11: Apple TV Experience for the complete guide to King Kiosk's revolutionary multi-window system on tvOS.
Controlling Windows via MQTT¶
Creating, moving, and managing windows remotely is where King Kiosk shines.
Creating a Clock Window:
Creating a Web Browser Window:
Moving a Window:
Window movement is smoothly animated by default (including Apple TV). To snap immediately instead of animating:
Resizing a Window:
When you resize by dragging the on-screen handle, King Kiosk preserves the window's aspect ratio by default. For most built-in widgets, the whole widget content also scales proportionally inside the window so labels, text, and graphics stay matched.
Closing a Window:
Closing All Windows:
Bringing a Window to Front:
Sending a Window to Back:
Maximizing a Window:
Minimizing a Window:
Window Layouts and Arrangements¶
Switch between different layout modes:
Set Window Mode:
Available modes:
- tiling or tile - Windows automatically arrange in a grid
- floating or float - Windows can overlap freely
- toggle - Switch between tiling and floating
5. Saving and Managing Screen States¶
What is a Screen State?¶
A screen state is a snapshot of your current display layout - all the windows, their positions, sizes, and configurations. Think of it as a "preset" you can recall instantly.
This is incredibly powerful for scenarios like: - Morning Mode - Calendar, weather, and news - Movie Mode - Single full-screen media player - Dashboard Mode - Multiple gauges and charts for monitoring - Presentation Mode - Specific content for meetings
Saving Your Layout¶
Once you've arranged your display how you like it:
If a state with that name already exists and you want to overwrite it:
Loading Saved Layouts¶
Instantly switch to a saved layout:
All current windows will close, and the saved layout will be restored exactly as you saved it.
Listing Your Saved States¶
See all available layouts:
Response includes the name, window count, and when each was saved.
Deleting a Saved State¶
Remove a layout you no longer need:
Exporting and Importing for Backup¶
Export a layout for backup or transfer:
The response includes the complete screen state data that you can save externally.
Import a previously exported layout:
{
"command": "import_screen_state",
"name": "Imported Dashboard",
"screen_state": { ...exported data... },
"overwrite": false
}
Tip: You can include response_topic and correlation_id on these commands to make automation workflows easier to track.
Full Device Backup and Restore (Settings + Layouts)¶
If you want a complete portable snapshot (not just one layout), use get_config.
Step 1: Export a full backup
{
"command": "get_config",
"include_secrets": true,
"include_layouts": true,
"correlation_id": "backup-2026-02-06",
"response_topic": "kingkiosk/my_device/system/response"
}
This returns:
- config (all current settings)
- screen_states (all saved named layouts)
- current_layout (the live layout snapshot)
- screen_state_count
If you plan to share the backup outside your trusted environment, set include_secrets to false so sensitive fields are masked.
Step 2: Restore (or clone to another tablet)
{
"command": "provision",
"settings": { ...config from backup... },
"screen_states": [ ...screen_states from backup... ],
"current_layout": { ...current_layout from backup... },
"overwrite": true,
"correlation_id": "restore-2026-02-06"
}
To clone multiple devices, publish the same payload to each target device's kingkiosk/{device_id}/system/cmd topic.
Provision responses include what succeeded and failed:
- applied_settings
- failed_settings
- screen_states_imported
- screen_states_failed
- current_layout_applied
Remote Feature Server Provisioning¶
You can configure Feature Server settings remotely over MQTT, so you do not need to open the Settings screen on each device.
In plain English:
- featureServerEnabled: turns Feature Server on or off.
- featureServerAutoConnect: if on, the app will reconnect automatically after app restart/network recovery.
- featureServerUrl: the server host/IP to connect to (for best cross-platform compatibility, use just host/IP, for example 192.168.1.50).
- featureServerUseHttps: use secure WebSocket (wss) if your server is configured for TLS.
- featureServerProduceAudio: whether microphone audio should be produced to the server.
- autoAcceptVideoconferenceCalls: whether incoming video conference calls should auto-open and join on this kiosk.
- intercomEnabled: whether the device should participate in intercom/broadcast.
- autoAcceptIncomingIntercom: whether incoming intercom broadcasts should auto-open on this kiosk.
Example command (publish to kingkiosk/{device_id}/system/cmd):
{
"command": "provision",
"settings": {
"featureServerEnabled": true,
"featureServerAutoConnect": true,
"featureServerUrl": "192.168.1.50",
"featureServerUseHttps": false,
"featureServerProduceAudio": true,
"autoAcceptVideoconferenceCalls": true,
"intercomEnabled": true,
"autoAcceptIncomingIntercom": true
},
"correlation_id": "feature-server-setup-001"
}
How to verify it worked:
- Subscribe to kingkiosk/{device_id}/feature_server/state.
- This topic is retained, so you get the latest status immediately.
- Watch fields like enabled, connected, state, and last_error.
Important behavior:
- If featureServerEnabled is false, state stays disabled (gray in UI) and auto-reconnect does not run.
- If autoAcceptVideoconferenceCalls is false, incoming recipient-side conference calls do not open a built-in dialog. The kiosk publishes MQTT notifications instead so you can automate custom behavior.
- If autoAcceptIncomingIntercom is false, incoming intercom broadcasts do not auto-open. The kiosk publishes MQTT notifications instead.
- Incoming communication notifications are published to kingkiosk/{device_id}/communications/event.
- Current event names are incoming_conference_call, incoming_conference_call_accepted, incoming_conference_call_ended, incoming_conference_call_rejected, incoming_intercom, incoming_intercom_accepted, and incoming_intercom_ended.
Scheduling Screen States¶
Automatically switch layouts based on time of day:
Set up a schedule:
{
"command": "set_screen_schedule",
"entries": [
{
"id": "morning",
"screen_state": "Morning Dashboard",
"at": "07:00",
"days": [1, 2, 3, 4, 5],
"enabled": true
},
{
"id": "evening",
"screen_state": "Evening Mode",
"at": "18:00",
"days": [1, 2, 3, 4, 5, 6, 7],
"enabled": true
}
],
"enabled": true
}
Days are: 1=Monday, 2=Tuesday, ... 7=Sunday
View current schedule:
Enable/Disable scheduling:
Manually trigger a scheduled state:
Fleet Layout Replication¶
Fleet management is not just for layouts.
Think of a fleet as a shared "group channel" where all subscribed screens can receive:
- Layout updates (kingkiosk/fleet/{fleet_id}/layout)
- Real-time system commands (kingkiosk/fleet/{fleet_id}/cmd)
- Real-time element commands (kingkiosk/fleet/{fleet_id}/element/{element_id}/cmd)
In plain terms:
- subscribe_fleet = "Join this group for layout and/or command updates."
- replicate_layout = "Copy my current layout to the whole group."
- broadcast_fleet_command = "Send one command to the whole group at once."
- unsubscribe_fleet = "Leave the group."
Common Use Cases¶
- Retail chains: Push a layout and then trigger a synchronized chime across all stores.
- Schools/offices: One command to show alerts, adjust volume, or speak a message everywhere.
- Emergency response: Immediately notify all kiosks with sound + visual message.
- Operations: Roll out layout changes and run follow-up commands in one workflow.
Topics Used (What Goes Where)¶
- Per-device command input:
kingkiosk/{device_id}/system/cmd - Fleet layout bus:
kingkiosk/fleet/{fleet_id}/layout - Fleet command bus:
kingkiosk/fleet/{fleet_id}/cmd - Fleet element command bus:
kingkiosk/fleet/{fleet_id}/element/{element_id}/cmd - Per-device responses:
kingkiosk/{device_id}/system/response(or an allowed same-deviceresponse_topic)
Step 1: Subscribe Devices to a Fleet¶
Send this to each screen that should join the fleet:
{
"command": "subscribe_fleet",
"fleet_id": "lobby-displays",
"auto_apply": true,
"subscribe_layout": true,
"subscribe_commands": true
}
auto_apply: trueapplies incoming fleet layouts immediately.subscribe_commands: trueenables fleet-wide command broadcast execution.allow_self_commands: trueis optional if the publishing device should also execute its own fleet command.
Step 2A: Broadcast a Layout to the Fleet¶
Useful options:
{
"command": "replicate_layout",
"target_topic": "kingkiosk/fleet/lobby-displays/layout",
"correlation_id": "fleet-sync-2026-03-03-001"
}
Step 2B: Broadcast a Command to the Fleet¶
Option 1: Publish directly to the fleet command topic:
Topic:
kingkiosk/fleet/lobby-displays/cmd
Payload:
{
"command": "play_audio",
"url": "https://example.com/alert.mp3",
"source_device": "hq-controller",
"correlation_id": "sound-all-2026-03-03-001"
}
For element-specific fan-out (same element IDs on all screens), publish to:
Topic:
kingkiosk/fleet/lobby-displays/element/ticker-main/cmd
Payload:
{
"command": "set_opacity",
"opacity": 0.5,
"source_device": "hq-controller",
"correlation_id": "element-sync-2026-03-03-001"
}
Option 2: Ask a device to publish a fleet command for you:
{
"command": "broadcast_fleet_command",
"fleet_id": "lobby-displays",
"fleet_command": {
"command": "notify",
"title": "Attention",
"message": "Store closes in 10 minutes."
}
}
To fan out to the same element IDs on every device, set target_topic to the fleet element bus:
{
"command": "broadcast_fleet_command",
"fleet_id": "lobby-displays",
"target_topic": "kingkiosk/fleet/lobby-displays/element/ticker-main/cmd",
"fleet_command": {
"command": "set_opacity",
"opacity": 0.3
}
}
Tip: for fleet element routing, the element target comes from the topic (.../element/{element_id}/cmd).
Step 3: Leave the Fleet (Layout, Commands, or Both)¶
Only leave one side if needed:
{
"command": "unsubscribe_fleet",
"fleet_id": "lobby-displays",
"unsubscribe_layout": false,
"unsubscribe_commands": true
}
Practical Rollout Pattern¶
- Pick a fleet name by location (
lobby-displays,menu-boards,gate-signage). - Run
subscribe_fleetonce per device. - Use
replicate_layoutfor visual consistency. - Use
broadcast_fleet_commandor direct publish tofleet/{fleet_id}/cmd(system) orfleet/{fleet_id}/element/{element_id}/cmd(topic-addressed element sync) for synchronized actions. - Use
correlation_idvalues in automations to trace distributed command runs.
6. System Commands¶
Device Control¶
List all active windows:
Returns detailed information about every window including ID, type, position, size, and configuration.
Get device information:
Subscribe to kingkiosk/{device_id}/info to receive:
- Device ID and version
- Platform (Android, iOS, macOS, etc.)
- Supported capabilities
- Available widget types
- Currently active widgets
- timestamp (when this info message was published)
- app_start_timestamp (when the app process started)
Volume and Brightness¶
Set Volume (0.0 to 1.0):
Mute/Unmute:
Set Brightness (0.0 to 1.0):
Get Current Brightness:
Restore Default Brightness:
Notifications and Alerts¶
Show a Toast Notification:
Priority levels: low, normal, high
Show a rich notification with formatting:
{
"command": "notify",
"title": "Update Available",
"message": "**Version 2.0** is now available with *exciting new features*!",
"format": "markdown",
"thumbnail": "https://example.com/icon.png",
"priority": "high"
}
Show an Alert Dialog:
{
"command": "alert",
"title": "Attention Required",
"message": "Please check the system status.",
"type": "warning",
"position": "center",
"auto_dismiss_seconds": 30
}
Alert options:
- type: info, success, warning, error
- position: Where on screen to show
- show_border: true/false
- border_color: Hex color code
- auto_dismiss_seconds: 1-300 seconds
Visual Effects¶
Halo Effect (Border Glow):
Create an attention-grabbing glowing border around the screen or a specific window:
{
"command": "halo_effect",
"enabled": true,
"color": "#FF0000",
"width": 20,
"intensity": 0.8,
"pulse_mode": "gentle"
}
Pulse modes: none, gentle, moderate, alert
Apply to a specific window:
{
"command": "halo_effect",
"window_id": "important-alert",
"enabled": true,
"color": "#FFD700",
"pulse_mode": "alert"
}
Disable the effect:
Screensaver (Bouncing Content):
Display a full-screen screensaver with bouncing items like clocks, images, or text. Each item moves independently around the screen, bouncing off the edges. Tap anywhere to dismiss.
Turn on a simple clock screensaver:
{
"command": "screensaver",
"action": "enable",
"items": [
{
"type": "clock",
"config": { "show_seconds": true, "text_color": "#00FF00" },
"width": 250,
"height": 100,
"speed": 1.0,
"scale": 1.5
}
],
"background_color": "#000000",
"background_opacity": 0.9
}
Add multiple bouncing items (each moves independently):
{
"command": "screensaver",
"action": "enable",
"items": [
{
"id": "clock_1",
"type": "clock",
"config": { "show_seconds": false },
"speed": 0.8
},
{
"id": "logo_1",
"type": "image",
"config": { "url": "https://example.com/logo.png" },
"width": 150,
"height": 150,
"speed": 1.2
},
{
"id": "welcome",
"type": "text",
"config": { "text": "Welcome!", "font_size": 48 },
"speed": 1.0
}
]
}
Item types you can use:
- clock - Digital clock (options: show_seconds, show_date, text_color, font_size)
- image or logo - Image from URL (options: url, fit)
- text - Custom text (options: text, font_size, font_weight, text_color)
- icon - System icon (options: icon, size, color) - icons include: star, heart, home, music, sun, moon
Item properties:
- speed - How fast it bounces (0.5 = slow, 1.0 = normal, 2.0 = fast)
- scale - Size multiplier (1.5 = 50% bigger)
- width, height - Base size in pixels
Turn off the screensaver:
Add an item to a running screensaver:
{
"command": "screensaver",
"action": "add_item",
"item": {
"id": "new_icon",
"type": "icon",
"config": { "icon": "star", "color": "#FFD700" },
"speed": 1.5
}
}
Remove an item:
Text-to-Speech¶
Make your display speak. When the Feature Server is connected, TTS automatically uses the high-quality Piper engine. When disconnected, it falls back to on-device TTS. The same commands work in both modes.
Basic speech:
With voice customization:
{
"command": "tts",
"text": "The current temperature is 72 degrees.",
"language": "en-US",
"volume": 0.8,
"rate": 0.9,
"pitch": 1.0
}
With a specific Piper voice (Feature Server):
{
"command": "tts",
"text": "Good morning! The front door is unlocked.",
"voice": "en_US-lessac-medium",
"volume": 0.9,
"rate": 0.5
}
Control playback:
Actions: speak, stop, pause, resume
Get available voices:
{
"command": "tts",
"action": "getVoices",
"response_topic": "kingkiosk/my_device/system/response"
}
When the Feature Server is connected, this returns Piper voices with details like voiceId, languageCode, quality, and installed status. You can filter results:
{
"command": "tts",
"action": "getVoices",
"language": "en_US",
"quality": "medium",
"response_topic": "kingkiosk/my_device/system/response"
}
Install a voice on the Feature Server:
{
"command": "tts",
"action": "voice_pull",
"voice": "en_US-lessac-medium",
"response_topic": "kingkiosk/my_device/system/response"
}
Check TTS status:
The response includes feature_server_connected, engine (either piper or the on-device engine), and current settings.
Speech-to-Text¶
Enable voice input:
Start listening:
Stop listening and get result:
Configure language:
Send transcriptions to AI agent:
Audio Input Device (Microphone)
The microphone used for Speech-to-Text is the one selected in Settings -> Speech & AI -> Audio Input Device.
You can also query/change it via MQTT:
{
"command": "unified_audio",
"action": "status",
"response_topic": "kingkiosk/{device_id}/system/response"
}
{
"command": "unified_audio",
"action": "list_devices",
"response_topic": "kingkiosk/{device_id}/system/response"
}
{
"command": "unified_audio",
"action": "set_device",
"device_id": "1",
"response_topic": "kingkiosk/{device_id}/system/response"
}
Screenshots and Screen Capture¶
Take a screenshot:
The screenshot is published as image data to kingkiosk/{device_id}/screenshot. Home Assistant can use this as a camera feed to view your display remotely.
Enable periodic screenshot capture:
This captures and publishes a screenshot every 30 seconds.
Disable screenshot capture:
Camera Snapshots¶
If your device has a camera (or Continuity Camera on Apple TV), you can capture still images from the live camera feed and publish them to MQTT — just like screenshots, but from the real camera.
Take a single camera snapshot:
The JPEG image is published to kingkiosk/{device_id}/camera/snapshot. Home Assistant automatically discovers it as a camera entity.
Enable periodic camera snapshots (every 30 seconds):
Auto-capture on motion detection:
When motion detection is enabled and motion is detected, a camera snapshot is automatically captured and published. This is perfect for security-style "capture on motion" use cases — the image appears in Home Assistant and can trigger automations (send to phone, save to NAS, etc.).
Adjust JPEG quality (default 80):
Disable camera snapshots:
Background Settings¶
Set a solid color background:
Set an image background:
Set a web page as background:
{
"command": "set_background",
"type": "webview",
"web_url": "https://example.com/animated-background"
}
Get current background:
Screensaver¶
King Kiosk includes a built-in screensaver that activates after a period of inactivity. This helps prevent screen burn-in and can create a more polished appearance when the display isn't being actively used.
Configuring the Screensaver (In-App):
Go to Settings → App Settings and find the Screensaver option. You can choose from three modes:
| Mode | Description |
|---|---|
| Off | Screensaver is disabled |
| Dim | Screen goes black after the timeout period |
| Screensaver | A bouncing clock appears after the timeout period |
You can also set the Timeout (1-60 minutes) - how long the device must be idle before the screensaver activates.
Waking the Screensaver:
- Touch/Remote: Tap anywhere on the screen or press any button on the remote to wake the display
- MQTT Command: Send a wake command to dismiss the screensaver remotely
Wake from screensaver via MQTT:
Alternative actions that also wake the screensaver: wake_up, deactivate
Get screensaver state:
Note: The idle screensaver is separate from the MQTT-controlled bouncing screensaver. The idle screensaver activates automatically based on inactivity, while the MQTT screensaver can be triggered remotely with custom items like images, text, and clocks.
AI Integration¶
King Kiosk can connect to various AI services for voice conversations and intelligent responses.
Enable AI agent:
Configure Home Assistant conversation:
{
"command": "ai_agent",
"action": "configure_home_assistant",
"base_url": "http://homeassistant.local:8123",
"access_token": "your_long_lived_token"
}
Configure a chat bot:
{
"command": "provision_ai_chatbot",
"provider": "anthropic",
"api_key": "your_api_key",
"model": "claude-3-sonnet",
"enable_speech": true,
"enable_tts": true
}
Supported providers: anthropic, openai, gemini, ollama
Send a message to the AI:
Batch Commands and Scripting¶
Batch scripting lets you send a single MQTT command that runs a whole sequence of steps on the kiosk — one after another, with optional pauses between them. Instead of publishing five separate MQTT messages from Home Assistant and hoping the timing works out, you send one batch command and King Kiosk handles the rest.
Any command you can send individually over MQTT works inside a batch. Open windows, load a saved layout, play audio, speak text, change the background, adjust brightness — mix and match however you want.
Why Use Batches?¶
- Morning routines — Set the volume, load a dashboard, announce the weather, all from one automation trigger
- Scene changes — Close everything, set a background, open specific windows in the right positions, with timed delays so the transitions look smooth
- Presentations and demos — Script a walkthrough that steps through screens automatically
- Provisioning a new device — Configure a kiosk from scratch: set the background, add windows, enable features, all in one shot
How It Works¶
Send a batch command with a commands array. Each item is a normal King Kiosk MQTT command. They execute in order, top to bottom. Use wait steps to pause between commands.
Only one batch can run at a time. If you send a new batch while one is already running, it will be rejected.
If any individual command fails, the batch logs the error and keeps going with the next step — it won't stop the whole sequence.
Basic Example¶
{
"command": "batch",
"commands": [
{ "command": "set_volume", "value": 0.3 },
{ "command": "wait", "seconds": 2 },
{ "command": "notify", "title": "Starting", "message": "Morning routine beginning" },
{ "command": "load_screen_state", "name": "Morning Dashboard" },
{ "command": "tts", "text": "Good morning! Here's your dashboard." }
]
}
Provisioning a Fresh Device¶
Set up a brand new kiosk with one command:
{
"command": "batch",
"commands": [
{ "command": "close_all_windows" },
{ "command": "set_background", "type": "image", "image_url": "https://example.com/wallpaper.jpg" },
{ "command": "wait", "seconds": 1 },
{ "command": "open_browser", "window_id": "dashboard", "url": "http://homeassistant.local:8123", "auto_connect": true },
{ "command": "set_volume", "value": 0.5 },
{ "command": "set_brightness", "value": 0.8 },
{ "command": "save_screen_state", "name": "Default Layout" }
]
}
Scene Transitions¶
Create smooth scene changes with timed delays:
{
"command": "batch",
"commands": [
{ "command": "close_all_windows" },
{ "command": "wait", "seconds": 0.5 },
{ "command": "set_background", "type": "image", "image_url": "https://example.com/movie-night.jpg" },
{ "command": "wait", "seconds": 0.5 },
{ "command": "open_browser", "window_id": "media", "url": "https://youtube.com", "auto_connect": true },
{ "command": "set_brightness", "value": 0.3 },
{ "command": "set_volume", "value": 0.7 },
{ "command": "tts", "text": "Movie night is ready." }
]
}
The Wait Command¶
Use wait to pause between steps. The seconds value can range from 0 to 300 (5 minutes max). Fractional values like 0.5 work fine for short pauses.
Waits are interruptible — if you cancel the batch during a wait, it stops immediately rather than waiting for the full duration.
Saving and Loading Scenes with Screen States¶
You can save the current layout as a named screen state, then restore it later — either manually or as part of a batch. This is the easiest way to build reusable "scenes."
Save a scene:
Load a saved scene:
List all saved scenes:
Delete a scene:
Export a scene as JSON (for backup or sharing to other devices):
{
"command": "export_screen_state",
"name": "Movie Night",
"response_topic": "kingkiosk/my_device/system/response"
}
Import a scene from JSON:
{
"command": "import_screen_state",
"name": "Movie Night",
"screen_state": { ... },
"overwrite": true
}
Monitoring and Canceling¶
Check batch status:
The response tells you whether a batch is running, how far along it is, and the total number of steps.
Cancel a running batch:
The batch stops after the current command finishes (or immediately if it's in a wait step).
Home Assistant Automation Example¶
automation:
- alias: "Good Morning Routine"
trigger:
- platform: time
at: "07:00:00"
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen_tablet/system/cmd"
payload: >
{
"command": "batch",
"commands": [
{ "command": "set_brightness", "value": 0.6 },
{ "command": "set_volume", "value": 0.4 },
{ "command": "load_screen_state", "name": "Morning Dashboard" },
{ "command": "wait", "seconds": 1 },
{ "command": "tts", "text": "Good morning! Here's your schedule for today." }
]
}
7. Widget Reference¶
Web Browsers¶
Display any web page in a window.
Open a web browser:
{
"command": "open_browser",
"url": "https://www.google.com",
"title": "Search",
"window_id": "google-browser",
"x": 100,
"y": 100,
"width": 800,
"height": 600
}
Alternative commands: open_web, open_simple_web
Apple TV Convenience: On tvOS (Apple TV), these browser commands (open_browser, open_web, open_simple_web) automatically map to create_remote_browser. This means you can use the same familiar API across all platforms - King Kiosk handles the platform-specific implementation for you. Your automations work everywhere without modification.
Remote Browser (Apple TV & iOS)¶
For Apple TV and iOS devices where local browser rendering is limited, King Kiosk offers a remote browser experience. A server renders the web page and streams it to your device in real-time.
Create a remote browser:
{
"command": "create_remote_browser",
"window_id": "browser_1",
"initial_url": "https://www.google.com",
"auto_connect": true
}
Optional parameters: name, video_profile (auto, 720p30, 1080p30, 1080p60), show_overlay
Navigate to a URL:
{
"command": "navigate_remote_browser",
"window_id": "browser_1",
"url": "https://www.example.com"
}
Browser navigation:
Simulate interaction:
Zoom in/out:
Use"step": "in" or "step": "out" for relative zoom (10% per step). Level range: 0.25 (25%) to 5.0 (500%).
Apple TV Remote mapping: - D-pad: Move pointer (with acceleration) - Select/Enter: Click - Menu/Escape: Navigate back - Play/Pause: Toggle Pointer/Scroll mode - Touch swipe: Pointer mode = move cursor, Scroll mode = scroll - Long press: Right-click
Browser Session Persistence:
Remote browser sessions automatically remember your logins, just like a regular browser. When you log into Netflix, YouTube, or any website, you'll stay logged in - even after closing the app or restarting your device.
How it works:
- Your login sessions and site data are tied to the window_id you specify
- Use the same window_id = same login sessions preserved
- Use a different window_id = fresh browser with no saved logins
Example: If you create a browser with "window_id": "netflix", log into Netflix, then close it - the next time you create a browser with "window_id": "netflix", you'll still be logged in. But "window_id": "youtube" would be a separate browser that doesn't share Netflix's login.
Key points:
- Automatic: No configuration needed - just works
- Isolated: Each window_id has its own separate logins and data
- Persistent: Survives app restarts and device reboots
Clear browser data (logout/reset):
This clears all cookies, localStorage, and cached data for the browser tile, then restarts the session. Use this to implement "logout" functionality for web apps.Debug status (helpful for double-audio / missing-audio issues):
Clock Widget¶
Display beautiful analog or digital clocks.
Create a clock:
{
"command": "open_clock",
"window_id": "main-clock",
"mode": "analog",
"theme": "dark",
"show_numbers": true,
"show_second_hand": true,
"x": 50,
"y": 50,
"width": 300,
"height": 300
}
Change clock mode via element command:
Topic: kingkiosk/{device_id}/element/main-clock/cmd
Toggle between analog and digital:
Configure appearance:
{
"command": "configure",
"theme": "light",
"background_mode": "gradient",
"background_color": "#1a1a2e"
}
Background modes: transparent, gradient, color, image
Weather Widget¶
Display current weather and forecasts from OpenWeather.
Create a weather widget:
{
"command": "open_weather_client",
"window_id": "weather-1",
"api_key": "your_openweather_api_key",
"location": "New York",
"units": "imperial",
"show_forecast": true,
"auto_refresh": true,
"refresh_interval": 1800,
"x": 400,
"y": 50,
"width": 350,
"height": 400
}
Units: imperial (Fahrenheit), metric (Celsius), standard (Kelvin)
You can also use coordinates instead of city name:
Media Player¶
Play video, audio, and images.
Play a video:
{
"command": "play_media",
"type": "video",
"url": "https://example.com/video.mp4",
"window_id": "video-player",
"title": "My Video",
"loop": true,
"x": 100,
"y": 100,
"width": 640,
"height": 360
}
Play audio:
{
"command": "play_media",
"type": "audio",
"url": "https://example.com/music.mp3",
"style": "visualizer"
}
Audio styles: window (basic player), visualizer (spectrum analyzer)
Visualizer options:
{
"command": "play_media",
"type": "audio",
"url": "https://example.com/music.mp3",
"style": "visualizer",
"visualizer_type": "fft",
"bars": 64,
"color_scheme": "rainbow",
"show_peaks": true
}
Display an image:
{
"command": "play_media",
"type": "image",
"url": "https://example.com/photo.jpg",
"window_id": "image-display"
}
Control playback:
Background audio control:
YouTube Player¶
Play YouTube videos directly.
Open YouTube:
{
"command": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"title": "Music Video",
"window_id": "youtube-player",
"x": 100,
"y": 100,
"width": 800,
"height": 450
}
Image Carousel¶
Create beautiful slideshows.
Create a carousel:
{
"command": "create_carousel",
"window_id": "photo-carousel",
"title": "Family Photos",
"items": [
{ "type": "image", "url": "https://example.com/photo1.jpg" },
{ "type": "image", "url": "https://example.com/photo2.jpg" },
{ "type": "image", "url": "https://example.com/photo3.jpg" }
],
"config": {
"auto_play": true,
"interval": 5,
"infinite_scroll": true,
"show_indicator": true
}
}
Video carousel:
{
"command": "create_video_carousel",
"window_id": "video-carousel",
"items": [
{ "type": "video", "url": "https://example.com/video1.mp4" },
{ "type": "video", "url": "https://example.com/video2.mp4" }
]
}
Add items to existing carousel:
{
"command": "add_carousel_item",
"window_id": "photo-carousel",
"item": { "type": "image", "url": "https://example.com/photo4.jpg" }
}
Navigate carousel:
Or use next or previous as index values.
Configure carousel:
{
"command": "set_carousel_config",
"window_id": "photo-carousel",
"auto_play": false,
"interval": 10,
"scroll_direction": "vertical"
}
Gauges and Thermostats¶
Use gauges when you want a quick visual read on sensor values (temperature, power, humidity, etc.).
Gauges now use a modern dark style by default with smooth value animation.
Create a simple gauge:
{
"command": "create_gauge",
"gauge_id": "cpu-usage",
"title": "CPU Usage",
"gauge_type": "circular",
"min": 0,
"max": 100,
"value": 45,
"unit": "%",
"x": 100,
"y": 100,
"width": 200,
"height": 200
}
Gauge types: linear, circular, radial, semicircular, thermostat
If you want the whole gauge window to be transparent, colored, gradient-backed, or image-backed, use the shared window surface settings described in The Windowing System. That outer window background is separate from the gauge's own internal styling.
Gauge styling keys are forgiving. You can usually use either camelCase or snake_case:
backgroundColororbackground_colorvalueBarColororvalue_bar_colorshowLabelsorshow_labelslabelFontSize,label_font_size, orlabel_font_size_pxlabelColororlabel_colorshow_indicatororshowIndicator
If you want a vertical thermometer-style bar instead of a draggable knob, use a linear gauge with orientation: "vertical" and show_indicator: false.
{
"command": "create_gauge",
"gauge_id": "outside-temp",
"title": "Outside",
"gauge_type": "linear",
"orientation": "vertical",
"min": 20,
"max": 110,
"value": 78,
"unit": "°F",
"color_mode": "solid",
"value_bar_color": "#FF6B35",
"show_indicator": false,
"show_labels": false,
"width": 120,
"height": 220
}
Create a thermostat from Home Assistant climate state (current + heat + cool + optional humidity):
{
"command": "create_gauge",
"window_id": "widget_upstairs_thermostat",
"gauge_id": "widget_upstairs_thermostat",
"title": "Upstairs Thermostat",
"gauge_type": "thermostat",
"min": 50,
"max": 90,
"unit": "°F",
"step_size": 1,
"decimals": 0,
"interactive": true,
"locked": false,
"pointers": [
{
"id": "current",
"label": "Current",
"style": "needle",
"locked": true,
"subscribe_topic": "kingkiosk/ha/state/climate/upstairs",
"json_field": "attributes.current_temperature"
},
{
"id": "heat",
"label": "Heat",
"style": "dot",
"color": "#FF6B35",
"subscribe_topic": "kingkiosk/ha/state/climate/upstairs",
"publish_topic": "kingkiosk/ha/command/climate/upstairs",
"json_field": "attributes.target_temp_low"
},
{
"id": "cool",
"label": "Cool",
"style": "dot",
"color": "#4FC3F7",
"subscribe_topic": "kingkiosk/ha/state/climate/upstairs",
"publish_topic": "kingkiosk/ha/command/climate/upstairs",
"json_field": "attributes.target_temp_high"
},
{
"id": "humidity",
"label": "Humidity",
"style": "dot",
"locked": true,
"color": "#94A3B8",
"subscribe_topic": "kingkiosk/ha/state/climate/upstairs",
"json_field": "attributes.current_humidity"
}
]
}
Why this works:
- Each pointer can listen to the same MQTT topic but extract a different JSON field.
- A thermostat with current + heat + cool pointers renders a Nest-style range gauge.
- If you include a humidity pointer, the thermostat displays HUMIDITY xx% text in the center.
- Humidity pointer values are not clamped to thermostat min/max, so 45% stays 45% even if temperature range is 50..90.
- If you only provide a current pointer, thermostat mode falls back to a single-value gauge.
- In multi-pointer thermostat mode, values only change when you drag a setpoint dot handle, so accidental taps do not jump temperatures.
Color modes: solid, gradient, thresholds, zones
Pointer styles: needle, dot, triangle, line, target
Update gauge value:
Update a specific pointer:
{
"command": "set_pointer_value",
"gauge_id": "living-room-thermostat",
"pointer_id": "current",
"value": 72
}
Lock/unlock gauge:
Configure gauge:
{
"command": "configure_gauge",
"gauge_id": "cpu-usage",
"min": 0,
"max": 100,
"decimals": 1,
"show_min_max": true
}
Tip: pointer MQTT fields (subscribe_topic, publish_topic, json_field) belong inside each pointer object. Top-level subscribe_topic is not used by in-app gauges.
Tip: if publish_topic is a Home Assistant climate command topic (.../ha/command/climate/...), King Kiosk now sends service: set_temperature payloads automatically. You can override with pointer publish_payload templates.
Custom publish_payload templates for thermostat range mode (copy/paste):
{
"id": "heat",
"label": "Heat",
"style": "dot",
"publish_topic": "kingkiosk/ha/command/climate/upstairs",
"publish_payload": {
"service": "set_temperature",
"data": {
"target_temp_low": "{{value}}",
"target_temp_high": "{{cool}}"
}
}
}
{
"id": "cool",
"label": "Cool",
"style": "dot",
"publish_topic": "kingkiosk/ha/command/climate/upstairs",
"publish_payload": {
"service": "set_temperature",
"data": {
"target_temp_low": "{{heat}}",
"target_temp_high": "{{value}}"
}
}
}
Template tokens available in pointer publish_payload:
- {{value}} = this pointer's value
- {{heat}} = current heat/low setpoint pointer value
- {{cool}} = current cool/high setpoint pointer value
- {{current}} = current temperature pointer value
- {{pointer_id}}, {{gauge_id}}, {{timestamp}}
Platform controls: - tvOS: Use Siri Remote D-pad to select pointers and adjust values - iOS: For thermostat range mode, drag the heat/cool dots to adjust; background taps are ignored - macOS: For thermostat range mode, click-drag the heat/cool dots to adjust; background clicks are ignored
Charts¶
Charts support line, bar, and pie, with dark themed styling and smooth transitions by default.
Create a line chart:
{
"command": "create_chart",
"chart_id": "temperature-chart",
"chart_type": "line",
"title": "Temperature History",
"max_points": 60,
"x": 100,
"y": 100,
"width": 400,
"height": 300
}
Chart types: line, bar, pie
Add data to chart:
Replace all chart data:
{
"command": "replace_chart_data",
"chart_id": "temperature-chart",
"values": [68, 70, 72, 75, 73, 71, 69]
}
Update pie chart:
{
"command": "update_pie_chart",
"chart_id": "sales-pie",
"slices": [
{ "value": 40, "label": "Product A", "color": "#3498db" },
{ "value": 30, "label": "Product B", "color": "#2ecc71" },
{ "value": 30, "label": "Product C", "color": "#e74c3c" }
]
}
Configure chart:
{
"command": "configure_chart",
"chart_id": "temperature-chart",
"config": {
"type": "bar",
"primaryColor": "#06B6D4"
}
}
Tip: configure_chart supports quick type/color changes. For full chart styling (like title, showGrid, minY, maxY, pie sizing), publish JSON to kingkiosk/chart/<chart_id>/config.
Reset chart data:
Maps¶
Display interactive maps with custom pins and overlays.
Create a map:
{
"command": "open_map",
"window_id": "location-map",
"title": "Store Locations",
"initial_camera": {
"lat": 40.7128,
"lon": -74.0060,
"zoom": 12
},
"interaction": {
"touch_enabled": true,
"remote_enabled": true,
"allow_user_drop_pins": false
},
"x": 100,
"y": 100,
"width": 600,
"height": 400
}
Custom tile provider (e.g., for satellite imagery):
{
"provider": {
"url_template": "https://your-tile-server/{z}/{x}/{y}.png",
"attribution": "Map data © Your Provider"
}
}
Add pins to map:
Topic: kingkiosk/{device_id}/element/location-map/cmd
{
"command": "add_pins",
"pins": [
{
"pin_id": "store-1",
"lat": 40.7128,
"lon": -74.0060,
"icon": { "type": "default" }
},
{
"pin_id": "store-2",
"lat": 40.7580,
"lon": -73.9855,
"icon": { "type": "url", "value": "https://example.com/marker.png" }
}
]
}
Move the camera:
Add text overlays:
{
"command": "add_text",
"texts": [
{
"text_id": "label-1",
"text": "Headquarters",
"anchor_type": "geo",
"geo": { "lat": 40.7128, "lon": -74.0060 },
"style": {
"font_size_px": 16,
"color": "#FFFFFF",
"background_color": "#333333"
}
}
]
}
Clear all pins:
Canvas (Visual Diagrams)¶
Create custom visual diagrams with objects and connections.
Create a canvas:
{
"command": "open_canvas",
"window_id": "network-diagram",
"title": "Network Topology",
"doc": {
"objects": [
{
"object_id": "router",
"type": "node",
"label": "Main Router",
"x": 200,
"y": 100,
"width": 100,
"height": 60
},
{
"object_id": "server",
"type": "node",
"label": "Server",
"x": 200,
"y": 250,
"width": 100,
"height": 60
}
],
"connections": [
{
"connection_id": "link-1",
"from": { "object_id": "router" },
"to": { "object_id": "server" },
"style": {
"color": "#00FF00",
"arrow": "end"
}
}
]
},
"interaction": {
"touch_enabled": true,
"edit_mode": false
}
}
Object types: node, text, shape, image, embed, group
Shape types (for type: shape): rect, round_rect, circle, line
Add objects:
Topic: kingkiosk/{device_id}/element/network-diagram/cmd
{
"command": "add_objects",
"objects": [
{
"object_id": "workstation-1",
"type": "node",
"label": "Workstation",
"x": 50,
"y": 250,
"width": 80,
"height": 50
}
]
}
Add connections:
{
"command": "add_connections",
"connections": [
{
"connection_id": "link-2",
"from": { "object_id": "router" },
"to": { "object_id": "workstation-1" },
"style": { "animated": true }
}
]
}
Embed widgets in canvas:
{
"object_id": "gauge-embed",
"type": "embed",
"x": 350,
"y": 100,
"width": 120,
"height": 120,
"embed": {
"widget_type": "gauge",
"id": "bandwidth-gauge",
"config": {
"min": 0,
"max": 100,
"unit": "Mbps"
}
}
}
Live dashboards with MQTT bindings:
Canvas objects can update their appearance in real time based on MQTT data. Objects change color, text, and icons automatically as device states change — tap an object to send a command, and watch it update when the state changes. This makes canvases ideal for interactive smart home dashboards, network monitoring displays, and other live visualizations.
For reusable canvases, tap-publish bindings can target the local device by using kingkiosk/{device_id}/system/cmd in mqtt.publish.topic. Camera nodes can publish a webrtc_player payload with centered: true to open a modal-style WHEP preview without hardcoding the kiosk ID.
Animated Text¶
Display text with stunning animations.
Create animated text:
{
"command": "open_animated_text",
"window_id": "welcome-text",
"text": "Welcome to King Kiosk!",
"preset": "typewriterShimmer",
"style": {
"fontSize": 48,
"color": "#FFFFFF"
},
"x": 100,
"y": 100,
"width": 600,
"height": 200
}
Available presets:
- typewriterShimmer - Characters appear one by one with shimmer
- neonPulse - Glowing neon effect
- bounceCascade - Characters bounce in
- alertFlash - Attention-grabbing flash
- tickerMarquee - Scrolling ticker tape
Custom effects:
{
"command": "open_animated_text",
"window_id": "custom-text",
"text": "Breaking News!",
"effects": [
{
"type": "fade",
"durationMs": 500,
"stagger": { "eachMs": 50 }
},
{
"type": "slide",
"from": { "x": 0, "y": -50 },
"to": { "x": 0, "y": 0 },
"durationMs": 800,
"easing": "easeOutBack"
}
],
"timeline": {
"mode": "loop"
}
}
Effect types: fade, slide, scale, colorize, marquee, typewriter
Easing options: linear, easeOutCubic, easeInOutCubic, easeOutBack
Update text content:
Topic: kingkiosk/{device_id}/element/welcome-text/cmd
Trigger animation replay:
MQTT Image¶
Display images that update automatically from MQTT messages.
Create an MQTT image tile:
{
"command": "mqtt_image",
"action": "open",
"window_id": "camera-feed",
"window_name": "Security Camera",
"mqtt_topic": "home/camera/living_room/image",
"is_base64": true,
"x": 100,
"y": 100,
"width": 640,
"height": 480
}
The image updates automatically whenever a new message arrives on the subscribed topic.
For JSON payloads:
This extracts the image from { "data": { "image": "base64data..." } }.
Update subscription topic:
{
"command": "mqtt_image",
"action": "update_topic",
"window_id": "camera-feed",
"mqtt_topic": "home/camera/backyard/image"
}
MQTT Button¶
Create buttons that publish MQTT messages when pressed.
Create an MQTT button:
{
"command": "mqtt_button",
"action": "configure",
"window_id": "light-toggle",
"label": "Living Room Light",
"mode": "toggle",
"publish_topic": "home/lights/living_room/set",
"publish_payload": { "state": "TOGGLE" },
"subscription_topic": "home/lights/living_room/state",
"icon_on": "lightbulb",
"icon_off": "lightbulb_outline",
"color_on": "#FFD700",
"color_off": "#808080"
}
Modes:
- toggle or switch - Two-state button
- icon_button or button - Momentary button
Trigger the button via MQTT:
Calendar (with Bidirectional Sync)¶
The King Kiosk calendar is more than just a display widget - it features full bidirectional MQTT synchronization. This means Home Assistant or any external system can add, update, and delete calendar events, and King Kiosk will broadcast changes made locally so external systems stay in sync.
The calendar sync is always-on - it works even when no calendar widget is visible on screen, making it perfect for automation scenarios.
Create a calendar widget:
{
"command": "calendar",
"action": "create",
"window_id": "main-calendar",
"name": "Family Calendar",
"x": 100,
"y": 100,
"width": 400,
"height": 350
}
Add an event (via system command):
{
"command": "calendar",
"action": "add_event",
"window_id": "main-calendar",
"event": {
"title": "Team Meeting",
"start": "2024-12-20T10:00:00",
"end": "2024-12-20T11:00:00"
}
}
Navigate to a specific date:
{
"command": "calendar",
"action": "go_to_date",
"window_id": "main-calendar",
"date": "2024-12-25"
}
Clear all events:
Bidirectional MQTT Sync¶
For advanced integration with Home Assistant or other systems, use the element-level commands on the calendar's dedicated MQTT topic.
Topic: kingkiosk/{device_id}/element/calendar/cmd
Create or Update an Event (Upsert):
{
"command": "calendar_upsert",
"correlation_id": "ha-req-123",
"event": {
"uid": "doctor-appt-001",
"title": "Doctor Appointment",
"start": "2025-01-15T00:00:00Z",
"end": "2025-01-15T23:59:59Z",
"allDay": true,
"description": "Bring insurance card",
"color": "#4CAF50"
}
}
The uid (or id) uniquely identifies the event. If an event with that ID exists, it's updated. Otherwise, a new event is created. Multiple events per day are fully supported.
Delete an Event:
Bulk Replace All Events:
Replace all calendar events at once - perfect for syncing from an external calendar source:
{
"command": "calendar_bulk_replace",
"correlation_id": "ha-req-125",
"events": [
{
"uid": "meeting-001",
"title": "Team Standup",
"start": "2025-01-15T09:00:00Z",
"allDay": false
},
{
"uid": "meeting-002",
"title": "Project Review",
"start": "2025-01-15T14:00:00Z",
"allDay": false
},
{
"uid": "holiday-001",
"title": "Company Holiday",
"start": "2025-01-20T00:00:00Z",
"allDay": true,
"color": "#FF5722"
}
]
}
Get Calendar Status:
Receiving Calendar Events¶
When events are added, updated, or deleted locally on the King Kiosk device (via touch interaction), the changes are broadcast so external systems can stay synchronized.
Subscribe to: kingkiosk/{device_id}/element/calendar/event
Event Added/Updated Locally:
{
"event": "calendar_event_upserted",
"origin": "kingkiosk",
"timestamp": "2025-01-15T10:30:00Z",
"event_data": {
"uid": "new-event-001",
"title": "Lunch with Client",
"start": "2025-01-16T12:00:00Z",
"allDay": false,
"description": "Downtown restaurant"
}
}
Event Deleted Locally:
{
"event": "calendar_event_deleted",
"origin": "kingkiosk",
"timestamp": "2025-01-15T10:35:00Z",
"uid": "new-event-001"
}
Command Acknowledgements:
When your system sends a command, King Kiosk acknowledges successful processing:
- calendar_upsert_applied - Event was created/updated
- calendar_delete_applied - Event was deleted
- calendar_bulk_applied - Bulk replace completed
Smart Loop Prevention¶
The calendar sync includes intelligent loop prevention. When King Kiosk applies a change received via MQTT, it suppresses broadcasting that same change back out. The correlation_id field helps track and deduplicate commands, so you can safely have bidirectional sync without echo loops.
Timers and Stopwatch¶
Create a stopwatch:
{
"command": "stopwatch",
"window_id": "main-stopwatch",
"name": "Workout Timer",
"x": 100,
"y": 100,
"width": 250,
"height": 150
}
Create a countdown timer:
{
"command": "timer_widget",
"window_id": "countdown-1",
"name": "Cooking Timer",
"config": {
"duration": 600
}
}
Control timers:
Actions: start, stop, pause, reset
Alarmo (Security System)¶
Integrate with the Alarmo Home Assistant add-on. Full MQTT parity: per-mode PIN requirements, force arm (bypass open sensors), and skip exit delay.
Create an Alarmo panel:
{
"command": "alarmo",
"window_id": "alarm-panel",
"name": "Home Security",
"mqtt_base_topic": "alarmo",
"entity": "alarm_control_panel.alarmo",
"require_code_to_arm": true,
"require_code_to_disarm": true,
"code_length": 4,
"available_modes": ["armed_away", "armed_home", "armed_night"],
"x": 100,
"y": 100,
"width": 350,
"height": 500
}
Per-mode PIN configuration — require a code for Away but not Home:
{
"command": "alarmo",
"window_id": "alarm-panel",
"require_code_to_arm": true,
"require_code_to_disarm": true,
"code_required_modes": { "away": true, "home": false, "night": true }
}
Force arm & skip delay — the panel shows toggle chips for these options. You can also set defaults:
Key parameters: require_code (legacy, sets both arm/disarm), require_code_to_arm, require_code_to_disarm, code_required_modes, force, skip_delay, mqtt_base_topic, area, available_modes, code_length.
DLNA Player¶
Display and control DLNA/UPnP media.
Create a DLNA player:
{
"command": "dlna_player",
"window_id": "dlna-1",
"name": "DLNA Player",
"x": 100,
"y": 100,
"width": 640,
"height": 480
}
The DLNA player shows content pushed from DLNA controllers on your network (phones, computers, media servers).
Video Conferencing¶
Multi-point video and audio conferencing powered by WebRTC. Each conference window can hold multiple independent endpoints — bridge to other KingKiosk rooms or dial SIP phone numbers. Requires the Feature Server (KingKiosk Core 3) for WebRTC transport.
Create a video conference window:
{
"command": "video_call",
"window_id": "conf-main",
"name": "Conference",
"width": 1024,
"height": 768
}
Create an audio-only call window:
Once the window exists, send element commands to kingkiosk/{device_id}/element/{window_id}/cmd:
List available rooms to call:
This returns a list of rooms currently active on the Feature Server, each with a room_id you can dial.
Dial a room (intercom bridge):
Dial a SIP endpoint:
{
"command": "add_sip_endpoint",
"payload": {
"sip_uri": "reception@pbx.local",
"video": true,
"from_display": "Kitchen Display"
}
}
Accepted destination forms: sip:user@host, user@host, 192.168.1.50, or 192.168.1.50:5060. The video and from_display fields are optional.
Add a second endpoint to create a multi-party conference:
Conference controls:
| Command | Description |
|---|---|
mute_mic |
Mute your microphone |
unmute_mic |
Unmute your microphone |
toggle_mic |
Toggle microphone mute |
set_master_volume |
Set volume for all endpoints (payload: {"volume": 0.7}) |
mute_endpoint |
Mute a specific endpoint (payload: {"endpoint_id": "..."}) |
unmute_endpoint |
Unmute a specific endpoint |
set_endpoint_volume |
Set per-endpoint volume (payload: {"endpoint_id": "...", "volume": 0.5}) |
send_dtmf |
Send DTMF to SIP endpoints (payload: {"digits": "123#"}) |
list_sip_calls |
List active SIP calls from the gateway |
hangup_endpoint |
Hang up one endpoint (payload: {"endpoint_id": "..."}) |
hangup_all |
End all calls and return to idle |
accept_incoming |
Accept a waiting incoming call |
reject_incoming |
Reject a waiting incoming call |
Monitor conference state by subscribing to kingkiosk/{device_id}/element/{window_id}/state. The state includes all active endpoints with their connection status, call type (intercom or SIP), duration, mute state, and volume.
Monitor recipient-side incoming conference notifications by subscribing to kingkiosk/{device_id}/communications/event. When Auto accept videoconference calls is off, the kiosk publishes incoming_conference_call so you can trigger ringing, alerts, or your own external workflow; it does not show a built-in accept dialog.
Close the conference window:
PDF Viewer¶
Display PDF documents.
Open a PDF:
{
"command": "open_pdf",
"url": "https://example.com/document.pdf",
"title": "User Manual",
"window_id": "manual-pdf",
"x": 100,
"y": 100,
"width": 600,
"height": 800
}
Games¶
Because even kiosks need a break sometimes.
Launch Missile Command:
{
"command": "stop_the_missiles",
"title": "Missile Command",
"window_id": "game-1",
"x": 100,
"y": 100,
"width": 600,
"height": 400
}
Control game:
Actions: start, restart, stop, pause, resume, toggle_sound, set_transparent, set_background_mode, set_background_opacity
LED Light Panel¶
Turn any King Kiosk screen into a virtual LED light wall. The LED Light Panel widget renders a grid of illuminated shapes — squares, triangles, or hexagons — that you can program with color animations, scrolling text, or pixel-level control. Shapes tessellate naturally (hexagons form a honeycomb, triangles interlock) and you can toggle between a clean flat look and a 3D wall-mounted effect with shadows and glow.
Three shapes are available: - Squares — clean pixel-grid aesthetic, great for text and pixel art - Triangles — tessellated up/down pattern, striking geometric look - Hexagons — honeycomb layout, the classic modular light panel look
Two rendering modes: - Flat — simple filled shapes with subtle borders (best performance) - 3D Wall — drop shadows, face gradients, and colored glow on bright cells
Example 1: Ambient Mood Lighting
Create a honeycomb panel with a slowly rotating rainbow — perfect as a living room accent wall behind a TV.
{
"command": "create_led_panel",
"window_id": "mood-wall",
"title": "Mood Lighting",
"shape": "hexagon",
"rows": 6,
"cols": 10,
"gap": 3,
"render_mode": "wall3d",
"preset": "rainbow_wave",
"preset_speed": 0.3,
"width": 800,
"height": 400
}
Change the mood on the fly:
{
"command": "preset",
"window_id": "mood-wall",
"name": "breathing",
"color": "#FF6600",
"speed": 0.5
}
Built-in presets: rainbow_wave, breathing, color_chase, sparkle, fire, matrix, gradient_rotate, solid, random, wave
Example 2: Status Board / Scrolling Marquee
Use a square grid as a pixel-art message board in a retail store, office lobby, or conference room.
{
"command": "create_led_panel",
"window_id": "lobby-sign",
"title": "Lobby Display",
"shape": "square",
"rows": 7,
"cols": 40,
"gap": 1,
"render_mode": "flat",
"width": 1200,
"height": 200
}
Display scrolling text:
{
"command": "set_text",
"window_id": "lobby-sign",
"text": "WELCOME TO ACME CORP",
"color": "#00FF00",
"bg": "#001100",
"scroll": true,
"speed": 40,
"direction": "left"
}
The panel includes a built-in 5x7 pixel bitmap font (A-Z, 0-9, common punctuation). You can even define custom glyphs for logos or special characters.
Example 3: Custom Animation with the Scripting Engine
The scripting engine lets you write mathematical expressions that compute each cell's color every frame. This creates a pulsing concentric ring effect on a triangle grid:
{
"command": "create_led_panel",
"window_id": "art-panel",
"title": "Generative Art",
"shape": "triangle",
"rows": 10,
"cols": 10,
"render_mode": "wall3d"
}
Then run a script:
{
"command": "run_script",
"window_id": "art-panel",
"script": {
"fps": 30,
"loop": true,
"variables": {
"ring_speed": 3.0,
"color_speed": 30.0
},
"per_cell": {
"h": "(cell_dist_center * 360 + t * color_speed) % 360",
"s": "0.9",
"l": "0.3 + 0.2 * sin(cell_dist_center * 6.28 - t * ring_speed)"
}
}
}
Script expressions have access to per-cell variables (cell_x, cell_y, cell_dist_center, cell_angle), time (t, frame), math functions (sin, cos, noise, smoothstep, and more), and user-defined variables. HSL output means you work with hue (0-360), saturation (0-1), and lightness (0-1) directly.
Per-cell MQTT control:
For direct integration with sensors or external systems, you can set individual cells or the entire buffer:
{
"command": "set_cells",
"window_id": "mood-wall",
"cells": {
"0": "#FF0000",
"1": "#FF4400",
"2": "#FF8800",
"3": "#FFCC00",
"4": "#FFFF00"
}
}
Or fill with a gradient:
{
"command": "fill_gradient",
"window_id": "mood-wall",
"start": "#0000FF",
"end": "#FF0000",
"direction": "horizontal"
}
Reconfigure on the fly:
{
"command": "configure",
"window_id": "mood-wall",
"shape": "triangle",
"render_mode": "flat",
"rows": 12,
"cols": 8
}
Tip: For the best visual impact, use render_mode: "wall3d" with hexagons and a gap of 2-4 pixels. The 3D mode adds realistic shadows and a soft glow effect around bright cells.
Tip: The scripting engine's noise() function generates smooth, organic-looking patterns. Try "l": "0.3 + 0.2 * noise(cell_x * 0.3, cell_y * 0.3 + t)" for a lava-lamp effect.
For full parameter details, see the MQTT Widget Reference.
RSS Feed Reader¶
Turn any screen into a beautiful ambient news display. The RSS Feed Reader aggregates multiple RSS, Atom, and JSON Feed sources into a single widget with six display modes, glassmorphism card design, content filtering, a full-article reader overlay, and QR code "send to phone" sharing. It auto-refreshes, auto-rotates, and is fully controllable over MQTT.
Six display modes are available:
- Carousel (default) -- Full glassmorphism card with hero image, source badge, timestamp, and excerpt. Auto-rotates with smooth mode-specific animations.
- Ticker -- Horizontal scrolling headline strip (CNN/Bloomberg style). Each headline shows a colored source dot and title. Tap any headline to expand.
- Grid -- Multi-card magazine layout with responsive columns.
- List -- Vertical scrolling list with thumbnail, headline, source badge, and a per-article QR code. Clean, dense, scannable. Point your phone at any row to open that article instantly.
- Hero -- Single article fills the entire widget. Full-bleed image with dramatic text overlay. High-impact ambient mode.
- Quote -- Centered text only. Ideal for daily quotes, word-of-the-day, announcements, or school bulletins.
Example 1: Multi-Source News Wall
Aggregate multiple news feeds into a carousel that auto-rotates every 10 seconds. Each source gets a colored badge so you can tell them apart at a glance.
{
"command": "create_rss",
"window_id": "news-wall",
"title": "News Feed",
"display_mode": "carousel",
"rotation_interval": 10,
"transition": "slide",
"feeds": [
{
"url": "https://feeds.bbci.co.uk/news/world/rss.xml",
"title": "BBC World",
"color": "#BB1919"
},
{
"url": "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
"title": "NYT",
"color": "#1A1A1A"
},
{
"url": "https://feeds.reuters.com/reuters/topNews",
"title": "Reuters",
"color": "#FF8800"
}
],
"show_source_badge": true,
"show_timestamp": true,
"x": 0,
"y": 0,
"width": 800,
"height": 500
}
Navigate articles via MQTT:
Example 2: Bloomberg-Style Ticker Strip
A scrolling headline ticker across the bottom of a dashboard. Reads from a tech news feed and scrolls at 60 pixels/second.
{
"command": "create_rss",
"window_id": "news-ticker",
"title": "Tech Headlines",
"display_mode": "ticker",
"ticker_speed": 60,
"feeds": [
{ "url": "https://hnrss.org/frontpage", "title": "HN" },
{ "url": "https://www.theverge.com/rss/index.xml", "title": "Verge" }
],
"background_mode": "color",
"background_color": "#0D1117",
"background_opacity": 0.85,
"x": 0,
"y": 700,
"width": 1920,
"height": 60
}
Example 3: Ambient Hero Spotlight
A single dramatic article fills a large screen with a full-bleed image and glassmorphism text overlay. Perfect for a living room TV or lobby display.
{
"command": "create_rss",
"window_id": "spotlight",
"title": "Photo of the Day",
"display_mode": "hero",
"rotation_interval": 30,
"transition": "fade",
"feeds": [
{
"url": "https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss",
"title": "NASA IOTD"
}
],
"show_source_badge": true,
"width": 1920,
"height": 1080
}
Example 4: Daily Quote / Announcement Board
Minimal centered text -- ideal for inspirational quotes, school announcements, or HOA alerts. No images, no distractions.
{
"command": "create_rss",
"window_id": "daily-quote",
"title": "Quote of the Day",
"display_mode": "quote",
"rotation_interval": 20,
"transition": "fade",
"feeds": [
{ "url": "https://www.quoterss.com/quote-of-the-day.rss" }
],
"show_images": false,
"show_source_badge": false,
"background_mode": "gradient",
"background_opacity": 0.8,
"width": 600,
"height": 300
}
Example 5: Scannable List with Per-Article QR Codes
A clean vertical list showing every article with a thumbnail and a QR code on every row. Perfect for a break room, waiting room, or lobby where people walk up, scan a QR code with their phone, and keep reading on the go.
{
"command": "create_rss",
"window_id": "lobby-list",
"title": "Today's Headlines",
"display_mode": "list",
"feeds": [
{
"url": "https://feeds.bbci.co.uk/news/world/rss.xml",
"title": "BBC World",
"color": "#BB1919"
},
{
"url": "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
"title": "NYT",
"color": "#000000"
}
],
"show_images": true,
"show_source_badge": true,
"show_timestamp": true,
"max_articles": 20,
"filter": { "family_mode": true },
"background_mode": "color",
"background_color": "#0F172A",
"background_opacity": 0.9,
"width": 700,
"height": 900
}
Each row shows: thumbnail | headline + source badge + time | QR code linking to the article. Tap any row to open the full reader overlay. Scan the QR code to open it on your phone instead.
Example 6: Family-Safe Magazine Grid with Filtering
A responsive magazine grid for a household display. Family mode is enabled to automatically block violent, adult, and gambling content. Additional custom keywords are blocked. Only articles in allowed categories make it through.
{
"command": "create_rss",
"window_id": "family-news",
"title": "Family Feed",
"display_mode": "grid",
"rotation_interval": 15,
"feeds": [
{
"url": "https://feeds.bbci.co.uk/news/rss.xml",
"title": "BBC",
"color": "#BB1919"
},
{
"url": "https://rss.cnn.com/rss/edition.rss",
"title": "CNN",
"color": "#CC0000"
}
],
"filter": {
"family_mode": true,
"blocked_keywords": ["shooting", "scandal"],
"allowed_categories": ["science", "technology", "health", "education"],
"custom_blocked_terms": ["crypto", "betting"]
},
"width": 1200,
"height": 800
}
Note: custom_blocked_terms are applied only when family_mode is true.
Update filters at any time:
Example 7: Quiet Hours (Night Mode)
Suppress feed fetches during sleeping hours so the display isn't pulling data at 3 AM:
{
"command": "create_rss",
"window_id": "bedroom-news",
"title": "Morning Briefing",
"display_mode": "carousel",
"feeds": [
{ "url": "https://feeds.bbci.co.uk/news/world/rss.xml", "title": "BBC" }
],
"quiet_hours": {
"enabled": true,
"start": "22:00",
"end": "07:00"
},
"width": 600,
"height": 400
}
Reading articles and sharing:
Tap an article (or send the expand command) to open the full reader overlay with the complete article text, images, and source attribution. By default, the reader auto-dismisses after 30 seconds.
Send an article to a phone via QR code:
This shows a QR code overlay containing the article URL and publishes a share event on MQTT that you can wire to a Home Assistant notification or any other automation.
Controlling display on the fly:
Switch between display modes without recreating the widget:
Pause and resume auto-rotation:
Add a new feed to an existing widget:
{
"command": "add_feed",
"url": "https://example.com/feed.xml",
"title": "New Source",
"color": "#6366F1"
}
Remove a feed by URL:
Supported feed formats: RSS 2.0 (<rss>), Atom (<feed>), and JSON Feed (jsonfeed.org spec). The widget auto-detects the format. Images are extracted from enclosures, media:content, media:thumbnail, or the first <img> tag in the HTML content.
Apple TV: On Apple TV, the RSS widget supports Siri Remote navigation. Use directional focus to move between articles/cards, press Select to open the reader, and Menu/Back to dismiss reader or QR overlays.
For full parameter details and MQTT command reference, see the MQTT Widget Reference.
8. Building Your Own Widgets¶
One of King Kiosk's most powerful features is the ability to create your own custom widgets using standard web technologies (HTML, CSS, and JavaScript). If you can build a web page, you can build a King Kiosk widget.
Why Build Custom Widgets?¶
The built-in widgets cover most common use cases, but sometimes you need something unique:
- Branded Displays - Create widgets that match your company's visual identity
- Custom Data Visualization - Display data from your specific systems in exactly the format you need
- Interactive Kiosks - Build check-in systems, surveys, or product configurators
- Specialized Integrations - Connect to APIs and services that King Kiosk doesn't support natively
- Internal Tools - Create dashboards for your specific workflows
Custom widgets run inside a browser surface and communicate with King Kiosk through a bridge connection. The Remote Browser custom-widget path supports full command/telemetry/storage bridging (including persisted storage across restarts).
Quick Start¶
Creating a custom widget is as simple as creating an HTML file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Widget</title>
<style>
body {
margin: 0;
padding: 20px;
background: #1a1a2e;
color: white;
font-family: sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.value { font-size: 72px; font-weight: bold; }
</style>
</head>
<body>
<div class="value" id="display">--</div>
<script>
// Wait for the KingKiosk bridge to be ready
window.addEventListener('kingkiosk-ready', function() {
console.log('KingKiosk bridge ready!');
// Listen for commands
window.KingKiosk.onCommand(function(command, payload) {
if (command === 'set_value') {
document.getElementById('display').textContent = payload.value;
}
});
});
</script>
</body>
</html>
Adding Your Widget¶
For full custom-widget bridge support, use one of these runtime paths:
Option 1: Remote Browser custom widget bridge (recommended, required on tvOS)
Host your widget files, then create a remote browser window:
{
"command": "create_remote_browser",
"window_id": "my-custom-widget",
"initial_url": "https://your-server.com/widgets/my-widget/",
"auto_connect": true
}
Option 2: Local customWebView tile (advanced)
If you already have a local customWebView tile, configure it via the window command topic:
Topic: kingkiosk/{device_id}/window/{window_id}/command
{
"action": "configure",
"url": "https://your-server.com/widgets/my-widget/",
"title": "My Custom Widget",
"storage": {
"theme": "dark"
}
}
If you do not need the web bridge API, you can also use native MQTT widgets:
Native widget examples:
{
"command": "create_gauge",
"gauge_id": "temperature",
"gauge_type": "radial",
"min": 0,
"max": 100,
"unit": "°F"
}
{
"command": "mqtt_button",
"action": "configure",
"window_id": "light-switch",
"label": "Living Room Light",
"mode": "toggle",
"publish_topic": "home/lights/living/set",
"subscription_topic": "home/lights/living/state"
}
Native carousel example:
{
"command": "create_carousel",
"window_id": "lobby-display",
"items": [
{"type": "image", "url": "https://example.com/slide1.jpg"},
{"type": "image", "url": "https://example.com/slide2.jpg"}
],
"config": {
"auto_play": true,
"interval": 10
}
}
Connecting Your Widget to King Kiosk¶
King Kiosk provides a special window.KingKiosk object in your widget's web page that lets your code communicate with the app. Always wait for this connection to be ready before using it:
window.addEventListener('kingkiosk-ready', function() {
// Bridge is now available - start using the API
console.log('KingKiosk API ready!');
});
Available Methods:
| Method | Purpose |
|---|---|
KingKiosk.onCommand(callback) |
Receive commands from MQTT or King Kiosk |
KingKiosk.sendCommand(cmd, payload) |
Send a command (published to MQTT) |
KingKiosk.publishTelemetry(data) |
Send sensor readings or status data to other systems |
KingKiosk.storage.get(key) |
Retrieve a stored value |
KingKiosk.storage.set(key, value) |
Store a value persistently |
KingKiosk.storage.getAll() |
Get all stored values |
KingKiosk.getWidgetInfo() |
Get widget ID and platform info |
Receiving Commands¶
Register a callback to receive commands sent from MQTT or King Kiosk:
window.KingKiosk.onCommand(function(command, payload) {
console.log('Received:', command, payload);
switch (command) {
case 'set_value':
updateDisplay(payload.value);
break;
case 'set_color':
document.body.style.backgroundColor = payload.color;
break;
case 'refresh':
fetchLatestData();
break;
}
});
Send commands to your widget via MQTT:
Remote Browser bridge topic: kingkiosk/{device_id}/element/{window_id}/cmd
Local customWebView topic: kingkiosk/{device_id}/window/{window_id}/command
Sending Data Back From Your Widget¶
Your widget can send commands back to King Kiosk or share data (like sensor readings or user actions) with other systems like Home Assistant.
Send a Command:
// Notify that a button was pressed
window.KingKiosk.sendCommand('button_pressed', { buttonId: 'start' });
// Send form data
window.KingKiosk.sendCommand('form_submitted', {
name: 'John Doe',
email: 'john@example.com'
});
Commands are published to: kingkiosk/{device_id}/widget/{widget_id}/event
Share Sensor Data or Status Updates:
// Share sensor readings with Home Assistant or other systems
window.KingKiosk.publishTelemetry({ temperature: 72.5 });
// Publish multiple metrics
window.KingKiosk.publishTelemetry({
cpu_usage: 45.2,
memory_used: 8192,
uptime_seconds: 86400
});
// Periodic heartbeat
setInterval(function() {
window.KingKiosk.publishTelemetry({
heartbeat: true,
timestamp: Date.now()
});
}, 30000);
Telemetry is published to: kingkiosk/{device_id}/widget/{widget_id}/telemetry
Persistent Storage¶
Store data that persists across widget reloads:
// Store values
window.KingKiosk.storage.set('theme', 'dark');
window.KingKiosk.storage.set('lastUpdate', Date.now());
window.KingKiosk.storage.set('settings', { volume: 80, muted: false });
// Retrieve values (async)
const theme = await window.KingKiosk.storage.get('theme');
console.log('Current theme:', theme); // 'dark'
// Get all stored values
const allData = await window.KingKiosk.storage.getAll();
Storage behavior by runtime:
- Remote Browser bridge: persisted across restarts.
- Local customWebView: runtime-scoped to the active tile/controller.
Platform Considerations¶
Custom widgets work across King Kiosk platforms, with path-specific behavior:
| Platform | Local customWebView |
Remote Browser bridge | Notes |
|---|---|---|---|
| macOS | Yes | Yes | Either path works |
| iOS | Yes | Yes | Either path works |
| Android | Yes | Yes | Either path works |
| Windows | Yes | Yes | Either path works |
| Linux | Yes | Yes | Either path works |
| Web | Yes | Depends on deployment | Verify WebRTC/bridge support in your runtime |
| tvOS | No | Yes | Remote Browser path only |
tvOS (Apple TV) Special Notes:
Apple TV uses the Remote Browser path (no local customWebView runtime). For tvOS widgets:
- Use create_remote_browser with initial_url
- server_url is optional (override); default server settings are used when omitted
- The full bridge API is available: onCommand, sendCommand, publishTelemetry, storage.*
- Prefer hosted URLs for larger widgets; inline/data-URL approaches can hit URL-length limits
Responsive Design Tips:
const info = await window.KingKiosk.getWidgetInfo();
if (info.platform === 'ios' || info.platform === 'android') {
// Touch-optimized interface
document.body.classList.add('touch-mode');
} else {
// Desktop-like layout
document.body.classList.add('desktop-mode');
}
Complete Example: Smart Thermostat¶
Here's a fully-featured custom widget demonstrating all API features:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Smart Thermostat</title>
<style>
body {
font-family: -apple-system, sans-serif;
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: white;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 0;
padding: 20px;
}
.temperature { font-size: 96px; font-weight: 200; }
.unit { font-size: 36px; vertical-align: top; }
.label { font-size: 14px; text-transform: uppercase; opacity: 0.7; }
.controls { display: flex; gap: 20px; margin-top: 40px; }
.btn {
width: 60px; height: 60px; border-radius: 50%;
border: 2px solid rgba(255,255,255,0.3);
background: rgba(255,255,255,0.1);
color: white; font-size: 24px; cursor: pointer;
}
.btn:hover { background: rgba(255,255,255,0.2); }
.mode {
margin-top: 20px; padding: 8px 16px;
background: rgba(255,255,255,0.1); border-radius: 20px;
}
.mode.heating { background: rgba(255,100,100,0.3); }
.mode.cooling { background: rgba(100,100,255,0.3); }
</style>
</head>
<body>
<div class="temperature">
<span id="temp">72</span><span class="unit">°F</span>
</div>
<div class="label">Target Temperature</div>
<div class="controls">
<button class="btn" id="btnDown">-</button>
<button class="btn" id="btnUp">+</button>
</div>
<div class="mode" id="mode">OFF</div>
<script>
let targetTemp = 72;
let mode = 'off';
window.addEventListener('kingkiosk-ready', async function() {
// Load saved state
const saved = await window.KingKiosk.storage.get('targetTemp');
if (saved) targetTemp = saved;
updateDisplay();
// Handle incoming commands
window.KingKiosk.onCommand(function(cmd, payload) {
if (cmd === 'set_temperature') {
targetTemp = payload.temperature;
window.KingKiosk.storage.set('targetTemp', targetTemp);
updateDisplay();
sendTelemetry();
}
if (cmd === 'set_mode') {
mode = payload.mode;
updateDisplay();
}
});
// Send initial telemetry
sendTelemetry();
});
function updateDisplay() {
document.getElementById('temp').textContent = targetTemp;
document.getElementById('mode').textContent = mode.toUpperCase();
document.getElementById('mode').className = 'mode ' + mode;
}
function sendTelemetry() {
window.KingKiosk.publishTelemetry({
target_temperature: targetTemp,
mode: mode
});
}
document.getElementById('btnUp').onclick = function() {
targetTemp++;
window.KingKiosk.storage.set('targetTemp', targetTemp);
updateDisplay();
sendTelemetry();
window.KingKiosk.sendCommand('temperature_changed', {
temperature: targetTemp, direction: 'up'
});
};
document.getElementById('btnDown').onclick = function() {
targetTemp--;
window.KingKiosk.storage.set('targetTemp', targetTemp);
updateDisplay();
sendTelemetry();
window.KingKiosk.sendCommand('temperature_changed', {
temperature: targetTemp, direction: 'down'
});
};
</script>
</body>
</html>
This widget:
- Loads its saved state on startup
- Responds to set_temperature and set_mode commands via MQTT
- Publishes telemetry when values change
- Sends events when the user interacts with buttons
- Persists its state between reloads
Native Widget Alternatives¶
While building custom widgets with web technologies (HTML/CSS/JavaScript) offers maximum flexibility, King Kiosk also provides built-in alternatives that don't require any coding. These are perfect when you want high-performance displays without the complexity of web development, or when you're working in environments where custom coding isn't practical.
Three Native Approaches:
| Approach | Best For | Complexity | Performance |
|---|---|---|---|
| Canvas Widget | Complex dashboards, diagrams, network maps | Medium | Excellent |
| MQTT Image | External rendering, Python/Node.js integration | Low | Good |
| Dynamic Native Widgets | Simple displays using built-in components | Low | Excellent |
Canvas: Declarative Native Graphics¶
The Canvas widget is King Kiosk's most powerful native rendering system. Instead of writing code, you describe what to draw using JSON - King Kiosk handles all the rendering natively using a high-performance graphics engine.
Why Canvas? - No web overhead - Pure native rendering, no WebView memory or CPU cost - Declarative - Describe what you want, not how to draw it - Composable - Embed gauges, charts, and buttons inside your canvas - Interactive - Objects can publish MQTT messages when tapped - Real-time updates - Modify any object via MQTT commands
Create a Canvas:
{
"command": "open_canvas",
"window_id": "my-dashboard",
"title": "Server Status",
"x": 50, "y": 50,
"width": 800, "height": 600,
"doc": {
"background": {
"color": "#1a1a2e"
},
"objects": [],
"connections": []
}
}
Canvas Objects¶
Every visual element on a canvas is an "object" with a unique ID, position, size, and type-specific properties.
Common Object Properties:
| Property | Type | Description |
|---|---|---|
object_id |
string | Unique identifier (required) |
type |
string | node, text, shape, image, embed, group |
x, y |
number | Position (top-left corner) |
width, height |
number | Size |
z_index |
int | Render order (higher = on top) |
visible |
bool | Show/hide without removing |
locked |
bool | Prevent local dragging |
rotation_deg |
number | Rotation around center |
opacity |
number | 0.0 to 1.0 |
style |
object | Colors, borders, etc. |
mqtt |
object | Tap-to-publish and live subscribe bindings |
Text Objects:
{
"object_id": "title",
"type": "text",
"text": "System Status",
"x": 50, "y": 30,
"style": {
"color": "#ffffff",
"font_size_px": 28,
"font_weight": "bold"
}
}
Shape Objects:
{
"object_id": "status-bg",
"type": "shape",
"shape": "round_rect",
"x": 50, "y": 80,
"width": 200, "height": 100,
"style": {
"fill_color": "#2d3436",
"stroke_color": "#00ff00",
"stroke_width_px": 2,
"corner_radius_px": 12
}
}
Shape types: rect, round_rect, circle, line
Image Objects:
{
"object_id": "server-icon",
"type": "image",
"x": 70, "y": 100,
"width": 64, "height": 64,
"source": {
"url": "https://example.com/icons/server.png"
},
"fit": "contain",
"opacity": 0.9
}
Node Objects (for diagrams):
{
"object_id": "server-1",
"type": "node",
"x": 100, "y": 200,
"width": 120, "height": 80,
"label": "Web Server",
"label_font_size_px": 18,
"subtitle": "Primary",
"subtitle_font_size_px": 13,
"icon_size_px": 34,
"content_scale": 1.0,
"icon": { "set": "material", "name": "dns" },
"style": {
"color": "#3498db",
"border_color": "#2980b9"
},
"ports": [
{ "port_id": "out", "pos": { "edge": "east", "t": 0.5 } }
]
}
Embedded Widgets:
This is where Canvas becomes incredibly powerful - embed fully functional native widgets inside your canvas:
{
"object_id": "cpu-gauge",
"type": "embed",
"x": 300, "y": 150,
"width": 150, "height": 150,
"embed": {
"widget_type": "gauge",
"id": "cpu-gauge-widget",
"config": {
"gauge_type": "radial",
"min": 0,
"max": 100,
"value": 45,
"unit": "%",
"title": "CPU",
"thresholds": [
{ "value": 70, "color": "#f1c40f" },
{ "value": 90, "color": "#e74c3c" }
]
}
}
}
Embeddable widgets: gauge, chart, mqtt_button, mqtt_action_status
For these supported embed types, use the same config keys you would use for the standalone widget/window version. The canvas embed path feeds that config into the same widget/controller logic rather than maintaining a separate canvas-only schema.
Connections (Lines Between Objects)¶
Connect objects with lines - perfect for network diagrams, flowcharts, and system maps:
{
"connections": [
{
"connection_id": "link-1",
"from": { "object_id": "server-1", "port_id": "out" },
"to": { "object_id": "database-1", "port_id": "in" },
"style": {
"color": "#00ff00",
"width_px": 2,
"arrow": "end"
}
}
]
}
Connection styles:
- arrow: none, end, both
- dash: Array of dash/gap lengths, e.g., [5, 3]
- animated: true for flowing animation effect
Interactive Canvas Objects¶
Make objects respond to taps by publishing MQTT messages:
{
"object_id": "restart-btn",
"type": "shape",
"shape": "round_rect",
"x": 600, "y": 500,
"width": 150, "height": 50,
"style": {
"fill_color": "#e74c3c",
"corner_radius_px": 8
},
"mqtt": {
"publish": {
"topic": "home/server/command",
"payload": { "action": "restart" }
}
}
}
Live MQTT Subscribe Bindings¶
Canvas objects and connections can also subscribe to MQTT topics and update themselves in real time. Canvas now matches the gauge widget's path field naming:
- Prefer
json_fieldin new documents json_pathandvalue_pathremain supported aliasespath_syntaxcan be"auto","dot", or"jsonpath"- In
"auto"mode, paths starting with$are treated as JSONPath-like paths and everything else uses dot paths
Example: Home Assistant-backed node with explicit JSONPath syntax
{
"object_id": "string-lights",
"type": "node",
"label": "String Lights",
"subtitle": "Off",
"x": 480, "y": 320,
"width": 110, "height": 44,
"mqtt": {
"publish": {
"topic": "kingkiosk/ha/command/light/backyard_string_lights",
"payload": { "service": "toggle" }
},
"subscribe": [
{
"topic": "kingkiosk/ha/state/light/backyard_string_lights",
"role": "value",
"json_field": "$.state",
"path_syntax": "jsonpath",
"default": "off"
},
{
"topic": "kingkiosk/ha/state/light/backyard_string_lights",
"role": "color",
"json_field": "$.state",
"path_syntax": "jsonpath",
"default": "off",
"map": {
"enum": {
"on": { "color": "#FFFDD835" },
"off": { "color": "#FF616161" }
}
}
}
]
}
}
Common path forms:
- Dot path:
"json_field": "attributes.current_temperature" - JSONPath-like object field:
"json_field": "$.state" - JSONPath-like array index:
"json_field": "$.items[0].value" - JSONPath-like quoted key:
"json_field": "$['attributes']['friendly_name']"
Role behavior at a glance:
textupdates primary visible textvalueupdates secondary visible text, usually a node subtitlestateupdates secondary text and can also drive color/icon mappingcolorcan use either a literal incoming color or a mapped state-to-color table
For full role and binding details, see the MQTT Widget Reference.
Updating Canvas Objects via MQTT¶
After creating a canvas, update individual objects without recreating the entire canvas:
Update an object:
{
"command": "update_objects",
"objects": [
{
"object_id": "cpu-gauge",
"embed": {
"config": {
"value": 78
}
}
}
]
}
Change text:
{
"command": "update_objects",
"objects": [
{
"object_id": "status-text",
"text": "All Systems Operational",
"style": {
"color": "#00ff00"
}
}
]
}
Add new objects:
{
"command": "add_objects",
"objects": [
{
"object_id": "alert-banner",
"type": "text",
"text": "WARNING: High CPU Usage",
"x": 50, "y": 550,
"style": { "color": "#e74c3c", "font_size_px": 18 }
}
]
}
Remove objects:
Complete Canvas Example: Server Dashboard¶
Here's a full example creating a server monitoring dashboard:
{
"command": "open_canvas",
"window_id": "server-dashboard",
"title": "Infrastructure Status",
"width": 900, "height": 700,
"doc": {
"background": {
"color": "#0f0f23"
},
"objects": [
{
"object_id": "header",
"type": "text",
"text": "Infrastructure Monitor",
"x": 30, "y": 20,
"style": { "color": "#ffffff", "font_size_px": 32, "font_weight": "bold" }
},
{
"object_id": "web-server",
"type": "node",
"x": 100, "y": 150,
"width": 140, "height": 90,
"label": "Web Server",
"icon": { "set": "material", "name": "public" },
"style": { "color": "#3498db" },
"ports": [{ "port_id": "out", "pos": { "edge": "east", "t": 0.5 } }]
},
{
"object_id": "api-server",
"type": "node",
"x": 380, "y": 150,
"width": 140, "height": 90,
"label": "API Server",
"icon": { "set": "material", "name": "dns" },
"style": { "color": "#9b59b6" },
"ports": [
{ "port_id": "in", "pos": { "edge": "west", "t": 0.5 } },
{ "port_id": "out", "pos": { "edge": "east", "t": 0.5 } }
]
},
{
"object_id": "database",
"type": "node",
"x": 660, "y": 150,
"width": 140, "height": 90,
"label": "Database",
"icon": { "set": "material", "name": "storage" },
"style": { "color": "#27ae60" },
"ports": [{ "port_id": "in", "pos": { "edge": "west", "t": 0.5 } }]
},
{
"object_id": "cpu-gauge",
"type": "embed",
"x": 100, "y": 350,
"width": 180, "height": 180,
"embed": {
"widget_type": "gauge",
"id": "cpu",
"config": {
"gauge_type": "radial",
"min": 0, "max": 100,
"value": 42,
"unit": "%",
"title": "CPU Usage",
"thresholds": [
{ "value": 70, "color": "#f39c12" },
{ "value": 90, "color": "#e74c3c" }
]
}
}
},
{
"object_id": "memory-gauge",
"type": "embed",
"x": 360, "y": 350,
"width": 180, "height": 180,
"embed": {
"widget_type": "gauge",
"id": "memory",
"config": {
"gauge_type": "radial",
"min": 0, "max": 64,
"value": 28,
"unit": "GB",
"title": "Memory",
"thresholds": [
{ "value": 48, "color": "#f39c12" },
{ "value": 58, "color": "#e74c3c" }
]
}
}
},
{
"object_id": "requests-chart",
"type": "embed",
"x": 620, "y": 350,
"width": 250, "height": 180,
"embed": {
"widget_type": "chart",
"id": "requests",
"config": {
"chart_type": "line",
"title": "Requests/sec",
"max_points": 30
}
}
},
{
"object_id": "status-text",
"type": "text",
"text": "All Systems Operational",
"x": 30, "y": 600,
"style": { "color": "#2ecc71", "font_size_px": 24 }
},
{
"object_id": "restart-btn",
"type": "embed",
"x": 700, "y": 580,
"width": 160, "height": 60,
"embed": {
"widget_type": "mqtt_button",
"id": "restart-all",
"config": {
"label": "Restart All",
"publish_topic": "infrastructure/command",
"publish_payload": { "action": "restart_all" },
"style": "danger"
}
}
}
],
"connections": [
{
"connection_id": "web-to-api",
"from": { "object_id": "web-server", "port_id": "out" },
"to": { "object_id": "api-server", "port_id": "in" },
"style": { "color": "#2ecc71", "width_px": 3, "animated": true }
},
{
"connection_id": "api-to-db",
"from": { "object_id": "api-server", "port_id": "out" },
"to": { "object_id": "database", "port_id": "in" },
"style": { "color": "#2ecc71", "width_px": 3, "animated": true }
}
]
}
}
MQTT Image: External Rendering¶
If you have existing tools that generate images (Python scripts, Node.js apps, image processing pipelines), the MQTT Image widget lets you stream rendered frames directly to King Kiosk.
Why MQTT Image? - Use any language - Python, Node.js, Go, Rust - anything that can publish to MQTT - Leverage existing libraries - PIL/Pillow, Cairo, ImageMagick, matplotlib, D3.js (server-side) - Pre-rendered content - Complex visualizations rendered on powerful hardware - Integration flexibility - Connect to systems that already generate images
Create an MQTT Image widget:
{
"command": "mqtt_image",
"window_id": "external-render",
"mqtt_topic": "myapp/rendered/frame",
"is_base64": true,
"title": "External Dashboard",
"x": 100, "y": 100,
"width": 800, "height": 600
}
Publish images from Python:
import paho.mqtt.client as mqtt
from PIL import Image, ImageDraw, ImageFont
import base64
import io
def render_dashboard(cpu, memory, status):
# Create image
img = Image.new('RGB', (800, 600), color='#1a1a2e')
draw = ImageDraw.Draw(img)
# Draw content
draw.text((50, 30), "System Dashboard", fill='white')
draw.text((50, 100), f"CPU: {cpu}%", fill='#3498db')
draw.text((50, 150), f"Memory: {memory}GB", fill='#9b59b6')
draw.text((50, 200), f"Status: {status}", fill='#2ecc71')
# Convert to base64
buffer = io.BytesIO()
img.save(buffer, format='PNG')
return base64.b64encode(buffer.getvalue()).decode()
# Connect and publish
client = mqtt.Client()
client.connect("your-broker", 1883)
# Render and send
frame = render_dashboard(cpu=45, memory=28, status="Online")
client.publish("myapp/rendered/frame", frame)
Publish from Node.js:
const mqtt = require('mqtt');
const { createCanvas } = require('canvas');
const client = mqtt.connect('mqtt://your-broker');
function renderFrame(data) {
const canvas = createCanvas(800, 600);
const ctx = canvas.getContext('2d');
// Dark background
ctx.fillStyle = '#1a1a2e';
ctx.fillRect(0, 0, 800, 600);
// Draw content
ctx.fillStyle = 'white';
ctx.font = '32px sans-serif';
ctx.fillText('System Dashboard', 50, 50);
ctx.fillStyle = '#3498db';
ctx.font = '24px sans-serif';
ctx.fillText(`CPU: ${data.cpu}%`, 50, 120);
// Return as base64
return canvas.toBuffer('image/png').toString('base64');
}
// Publish every 5 seconds
setInterval(() => {
const frame = renderFrame({ cpu: Math.round(Math.random() * 100) });
client.publish('myapp/rendered/frame', frame);
}, 5000);
Update interval:
For animated displays, the widget can poll for new images:
{
"command": "mqtt_image",
"mqtt_topic": "myapp/rendered/frame",
"is_base64": true,
"update_interval": 1000
}
Dynamic Native Widgets¶
Don't overlook the power of King Kiosk's built-in widgets. Gauges, charts, and animated text are highly configurable and can be dynamically updated via MQTT - often eliminating the need for custom development entirely.
Dynamic Gauge Updates:
Create a gauge once, then update its value from any system:
{
"command": "create_gauge",
"window_id": "temp-gauge",
"gauge_type": "radial",
"title": "Temperature",
"min": 0, "max": 100,
"value": 72,
"unit": "°F",
"thresholds": [
{ "value": 80, "color": "#f39c12" },
{ "value": 90, "color": "#e74c3c" }
]
}
Update from Home Assistant, Node-RED, or any MQTT client:
Dynamic Charts:
Create real-time charts that update as data arrives:
{
"command": "create_chart",
"window_id": "sensor-chart",
"chart_type": "line",
"title": "Temperature History",
"max_points": 60,
"mqtt_topic_prefix": "kingkiosk"
}
Append data points over time:
Dynamic Animated Text:
Create eye-catching displays without writing any code:
{
"command": "open_animated_text",
"window_id": "alert-display",
"preset": "tickerMarquee",
"text": "Welcome to King Kiosk!",
"layout": {
"alignment": "center",
"vertical_alignment": "middle"
},
"background_mode": "gradient",
"background_color": "#1a1a2e"
}
Update the text dynamically via element command:
Topic: kingkiosk/{device_id}/element/alert-display/cmd
When to Use Each Approach:
| Scenario | Recommended Approach |
|---|---|
| Complex dashboard with multiple data sources | Canvas with embedded widgets |
| Network topology / system diagram | Canvas with nodes and connections |
| Already have Python/Node rendering code | MQTT Image |
| Simple numeric displays | Dynamic Gauges |
| Time-series data visualization | Dynamic Charts |
| Scrolling announcements | Animated Text |
| Full interactivity with custom logic | Custom web widget (HTML/JS) |
OS Widgets (Home Screen Widgets)¶
King Kiosk can create native widgets on your Android home screen or iOS/iPadOS home screen and lock screen. These widgets update automatically and work even when the King Kiosk app isn't open — perfect for at-a-glance information like temperature sensors, alarm status, or quick-action buttons.
What are OS Widgets?
OS widgets are small visual elements that appear on your phone or tablet's home screen (Android) or home/lock screen (iOS). Unlike the widgets inside King Kiosk (which appear in windows on the kiosk display), OS widgets are native to your device's operating system.
Think of it this way: - King Kiosk window widgets = Displayed inside the King Kiosk app on your wall-mounted tablet - OS widgets = Small info cards on your phone's home screen that you can glance at without opening any app
Why Use OS Widgets?
- Always visible — No need to open King Kiosk to check a sensor value
- Updates automatically — Widgets refresh their data from MQTT on their own schedule
- Battery efficient — Widgets use minimal power compared to running the full app
- Quick actions — Tap a button widget to control your home without opening the app
- Multiple widgets — Create as many as you need for different sensors or controls
Supported Widget Types:
| Widget Type | What It Shows | Best For |
|---|---|---|
| Gauge | Numeric value with optional zones/thresholds | Temperature, humidity, battery levels |
| Chart | Small line chart showing value history | Temperature trends, power usage |
| Weather | Current conditions and forecast | Current weather, daily forecast |
| Alarmo | Security system status and arm/disarm controls | Home security, alarm status |
| MQTT Button | Toggle button with on/off states | Light switches, scene triggers |
| Sensor | Simple value display | Any numeric sensor value |
| Canvas | Visual diagram snapshot | System diagrams, network maps |
| Clock | Current time display | Quick time check |
Creating an OS Widget:
To create a native OS widget, add the os_widget: true parameter when creating a widget via MQTT. The widget will be created both inside King Kiosk (if you want it there) and as a native OS widget.
Example: Temperature Gauge Widget
{
"command": "create_gauge",
"window_id": "living-room-temp",
"gauge_type": "radial",
"title": "Living Room",
"min": 50,
"max": 90,
"value": 72,
"unit": "°F",
"os_widget": true,
"mqtt_topic": "homeassistant/sensor/living_room_temperature/state",
"json_field": "temperature"
}
What happens: 1. King Kiosk creates the gauge inside the app 2. King Kiosk also registers a native OS widget with your device 3. You can now add this widget to your home screen just like any other widget 4. The widget automatically subscribes to the MQTT topic and updates when new values arrive
Example: Security System Widget (Alarmo)
{
"command": "alarmo_widget",
"window_id": "home-alarm",
"mqtt_base_topic": "alarmo",
"require_code": true,
"code_length": 4,
"available_modes": ["armed_away", "armed_home", "armed_night"],
"os_widget": true
}
The widget shows alarm status and provides arm/disarm buttons. Tapping the widget opens quick controls without opening the full app.
Example: MQTT Button Widget
{
"command": "mqtt_button",
"window_id": "porch-light",
"label": "Porch Light",
"mode": "toggle",
"publish_topic": "home/lights/porch/set",
"subscription_topic": "home/lights/porch/state",
"icon": "light_bulb",
"color_on": "0xFFFFC107",
"color_off": "0xFF757575",
"os_widget": true
}
Creates a home screen button that toggles your porch light. The button color updates based on the light's state.
Adding Widgets to Your Home Screen:
On Android: 1. Long-press on your home screen 2. Tap "Widgets" 3. Scroll to "King Kiosk" 4. Drag the widget type you created to your home screen 5. Select which widget (if you have multiple)
On iOS/iPadOS: 1. Long-press on your home screen 2. Tap the "+" button in the top corner 3. Search for "King Kiosk" 4. Choose the widget size and type 5. Add to home screen or lock screen 6. Edit the widget to select which data source
How Updates Work:
OS widgets connect to MQTT independently of the main King Kiosk app. They use a lightweight MQTT client built into the widget extension:
- Automatic updates — Widgets subscribe to their configured MQTT topic
- Efficient refresh — iOS widgets refresh every 15-60 minutes, Android widgets can refresh more frequently
- Cached values — King Kiosk also caches the latest values in shared storage for instant widget display
Platform Differences:
| Feature | Android | iOS |
|---|---|---|
| Widget placement | Home screen only | Home screen, lock screen, Today view |
| Update frequency | Every few minutes | Every 15-60 minutes (OS controlled) |
| Interactive buttons | Yes | Yes (iOS 17+) |
| Resize | Yes | Limited sizes |
Privacy & Battery:
- Widgets only connect to MQTT when refreshing — not continuously
- MQTT credentials are stored securely in your device's keychain
- Widgets use minimal battery (less than 1% per day typically)
- Widgets can be removed at any time without affecting King Kiosk
Removing OS Widgets:
To remove a widget from your home screen, simply delete it like any other widget (long-press and remove). To fully unregister it from King Kiosk, send a remove command:
This removes both the in-app widget and the OS widget registration.
Troubleshooting:
- Widget not appearing in widget list? Make sure you created it with
os_widget: trueand the device supports widgets - Widget showing old data? The widget updates on its own schedule. iOS restricts update frequency to preserve battery
- Widget showing "No data"? Check that MQTT credentials are configured and the topic has published at least once
- Button widget not responding? Verify the publish topic and payload are correct in your MQTT broker
9. Home Assistant Integration¶
King Kiosk is designed to work seamlessly with Home Assistant. Basic auto-discovery setup is covered in Getting Started - once enabled, your displays automatically appear as devices in Home Assistant.
This section covers advanced integration patterns for power users.
Creating Custom Sensors¶
Monitor widget states in Home Assistant:
mqtt:
sensor:
- name: "Living Room Clock Mode"
state_topic: "kingkiosk/living_room/element/clock-1/state"
value_template: "{{ value_json.mode }}"
json_attributes_topic: "kingkiosk/living_room/element/clock-1/state"
Controlling King Kiosk from Home Assistant¶
Create buttons to control your displays:
mqtt:
button:
- name: "Living Room Morning Mode"
command_topic: "kingkiosk/living_room/system/cmd"
payload_press: '{"command": "load_screen_state", "name": "Morning Dashboard"}'
- name: "Toggle Clock Mode"
command_topic: "kingkiosk/living_room/element/clock-1/cmd"
payload_press: '{"command": "toggle_mode"}'
Use automations to control displays based on events:
automation:
- alias: "Turn on lobby display when motion detected"
trigger:
- platform: state
entity_id: binary_sensor.lobby_motion
to: "on"
action:
- service: mqtt.publish
data:
topic: "kingkiosk/lobby_display/system/cmd"
payload: '{"command": "set_brightness", "value": 1.0}'
10. Tips and Best Practices¶
Performance Optimization¶
For smooth operation:
- Limit the number of simultaneous video streams
- Use appropriate image sizes (don't load 4K images for thumbnail displays)
- Close unused widgets to free memory
- Use the reset_media command if playback becomes sluggish
Understanding Performance Mode¶
King Kiosk includes a Performance Mode setting in App Settings with three choices:
- Auto (recommended) - King Kiosk chooses the effective mode based on device RAM and current memory pressure
- Forced Standard - keeps full visuals and normal behavior even on weaker hardware
- Forced Low-Memory - prioritizes stability and lower idle load over visual polish
The dropdown is an override. If you choose Forced Standard or Forced Low-Memory, that choice wins until you change it back to Auto.
What Low-Memory mode changes: - Reduces image cache size to lower RAM use - Disables halo effects - Removes or shortens decorative UI animations where possible - Uses cheaper clock behavior, including slower split-flap updates - Stops nonessential pulsing effects such as the notification chip pulse
What Low-Memory mode does not do: - It does not magically make expensive content cheap - Live video, camera capture, face recognition, web views, and multiple active widgets can still overwhelm weak hardware - It does not reduce network bandwidth requirements for media streams
Pros of Low-Memory mode: - Lower idle CPU usage - Lower memory pressure - Better stability on Raspberry Pi, low-end Android tablets, and older phones - Less chance of jank, watchdog kills, or thermal throttling
Cons of Low-Memory mode: - Less visual polish - Fewer animated effects - Some UI transitions become instant instead of smooth - Visual widgets may feel more utilitarian
Practical guidance: - On a modern desktop, keep Auto - On a Raspberry Pi Zero 2 W, older tablet, or any device that feels borderline, start with Forced Low-Memory - If a weak device is still struggling, also turn off unused services, avoid camera/face features, and keep the number of open widgets low
For battery-powered devices: - Reduce screen brightness - Extend carousel intervals - Disable unnecessary sensors - Use screen schedules to dim displays during off-hours
Network Considerations¶
For reliable MQTT: - Use a local broker when possible for lowest latency - Enable SSL for secure communications over the internet - For critical commands, consider using higher reliability settings (QoS - Quality of Service levels) to ensure delivery - Monitor your server's connection count if managing many displays
For media streaming: - Ensure adequate bandwidth for video content - Use local media servers when possible - Consider CDN-hosted content for distributed deployments
Troubleshooting Common Issues¶
MQTT Not Connecting: 1. Verify broker host and port in settings 2. Check username and password 3. Ensure the device can reach the broker (firewall rules) 4. Try disabling SSL to rule out certificate issues
Widget Not Responding to Commands: 1. Verify the widget ID in your command matches the ID you gave when creating the widget 2. Check for typos in the command name 3. Ensure the widget is actually created and visible on screen 4. Check the response messages for error details
Display Seems Slow:
1. Close unused windows
2. Check network connectivity
3. Reduce video quality or count
4. Use the reset_media command
5. Restart the app if necessary
Layout Not Saving: 1. Ensure you have permission to write to storage 2. Check available storage space 3. Try a different state name 4. Export and re-import as a workaround
11. Apple TV Experience¶
King Kiosk on Apple TV isn't just a port - it's a completely reimagined experience built from the ground up for the living room. While other apps offer basic single-content displays on tvOS, King Kiosk brings its full Programmable Display Runtime™ to your television, controlled entirely with the Siri Remote.
There's simply nothing else like it on the Apple TV platform.
A Revolutionary Window System for Television¶
Traditional TV apps show one thing at a time. King Kiosk shatters that limitation. On your Apple TV, you can simultaneously display:
- A clock in one corner
- Weather forecast in another
- A live security camera feed
- Your Home Assistant dashboard
- A news ticker
- And more - all visible at once, each in its own resizable tile
Two Layout Modes:
Floating Mode - Windows float freely on screen. Position them anywhere, resize them to any dimension, overlap them as needed. Perfect for creative dashboard layouts where you want precise control over every pixel.
Tiling Mode - Inspired by the i3 window manager beloved by power users, tiling mode automatically arranges your windows in a clean, non-overlapping grid. Add a new window and it intelligently splits the space. Remove one and the others expand to fill the gap. No manual arrangement needed.
Switch between modes anytime with the MQTT command:
Or toggle with "mode": "toggle" to flip between floating and tiling.
Controlling with the Siri Remote¶
The Siri Remote becomes your complete control center for King Kiosk. Here's what each button does:
| Button | Function |
|---|---|
| D-pad (Up/Down/Left/Right) | Navigate between windows using spatial focus |
| Select (Center Click) | Interact with the focused window |
| Play/Pause | Enter/exit Move Mode (floating layout only) |
| Menu | Exit current mode, go back, or close menus |
| TV/Home | Standard tvOS home behavior |
The Focus Ring
When navigating between windows, a glowing focus ring shows which tile is currently selected. This ring is smart:
- Auto-hides after 5 seconds of inactivity for a cleaner display
- Instantly reappears when you press any remote button
- Changes color to indicate different modes (white for normal, cyan for interactive)
- Shows mode indicators like "MOVE ARROWS" when in move mode
Understanding Focus Navigation¶
Unlike touch screens where you tap directly on what you want, Apple TV uses spatial navigation. When you press a direction on the D-pad, King Kiosk uses intelligent proximity detection to determine which window should receive focus next.
How it works:
- The system calculates the center point of each visible window
- When you press Right, it filters to windows with centers to the right of your current focus
- It scores candidates by distance (closer = better)
- The closest window in that direction receives focus
This feels natural and intuitive - press toward the window you want, and focus moves there.
Edge Cases:
If no window exists in the direction you pressed, focus either: - Stays on the current window, or - Cycles through windows by z-order (if you keep pressing)
This means you can never get "stuck" - keep pressing a direction and you'll eventually cycle through all windows.
Move Mode: Rearranging Your Layout¶
In floating mode, you can reposition windows using just the remote:
- Focus on the window you want to move
- Press Play/Pause to enter Move Mode
- The focus ring changes and shows "MOVE ARROWS"
- Use the D-pad to nudge the window (40 pixels per press)
- Press Play/Pause again (or Menu) to exit Move Mode
While in Move Mode: - The moving window stays focused (you can't accidentally navigate away) - Other windows ignore D-pad input - Smooth animation makes positioning feel responsive
Note: Move Mode is only available in floating layout. In tiling mode, window positions are automatic.
Interactive Mode: Deep Widget Control¶
Some widgets need more than simple selection - they need to capture D-pad input for their own purposes. King Kiosk calls this Interactive Mode or Input Capture.
Widgets that support Interactive Mode:
| Widget | D-pad Behavior in Interactive Mode |
|---|---|
| PDF Viewer | Left/Right = Previous/Next page |
| Media Player | Left/Right = Skip back/forward 10 seconds |
| DLNA Player | Full playback controls |
| Remote Browser | Navigate web pages |
| Games | Full game controls |
| Custom WebView | Widget-defined controls |
Entering Interactive Mode:
Select a widget that supports it, and it automatically captures D-pad input. The focus ring turns cyan with a glow effect and displays "INTERACTIVE" to indicate the mode change.
Exiting Interactive Mode:
Press the Menu button to release input capture and return to normal window navigation.
MQTT Control for Apple TV¶
Every feature of King Kiosk on Apple TV is controllable via MQTT - perfect for Home Assistant automations, Node-RED flows, or custom control systems.
All standard window commands work identically on Apple TV:
{
"command": "open_clock",
"window_id": "living-room-clock",
"mode": "analog",
"theme": "dark",
"x": 50,
"y": 50,
"width": 300,
"height": 300
}
{
"command": "create_carousel",
"window_id": "family-photos",
"items": [
{"type": "image", "url": "https://example.com/photo1.jpg"},
{"type": "image", "url": "https://example.com/photo2.jpg"}
],
"config": {
"auto_play": true,
"interval": 10
}
}
Apple TV publishes its state too:
Subscribe to kingkiosk/{device_id}/windows to receive real-time updates about:
- Window list and positions
- Focus state changes
- Mode changes (floating/tiling)
- Window events (created, closed, moved, resized)
Remote Browser: Full Web on Your TV¶
Apple TV has no built-in ability to display web pages directly, which traditionally limits what TV apps can show. King Kiosk solves this brilliantly with the Remote Browser feature.
How it works:
- A Remote Browser server (which you set up) renders web pages using a full browser engine
- The rendered output streams to your Apple TV in real-time as video
- Your remote control inputs are sent back to the server so you can interact with web pages
The result? Full, interactive web browsing on your TV - something Apple doesn't natively allow.
Setting up Remote Browser:
{
"command": "create_remote_browser",
"window_id": "ha-dashboard",
"initial_url": "http://homeassistant.local:8123",
"auto_connect": true
}
What you can display: - Home Assistant Lovelace dashboards - Grafana monitoring panels - Node-RED dashboards - Any web application - Custom HTML widgets (with full JavaScript)
Platform Convenience:
On Apple TV, the standard browser commands automatically use Remote Browser:
- open_browser -> Creates a Remote Browser
- open_web -> Creates a Remote Browser
- open_simple_web -> Creates a Remote Browser
This means your automations work across all platforms without modification. Send the same open_browser command to an iPad and an Apple TV - each uses its optimal rendering method automatically.
Custom Widgets via Remote Browser:
The full Custom Widget SDK works on Apple TV through Remote Browser:
- KingKiosk.onCommand() receives MQTT commands
- KingKiosk.sendCommand() sends commands back
- KingKiosk.publishTelemetry() publishes sensor data
- KingKiosk.storage.* persists widget state
Build your custom widget once, deploy it everywhere - including Apple TV.
Apple TV Tips and Best Practices¶
Layout Design for 10-Foot UI: - Use larger fonts (minimum 24pt for body text) - High contrast colors work best on TVs - Leave generous margins - TV overscan can clip edges - Test your layout from across the room
Performance on Apple TV: - Video playback is hardware-accelerated and smooth - Limit simultaneous video streams to 2-3 for best performance - Remote Browser works best on local network (low latency) - Complex web dashboards may need a powerful Remote Browser server
Ideal Use Cases: - Smart home control center in the living room - Digital photo frame with weather and time overlay - Security camera monitor with multiple feeds - Meeting room display with calendar and room status - Retail displays with multiple content zones
Automation Ideas:
"Movie mode" when Apple TV starts playing:
automation:
- alias: "King Kiosk Movie Mode"
trigger:
- platform: state
entity_id: media_player.apple_tv
to: 'playing'
action:
- service: mqtt.publish
data:
topic: "kingkiosk/living_room_tv/system/cmd"
payload: '{"command": "close_all_windows"}'
Show security cameras when doorbell rings:
automation:
- alias: "Doorbell Camera on TV"
trigger:
- platform: state
entity_id: binary_sensor.doorbell
to: 'on'
action:
- service: mqtt.publish
data:
topic: "kingkiosk/living_room_tv/system/cmd"
payload: |
{
"command": "play_media",
"window_id": "doorbell-cam",
"url": "rtsp://camera.local/doorbell",
"width": 640,
"height": 480,
"x": 100,
"y": 100
}
12. ESP32 Touchscreen Experience (KingKiosk Pico)¶
KingKiosk Pico brings the Programmable Display Runtime™ to the smallest, cheapest screens imaginable — $15 ESP32 touchscreen panels that mount next to light switches, on refrigerators, or beside your bed. It speaks the exact same MQTT protocol as the full KingKiosk client, so King Admin, Home Assistant, and your existing automations control it identically.
KingKiosk Pico is designed as a cross-platform embedded runtime. The codebase is split into shared common code (widgets, command dispatcher, tile manager, alert system, provisioning logic) and thin platform-specific layers. Today it runs on ESP32; an RP2040 (Raspberry Pi Pico W) port is in progress, and the architecture supports adding new microcontroller platforms with minimal effort.
This is the wall panel the Home Assistant community has been waiting for — no YAML, no reflashing, no ESPHome configs. Just send an MQTT command and the display changes instantly.
What You Need¶
- An ESP32 touchscreen board. The firmware is currently built and tested for the Sunton ESP32-3248S035C — a 3.5" 480x320 capacitive touchscreen panel with an ST7796 display and GT911 touch controller, available for ~$15 on AliExpress. Other ESP32 boards with SPI displays can be supported — contact KallInnovations support at support@kallinnovations.com for board compatibility questions.
- A WiFi network (2.4 GHz — the ESP32 does not support 5 GHz)
- An MQTT broker (Mosquitto, the one built into Home Assistant, or any standard broker)
- Optionally: King Admin for a graphical control experience
That's it. No cloud account, no subscription, no internet connection required.
Note on chipset: KingKiosk Pico currently targets the ESP32 (Xtensa dual-core, 4MB flash, no PSRAM). It does not yet run on ESP32-S3, ESP32-C3, or other variants — though porting to ESP32-S3 (with PSRAM for larger displays) is planned. An RP2040 (Raspberry Pi Pico W) port is also in progress.
First-Time Setup¶
When you power on a fresh KingKiosk Pico for the first time (or after a factory reset), the device automatically enters Setup Mode:
- The screen displays "Setup Mode" with a WiFi network name like KingPico-F23438
- On your phone or laptop, connect to that WiFi network (it's open, no password)
- Your phone should automatically open a setup page. If not, open a browser and go to http://192.168.4.1
- Fill in your WiFi credentials, MQTT broker address, and optionally a device name
- Tap Save & Reboot
The device reboots, connects to your WiFi, connects to your MQTT broker, and is ready to receive commands. The whole process takes about 30 seconds.
Re-entering Setup Mode later:
If you move the device to a new network, change your WiFi password, or just need to reconfigure:
- 10-tap gesture: Tap the screen rapidly 10 times within 5 seconds. A confirmation dialog appears showing your current device info (name, MAC address, WiFi and MQTT connection status with green/red indicators, timezone). Tap Setup to reboot into Setup Mode.
- MQTT command: Send
{"command":"start_ap_provisioning"}to reboot into Setup Mode remotely. - Factory reset: Send
{"command":"provision","settings":{"wifiSsid":"","wifiPassword":""}}to clear WiFi credentials — the device enters Setup Mode on next boot.
What's On Screen¶
After setup, you'll see a dark screen with a subtle crown watermark — that's the idle state. KingKiosk Pico is waiting for you to tell it what to display.
The Lock Button: A small icon appears in the bottom-right corner. This is the touch lock control. The icon and color tell you the current state at a glance:
| Color | Icon | Meaning | What Happens When Tapped |
|---|---|---|---|
| Green | Pen (edit) | Unlocked — the screen is interactive, you can tap widgets and make changes | Tap to lock the screen |
| Red | Eye (view-only) | Locked — the screen is display-only, touches are ignored | Tap to open the PIN keypad and unlock |
The metaphor is simple: green pen = you can edit, red eye = you can only look. The lock button auto-hides after 8 seconds of inactivity to keep your display clean. Just touch anywhere on the screen and it reappears. This means on a wall-mounted panel showing a dashboard, you'll see only your content — the lock button stays out of the way until you need it.
Notification Badge: When notifications arrive, a small blue badge appears in the top-right corner showing the unread count. Tap it to open the Notification Center — a scrollable panel showing all recent notifications with timestamps and priority indicators.
Available Widgets¶
KingKiosk Pico supports a rich set of widgets, all controlled via MQTT. Here's what you can display:
| Widget | What It Does |
|---|---|
| Clock | Digital or analog clock with timezone support |
| Gauge | Circular dial for temperature, humidity, speed — any numeric value |
| Text | Scrolling or static text messages, status displays |
| MQTT Button | Touchable buttons that publish MQTT messages (toggle lights, run scenes) |
| Timer / Stopwatch | Countdown timer or stopwatch with preset support |
| Weather | Current conditions with temperature, humidity, wind, and weather icons |
| Chart | Line, bar, or pie charts for historical data |
| Calendar | Monthly calendar view with highlighted event dates |
| RSS Feed | Auto-rotating news headlines from any RSS source |
| LED Panel | Pixel grid for status indicators, mini-animations, or ambient lighting |
| Image | Display images from HTTP URLs or MQTT topics (JPEG, PNG, QOI formats) |
| QR Code | Display any URL, text, or WiFi credentials as a scannable QR code |
| Alarmo | Home Assistant Alarmo integration panel with arm/disarm controls |
| Notifications | Toast alerts and a notification center with history |
| Alert Dialogs | Centered modal dialogs with confirmation buttons and MQTT response |
For complete parameter details on every widget, see the MQTT Widget Reference (MQTT_WIDGET_REFERENCE.md).
Controlling Your Panel¶
You have three ways to send commands to your KingKiosk Pico:
1. King Admin (Recommended)¶
If you have King Admin installed, it automatically discovers your Pico device on the network. You can:
- Create and arrange widgets with a visual editor
- Monitor device status in real-time
- Send commands with a tap instead of typing JSON
- Manage multiple Pico panels from one place
2. Home Assistant¶
KingKiosk Pico works directly with any MQTT broker that Home Assistant uses. You can control it from automations, scripts, dashboards, or the MQTT service call:
# Show a temperature gauge when the sensor updates
automation:
- alias: "Kitchen Temp on Panel"
trigger:
- platform: state
entity_id: sensor.kitchen_temperature
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload_template: >
{"command":"create_gauge","window_id":"temp",
"title":"Kitchen","unit":"°F",
"min":50,"max":90,"value":{{ states('sensor.kitchen_temperature') }}}
# Show doorbell camera when someone rings
automation:
- alias: "Doorbell Alert on Panel"
trigger:
- platform: state
entity_id: binary_sensor.doorbell
to: "on"
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: >
{"command":"alert","title":"Doorbell","message":"Someone is at the door!",
"type":"warning","auto_dismiss_seconds":30}
3. Direct MQTT¶
Use any MQTT client (mosquitto_pub, MQTT Explorer, Node-RED, or your own code):
# Open a clock
mosquitto_pub -h your-broker -t 'kingkiosk/kingpico-f23438/system/cmd' \
-m '{"command":"open_clock","window_id":"clock-1","mode":"digital"}'
# Create a light toggle button
mosquitto_pub -h your-broker -t 'kingkiosk/kingpico-f23438/system/cmd' \
-m '{"command":"mqtt_button","action":"configure","window_id":"light",
"label":"Living Room","publish_topic":"home/light/cmd",
"publish_payload":{"action":"toggle"},"color_on":"green","color_off":"red"}'
# Send a notification
mosquitto_pub -h your-broker -t 'kingkiosk/kingpico-f23438/system/cmd' \
-m '{"command":"notify","title":"Laundry","message":"Dryer is done!","priority":"high"}'
# Show a confirmation alert
mosquitto_pub -h your-broker -t 'kingkiosk/kingpico-f23438/system/cmd' \
-m '{"command":"alert","title":"Arm Security?","message":"Arm the alarm system?",
"type":"warning","confirmation":true}'
# Close everything
mosquitto_pub -h your-broker -t 'kingkiosk/kingpico-f23438/system/cmd' \
-m '{"command":"close_all_windows"}'
The Tile System¶
Widgets display in tiles — rectangular areas on screen. KingKiosk Pico supports two layout modes:
Grid Mode (default) — Tiles auto-arrange in an intelligent grid. Open one widget and it fills the screen. Open two and they split 50/50. Open four and you get a 2x2 grid. Close a tile and the others expand to fill the gap.
Floating Mode — Position tiles anywhere with exact pixel coordinates. Specify x, y, width, and height in your command to place widgets precisely. Tiles can overlap, and you control the z-order with bring_to_front. Perfect for custom dashboard layouts.
Switch modes with:
mosquitto_pub ... -m '{"command":"set_window_mode","mode":"floating"}'
mosquitto_pub ... -m '{"command":"set_window_mode","mode":"grid"}'
In either mode, the layout is saved to flash memory and restored on reboot — your panel looks the same after a power outage.
Virtual Desktops (Carousel)¶
KingKiosk Pico supports virtual desktops — up to 4 full-screen pages, each with its own set of widgets. Swipe left/right on the touchscreen to switch desktops, or control navigation via MQTT. This lets you organize dashboards by room or function (e.g., Home, Lights, Climate, Security) on a single panel.
# Create a 3-desktop carousel with indicator dots
mosquitto_pub ... -m '{"command":"create_carousel","window_id":"my-carousel","desktop_count":3,"show_indicator":true}'
# Navigate between desktops
mosquitto_pub ... -m '{"command":"navigate_carousel","direction":"next"}'
mosquitto_pub ... -m '{"command":"navigate_carousel","target":0}'
# Auto-play (rotate desktops automatically)
mosquitto_pub ... -m '{"command":"set_carousel_config","auto_play":true,"interval":10000}'
Widgets are created on the active desktop. Switch to a desktop first, then create widgets there. Up to 16 tiles total across all desktops. Indicator dots at the bottom show which desktop is active. The entire carousel state (desktops, config, and which tiles belong to which desktop) persists across reboots.
Backgrounds¶
Customize the screen background with solid colors, gradients, or images. All background settings persist across reboots.
# Solid color
mosquitto_pub ... -m '{"command":"set_background","type":"color","color":"#1a1a2e"}'
# Linear gradient (vertical or horizontal)
mosquitto_pub ... -m '{"command":"set_background","type":"gradient","color1":"#1a1a2e","color2":"#e94560","direction":"vertical"}'
# Image from URL (best with Feature Server for automatic QOI transcoding)
mosquitto_pub ... -m '{"command":"set_background","type":"image","url":"http://example.com/wallpaper.jpg"}'
# Reset to default crown watermark
mosquitto_pub ... -m '{"command":"set_background","type":"default"}'
Image size note: Without the Feature Server, background images must be small enough to decode in ESP32 memory (recommended: 160x120 pixels or smaller). With the Feature Server enabled, any image URL works — Core 3 automatically resizes and transcodes to QOI format for optimal memory usage.
Halo Effect¶
Add a glowing edge effect around the screen or individual windows for visual flair or status indication:
# Screen-wide cyan pulse
mosquitto_pub ... -m '{"command":"halo_effect","color":"cyan","mode":"pulse"}'
# Per-window halo
mosquitto_pub ... -m '{"command":"halo_effect","window_id":"temp-1","color":"red","mode":"solid"}'
Screenshots¶
Capture what's on screen via MQTT. The device returns an HTTP URL where you can download the screenshot as a QOI image (a compact, fast-to-encode format):
# Request screenshot
mosquitto_pub ... -m '{"command":"screenshot"}'
# Response: {"status":"success","url":"http://192.168.0.147/screenshot.qoi?t=12345"}
# Download the QOI image
curl -o screenshot.qoi "http://192.168.0.147/screenshot.qoi?t=12345"
The screenshot captures full 480x320 resolution in about 0.5 seconds. QOI files are ~16KB for typical dashboards (18x smaller than raw BMP). You can convert QOI to PNG with ffmpeg (ffmpeg -i screenshot.qoi screenshot.png) or view directly in apps like qView.
Feature Server Integration¶
When running KingKiosk Core 3 on your network, you can enable the Feature Server to unlock image transcoding for the ESP32. This lets the Pico display images from any URL at any resolution — Core 3 fetches the image, resizes it to the panel's display resolution, and converts it to QOI (the most memory-efficient format for ESP32).
# Point the Pico at your Core 3 instance
mosquitto_pub ... -m '{"command":"provision","settings":{"featureServer":"192.168.0.199:4000"}}'
Once enabled, all image loads (background images, mqtt_image widget) are automatically proxied through Core 3. The device passes its actual display resolution and platform, so an ESP32-S3 with a larger screen gets proportionally larger images. If Core 3 is unreachable, the device falls back to direct download.
Security: Locking Your Panel¶
KingKiosk Pico has a two-layer lock system to prevent unauthorized changes:
Touch Lock — Prevents accidental screen interactions. When locked, the screen shows content but ignores taps (except on the lock button itself). The lock icon turns red and a semi-transparent shield covers the display. Perfect for wall-mounted panels where passersby might bump the screen.
You can lock/unlock from the touchscreen (tap the lock button) or remotely via MQTT:
To unlock from the touchscreen, you'll need to enter your PIN. Setting up a PIN is required for the lock/unlock workflow to function:
Once set, tapping the red lock icon on-screen brings up a PIN keypad. Enter your 4-digit PIN to unlock. Wrong PIN shows a red error message; correct PIN unlocks immediately with a confirmation beep.
Settings Lock — A separate lock that prevents unauthorized configuration changes. When settings are locked, the provision command requires the PIN before it will modify WiFi, MQTT, or device settings.
# Lock settings (requires PIN to be set first)
mosquitto_pub ... -m '{"command":"lock_settings"}'
# Unlock settings remotely
mosquitto_pub ... -m '{"command":"unlock_settings"}'
Changing the PIN:
# Change to a new PIN (works anytime, even when locked)
mosquitto_pub ... -m '{"command":"set_pin","pin":"5678"}'
Tip: Set a PIN immediately after first-time setup. Without a PIN, anyone who touches the screen can lock/unlock the panel and access settings.
OTA Updates¶
KingKiosk Pico supports over-the-air firmware updates. Point it at an HTTP URL with the new firmware and it downloads, verifies, and installs automatically — with rollback protection if something goes wrong.
ESP32 Tips and Best Practices¶
Display Design for Small Screens: - The screen is 480x320 pixels — keep layouts simple - 1-2 widgets at a time works best for readability - Use large fonts for across-the-room visibility - High contrast colors (white text on dark backgrounds) are easiest to read
Performance: - The ESP32 has limited RAM (~180KB usable) — avoid opening too many complex widgets simultaneously - 2-4 tiles per desktop is the sweet spot; 16 tiles is the maximum across all virtual desktops - With virtual desktops, only the active desktop's widgets consume rendering resources — hidden desktops are nearly free - Notifications and alerts are lightweight and won't impact performance - For images, QOI format is strongly preferred over PNG (50% less memory). Enable the Feature Server for automatic transcoding
Network: - WiFi reconnects automatically if the connection drops - MQTT reconnects automatically if the broker restarts - All state is saved locally — the panel works offline with its last-known layout
Ideal Use Cases: - Kitchen timer and recipe display - Bedside clock with weather and smart home controls - Hallway status board (weather, calendar, notifications) - Garage or workshop gauges (temperature, humidity, air quality) - Kids' room task timer or chore checklist - Meeting room availability display - Front door smart lock status with QR code for guest WiFi
Automation Ideas:
Morning dashboard — show weather and calendar at sunrise:
automation:
- alias: "Morning Dashboard"
trigger:
- platform: sun
event: sunrise
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"close_all_windows"}'
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"open_clock","window_id":"clock","mode":"digital"}'
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"open_weather_client","window_id":"weather"}'
Bedtime mode — dim and show clock only:
automation:
- alias: "Bedtime Panel"
trigger:
- platform: time
at: "22:00:00"
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"close_all_windows"}'
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"set_brightness","value":20}'
- service: mqtt.publish
data:
topic: "kingkiosk/kingpico-f23438/system/cmd"
payload: '{"command":"open_clock","window_id":"night-clock","mode":"digital"}'
13. Feature Server¶
The Feature Server is the powerhouse behind King Kiosk's most advanced capabilities. It provides features that go beyond what a single tablet can do on its own — things like whole-house intercom, running a browser on AppleTV, streaming your tablet's camera to Go2RTC, Frigate, Home Assistant, and bridging your entire Home Assistant enabled smart home to MQTT.
Think of it as the brain that connects all your King Kiosk devices together and ties them into your broader smart home ecosystem.
Note: Advanced Feature Server capabilities (intercom, remote browser, RTSP export, AI/ML) are available on the Flutter-based KingKiosk clients (Android, iOS, macOS, Windows, Linux, tvOS). KingKiosk Pico (ESP32) uses the Feature Server for image transcoding — when configured, all image loads are automatically proxied through Core 3 which resizes and converts them to QOI format optimized for the ESP32's memory constraints. See the Pico section above for setup details.
What is the Feature Server?¶
The Feature Server is a set of services built into the KingKiosk Core server. You don't install it separately — it's already there. Each feature can be enabled or disabled independently based on what you need. The four main features are:
| Feature | What It Does |
|---|---|
| Intercom & Broadcast | Send live video and audio from one kiosk to all others |
| Remote Browser | Run a full Chromium browser on your AppleTV |
| RTSP Camera Export | Turn your tablet into a security camera visible in Home Assistant, Frigate, etc. |
| Home Assistant MQTT Bridge | Control all your Home Assistant devices through MQTT |
| High-Quality TTS (Piper) | Natural-sounding text-to-speech with 100+ voices in 30+ languages |
Important: When you enable the Feature Server, local on-device object/person detection and facial recognition are disabled. The camera stream is sent to the Feature Server instead of being processed locally. If you need local AI detection, keep the Feature Server disabled.
Automatic Feature Server Discovery¶
King Kiosk can automatically find and connect to your Feature Server without you having to manually enter its address. This makes setup easier, especially if you have multiple devices or if your server's IP address changes.
How It Works:
When your KingKiosk Core server starts up, it publishes its location on the MQTT topic kingkiosk/core3/api. This message contains the server's URL and version information. Every King Kiosk client device listens to this topic and automatically updates its connection settings when it sees this announcement.
Think of it like your server saying "Hey everyone, I'm here at this address!" and all your devices responding "Got it, connecting now!"
Setting Up Autodiscovery:
- Make sure MQTT is working - Both your Feature Server and your King Kiosk devices must be connected to the same MQTT broker
- Start your Feature Server - When it starts, it automatically announces itself
- Your devices connect automatically - No manual configuration needed
Manual Override:
Sometimes you might want to manually set the Feature Server address (for testing, using a different server, etc.). In the King Kiosk settings under Feature Server, you can:
- Enter a specific server address (like
http://192.168.1.100:3000) - Enable Manual Override Lock - This prevents autodiscovery from changing your manual setting
- When locked, the device will ignore autodiscovery announcements and only use your manually configured address
To re-enable autodiscovery, simply disable the manual override lock. The device will then listen for server announcements again.
Troubleshooting:
- Device not connecting? Check that both device and server are connected to the same MQTT broker
- Server address keeps changing? Enable manual override lock to force a specific address
- Want to use a different server temporarily? Enter the address manually and lock it, then unlock when done
High-Quality Text-to-Speech (Piper)¶
When the Feature Server is connected, TTS commands automatically use the Piper speech synthesis engine instead of the device's built-in TTS. Piper produces natural-sounding speech with a large library of voices across many languages.
How It Works¶
You don't need to change any of your existing TTS automations. The same MQTT commands work in both modes:
- Feature Server connected — Speech is synthesized on the server using Piper, then played back as audio on the device. Higher quality, more voices, consistent across all devices.
- Feature Server disconnected — Falls back to the device's built-in TTS engine (FlutterTts or AVSpeechSynthesizer). Works offline.
Choosing a Voice¶
First, discover what voices are available:
{
"command": "tts",
"action": "getVoices",
"language": "en_US",
"response_topic": "kingkiosk/my_device/system/response"
}
The response includes voice details like voiceId, quality, and whether the voice is already installed. Then use a voice in your speak commands:
Installing Voices¶
If a voice isn't installed yet, you can pull it:
If auto_download is enabled in the server config, voices are downloaded automatically on first use.
Home Assistant Automation Example¶
automation:
- alias: "Announce Front Door"
trigger:
- platform: state
entity_id: binary_sensor.front_door
to: 'on'
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen_tablet/system/cmd"
payload: >
{
"command": "tts",
"text": "The front door just opened.",
"voice": "en_US-lessac-medium",
"volume": 0.8
}
Intercom & Broadcast¶
Intercom lets you broadcast live audio and video from one King Kiosk device to all other enabled devices. Walk up to any kiosk, press a button, and instantly appear on every other screen — like a whole-house intercom system using your existing tablets.
Why Use It?¶
Instead of yelling across the house, making phone calls for a quick message, or relying on one-way smart speaker announcements, King Kiosk Intercom gives you:
- One-button broadcasting — Start instantly from any kiosk
- Video and audio — They see and hear you
- Every room at once — All enabled kiosks receive the broadcast
- No extra app needed — It's built into your wall-mounted tablets
How It Works¶
┌─────────────────┐
┌───>│ Living Room │ speakers + screen
│ └─────────────────┘
┌─────────────────┐ │ ┌─────────────────┐
│ Kitchen Kiosk │ ─────┼───>│ Bedroom │ speakers + screen
│ "Hey everyone!" │ │ └─────────────────┘
└─────────────────┘ │ ┌─────────────────┐
SENDER └───>│ Office │ speakers + screen
└─────────────────┘
RECEIVERS
- You start a broadcast from one kiosk
- All other enabled kiosks either auto-open the broadcast or publish an incoming intercom MQTT event, depending on their auto-accept setting
- You see yourself on your own screen as a preview (but don't hear yourself — no echo)
- You stop the broadcast when you're done
Getting Started¶
Enable intercom on each device — Before a kiosk can participate, you need to enable intercom in the Feature Server settings:
- Open Settings on your King Kiosk device
- Go to the Feature Server section
- Make sure the Feature Server is connected (enter your server hostname/IP if needed)
- Toggle Enable intercom on
- In Media Settings, choose whether Auto accept incoming intercom should be on or off
Do this for each kiosk you want to be part of the intercom system. The device will automatically join the default intercom group.
If Auto accept incoming intercom is off, the kiosk does not show a built-in confirmation dialog. Instead it publishes kingkiosk/{device_id}/communications/event messages such as incoming_intercom so you can trigger your own sound, popup, or automation in Home Assistant, Node-RED, or another controller.
Adding an Intercom Button to Your Kiosk¶
The easiest way to use intercom is to add a dedicated toggle button directly on your kiosk screen. This creates a tap-to-talk button that turns green when broadcasting and gray when idle:
{
"command": "mqtt_button",
"action": "configure",
"window_id": "intercom_btn",
"mode": "icon_button",
"publish_topic": "kingkiosk/{deviceId}/intercom/toggle",
"subscription_topic": "kingkiosk/{deviceId}/intercom/status",
"status_path": "broadcasting",
"icon_on": "mic",
"icon_off": "mic_off",
"color_on": "0xFF4CAF50",
"color_off": "0xFF757575",
"label": "Intercom"
}
Replace {deviceId} with your device's ID. Tap the button to start broadcasting, tap again to stop. The icon and color update automatically based on the intercom status.
When you're broadcasting, a small self-view of your camera is overlayed into the mic icon so you can see what others are seeing. Audio is muted on the self-view to prevent echo — you only see yourself, you don't hear yourself.
Home Assistant Automation Example¶
Broadcast "dinner's ready" when a button is pressed:
automation:
- alias: "Dinner Announcement"
trigger:
- platform: state
entity_id: input_button.dinner_ready
action:
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen-kiosk/intercom/start"
payload: '{"group": "default"}'
- delay: "00:00:10"
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen-kiosk/intercom/stop"
payload: '{}'
Intercom via MQTT Commands¶
You can also start and stop intercom broadcasts entirely through MQTT, without needing an on-screen button:
Start broadcasting:
Stop broadcasting:
Toggle (start if stopped, stop if started):
Check broadcast status:
Subscribe to kingkiosk/{device_id}/intercom/status to get the current state:
Common Use Cases¶
- Whole-house announcement — "Dinner's ready!" from the kitchen to everywhere
- Doorbell response — Broadcast from the front door kiosk so all rooms see who's there
- Baby monitor check-in — Broadcast from the nursery kiosk to the parent's bedroom or living room
- Office paging — Announce visitors or meetings across all office kiosks
- Classroom broadcast — Teacher's kiosk broadcasts to all student kiosks
- Security alert — Automatically start a broadcast from the front door camera when motion is detected
More Home Assistant Examples¶
Broadcast from the front door camera when someone rings the doorbell:
automation:
- alias: "Doorbell Broadcast"
trigger:
- platform: state
entity_id: binary_sensor.doorbell
to: "on"
action:
- service: mqtt.publish
data:
topic: "kingkiosk/front-door-kiosk/intercom/start"
payload: '{"group": "default"}'
# Auto-stop after 30 seconds
- delay: "00:00:30"
- service: mqtt.publish
data:
topic: "kingkiosk/front-door-kiosk/intercom/stop"
payload: '{}'
Play a TTS announcement followed by intercom on all kiosks:
automation:
- alias: "Visitor at Door with Announcement"
trigger:
- platform: state
entity_id: binary_sensor.front_door
to: "on"
action:
# Announce on all devices
- service: mqtt.publish
data:
topic: "kingkiosk/all-devices/system/cmd"
payload: '{"command": "tts", "text": "Someone is at the front door"}'
- delay: "00:00:03"
# Then start video broadcast from the door kiosk
- service: mqtt.publish
data:
topic: "kingkiosk/front-door-kiosk/intercom/start"
payload: '{"group": "default"}'
Intercom vs. Video Conferencing — When to Use Which¶
| Intercom | Video Conferencing | |
|---|---|---|
| Direction | One-to-many (broadcast) | Two-way or multi-way conversation |
| Best for | Announcements, paging, quick messages | Meetings, phone calls, doorbell conversations |
| Participants | One sender, all receivers | Any number of participants talking to each other |
| Setup | One button press or MQTT command | Create a conference window, then dial participants |
| SIP phone calls | No | Yes — dial phone numbers and extensions |
| On-screen controls | Start/Stop only | Mute, volume, add participants, DTMF, hang up |
Rule of thumb: If you need to tell everyone something quickly, use intercom. If you need to have a conversation with specific people or devices, use video conferencing.
Privacy¶
- Any device can opt out by disabling intercom at any time
- Only devices with intercom enabled can send or receive
- Broadcasts are live only — nothing is recorded or stored
Troubleshooting¶
- Broadcast not reaching other devices? Make sure receivers have intercom enabled and are in the same group.
- Hearing yourself (echo)? The sender should only see video, not hear audio. Check that the client properly handles the
isSenderflag. - Video/audio won't auto-play? KingKiosk handles this automatically, but web browsers may require user interaction first.
Video Conferencing & SIP Calling¶
Video conferencing takes your kiosks beyond simple broadcasts. While intercom is "one person speaking to everyone," conferencing is a real two-way (or multi-way) video call between specific devices or phone numbers.
What Can You Do?¶
- Call another kiosk — Tap a room in the on-screen directory to start a two-way video call with the kitchen display, office tablet, or any other connected device
- Make a phone call — Dial a SIP phone number or extension directly from your kiosk using the on-screen dialpad, just like a video phone
- Multi-party conferences — Connect three, four, or more endpoints into a single conference. Mix and match kiosk-to-kiosk calls with SIP phone calls in the same session
- Control everything remotely — MQTT commands let Home Assistant, Node-RED, or any automation platform create calls, mute microphones, adjust volume, hang up, and more
How It Works¶
┌─────────────────┐ ┌─────────────────┐
│ Kitchen Kiosk │◄───────►│ Office Kiosk │
│ (video + audio) │ │ │ (video + audio) │
└─────────────────┘ │ └─────────────────┘
│
┌────────┴────────┐
│ Feature Server │
│ (Core 3) │
└────────┬────────┘
│
┌────────┴────────┐
│ SIP Gateway │
│ (optional) │
└────────┬────────┘
│
┌────────┴────────┐
│ Reception Desk │
│ (SIP phone) │
└─────────────────┘
- A video call window opens on your kiosk showing available devices to call
- You pick a device from the room directory, or dial a phone number with the SIP dialpad
- The Feature Server sets up the WebRTC connection between both sides
- Video and audio flow in both directions — a real conversation
- You can add more participants to turn it into a multi-party call
Getting Started¶
Prerequisites:
- The Feature Server (KingKiosk Core 3) must be running and connected
- Audio production must be enabled in Feature Server settings (so your kiosk sends its microphone)
- For video calls, the device needs a camera
- For SIP calls, the Feature Server must have a SIP gateway configured
Creating a Video Call Window:
The simplest way is via MQTT:
This opens a conference window on your kiosk. It starts in idle mode showing a room directory — a list of other kiosks currently connected to the Feature Server.
For audio-only calls (no camera needed):
Calling Another Kiosk¶
Once the conference window is open, you have two options:
From the on-screen UI: The room directory shows all connected devices with their online status. Select a room to call it. On Apple TV, use the Siri Remote to navigate the directory and press Select to dial.
From MQTT (for automations):
The call connects automatically. Both sides see and hear each other. A status bar at the top shows participant count and call duration. Controls at the bottom let you mute, adjust volume, add more participants, or hang up.
Making a SIP Phone Call¶
SIP calling turns your kiosk into a video phone. You can dial extensions on your office PBX, call a reception desk, or reach any SIP-compatible device.
From the on-screen UI: Tap the "SIP Dial" button in the room directory to open the dialpad. Enter a phone number or SIP address using the on-screen number pad, then tap Call. On Apple TV, navigate the dialpad with the Siri Remote d-pad, press Select to enter digits, then navigate to the Call button.
From MQTT (for automations):
{
"command": "add_sip_endpoint",
"payload": {
"sip_uri": "reception@pbx.local",
"video": true,
"from_display": "Kitchen Kiosk"
}
}
The sip_uri accepts multiple formats: sip:user@host, user@host, a bare IP address like 192.168.1.50, or with a port like 192.168.1.50:5060. The video and from_display fields are optional.
DTMF tones: During a SIP call, you can send touch-tone digits (for phone menus, entering PINs, etc.):
On the device itself, the keypad button in the control bar opens an on-screen DTMF pad.
Multi-Party Conferences¶
Add as many participants as you need. Each new endpoint joins the existing conference:
You can mix kiosk-to-kiosk calls with SIP calls in the same conference. The screen automatically tiles to show all participants. Each tile shows the participant's name, connection status, and call duration.
In-Call Controls¶
All of these can be done from the on-screen controls or via MQTT:
| What You Want to Do | On-Screen | MQTT Command |
|---|---|---|
| Mute your microphone | Tap the mic button | mute_mic or toggle_mic |
| Unmute | Tap the mic button again | unmute_mic or toggle_mic |
| Adjust master volume | Tap the volume button, use the slider | set_master_volume with {"volume": 0.7} |
| Mute one participant | Tap the speaker icon on their tile | mute_endpoint with {"endpoint_id": "..."} |
| Hang up one participant | Tap the red phone icon on their tile | hangup_endpoint with {"endpoint_id": "..."} |
| End all calls | Tap the red "End All" button | hangup_all |
| Add another participant | Tap the "Add" button | add_room_endpoint or add_sip_endpoint |
On Apple TV, press Select on the conference window to enter interactive mode. Use the d-pad to navigate between controls, and press Select to activate them.
Receiving Incoming Calls¶
When another kiosk or SIP caller dials your device, you see an incoming call screen with the caller's name and Accept/Decline buttons.
Auto-accept mode: If "Auto accept videoconference calls" is enabled in settings, calls connect automatically without prompting. Great for lobby displays or always-on conference rooms.
Manual accept mode: The kiosk shows the incoming call dialog. On Apple TV, use left/right to choose Decline or Accept, then press Select.
MQTT-driven acceptance: When auto-accept is off, the kiosk publishes an MQTT event instead of showing a dialog, letting you handle the call in your automation:
Subscribe to kingkiosk/{device_id}/communications/event to receive:
{
"event": "incoming_conference_call",
"call_type": "videoconference",
"call_id": "conf-abc123",
"caller": "Kitchen Display",
"auto_accept_enabled": false
}
Then accept or reject via the element command topic.
Home Assistant Automation Examples¶
Auto-open a conference when the doorbell rings:
automation:
- alias: "Doorbell Video Call"
trigger:
- platform: state
entity_id: binary_sensor.doorbell
to: "on"
action:
# Create conference window
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen-display/system/cmd"
payload: >
{"command": "video_call", "window_id": "door-call", "name": "Front Door"}
# Wait for window to initialize
- delay: "00:00:02"
# Dial the front door camera
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen-display/element/door-call/cmd"
payload: >
{"command": "add_room_endpoint", "payload": {"room_id": "kingkiosk-front-door"}}
# Unmute mic so you can talk to the visitor
- service: mqtt.publish
data:
topic: "kingkiosk/kitchen-display/element/door-call/cmd"
payload: '{"command": "unmute_mic"}'
Call the front desk when a button is pressed:
automation:
- alias: "Call Reception"
trigger:
- platform: state
entity_id: input_button.call_reception
action:
- service: mqtt.publish
data:
topic: "kingkiosk/lobby-kiosk/system/cmd"
payload: >
{"command": "video_call", "window_id": "reception-call", "name": "Reception"}
- delay: "00:00:02"
- service: mqtt.publish
data:
topic: "kingkiosk/lobby-kiosk/element/reception-call/cmd"
payload: >
{"command": "add_sip_endpoint", "payload": {"sip_uri": "100@pbx.local", "video": true}}
Auto-hangup after 5 minutes:
automation:
- alias: "Conference Timeout"
trigger:
- platform: state
entity_id: input_button.call_reception
action:
# ... (call setup as above) ...
- delay: "00:05:00"
- service: mqtt.publish
data:
topic: "kingkiosk/lobby-kiosk/element/reception-call/cmd"
payload: '{"command": "hangup_all"}'
- service: mqtt.publish
data:
topic: "kingkiosk/lobby-kiosk/system/cmd"
payload: >
{"command": "video_call", "action": "close", "window_id": "reception-call"}
Monitoring Call State¶
Subscribe to the conference state to build dashboards or trigger automations based on call activity:
The state includes whether a conference is active, microphone mute status, master volume, call duration, number of endpoints, and details about each endpoint (name, type, connection state, duration, mute state).
Troubleshooting¶
- No audio from the other side? Check that the remote device has audio production enabled in Feature Server settings, and that your master volume is above zero.
- One-way video? Both devices need cameras enabled. Check Feature Server connection on both sides.
- SIP call stuck on "Calling"? Verify the SIP gateway is running on Core 3 and the destination is reachable. Check the SIP URI format.
- Can't see other devices in the room directory? Both devices must be connected to the same Feature Server. Check Feature Server status in settings.
- DTMF not working? DTMF is only sent to SIP endpoints, not to kiosk-to-kiosk calls. Make sure the SIP call is connected before sending tones.
- Incoming call not showing? Check that "Auto accept videoconference calls" is configured as expected. If it's off, the call notification goes to MQTT only.
Remote Browser¶
Remote Browser lets you display and control a full web browser running on your server, streamed directly to your King Kiosk tablet. The heavy lifting happens on your server — your tablet just shows the video and sends back your touches.
Why Use It?¶
Tablets and kiosks have limitations — weak hardware, limited memory, battery drain from complex sites, and logins that get lost on restart. Remote Browser solves all of this:
- Server does the work — A full Chromium browser runs on your powerful server
- Tablet just displays — It only needs to show a video stream
- Stay logged in — Sessions persist across restarts when you use a persistence ID
- Touch or keyboard — Control works however you prefer
How It Works¶
┌─────────────────┐ video stream ┌─────────────────┐
│ KingKiosk App │ <──────────────────── │ KingKiosk Server│
│ (your tablet) │ │ (runs Chromium) │
│ │ ──────────────────── > │ │
└─────────────────┘ touch/mouse/keys └─────────────────┘
- KingKiosk Server runs a headless Chromium browser
- Video of the browser is captured and streamed to your tablet
- Your touches are sent back to the server and applied to the browser
- Everything happens in real-time with low latency
Creating a Remote Browser¶
Send this MQTT command to your device's system topic to open a remote browser window:
{
"command": "create_remote_browser",
"window_id": "browser_1",
"initial_url": "https://www.google.com",
"auto_connect": true
}
| Parameter | Required | Description |
|---|---|---|
window_id |
yes | Unique ID for this browser tile |
initial_url |
yes | URL to load when the session starts |
auto_connect |
yes | Always set to true to connect immediately |
name |
no | Title shown on the tile |
video_profile |
no | Quality: auto, 720p30, 1080p30, 1080p60 |
show_overlay |
no | Show URL bar and stats overlay |
The server automatically picks the best video encoder for your hardware (NVIDIA GPU, Intel/AMD GPU, Raspberry Pi, or software fallback).
Navigating to a URL¶
Once a remote browser session is running, you can navigate it:
Controlling the Browser¶
- Tap = Click
- Swipe = Scroll
- Long press = Right-click
- Physical or on-screen keyboard = Type text
Common Use Cases¶
- Dashboard kiosk — Display a Home Assistant dashboard at full quality
- Spotify player — Stream music with album art on your wall display
- YouTube TV — Turn a tablet into a smart TV display
- Security camera viewer — View Frigate or other camera feeds in a browser
Troubleshooting¶
- Session won't start? Check server logs, verify Chromium is installed, and make sure the Feature Server is connected.
- Video is laggy? Try a lower quality profile, check WiFi, or reduce concurrent sessions.
- Black screen? Wait a moment for initial load, or try a simpler URL first.
RTSP Camera Export¶
RTSP Export lets you stream live video and audio from your King Kiosk tablet's camera to your smart home system. Once enabled, your tablet appears as a camera in Home Assistant, Frigate, or any app that supports RTSP streams.
In simple terms: Turn your wall-mounted tablet into a security camera that shows up in Home Assistant just like your other cameras.
Why Use It?¶
By default, King Kiosk only sends periodic snapshots (every few seconds) for AI detection. That works great for person detection and presence sensing, but it's not ideal when you want to:
- Watch a live video feed of what the tablet sees
- Record continuous video to Frigate
- Use the tablet as a video doorbell or baby monitor
- See real-time motion in Home Assistant
RTSP Export sends a continuous live video stream through go2rtc, making your tablet work exactly like any other security camera.
How It Works¶
┌─────────────────┐ live video ┌─────────────┐ standard RTSP ┌─────────────────┐
│ KingKiosk App │ ────────────────> │ go2rtc │ ──────────────────> │ Home Assistant │
│ (your tablet) │ │ (bridge) │ │ / Frigate │
└─────────────────┘ └─────────────┘ └─────────────────┘
Setup¶
Step 1: Make sure go2rtc is running. Most Home Assistant users already have it (it's built into Frigate). Check by opening http://your-home-assistant:1984.
Step 2: Configure KingKiosk Server. For Home Assistant add-on users, go to Settings > Add-ons > KingKiosk Core > Configuration and set:
- go2rtc_url: rtsp://your-go2rtc-ip:8554
- go2rtc_api_url: http://your-go2rtc-ip:1984
For standalone users, add to config.toml:
[gstreamer]
go2rtc_url = "rtsp://192.168.1.100:8554"
go2rtc_api_url = "http://192.168.1.100:1984"
go2rtc_manage_streams = true
Step 3: Enable RTSP Export in KingKiosk's Feature Server Settings and turn the toggle on.
Step 4: Your device automatically appears in go2rtc using its device name (e.g., kitchen-tablet). View it at rtsp://your-go2rtc:8554/kitchen-tablet.
Adding to Home Assistant¶
Generic Camera: Settings > Devices & Services > Add Integration > Generic Camera. Enter rtsp://your-go2rtc:8554/kitchen-tablet.
Frigate:
cameras:
kitchen_tablet:
ffmpeg:
inputs:
- path: rtsp://your-go2rtc:8554/kitchen-tablet
roles:
- detect
- record
WebRTC Camera Card (lowest latency):
Snapshots vs. RTSP Export¶
| Feature | Snapshots (Default) | RTSP Export |
|---|---|---|
| Use case | AI detection, periodic checks | Live viewing, recording |
| Updates | Every few seconds | Continuous (30fps) |
| Bandwidth | Very low | Higher (like a security camera) |
| CPU usage | Low | Moderate |
| Best for | Person detection, presence sensing | Baby monitor, security camera |
You can use both at the same time — snapshots for AI detection and RTSP Export for live viewing.
Troubleshooting¶
- Stream not showing in go2rtc? Check server logs, verify go2rtc is reachable, confirm your config URLs.
- Stream won't play? Wait a few seconds for the first keyframe, or try VLC with TCP mode.
- No audio? KingKiosk uses Opus format. Add
default_query: video=h264&audio=aacto your go2rtc config to convert to AAC. - Choppy video? Use a wired connection if possible, or check WiFi signal strength.
Home Assistant MQTT Bridge¶
The Home Assistant MQTT Bridge is a two-way communication system that connects your Home Assistant smart home to MQTT. It makes all your Home Assistant devices (lights, switches, sensors, etc.) available through MQTT, and lets you control them by sending MQTT messages.
In simple terms: It's a translator that lets King Kiosk (and any other MQTT-connected system) see and control everything in your Home Assistant setup.
Why Use It?¶
Home Assistant is great at managing smart home devices, but sometimes other systems need to monitor or control those devices too. By bridging Home Assistant to MQTT, you can:
- Connect external systems that don't work with Home Assistant directly
- Build custom automations using tools that understand MQTT
- Monitor your entire home from any MQTT-capable application
- Control devices from King Kiosk or other systems without going through the HA API
How It Works¶
The bridge operates in both directions:
- Home Assistant to MQTT: When something changes in HA (a light turns on, a temperature changes, motion is detected), the bridge immediately publishes that update to MQTT.
- MQTT to Home Assistant: When you send a command through MQTT (turn on a light, set a thermostat), the bridge forwards it to Home Assistant.
What Gets Shared¶
| Category | What It Contains |
|---|---|
| Entity States | Current status of all devices — lights (on/off, brightness, color), sensors (temperature, humidity), switches, covers, climate |
| Entity Registry | Device names, room assignments, manufacturer info, unique IDs |
| Device Registry | Physical device info — firmware versions, connection status, serial numbers |
| Area Registry | Rooms and locations in your home |
| Services Registry | All available actions and their parameters |
Setup¶
Step 1: Get a Home Assistant Long-Lived Access Token. Go to your HA profile > Long-Lived Access Tokens > Create Token.
Step 2: Configure the bridge in your config.toml:
[home_assistant_bridge]
enabled = true
ha_url = "http://homeassistant.local:8123"
ha_token = "YOUR_LONG_LIVED_TOKEN_HERE"
mqtt_prefix = "kingkiosk/ha"
publish_registry = true
publish_services = true
publish_attributes = true
retain_state = true
Step 3 (optional): Filter what gets shared if you have many devices:
Reading Device States¶
Subscribe to MQTT topics following this pattern:
Example — living room light:
Returns:{
"entity_id": "light.living_room",
"state": "on",
"attributes": {
"brightness": 200,
"friendly_name": "Living Room Light"
}
}
Controlling Devices¶
Send commands to:
Turn on a light:
Set thermostat temperature:
Turn off a switch:
Command results are published to kingkiosk/ha/command/{domain}/{object_id}/result.
Quick Reference¶
| I Want To... | MQTT Topic |
|---|---|
| See all devices | kingkiosk/ha/registry/entities |
| Monitor a light | kingkiosk/ha/state/light/{name} |
| Turn on a light | kingkiosk/ha/command/light/{name} with {"service": "turn_on"} |
| See all available commands | kingkiosk/ha/registry/services |
| Check if bridge is working | kingkiosk/ha/status |
Troubleshooting¶
- Bridge shows "offline"? Check that Home Assistant is running and accessible, verify your token, confirm the MQTT broker is connected.
- Can see states but can't control devices? Check your exclude filters, look at the command result topic for errors, verify the entity ID matches exactly.
- Too much MQTT traffic? Use filters to limit domains, disable registry/attribute publishing if you only need states.
14. Detection, Recognition & Motion¶
King Kiosk turns any screen with a camera into an intelligent sensing device. It can detect people, recognize faces, identify objects, and sense motion — then publish all of that to MQTT so your smart home automations can react in real-time. Walk into a room and have the lights turn on, see who's at the front door without opening an app, or trigger a dashboard change when nobody's watching.
Detection Overview¶
King Kiosk provides four detection capabilities:
| Capability | What It Does | Example Use Case |
|---|---|---|
| Person Detection | Detects human figures in the camera frame | Turn on lights when someone enters a room |
| Object Detection | Identifies and classifies objects (person, dog, car, etc.) | Count people in a space, detect pets |
| Facial Recognition | Identifies who a person is from trained faces | Personalized greetings, access control |
| Motion Detection | Detects pixel-level changes between frames | Security monitoring, activity alerts |
All four publish their results to MQTT topics that Home Assistant auto-discovers as sensors, so you can build automations without any manual configuration.
On-Device vs Feature Server Detection¶
King Kiosk supports two detection engines. They produce identical MQTT output — your automations work the same regardless of which engine is running.
On-Device Detection runs TensorFlow Lite models directly on the device. It works on Android, iOS, macOS, Windows, Linux, and Raspberry Pi without any server. Processing runs in a background thread so the UI stays smooth. This is the default when no Feature Server is connected.
Feature Server Detection offloads processing to KingKiosk Core3, which can use GPU acceleration for faster, more accurate results. When a Feature Server is connected, on-device ML is automatically disabled and the server takes over. This is the only option for tvOS and Web, which lack on-device ML support.
| On-Device | Feature Server | |
|---|---|---|
| Requires server | No | Yes (KingKiosk Core3) |
| Platforms | Android, iOS, macOS, Windows, Linux, RPi | All platforms including tvOS, Web |
| Processing | Background isolate on device CPU | Server-side (GPU if available) |
| Accuracy | Good (MobileNet SSD, BlazeFace) | Better (server models) |
| Latency | ~2 seconds per frame | ~2 seconds (network dependent) |
| Switchover | Automatic — no config needed | Automatic when Feature Server connects |
You don't need to choose between them. When the Feature Server is available, detection routes through it automatically. When it's not, on-device processing kicks in. Your MQTT topics and Home Assistant sensors stay the same either way.
Person & Object Detection¶
Person detection identifies human figures in the camera frame. Object detection goes further, classifying what each detected object is (person, dog, cat, car, etc.) and reporting bounding box coordinates.
Enabling Person Detection¶
From Settings: Go to Settings > AI and toggle Enable Person Detection.
Via MQTT: Send to kingkiosk/{device_id}/system/cmd:
The action field accepts: enable, disable, toggle, or status.
Add "confirm": true to receive a confirmation on the response topic.
What Gets Published¶
When person detection is active, two MQTT topics are continuously updated:
Person Presence (kingkiosk/{device_id}/person_presence):
{
"person_present": true,
"confidence": 0.92,
"frames_processed": 1547,
"timestamp": "2026-03-12T14:30:00.000Z"
}
Object Detection (kingkiosk/{device_id}/object_detection):
{
"total_objects": 2,
"objects": [
{
"class": "person",
"confidence": 0.92,
"bbox": {"x": 0.25, "y": 0.1, "width": 0.3, "height": 0.8},
"person": true
},
{
"class": "dog",
"confidence": 0.78,
"bbox": {"x": 0.6, "y": 0.5, "width": 0.2, "height": 0.3},
"person": false
}
],
"timestamp": "2026-03-12T14:30:00.000Z"
}
Bounding box coordinates are normalized to 0-1 (where 0,0 is top-left and 1,1 is bottom-right), so they work regardless of camera resolution.
Tuning Detection¶
The detection pipeline has three confidence thresholds you can adjust:
- Detection threshold (default 0.3) — minimum confidence for an object to be detected at all
- Base confidence threshold (default 0.5) — general processing threshold
- MQTT publishing threshold (default 0.7) — only detections above this confidence are published to MQTT, reducing noise for automations
The processing interval defaults to 2 seconds between frames. On less powerful devices, it adapts automatically (2-10 seconds) to avoid impacting UI performance.
Facial Recognition¶
Facial recognition builds on person detection. When a person is detected, King Kiosk extracts their face and compares it against a database of known faces. It reports who is present, not just that someone is present.
Facial recognition requires person detection to be enabled — it won't work without it.
Enabling Facial Recognition¶
Via MQTT provisioning:
{
"command": "provision",
"settings": {
"personDetectionEnabled": true,
"facialRecognitionEnabled": true
}
}
What Gets Published¶
Face Presence (kingkiosk/{device_id}/face_presence):
{
"faces_present": true,
"faces_count": 2,
"known_faces": 1,
"unknown_faces": 1,
"timestamp": "2026-03-12T14:30:00.000Z",
"faces_detail": [
{
"bbox": {"x1": 0.3, "y1": 0.1, "x2": 0.5, "y2": 0.4},
"detection_confidence": 0.95,
"recognized": true,
"name": "Raj",
"similarity": 0.82,
"threshold": 0.70
},
{
"bbox": {"x1": 0.6, "y1": 0.15, "x2": 0.75, "y2": 0.45},
"detection_confidence": 0.88,
"recognized": false,
"name": "unknown",
"similarity": 0.0,
"threshold": 0.70
}
]
}
The faces_detail array gives you per-face information including bounding boxes, whether the face was recognized, the matched name, and the similarity score. A face is considered recognized when its similarity exceeds the verification threshold (default 0.70).
Recognition Thresholds¶
Facial recognition uses a multi-layered verification system to minimize false positives:
| Threshold | Default | Purpose |
|---|---|---|
| Verification | 0.70 | Similarity must exceed this for a positive match |
| Rejection | 0.55 | Below this, definitely not a match |
| Identity separation margin | 0.04 | Gap required between best match and runner-up |
| Temporal verification frames | 2 | Consecutive frames that must agree on identity |
The system also applies per-person cooldowns (default 60 seconds) to avoid flooding your automations with repeated notifications for the same person standing in view.
Face Training (Enrollment)¶
Before King Kiosk can recognize someone, you need to train it with their face. Training captures multiple samples from different angles to build a reliable face profile.
Starting Training via MQTT¶
Send to kingkiosk/{device_id}/system/cmd:
| Key | Type | Default | Notes |
|---|---|---|---|
command |
string | required | start_face_training or enroll_face (both work) |
name |
string | required | Person's name (also accepts person_name, label, or person) |
target_samples |
int | 20 | Number of face samples to capture (also accepts samples) |
The person should slowly turn their head during training to capture different angles. The system provides quality feedback including face position, lighting, and sharpness checks.
Training automatically completes after collecting the target number of samples. You'll receive a response on the system response topic:
Cancelling Training¶
Passive Enrichment¶
After initial training, King Kiosk automatically improves its model over time. When it recognizes someone with high confidence (above 0.72) and the face looks sufficiently different from existing samples, it silently adds the new embedding. This means recognition accuracy improves naturally as the system sees someone in different lighting conditions, angles, and over time.
Feature Server Face Training¶
When a Feature Server is connected, training commands are transparently routed to the server. The MQTT interface stays identical — your automations and scripts don't need to change. The server stores face data in its own database and can share trained faces across multiple King Kiosk devices.
Motion Detection¶
Motion detection uses frame-differencing to detect pixel-level changes between camera frames. It's lightweight, works independently of person detection, and is useful for security monitoring and activity-based triggers.
Enabling Motion Detection¶
Via MQTT provisioning:
What Gets Published¶
Motion Result (kingkiosk/{device_id}/motion/result):
{
"event": "motionDetection",
"score": 0.45,
"regions": [
{
"x": 0.25,
"y": 0.33,
"width": 0.125,
"height": 0.167,
"changed_ratio": 0.12
}
],
"timestamp": 1710252600000
}
The score is a 0-1 value representing overall motion intensity. The regions array breaks the frame into a grid and reports which areas have activity and how much change occurred in each. This lets you distinguish between a door opening in one corner vs full-frame motion.
Tuning Motion Sensitivity¶
- Sensitivity threshold (default 0.018) — ratio of changed pixels needed to count as motion. Lower = more sensitive.
- Analysis interval (default 700ms) — how often frames are compared.
- Pixel delta threshold (18) — minimum per-pixel brightness change to count as different.
MQTT publishes are throttled to at most once every 2 seconds to avoid flooding your broker.
Camera Snapshots (Motion-Triggered)¶
King Kiosk can automatically capture camera snapshots when motion is detected, giving you visual evidence of what triggered the motion alert.
Enable Motion-Triggered Snapshots¶
You can also set periodic snapshots:
Or capture a one-off snapshot:
Snapshots are published as raw JPEG bytes to kingkiosk/{device_id}/camera/snapshot, with metadata on kingkiosk/{device_id}/camera/state. Home Assistant auto-discovers this as a camera entity.
Detection MQTT Topics Reference¶
| Topic | Payload Type | Description |
|---|---|---|
kingkiosk/{id}/person_presence |
JSON | Person present (bool), confidence |
kingkiosk/{id}/object_detection |
JSON | Detected objects with classes and bounding boxes |
kingkiosk/{id}/face_presence |
JSON | Face count, known/unknown faces, per-face details |
kingkiosk/{id}/motion/result |
JSON | Motion score (0-1), active regions |
kingkiosk/{id}/camera/snapshot |
JPEG bytes | Raw camera snapshot image |
kingkiosk/{id}/camera/state |
JSON | Snapshot metadata (timestamp, size, format) |
Command topics (send to kingkiosk/{id}/system/cmd):
| Command | Purpose |
|---|---|
person_detection |
Enable/disable/toggle person & object detection |
start_face_training |
Begin face enrollment for a named person |
enroll_face |
Alias for start_face_training |
cancel_face_training |
Cancel an in-progress training session |
camera_snapshot |
Capture, enable periodic, or enable motion-triggered snapshots |
Detection Home Assistant Integration¶
When Home Assistant MQTT Discovery is enabled, King Kiosk automatically registers four detection sensors:
| HA Entity | Sensor Type | Value | Icon |
|---|---|---|---|
sensor.{device}_object_detection |
Count | Number of detected objects | mdi:eye |
sensor.{device}_person_presence |
Occupancy | ON/OFF | mdi:human |
sensor.{device}_face_detection |
Count | Number of faces (0 when none) | mdi:face-recognition |
sensor.{device}_motion_detection |
Percentage | Motion score as 0-100% | mdi:run-fast |
Each sensor also exposes the full JSON payload as attributes, so you can access details like individual face names, object classes, or motion regions in your templates.
To enable auto-discovery, go to Settings > MQTT and toggle Home Assistant Discovery, or provision via MQTT:
Detection Automation Examples¶
Home Assistant: Turn on lights when someone enters¶
automation:
- alias: "Lights on when person detected"
trigger:
- platform: state
entity_id: sensor.kitchen_tablet_person_presence
to: "ON"
action:
- service: light.turn_on
target:
entity_id: light.kitchen
Home Assistant: Greet a recognized person¶
automation:
- alias: "Welcome home greeting"
trigger:
- platform: state
entity_id: sensor.front_door_tablet_face_detection
condition:
- condition: template
value_template: >
{{ state_attr('sensor.front_door_tablet_face_detection', 'faces_detail')
| selectattr('recognized', 'equalto', true) | list | count > 0 }}
action:
- service: mqtt.publish
data:
topic: "kingkiosk/front_door_tablet/system/cmd"
payload: >
{"command": "tts", "text": "Welcome home, {{ state_attr('sensor.front_door_tablet_face_detection', 'faces_detail')
| selectattr('recognized', 'equalto', true) | map(attribute='name') | first }}!"}
Home Assistant: Motion-triggered security snapshot¶
automation:
- alias: "Capture snapshot on motion"
trigger:
- platform: numeric_state
entity_id: sensor.hallway_tablet_motion_detection
above: 30
action:
- service: mqtt.publish
data:
topic: "kingkiosk/hallway_tablet/system/cmd"
payload: '{"command": "camera_snapshot", "action": "capture"}'
- service: notify.mobile_app
data:
message: "Motion detected in hallway ({{ states('sensor.hallway_tablet_motion_detection') }}%)"
Node-RED: Object counting dashboard¶
In Node-RED, subscribe to kingkiosk/{device_id}/object_detection with an MQTT-in node. Parse the JSON and use msg.payload.objects to filter by class:
// Function node: count specific object types
const objects = msg.payload.objects || [];
msg.people = objects.filter(o => o.class === 'person').length;
msg.pets = objects.filter(o => o.class === 'dog' || o.class === 'cat').length;
return msg;
Node-RED: Face-based scene switching¶
Subscribe to kingkiosk/{device_id}/face_presence and route based on who's recognized:
// Function node: trigger different layouts per person
const faces = msg.payload.faces_detail || [];
const known = faces.filter(f => f.recognized).map(f => f.name);
if (known.includes('Kids')) {
msg.payload = {"command": "load_screen_state", "name": "kids-dashboard"};
} else if (known.length > 0) {
msg.payload = {"command": "load_screen_state", "name": "family-dashboard"};
}
return msg;
Then connect to an MQTT-out node publishing to kingkiosk/{device_id}/system/cmd.
15. Quick Reference Card¶
Essential Topics¶
| Purpose | Topic |
|---|---|
| Send system commands | kingkiosk/{device_id}/system/cmd |
| Send element commands | kingkiosk/{device_id}/element/{element_id}/cmd |
| Send window-controller commands (legacy compatibility path) | kingkiosk/{device_id}/window/{window_id}/command |
| System responses | kingkiosk/{device_id}/system/response |
| Fleet layout bus | kingkiosk/fleet/{fleet_id}/layout |
| Fleet command bus | kingkiosk/fleet/{fleet_id}/cmd |
| Fleet element command bus | kingkiosk/fleet/{fleet_id}/element/{element_id}/cmd |
| Element state | kingkiosk/{device_id}/element/{element_id}/state |
| Device info | kingkiosk/{device_id}/info |
| Online status | kingkiosk/{device_id}/status |
Most-Used Commands¶
| Action | Command |
|---|---|
| Create clock | {"command": "open_clock", "window_id": "clock-1"} |
| Create web browser | {"command": "open_browser", "url": "...", "window_id": "web-1"} |
| Close window | {"command": "close_window", "window_id": "..."} |
| Close all windows | {"command": "close_all_windows"} |
| Set volume | {"command": "set_volume", "value": 0.5} |
| Set brightness | {"command": "set_brightness", "value": 0.8} |
| Show notification | {"command": "notify", "title": "...", "message": "..."} |
| Text-to-speech | {"command": "tts", "text": "..."} |
| Take screenshot | {"command": "screenshot"} |
| Save layout | {"command": "save_screen_state", "name": "..."} |
| Load layout | {"command": "load_screen_state", "name": "..."} |
| Join fleet updates | {"command":"subscribe_fleet","fleet_id":"lobby-displays","auto_apply":true,"subscribe_layout":true,"subscribe_commands":true} |
| Broadcast current layout to fleet | {"command":"replicate_layout","fleet_id":"lobby-displays"} |
| Broadcast a command to fleet | {"command":"broadcast_fleet_command","fleet_id":"lobby-displays","fleet_command":{"command":"notify","title":"Attention","message":"..."}} |
| Broadcast an element command to fleet | {"command":"broadcast_fleet_command","fleet_id":"lobby-displays","target_topic":"kingkiosk/fleet/lobby-displays/element/ticker-main/cmd","fleet_command":{"command":"set_opacity","opacity":0.5}} |
| Leave fleet updates | {"command":"unsubscribe_fleet","fleet_id":"lobby-displays"} |
| Backup full config + layouts | {"command":"get_config","include_secrets":true,"include_layouts":true} |
| Restore/clone full config + layouts | {"command":"provision","settings":{...},"screen_states":[...],"current_layout":{...},"overwrite":true} |
| List windows | {"command": "list_windows"} |
Widget Types¶
| Widget | Create Command | Key Parameters |
|---|---|---|
| Clock | open_clock |
mode, theme, show_numbers |
| Weather | open_weather_client |
api_key, location, units |
| Web Browser | open_browser |
url, title |
| Remote Browser | create_remote_browser |
window_id, initial_url, optional server_url |
| Custom Widget | create_remote_browser |
window_id, initial_url, optional server_url |
| Video/Audio | play_media |
type, url, loop |
| YouTube | youtube |
url |
| Carousel | create_carousel |
items, config |
| Gauge | create_gauge |
gauge_type, min, max, unit, pointers[] |
| Chart | create_chart |
chart_type, max_points, title |
| Map | open_map |
initial_camera, provider |
| Canvas | open_canvas |
doc, interaction |
| Animated Text | open_animated_text |
text, preset, effects |
| MQTT Image | mqtt_image |
mqtt_topic, is_base64 |
| MQTT Button | mqtt_button |
publish_topic, subscription_topic |
| Calendar | calendar |
action, uid (for sync) |
| Timer | timer_widget |
config.duration |
| Stopwatch | stopwatch |
- |
| DLNA Player | dlna_player |
- |
open_pdf |
url |
|
| Alarmo | alarmo |
mqtt_base_topic, entity, require_code_to_arm, require_code_to_disarm, code_required_modes, force, skip_delay |
| Games | stop_the_missiles |
game_type, config |
Thank you for being a King Kiosk beta tester! Your feedback shapes the future of the Programmable Display Runtime™. Report issues and share ideas in our Facebook group.
King Kiosk - The Programmable Display Runtime™