🚀 Xiaoai Speaker Voice Notification Tool
Send voice notifications to Xiaoai speakers via CLI, TUI, MCP, or Webhook.

██╗ ██╗██╗ █████╗ ██████╗ ██╗
╚██╗██╔╝██║██╔══██╗██╔═══██╗██║
╚███╔╝ ██║███████║██║ ██║██║
██╔██╗ ██║██╔══██║██║ ██║██║
██╔╝ ██╗██║██║ ██║╚██████╔╝██║
╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝
✨ Features
- TUI Interactive Interface: Configure accounts, send notifications, and manage Webhook/PM2.
- CLI Commands: Suitable for scripting and automation scenarios.
- MCP Server: Can be called by AI programming assistants like Codex, Cursor, or VS Code.
- Webhook Service: Provides HTTP interfaces for easy integration with third - party systems.
- Multi - Speaker Routing: Supports maintaining a speaker list, setting a default speaker, and temporarily overriding based on the requested
did.
- PM2 Persistence: Run the Webhook in the background with a single command (no need to keep the terminal open).
📜 Changelog
v1.0.10 (2026 - 02 - 14)
- Added multi - speaker routing capabilities: Supports
speaker.defaultDid and speaker.speakers, and is compatible with the old speaker.did.
- The four Webhook interfaces support passing
did in the body (tts/audio/volume/command). If not passed, it will be routed according to the default priority.
- Added TUI "Speaker List Management": Supports adding devices, setting defaults, enabling/disabling, and deleting.
- Added CLI MiOT capabilities:
xiaoi command (actions) and xiaoi getprop (property reading).
- Added MCP tools:
do_action and get_property, and both support an optional did.
- Updated Docker and documentation: Added
XIAOI_DEFAULT_DID and simple cmd use cases for OH2P (xiaomi.wifispeaker.oh2p).
v1.0.9 (2026 - 02 - 14)
- Fixed the issue where
.mi.json (login credential cache) was written to the current directory when the CLI was executed in any directory. Now it is fixed to be written to the ~/.xiaoi/ directory.
v1.0.8 (2026 - 02 - 12)
- Fixed the Docker container startup failure: Removed the
--log - date - format parameter that pm2 - runtime does not support.
- Fixed the Docker build failure: Changed
npm ci to npm install (compatible with pnpm projects).
- Added an example of the
XIAOI_TOKEN environment variable in the documentation to facilitate users to customize the Webhook authentication token.
v1.0.7 (2026 - 02 - 12)
- Added Docker containerized deployment: Supports configuration through environment variables without manually editing the configuration file. The Webhook service can be started with a single command.
- Added GitHub Actions CI/CD: Automatically publish npm packages and build and push Docker images (Docker Hub + GHCR, supporting both amd64/arm64 architectures) when tagging.
- Added an
.env.example environment variable template to lower the threshold for Docker deployment.
- Use PM2 (
pm2 - runtime) to manage processes in the container, with automatic health checks and restarts.
v1.0.6 (2026 - 02 - 12)
- Fixed the input parsing error of
ttscmd: [7,3] is no longer misparsed as [0,7].
- Optimized the account settings display: When the default
ttscmd is not set, the device commands parsed from the current did are automatically displayed (automatic mapping).
v1.0.5 (2026 - 02 - 12)
- Added automatic mapping log output for
ttscmd: Displays model / match / source / command.
- Added the
[XIAOI] prefix to all detailed logs for easy filtering and retrieval in PM2/console.
v1.0.4 (2026 - 02 - 12)
- Added the ability to automatically parse the device model by
did on the account settings page and display the "effective ttscmd mapping".
- Improved the link debugging capabilities: Supports manual
cmd, temporary link mode, and detailed log switches.
v1.0.3 (2026 - 02 - 12)
- Added TTS link mode switching:
auto / command / default.
- The connection test supports manually entering a temporary
ttscmd and temporary mode for on - site debugging.
- Added a detailed log switch to show/hide the execution details of
ttscmd and the default link.
v1.0.2 (2026 - 02 - 11)
- Adjusted the TTS call order: Prioritize
ttscmd(MiOT.doAction), and fall back to the default MiNA.play(text) if it fails.
- Built - in complete model
ttsFallbackCommands mapping (including common models such as LX04), and supports user - defined overrides.
- Added "View device list and select did" in the account settings, which can directly write the configuration back from the device list with one click, reducing the probability of filling in the wrong
did.
- Added
speaker.ttsFallbackCommand and speaker.ttsFallbackCommands to the configuration template/automatically generated configuration/README.
- Added an active test mode: Supports testing the
ttscmd link and the default link (MiNA.play) separately.
- Added a
ttscmd editing entry in the account settings: Supports modifying the default command and overriding commands by model.
- Added TTS link modes: Supports three modes of
auto / command / default, and users can manually switch.
- Added a detailed log switch: Supports showing/hiding the execution logs of
ttscmd and the default link.
- The connection test supports manually entering a temporary
ttscmd and temporary link mode for on - site debugging.
v1.0.1 (2026 - 02 - 10)
- Fixed the misjudgment of
pm2/npm/npx detection on Windows (the ENOENT/non - executable problem caused by the .cmd shim).
- Fixed the failure to recognize the global pm2 due to
npm v10+ not supporting npm bin - g (fall back to npm prefix - g).
- Optimized the experience of using arrow keys/keypad numbers for selection in the TUI (reduced redraw lag and no need for a second enter when returning).
- Optimized the Webhook menu status display (distinguish between embedded/PM2 persistence to avoid misleading).
- Added: View PM2 logs in the TUI.
- Added: Check for updates every time it starts (can be disabled with
XIAOI_NO_UPDATE_CHECK = 1).
📦 Installation
Global Installation (Recommended)
npm i -g xiaoii
pnpm add -g xiaoii
After installation, you can use the xiaoi and xiaoi - mcp commands in any directory.
Installation from Source
git clone https://github.com/xvhuan/xiaoi.git
cd xiaoi
npm i
npm link
pnpm install
pnpm link --global
⚙️ Configuration
Automatic Creation (Installation/First Run)
When the installation is completed or xiaoi is executed for the first time, the following will be automatically created:
- Directory:
~/.xiaoi/ (on Windows, it is %USERPROFILE%\.xiaoi\).
- Configuration:
~/.xiaoi/config.json (empty template).
Manual Configuration
Edit ~/.xiaoi/config.json:
{
"speaker": {
"userId": "Your Xiaomi ID (number, not phone number)",
"password": "Your password (not recommended)",
"passToken": "Your passToken (recommended)",
"did": "The name of the speaker in the Mi Home app",
"defaultDid": "Default speaker did (optional)",
"speakers": [
{
"did": "Speaker did",
"name": "Living room Xiaoai",
"model": "lx04",
"enabled": true
}
],
"ttsMode": "auto",
"verboseLog": false,
"ttsFallbackCommand": [5, 1],
"ttsFallbackCommands": {
"oh2p": [7, 3],
"oh2": [5, 3],
"lx06": [5, 1],
"s12": [5, 1],
"l15a": [7, 3],
"lx5a": [5, 1],
"lx05": [5, 1],
"x10a": [7, 3],
"l17a": [7, 3],
"l06a": [5, 1],
"lx01": [5, 1],
"l05b": [5, 3],
"l05c": [5, 3],
"l09a": [3, 1],
"lx04": [5, 1],
"asx4b": [5, 3],
"x6a": [7, 3],
"x08e": [7, 3],
"x8f": [7, 3]
}
},
"webhook": {
"port": 51666,
"host": "localhost",
"token": "",
"logFile": "log/webhook.log"
},
"mcp": {
"logFile": "log/mcp_server.log"
}
}
Configuration file lookup priority:
~/.xiaoi/config.json
- (Fallback)
config.json in the installation directory/project directory.
Field descriptions (common):
| Property |
Details |
speaker.userId |
Xiaomi ID (number, view in Xiaomi account personal information) |
speaker.password |
Xiaomi account password (may fail due to security verification) |
speaker.passToken |
passToken (recommended) |
speaker.did |
Compatible field (default device in the old version); will be synchronized with defaultDid in the new version |
speaker.defaultDid |
Default speaker did (used preferentially when did is not passed in the request body) |
speaker.speakers |
List of added speakers (did/name/model/enabled) |
speaker.ttsMode |
TTS link mode: auto (first ttscmd then default), command (only ttscmd), default (only default link) |
speaker.verboseLog |
Detailed log switch (true/false), controls whether to print link execution details |
speaker.ttsFallbackCommand |
Default ttscmd (default [5,1], called first) |
speaker.ttsFallbackCommands |
Override ttscmd by model (e.g., lx04:[5,1], l09a:[3,1]) |
webhook.host |
Listening address; can be set to 0.0.0.0 for external network access (note security) |
webhook.port |
Webhook port |
webhook.token |
Webhook authentication token (optional; if left empty for the persistent Webhook, it will be automatically generated and written back to the configuration) |
Webhook default speaker priority:
speaker.defaultDid
XIAOI_DEFAULT_DID (environment variable)
speaker.did (compatible field)
When did is explicitly passed in the request body, did must be in speaker.speakers and enabled = true, otherwise a 400 will be returned.
💡 Usage Tip
You can switch the TTS mode, modify the default/model ttscmd, and toggle the detailed log in the "Account Settings" of the TUI; you can manually enter a temporary ttscmd and temporary mode for debugging in the "Connection Test".
It is recommended to use passToken for login. For reference on obtaining passToken, see [migpt - next/issues/4](https://github.com/idootop/migpt - next/issues/4).
💻 Usage Examples
TUI Interactive Interface
xiaoi
CLI Commands
xiaoi tts "Code compilation completed"
xiaoi tts Deployment completed, please check
xiaoi volume 30
xiaoi command 3 1 "[]"
xiaoi command 3 1 '[{"piid":1,"value":true}]' --did Living room Xiaoai
xiaoi getprop 3 1 --did Living room Xiaoai
xiaoi status
xiaoi help
Simple MiOT cmd Use Case (xiaomi.wifispeaker.oh2p)
- Mi Home specification document (example for your speaker):
https://home.miot - spec.com/spec/xiaomi.wifispeaker.oh2p
- Common TTS
ttscmd for this model: [7,3]
xiaoi tts "Master, this is an OH2P test" --did Your device did
xiaoi command 7 3 "[\"Master, this is an OH2P cmd test\"]" --did Your device did
xiaoi getprop 3 1 --did Your device did
Simple method to build cmd (siid/aiid/params):
- Open the corresponding device specification page (e.g., the
oh2p link above).
- Find the
Service / Action corresponding to the target capability and record siid and aiid.
- Prepare
params according to the parameter definition of the Action: Use [] for no parameters, and assemble a JSON array in the order of the documentation for parameters.
- Execute:
xiaoi command <siid> <aiid> '<paramsJson>' [--did <device did>].
Note: The params structure of different actions is different. Be sure to follow the action parameter definition in the specification.
MCP Server (AI Programming Assistant Integration)
You need to install and run xiaoi globally first to complete the account configuration.
VS Code / Cursor (JSON)
Create .vscode/mcp.json in the project:
{
"servers": {
"xiaoi - voice - notify": {
"type": "stdio",
"command": "xiaoi - mcp"
}
}
}
Codex CLI (TOML)
[mcp_servers.xiaoi - voice - notify]
command = "xiaoi - mcp"
Tool list:
| Tool |
Description |
notify |
Send voice notifications (TTS) |
play_audio |
Play audio links |
set_volume |
Set volume |
do_action |
Send MiOT commands (siid/aiid/params) |
get_property |
Read MiOT property values (siid/piid) |
All 5 MCP tools support an optional parameter did: If not passed, it will use the default speaker; if passed, it will use the specified speaker.
Webhook Service (HTTP Interface)
You can start the Webhook in the TUI or use the PM2 persistence method (see the next section).
When you configure webhook.token (or use the token automatically generated by the persistent Webhook), please include the following headers in the request:
Authorization: Bearer <token>
- or
X - Xiaoi - Token: <token>
You can also pass did in the request body to specify the target speaker for this time; if not passed, it will be automatically routed according to the default priority.
curl -X POST http://localhost:51666/webhook/tts \
-H "Content - Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"text":"Hello, world","did":"Living room Xiaoai"}'
curl -X POST http://localhost:51666/webhook/audio \
-H "Content - Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"url":"https://example.com/audio.mp3","did":"Bedroom Xiaoai"}'
curl -X POST http://localhost:51666/webhook/volume \
-H "Content - Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"volume":50,"did":"Living room Xiaoai"}'
curl -X POST http://localhost:51666/webhook/command \
-H "Content - Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"siid":3,"aiid":1,"params":[],"did":"Living room Xiaoai"}'
Webhook Persistence (One - Click Start with PM2)
If you want the Webhook to run in the background on the server/computer for a long time (no need to keep the terminal open), use the built - in PM2 management commands:
xiaoi pm2 start
xiaoi pm2 deploy
xiaoi pm2 status
xiaoi pm2 logs 200
xiaoi pm2 webhook - log 200
xiaoi pm2 public on
xiaoi pm2 public off
xiaoi pm2 stop
xiaoi pm2 delete
xiaoi pm2 save
xiaoi pm2 startup
If your Webhook is to provide services to the external network, be sure to set webhook.token or use a firewall/reverse proxy for authentication to avoid being called arbitrarily.
Docker Deployment (Containerized Webhook)
Package the xiaoi Webhook into a Docker container for running, which is suitable for servers, NAS, and cloud hosts. In basic scenarios, there is no need to manually edit the configuration file. You only need to fill in the environment variables to start (see "Multi - Speaker Configuration" below for multi - speaker scenarios).
Quick Start (Pull the image directly, no need to clone)
docker pull iusy/xiaoi:latest
docker run -d \
--name xiaoi - webhook \
--restart unless - stopped \
-p 51666:51666 \
-e XIAOI_USER_ID=Your Xiaomi ID \
-e XIAOI_PASS_TOKEN=Your passToken \
-e XIAOI_DID=Your speaker name \
-e XIAOI_DEFAULT_DID=Default speaker did \
-e XIAOI_TOKEN=Your Webhook authentication token \
iusy/xiaoi:latest
Done! 🎉 No need to clone the code or edit the configuration file.
Or Use Docker Compose
curl -O https://raw.githubusercontent.com/xvhuan/xiaoi/main/docker - compose.yml
curl -o .env https://raw.githubusercontent.com/xvhuan/xiaoi/main/.env.example
docker - compose up -d
It is recommended to fill in at least 3 items in the .env file (optionally add the default speaker):
XIAOI_USER_ID=Your Xiaomi ID (number)
XIAOI_PASS_TOKEN=Your passToken
XIAOI_DID=Your speaker name
XIAOI_DEFAULT_DID=Default speaker did
XIAOI_TOKEN=Your Webhook authentication token
Method to obtain passToken: [migpt - next/issues/4](https://github.com/idootop/migpt - next/issues/4)
Multi - Speaker Configuration (Two Ways)
When you have multiple speakers, it is recommended to write all target devices into speaker.speakers and set speaker.defaultDid.
Way 1: Enter the container and use xiaoi for interactive configuration (suitable for manual operation and maintenance)
docker exec -it xiaoi - webhook node /app/bin/xiaoi.js
docker compose exec xiaoi - webhook node /app/bin/xiaoi.js
Then enter in the TUI: Account Settings -> Speaker List Management to complete "Add Speaker/Set Default Speaker".
Way 2: Directly edit the configuration file (suitable for batch/automation)
The configuration file path in the container is /root/.xiaoi/config.json (on the premise that a persistent volume is mounted).
{
"speaker": {
"defaultDid": "Living room speaker did",
"speakers": [
{ "did": "Living room speaker did", "name": "Living room", "model": "oh2p", "enabled": true },
{ "did": "Bedroom speaker did", "name": "Bedroom", "model": "lx04", "enabled": true }
]
}
}
Restart the container after modification to take effect:
docker restart xiaoi - webhook
docker compose restart xiaoi - webhook
Call rules:
- If
did is not passed in the request: Use the default speaker (defaultDid > XIAOI_DEFAULT_DID > speaker.did).
- If
did is passed in the request: Use the specified speaker.
did must be in speaker.speakers and enabled = true.
Pure Docker Commands (Without docker - compose)
docker build -t xiaoi - webhook .
docker run -d \
--name xiaoi - webhook \
--restart unless - stopped \
-p 51666:51666 \
-e XIAOI_USER_ID=Your Xiaomi ID \
-e XIAOI_PASS_TOKEN=Your passToken \
-e XIAOI_DID=Your speaker name \
-e XIAOI_DEFAULT_DID=Default speaker did \
xiaoi - webhook
List of Environment Variables
| Variable |
Required |
Description |
XIAOI_USER_ID |
✅ |
Xiaomi ID (number, view in Xiaomi account personal information) |
XIAOI_PASS_TOKEN |
✅ |
passToken (recommended login method) |
XIAOI_DID |
✅ |
The name of the speaker in the Mi Home app (must be exactly the same) |
XIAOI_DEFAULT_DID |
|
Default speaker did (fall back to XIAOI_DID if not filled) |
XIAOI_PASSWORD |
|
Password login (not recommended, may be blocked by security verification) |
XIAOI_TOKEN |
|
Webhook authentication token (automatically generated if left empty) |
XIAOI_PORT |
|
Port number (default 51666) |
XIAOI_TTS_MODE |
|
TTS mode: auto / command / default |
XIAOI_VERBOSE_LOG |
|
Detailed log: true / false |
Verify the Service
docker - compose logs
curl http://localhost:51666/
curl -X POST http://localhost:51666/webhook/tts \
-H "Content - Type: application/json" \
-H "Authorization: Bearer <token obtained from the logs>" \
-d '{"text":"Docker deployment successful!"}'
Common Commands
docker - compose logs -f
docker - compose restart
docker - compose down
git pull
docker - compose up -d --build
📁 Project Structure
xiaoi/
├── bin/
│ └── xiaoi.js # CLI + TUI entry
├── lib/
│ ├── config.js # User directory configuration management (automatically generates ~/.xiaoi/config.json)
│ ├── speaker.js # Core module (direct connection to speaker API)
│ ├── tui.js # TUI interactive interface
│ ├── webhook_server.js # Persistent Webhook service entry (can be used with PM2)
│ └── pm2.js # PM2 one - click management encapsulation
├── mcp_server.js # MCP Server
├── config.example.json # Configuration template
├── Dockerfile # Docker image build
├── docker - compose.yml # Docker Compose orchestration
├── docker - entrypoint.sh # Container startup entry script
├── .env.example # Docker environment variable template
└── README.md
❓ Frequently Asked Questions
What if the login fails?
- Confirm that
userId is the Xiaomi ID (number), not a phone number or email.
- It is recommended to use
passToken instead of password for login.
- For reference on obtaining passToken, see [migpt - next/issues/4](https://github.com/idootop/migpt - next/issues/4).
Can't find the device?
- Confirm that
did is exactly the same as the speaker name in the Mi Home app.
🙏 Acknowledgments
Built on @mi - gpt/next.
https://github.com/idootop/migpt - next
📄 License
MIT, see LICENSE for details.