Skip to content

King Kiosk User Guide

Your Complete Guide to the World's First Programmable Display Runtime™

Version 2.3 | Beta Tester Edition


Table of Contents

  1. Welcome to King Kiosk
  2. What is King Kiosk?
  3. Why King Kiosk is Different
  4. Platform Availability
  5. How Widget Windows Work
  6. Getting Started
  7. Initial Setup
  8. Privacy & Telemetry
  9. Android Kiosk Mode (Lockdown)
  10. iOS/iPadOS Kiosk Mode (Guided Access)
  11. macOS Kiosk Mode (Best-Effort)
  12. Connecting to Your MQTT Server
  13. Understanding Your Device ID
  14. Home Assistant Auto-Discovery
  15. The MQTT Architecture
  16. How King Kiosk Communicates
  17. Message Addresses (Topics) Explained
  18. Command and Response Flow
  19. Secure Command Signing (Optional)
  20. The Windowing System
  21. Understanding Tiles and Windows
  22. Controlling Windows via Touch and Mouse
  23. Controlling Windows via MQTT
  24. Window Layouts and Arrangements
  25. Saving and Managing Screen States
  26. What is a Screen State?
  27. Saving Your Layout
  28. Loading Saved Layouts
  29. Exporting and Importing for Backup
  30. Remote Feature Server Provisioning
  31. Scheduling Screen States
  32. Fleet Layout Replication
  33. System Commands
  34. Device Control
  35. Volume and Brightness
  36. Notifications and Alerts
  37. Visual Effects
  38. Text-to-Speech
  39. Speech-to-Text
  40. Screenshots and Screen Capture
  41. Background Settings
  42. Screensaver
  43. AI Integration
  44. Batch Commands and Scripting
  45. Widget Reference
  46. Web Browsers
  47. Remote Browser (Apple TV & iOS)
  48. Clock Widget
  49. Weather Widget
  50. Media Player
  51. YouTube Player
  52. Image Carousel
  53. Gauges and Thermostats
  54. Charts
  55. Maps
  56. Canvas (Visual Diagrams)
  57. Animated Text
  58. MQTT Image
  59. MQTT Button
  60. Calendar (with Bidirectional Sync)
  61. Timers and Stopwatch
  62. Alarmo (Security System)
  63. DLNA Player
  64. PDF Viewer
  65. Games
  66. LED Light Panel
  67. RSS Feed Reader
  68. Building Your Own Widgets
  69. Why Build Custom Widgets?
  70. Quick Start
  71. Adding Your Widget
  72. Connecting Your Widget to King Kiosk
  73. Receiving Commands
  74. Sending Data Back From Your Widget
  75. Persistent Storage
  76. Platform Considerations
  77. Complete Example: Smart Thermostat
  78. Native Widget Alternatives
  79. Canvas: Declarative Native Graphics
  80. MQTT Image: External Rendering
  81. Dynamic Native Widgets
  82. Home Assistant Integration
  83. Creating Custom Sensors
  84. Controlling King Kiosk from Home Assistant
  85. Tips and Best Practices
  86. Performance Optimization
  87. Network Considerations
  88. Troubleshooting Common Issues
  89. Apple TV Experience
  90. A Revolutionary Window System for Television
  91. Controlling with the Siri Remote
  92. Understanding Focus Navigation
  93. Move Mode: Rearranging Your Layout
  94. Interactive Mode: Deep Widget Control
  95. MQTT Control for Apple TV
  96. Feature Server
  97. What is the Feature Server?
  98. Intercom & Broadcast
  99. Remote Browser
  100. RTSP Camera Export
  101. Home Assistant MQTT Bridge
  102. Detection, Recognition & Motion
  103. Overview
  104. On-Device vs Feature Server Detection
  105. Person & Object Detection
  106. Facial Recognition
  107. Face Training (Enrollment)
  108. Motion Detection
  109. Camera Snapshots (Motion-Triggered)
  110. MQTT Topics Reference
  111. Home Assistant Integration
  112. Automation Examples
  113. Quick Reference Card
  114. 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.

  • transparent means "make the window and the widget's base panel transparent"
  • translucent means "give me a glassy semi-transparent panel behind the widget"
  • opaque means "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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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:

  1. 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.
  2. 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_telemetry command to kingkiosk/{device_id}/system/cmd:
    {"command": "set_telemetry", "payload": {"enabled": true}}
    
    To query the current state, send {"command": "get_telemetry"} and check the response on kingkiosk/{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.

  1. Factory reset the device.
  2. 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.)
  3. 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:

