# openclaw-xiaomi-home

Adopt this skill: https://proskills.md/skills/other/canmaxfire-openclaw-xiaomi-home

Page: https://proskills.md/skills/other/canmaxfire-openclaw-xiaomi-home
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: SKILL.md
size: 3323
sha: 9f9dd4b1040f3205ba54ee8bd33c95efff4baea6
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/SKILL.md

````text
---
name: openclaw-xiaomi-home
description: Your OpenClaw can now control your smart home. It connects to your Xiaomi/Mijia devices through Home Assistant and lets you control lights, AC, locks, sensors and more with plain text or voice commands. Ask things like "turn on the living room light", "set AC to 26 degrees", "is the front door locked?", or "what's the bedroom temperature?" and your AI assistant handles it instantly. Triggers: "turn on the light", "set AC temperature", "is the door locked?", "check the temperature", "lock the door", "turn everything off".

NOTE: Requires Home Assistant (Docker, localhost:8123) and Xiaomi devices paired with Mi Home.

Required credentials:
- `HA_TOKEN`: Long-Lived Access Token from Home Assistant, stored in `.env` (gitignored). Used as Bearer token to authenticate MCP server requests. Never sent to any external service.

Security:
- MCP server only accepts requests from localhost
- All MCP calls require "Authorization: Bearer <HA_TOKEN>" header
- HA_TOKEN is never transmitted to any service other than your local Home Assistant
- No data collection or exfiltration — purely local device control
---

# Xiaomi Home Control

**Your OpenClaw can now control your smart home.** Connect to Xiaomi/Mijia devices through Home Assistant and control everything with plain text or voice.

## What It Does

Just tell your AI what you want:

```
"Turn on the living room light"
"Set bedroom AC to 26 degrees"
"Is the front door locked?"
"What's the temperature?"
"Lock all doors"
"Turn off all lights"
```

Done. No apps. No switching. No pointing and clicking.

## What You Can Control

| Category | Examples |
|----------|---------|
| 💡 Lights | Turn on/off, adjust brightness |
| ❄️ Air Conditioning | Set temperature, mode, fan speed |
| 🔐 Door Locks | Lock/unlock from anywhere |
| 🌡️ Sensors | Temperature, humidity, motion |
| 💨 Fans & Humidifiers | On/off, speed control |
| 🪟 Blinds & Curtains | Open/close |
| 🤖 Robot Vacuums | Start, stop, return to charger |

Works with **1837+ Xiaomi/Mijia devices** via the official Xiaomi Home integration.

## Setup (One Time)

```bash
# 1. Start Home Assistant
docker compose up -d

# 2. Connect your Xiaomi devices
# (open localhost:8123 → add Xiaomi Home integration)

# 3. Install the control server
cd scripts/ha-mcp-server && npm install

# 4. Done
```

Full guide in README.md.

## How It Works

```
You → "Turn on the light"
  ↓
OpenClaw AI
  ↓
Home Assistant (your local server)
  ↓
Xiaomi Device
```

All on **your local network** — no cloud, no subscription.

## Why It's Better

| Before | After |
|--------|-------|
| Open app → find device → tap | Just say what you want |
| One device at a time | Control everything at once |
| Can't do it remotely | Through AI assistant from anywhere |
| Remember which app for which device | Describe it in plain English |

## Privacy

- Everything stays on your home network
- No cloud dependency after setup
- No data collection or tracking
- Runs on your own hardware

## Example Commands

```
"Turn on the living room light"
"Dim the bedroom to 30%"
"Set AC to cool mode at 24 degrees"
"Is the door locked?"
"Lock the front door"
"What's the living room temperature?"
"Turn off all lights"
"Open the blinds"
"Start the vacuum"
```
````

## File: README.md
size: 5755
sha: fc336c6ac863e650460a763e83c9a0e47a042014
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/README.md

````text
# openclaw-xiaomi-home

