feyagate-skill 1.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- feyagate_skill/__init__.py +9 -0
- feyagate_skill/auth.py +133 -0
- feyagate_skill/cli.py +212 -0
- feyagate_skill/data/SKILL.md +115 -0
- feyagate_skill/data/camera_extra_info.yaml +51 -0
- feyagate_skill/data/config.yaml.example +17 -0
- feyagate_skill/data/skills/automation.md +71 -0
- feyagate_skill/data/skills/ewelink.md +115 -0
- feyagate_skill/data/skills/extension.md +57 -0
- feyagate_skill/data/skills/midea.md +116 -0
- feyagate_skill/data/skills/tuya.md +124 -0
- feyagate_skill/data/skills/xiaomi.md +94 -0
- feyagate_skill/installer.py +303 -0
- feyagate_skill/mcp.py +48 -0
- feyagate_skill/scheduled.py +134 -0
- feyagate_skill/service.py +189 -0
- feyagate_skill/snapshot.py +85 -0
- feyagate_skill-1.2.2.dist-info/METADATA +585 -0
- feyagate_skill-1.2.2.dist-info/RECORD +23 -0
- feyagate_skill-1.2.2.dist-info/WHEEL +5 -0
- feyagate_skill-1.2.2.dist-info/entry_points.txt +2 -0
- feyagate_skill-1.2.2.dist-info/licenses/LICENSE +21 -0
- feyagate_skill-1.2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""FeyaGate Skill - MCP Smart Home Gateway for AI Agents."""
|
|
2
|
+
|
|
3
|
+
__version__ = "1.2.2"
|
|
4
|
+
__author__ = "panzuji"
|
|
5
|
+
|
|
6
|
+
DEFAULT_INSTALL_DIR = "~/.feyagate"
|
|
7
|
+
FOTA_URL = "https://oneapi.sooncore.com/ota/fota.json"
|
|
8
|
+
MCP_DEFAULT_PORT = 38080
|
|
9
|
+
MCP_DEFAULT_HOST = "127.0.0.1"
|
feyagate_skill/auth.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Xiaomi account authorization tool."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from urllib.parse import urlparse, parse_qs
|
|
8
|
+
|
|
9
|
+
from . import MCP_DEFAULT_HOST, MCP_DEFAULT_PORT
|
|
10
|
+
from .mcp import mcp_call
|
|
11
|
+
|
|
12
|
+
COLORS = {
|
|
13
|
+
'reset': '\033[0m', 'red': '\033[91m', 'green': '\033[92m',
|
|
14
|
+
'yellow': '\033[93m', 'blue': '\033[94m', 'cyan': '\033[96m',
|
|
15
|
+
'bold': '\033[1m',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _c(name, text):
|
|
20
|
+
return f"{COLORS.get(name, '')}{text}{COLORS['reset']}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _tool_map(tool):
|
|
24
|
+
mapping = {
|
|
25
|
+
"auth/status": "xiaomi/auth_status",
|
|
26
|
+
"auth/url": "xiaomi/auth_url",
|
|
27
|
+
"auth/callback": "xiaomi/auth_callback",
|
|
28
|
+
}
|
|
29
|
+
return mapping.get(tool, tool)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _mcp(host, port, tool, arguments=None):
|
|
33
|
+
return mcp_call(host, port, _tool_map(tool), arguments)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def check_status(host, port):
|
|
37
|
+
status = _mcp(host, port, "auth/status")
|
|
38
|
+
authorized = status.get("authorized", False)
|
|
39
|
+
remaining = status.get("remaining_seconds", 0)
|
|
40
|
+
|
|
41
|
+
if authorized:
|
|
42
|
+
hours = remaining // 3600
|
|
43
|
+
mins = (remaining % 3600) // 60
|
|
44
|
+
days = hours // 24
|
|
45
|
+
hours = hours % 24
|
|
46
|
+
time_str = f"{days}d " if days > 0 else ""
|
|
47
|
+
time_str += f"{hours}h {mins}m" if hours > 0 or mins > 0 else "expiring soon"
|
|
48
|
+
print(_c('green', f" Authorized, remaining: {time_str}"))
|
|
49
|
+
return True
|
|
50
|
+
else:
|
|
51
|
+
print(_c('red', " Not authorized"))
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_auth_url(host, port):
|
|
56
|
+
result = _mcp(host, port, "auth/url")
|
|
57
|
+
return result.get("url", "")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def submit_code(host, port, code):
|
|
61
|
+
code_input = code.strip()
|
|
62
|
+
if code_input.startswith("http://") or code_input.startswith("https://"):
|
|
63
|
+
parsed = urlparse(code_input)
|
|
64
|
+
params = parse_qs(parsed.query)
|
|
65
|
+
code_list = params.get("code", [])
|
|
66
|
+
if not code_list:
|
|
67
|
+
print(_c('red', " No 'code' parameter found in URL"))
|
|
68
|
+
return False
|
|
69
|
+
code_input = code_list[0]
|
|
70
|
+
|
|
71
|
+
print(_c('yellow', f" Submitting auth code: {code_input[:10]}..."))
|
|
72
|
+
result = _mcp(host, port, "auth/callback", {"code": code_input})
|
|
73
|
+
|
|
74
|
+
if "error" in result:
|
|
75
|
+
print(_c('red', f" Auth failed: {result['error']}"))
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
time.sleep(1)
|
|
79
|
+
if check_status(host, port):
|
|
80
|
+
return True
|
|
81
|
+
else:
|
|
82
|
+
print(_c('yellow', " Code submitted but status not updated."))
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def do_auth(host=None, port=None, code=None, status_only=False):
|
|
87
|
+
"""Run the auth flow."""
|
|
88
|
+
host = host or MCP_DEFAULT_HOST
|
|
89
|
+
port = port or MCP_DEFAULT_PORT
|
|
90
|
+
|
|
91
|
+
if status_only:
|
|
92
|
+
print("=== Auth Status ===")
|
|
93
|
+
check_status(host, port)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
if code:
|
|
97
|
+
print("=== Submit Auth Code ===")
|
|
98
|
+
if submit_code(host, port, code):
|
|
99
|
+
print(_c('green', "\nAuthorization successful!"))
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Interactive flow
|
|
103
|
+
print("=" * 50)
|
|
104
|
+
print(_c('blue', " Xiaomi Account Authorization"))
|
|
105
|
+
print("=" * 50)
|
|
106
|
+
print()
|
|
107
|
+
|
|
108
|
+
print("Step 1: Check current status")
|
|
109
|
+
if check_status(host, port):
|
|
110
|
+
print(_c('yellow', "\nAlready authorized. Re-auth after token expires."))
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
print()
|
|
114
|
+
print("Step 2: Get authorization URL")
|
|
115
|
+
auth_url = get_auth_url(host, port)
|
|
116
|
+
if not auth_url:
|
|
117
|
+
print(_c('red', " Cannot get auth URL. Is MCP server running?"))
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
print(_c('green', " Auth URL generated"))
|
|
121
|
+
print(f"\n {_c('cyan', auth_url)}")
|
|
122
|
+
print(_c('yellow', "\n Open URL in browser, then copy the full redirect URL"))
|
|
123
|
+
print(_c('yellow', " (The 'page not found' error is normal)"))
|
|
124
|
+
print()
|
|
125
|
+
|
|
126
|
+
print("Step 3: Submit auth code")
|
|
127
|
+
callback_url = input("Paste the full callback URL: ").strip()
|
|
128
|
+
if not callback_url:
|
|
129
|
+
print(_c('red', " No input"))
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if submit_code(host, port, callback_url):
|
|
133
|
+
print(_c('green', "\nAuthorization complete!"))
|
feyagate_skill/cli.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""FeyaGate Skill CLI - Main entry point."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from . import __version__, DEFAULT_INSTALL_DIR, MCP_DEFAULT_PORT
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _install_claude():
|
|
13
|
+
"""Configure Claude Code MCP settings."""
|
|
14
|
+
claude_config = Path.home() / ".claude.json"
|
|
15
|
+
install_dir = Path(os.path.expanduser(DEFAULT_INSTALL_DIR))
|
|
16
|
+
binary = install_dir / "bin" / "miloco-mcp-server"
|
|
17
|
+
|
|
18
|
+
if not binary.exists():
|
|
19
|
+
print("ERROR: MCP server not installed. Run: feyagate setup")
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
config = {}
|
|
23
|
+
if claude_config.exists():
|
|
24
|
+
try:
|
|
25
|
+
config = json.loads(claude_config.read_text(encoding="utf-8"))
|
|
26
|
+
except Exception:
|
|
27
|
+
config = {}
|
|
28
|
+
|
|
29
|
+
mcp_servers = config.setdefault("mcpServers", {})
|
|
30
|
+
mcp_servers["feyagate"] = {
|
|
31
|
+
"type": "streamable-http",
|
|
32
|
+
"url": "http://localhost:38080/mcp/http",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
claude_config.write_text(json.dumps(config, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
36
|
+
print(f"Added 'feyagate' MCP server to {claude_config}")
|
|
37
|
+
print(f" URL: http://localhost:38080/mcp/http")
|
|
38
|
+
print()
|
|
39
|
+
print("Make sure to start the server: feyagate start")
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _install_cursor():
|
|
44
|
+
"""Configure Cursor MCP settings."""
|
|
45
|
+
cursor_config = Path.home() / ".cursor" / "mcp.json"
|
|
46
|
+
install_dir = Path(os.path.expanduser(DEFAULT_INSTALL_DIR))
|
|
47
|
+
binary = install_dir / "bin" / "miloco-mcp-server"
|
|
48
|
+
|
|
49
|
+
if not binary.exists():
|
|
50
|
+
print("ERROR: MCP server not installed. Run: feyagate setup")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
cursor_config.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
config = {}
|
|
56
|
+
if cursor_config.exists():
|
|
57
|
+
try:
|
|
58
|
+
config = json.loads(cursor_config.read_text(encoding="utf-8"))
|
|
59
|
+
except Exception:
|
|
60
|
+
config = {}
|
|
61
|
+
|
|
62
|
+
mcp_servers = config.setdefault("mcpServers", {})
|
|
63
|
+
mcp_servers["feyagate"] = {
|
|
64
|
+
"type": "streamable-http",
|
|
65
|
+
"url": "http://localhost:38080/mcp/http",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
cursor_config.write_text(json.dumps(config, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
69
|
+
print(f"Added 'feyagate' MCP server to {cursor_config}")
|
|
70
|
+
print(f" URL: http://localhost:38080/mcp/http")
|
|
71
|
+
print()
|
|
72
|
+
print("Make sure to start the server: feyagate start")
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def main():
|
|
77
|
+
parser = argparse.ArgumentParser(
|
|
78
|
+
prog="feyagate",
|
|
79
|
+
description="FeyaGate Skill - MCP Smart Home Gateway for AI Agents",
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument("--version", action="version", version=f"feyagate-skill {__version__}")
|
|
82
|
+
|
|
83
|
+
sub = parser.add_subparsers(dest="command", help="Available commands")
|
|
84
|
+
|
|
85
|
+
# setup
|
|
86
|
+
p_setup = sub.add_parser("setup", help="Download and install MCP server binary")
|
|
87
|
+
p_setup.add_argument("--dir", default=None, help="Install directory (default: ~/.feyagate)")
|
|
88
|
+
|
|
89
|
+
# start
|
|
90
|
+
p_start = sub.add_parser("start", help="Start MCP server")
|
|
91
|
+
p_start.add_argument("--port", type=int, default=None, help="HTTP port (default: from config)")
|
|
92
|
+
|
|
93
|
+
# stop
|
|
94
|
+
sub.add_parser("stop", help="Stop MCP server")
|
|
95
|
+
|
|
96
|
+
# restart
|
|
97
|
+
p_restart = sub.add_parser("restart", help="Restart MCP server")
|
|
98
|
+
p_restart.add_argument("--port", type=int, default=None, help="HTTP port")
|
|
99
|
+
|
|
100
|
+
# status
|
|
101
|
+
sub.add_parser("status", help="Show server status")
|
|
102
|
+
|
|
103
|
+
# log
|
|
104
|
+
p_log = sub.add_parser("log", help="Show server log")
|
|
105
|
+
p_log.add_argument("-n", "--lines", type=int, default=30, help="Number of lines")
|
|
106
|
+
|
|
107
|
+
# auth
|
|
108
|
+
p_auth = sub.add_parser("auth", help="Xiaomi account authorization")
|
|
109
|
+
p_auth.add_argument("--host", default=None, help="MCP host")
|
|
110
|
+
p_auth.add_argument("--port", type=int, default=None, help="MCP port")
|
|
111
|
+
p_auth.add_argument("--status", action="store_true", help="Check auth status only")
|
|
112
|
+
p_auth.add_argument("--code", default=None, help="Submit auth code directly")
|
|
113
|
+
|
|
114
|
+
# snapshot
|
|
115
|
+
p_snap = sub.add_parser("snapshot", help="Camera snapshot capture")
|
|
116
|
+
p_snap.add_argument("--host", default=None)
|
|
117
|
+
p_snap.add_argument("--port", type=int, default=None)
|
|
118
|
+
p_snap.add_argument("--list", action="store_true", help="List cameras")
|
|
119
|
+
p_snap.add_argument("--camera-id", default=None, help="Camera DID")
|
|
120
|
+
p_snap.add_argument("--connect", action="store_true", help="Connect before snapshot")
|
|
121
|
+
p_snap.add_argument("--channel", type=int, default=0)
|
|
122
|
+
p_snap.add_argument("--count", type=int, default=1)
|
|
123
|
+
p_snap.add_argument("--output-dir", default=None)
|
|
124
|
+
|
|
125
|
+
# scheduled
|
|
126
|
+
p_sched = sub.add_parser("scheduled", help="Scheduled camera analysis")
|
|
127
|
+
p_sched.add_argument("--host", default=None)
|
|
128
|
+
p_sched.add_argument("--port", type=int, default=None)
|
|
129
|
+
p_sched.add_argument("--camera-id", action="append", required=True, help="Camera DID (repeatable)")
|
|
130
|
+
p_sched.add_argument("--channel", type=int, default=0)
|
|
131
|
+
p_sched.add_argument("--interval", type=int, default=300, help="Seconds between captures")
|
|
132
|
+
p_sched.add_argument("--output-dir", default=None)
|
|
133
|
+
p_sched.add_argument("--prompt", default="", help="AI analysis prompt")
|
|
134
|
+
p_sched.add_argument("--auto-connect", action="store_true")
|
|
135
|
+
p_sched.add_argument("--max-runs", type=int, default=0, help="0 = unlimited")
|
|
136
|
+
p_sched.add_argument("--one-shot", action="store_true")
|
|
137
|
+
|
|
138
|
+
# install-claude
|
|
139
|
+
sub.add_parser("install-claude", help="Configure Claude Code MCP settings")
|
|
140
|
+
|
|
141
|
+
# install-cursor
|
|
142
|
+
sub.add_parser("install-cursor", help="Configure Cursor MCP settings")
|
|
143
|
+
|
|
144
|
+
# upgrade
|
|
145
|
+
sub.add_parser("upgrade", help="Upgrade MCP server to latest version")
|
|
146
|
+
|
|
147
|
+
args = parser.parse_args()
|
|
148
|
+
|
|
149
|
+
if not args.command:
|
|
150
|
+
parser.print_help()
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
if args.command == "setup":
|
|
154
|
+
from .installer import do_setup
|
|
155
|
+
do_setup(args.dir)
|
|
156
|
+
|
|
157
|
+
elif args.command == "start":
|
|
158
|
+
from .service import do_start
|
|
159
|
+
do_start(args.port)
|
|
160
|
+
|
|
161
|
+
elif args.command == "stop":
|
|
162
|
+
from .service import do_stop
|
|
163
|
+
do_stop()
|
|
164
|
+
|
|
165
|
+
elif args.command == "restart":
|
|
166
|
+
from .service import do_stop, do_start
|
|
167
|
+
do_stop()
|
|
168
|
+
do_start(args.port)
|
|
169
|
+
|
|
170
|
+
elif args.command == "status":
|
|
171
|
+
from .service import do_status
|
|
172
|
+
do_status()
|
|
173
|
+
|
|
174
|
+
elif args.command == "log":
|
|
175
|
+
from .service import do_log
|
|
176
|
+
do_log(args.lines)
|
|
177
|
+
|
|
178
|
+
elif args.command == "auth":
|
|
179
|
+
from .auth import do_auth
|
|
180
|
+
do_auth(host=args.host, port=args.port, code=args.code, status_only=args.status)
|
|
181
|
+
|
|
182
|
+
elif args.command == "snapshot":
|
|
183
|
+
from .snapshot import do_snapshot
|
|
184
|
+
do_snapshot(
|
|
185
|
+
host=args.host, port=args.port, camera_id=args.camera_id,
|
|
186
|
+
connect=args.connect, channel=args.channel, count=args.count,
|
|
187
|
+
output_dir=args.output_dir, list_cameras=args.list,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
elif args.command == "scheduled":
|
|
191
|
+
from .scheduled import do_scheduled
|
|
192
|
+
do_scheduled(
|
|
193
|
+
host=args.host, port=args.port, camera_ids=args.camera_id,
|
|
194
|
+
channel=args.channel, interval=args.interval,
|
|
195
|
+
output_dir=args.output_dir, prompt=args.prompt,
|
|
196
|
+
auto_connect=args.auto_connect, max_runs=args.max_runs,
|
|
197
|
+
one_shot=args.one_shot,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
elif args.command == "install-claude":
|
|
201
|
+
_install_claude()
|
|
202
|
+
|
|
203
|
+
elif args.command == "install-cursor":
|
|
204
|
+
_install_cursor()
|
|
205
|
+
|
|
206
|
+
elif args.command == "upgrade":
|
|
207
|
+
from .installer import do_setup
|
|
208
|
+
do_setup()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
main()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feyagate
|
|
3
|
+
description: MCP smart home gateway skill for AI agents (OpenClaw, Claude Code, Hermes, Codex, Windsurf, Copilot, 小智AI). Control multi-platform IoT devices via MCP protocol — supports Xiaomi/Mi Home, Tuya, Midea, eWeLink, Serial, and GPIO platforms. Provides cross-platform device discovery and control, MIOT device properties, XiaoAI speaker TTS/music, camera P2P streaming and snapshots, scheduled tasks, trigger automation, vision AI analysis, memory system, room management, and skill management. Use when working with smart home, IoT device control, home automation, Xiaomi, Tuya, Midea, eWeLink, MIOT protocol, cameras, surveillance, XiaoAI speakers, TTS, automation, scheduling, OpenClaw skill, Hermes skill, MCP server, MCP skill, or AI agent tool.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# FeyaGate Skill — MCP Smart Home Gateway for AI Agents
|
|
7
|
+
|
|
8
|
+
MCP-based multi-platform smart home gateway supporting Xiaomi, Tuya, Midea, eWeLink, Serial, and GPIO. Compatible with OpenClaw, Claude Code, Hermes, Codex, Windsurf, Copilot, 小智AI and other MCP-compatible AI agents.
|
|
9
|
+
|
|
10
|
+
**Dual API endpoints:**
|
|
11
|
+
|
|
12
|
+
| Endpoint | Protocol | URL |
|
|
13
|
+
|----------|----------|-----|
|
|
14
|
+
| MCP (PC proxy) | Streamable HTTP JSON-RPC 2.0 | `http://localhost:38080/mcp/http` |
|
|
15
|
+
| MCP (ESP32 gateway) | WebSocket / Streamable HTTP | `ws://<gateway>:8765/mcp` / `http://<gateway>:8765/mcp/http` |
|
|
16
|
+
| HTTP REST (ESP32 gateway) | REST API | `http://<gateway>:8080/api/v1/...` |
|
|
17
|
+
|
|
18
|
+
> **Note:** The PC proxy (miloco-mcp-server) provides all gateway MCP tools plus additional extensions (camera P2P, Xiaomi auth, etc.).
|
|
19
|
+
|
|
20
|
+
## Server Lifecycle
|
|
21
|
+
|
|
22
|
+
| Action | macOS/Linux | Windows |
|
|
23
|
+
|--------|-------------|---------|
|
|
24
|
+
| Install | `bash scripts/install.sh` | `scripts\install.ps1` |
|
|
25
|
+
| Setup | `bash scripts/setup.sh` | `scripts\setup.bat` |
|
|
26
|
+
| Start | `bash scripts/start.sh` | `scripts\start.bat` |
|
|
27
|
+
| Stop | `bash scripts/stop.sh` | `scripts\stop.bat` |
|
|
28
|
+
| Verify | `bash scripts/verify.sh` | `scripts\verify.bat` |
|
|
29
|
+
| Status | `bash scripts/health_check.sh` | `scripts\health_check.bat` |
|
|
30
|
+
| Custom port | `bash scripts/start.sh --port 9090` | `scripts\start.bat --port 9090` |
|
|
31
|
+
| Upgrade | `bash scripts/upgrade.sh` | `powershell -File scripts\upgrade.ps1` |
|
|
32
|
+
| Check updates | `bash scripts/upgrade.sh --check` | `powershell -File scripts\upgrade.ps1 -Check` |
|
|
33
|
+
|
|
34
|
+
### System Dependencies (Linux)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
sudo apt-get install -y libfmt8 libmosquitto1 libyaml-cpp0.7
|
|
38
|
+
ldd bin/miloco-mcp-server | grep "not found" # verify
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Cross-Platform Tools
|
|
42
|
+
|
|
43
|
+
Unified interfaces that work across all platforms. System auto-detects device platform.
|
|
44
|
+
|
|
45
|
+
| Tool | Arguments | Returns |
|
|
46
|
+
|------|-----------|---------|
|
|
47
|
+
| `device/list` | `filter` (string[]), `platform` (opt) | `devices[]` with `platform` field |
|
|
48
|
+
| `device/specs` | `deviceId` (string) | Platform-specific spec: properties, actions |
|
|
49
|
+
| `platform/status` | — | All platform connection/auth/sync status |
|
|
50
|
+
| `gateway/info` | — | Version, device count, ports |
|
|
51
|
+
| `scene/list` | `platform` (string) | `scenes[]` |
|
|
52
|
+
| `scene/trigger` | `platform` (string), `sceneId` (string) | Trigger result |
|
|
53
|
+
|
|
54
|
+
> **Parameter convention:** `device/specs` uses `deviceId` (camelCase); platform-specific tools (`xiaomi/*`, `tuya/*`, etc.) use `device_id` (snake_case).
|
|
55
|
+
|
|
56
|
+
## Platform Skill Modules
|
|
57
|
+
|
|
58
|
+
Detailed tool references are split into sub-skill files by platform. Load as needed:
|
|
59
|
+
|
|
60
|
+
| Module | File | Covers |
|
|
61
|
+
|--------|------|--------|
|
|
62
|
+
| **Xiaomi** | [skills/xiaomi.md](skills/xiaomi.md) | MIOT device control, OAuth auth, camera P2P, XiaoAI speaker |
|
|
63
|
+
| **Tuya** | [skills/tuya.md](skills/tuya.md) | DP property read/write, QR code auth |
|
|
64
|
+
| **Midea** | [skills/midea.md](skills/midea.md) | AC/appliance control, account auth |
|
|
65
|
+
| **eWeLink** | [skills/ewelink.md](skills/ewelink.md) | Sonoff/eWeLink switches, multi-channel |
|
|
66
|
+
| **Automation** | [skills/automation.md](skills/automation.md) | Schedule, trigger engine, room, memory, skill system |
|
|
67
|
+
| **Extension** | [skills/extension.md](skills/extension.md) | Serial, GPIO, Xiaozhi AI, license, config, stats |
|
|
68
|
+
|
|
69
|
+
## License System
|
|
70
|
+
|
|
71
|
+
- **Free edition**: Xiaomi platform only (device control, cameras, XiaoAI, MCP proxy)
|
|
72
|
+
- **Licensed edition**: All platforms (Xiaomi + Tuya + Midea + eWeLink)
|
|
73
|
+
- `tuya/set_property`, `midea/set_property`, `ewelink/set_property` return `license_required` on free edition
|
|
74
|
+
- `get_properties` and all other tools work without license
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
`config/config.yaml`:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
server:
|
|
82
|
+
http_port: 38080
|
|
83
|
+
ws_port: 8765
|
|
84
|
+
bind_address: "0.0.0.0"
|
|
85
|
+
auth:
|
|
86
|
+
cloud_server: "cn" # cn / de / sg / us / ru / i2
|
|
87
|
+
token_file: "data/auth_token.json"
|
|
88
|
+
camera:
|
|
89
|
+
frame_interval: 500
|
|
90
|
+
buffer_max_size: 20
|
|
91
|
+
buffer_ttl: 300
|
|
92
|
+
jpeg_quality: 90
|
|
93
|
+
tuya:
|
|
94
|
+
token_file: "data/tuya_token.json"
|
|
95
|
+
midea:
|
|
96
|
+
token_file: "data/midea_token.json"
|
|
97
|
+
ewelink:
|
|
98
|
+
token_file: "data/ewelink_token.json"
|
|
99
|
+
xiaozhi:
|
|
100
|
+
endpoint: "" # ws:// or wss:// (empty = disabled)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Troubleshooting
|
|
104
|
+
|
|
105
|
+
| Symptom | Fix |
|
|
106
|
+
|---------|-----|
|
|
107
|
+
| `connection refused` | Start server: `bash scripts/start.sh` |
|
|
108
|
+
| `authorized: false` | Authorize via `xiaomi/auth_url` → `xiaomi/auth_callback` |
|
|
109
|
+
| `cannot open shared object file` | `sudo apt-get install -y libfmt8 libmosquitto1 libyaml-cpp0.7` |
|
|
110
|
+
| `Tool not found` | Check tool name (see `tools/list` output) |
|
|
111
|
+
| `key 'device_id' not found` | Platform tools use `device_id`; cross-platform `device/specs` uses `deviceId` |
|
|
112
|
+
| `license_required` error | Set license key via `license/set` tool |
|
|
113
|
+
| Library load error | `ldd bin/miloco-mcp-server \| grep "not found"` |
|
|
114
|
+
|
|
115
|
+
Full API reference: [FeyaGate_MCP_API.md](FeyaGate_MCP_API.md), [FeyaGate_HTTP_API.md](FeyaGate_HTTP_API.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
support_classes:
|
|
2
|
+
- camera
|
|
3
|
+
extra_info:
|
|
4
|
+
chuangmi.camera.068ac1:
|
|
5
|
+
channel_count: 2
|
|
6
|
+
name: 小白智能户外摄像机Q2 双摄照明版
|
|
7
|
+
vendor: 上海创米科技
|
|
8
|
+
chuangmi.camera.111ac1:
|
|
9
|
+
channel_count: 2
|
|
10
|
+
name: 小白智能摄像机 A2双摄版
|
|
11
|
+
vendor: 上海创米科技
|
|
12
|
+
chuangmi.camera.72ac1:
|
|
13
|
+
channel_count: 2
|
|
14
|
+
name: 小米智能摄像机C300双摄版
|
|
15
|
+
vendor: 上海创米科技
|
|
16
|
+
isa.camera.cw501d:
|
|
17
|
+
channel_count: 2
|
|
18
|
+
name: 小米室外摄像机 4 双摄版
|
|
19
|
+
vendor: 天津华来科技有限公司
|
|
20
|
+
isa.camera.hlmax:
|
|
21
|
+
channel_count: 2
|
|
22
|
+
name: 小米室外摄像机CW500双摄版
|
|
23
|
+
vendor: 天津华来科技有限公司
|
|
24
|
+
mxiang.camera.c500ch:
|
|
25
|
+
channel_count: 2
|
|
26
|
+
name: 小米智能摄像机C500双摄版
|
|
27
|
+
vendor: 上海摩象网络科技有限公司
|
|
28
|
+
xiaomi.camera.082ac1:
|
|
29
|
+
channel_count: 2
|
|
30
|
+
name: 小米智能摄像机 4 双摄版
|
|
31
|
+
vendor: 小米
|
|
32
|
+
xiaomi.camera.083ac1:
|
|
33
|
+
channel_count: 1
|
|
34
|
+
name: 小米智能摄像机 4 变焦版
|
|
35
|
+
vendor: 小米
|
|
36
|
+
blacklist:
|
|
37
|
+
- chuangmi.camera.ipc004b
|
|
38
|
+
- miir.camera.ir01
|
|
39
|
+
- mxiang.camera.mwc11
|
|
40
|
+
- isa.camera.hl5
|
|
41
|
+
- xiaovv.camera.xva3
|
|
42
|
+
- xzh.camera.camera
|
|
43
|
+
- cgzn.camera.camera
|
|
44
|
+
- isa.camera.virtual
|
|
45
|
+
- lumi.camera.aq1
|
|
46
|
+
- fimi.camera.c1b
|
|
47
|
+
- yunyi.camera.v1
|
|
48
|
+
- fimi.camera.c1
|
|
49
|
+
- chuangmi.camera.v4
|
|
50
|
+
- chuangmi.camera.ipc007b
|
|
51
|
+
- chuangmi.camera.ipc010
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
server:
|
|
2
|
+
ws_port: 8765
|
|
3
|
+
http_port: 38080
|
|
4
|
+
bind_address: "0.0.0.0"
|
|
5
|
+
webui_dir: "webui" # path to WebUI static files (empty = disabled)
|
|
6
|
+
|
|
7
|
+
auth:
|
|
8
|
+
cloud_server: "cn" # cn / de / sg / us / ru / i2
|
|
9
|
+
token_file: "data/auth_token.json"
|
|
10
|
+
|
|
11
|
+
camera:
|
|
12
|
+
frame_interval: 500 # ms between JPEG captures
|
|
13
|
+
buffer_max_size: 20 # max frames per camera/channel
|
|
14
|
+
buffer_ttl: 300 # seconds before frames expire
|
|
15
|
+
reconnect_min: 3 # seconds, initial reconnect delay
|
|
16
|
+
reconnect_max: 1200 # seconds, max reconnect delay
|
|
17
|
+
jpeg_quality: 90 # JPEG encode quality (1-100)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feyagate-automation
|
|
3
|
+
description: Automation tools for FeyaGate. Schedule tasks (timed device control), trigger engine (camera-based automation), room management, and memory/note system. Use when setting up scheduled actions, automated triggers, room organization, or persistent memory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Automation & Utility Tools
|
|
7
|
+
|
|
8
|
+
## Schedule
|
|
9
|
+
|
|
10
|
+
| Tool | Arguments | Returns |
|
|
11
|
+
|------|-----------|---------|
|
|
12
|
+
| `schedule/add` | `name`, `scheduledTime`, `toolName`, `toolArgs`, `repeat`, `repeatDays` (opt) | Task ID |
|
|
13
|
+
| `schedule/list` | — | `tasks[]` |
|
|
14
|
+
| `schedule/get` | `id` | Task detail |
|
|
15
|
+
| `schedule/update` | `id` + fields to update | Update result |
|
|
16
|
+
| `schedule/delete` | `id` | Delete result |
|
|
17
|
+
| `schedule/cancel` | `id` | Cancel result |
|
|
18
|
+
|
|
19
|
+
**Repeat types:** `none` / `daily` / `weekdays` / `weekends` / `weekly` / `custom_days`
|
|
20
|
+
|
|
21
|
+
**Example:**
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"name": "schedule/add",
|
|
25
|
+
"arguments": {
|
|
26
|
+
"name": "睡前关灯",
|
|
27
|
+
"scheduledTime": "2026-05-16T23:00:00+08:00",
|
|
28
|
+
"toolName": "xiaomi/set_property",
|
|
29
|
+
"toolArgs": "{\"device_id\":\"xxx\",\"siid\":2,\"piid\":1,\"value\":false}",
|
|
30
|
+
"repeat": "daily"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Trigger Engine (PC proxy)
|
|
36
|
+
|
|
37
|
+
| Tool | Arguments | Returns |
|
|
38
|
+
|------|-----------|---------|
|
|
39
|
+
| `trigger/create` | `name`, `cameras`, `condition`, `actions` | Rule ID |
|
|
40
|
+
| `trigger/list` | — | `rules[]` |
|
|
41
|
+
| `trigger/update` | `id`, fields to update | Update result |
|
|
42
|
+
| `trigger/delete` | `id` | Delete result |
|
|
43
|
+
| `trigger/toggle` | `id`, `enabled` | Toggle result |
|
|
44
|
+
| `trigger/logs` | `limit`, `rule_id` (opt) | `logs[]` |
|
|
45
|
+
|
|
46
|
+
## Room Management
|
|
47
|
+
|
|
48
|
+
| Tool | Arguments | Returns |
|
|
49
|
+
|------|-----------|---------|
|
|
50
|
+
| `room/list` | — | Unique room name list |
|
|
51
|
+
| `room/set_device` | `device_ids` (string[]), `room_name` (string) | Assignment result |
|
|
52
|
+
|
|
53
|
+
## Memory System
|
|
54
|
+
|
|
55
|
+
| Tool | Arguments | Returns |
|
|
56
|
+
|------|-----------|---------|
|
|
57
|
+
| `memory/read` | — | All long-term memories |
|
|
58
|
+
| `memory/add` | `content`, `category` (opt) | Added entry |
|
|
59
|
+
| `memory/update` | `id`, `content` | Update result |
|
|
60
|
+
| `memory/delete` | `id` | Delete result |
|
|
61
|
+
| `memory/search` | `keyword` | Matching entries |
|
|
62
|
+
| `memory/note` | `content` | Add today's note |
|
|
63
|
+
| `memory/today` | — | Today's notes |
|
|
64
|
+
|
|
65
|
+
## Skill System
|
|
66
|
+
|
|
67
|
+
| Tool | Arguments | Returns |
|
|
68
|
+
|------|-----------|---------|
|
|
69
|
+
| `skill/list` | — | Available skills |
|
|
70
|
+
| `skill/read` | `id` | Skill content |
|
|
71
|
+
| `skill/manage` | `action` (add/update/delete) + other fields | Manage result |
|