{
  "command": "command_name",
  "parameter1": "value1",
  "parameter2": "value2"
}

For example, to set the volume to 50%:

{
  "command": "set_volume",
  "value": 0.5
}

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:

{
  "command": "set_volume",
  "value": 0.5,
  "correlation_id": "req-0007"
}

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:

{
  "ts": 1703001234,
  "msg": "{\"command\":\"play\"}",
  "sig": "a1b2c3d4e5f6..."
}

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:

{
  "command": "open_clock",
  "window_id": "my-clock",
  "opacity": 0.8,
  "x": 100,
  "y": 100
}

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:

{
  "command": "update_window",
  "window_id": "kitchen-timer",
  "showBorder": false
}

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:

{
  "command": "bring_to_front",
  "window_id": "important-widget"
}
{
  "command": "send_to_back",
  "window_id": "background-widget"
}

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:

{
  "command": "open_clock",
  "window_id": "lobby-clock",
  "mode": "analog",
  "theme": "dark"
}

Creating a Web Browser Window:

{
  "command": "open_browser",
  "url": "https://example.com",
  "title": "My Browser"
}

Moving a Window:

{
  "command": "move_window",
  "window_id": "lobby-clock",
  "x": 500,
  "y": 200
}

Window movement is smoothly animated by default (including Apple TV). To snap immediately instead of animating:

{
  "command": "move_window",
  "window_id": "lobby-clock",
  "x": 500,
  "y": 200,
  "animate": false
}

Resizing a Window:

{
  "command": "resize_window",
  "window_id": "lobby-clock",
  "width": 400,
  "height": 400
}

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:

{
  "command": "close_window",
  "window_id": "lobby-clock"
}

Closing All Windows:

{
  "command": "close_all_windows"
}

Bringing a Window to Front:

{
  "command": "bring_to_front",
  "window_id": "lobby-clock"
}

Sending a Window to Back:

{
  "command": "send_to_back",
  "window_id": "lobby-clock"
}

Maximizing a Window:

{
  "command": "maximize_window",
  "window_id": "lobby-clock"
}

Minimizing a Window:

{
  "command": "minimize_window",
  "window_id": "lobby-clock"
}

Window Layouts and Arrangements

Switch between different layout modes:

Set Window Mode:

{
  "command": "window_mode",
  "mode": "tiling"
}

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:

{
  "command": "save_screen_state",
  "name": "Morning Dashboard"
}

If a state with that name already exists and you want to overwrite it:

{
  "command": "save_screen_state",
  "name": "Morning Dashboard",
  "overwrite": true
}

Loading Saved Layouts

Instantly switch to a saved layout:

{
  "command": "load_screen_state",
  "name": "Morning Dashboard"
}

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:

{
  "command": "list_screen_states"
}

Response includes the name, window count, and when each was saved.

Deleting a Saved State

Remove a layout you no longer need:

{
  "command": "delete_screen_state",
  "name": "Old Layout"
}

Exporting and Importing for Backup

Export a layout for backup or transfer:

{
  "command": "export_screen_state",
  "name": "Morning Dashboard"
}

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:

{
  "command": "list_screen_schedule"
}

Enable/Disable scheduling:

{
  "command": "enable_screen_schedule"
}
{
  "command": "disable_screen_schedule"
}

Manually trigger a scheduled state:

{
  "command": "trigger_screen_schedule",
  "screen_state": "Morning Dashboard"
}

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-device response_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: true applies incoming fleet layouts immediately.
  • subscribe_commands: true enables fleet-wide command broadcast execution.
  • allow_self_commands: true is optional if the publishing device should also execute its own fleet command.

Step 2A: Broadcast a Layout to the Fleet

{
  "command": "replicate_layout",
  "fleet_id": "lobby-displays"
}

Useful options:

