tolpo 0.0.0-stage → 0.2.0
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.
- package/LICENSE +21 -0
- package/README.md +526 -2
- package/bin/tolpo.js +48 -0
- package/package.json +46 -4
- package/pyproject.toml +67 -0
- package/src/tolpo/__init__.py +1 -0
- package/src/tolpo/__main__.py +3 -0
- package/src/tolpo/api.py +262 -0
- package/src/tolpo/areas/__init__.py +187 -0
- package/src/tolpo/areas/automation.py +227 -0
- package/src/tolpo/areas/camera_detection.py +893 -0
- package/src/tolpo/areas/camera_media.py +745 -0
- package/src/tolpo/areas/camera_system.py +866 -0
- package/src/tolpo/areas/common.py +25 -0
- package/src/tolpo/areas/device_admin.py +650 -0
- package/src/tolpo/areas/doorbell.py +674 -0
- package/src/tolpo/areas/home_plan.py +229 -0
- package/src/tolpo/areas/hub.py +995 -0
- package/src/tolpo/areas/lights.py +798 -0
- package/src/tolpo/areas/lock.py +367 -0
- package/src/tolpo/areas/network.py +114 -0
- package/src/tolpo/areas/overview.py +21 -0
- package/src/tolpo/areas/pairing.py +127 -0
- package/src/tolpo/areas/plugs.py +660 -0
- package/src/tolpo/areas/vacuum.py +649 -0
- package/src/tolpo/automation/__init__.py +8 -0
- package/src/tolpo/automation/engine.py +456 -0
- package/src/tolpo/automation/events.py +81 -0
- package/src/tolpo/automation/graph.py +26 -0
- package/src/tolpo/automation/models.py +194 -0
- package/src/tolpo/automation/store.py +194 -0
- package/src/tolpo/automation/sun.py +28 -0
- package/src/tolpo/automation/tools.py +61 -0
- package/src/tolpo/calls.py +92 -0
- package/src/tolpo/cli.py +240 -0
- package/src/tolpo/compat.py +30 -0
- package/src/tolpo/config.py +147 -0
- package/src/tolpo/home.py +147 -0
- package/src/tolpo/mcp_server.py +262 -0
- package/src/tolpo/media.py +630 -0
- package/src/tolpo/network/__init__.py +5 -0
- package/src/tolpo/network/catalog.py +80 -0
- package/src/tolpo/network/config.py +123 -0
- package/src/tolpo/network/enforcement.py +184 -0
- package/src/tolpo/network/models.py +223 -0
- package/src/tolpo/network/providers.py +452 -0
- package/src/tolpo/network/service.py +422 -0
- package/src/tolpo/network/storage.py +132 -0
- package/src/tolpo/policy.py +110 -0
- package/src/tolpo/provision.py +729 -0
- package/src/tolpo/registry.py +948 -0
- package/src/tolpo/snapshot.py +441 -0
- package/src/tolpo/tsvideo.py +93 -0
- package/src/tolpo/views.py +220 -0
- package/src/tolpo/web/home.css +253 -0
- package/src/tolpo/web/home.html +56 -0
- package/src/tolpo/web/home.js +1413 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 signerless
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,527 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/signerless/tolpo/main/assets/logo-dark.svg">
|
|
4
|
+
<img alt="tolpo" src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/logo-light.svg" width="280">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
<p align="center"><strong>Local-first control of TP-Link Tapo smart-home devices for AI agents.</strong></p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
|
|
12
|
+
[](https://github.com/signerless/tolpo/actions/workflows/ci.yml)
|
|
13
|
+
[](https://github.com/signerless/tolpo/blob/main/LICENSE)
|
|
14
|
+
[](https://www.python.org/downloads/)
|
|
15
|
+
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
`tolpo` turns the Tapo devices on your network into a single interface that an AI agent, or
|
|
19
|
+
your own code, can read and control. It talks to each device directly over the LAN through the open
|
|
20
|
+
source [python-kasa](https://github.com/python-kasa/python-kasa) library, so no request ever leaves
|
|
21
|
+
your home and no vendor cloud sits in the path. The only credential it needs is the Tapo account the
|
|
22
|
+
devices already belong to.
|
|
23
|
+
|
|
24
|
+
It ships two interfaces over the same core:
|
|
25
|
+
|
|
26
|
+
- An **MCP server** that assistants such as Claude use as tools (`turn off the office lights`,
|
|
27
|
+
`is any door open?`).
|
|
28
|
+
- An **HTTP API** with an OpenAPI description at `/docs`, for scripts and dashboards.
|
|
29
|
+
|
|
30
|
+
The aim is that everything the Tapo app can do on your network can also be done by the agent: lights, plugs,
|
|
31
|
+
hubs and their sensors, cameras, doorbells, robot vacuums, schedules, firmware, rooms, pairing, and even what
|
|
32
|
+
the devices say to the internet. That is a lot of power, so every action has a level, and only the first
|
|
33
|
+
levels are on until you raise them. See [What an agent can do](#what-an-agent-can-do) and the
|
|
34
|
+
[Security model](#security-model).
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- Lights, plugs, the H100/H200 hub and its sensors, cameras and the doorbell, each with live state and a list of what
|
|
39
|
+
can be changed.
|
|
40
|
+
- The parts of the Tapo app are split into areas, such as lights, plugs, cameras, doorbells and the network. tolpo
|
|
41
|
+
remembers which kinds of device it found in earlier runs, and an area for a kind you have never had stays off.
|
|
42
|
+
- Every action has a level from 0 to 4. Reading and reversible control (power, brightness, color) are on. Settings,
|
|
43
|
+
security changes and things that are hard to undo, such as pairing or a factory reset, stay off until you raise the
|
|
44
|
+
level, and the last two kinds also need a confirmation phrase on every call. Wi-Fi names, setup codes, passwords
|
|
45
|
+
and tokens are never returned.
|
|
46
|
+
- A camera you name in the config gives an agent a still picture, a short run of pictures or a saved clip, through the
|
|
47
|
+
same video channel the Tapo app uses. No extra camera account is needed.
|
|
48
|
+
- Discovery combines a broadcast with a direct per-address sweep, remembers devices between runs and retries the ones
|
|
49
|
+
that are slow to wake. When a device cannot be reached, the answer says which one and why.
|
|
50
|
+
- State is cached briefly and device names resolve at once, so an agent is not blocked on a slow hub.
|
|
51
|
+
- A full test suite runs against simulated devices, so it needs no hardware.
|
|
52
|
+
|
|
53
|
+
## Supported devices
|
|
54
|
+
|
|
55
|
+
tolpo works with whatever [python-kasa](https://github.com/python-kasa/python-kasa) can talk to, so the list is
|
|
56
|
+
longer than the hardware we own. The table keeps two things apart: what we have run against real devices, and
|
|
57
|
+
what python-kasa documents as supported but we have not tried.
|
|
58
|
+
|
|
59
|
+
| | Device | Tested here | Documented by python-kasa, not tried here | What an agent gets |
|
|
60
|
+
|---|---|---|---|---|
|
|
61
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/light.svg" width="36" alt="Light bulb"> | Color bulbs | L430C | L430P, L530B, L530E, L530EA, L535E | State, brightness, white temperature and color |
|
|
62
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/plug.svg" width="36" alt="Smart plug"> | Smart plugs | P115 | P110, P110M | State, plus power, voltage and current readings |
|
|
63
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/hub.svg" width="36" alt="Hub"> | Hubs | H200 | H100, and S200B and S200D buttons paired to a hub | The sensors paired to it show up as devices of their own, and its event log is readable. With a higher level: siren, sensor settings, pairing |
|
|
64
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/contact.svg" width="36" alt="Door and window sensor"> | Door and window sensors | T110 | | Open or closed, low-battery flag |
|
|
65
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/climate.svg" width="36" alt="Temperature and humidity sensor"> | Temperature and humidity sensors | T310 | T315 (the one with a display) | Temperature, humidity, low-battery flag |
|
|
66
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/motion.svg" width="36" alt="Motion sensor"> | Motion sensors | | T100 | Motion, as python-kasa documents it. Not checked on real hardware |
|
|
67
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/leak.svg" width="36" alt="Water leak sensor"> | Water leak sensors | T300 | | Leak alert and battery level |
|
|
68
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/camera.svg" width="36" alt="Camera"> | Cameras | C225, C610, C465 | C100, C101, C110, C200, C210, C220, C325WB, C460, C520WS, C720, TC40, TC65, TC70 | State, battery where there is one, last alert and detection switches. With a higher level: detection tuning, image and audio settings, pan and tilt, the memory card. Pictures and clips, for the cameras you enable |
|
|
69
|
+
| <img src="https://raw.githubusercontent.com/signerless/tolpo/main/assets/devices/doorbell.svg" width="36" alt="Video doorbell"> | Doorbells | D230 | D100C, D130 | State and battery. With a higher level: ring sounds, quick replies, anti-theft, wake-up |
|
|
70
|
+
| | Robot vacuums | | Models python-kasa has a cleaning module for | Status, start and stop, rooms and zones, suction and water, schedules. Built from the app's protocol and not tried on real hardware |
|
|
71
|
+
| | Door locks | | Not supported by python-kasa | Not usable yet. The DL100, DL105, DL110, DL130 and DLW10 speak a protocol python-kasa does not know. tolpo can parse their discovery record, and nothing is connected to it yet |
|
|
72
|
+
|
|
73
|
+
The C610 and the C465 are not on python-kasa's list yet, but both connected fine. The C465 needs the small
|
|
74
|
+
certificate fix in `compat.py`. The full upstream list is in
|
|
75
|
+
[python-kasa's SUPPORTED.md](https://github.com/python-kasa/python-kasa/blob/master/SUPPORTED.md).
|
|
76
|
+
|
|
77
|
+
A model that is not listed may still work, because tolpo hands the device to python-kasa and shows whatever it
|
|
78
|
+
reports. Run `tolpo scan` to find out. If a device works or does not, tell us through a
|
|
79
|
+
[device support issue](https://github.com/signerless/tolpo/issues/new?template=device_support.yml) so the table can improve.
|
|
80
|
+
|
|
81
|
+
## Requirements
|
|
82
|
+
|
|
83
|
+
- Python 3.11 or newer
|
|
84
|
+
- One or more TP-Link Tapo devices on the same network, already set up in the Tapo app
|
|
85
|
+
- Your Tapo account email and password
|
|
86
|
+
- [ffmpeg](https://ffmpeg.org), only if you want pictures from cameras
|
|
87
|
+
- [uv](https://docs.astral.sh/uv/) (or plain `pip`) to install
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
### With npx
|
|
92
|
+
|
|
93
|
+
You need [Node](https://nodejs.org) and [uv](https://docs.astral.sh/uv/). Nothing is installed globally:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx -y tolpo scan # find your devices
|
|
97
|
+
npx -y tolpo mcp # run as an MCP server
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
tolpo is a Python program. The npm package carries the Python code and runs it with uv, so the first run
|
|
101
|
+
downloads about forty Python packages and takes longer than later runs. If an AI client gives up waiting, run
|
|
102
|
+
`npx -y tolpo --help` once and start the client again.
|
|
103
|
+
|
|
104
|
+
### With Docker
|
|
105
|
+
|
|
106
|
+
For a machine that stays on, such as a Raspberry Pi or a Linux home server. The image is public, so pulling it
|
|
107
|
+
needs no login, and it is built for amd64 and arm64.
|
|
108
|
+
|
|
109
|
+
Keep the credentials in a file that only you can read, and pass it with `--env-file`:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
(umask 077; printf 'TAPO_EMAIL=you@example.com\nTAPO_PASSWORD=your-password\n' > ~/.tolpo.env)
|
|
113
|
+
|
|
114
|
+
docker run --rm -i --network host --env-file ~/.tolpo.env ghcr.io/signerless/tolpo
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
That runs the MCP server over stdio. For the HTTP API, add a token, because it is no longer bound to loopback:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
echo "TOLPO_TOKEN=$(openssl rand -hex 24)" >> ~/.tolpo.env
|
|
121
|
+
|
|
122
|
+
docker run -d --name tolpo --restart unless-stopped --network host --env-file ~/.tolpo.env \
|
|
123
|
+
ghcr.io/signerless/tolpo serve --host 0.0.0.0
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--network host` lets the container find devices by UDP broadcast, and it only works on Linux. Docker Desktop on
|
|
127
|
+
macOS and Windows runs containers in a virtual machine that cannot see your devices that way, so use the npx
|
|
128
|
+
install there.
|
|
129
|
+
|
|
130
|
+
CI builds the image for both architectures and checks the MCP handshake inside it. Finding real devices from
|
|
131
|
+
inside a container has not been tried yet. The image has no ffmpeg, so camera pictures do not work in it yet.
|
|
132
|
+
|
|
133
|
+
### From source
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/signerless/tolpo && cd tolpo
|
|
137
|
+
uv venv --python 3.13 .venv
|
|
138
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
Credentials can come from environment variables or a config file. The server reads the file first,
|
|
144
|
+
then lets the environment override it.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
export TAPO_EMAIL='you@example.com'
|
|
148
|
+
export TAPO_PASSWORD='your-password'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or `~/.config/tolpo/config.toml` (run `chmod 600` on it, since it holds a password):
|
|
152
|
+
|
|
153
|
+
```toml
|
|
154
|
+
[tapo]
|
|
155
|
+
email = "you@example.com"
|
|
156
|
+
password = "your-password"
|
|
157
|
+
# or point at an existing JSON file instead of copying the secret:
|
|
158
|
+
# credentials_file = "~/.tapo_config.json"
|
|
159
|
+
|
|
160
|
+
[server]
|
|
161
|
+
host = "127.0.0.1" # loopback only; a token is required to bind elsewhere
|
|
162
|
+
port = 8765
|
|
163
|
+
# token = "a-long-random-string"
|
|
164
|
+
# cache_ttl_seconds = 10
|
|
165
|
+
# areas = ["lights", "plugs", "hub"] # which parts offer tools; empty means every part for the devices you own
|
|
166
|
+
|
|
167
|
+
[discovery]
|
|
168
|
+
# subnets = ["192.168.1.0/24"] # default: the /24 of your default-route interface
|
|
169
|
+
# hosts = ["192.168.10.20"] # devices that ignore broadcast or live on another VLAN
|
|
170
|
+
# sweep = true # also probe every address in the subnet (networks up to /22)
|
|
171
|
+
interval_seconds = 600
|
|
172
|
+
|
|
173
|
+
[policy]
|
|
174
|
+
# The highest level of action an agent may use, from 0 to 4. See the Security model.
|
|
175
|
+
tier = 1
|
|
176
|
+
# Single device methods an agent may use above `tier`, for example ["setLedStatus"].
|
|
177
|
+
allow_methods = []
|
|
178
|
+
# Features an agent may write beyond power, brightness and color.
|
|
179
|
+
# Plain ids ("pan_left") or kind-qualified ones ("camera.state").
|
|
180
|
+
allow_write = []
|
|
181
|
+
|
|
182
|
+
[cameras]
|
|
183
|
+
# Pictures are off for every camera until you name it here, as it is called in the Tapo app.
|
|
184
|
+
snapshots = [] # for example ["Hallway Cam"]
|
|
185
|
+
# Optional. A camera account made in the Tapo app switches pictures to the standard RTSP stream.
|
|
186
|
+
# username = "camera-account-name"
|
|
187
|
+
# password = "camera-account-password"
|
|
188
|
+
# ffmpeg = "/opt/homebrew/bin/ffmpeg" # only if ffmpeg is not on your PATH
|
|
189
|
+
# clip_dir = "/Users/you/tolpo-clips" # the one folder where camera clips may be saved; no folder, no clips
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Environment variables override the file: `TOLPO_CONFIG` (the file itself), `TOLPO_CREDENTIALS_FILE`, `TOLPO_TIER`,
|
|
193
|
+
`TOLPO_AREAS` (names separated by commas), `TOLPO_SNAPSHOTS`, `TOLPO_CLIP_DIR`, `TOLPO_FFMPEG`,
|
|
194
|
+
`TOLPO_CAMERA_USERNAME` and `TOLPO_CAMERA_PASSWORD`. A `tier` that is not a whole number from 0 to 4 stops the
|
|
195
|
+
server from starting.
|
|
196
|
+
|
|
197
|
+
Some parts keep their own files, so that no secret ends up in the main one. Automation keeps its rules and alert
|
|
198
|
+
outbox, and the local rooms and groups are kept in `~/.local/state/tolpo` (change it with `TOLPO_STATE_DIR`). The
|
|
199
|
+
network area reads `~/.config/tolpo/network.toml` (change it with `TOLPO_NETWORK_CONFIG`), which names the
|
|
200
|
+
environment variable that holds each router password and never holds the password itself.
|
|
201
|
+
|
|
202
|
+
Verify your setup with a read-only scan: `npx -y tolpo scan`, or `.venv/bin/tolpo scan` from a source checkout.
|
|
203
|
+
|
|
204
|
+
## Usage with Claude (MCP)
|
|
205
|
+
|
|
206
|
+
Put your credentials in `~/.config/tolpo/config.toml` (see Configuration, and `chmod 600` it). The server
|
|
207
|
+
reads that file by itself, so nothing secret ends up in your AI client's settings. Then register the server
|
|
208
|
+
once:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
claude mcp add tolpo -- npx -y tolpo mcp
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
From a source checkout, point at your own copy instead:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
claude mcp add tolpo -- $PWD/.venv/bin/tolpo mcp
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
If you would rather keep the credentials out of a file, pass them with `-e TAPO_EMAIL=... -e TAPO_PASSWORD=...`,
|
|
221
|
+
but your client will then store them in its own settings in plain text.
|
|
222
|
+
|
|
223
|
+
After the next restart you can ask things like "dim office-1 to 30%", "is any door open?" or "what is the
|
|
224
|
+
temperature upstairs?".
|
|
225
|
+
|
|
226
|
+
| Tool | What it does |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `home_overview` | The whole house at a glance: what is on, open doors and windows, water alerts, offline devices, low batteries, temperatures |
|
|
229
|
+
| `list_devices` | Devices with state; filter by `kind` or name |
|
|
230
|
+
| `get_device` | Every feature of one device, and which ones are writable |
|
|
231
|
+
| `camera_snapshot` | One still picture from a camera you enabled |
|
|
232
|
+
| `set_power` | Switch lights and plugs; accepts several targets and globs like `office*` |
|
|
233
|
+
| `set_light` | Brightness, white temperature, or hue and saturation |
|
|
234
|
+
| `set_feature` | Any other feature, only when the policy allows it |
|
|
235
|
+
| `rescan` | Look for new or moved devices |
|
|
236
|
+
|
|
237
|
+
Read tools are marked read-only and `set_feature` is marked destructive, so MCP clients can ask for
|
|
238
|
+
confirmation where it matters. Responses are compact: all the devices of a typical home fit in under a
|
|
239
|
+
thousand tokens.
|
|
240
|
+
|
|
241
|
+
## What an agent can do
|
|
242
|
+
|
|
243
|
+
The Tapo app is split into areas. Each area is a group of MCP tools with its own page that lists every tool.
|
|
244
|
+
tolpo remembers the kinds of device it found in earlier runs, and an area for a kind you have never had is off, so a
|
|
245
|
+
house without a vacuum does not load vacuum tools. On a first run, or when a model is not recognized, every area is
|
|
246
|
+
on. Pick the areas yourself with `areas` under `[server]`, or `TOLPO_AREAS`. The tool `tolpo_areas` tells an agent
|
|
247
|
+
what is on.
|
|
248
|
+
|
|
249
|
+
| Area | What it covers | Tools |
|
|
250
|
+
|---|---|---:|
|
|
251
|
+
| [lights](https://github.com/signerless/tolpo/blob/main/docs/areas/lights.md) | Bulbs and strips: effects, presets, schedules, countdowns, Away Mode, power-on defaults | 19 |
|
|
252
|
+
| [plugs](https://github.com/signerless/tolpo/blob/main/docs/areas/plugs.md) | Plugs and switches: schedules, countdowns, energy history, electricity price, auto-off, protections | 21 |
|
|
253
|
+
| [hub](https://github.com/signerless/tolpo/blob/main/docs/areas/hub.md) | Hubs, sensors and radiator valves: siren, sensor settings, the door, leak, motion and button event log | 29 |
|
|
254
|
+
| [doorbell](https://github.com/signerless/tolpo/blob/main/docs/areas/doorbell.md) | Video doorbells and chimes: ring sounds, quick replies, anti-theft, wake-up, battery mode | 17 |
|
|
255
|
+
| [camera_detection](https://github.com/signerless/tolpo/blob/main/docs/areas/camera_detection.md) | Motion, person, pet and vehicle detection, zones, schedules, alarm and spotlight, what warns you | 17 |
|
|
256
|
+
| [camera_system](https://github.com/signerless/tolpo/blob/main/docs/areas/camera_system.md) | Pan and tilt, image, audio, battery, privacy mode, UPnP, firmware, reboot | 33 |
|
|
257
|
+
| [camera_media](https://github.com/signerless/tolpo/blob/main/docs/areas/camera_media.md) | Memory card, record plan, recording index, thumbnails of past events, clips saved to a folder | 11 |
|
|
258
|
+
| [vacuum](https://github.com/signerless/tolpo/blob/main/docs/areas/vacuum.md) | Robot vacuums: status, start and stop, rooms and zones, suction and water, schedules, wear counters | 27 |
|
|
259
|
+
| [lock](https://github.com/signerless/tolpo/blob/main/docs/areas/lock.md) | Door locks: read the state from the discovery record. Not usable yet | 1 |
|
|
260
|
+
| [device_admin](https://github.com/signerless/tolpo/blob/main/docs/areas/device_admin.md) | Names, clock, LED, firmware, and tolpo's own rooms and groups | 24 |
|
|
261
|
+
| [automation](https://github.com/signerless/tolpo/blob/main/docs/areas/automation.md) | A local rule engine in place of Smart Actions: triggers, conditions, actions and an alert outbox | 10 |
|
|
262
|
+
| [pairing](https://github.com/signerless/tolpo/blob/main/docs/areas/pairing.md) | Pair and remove hub devices, join Wi-Fi, reboot and factory reset. Most of it is level 4, a reboot is level 2 | 9 |
|
|
263
|
+
| [network](https://github.com/signerless/tolpo/blob/main/docs/areas/network.md) | What each device talks to, seen through your router, DNS server or firewall, and optional blocking. Blocking needs level 4, `mode = "enforce"` in `network.toml` and a Linux host that routes the traffic | 10 |
|
|
264
|
+
|
|
265
|
+
On top of these, `home_overview`, `list_devices`, `get_device`, `set_power`, `set_light`, `set_feature`,
|
|
266
|
+
`camera_snapshot` and `rescan` work for every device.
|
|
267
|
+
|
|
268
|
+
All the areas together are 241 tools. Their names, descriptions and parameter lists come to about 40,000 tokens. The
|
|
269
|
+
standard setup switches on only the areas that match the devices found at home. A client that loads every tool at once
|
|
270
|
+
can use `areas` to keep only what it needs.
|
|
271
|
+
|
|
272
|
+
Most of this was built from analysis of the Tapo app and tested against simulated devices. Each area page says
|
|
273
|
+
which parts were tried on real devices and which were not. Where the exact form of a request was a guess, the
|
|
274
|
+
page marks it `UNVERIFIED`, and the tool refuses when the device does not report support for it.
|
|
275
|
+
|
|
276
|
+
## Usage over HTTP
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npx -y tolpo serve # or .venv/bin/tolpo serve from a source checkout
|
|
280
|
+
# http://127.0.0.1:8765, interactive docs at /docs
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Read
|
|
285
|
+
curl -s localhost:8765/devices # everything, with state and capabilities
|
|
286
|
+
curl -s localhost:8765/devices/front-door # one device, every feature
|
|
287
|
+
curl -s "localhost:8765/devices?kind=sensor" # doors, windows, water, temperature
|
|
288
|
+
|
|
289
|
+
# Write
|
|
290
|
+
curl -s localhost:8765/devices/hallway-cam/snapshot -o picture.jpg # a camera you enabled
|
|
291
|
+
curl -s -X POST localhost:8765/devices/desk-lamp/power \
|
|
292
|
+
-H 'content-type: application/json' -d '{"on": true}'
|
|
293
|
+
curl -s -X POST localhost:8765/devices/office-1/light \
|
|
294
|
+
-H 'content-type: application/json' -d '{"brightness": 40, "color_temp": 3000}'
|
|
295
|
+
curl -s -X POST localhost:8765/batch/light \
|
|
296
|
+
-H 'content-type: application/json' -d '{"targets": ["office*"], "brightness": 60}'
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
A device is addressed by its short `id` (`office-1`), by its name in the Tapo app (case-insensitive),
|
|
300
|
+
or, in the batch endpoints, by a glob such as `office*`. If a name matches several devices the server
|
|
301
|
+
returns `409` with the candidates instead of guessing.
|
|
302
|
+
|
|
303
|
+
### The house in 3D
|
|
304
|
+
|
|
305
|
+
`tolpo serve` also serves a 3D view of the house at `http://127.0.0.1:8765/home`. It draws the rooms, puts every
|
|
306
|
+
device in its room and shows its state: lights glow in their real color, a door or window drawn on the wall opens when
|
|
307
|
+
its sensor reports open, a water leak turns red, the temperature floats over its sensor. Click a device to switch it,
|
|
308
|
+
dim it or change its color. Click a camera to watch it live. Rooms without a device are left out.
|
|
309
|
+
|
|
310
|
+
The floor plan is yours and stays on your machine, in `~/.config/tolpo/home.json` (`server.home_layout` or
|
|
311
|
+
`TOLPO_HOME_LAYOUT` to move it). The easiest way to make one is to ask your agent to "build my home plan": with the
|
|
312
|
+
`home_plan_draft`, `home_plan_get` and `home_plan_set` tools it drafts a plan from your devices, asks you about your
|
|
313
|
+
rooms and saves it. You can also press `Edit layout` in the view, drag devices where they belong, and save. The format
|
|
314
|
+
is in [docs/areas/home_plan.md](https://github.com/signerless/tolpo/blob/main/docs/areas/home_plan.md):
|
|
315
|
+
|
|
316
|
+
```json
|
|
317
|
+
{"version": 1,
|
|
318
|
+
"rooms": [{"id": "office", "name": "Office", "x": -7, "z": -5, "w": 5, "d": 5},
|
|
319
|
+
{"id": "garden", "name": "Garden", "x": -9, "z": -12, "w": 21, "d": 7, "outside": true}],
|
|
320
|
+
"devices": {"office-1": {"room": "office", "x": -5.75, "z": -3.75},
|
|
321
|
+
"garden-cam": {"room": "garden", "x": 1.5, "z": -5.6, "yaw": 0}}}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Live video comes from `GET /devices/{id}/live`, a stream of JPEG pictures (about 10 per second) from a camera you listed
|
|
325
|
+
in `cameras.snapshots`. One stream per camera and two cameras at once. A view ends after 15 minutes, or after 2 minutes
|
|
326
|
+
on a battery camera, because live video keeps it awake. The page loads three.js from one pinned jsDelivr path and may
|
|
327
|
+
talk to this server only. It asks battery cameras for their state every five minutes, not every few seconds, so it does
|
|
328
|
+
not wake them.
|
|
329
|
+
|
|
330
|
+
## Camera pictures
|
|
331
|
+
|
|
332
|
+
An agent can ask a camera for one still picture. Every camera is off until you name it in the config, with the
|
|
333
|
+
name it has in the Tapo app:
|
|
334
|
+
|
|
335
|
+
```toml
|
|
336
|
+
[cameras]
|
|
337
|
+
snapshots = ["Hallway Cam"]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
You also need [ffmpeg](https://ffmpeg.org) on the machine that runs tolpo. Check the setup yourself before an
|
|
341
|
+
agent uses it. This saves one picture to a file that only you can read:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
npx -y tolpo snapshot "Hallway Cam" -o hallway.jpg
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
To watch a camera live on your own screen, use `npx -y tolpo watch "Hallway Cam"`. It opens ffplay, which comes with
|
|
348
|
+
ffmpeg, and closes when you close the window. `--seconds 30` stops it sooner. Nothing is recorded and nothing goes to
|
|
349
|
+
an agent.
|
|
350
|
+
|
|
351
|
+
tolpo takes one frame from the camera's live stream with ffmpeg and returns it as a JPEG, at most 1280 pixels
|
|
352
|
+
wide. `--quality high` asks for the main stream instead of the small one. There are two ways to reach the stream:
|
|
353
|
+
|
|
354
|
+
- The video channel of the Tapo app, on port 8800 of the camera. It uses the Tapo account tolpo already has, so
|
|
355
|
+
there is nothing more to set up.
|
|
356
|
+
- RTSP, if you create a camera account in the Tapo app and add it under `[cameras]` as `username` and `password`.
|
|
357
|
+
When a camera account is configured, tolpo uses RTSP.
|
|
358
|
+
|
|
359
|
+
What to know before you turn it on:
|
|
360
|
+
|
|
361
|
+
- A picture goes to the AI model your agent runs on. Enable only the cameras you are happy for it to see.
|
|
362
|
+
- A camera in privacy mode gives no picture. An agent cannot switch privacy mode off unless you allow
|
|
363
|
+
`camera.state` in `allow_write`.
|
|
364
|
+
- Every picture is logged with the camera and the time, never with the picture itself.
|
|
365
|
+
- If a camera rejects the login, tolpo leaves it alone for five minutes, because repeated failures can lock a
|
|
366
|
+
camera out.
|
|
367
|
+
- A picture is one frame from now, not a recording. Battery cameras can be asleep and may not answer.
|
|
368
|
+
- With RTSP, ffmpeg receives the camera account on its command line, so other users on the same machine can see
|
|
369
|
+
it for a moment.
|
|
370
|
+
- The doorbell sits behind the hub and has no stream of its own, so it gives no picture.
|
|
371
|
+
|
|
372
|
+
The `camera_media` area builds on this. It can read the memory card and its recording index, return the thumbnail
|
|
373
|
+
of a past event, take a short run of pictures from the live stream, and save a recording to the one folder you set
|
|
374
|
+
as `clip_dir`. Thumbnails, runs of pictures and clips follow the same rules as pictures: only cameras you named, never
|
|
375
|
+
in privacy mode, and the tool that saves a clip returns the path and never the video. Reading the card status, the
|
|
376
|
+
record plan and the list of recordings and events works for any camera. Formatting a card is a level 4 action.
|
|
377
|
+
|
|
378
|
+
## Security model
|
|
379
|
+
|
|
380
|
+
This server controls physical things, so it refuses by default and widens only on request.
|
|
381
|
+
|
|
382
|
+
Every action on a device has a level. You choose the highest level an agent may use with `tier` under `[policy]`
|
|
383
|
+
(1 by default), and you can allow single device methods above it with `allow_methods`.
|
|
384
|
+
|
|
385
|
+
| Level | Name | What it is | Examples |
|
|
386
|
+
|---|---|---|---|
|
|
387
|
+
| 0 | Read | Look at anything that is not secret | State, settings, energy, event logs, a memory card index |
|
|
388
|
+
| 1 | Control | Reversible, as you would do by hand | Power, brightness, color, a camera sweep, vacuum start and stop |
|
|
389
|
+
| 2 | Setting | Changes behavior | Schedules, detection tuning, defaults, renaming, automation rules |
|
|
390
|
+
| 3 | Security | Matters for safety or privacy | Camera privacy mode, a siren, alarm and arm state, a camera light turned off, UPnP, audio out |
|
|
391
|
+
| 4 | Lifecycle | Hard to undo | Pairing and removing devices, factory reset, firmware install, Wi-Fi change, firewall changes, formatting a memory card |
|
|
392
|
+
|
|
393
|
+
Levels 3 and 4 also need a confirmation phrase on every call, such as `setLedStatus hallway-cam`. The refusal says
|
|
394
|
+
what the phrase is, so the phrase is a pause for the agent to ask the person, not proof that the person agreed. The
|
|
395
|
+
phrase names the action and the device (a few name a change or a plan instead), so one confirmation cannot be reused
|
|
396
|
+
for another device or another action. A refusal says what the action is, and never how to widen the permission.
|
|
397
|
+
|
|
398
|
+
| Scope | Allowed |
|
|
399
|
+
|---|---|
|
|
400
|
+
| Read | Everything that is not secret. The Wi-Fi name, setup codes, passwords, tokens, keys and MAC addresses are never returned |
|
|
401
|
+
| Control | Power, brightness, color temperature, color and light presets, on lights and plugs, at level 1 |
|
|
402
|
+
| More | Whatever level you allow with `tier`, or single methods named in `allow_methods` |
|
|
403
|
+
| Features | Anything else written through `set_feature`, named in `policy.allow_write` (alarm test, pan/tilt, detection switches) |
|
|
404
|
+
| Pictures | Only from the cameras named in `cameras.snapshots`. Off by default. The other camera tools (settings, card index, event lists) work for every camera at the level you allow |
|
|
405
|
+
| Pairing | Only through the pairing tools, at level 4. The hub `pair` and `unpair` features stay blocked in `set_feature` |
|
|
406
|
+
|
|
407
|
+
Cameras are read-only through `set_feature`: the `state` switch on a camera is its privacy shutter, not a power
|
|
408
|
+
relay, so it is not treated as one. Enable it deliberately with `allow_write = ["camera.state"]`, or use the
|
|
409
|
+
privacy tool at level 3. A feature named in `allow_write` is allowed at any level and needs no phrase, so name only
|
|
410
|
+
what you mean to give away.
|
|
411
|
+
|
|
412
|
+
Rules that run without anyone asking (automation) are held to the same levels at the moment they run, not only when
|
|
413
|
+
they are made. Setting them up needs level 2. An agent can stop all rules at any time, but starting them again
|
|
414
|
+
needs level 3 and a phrase. You can do it yourself with `tolpo automation resume`, which a server that is already
|
|
415
|
+
running picks up when it restarts.
|
|
416
|
+
|
|
417
|
+
The server listens on `127.0.0.1` only. It refuses to start on any other address without a bearer
|
|
418
|
+
token, and without a token it also rejects requests whose `Host` header is not local, which blocks
|
|
419
|
+
DNS-rebinding attacks from a web page.
|
|
420
|
+
|
|
421
|
+
Device names are free text typed by whoever set the device up. The server flattens and shortens them
|
|
422
|
+
before an agent sees them, and the instructions it sends tell the agent to treat names and readings as
|
|
423
|
+
data, not commands. A shared Tapo account still means anyone in it can rename a device, so keep the
|
|
424
|
+
account to people you trust.
|
|
425
|
+
|
|
426
|
+
## How discovery handles a busy network
|
|
427
|
+
|
|
428
|
+
Homes often have many devices, some battery-powered and slow to answer. Discovery is designed for that:
|
|
429
|
+
|
|
430
|
+
- A UDP broadcast, followed by a direct query to each address in the subnet (`discovery.sweep`),
|
|
431
|
+
because some battery cameras ignore broadcasts yet always answer a direct question.
|
|
432
|
+
- Devices seen before are remembered in `~/.cache/tolpo/hosts.json` (address, name and model,
|
|
433
|
+
no secrets) and queried directly at startup. A device missing from one scan is never forgotten;
|
|
434
|
+
entries unseen for 30 days are dropped.
|
|
435
|
+
- Devices that do not answer are retried after 5, 15 and 45 seconds, and again on every rescan.
|
|
436
|
+
|
|
437
|
+
Startup is staged so the server is useful immediately: the first scan runs in the background, and each
|
|
438
|
+
device is connected the moment it answers. A device you name exactly is usable as soon as it is up,
|
|
439
|
+
without waiting for the hub, which can take about ten seconds from cold. Operations that span the whole
|
|
440
|
+
house wait for the scan, but never longer than fifteen seconds; anything still connecting finishes in
|
|
441
|
+
the background.
|
|
442
|
+
|
|
443
|
+
When something cannot be reached, the answers say so. `home_overview` and `list_devices` carry
|
|
444
|
+
`complete: false` and a `not_connected` list with the name, model and reason for each unreachable
|
|
445
|
+
device, so an agent does not mistake an offline door sensor for a closed door.
|
|
446
|
+
|
|
447
|
+
## For AI agents
|
|
448
|
+
|
|
449
|
+
Call `tolpo_areas` first. It lists the parts of tolpo that are on, what each one offers, and the level the owner
|
|
450
|
+
allows. If you are an AI agent asked to set tolpo up or to use it, start with
|
|
451
|
+
[llms.txt](https://raw.githubusercontent.com/signerless/tolpo/main/llms.txt). If you are asked to change the code,
|
|
452
|
+
read [AGENTS.md](https://github.com/signerless/tolpo/blob/main/AGENTS.md) first.
|
|
453
|
+
|
|
454
|
+
## Development
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
.venv/bin/python -m pytest # runs against simulated devices, no hardware required
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Project layout:
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
src/tolpo/
|
|
464
|
+
registry.py device discovery, state cache, control operations
|
|
465
|
+
policy.py what may be read and written (deny-by-default)
|
|
466
|
+
views.py shared presentation for the HTTP and MCP layers
|
|
467
|
+
api.py FastAPI HTTP server
|
|
468
|
+
mcp_server.py MCP server (stdio)
|
|
469
|
+
config.py config file and environment loading
|
|
470
|
+
compat.py small fixes for devices python-kasa cannot reach yet
|
|
471
|
+
snapshot.py one picture from a camera, taken with ffmpeg
|
|
472
|
+
media.py the video channel of the Tapo app (port 8800)
|
|
473
|
+
tsvideo.py finds the video track in a camera's stream
|
|
474
|
+
calls.py the one way an area sends a method to a device, checked against its level
|
|
475
|
+
provision.py Wi-Fi setup of a new device from this machine (SoftAP), behind a plan
|
|
476
|
+
areas/ one file per area of the Tapo app; adding an area adds a file
|
|
477
|
+
automation/ the local rule engine
|
|
478
|
+
network/ providers for routers, DNS servers and firewalls
|
|
479
|
+
cli.py serve / mcp / scan / snapshot / watch / automation entry points
|
|
480
|
+
tests/ full suite against simulated devices
|
|
481
|
+
docs/protocols.md how Tapo devices talk on the local network
|
|
482
|
+
docs/areas/ one page per area, with every tool and what was only tried with simulated devices
|
|
483
|
+
assets/ logo, device icons and social preview
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
See [CONTRIBUTING.md](https://github.com/signerless/tolpo/blob/main/CONTRIBUTING.md) for the ground rules, [docs/protocols.md](https://github.com/signerless/tolpo/blob/main/docs/protocols.md) for the protocol notes and
|
|
487
|
+
[SECURITY.md](https://github.com/signerless/tolpo/blob/main/SECURITY.md) to report a vulnerability.
|
|
488
|
+
|
|
489
|
+
## Background
|
|
490
|
+
|
|
491
|
+
The protocols behind Tapo devices are not documented by the vendor. Part of this project is independent
|
|
492
|
+
analysis of the official Tapo app and of real devices, done so that software can interoperate with hardware
|
|
493
|
+
its owners already have. The result is written down in [docs/protocols.md](https://github.com/signerless/tolpo/blob/main/docs/protocols.md): which
|
|
494
|
+
transport each model uses, how discovery works, what the newer TPAP protocol looks like, and what we could not
|
|
495
|
+
establish. The notes are in our own words. They contain no vendor code, keys or credentials, and tolpo itself
|
|
496
|
+
only ever signs in with your own Tapo account.
|
|
497
|
+
|
|
498
|
+
## Compatibility and status
|
|
499
|
+
|
|
500
|
+
This is a development version after 0.1. Lights, plugs, hubs and sensors are supported over the KLAP and SSL-AES
|
|
501
|
+
transports through `python-kasa`, plus a small patch (`compat.py`) for cameras with an ECDSA certificate, such as
|
|
502
|
+
the C465, that `python-kasa` cannot reach on its own.
|
|
503
|
+
|
|
504
|
+
The areas beyond the first release were built from analysis of the Tapo app and tested with simulated devices.
|
|
505
|
+
Reading was tried on a real home with four bulbs, a hub, eight sensors, a doorbell, three cameras and a plug. A few
|
|
506
|
+
reversible writes were tried there too: the bulbs (power, brightness, color, power-on defaults, saved presets and
|
|
507
|
+
disabled rules), the plug (power, countdown, LED, power-on default and disabled rules), a camera light, the hub light, a
|
|
508
|
+
microphone volume, a detection switch and an automation rule, each set back afterwards. Most other writes have not been
|
|
509
|
+
tried on real hardware, and the area pages say which. Robot vacuums, door locks, radiator valves and the firewall providers were never
|
|
510
|
+
tried on the real thing.
|
|
511
|
+
|
|
512
|
+
Known limits: cameras give still pictures, short runs of pictures and saved clips, but no continuous video to an
|
|
513
|
+
agent, and the doorbell gives none; the cloud-only parts of the app (account, homes, sharing, push notifications,
|
|
514
|
+
Tapo Care, voice assistants) are not reachable, and the automation area replaces some of them locally; the MCP
|
|
515
|
+
server runs over stdio, one process per client, with no shared HTTP transport yet; the HTTP API has the original
|
|
516
|
+
routes plus a few for the camera memory card (`/devices/{id}/storage`, `/recordings/...`) and the door lock state, and
|
|
517
|
+
the other areas are MCP tools only; and devices on a second subnet have not been tested.
|
|
518
|
+
|
|
519
|
+
## License
|
|
520
|
+
|
|
521
|
+
MIT, see [LICENSE](https://github.com/signerless/tolpo/blob/main/LICENSE).
|
|
522
|
+
|
|
523
|
+
This project is not affiliated with or endorsed by TP-Link. Tapo is a trademark of TP-Link. All device
|
|
524
|
+
communication goes through the open source
|
|
525
|
+
[python-kasa](https://github.com/python-kasa/python-kasa) library.
|
|
526
|
+
|
|
527
|
+
<!-- mcp-name: io.github.signerless/tolpo -->
|
package/bin/tolpo.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// tolpo is a Python program. This launcher exists so that `npx tolpo mcp` works: it runs the Python code
|
|
5
|
+
// shipped inside this package with uvx, which builds an isolated environment the first time.
|
|
6
|
+
//
|
|
7
|
+
// The launcher must never write to stdout. With `tolpo mcp`, stdout carries the MCP protocol, and
|
|
8
|
+
// stdin, stdout and stderr are handed to the child process untouched.
|
|
9
|
+
|
|
10
|
+
const { spawn } = require("node:child_process");
|
|
11
|
+
const { constants } = require("node:os");
|
|
12
|
+
const path = require("node:path");
|
|
13
|
+
|
|
14
|
+
const root = path.resolve(__dirname, "..");
|
|
15
|
+
|
|
16
|
+
const MISSING_UV = `tolpo runs its Python code through uv, and uvx was not found on your PATH.
|
|
17
|
+
|
|
18
|
+
Install uv, then run this again:
|
|
19
|
+
macOS and Linux: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
20
|
+
Homebrew: brew install uv
|
|
21
|
+
Windows: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
22
|
+
|
|
23
|
+
More options: https://docs.astral.sh/uv/getting-started/installation/
|
|
24
|
+
If an editor or an AI client starts tolpo, make sure it can see uv: use the full path to uvx or add its
|
|
25
|
+
folder to the PATH that the client uses.
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const child = spawn("uvx", ["--from", root, "tolpo", ...process.argv.slice(2)], { stdio: "inherit" });
|
|
29
|
+
|
|
30
|
+
child.on("error", (error) => {
|
|
31
|
+
if (error.code === "ENOENT") {
|
|
32
|
+
process.stderr.write(MISSING_UV);
|
|
33
|
+
process.exit(127);
|
|
34
|
+
}
|
|
35
|
+
process.stderr.write(`tolpo could not start uvx: ${error.message}\n`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on("exit", (code, signal) => {
|
|
40
|
+
if (signal) {
|
|
41
|
+
process.exit(128 + (constants.signals[signal] || 0));
|
|
42
|
+
}
|
|
43
|
+
process.exit(code === null ? 1 : code);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
47
|
+
process.on(signal, () => child.kill(signal));
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tolpo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Local-first MCP server and HTTP API that let AI agents read and control TP-Link Tapo smart-home devices",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "signerless",
|
|
7
|
+
"homepage": "https://github.com/signerless/tolpo#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/signerless/tolpo.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/signerless/tolpo/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"tapo",
|
|
19
|
+
"tp-link",
|
|
20
|
+
"smart-home",
|
|
21
|
+
"home-automation",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"local-first"
|
|
24
|
+
],
|
|
25
|
+
"mcpName": "io.github.signerless/tolpo",
|
|
26
|
+
"bin": {
|
|
27
|
+
"tolpo": "bin/tolpo.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/",
|
|
31
|
+
"src/tolpo/*.py",
|
|
32
|
+
"src/tolpo/areas/*.py",
|
|
33
|
+
"src/tolpo/automation/*.py",
|
|
34
|
+
"src/tolpo/network/*.py",
|
|
35
|
+
"src/tolpo/web/home.html",
|
|
36
|
+
"src/tolpo/web/home.js",
|
|
37
|
+
"src/tolpo/web/home.css",
|
|
38
|
+
"pyproject.toml",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"test": "node --test"
|
|
47
|
+
}
|
|
48
|
+
}
|