[![Version](https://img.shields.io/badge/version-v1.2.0-blue.svg)](https://github.com/canmaxfire/openclaw-xiaomi-home)
[![ClawHub](https://img.shields.io/badge/ClawHub-openclaw--xiaomi--home-green.svg)](https://clawhub.com/openclaw-xiaomi-home)

**Your OpenClaw can now control your smart home.** Connect to Xiaomi/Mijia devices through Home Assistant and control everything with plain text or voice.

## The Problem It Solves

Smart home devices are scattered across apps:
- Open Mi Home app → find device → tap button

**This skill fixes that.** Just tell your AI what you want:

```
"Turn on the living room light"
"Set bedroom AC to 26 degrees"
"Is the front door locked?"
```

Done.

## What You Can Control

| Category | Examples |
|----------|----------|
| 💡 Lights | On/off, brightness |
| ❄️ Air Conditioning | Temperature, mode, fan speed |
| 🔐 Door Locks | Lock/unlock from anywhere |
| 🌡️ Sensors | Temperature, humidity, motion |
| 💨 Fans & Humidifiers | On/off, speed |
| 🪟 Blinds & Curtains | Open/close |
| 🤖 Robot Vacuums | Start, stop, return to base |
| 🔊 **XiaoAI Speakers** | **Voice announcements (TTS)** |

Works with **1837+ Xiaomi/Mijia devices**.

## How It Works

```
You → "Turn on the light"
  ↓
OpenClaw AI
  ↓
Home Assistant (local)
  ↓
Xiaomi Device
```

All stays **on your local network** — no cloud, no subscription.

## Installation

```bash
# ClawHub
clawhub install openclaw-xiaomi-home

# Manual
git clone https://github.com/canmaxice-maker/openclaw-xiaomi-home.git
mv openclaw-xiaomi-home ~/.openclaw/skills/
```

## Setup (One Time)

### 1. Start Home Assistant

```bash
cd ~/.openclaw/skills/openclaw-xiaomi-home
docker compose up -d
```

Open http://localhost:8123 and create your account.

### 2. Add Xiaomi Devices

1. **Settings → Devices & Services → Add Integration**
2. Search **Xiaomi Home** → login with Xiaomi account
3. Wait for devices to import

### 3. Create Access Token

1. HA profile → **Long-Lived Access Tokens** → Create Token
2. Copy and save it

### 4. Install Control Server

```bash
cd ~/.openclaw/skills/openclaw-xiaomi-home/scripts/ha-mcp-server
npm install && cp .env.example .env
# Edit .env with your HA_URL and HA_TOKEN
```

### 5. Start

```bash
launchctl load ~/.openclaw/skills/openclaw-xiaomi-home/scripts/ha-mcp-server/ai.openclaw.ha-mcp.plist
```

### 6. Test

```bash
node src/call-tool.mjs ping_ha
node src/call-tool.mjs list_all_devices '{"domain":"light"}'
```

## Usage

Just talk to your AI assistant:

```
"Turn on the living room light"
"Set bedroom AC to 26 degrees"
"Is the front door locked?"
"What's the temperature?"
"Lock all doors"
"Turn off all lights"
"Open the blinds"
"Start the vacuum"
```

## XiaoAI TTS Voice Announcement

Use XiaoAI speakers as a voice announcement system — your AI can "speak" reminders and alerts through the speaker.

**⚠️ Note:** Entity IDs are device-specific. You must find your own XiaoAI speaker's `notify` entity in Home Assistant (see [Finding Your Entity IDs](#finding-your-entity-ids) below).

**Python script:** `~/.openclaw/workspace/scripts/xiaoai_announce.py`

```bash
# Find your notify entity ID first (see below), then:
python3 ~/.openclaw/workspace/scripts/xiaoai_announce.py "06:30 该起床了" "notify.YOUR_ENTITY_ID"
```

**MCP tool:** `xiaoai_announce(message, entity_id?)`

**Use cases:** Health reminders, smart home alerts, agent notifications.

### Finding Your Entity IDs

XiaoAI speaker entity IDs are unique to your account. To find them:

1. Open Home Assistant → **Developer Tools → States**
2. Filter by `notify` and look for entities containing `xiaoai`, `play_text`, or `speaker`
3. Copy the entity ID (format: `notify.xiaomi_cn_XXXXXXXXXX_xxxx`)

---

## Security & Privacy

### Required Credentials

| Credential | Purpose | Storage |
|------------|---------|---------|
| `HA_TOKEN` | Home Assistant Long-Lived Access Token | `.env` file (gitignored) |

### Security Model

| Concern | Mitigation |
|---------|------------|
| Unauthenticated access | MCP server requires `Authorization: Bearer <HA_TOKEN>` on every request |
| Cross-origin abuse | CORS restricted to `http://localhost` only |
| LAN access | Server binds to localhost, not all interfaces |
| Token exfiltration | HA_TOKEN is only sent to `localhost:8123` — never to external services |
| Credential leakage | `.env` is in `.gitignore` — never committed to git |

### What This Skill Does NOT Do

- ❌ Does NOT transmit your HA token to any external service
- ❌ Does NOT accept requests from non-localhost origins
- ❌ Does NOT collect, log, or transmit usage data
- ❌ Does NOT use any LLM or AI processing
- ❌ Does NOT expose unauthenticated endpoints

## Troubleshooting

### HA won't start
```bash
docker logs homeassistant
```

### Devices not showing
- Use **China mainland** Xiaomi server in Mi Home
- Restart the homeassistant container

### Token not working
- Re-create the Long-Lived Access Token in HA
- Update `HA_TOKEN` in `.env`

## Credits

- [Home Assistant](https://www.home-assistant.io/)
- [ha_xiaomi_home](https://github.com/nickoowen/ha-xiaomi-home)
- [OpenClaw](https://openclaw.ai/)

## Changelog

| Version | Date | Changes |
|---------|------|---------|
| [v1.2.0](https://github.com/canmaxice-maker/openclaw-xiaomi-home/releases/tag/v1.2.0) | 2026-04-24 | Added XiaoAI TTS voice announcement via `xiaoai_announce` tool |
| [v1.1.0](https://github.com/canmaxice-maker/openclaw-xiaomi-home/releases/tag/v1.1.0) | 2026-04-21 | Plain language rewrite — user benefit focus |
| [v1.0.0](https://github.com/canmaxice-maker/openclaw-xiaomi-home/releases/tag/v1.0.0) | 2026-04-21 | Initial release |

## License

MIT
````

## File: scripts/ha-mcp-server/.env.example
size: 137
sha: 77c671b22c29478efbb504539d1635ca6ebf8ee7
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/.env.example

```text
# Home Assistant Configuration
HA_URL=http://localhost:8123
HA_TOKEN=your_long_lived_access_token_here

# Server Configuration
PORT=3002
```

## File: scripts/ha-mcp-server/ai.openclaw.ha-mcp.plist
size: 986
sha: 67ac4f04ecd88c51dbd7359a80560768bd5c2f73
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/ai.openclaw.ha-mcp.plist

```text
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>ai.openclaw.ha-mcp</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/Users/nanali/.openclaw/skills/openclaw-xiaomi-home/scripts/ha-mcp-server/src/http-server.mjs</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>HOME</key>
        <string>/Users/nanali</string>
    </dict>
    <key>WorkingDirectory</key>
    <string>/Users/nanali/.openclaw/skills/openclaw-xiaomi-home/scripts/ha-mcp-server</string>
    <key>StandardOutPath</key>
    <string>/Users/nanali/.openclaw/logs/ha-mcp.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/nanali/.openclaw/logs/ha-mcp.err.log</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
```

## File: scripts/ha-mcp-server/package-lock.json
size: 58051
sha: 1c2827c3c1623ce93d46223052377cfb9df6a12e
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/package-lock.json

Content is not inlined in the catalog. Read the upstream file: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/package-lock.json

## File: scripts/ha-mcp-server/package.json
size: 550
sha: 38e5be15861c9a3facb2b1ce6f00dfdccb12e8f7
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/package.json

```text
{
  "name": "ha-mcp-server",
  "version": "1.0.0",
  "description": "MCP server for Home Assistant - controls Xiaomi devices via natural language",
  "type": "module",
  "main": "src/http-server.mjs",
  "scripts": {
    "start": "node src/http-server.mjs",
    "start:stdio": "node src/index.mjs",
    "test": "node src/call-tool.mjs ping_ha"
  },
  "keywords": [
    "home-assistant",
    "xiaomi",
    "mcp",
    "smart-home",
    "openclaw"
  ],
  "author": "Nana / OpenClaw",
  "license": "MIT",
  "dependencies": {
    "dotenv": "^16.4.5"
  }
}
```

## File: scripts/ha-mcp-server/src/call-tool.mjs
size: 2039
sha: 41c41bdbf16fb4e18d566bd59d0057e631db18e4
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/src/call-tool.mjs

```text
/**
 * HA MCP Client - Call tools via HTTP JSON-RPC
 * Usage: node call-tool.mjs <tool_name> [args_json]
 */

import http from "http";
import { readFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const envPath = path.resolve(__dirname, "../.env");
const env = {};
try {
  const lines = readFileSync(envPath, "utf8").split("\n");
  for (const line of lines) {
    const [key, ...vals] = line.split("=");
    if (key && vals.length) env[key.trim()] = vals.join("=").trim();
  }
} catch (e) { /* no .env */ }

const HOST = process.env.HA_MCP_HOST || "localhost";
const PORT = process.env.HA_MCP_PORT || "3002";
const HA_TOKEN = env.HA_TOKEN || "";

function rpc(method, params = {}) {
  return new Promise((resolve, reject) => {
    const body = JSON.stringify({ jsonrpc: "2.0", id: 1, method, params });
    const headers = {
      "Content-Type": "application/json",
      "Content-Length": Buffer.byteLength(body),
      "Authorization": `Bearer ${HA_TOKEN}`,
    };
    const options = { hostname: HOST, port: PORT, path: "/", method: "POST", headers };
    const req = http.request(options, res => {
      let data = "";
      res.on("data", chunk => data += chunk);
      res.on("end", () => {
        try {
          const json = JSON.parse(data);
          if (json.error) reject(new Error(json.error.message));
          else resolve(json.result);
        } catch (e) { reject(e); }
      });
    });
    req.on("error", reject);
    req.write(body);
    req.end();
  });
}

const tool = process.argv[2];
const args = process.argv[3] ? JSON.parse(process.argv[3]) : {};

if (!tool) {
  console.error("Usage: node call-tool.mjs <tool_name> [args_json]");
  process.exit(1);
}

rpc("tools/call", { name: tool, arguments: args })
  .then(result => {
    const text = result.content?.[0]?.text || JSON.stringify(result);
    console.log(text);
  })
  .catch(err => {
    console.error("Error:", err.message);
    process.exit(1);
  });
```

## File: scripts/ha-mcp-server/src/http-server.mjs
size: 11886
sha: caaa58b5c872ef7f9b33fbeca615af9dd76250c8
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/src/http-server.mjs

```text
/**
 * Home Assistant MCP Server - Simple HTTP JSON-RPC
 * Compatible with any MCP client via HTTP POST
 */

import http from "http";
import { readFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const envPath = path.resolve(__dirname, "../.env");
const env = {};
try {
  const lines = readFileSync(envPath, "utf8").split("\n");
  for (const line of lines) {
    const [key, ...vals] = line.split("=");
    if (key && vals.length) env[key.trim()] = vals.join("=").trim();
  }
} catch (e) { /* no .env */ }

const HA_URL = env.HA_URL || "http://localhost:8123";
const HA_TOKEN = env.HA_TOKEN || "";
const PORT = parseInt(env.PORT || "3002", 10);
const ALLOWED_ORIGIN = "http://localhost"; // Only allow localhost for CORS

// Simple bearer token auth: must send "Authorization: Bearer <HA_TOKEN>" header
function checkAuth(req) {
  const auth = req.headers["authorization"] || req.headers["Authorization"];
  if (!auth || !auth.startsWith("Bearer ")) return false;
  const token = auth.slice("Bearer ".length);
  return token === HA_TOKEN;
}

// ============================================================================
// HA API
// ============================================================================

async function haFetch(endpoint, options = {}) {
  const url = `${HA_URL}/api${endpoint}`;
  const response = await fetch(url, {
    ...options,
    headers: {
      "Authorization": `Bearer ${HA_TOKEN}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });
  if (!response.ok) throw new Error(`HA API error: ${response.status}`);
  return response.json();
}

const getEntity = (id) => haFetch(`/states/${id}`);
const callService = (d, s, data) => haFetch(`/services/${d}/${s}`, { method: "POST", body: JSON.stringify(data) });
const getAllEntities = () => haFetch("/states");

// ============================================================================
// Tool Implementations
// ============================================================================

const tools = {
  // Lights
  light_turn_on: async ({ entity_id, brightness }) => {
    const data = { entity_id };
    if (brightness !== undefined) data.brightness = brightness;
    await callService("light", "turn_on", data);
    return textResult(`Light ${entity_id} turned on${brightness ? ` (brightness: ${brightness})` : ""}`);
  },
  light_turn_off: async ({ entity_id }) => {
    await callService("light", "turn_off", { entity_id });
    return textResult(`Light ${entity_id} turned off`);
  },
  get_light_state: async ({ entity_id }) => {
    const e = await getEntity(entity_id);
    return textResult(`${e.entity_id}: ${e.state} (brightness: ${e.attributes.brightness ?? "N/A"})`);
  },

  // Climate / AC
  climate_set_temperature: async ({ entity_id, temperature }) => {
    await callService("climate", "set_temperature", { entity_id, temperature });
    return textResult(`${entity_id} set to ${temperature}°C`);
  },
  climate_set_mode: async ({ entity_id, mode }) => {
    await callService("climate", "set_hvac_mode", { entity_id, hvac_mode: mode });
    return textResult(`${entity_id} mode set to ${mode}`);
  },
  get_climate_state: async ({ entity_id }) => {
    const e = await getEntity(entity_id);
    const a = e.attributes;
    return textResult(`${e.entity_id}: ${e.state}, temp: ${a.current_temperature}°C, target: ${a.temperature}°C`);
  },

  // Fans
  fan_turn_on: async ({ entity_id }) => {
    await callService("fan", "turn_on", { entity_id });
    return textResult(`${entity_id} turned on`);
  },
  fan_turn_off: async ({ entity_id }) => {
    await callService("fan", "turn_off", { entity_id });
    return textResult(`${entity_id} turned off`);
  },
  fan_set_speed: async ({ entity_id, speed }) => {
    await callService("fan", "set_percentage", { entity_id, percentage: speed });
    return textResult(`${entity_id} speed set to ${speed}%`);
  },
  get_fan_state: async ({ entity_id }) => {
    const e = await getEntity(entity_id);
    return textResult(`${e.entity_id}: ${e.state} (speed: ${e.attributes.percentage ?? "N/A"}%)`);
  },

  // Switches
  switch_turn_on: async ({ entity_id }) => { await callService("switch", "turn_on", { entity_id }); return textResult(`${entity_id} turned on`); },
  switch_turn_off: async ({ entity_id }) => { await callService("switch", "turn_off", { entity_id }); return textResult(`${entity_id} turned off`); },
  get_switch_state: async ({ entity_id }) => { const e = await getEntity(entity_id); return textResult(`${e.entity_id}: ${e.state}`); },

  // Locks
  lock_lock: async ({ entity_id }) => { await callService("lock", "lock", { entity_id }); return textResult(`${entity_id} locked`); },
  lock_unlock: async ({ entity_id }) => { await callService("lock", "unlock", { entity_id }); return textResult(`${entity_id} unlocked`); },
  get_lock_state: async ({ entity_id }) => { const e = await getEntity(entity_id); return textResult(`${e.entity_id}: ${e.state}`); },

  // Sensors
  get_sensor_reading: async ({ entity_id }) => { const e = await getEntity(entity_id); return textResult(`${e.entity_id}: ${e.state} ${e.attributes.unit_of_measurement ?? ""}`); },
  list_sensors: async () => {
    const entities = await getAllEntities();
    const sensors = entities.filter(e => e.entity_id.startsWith("sensor.") || e.entity_id.startsWith("binary_sensor."));
    const list = sensors.slice(0, 30).map(e => `  - ${e.entity_id}: ${e.state} ${e.attributes.unit_of_measurement ?? ""}`).join("\n");
    return textResult(`Sensors (${sensors.length} total):\n${list}`);
  },

  // Humidifier
  humidifier_turn_on: async ({ entity_id }) => { await callService("humidifier", "turn_on", { entity_id }); return textResult(`${entity_id} turned on`); },
  humidifier_turn_off: async ({ entity_id }) => { await callService("humidifier", "turn_off", { entity_id }); return textResult(`${entity_id} turned off`); },
  get_humidifier_state: async ({ entity_id }) => { const e = await getEntity(entity_id); return textResult(`${e.entity_id}: ${e.state}, humidity: ${e.attributes.current_humidity ?? "N/A"}%`); },

  // Cover / Blinds
  cover_open: async ({ entity_id }) => { await callService("cover", "open_cover", { entity_id }); return textResult(`${entity_id} opened`); },
  cover_close: async ({ entity_id }) => { await callService("cover", "close_cover", { entity_id }); return textResult(`${entity_id} closed`); },
  get_cover_state: async ({ entity_id }) => { const e = await getEntity(entity_id); return textResult(`${e.entity_id}: ${e.state}`); },

  // Vacuum
  vacuum_start: async ({ entity_id }) => { await callService("vacuum", "start", { entity_id }); return textResult(`${entity_id} started`); },
  vacuum_stop: async ({ entity_id }) => { await callService("vacuum", "stop", { entity_id }); return textResult(`${entity_id} stopped`); },
  vacuum_return_to_base: async ({ entity_id }) => { await callService("vacuum", "return_to_base", { entity_id }); return textResult(`${entity_id} returning to base`); },

  // Devices
  list_all_devices: async (args = {}) => {
    const entities = await getAllEntities();
    const filtered = args.domain ? entities.filter(e => e.entity_id.startsWith(args.domain + ".")) : entities;
    const list = filtered.slice(0, 50).map(e => `  - ${e.entity_id}: ${e.state}`).join("\n");
    return textResult(`${filtered.length} entities (showing 50):\n${list}`);
  },
  get_device_state: async ({ entity_id }) => {
    const e = await getEntity(entity_id);
    return textResult(`${e.entity_id}\nState: ${e.state}\nAttributes: ${JSON.stringify(e.attributes, null, 2)}`);
  },

  // Scene
  trigger_scene: async ({ entity_id }) => { await callService("scene", "turn_on", { entity_id }); return textResult(`Scene ${entity_id} triggered`); },

  // XiaoAI TTS Announce
  xiaoai_announce: async ({ message, entity_id }) => {
    const target = entity_id || "notify.xiaomi_cn_501481700_l16a_play_text_a_7_3";
    await haFetch("/services/notify/send_message", {
      method: "POST",
      body: JSON.stringify({ message, entity_id: target }),
    });
    return textResult(`XiaoAI announced: ${message}`);
  },

  // HA status
  ping_ha: async () => { await haFetch("/config"); return textResult("Home Assistant is reachable and running"); },
  get_ha_config: async () => { const c = await haFetch("/config"); return textResult(`Home Assistant version: ${c.version}`); },
};

function textResult(text) {
  return { content: [{ type: "text", text }] };
}

// ============================================================================
// HTTP Server
// ============================================================================

const server = http.createServer(async (req, res) => {
  if (req.method === "OPTIONS") {
    res.writeHead(200, { "Access-Control-Allow-Origin": ALLOWED_ORIGIN, "Access-Control-Allow-Methods": "POST, GET, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" });
    res.end();
    return;
  }
  // Reject requests from non-localhost origins
  const origin = req.headers["origin"] || req.headers["Origin"];
  if (origin && !origin.startsWith(ALLOWED_ORIGIN)) {
    res.writeHead(403, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: "Forbidden: cross-origin request denied" }));
    return;
  }
  // Require authentication for all tool calls
  if (req.method === "POST" && !checkAuth(req)) {
    res.writeHead(401, { "Content-Type": "application/json", "WWW-Authenticate": "Bearer" });
    res.end(JSON.stringify({ error: "Unauthorized: valid Bearer token required" }));
    return;
  }
  if (req.method === "GET" && req.url === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ status: "ok" }));
    return;
  }
  if (req.method !== "POST") { res.writeHead(404); res.end(); return; }

  let body = "";
  req.on("data", chunk => body += chunk);
  req.on("end", async () => {
    try {
      const json = JSON.parse(body);
      const jsonrpc = json.jsonrpc || "2.0";
      const method = json.method;
      const params = json.params || {};
      const responseId = json.id;

      if (method === "tools/list") {
        const toolList = Object.keys(tools).map(name => ({ name, description: "", inputSchema: { type: "object" } }));
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ jsonrpc, id: responseId, result: { tools: toolList } }));
        return;
      }
      if (method === "tools/call") {
        const toolName = params.name;
        const args = params.arguments || {};
        if (!tools[toolName]) {
          res.writeHead(200, { "Content-Type": "application/json" });
          res.end(JSON.stringify({ jsonrpc, id: responseId, error: { code: -32601, message: `Tool ${toolName} not found` } }));
          return;
        }
        try {
          const result = await tools[toolName](args);
          res.writeHead(200, { "Content-Type": "application/json" });
          res.end(JSON.stringify({ jsonrpc, id: responseId, result }));
        } catch (err) {
          res.writeHead(200, { "Content-Type": "application/json" });
          res.end(JSON.stringify({ jsonrpc, id: responseId, error: { code: -32603, message: err.message } }));
        }
        return;
      }
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ jsonrpc, id: responseId, error: { code: -32601, message: "Method not found" } }));
    } catch (e) {
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }));
    }
  });
});