{
  "command": "replicate_layout",
  "fleet_id": "lobby-displays",
  "retain": true
}
{
  "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)

{
  "command": "unsubscribe_fleet",
  "fleet_id": "lobby-displays"
}

Only leave one side if needed:

{
  "command": "unsubscribe_fleet",
  "fleet_id": "lobby-displays",
  "unsubscribe_layout": false,
  "unsubscribe_commands": true
}

Practical Rollout Pattern

  1. Pick a fleet name by location (lobby-displays, menu-boards, gate-signage).
  2. Run subscribe_fleet once per device.
  3. Use replicate_layout for visual consistency.
  4. Use broadcast_fleet_command or direct publish to fleet/{fleet_id}/cmd (system) or fleet/{fleet_id}/element/{element_id}/cmd (topic-addressed element sync) for synchronized actions.
  5. Use correlation_id values in automations to trace distributed command runs.

6. System Commands

Device Control

List all active windows:

{
  "command": "list_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):

{
  "command": "set_volume",
  "value": 0.5
}

Mute/Unmute:

{
  "command": "mute"
}
{
  "command": "unmute"
}

Set Brightness (0.0 to 1.0):

{
  "command": "set_brightness",
  "value": 0.8
}

Get Current Brightness:

{
  "command": "get_brightness"
}

Restore Default Brightness:

{
  "command": "restore_brightness"
}

Notifications and Alerts

Show a Toast Notification:

{
  "command": "notify",
  "title": "Welcome",
  "message": "Good morning!",
  "duration": 5
}

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:

{
  "command": "halo_effect",
  "enabled": false
}

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:

{
  "command": "screensaver",
  "action": "disable"
}

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:

{
  "command": "screensaver",
  "action": "remove_item",
  "item_id": "new_icon"
}

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:

{
  "command": "tts",
  "text": "Welcome to King Kiosk!"
}

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:

{
  "command": "tts",
  "action": "stop"
}

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:

{
  "command": "tts",
  "action": "status",
  "response_topic": "kingkiosk/my_device/system/response"
}

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:

{
  "command": "stt",
  "action": "start"
}

Stop listening and get result:

{
  "command": "stt",
  "action": "stop"
}

Configure language:

{
  "command": "stt",
  "action": "set_language",
  "language": "en"
}

Send transcriptions to AI agent:

{
  "command": "stt",
  "action": "send_to_ai_agent",
  "enabled": true
}

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:

{
  "command": "screenshot",
  "notify": true
}

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:

{
  "command": "screenshot_camera",
  "action": "enable",
  "interval": 30
}

This captures and publishes a screenshot every 30 seconds.

Disable screenshot capture:

{
  "command": "screenshot_camera",
  "action": "disable"
}

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:

{
  "command": "camera_snapshot",
  "action": "capture"
}

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):

{
  "command": "camera_snapshot",
  "action": "enable",
  "interval": 30
}

Auto-capture on motion detection:

{
  "command": "camera_snapshot",
  "action": "set_motion_trigger",
  "enabled": true
}

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):

{
  "command": "camera_snapshot",
  "action": "set_quality",
  "quality": 60
}

Disable camera snapshots:

{
  "command": "camera_snapshot",
  "action": "disable"
}

Background Settings

Set a solid color background:

{
  "command": "set_background",
  "type": "default"
}

Set an image background:

{
  "command": "set_background",
  "type": "image",
  "image_url": "https://example.com/wallpaper.jpg"
}

Set a web page as background:

{
  "command": "set_background",
  "type": "webview",
  "web_url": "https://example.com/animated-background"
}

Get current background:

