Content
<div align="center">
```
██╗ ██╗██╗ █████╗ ██████╗ ██╗
╚██╗██╔╝██║██╔══██╗██╔═══██╗██║
╚███╔╝ ██║███████║██║ ██║██║
██╔██╗ ██║██╔══██║██║ ██║██║
██╔╝ ██╗██║██║ ██║╚██████╔╝██║
╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝
```
**Xiaoai Speaker Voice Notification Tool**
Send voice notifications to Xiaoai speakers via CLI / TUI / MCP / Webhook
[](https://nodejs.org/)
[](LICENSE)
[](https://www.npmjs.com/package/xiaoii)
</div>
---
## Features
- TUI interactive interface: Configure accounts, send notifications, manage Webhook/PM2
- CLI commands: Suitable for script/automation scenarios
- MCP Server: For Codex/Cursor/VS Code and other AI programming assistants to call
- Webhook service: Provides HTTP interface for easy integration with third-party systems
- Multi-speaker routing: Supports maintaining a list of speakers, setting default speakers, and temporarily overriding by request did
- PM2 resident: One-click background operation of Webhook (no need to hang up the terminal)
## Changelog
### v1.0.10 (2026-02-14)
- Added multi-speaker routing capability: supports `speaker.defaultDid` and `speaker.speakers`, and is compatible with the old `speaker.did`
- Webhook four interfaces support body transmission `did` (`tts/audio/volume/command`), and if it is not transmitted, 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 capability: `xiaoi command` (action) and `xiaoi getprop` (property reading)
- Added MCP tools: `do_action` and `get_property`, and uniformly support optional `did`
- Docker/documentation update: Added `XIAOI_DEFAULT_DID` and OH2P (`xiaomi.wifispeaker.oh2p`) simple `cmd` use cases
### v1.0.9 (2026-02-14)
- Fixed the issue that `.mi.json` (login credential cache) was written to the current directory when executing CLI in any directory, and now it is fixed to be written to the `~/.xiaoi/` directory
### v1.0.8 (2026-02-12)
- Fixed Docker container startup failure: Removed the `--log-date-format` parameter that `pm2-runtime` does not support
- Fixed Docker build failure: `npm ci` changed to `npm install` (compatible with pnpm projects)
- Document supplemented `XIAOI_TOKEN` environment variable example to facilitate users to customize Webhook authentication Token
### v1.0.7 (2026-02-12)
- Added Docker containerized deployment: Supports configuration through environment variables, no need to manually edit the configuration file, and the Webhook service can be started with one command
- Added GitHub Actions CI/CD: Automatically publish npm packages + build and push Docker images when tagging (Docker Hub + GHCR, supports amd64/arm64 dual architecture)
- Added `.env.example` environment variable template to reduce the threshold for Docker deployment
- Use PM2 (`pm2-runtime`) to manage processes in the container, automatically health check and restart
### v1.0.6 (2026-02-12)
- Fixed `ttscmd` input parsing error: `[7,3]` is no longer mistakenly parsed as `[0,7]`
- Optimized account settings display: When the default `ttscmd` is not set, the device command parsed by the current `did` is automatically displayed (automatic mapping)
### v1.0.5 (2026-02-12)
- Added `ttscmd` automatic mapping log output: display `model / match / source / command`
- Detailed logs are uniformly added with the `[XIAOI]` prefix to facilitate filtering and retrieval in PM2/console
### v1.0.4 (2026-02-12)
- The account settings page adds automatic parsing of the model by `did` and displays the "effective ttscmd mapping"
- Link debugging capability is improved: supports manual `cmd`, temporary link mode, and detailed log switch
### v1.0.3 (2026-02-12)
- Added TTS link mode switching: `auto / command / default`
- Connection test supports manual input of temporary `ttscmd` and temporary mode, which is convenient for on-site debugging
- Added detailed log switch, which can show/hide `ttscmd` and default link execution details
### v1.0.2 (2026-02-11)
- Adjusted TTS call order: priority `ttscmd(MiOT.doAction)`, fallback to default `MiNA.play(text)` after failure
- Built-in complete model `ttsFallbackCommands` mapping (including common models such as `LX04`), and supports user-defined override
- The account setting adds "View device list and select did", which can directly write back the configuration from the device list with one click, reducing the probability of filling in the wrong did
- Configuration template/automatic generation of configuration/README synchronously adds `speaker.ttsFallbackCommand` and `speaker.ttsFallbackCommands`
- Added active test mode: supports testing `ttscmd` link and default link (`MiNA.play`) separately
- Account settings add `ttscmd` editing entry: support modifying default commands and overriding commands by model
- Added TTS link mode: supports three modes of `auto / command / default`, users can manually switch
- Added detailed log switch: supports displaying/hiding the execution log of `ttscmd` and the default link
- Connection test supports manual input of temporary `ttscmd` and temporary link mode, which is convenient for on-site debugging
### v1.0.1 (2026-02-10)
- Fixed misjudgment of `pm2/npm/npx` detection under Windows (`.cmd` shim caused ENOENT/unexecutable problem)
- Fixed npm v10+ does not support `npm bin -g` causing global pm2 identification failure (use `npm prefix -g` fallback)
- TUI direction key/keypad number selection experience optimization (reduce redrawing lag, return no longer secondary enter)
- Webhook menu status display optimization (distinguish between embedded/PM2 resident to avoid misleading)
- Added: TUI view PM2 logs
- Added: Update check every time you start (available `XIAOI_NO_UPDATE_CHECK=1` to disable)
## Installation
### Global Installation (Recommended)
```bash
# npm
npm i -g xiaoii
# or pnpm
pnpm add -g xiaoii
```
After installation, you can use the `xiaoi` and `xiaoi-mcp` commands in any directory.
### Install from source code
```bash
git clone https://github.com/xvhuan/xiaoi.git
cd xiaoi
# npm
npm i
npm link
# or pnpm
pnpm install
pnpm link --global
```
## Configuration
### Automatic Creation (Installation/First Run)
When the installation is complete or `xiaoi` is executed for the first time, it will be created automatically:
- Directory: `~/.xiaoi/` (Windows is `%USERPROFILE%\.xiaoi\`)
- Configuration: `~/.xiaoi/config.json` (empty template)
### Manual Configuration
Edit `~/.xiaoi/config.json`:
```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 Mijia",
"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:
1. `~/.xiaoi/config.json`
2. (Fallback) `config.json` in the installation directory/project directory
Field description (commonly used):
| Field | Description |
|------|------|
| `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); the new version will be synchronized with `defaultDid` |
| `speaker.defaultDid` | Default speaker did (preferred 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` (ttscmd first, 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]`, priority call) |
| `speaker.ttsFallbackCommands` | Override `ttscmd` by model (such as `lx04:[5,1]`, `l09a:[3,1]`) |
| `webhook.host` | Listening address; can be set to `0.0.0.0` for external network access (pay attention to security) |
| `webhook.port` | Webhook port |
| `webhook.token` | Webhook authentication Token (optional; the resident Webhook will automatically generate and write back the configuration if left blank) |
Webhook default speaker priority:
1. `speaker.defaultDid`
2. `XIAOI_DEFAULT_DID` (environment variable)
3. `speaker.did` (compatible field)
> When the request body explicitly passes `did`, `did` must be in `speaker.speakers` and `enabled=true`, otherwise `400` is returned.
> Tip: In the TUI's "Account Settings", you can switch the TTS mode, modify the default/model `ttscmd`, and switch detailed logs; in the "Connection Test", you can manually enter temporary `ttscmd` and temporary mode for debugging.
> It is recommended to use passToken to log in. For passToken acquisition, refer to: [migpt-next/issues/4](https://github.com/idootop/migpt-next/issues/4)
## Usage
### TUI Interactive Interface
```bash
xiaoi
```
### CLI Command
```bash
# Send voice notification
xiaoi tts "Code compilation completed"
xiaoi tts Deployment completed, please check
# Set volume
xiaoi volume 30
# Send MiOT command (optional did)
xiaoi command 3 1 "[]"
xiaoi command 3 1 '[{"piid":1,"value":true}]' --did Living Room Xiaoai
# Read MiOT property (optional did)
xiaoi getprop 3 1 --did Living Room Xiaoai
# Check connection status
xiaoi status
# Help
xiaoi help
```
#### MiOT `cmd` Simple Use Case (`xiaomi.wifispeaker.oh2p`)
- Mijia specification (your speaker example): `https://home.miot-spec.com/spec/xiaomi.wifispeaker.oh2p`
- Common TTS `ttscmd` for this model: `[7,3]`
```bash
# 1) Simplest: Send TTS directly (recommended)
xiaoi tts "Master, this is an OH2P test" --did Your device did
# 2) Send TTS via MiOT cmd (oh2p common [7,3])
xiaoi command 7 3 "[\"Master, this is an OH2P cmd test\"]" --did Your device did
# 3) Read playing-state (SIID=3, PIID=1)
xiaoi getprop 3 1 --did Your device did
# Common meanings of return values: 1=playing, 0=stopped, 2=paused
```
Simple method to build `cmd` (`siid/aiid/params`):
1. Open the corresponding device specification page (such as the `oh2p` link above).
2. Find the `Service` / `Action` corresponding to the target capability and record `siid` and `aiid`.
3. Prepare `params` according to the parameter definition of the `Action`: use `[]` for no parameters, and assemble a JSON array according to the document order for parameters.
4. Execute: `xiaoi command <siid> <aiid> '<paramsJson>' [--did <Device did>]`.
> Note: The `params` structure of different actions is different, and must be based on 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:
```json
{
"servers": {
"xiaoi-voice-notify": {
"type": "stdio",
"command": "xiaoi-mcp"
}
}
}
```
#### Codex CLI (TOML)
```toml
[mcp_servers.xiaoi-voice-notify]
command = "xiaoi-mcp"
```
Tool list:
| Tool | Description |
|------|------|
| `notify` | Send voice notification (TTS) |
| `play_audio` | Play audio link |
| `set_volume` | Set volume |
| `do_action` | Send MiOT command (`siid/aiid/params`) |
| `get_property` | Read MiOT property value (`siid/piid`) |
> These 5 MCP tools all support the optional parameter `did`: if not passed, the default speaker is used, and if passed, the specified speaker is used.
### Webhook Service (HTTP Interface)
You can start Webhook in TUI, or you can use the PM2 resident method (see the next section).
When you configure `webhook.token` (or the token automatically generated by the resident Webhook), please carry the request header 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 it is not passed, it will be automatically routed according to the default priority.
```bash
# Send voice notification (optional did to specify the target speaker)
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"}'
# Play audio (optional did)
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"}'
# Set volume (optional did)
curl -X POST http://localhost:51666/webhook/volume \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"volume":50,"did":"Living Room Xiaoai"}'
# Send MiOT command (optional did)
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 Resident (PM2 One-Click Start)
If you want Webhook to run in the background on the server/computer for a long time (no need to hang up the terminal), use the built-in PM2 management command:
```bash
# One-click resident startup (run in the background)
xiaoi pm2 start
# One-click deployment (start + save)
xiaoi pm2 deploy
# View status
xiaoi pm2 status
# View pm2 process logs (stdout/stderr)
xiaoi pm2 logs 200
```
# View Webhook Log Files (~/.xiaoi/log/webhook.log, etc.)
xiaoi pm2 webhook-log 200
# Enable/Disable Public Access (Modify webhook.host)
xiaoi pm2 public on
xiaoi pm2 public off
# Stop/Delete
xiaoi pm2 stop
xiaoi pm2 delete
# Save Process List (Can be used with pm2 startup for auto-start on boot)
xiaoi pm2 save
# Generate Auto-Start Command (Usually Requires Administrator/Root Privileges)
xiaoi pm2 startup
```
> If your Webhook provides services to the public network, be sure to set `webhook.token` or use a firewall/reverse proxy for authentication to avoid being called arbitrarily.
### Docker Deployment (Containerized Webhook)
Encapsulate xiaoi Webhook into a Docker container for running, suitable for servers/NAS/cloud hosts. In basic scenarios, **no manual configuration file editing is required**. Simply 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)
```bash
# Pull the image
docker pull iusy/xiaoi:latest
# Start in one line
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 code or edit configuration files.
#### Or use Docker Compose
```bash
# 1. Download docker-compose.yml and .env template
curl -O https://raw.githubusercontent.com/xvhuan/xiaoi/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/xvhuan/xiaoi/main/.env.example
# 2. Edit .env, fill in your information
# XIAOI_USER_ID, XIAOI_PASS_TOKEN, XIAOI_DID (Optional XIAOI_DEFAULT_DID)
# 3. Start with one command
docker-compose up -d
```
The `.env` file should contain at least 3 items (optionally add the default speaker):
```env
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
```
> passToken acquisition method: [migpt-next/issues/4](https://github.com/idootop/migpt-next/issues/4)
#### Multi-Speaker Configuration (Two Methods)
When you have multiple speakers, it is recommended to write the target devices into `speaker.speakers` and set `speaker.defaultDid`.
Method 1: Enter the container and use `xiaoi` for interactive configuration (suitable for manual operation and maintenance)
```bash
# docker run deployment
docker exec -it xiaoi-webhook node /app/bin/xiaoi.js
# docker compose deployment
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".
Method 2: Directly edit the configuration file (suitable for batch/automation)
Configuration file path inside the container: `/root/.xiaoi/config.json` (provided that a persistent volume is mounted).
```json
{
"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 for the changes to take effect:
```bash
docker restart xiaoi-webhook
# Or
docker compose restart xiaoi-webhook
```
Calling rules:
1. If `did` is not passed in the request: use the default speaker (`defaultDid > XIAOI_DEFAULT_DID > speaker.did`)
2. If `did` is passed in the request: use the specified speaker
3. `did` must be in `speaker.speakers` and `enabled=true`
#### Pure Docker Commands (Without docker-compose)
```bash
# Build the image
docker build -t xiaoi-webhook .
# Run (pass environment variables directly with -e)
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
```
#### Environment Variable Overview
| Variable | Required | Description |
|------|------|------|
| `XIAOI_USER_ID` | ✅ | Xiaomi ID (number, view in Xiaomi account personal information) |
| `XIAOI_PASS_TOKEN` | ✅ | passToken (recommended login method) |
| `XIAOI_DID` | ✅ | Speaker name in Mi Home App (must be exactly the same) |
| `XIAOI_DEFAULT_DID` | | Default speaker did (fallback to `XIAOI_DID` when not filled) |
| `XIAOI_PASSWORD` | | Password login (not recommended, may be intercepted by security verification) |
| `XIAOI_TOKEN` | | Webhook authentication Token (automatically generated if left blank) |
| `XIAOI_PORT` | | Port number (default `51666`) |
| `XIAOI_TTS_MODE` | | TTS mode: `auto` / `command` / `default` |
| `XIAOI_VERBOSE_LOG` | | Verbose log: `true` / `false` |
#### Verify Service
```bash
# View container logs (check Token and startup status)
docker-compose logs
# Status check
curl http://localhost:51666/
# Send voice notification
curl -X POST http://localhost:51666/webhook/tts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token obtained from logs>" \
-d '{"text":"Docker deployment successful!"}'
```
#### Common Commands
```bash
# View logs
docker-compose logs -f
# Restart
docker-compose restart
# Stop
docker-compose down
# Update (after pulling the latest code)
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 (directly connects to speaker API)
│ ├── tui.js # TUI interactive interface
│ ├── webhook_server.js # Resident 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 building
├── docker-compose.yml # Docker Compose orchestration
├── docker-entrypoint.sh # Container startup entry script
├── .env.example # Docker environment variable template
└── README.md
```
## Common Issues
### What if login fails?
1. Confirm that `userId` is the Xiaomi ID (number), not a phone number or email
2. It is recommended to use `passToken` instead of password login
3. passToken acquisition reference: [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
## Acknowledgements
Built based on `@mi-gpt/next`.
https://github.com/idootop/migpt-next
## License
MIT, see `LICENSE` for details.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
Python tool for converting files and office documents to Markdown.
awesome-claude-skills
A curated list of awesome Claude Skills, resources, and tools for...
antigravity-awesome-skills
The Ultimate Collection of 130+ Agentic Skills for Claude...
openfang
Open-source Agent Operating System
memU
MemU is a memory framework for LLM and AI agents, organizing multimodal...