server.listen(PORT, () => {
  console.error(`HA MCP Server running on http://localhost:${PORT}`);
});
```

## File: scripts/ha-mcp-server/tsconfig.json
size: 336
sha: 2b380862c53cb85ce77cde81ea6281ee6ecabf26
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/ha-mcp-server/tsconfig.json

```text
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## File: scripts/setup.sh
size: 3215
sha: 7c350679d6616c614a837a94c3f346d4898205ec
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/scripts/setup.sh

```text
#!/bin/bash
# Setup script for openclaw-xiaomi-home skill
# This script helps users install Home Assistant and configure the MCP server

set -e

SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SCRIPT_DIR="$SKILL_DIR/scripts/ha-mcp-server"
PLIST_SRC="$SCRIPT_DIR/ai.openclaw.ha-mcp.plist"

echo "=========================================="
echo "Xiaomi Home Skill Setup"
echo "=========================================="

# Check Docker
if ! command -v docker &> /dev/null; then
    echo "Error: Docker is not installed."
    echo "Please install Docker Desktop from: https://docs.docker.com/desktop/setup/install/mac-install/"
    exit 1
fi

if ! docker info &> /dev/null; then
    echo "Error: Docker is not running."
    echo "Please start Docker Desktop."
    exit 1
fi
echo "✓ Docker is installed and running"

# Start Home Assistant
echo ""
echo "Starting Home Assistant..."
cd "$SKILL_DIR"
docker compose up -d 2>/dev/null || docker run -d \
  --name homeassistant \
  --privileged \
  -p 8123:8123 \
  -v ~/homeassistant/config:/config \
  -v /etc/localtime:/etc/localtime:ro \
  -e TZ=Asia/Shanghai \
  --dns=8.8.8.8 \
  --dns=223.5.5.5 \
  --restart=unless-stopped \
  ghcr.io/home-assistant/home-assistant:stable

echo "✓ Home Assistant started"
echo "  Access at: http://localhost:8123"
echo ""
echo "  First-time setup:"
echo "  1. Open http://localhost:8123 and create your account"
echo "  2. Add 'external_url: http://localhost:8123' to ~/homeassistant/config/configuration.yaml"
echo "  3. Go to Settings → Devices & Services → Add Integration"
echo "  4. Search 'Xiaomi Home' and login with your Xiaomi account"
echo "  5. Profile → Security → Create Long-Lived Access Token"
echo ""

# Setup HA token
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "=========================================="
    echo "MCP Server Configuration"
    echo "=========================================="
    echo "Enter your Home Assistant Long-Lived Access Token:"
    read -r HA_TOKEN
    echo "HA_URL=http://localhost:8123" > "$SCRIPT_DIR/.env"
    echo "HA_TOKEN=$HA_TOKEN" >> "$SCRIPT_DIR/.env"
    echo "PORT=3002" >> "$SCRIPT_DIR/.env"
    echo "✓ Created .env file"
else
    echo "✓ .env file already exists"
fi

# Install MCP Server dependencies
echo ""
echo "Installing MCP Server..."
cd "$SCRIPT_DIR"
npm install --silent 2>/dev/null

# Setup LaunchAgent
echo ""
echo "Installing HA MCP Server as LaunchAgent..."
mkdir -p "$HOME/Library/LaunchAgents"
mkdir -p "$(dirname "$PLIST_SRC")"

# Create user-specific plist
PLIST_DST="$HOME/Library/LaunchAgents/ai.openclaw.ha-mcp.plist"
sed "s|/Users/nanali|$HOME|g" "$PLIST_SRC" > "$PLIST_DST"
launchctl load "$PLIST_DST" 2>/dev/null || true
echo "✓ HA MCP Server installed as LaunchAgent"

echo ""
echo "=========================================="
echo "Setup Complete!"
echo "=========================================="
echo ""
echo "HA MCP Server: http://localhost:3002"
echo "Home Assistant: http://localhost:8123"
echo ""
echo "Test the MCP server:"
echo "  node $SCRIPT_DIR/src/call-tool.mjs ping_ha"
echo ""
echo "For OpenClaw integration, add this skill to your OpenClaw"
echo "and the MCP server will automatically handle device control."
```