{
  "command": "get_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:

{
  "command": "screensaver",
  "action": "wake"
}

Alternative actions that also wake the screensaver: wake_up, deactivate

Get screensaver state:

{
  "command": "screensaver",
  "action": "get_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:

{
  "command": "ai_agent",
  "action": "enable"
}

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:

{
  "command": "ai_agent",
  "action": "send_message",
  "message": "What's the weather like today?"
}

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.

{ "command": "wait", "seconds": 5 }

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:

{ "command": "save_screen_state", "name": "Movie Night" }

Load a saved scene:

{ "command": "load_screen_state", "name": "Movie Night" }

List all saved scenes:

{
  "command": "list_screen_states",
  "response_topic": "kingkiosk/my_device/system/response"
}

Delete a scene:

{ "command": "delete_screen_state", "name": "Old Layout" }

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:

{
  "command": "batch_status",
  "response_topic": "kingkiosk/my_device/system/response"
}

The response tells you whether a batch is running, how far along it is, and the total number of steps.

Cancel a running batch:

{
  "command": "kill_batch_script"
}

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:

{
  "command": "remote_browser_back",
  "window_id": "browser_1"
}
{
  "command": "remote_browser_forward",
  "window_id": "browser_1"
}
{
  "command": "remote_browser_reload",
  "window_id": "browser_1"
}

Simulate interaction:

{
  "command": "remote_browser_click",
  "window_id": "browser_1",
  "x": 400,
  "y": 300
}
{
  "command": "remote_browser_scroll",
  "window_id": "browser_1",
  "delta_x": 0,
  "delta_y": -100
}
{
  "command": "remote_browser_key",
  "window_id": "browser_1",
  "key": "Enter"
}
{
  "command": "remote_browser_text",
  "window_id": "browser_1",
  "text": "search query"
}

Zoom in/out:

{
  "command": "remote_browser_zoom",
  "window_id": "browser_1",
  "level": 1.5
}
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):

{
  "command": "remote_browser_clear_data",
  "window_id": "browser_1"
}
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):

{
  "command": "remote_browser_status",
  "window_id": "browser_1"
}

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

{
  "command": "set_mode",
  "mode": "digital"
}

Toggle between analog and digital:

{
  "command": "toggle_mode"
}

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:

{
  "latitude": 40.7128,
  "longitude": -74.0060
}

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:

{
  "command": "play",
  "window_id": "video-player"
}
{
  "command": "pause",
  "window_id": "video-player"
}
{
  "command": "close",
  "window_id": "video-player"
}

Background audio control:

{
  "command": "play_audio"
}
{
  "command": "pause_audio"
}
{
  "command": "stop_audio"
}
{
  "command": "seek_audio",
  "position": 30
}

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
}

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:

{
  "command": "navigate_carousel",
  "window_id": "photo-carousel",
  "index": 5
}

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:

  • backgroundColor or background_color
  • valueBarColor or value_bar_color
  • showLabels or show_labels
  • labelFontSize, label_font_size, or label_font_size_px
  • labelColor or label_color
  • show_indicator or showIndicator

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:

{
  "command": "set_gauge_value",
  "gauge_id": "cpu-usage",
  "value": 72
}

Update a specific pointer:

{
  "command": "set_pointer_value",
  "gauge_id": "living-room-thermostat",
  "pointer_id": "current",
  "value": 72
}

Lock/unlock gauge:

{
  "command": "lock_gauge",
  "gauge_id": "living-room-thermostat"
}
{
  "command": "unlock_gauge",
  "gauge_id": "living-room-thermostat"
}

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:

{
  "command": "append_chart_data",
  "chart_id": "temperature-chart",
  "value": 72.5
}

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:

{
  "command": "reset_chart",
  "chart_id": "temperature-chart"
}

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:

{
  "command": "set_camera",
  "camera": {
    "lat": 40.7580,
    "lon": -73.9855,
    "zoom": 15
  }
}

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:

