Content
<div align="center">
```
██╗ ██╗██╗ █████╗ ██████╗ ██╗
╚██╗██╔╝██║██╔══██╗██╔═══██╗██║
╚███╔╝ ██║███████║██║ ██║██║
██╔██╗ ██║██╔══██║██║ ██║██║
██╔╝ ██╗██║██║ ██║╚██████╔╝██║
╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝
```
# Xiaoii Voice Notify Tool
Send voice notifications to Xiaoai speaker via CLI / TUI / MCP / Webhook
[](https://nodejs.org/)
[](LICENSE)
[](https://www.npmjs.com/package/xiaoii)
</div>
---
## Features
- TUI Interactive Interface: Configure account, send notifications, manage Webhook/PM2
- CLI Commands: Suitable for script/automation scenarios
- MCP Server: For Codex/Cursor/VS Code AI programming assistant calls
- Webhook Service: Provides HTTP interface for third-party system integration
- Multi-Speaker Routing: Supports maintaining speaker list, setting default speaker, and temporary override by request did
- PM2 Resident: One-click background running Webhook (no need to hang terminal)
## Update Log
### v1.0.10 (2026-02-14)
- Added multi-speaker routing capability: Supports `speaker.defaultDid` and `speaker.speakers`, and compatible with old `speaker.did`
- Webhook four interfaces support body passing `did` (`tts/audio/volume/command`), not passing according to default priority routing
- Added TUI "Speaker List Management": Supports adding devices, setting defaults, enabling/disabling, and deleting
- Added CLI MiOT capability: `xiaoi command` (action) and `xiaoi getprop` (attribute reading)
- Added MCP tool: `do_action` and `get_property`, and unified support optional `did`
- Docker/document update: Added `XIAOI_DEFAULT_DID` and OH2P (`xiaomi.wifispeaker.oh2p`) simple `cmd` use case
### v1.0.9 (2026-02-14)
- Fixed `.mi.json` (login credential cache) write to current directory issue when executing CLI in any directory, now fixed to write to `~/.xiaoi/` directory
### v1.0.8 (2026-02-12)
- Fixed Docker container startup failure: Removed `pm2-runtime` unsupported `--log-date-format` parameter
- Fixed Docker build failure: `npm ci` changed to `npm install` (compatible with pnpm project)
- Document supplemented `XIAOI_TOKEN` environment variable example, convenient for users to customize Webhook authentication Token
### v1.0.7 (2026-02-12)
- Added Docker containerized deployment: Supports configuration via environment variables, no need to manually edit configuration files, one-line command to start Webhook service
- Added GitHub Actions CI/CD: Tag automatic release npm package + build and push Docker image (Docker Hub + GHCR, support amd64/arm64 dual architecture)
- Added `.env.example` environment variable template, reducing Docker deployment threshold
- Container using PM2 (`pm2-runtime`) management process, automatic health check and restart
### v1.0.6 (2026-02-12)
- Fixed `ttscmd` input parsing error: `[7,3]` no longer misparsed as `[0,7]`
- Optimized account setting display: Default `ttscmd` not set, automatically display current `did` parsed device command (automatic mapping)
### v1.0.5 (2026-02-12)
- Added `ttscmd` automatic mapping log output: Display `model / match / source / command`
- Detailed log unified increase `[XIAOI]` prefix, convenient for filtering and retrieval in PM2/console
### v1.0.4 (2026-02-12)
- Account setting page added automatic analysis of machine model and display "effective ttscmd mapping" by `did`
- Link debugging capability improvement: Support 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 temporary `ttscmd` and temporary mode, convenient for on-site debugging
- Added detailed log switch: Can display/hide `ttscmd` and default link execution details
### v1.0.2 (2026-02-11)
- Adjusted TTS call order: Prioritize `ttscmd(MiOT.doAction)`, fallback to default `MiNA.play(text)`
- Built-in complete machine model `ttsFallbackCommands` mapping (including `LX04` and other common models), and support user-defined coverage
- Account setting added "View device list and select did", can directly write back configuration from device list with one click, reducing did fill-in error probability
- Configuration template/automatic generation configuration/README added `speaker.ttsFallbackCommand` and `speaker.ttsFallbackCommands`
- Added active test mode: Supports testing `ttscmd` link and default link (`MiNA.play`) separately
- Account setting added `ttscmd` editing entry: Supports modifying default command and covering command by model
- Added TTS link mode: Supports `auto / command / default` three modes, user can manually switch
- Added detailed log switch: Supports displaying/hiding `ttscmd` and default link execution log
- Connection test supports manual input temporary `ttscmd` and temporary link mode, convenient for on-site debugging
### v1.0.1 (2026-02-10)
- Fixed Windows under `pm2/npm/npx` detection misjudgment (`.cmd` shim caused ENOENT/inexecutable issue)
- Fixed npm v10+ not supporting `npm bin -g` caused global pm2 recognition failure (changed to `npm prefix -g` fallback)
- TUI direction key/small keyboard number selection experience optimization (reduce redraw stuttering, return no longer secondary enter)
- Webhook menu status display optimization (distinguish embedded/PM2 resident, avoid misleading)
- Added: TUI view PM2 log
- Added: Each startup will detect updates (available `XIAOI_NO_UPDATE_CHECK=1` disable)
## Installation
### Global Installation (Recommended)
```bash
# npm
npm i -g xiaoii
# or pnpm
pnpm add -g xiaoii
```
After installation, you can use `xiaoi` and `xiaoi-mcp` commands in any directory.
### Install from Source
```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)
After installation or first execution of `xiaoi`, it will automatically create:
- 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": "Speaker name in Mi Home",
"defaultDid": "Default speaker did (optional)",
"speakers": [
{
"did": "Speaker did",
"name": "Living room Xiaoi",
"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 search priority:
1. `~/.xiaoi/config.json`
2. (Fallback) Installation directory/project directory under `config.json`
Field description (common):
| Field | Description |
|------|------|
| `speaker.userId` | Xiaomi ID (number, view in Xiaomi account personal information) |
| `speaker.password` | Xiaomi account password (may cause security verification failure) |
| `speaker.passToken` | passToken (recommended) |
| `speaker.did` | Compatible field (old version default device); new version will synchronize with `defaultDid` |
| `speaker.defaultDid` | Default speaker did (priority use when not passing did in request) |
| `speaker.speakers` | Added speaker list (`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`), control whether to print link execution details |
| `speaker.ttsFallbackCommand` | Default `ttscmd` (default `[5,1]`, priority call) |
| `speaker.ttsFallbackCommands` | Cover `ttscmd` by model (e.g., `lx04:[5,1]`, `l09a:[3,1]`) |
| `webhook.host` | Listening address; need external access can set to `0.0.0.0` (note security) |
| `webhook.port` | Webhook port |
| `webhook.token` | Webhook authentication Token (optional; resident Webhook will automatically generate and write back configuration) |
Webhook default speaker priority:
1. `speaker.defaultDid`
2. `XIAOI_DEFAULT_DID` (environment variable)
3. `speaker.did` (compatible field)
> Request body explicitly pass `did` , `did` must be in `speaker.speakers` and `enabled=true`, otherwise return `400`.
> Tip: In TUI's "Account Settings", you can switch TTS mode, modify default/model `ttscmd`, and detailed log switch; in "Connection Test", you can manually input temporary `ttscmd` and temporary mode for debugging.
> Recommended use passToken login. passToken acquisition reference: [migpt-next/issues/4](https://github.com/idootop/migpt-next/issues/4)
## Usage
### TUI Interactive Interface
```bash
xiaoi
```
### CLI Commands
```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 Xiaoi
# Read MiOT attribute (optional did)
xiaoi getprop 3 1 --did Living room Xiaoi
# Check connection status
xiaoi status
# Help
xiaoi help
```
#### MiOT `cmd` simple use case (`xiaomi.wifispeaker.oh2p`)
- Mi Home specification book (your speaker example): `https://home.miot-spec.com/spec/xiaomi.wifispeaker.oh2p`
- The model common TTS `ttscmd`: `[7,3]`
```bash
# 1) Simplest: Directly send TTS (recommended)
xiaoi tts "Master, this is an OH2P test" --did Your device did
# 2) Walk MiOT cmd way send TTS (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
# Return value common meaning: 1=playing, 0=stop, 2=pause
```
Build `cmd` (`siid/aiid/params`) simple method:
1. Open the corresponding device specification page (e.g., above `oh2p` link).
2. Find the target capability corresponding `Service` / `Action`, record `siid` and `aiid`.
3. Prepare `params` according to the `Action` parameter definition: No parameter use `[]`, have parameter assemble JSON array according to document order.
4. Execute: `xiaoi command <siid> <aiid> '<paramsJson>' [--did <device did>]`.
> Description: Different actions `params` structure different, must take specification book in action parameter definition as standard.
### MCP Server (AI Programming Assistant Integration)
> Need to install and run `xiaoi` to complete account configuration first.
#### 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 attribute value (`siid/piid`) |
| `notify` |
> These 5 MCP tools all support optional parameter `did`: not pass go default speaker, pass specified speaker.
### Webhook Service (HTTP Interface)
You can start Webhook in TUI or use PM2 resident mode (see below).
When you configured `webhook.token` (or use resident Webhook automatically generated token), please bring request header:
- `Authorization: Bearer <token>`
- Or `X-Xiaoi-Token: <token>`
You can also pass `did` in request body specify target speaker; not pass automatically route according to priority.
```bash
# Send voice notification (optional did specify 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 Xiaoi"}'
# 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 Xiaoi"}'
# 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 Xiaoi"}'
# 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 Xiaoi"}'
```
### Webhook Resident (PM2 One-Click Start)
If you want Webhook run in server/computer background (no need to hang terminal), use built-in PM2 management command:
```bash
# One-click resident start (background running)
xiaoi pm2 start
# One-click deploy (start + save)
xiaoi pm2 deploy
# View status
xiaoi pm2 status
# View pm2 process log (stdout/stderr)
xiaoi pm2 logs 200
```
# View Webhook Log Files (e.g., ~/.xiaoi/log/webhook.log)
xiaoi pm2 webhook-log 200
# Toggle Public Network Access (modify webhook.host)
xiaoi pm2 public on
xiaoi pm2 public off
# Stop/Delete
xiaoi pm2 stop
xiaoi pm2 delete
# Save Process List (cooperate with pm2 startup to achieve boot auto-start)
xiaoi pm2 save
# Generate Boot Auto-start Command (usually requires administrator/Root privileges)
xiaoi pm2 startup
> If your Webhook needs to provide services to the external network, be sure to set `webhook.token` or cooperate with the firewall/reverse proxy to do authentication, to avoid being called arbitrarily.
### Docker Deployment (Containerized Webhook)
Encapsulate xiaoi Webhook into a Docker container to run, suitable for servers/NAS/cloud hosts. In the basic scenario, **no need to manually edit configuration files**, just fill in environment variables to start (see "Multi-Speaker Configuration" below for multi-speaker scenarios).
#### Quick Start (directly pull image, no need to clone)
```bash
# Pull image
docker pull iusy/xiaoi:latest
# Start with 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, no need to edit configuration files.
#### Or Use Docker Compose
```bash
# 1. Download docker-compose.yml and .env templates
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 line
docker-compose up -d
```
`.env` file suggests filling in at least 3 items (optional add 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 all target devices into `speaker.speakers` and set `speaker.defaultDid`.
Method 1: Enter the container and use `xiaoi` 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 TUI: `Account Settings -> Speaker List Management`, complete "Add Speaker/Set Default Speaker".
Method 2: Directly edit configuration files (suitable for batch/automation)
Container internal configuration file path: `/root/.xiaoi/config.json` (premise is that 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 container to take effect:
```bash
docker restart xiaoi-webhook
# or
docker compose restart xiaoi-webhook
```
Invocation rules:
1. Request does not pass `did`: go to default speaker (`defaultDid > XIAOI_DEFAULT_DID > speaker.did`)
2. Request passes `did`: go to specified speaker
3. `did` must be in `speaker.speakers` and `enabled=true`
#### Pure Docker Command (no docker-compose)
```bash
# Build image
docker build -t xiaoi-webhook .
# Run (directly use -e to pass environment variables)
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 List
| 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` if not filled) |
| `XIAOI_PASSWORD` | | Password login (not recommended, may be intercepted by security verification) |
| `XIAOI_TOKEN` | | Webhook authentication Token (leave blank to automatically generate) |
| `XIAOI_PORT` | | Port number (default `51666`) |
| `XIAOI_TTS_MODE` | | TTS mode: `auto` / `command` / `default` |
| `XIAOI_VERBOSE_LOG` | | Detailed log: `true` / `false` |
#### Verify Service
```bash
# View container logs (view 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 <get token 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 (pull latest code and then)
git pull
docker-compose up -d --build
```
## Project Structure
```
xiaoi/
├── bin/
│ └── xiaoi.js # CLI + TUI entrance
├── lib/
│ ├── config.js # User directory configuration management (automatically generate ~/.xiaoi/config.json)
│ ├── speaker.js # Core module (directly connect speaker API)
│ ├── tui.js # TUI interactive interface
│ ├── webhook_server.js # Resident Webhook service entrance (can cooperate with PM2)
│ └── pm2.js # PM2 one-key management package
├── mcp_server.js # MCP Server
├── config.example.json # Configuration template
├── Dockerfile # Docker image construction
├── docker-compose.yml # Docker Compose orchestration
├── docker-entrypoint.sh # Container startup entrance script
├── .env.example # Docker environment variable template
└── README.md
```
## Frequently Asked Questions
### What if login fails?
1. Confirm `userId` is Xiaomi ID (number), not mobile phone number or email
2. Recommended to use `passToken` instead of password login
3. Refer to [migpt-next/issues/4](https://github.com/idootop/migpt-next/issues/4) for passToken acquisition
### Can't find device?
- Confirm `did` is exactly the same as speaker name in Mi Home App
## Acknowledgements
Built 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...
claude-context-mode
claude-context-mode plugin reduces MCP context bloat, saving up to 99% of tokens.
context-mode
MCP is the protocol for tool access. We're the virtualization layer for context.