## File: references/api-notes.md
size: 2009
sha: ade0f653cd841639dd7017efea731c6273a403a0
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/references/api-notes.md

````text
# Home Assistant API Notes

## Authentication

All API requests require a Long-Lived Access Token in the Authorization header:

```
Authorization: Bearer YOUR_TOKEN_HERE
```

## Base URL

```
http://localhost:8123/api
```

## Key Endpoints

### States

```bash
# Get all states
GET /api/states

# Get single entity state
GET /api/states/{entity_id}

# Example
curl -H "Authorization: Bearer $HA_TOKEN" \
     http://localhost:8123/api/states/light.living_room
```

### Services

```bash
# Call a service
POST /api/services/{domain}/{service}

# Light examples
POST /api/services/light/turn_on
{"entity_id": "light.living_room", "brightness": 255}

POST /api/services/light/turn_off
{"entity_id": "light.living_room"}

# Climate examples
POST /api/services/climate/set_temperature
{"entity_id": "climate.living_room_ac", "temperature": 26}

POST /api/services/climate/set_hvac_mode
{"entity_id": "climate.living_room_ac", "hvac_mode": "cool"}

# Lock examples
POST /api/services/lock/lock
{"entity_id": "lock.front_door"}

POST /api/services/lock/unlock
{"entity_id": "lock.front_door"}
```