{
  "command": "clear_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

{
  "command": "set_text",
  "text": "New message here!"
}

Trigger animation replay:

{
  "command": "trigger"
}

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:

{
  "mqtt_topic": "home/camera/snapshot",
  "json_field": "data.image"
}

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:

{
  "command": "mqtt_button",
  "action": "trigger",
  "window_id": "light-toggle"
}

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:

{
  "command": "calendar",
  "action": "clear_events",
  "window_id": "main-calendar"
}

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:

{
  "command": "calendar_delete",
  "correlation_id": "ha-req-124",
  "uid": "doctor-appt-001"
}

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:

{
  "command": "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:

{
  "command": "timer_control",
  "timer_id": "countdown-1",
  "action": "start"
}

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:

{
  "command": "alarmo",
  "window_id": "alarm-panel",
  "force": true,
  "skip_delay": true
}

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:

{
  "command": "audio_call",
  "window_id": "phone-line",
  "name": "Phone Line"
}

Once the window exists, send element commands to kingkiosk/{device_id}/element/{window_id}/cmd:

List available rooms to call:

{
  "command": "list_rooms"
}

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):

{
  "command": "add_room_endpoint",
  "payload": { "room_id": "kingkiosk-kitchen-display" }
}

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:

{
  "command": "add_room_endpoint",
  "payload": { "room_id": "kingkiosk-living-room" }
}

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:

{
  "command": "video_call",
  "action": "close",
  "window_id": "conf-main"
}

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:

{
  "command": "game_control",
  "window_id": "game-1",
  "action": "start"
}

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:

{ "command": "next" }
{ "command": "previous" }
{ "command": "go_to", "index": 5 }

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:

{
  "command": "set_filters",
  "blocked_keywords": ["politics"],
  "family_mode": true
}

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.

{ "command": "expand" }

Send an article to a phone via QR code:

{ "command": "share" }

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:

{ "command": "set_mode", "mode": "hero" }

Pause and resume auto-rotation:

{ "command": "pause" }
{ "command": "resume" }

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:

{ "command": "remove_feed", "url": "https://example.com/feed.xml" }

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": "create_chart",
  "chart_id": "metrics",
  "chart_type": "line",
  "max_points": 50
}
{
  "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

{
  "command": "widget_command",
  "widget_command": "set_value",
  "payload": { "value": 42 }
}

Local customWebView topic: kingkiosk/{device_id}/window/{window_id}/command

{
  "action": "set_value",
  "value": 42
}

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_field in new documents
  • json_path and value_path remain supported aliases
  • path_syntax can 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:

  • text updates primary visible text
  • value updates secondary visible text, usually a node subtitle
  • state updates secondary text and can also drive color/icon mapping
  • color can 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:

{
  "command": "remove_objects",
  "object_ids": ["alert-banner"]
}

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:

{
  "command": "set_gauge_value",
  "gauge_id": "temp-gauge",
  "value": 85
}

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:

{
  "command": "append_chart_data",
  "chart_id": "sensor-chart",
  "value": 72
}

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

{
  "command": "set_text",
  "text": "ALERT: Server maintenance at 2:00 PM"
}

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:

{
  "command": "close_window",
  "window_id": "living-room-temp"
}

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: true and 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:

{
  "command": "window_mode",
  "mode": "tiling"
}

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:

  1. The system calculates the center point of each visible window
  2. When you press Right, it filters to windows with centers to the right of your current focus
  3. It scores candidates by distance (closer = better)
  4. 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:

  1. Focus on the window you want to move
  2. Press Play/Pause to enter Move Mode
  3. The focus ring changes and shows "MOVE ARROWS"
  4. Use the D-pad to nudge the window (40 pixels per press)
  5. 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:

  1. A Remote Browser server (which you set up) renders web pages using a full browser engine
  2. The rendered output streams to your Apple TV in real-time as video
  3. 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:

  1. The screen displays "Setup Mode" with a WiFi network name like KingPico-F23438
  2. On your phone or laptop, connect to that WiFi network (it's open, no password)
  3. Your phone should automatically open a setup page. If not, open a browser and go to http://192.168.4.1
  4. Fill in your WiFi credentials, MQTT broker address, and optionally a device name
  5. 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:

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.

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:

# Toggle lock state remotely
mosquitto_pub ... -m '{"command":"lock"}'

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:

# Set a 4-digit PIN (do this first!)
mosquitto_pub ... -m '{"command":"set_pin","pin":"1234"}'

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.

mosquitto_pub ... -m '{"command":"update_firmware","url":"http://your-server/firmware.bin"}'

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:

  1. Make sure MQTT is working - Both your Feature Server and your King Kiosk devices must be connected to the same MQTT broker
  2. Start your Feature Server - When it starts, it automatically announces itself
  3. 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:

  1. Enter a specific server address (like http://192.168.1.100:3000)
  2. Enable Manual Override Lock - This prevents autodiscovery from changing your manual setting
  3. 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:

{
  "command": "tts",
  "text": "Dinner is ready!",
  "voice": "en_US-lessac-medium"
}

Installing Voices

If a voice isn't installed yet, you can pull it:

{
  "command": "tts",
  "action": "voice_pull",
  "voice": "en_US-lessac-medium"
}

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
  1. You start a broadcast from one kiosk
  2. All other enabled kiosks either auto-open the broadcast or publish an incoming intercom MQTT event, depending on their auto-accept setting
  3. You see yourself on your own screen as a preview (but don't hear yourself — no echo)
  4. 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:

  1. Open Settings on your King Kiosk device
  2. Go to the Feature Server section
  3. Make sure the Feature Server is connected (enter your server hostname/IP if needed)
  4. Toggle Enable intercom on
  5. 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:

Topic: kingkiosk/{device_id}/intercom/start
Payload: {"group": "default"}

Stop broadcasting:

Topic: kingkiosk/{device_id}/intercom/stop
Payload: {}

Toggle (start if stopped, stop if started):

Topic: kingkiosk/{device_id}/intercom/toggle
Payload: {}

Check broadcast status:

Subscribe to kingkiosk/{device_id}/intercom/status to get the current state:

{
  "broadcasting": true,
  "group": "default"
}

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 isSender flag.
  • 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)     │
              └─────────────────┘
  1. A video call window opens on your kiosk showing available devices to call
  2. You pick a device from the room directory, or dial a phone number with the SIP dialpad
  3. The Feature Server sets up the WebRTC connection between both sides
  4. Video and audio flow in both directions — a real conversation
  5. 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:

{
  "command": "video_call",
  "window_id": "conference",
  "name": "Video Conference"
}

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):

{
  "command": "audio_call",
  "window_id": "phone",
  "name": "Phone"
}

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):

{
  "command": "add_room_endpoint",
  "payload": { "room_id": "kingkiosk-kitchen-display" }
}

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.):

