External bus
The frontend is able to set up a message bus with an external app that is embedding the Home Assistant frontend. This system is a generalization of the external authentication, making it easier to add more commands in the future without extensive plumbing on either the app or frontend side.
Message exchange
Just like external auth, message exchange is achieved by the external app making a JavaScript method available.
Messages are passed to the external app as serialized JSON objects. The function that will be called takes a single parameter: a string. The external app will have to process the message and deal with it accordingly (or ignore it).
On Android, the implementation depends on the WebView features:
V2 (recommended): Uses WebViewFeature.WEB_MESSAGE_LISTENER for secure origin validation. The frontend sends messages using the injected V2 object:
V1 (fallback): Used when the WebView doesn't support WebViewFeature.WEB_MESSAGE_LISTENER. Your app needs to define:
window.externalApp.externalBus(message: string)
On iOS, your app needs to define the following method:
window.webkit.messageHandlers.externalBus.postMessage(message: string);
To send messages to the frontend, serialize your message to JSON and call the following function from the external app:
window.externalBus(message: string)
Message format
The message describes an action or a piece of information that the sender wants the receiver to do or know about. If it's an action, the sender will expect a response with the result of that action. A response to a command can either be successful or failed.
Action and info message format
The format of a message that contains or provides information is the same. It contains an identifier, a type and an optional payload (depending on the type).
A result message will reuse the identifier in the response, to indicate to which action the response is related.
The basic format of a message is the following:
{
id: number;
type: string;
payload?: unknown;
}
An example message:
{
"id": 5,
"type": "config/get"
}
Result message format
If the message was an action, the sender will expect a response with the result. The response is either success or failure.
The type of result depends on the type of the message that it is responding to. For example, if it is responding to config/get, the result should be an object describing the configuration.
Message formats:
interface SuccessResult {
id: number;
type: "result";
success: true;
result: unknown;
}
interface ErrorResult {
id: number;
type: "result";
success: false;
error: {
code: string;
message: string;
};
}
Supported messages
Messages are organized by direction: from frontend to app, and from app to frontend.
Messages from frontend to app
These messages are sent from the Home Assistant frontend to the external app.
Messages expecting a response
config/get
Query the external app for the external configuration. The external configuration is used to customize the experience in the frontend.
Payload: None
Expected response:
{
hasSettingsScreen?: boolean;
hasSidebar?: boolean;
canWriteTag?: boolean;
hasExoPlayer?: boolean;
canCommissionMatter?: boolean;
canImportThreadCredentials?: boolean;
canTransferThreadCredentialsToKeychain?: boolean;
hasAssist?: boolean;
hasBarCodeScanner?: number;
canSetupImprov?: boolean;
appVersion?: string;
hasEntityAddTo?: boolean;
hasAssistSettings?: boolean;
}
hasSettingsScreen: Set to true if the external app will show a configuration screen when it receivesconfig_screen/showhasSidebar: Set to true if the external app has a sidebarcanWriteTag: Set to true if the external app can write NFC tags (i.e., the device has NFC hardware)hasExoPlayer: Set to true if the app supports HLS video playback via ExoPlayercanCommissionMatter: Set to true if the app can commission Matter devicescanImportThreadCredentials: Set to true if the app can import Thread credentialscanTransferThreadCredentialsToKeychain: Set to true if the app can transfer Thread credentials to the keychain (Apple only)hasAssist: Set to true if the app has a native Assist interface, replacing the frontend implementationhasBarCodeScanner: Set to1if the app has bar code scanning capability,0otherwisecanSetupImprov: Set to true if the app can set up Improv Wi-Fi devicesappVersion: The version string of the native apphasEntityAddTo: Set to true if the app supports adding entities to platform-specific locations (e.g., homescreen widget)hasAssistSettings: Set to true if the app has an Assist settings screen
entity/add_to/get_actions
Get available actions for adding an entity to the device (e.g., homescreen, watch face).
Payload:
{
entity_id: string;
}
Expected response:
{
actions: Array<{
enabled: boolean;
name: string;
details?: string;
mdi_icon: string;
app_payload: string;
}>;
}
enabled: Whether the action is currently availablename: Display name of the actiondetails: Optional additional details about the actionmdi_icon: Material Design Icon identifier for the action (e.g., "mdi:car")app_payload: Opaque string to be sent back inentity/add_toto execute the action
Messages not expecting a response
assist/settings
Open the Assist settings screen.
Payload: None
assist/show
Show the native Assist interface.
Payload (optional):
{
pipeline_id: "preferred" | "last_used" | string;
start_listening: boolean;
}
pipeline_id: The pipeline to use, or "preferred"/"last_used" for automatic selectionstart_listening: Whether to start listening immediately
bar_code/close
Close the bar code scanner.
Payload: None
bar_code/notify
Show a notification message in the bar code scanner.
Payload:
{
message: string;
}
bar_code/scan
Start scanning a bar code.
Payload:
{
title: string;
description: string;
alternative_option_label?: string;
}
title: Title to show in the scanner UI (required)description: Description text to show in the scanner UI (required)alternative_option_label: Optional label for an alternative action button, if not included the alternative action button should be hidden
config_screen/show
Show the configuration screen of the external app.
Payload: None
connection-status
Notify the external app if the frontend is connected to Home Assistant.
Payload:
{
event: "connected" | "auth-invalid" | "disconnected";
}
entity/add_to
Add an entity to a platform-specific location (e.g., homescreen widget).
Payload:
{
entity_id: string;
app_payload: string;
}
entity_id: The entity to addapp_payload: Opaque string received fromentity/add_to/get_actions
exoplayer/play_hls
Play an HLS video stream.
Payload:
{
url: string;
muted: boolean;
}
url: The HLS stream URLmuted: Whether to start playback muted
exoplayer/resize
Resize the HLS video player.
Payload:
{
left: number;
top: number;
right: number;
bottom: number;
}
left: Left coordinate of the playertop: Top coordinate of the playerright: Right coordinate of the playerbottom: Bottom coordinate of the player
exoplayer/stop
Stop HLS video playback.
Payload: None
focus_element
Focus a specific element in the frontend.
Payload:
{
element_id: string;
}
element_id: The ID of the element to focus
haptic
Notify the external app to trigger haptic feedback.
Payload:
{
hapticType:
| "success"
| "warning"
| "failure"
| "light"
| "medium"
| "heavy"
| "selection";
}
improv/configure_device
Configure a discovered Improv Wi-Fi device.
Payload:
{
name: string;
}
name: The name of the device to configure
improv/scan
Start scanning for Improv Wi-Fi devices.
Payload: None
matter/commission
Start the Matter device commissioning flow.
Payload (optional):
{
mac_extended_address: string | null;
extended_pan_id: string | null;
border_agent_id: string | null;
active_operational_dataset: string | null;
}
All payload properties describe the preferred Thread network which should be used when commissioning a Matter over Thread device.
mac_extended_address: The MAC extended addressextended_pan_id: The extended PAN IDborder_agent_id: The border agent IDactive_operational_dataset: The active operational dataset (TLV)
sidebar/show
Show the sidebar.
Payload: None
tag/write
Tell the external app to open the UI to write to a tag.
Payload:
{
tag: string;
name: string | null;
}
tag: The tag ID to writename: The name of the tag as entered by the user, ornullif no name has been set
theme-update
Notify the app that the theme has been updated. The app should refresh its status bar and navigation bar colors.
Payload: None
thread/import_credentials
Import Thread network credentials from the device. Credentials should be sent using the WebSocket API
Payload: None
thread/store_in_platform_keychain
Store Thread credentials in the platform keychain.
Payload:
{
mac_extended_address: string | null;
border_agent_id: string | null;
active_operational_dataset: string;
extended_pan_id: string;
}
mac_extended_address: The MAC extended addressborder_agent_id: The border agent IDactive_operational_dataset: The active operational dataset (TLV)extended_pan_id: The extended PAN ID
Messages from app to frontend
These messages are sent from the external app to the Home Assistant frontend.
Messages expecting a response
All commands in this section receive a result message back from the frontend:
{
id: number;
type: "result";
success: boolean;
result: null;
error?: { code: string; message: string };
}
automation/editor/show
Open the automation editor to create a new automation. The config can be used to pre-fill the editor with specific triggers, conditions, and actions.
Payload (optional):
{
config?: {
alias?: string;
description?: string;
triggers?: Trigger | Trigger[];
conditions?: Condition | Condition[];
actions?: Action | Action[];
mode?: "single" | "restart" | "queued" | "parallel";
max?: number;
};
}
config.alias: Pre-filled name for the automationconfig.description: Pre-filled descriptionconfig.triggers: One or more triggers to pre-populateconfig.conditions: One or more conditions to pre-populateconfig.actions: One or more actions to pre-populateconfig.mode: Execution mode for the automationconfig.max: Maximum number of concurrent runs (only applies toqueuedandparallelmodes)
bar_code/aborted
Notify that barcode scanning was aborted.
Payload:
{
reason: "canceled" | "alternative_options";
}
bar_code/scan_result
Send a bar code scanner result to the frontend.
Payload:
{
rawValue: string;
format:
| "aztec"
| "code_128"
| "code_39"
| "code_93"
| "codabar"
| "data_matrix"
| "ean_13"
| "ean_8"
| "itf"
| "pdf417"
| "qr_code"
| "upc_a"
| "upc_e"
| "unknown";
}
rawValue: A string decoded from the barcode dataformat: The barcode format as defined by the Barcode Detection API
improv/device_setup_done
Notify that Improv Wi-Fi device setup is complete.
Payload: None
improv/discovered_device
Notify that an Improv Wi-Fi device was discovered.
Payload:
{
name: string;
}
kiosk_mode/set
Enable or disable kiosk mode.
navigate
Navigate to a specific path in the frontend.
Payload:
{
path: string;
options?: {
replace?: boolean;
data?: any;
};
}
path: Absolute path to navigate to (for example,/config/voice-assistants/assistants). The path is passed directly tohistory.pushStateorhistory.replaceState, so it must start with/options.replace: If true, replaces the current browser history entry instead of pushing a new one.options.data: Optional data to store in the browser history state, accessible viahistory.state
notifications/show
Show the notifications panel.
Payload: None
restart
Request the frontend to restart.
Payload: None
sidebar/show
Show the sidebar. Returns an error response with code not_allowed if a dialog is currently open.
Payload: None
sidebar/toggle
Toggle the sidebar open/closed. Returns an error response with code not_allowed if a dialog is currently open.
Payload: None
Payload:
{
enable: boolean;
}