### Config

```bash
GET /api/config
```

### Events

```bash
# Listen to events (Server-Sent Events)
GET /api/events
```

## Entity ID Format

```
{domain}.{device_name}_{identifier}
```

Examples:
- `light.living_room_ceiling`
- `climate.bedroom_ac`
- `sensor.kitchen_temperature`
- `binary_sensor.front_door`
- `lock.front_door`
- `switch.tv_plug`

## Common Domains

| Domain | Type |
|--------|------|
| `light` | Lights |
| `switch` | Plugs, Switches |
| `climate` | AC, Heaters |
| `sensor` | Sensors (readings) |
| `binary_sensor` | Binary sensors (on/off) |
| `lock` | Smart locks |
| `cover` | Curtains, Blinds |
| `fan` | Fans |
| `scene` | Scenes |
| `automation` | Automations |

## Error Responses

```json
{
  "code": "not_found",
  "message": "Entity not found"
}
```

## Rate Limits

- No strict rate limit for local API
- Recommended: max 10 calls/second
- Cloud API (Xiaomi) may have stricter limits
````

## File: references/automations.md
size: 3519
sha: d5188bcb0a34ad9501625da7918d7aab7b0afc3d
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/references/automations.md

````text
# Automation Templates

## Home Assistant Automation Examples