{
  "command": "send_dtmf",
  "payload": { "digits": "1234#" }
}

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:

{
  "command": "add_room_endpoint",
  "payload": { "room_id": "kingkiosk-living-room" }
}
{
  "command": "add_sip_endpoint",
  "payload": { "sip_uri": "1002@pbx.local" }
}

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:

kingkiosk/{device_id}/element/{window_id}/state

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     └─────────────────┘
  1. KingKiosk Server runs a headless Chromium browser
  2. Video of the browser is captured and streamed to your tablet
  3. Your touches are sent back to the server and applied to the browser
  4. 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).

Once a remote browser session is running, you can navigate it:

{
  "command": "navigate_remote_browser",
  "window_id": "browser_1",
  "url": "https://youtube.com"
}

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):

type: custom:webrtc-camera
url: kitchen-tablet

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=aac to 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:

include_domains = ["light", "switch", "sensor"]
exclude_entity_globs = ["*_battery"]

Reading Device States

Subscribe to MQTT topics following this pattern:

kingkiosk/ha/state/{domain}/{object_id}

Example — living room light:

kingkiosk/ha/state/light/living_room
Returns:
{
  "entity_id": "light.living_room",
  "state": "on",
  "attributes": {
    "brightness": 200,
    "friendly_name": "Living Room Light"
  }
}

Controlling Devices

Send commands to:

kingkiosk/ha/command/{domain}/{object_id}

Turn on a light:

{"service": "turn_on", "data": {"brightness": 255}}

Set thermostat temperature:

{"service": "set_temperature", "data": {"temperature": 72}}

Turn off a switch:

{"service": "turn_off"}

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:

{"command": "person_detection", "action": "enable"}

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:

{
  "command": "start_face_training",
  "name": "Raj",
  "target_samples": 20
}
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:

{
  "status": "success",
  "message": "Face training started",
  "name": "Raj",
  "target_samples": 20
}

Cancelling Training

{"command": "cancel_face_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:

{
  "command": "provision",
  "settings": {
    "motionDetectionEnabled": true
  }
}

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

{"command": "camera_snapshot", "action": "set_motion_trigger", "enabled": true}

You can also set periodic snapshots:

{"command": "camera_snapshot", "action": "enable", "interval": 30}

Or capture a one-off snapshot:

{"command": "camera_snapshot", "action": "capture"}

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:

{
  "command": "provision",
  "settings": {
    "mqtt_ha_discovery": true
  }
}

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 -
PDF 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™