These automations can be configured in Home Assistant's UI or via YAML.

### Temperature-Based Climate Control

```yaml
automation:
  - alias: "Auto AC when hot"
    trigger:
      - platform: numeric_state
        entity_id: sensor.living_room_temperature
        above: 28
    condition:
      - condition: time
        after: "08:00:00"
        before: "22:00:00"
    action:
      - service: climate.set_temperature
        target:
          entity_id: climate.living_room_ac
        data:
          temperature: 26
      - service: notify.openclaw
        data:
          message: "🌡️ Living room temp above 28°C, AC turned on to 26°C"
```

### Motion-Activated Light

```yaml
automation:
  - alias: "Hallway light on motion"
    trigger:
      - platform: state
        entity_id: binary_sensor.hallway_motion
        to: "on"
    condition:
      - condition: sun
        after: sunset
        before: sunrise
    action:
      - service: light.turn_on
        target:
          entity_id: light.hallway
        data:
          brightness: 128
```

### Door Lock Reminder

```yaml
automation:
  - alias: "Front door auto-lock"
    trigger:
      - platform: state
        entity_id: binary_sensor.front_door
        to: "closed"
        for:
          minutes: 2
    condition:
      - condition: state
        entity_id: lock.front_door
        state: "unlocked"
    action:
      - service: lock.lock
        target:
          entity_id: lock.front_door
```

### Goodnight Routine

```yaml
automation:
  - alias: "Goodnight scene"
    trigger:
      - platform: time
        at: "22:30:00"
    action:
      - service: light.turn_off
        target:
          entity_id: group.all_lights
      - service: climate.set_temperature
        target:
          entity_id: climate.bedroom_ac
        data:
          temperature: 25
      - service: lock.lock
        target:
          entity_id: lock.front_door
```

### Leave Home

```yaml
automation:
  - alias: "Turn off when leaving"
    trigger:
      - platform: state
        entity_id: binary_sensor.front_door
        to: "closed"
        for:
          minutes: 5
    condition:
      - condition: state
        entity_id: person.jun
        state: "not_home"
    action:
      - service: switch.turn_off
        target:
          entity_id: group.all_switches
      - service: notify.openclaw
        data:
          message: "🔒 Home secured. All devices off."
```

## OpenClaw Webhook Integration

To receive alerts in OpenClaw, set up a webhook in Home Assistant.

### Configuration

Add to `configuration.yaml`:

```yaml
rest_command:
  notify_openclaw:
    url: "http://localhost:8123/api/webhook/openclaw_notify"
    method: POST
    content_type: "application/json"
    payload: '{"message": "{{ message }}"}'
```

### Enable Webhook in UI

Settings → Devices & Services → Helpers → Add Helper → Webhook

### Notify OpenClaw from Automation

```yaml
action:
  - service: rest_command.notify_openclaw
    data:
      message: "⚠️ Smoke detected in living room!"
```

## Natural Language Commands from OpenClaw

OpenClaw can execute these automations via the MCP tools:

```
User: "Turn on the fan when temperature exceeds 30°C"
→ OpenClaw: Creates automation in HA or triggers manual command

User: "Lock all doors at 10 PM"  
→ OpenClaw: Creates time-based automation

User: "Turn off everything when I leave"
→ OpenClaw: Creates device-based automation
```
````

## File: references/device-support.md
size: 2947
sha: 9401dd5eb9dae8e829bde03458ad71de21193212
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/references/device-support.md

```text
# Device Support Reference

## Supported Devices

### Lighting ✅
| Category | Examples | Notes |
|----------|----------|-------|
| Ceiling Lights | Mi LED Ceiling Light, Philips Ceiling Light | Full on/off/brightness/color control |
| Table Lamps | Mi Bedside Lamp, Mi Desk Lamp | Full control |
| Floor Lamps | Mi Floor Lamp | Full control |
| Bulbs | Mi LED Bulb, Philips Hue (via ecosystem) | Full on/off/brightness |

### Climate ✅
| Category | Examples | Notes |
|----------|----------|-------|
| Air Conditioners | Mi Air Conditioner, various IR-based ACs | Temperature, mode, fan speed |
| Heaters | Mi Smart Heater, Mi Oil Heater | Temperature control |
| Fans | Mi Smart Fan, Mi Standing Fan | On/off, speed |

### Sensors ✅
| Category | Examples | Notes |
|----------|----------|-------|
| Temperature/Humidity | Mi Temperature and Humidity Sensor | Readings |
| Door/Window | Mi Door/Window Sensor | Open/close status |
| Motion | Mi Motion Sensor | Detected/idle |
| Water Leak | Mi Water Leak Sensor | Detected/safe |

### Smart Locks ✅
| Category | Examples | Notes |
|----------|----------|-------|
| Door Locks | Xiaomi Smart Door Lock, Aqara Smart Lock | Lock/unlock, auto-lock settings |

### Plugs & Switches ✅
| Category | Examples | Notes |
|----------|----------|-------|
| Smart Plugs | Mi Smart Plug, Aqara Plug | On/off, power monitoring |
| Wall Switches | Mi Wall Switch, Aqara Wall Switch | On/off |

### Appliances ⚠️
| Category | Examples | Notes |
|----------|----------|-------|
| Refrigerators | Mi Refrigerator | Basic on/off, temperature |
| Washers | Mi Washing Machine | Start/stop, basic modes |
| Projectors | Mi Laser Projector | On/off, input selection |

## NOT Supported ❌

- **Bluetooth devices** (direct BLE connection)
- **Infrared (IR) devices** (TVs, AC remotes via IR blaster)
- **Virtual devices** (scenes, automations created in Mi Home)
- **Wearable devices** (Mi Band, etc.)
- **Air Purifiers** (some models may work)
- **Robot Vacuums** (different integration path)

## Local vs Cloud Control

### With Central Hub Gateway ✅
- Commands go through the local hub
- Faster response (< 1 second)
- Works without internet
- Requires: Hub firmware 3.3.0_0023+

### Without Central Hub
- Commands go through Xiaomi Cloud
- Slower response (1-3 seconds)
- Requires internet connection
- May have latency during cloud outages

### LAN Control (Partial Alternative)
Enable in Xiaomi Home integration settings:
Settings → Devices & Services → Xiaomi Home → Configure → LAN Control

⚠️ Note: LAN control only works for IP devices (WiFi), not BLE Mesh/ZigBee devices.

## Entity Naming Convention

In Home Assistant, Xiaomi devices appear as:
- `light.xiaomi_xxx` - Lights
- `climate.xiaomi_xxx` - AC/Heaters
- `sensor.xiaomi_temperature` - Sensors
- `binary_sensor.xiaomi_door` - Door/Window sensors
- `lock.xiaomi_lock` - Smart locks
- `switch.xiaomi_plug` - Plugs/Switches
```

## File: references/installation.md
size: 2371
sha: a78382a729b1f3b231c242d7772abdc90f5a3e23
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/references/installation.md

````text
# Detailed Installation Guide

## Prerequisites

- Docker Desktop 4.x+
- Xiaomi account with Mi Home app and linked devices
- (Optional) Xiaomi Central Hub Gateway for local control

## Step 1: Install Docker Desktop

Download from: https://docs.docker.com/desktop/setup/install/mac-install/

After installation, ensure Docker is running (whale icon in menu bar).

## Step 2: Clone and Setup the Skill

```bash
git clone https://github.com/YOUR_USERNAME/openclaw-xiaomi-home.git
cd openclaw-xiaomi-home
```

## Step 3: Configure mcporter

Add the MCP server to your mcporter config:

```bash
mcporter config add ha-mcp \
  --stdio "cd /path/to/openclaw-xiaomi-home/scripts/ha-mcp-server && npm start"
```

Or add it manually to `~/.openclaw/config/mcporter.json`:

```json
{
  "servers": {
    "ha-mcp": {
      "command": "node",
      "args": ["/path/to/openclaw-xiaomi-home/scripts/ha-mcp-server/dist/index.js"],
      "env": {
        "HA_URL": "http://localhost:8123",
        "HA_TOKEN": "your_long_lived_token"
      }
    }
  }
}
```

## Step 4: Get Home Assistant Token

1. Open Home Assistant: http://localhost:8123
2. Create account / Login
3. Profile (your username) → Security
4. Create Long-Lived Access Token
5. Copy the token

## Step 5: Update .env

Edit `scripts/ha-mcp-server/.env`:

```
HA_URL=http://localhost:8123
HA_TOKEN=your_copied_token_here
PORT=3002
```

## Step 6: Install ha_xiaomi_home Integration

1. Home Assistant → Settings → Devices & Services
2. Add Integration → Search "Xiaomi Home"
3. Click "Xiaomi Home" → Continue
4. Login with Xiaomi account
5. Select your home and devices to import

## Step 7: Test

After HA restarts and devices are imported, test:

```bash
mcporter call ha-mcp.list_all_devices
mcporter call ha-mcp.ping_ha
```

## Troubleshooting

### HA Container Won't Start

Check logs:
```bash
docker logs homeassistant
```

### Can't Login to Xiaomi

The Xiaomi Cloud API may be blocked in your region. Use a VPN or enable LAN control in the integration settings.

### Devices Not Showing

- Ensure devices are added to Mi Home app first
- Check if device type is supported (no BLE/IR devices)
- Try re-authorizing the Xiaomi account in integration settings

## Updating

```bash
cd openclaw-xiaomi-home
git pull
cd scripts/ha-mcp-server
npm install && npm run build
docker compose restart homeassistant
```
````

## File: .github/ISSUE_TEMPLATE/bug_report.md
size: 628
sha: 003460b4115f38ac604627d7cf50c1abaaadd69a
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/.github/ISSUE_TEMPLATE/bug_report.md

```text
---
name: "\U0001F41B Bug Report"
about: Report something that isn't working correctly
title: "[Bug] "
labels: bug
assignees: ''
---

## 🐛 Description
[Describe the bug clearly and concisely]

## 🔄 Steps to Reproduce
1. 
2. 
3. 

## ✅ Expected Behavior
[What should happen]

## ❌ Actual Behavior
[What actually happens]

## 📋 Environment
- **Home Assistant version**: 
- **openclaw-xiaomi-home version**: 
- **Device type**: 
- **macOS / Linux version**: 

## 📸 Screenshots / Logs
[If applicable, add screenshots or relevant log output]

## 🔍 Additional Context
[Add any other context about the problem here]
```

## File: .github/ISSUE_TEMPLATE/config.yml
size: 405
sha: 6732e04732f6b3e4a6744fd5e37780ab01e2c525
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/.github/ISSUE_TEMPLATE/config.yml

```text
blank_issues_enabled: false
contact_links:
  - name: "\U0001F4AC General Question / Usage Help"
    url: https://github.com/canmaxfire/openclaw-xiaomi-home/discussions"
    about: Please ask and answer general questions here.
  - name: "\U0001F310 Installation Help"
    url: https://github.com/canmaxfire/openclaw-xiaomi-home#quick-start"
    about: Check the README for installation instructions first.
```

## File: .github/ISSUE_TEMPLATE/feature_request.md
size: 737
sha: 72711c6456d8b091b47fe1a43d9d2eaf9cdb887b
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/.github/ISSUE_TEMPLATE/feature_request.md

```text
---
name: "\U0001F31F Feature Request"
about: Suggest a new feature or improvement
title: "[Feature] "
labels: enhancement
assignees: ''
---

## ✨ Problem / Use Case
[Describe the problem you want to solve, or the use case this feature would enable]

## 💡 Proposed Solution
[Describe what you'd like to happen]

## 🔄 Current Workaround
[If applicable, describe any workaround you're currently using]

## 📋 Alternative Solutions
[Describe any alternative solutions or features you've considered]

## 🔧 Device / Integration
[Is this related to a specific device type or Home Assistant integration?]
- Device type: 
- Home Assistant version: 

## 📸 Mockup / Examples
[If applicable, add screenshots, diagrams, or examples]
```

## File: .github/PULL_REQUEST_TEMPLATE.md
size: 789
sha: b05275f73e2f29a699712e585dbcbfafa23b4068
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/.github/PULL_REQUEST_TEMPLATE.md

```text
## 📦 Pull Request

### Description
[Describe what changes this PR makes and why]

### Related Issue
[Link to the issue this PR addresses, e.g. "Fixes #12"]

### Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Other: 

### Testing
[Describe what testing you've done]
- [ ] Tested locally with Home Assistant
- [ ] Tested with specific device(s): 
- [ ] No testing done (please explain why)

### Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings

### Screenshots / Logs
[If applicable]
```

## File: .gitignore
size: 41
sha: 4274b5191944592c863c9ace12111c3a2e042834
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/.gitignore

```text
node_modules/
dist/
.env
*.log
.DS_Store
```

## File: docker-compose.yml
size: 463
sha: 545018b39c2b95c70fa0a55a4b4339f8fcc59eb1
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/docker-compose.yml

```text
# Home Assistant + Xiaomi Home Integration
# Usage: docker compose up -d

services:
  homeassistant:
    container_name: homeassistant
    image: ghcr.io/home-assistant/home-assistant:stable
    privileged: true
    network_mode: host
    volumes:
      - ./config:/config
      - /etc/localtime:/etc/localtime:ro
    environment:
      - TZ=Asia/Shanghai
    restart: unless-stopped
    # For Apple Silicon Macs, you may need to add:
    # platform: linux/arm64
```

## File: LICENSE
size: 1072
sha: 1809662b5abb9cc1ef6f06a163570fb013cb78a3
upstream: https://github.com/canmaxfire/openclaw-xiaomi-home/blob/HEAD/LICENSE

```text
MIT License

Copyright (c) 2026 Nana / OpenClaw

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
