larnitech-mcp 1.0.2b0__tar.gz

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.
Files changed (54) hide show
  1. larnitech_mcp-1.0.2b0/LICENSE +21 -0
  2. larnitech_mcp-1.0.2b0/PKG-INFO +292 -0
  3. larnitech_mcp-1.0.2b0/README.md +272 -0
  4. larnitech_mcp-1.0.2b0/larnitech_mcp/__init__.py +3 -0
  5. larnitech_mcp-1.0.2b0/larnitech_mcp/__main__.py +164 -0
  6. larnitech_mcp-1.0.2b0/larnitech_mcp/client.py +165 -0
  7. larnitech_mcp-1.0.2b0/larnitech_mcp/config.py +165 -0
  8. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/api2_protocol.md +418 -0
  9. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/bugs.md +92 -0
  10. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/_device_types.md +747 -0
  11. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/ac.md +93 -0
  12. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/blinds.md +36 -0
  13. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/climate-control.md +54 -0
  14. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/co2-sensor.md +39 -0
  15. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/com-port.md +40 -0
  16. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/conditioner.md +62 -0
  17. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/dimmer-lamp.md +55 -0
  18. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/door-sensor.md +40 -0
  19. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/fancoil.md +67 -0
  20. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/gate.md +59 -0
  21. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/humidity-sensor.md +40 -0
  22. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/illumination-sensor.md +36 -0
  23. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/ir-receiver.md +40 -0
  24. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/ir-transmitter.md +34 -0
  25. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/jalousie.md +36 -0
  26. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/json.md +118 -0
  27. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/lamp.md +54 -0
  28. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/leak-sensor.md +36 -0
  29. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/light-scheme.md +72 -0
  30. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/motion-sensor.md +42 -0
  31. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/remote-control.md +41 -0
  32. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/rgb-lamp.md +55 -0
  33. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/script.md +38 -0
  34. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/switch.md +70 -0
  35. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/temperature-sensor.md +59 -0
  36. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/valve-heating.md +79 -0
  37. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/valve.md +51 -0
  38. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/vent.md +52 -0
  39. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/ventilation.md +56 -0
  40. larnitech_mcp-1.0.2b0/larnitech_mcp/docs/device-types/virtual.md +174 -0
  41. larnitech_mcp-1.0.2b0/larnitech_mcp/docs.py +270 -0
  42. larnitech_mcp-1.0.2b0/larnitech_mcp/paths.py +71 -0
  43. larnitech_mcp-1.0.2b0/larnitech_mcp/report.py +123 -0
  44. larnitech_mcp-1.0.2b0/larnitech_mcp/server.py +725 -0
  45. larnitech_mcp-1.0.2b0/larnitech_mcp/validate.py +194 -0
  46. larnitech_mcp-1.0.2b0/larnitech_mcp/watch.py +195 -0
  47. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/PKG-INFO +292 -0
  48. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/SOURCES.txt +52 -0
  49. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/dependency_links.txt +1 -0
  50. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/entry_points.txt +2 -0
  51. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/requires.txt +2 -0
  52. larnitech_mcp-1.0.2b0/larnitech_mcp.egg-info/top_level.txt +1 -0
  53. larnitech_mcp-1.0.2b0/pyproject.toml +39 -0
  54. larnitech_mcp-1.0.2b0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mykhailo Popovych, ThinkHome LLC
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.
@@ -0,0 +1,292 @@
1
+ Metadata-Version: 2.4
2
+ Name: larnitech-mcp
3
+ Version: 1.0.2b0
4
+ Summary: MCP server for Larnitech smart-home controllers over API2
5
+ Author-email: Mykhailo Popovych <m.popovych@thinkhome.io>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mpopovych-thinkhome/larnitech-mcp
8
+ Project-URL: Issues, https://github.com/mpopovych-thinkhome/larnitech-mcp/issues
9
+ Keywords: mcp,larnitech,smart-home,home-automation,api2
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Home Automation
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: mcp>=2.0
18
+ Requires-Dist: websockets>=13
19
+ Dynamic: license-file
20
+
21
+ # Larnitech MCP
22
+
23
+ <!-- mcp-name: io.github.mpopovych-thinkhome/larnitech-mcp -->
24
+
25
+ **Version 1.0.2 Beta** · [Changelog](CHANGELOG.md) · MIT licensed
26
+
27
+ An [MCP](https://modelcontextprotocol.io) server that lets an AI agent read
28
+ and control a [Larnitech](https://larnitech.com) smart-home installation over
29
+ the API2 protocol — lights, climate, blinds, sensors, meters.
30
+
31
+ It ships with a documented device-type reference built from live testing
32
+ against real controllers, so the agent looks up how a widget actually
33
+ behaves instead of guessing from key names. That matters more than it
34
+ sounds: on this platform writing `state: "closed"` to a gate is
35
+ acknowledged with `success: true` and then silently ignored, and several
36
+ climate types drop half of any two-key write. Those traps are documented,
37
+ checked before every write, and listed in this README's own safety section.
38
+
39
+ ---
40
+
41
+ ## What it does
42
+
43
+ **Reading** — open, no configuration beyond the API key.
44
+
45
+ | Tool | What it does |
46
+ |---|---|
47
+ | `list_objects` | configured controllers (never returns keys) |
48
+ | `check_connection` | connect, authorize, report device count |
49
+ | `list_devices` | full snapshot, filterable by area / type / name |
50
+ | `get_device` | status of one device by address |
51
+
52
+ **Understanding what came back** — statuses are type-specific and
53
+ occasionally not key/value at all.
54
+
55
+ | Tool | What it does |
56
+ |---|---|
57
+ | `get_docs()` | overview of every documented device type |
58
+ | `get_docs("AC")` | full detail for one type: status keys, enums, XML attributes, script byte layout, quirks |
59
+ | `get_docs("bugs")` | numbered registry of confirmed vendor bugs |
60
+ | `get_docs("protocol")` | API2 protocol reference |
61
+
62
+ Responses flag statuses that need care: an opaque `hex` blob, a
63
+ `malfunction` fault code in place of a normal reading, or an all-`null`
64
+ payload from a meter that missed its poll cycle — which means *no data*,
65
+ not zeros.
66
+
67
+ **Watching** — non-blocking, for "press the switch and tell me what moved".
68
+
69
+ | Tool | What it does |
70
+ |---|---|
71
+ | `watch_start` | begin watching; returns immediately |
72
+ | `watch_read` | drain what changed since the last read, per key `from`/`to` |
73
+ | `watch_stop` / `watch_list` | stop one / list active |
74
+
75
+ A watch keeps its own connection alive, so it survives the controller's
76
+ 5-minute idle timeout and can stay open across a long conversation.
77
+
78
+ **Writing** — off by default, two-phase, and never a single tool call.
79
+
80
+ | Tool | What it does |
81
+ |---|---|
82
+ | `set_device` | validate, preview the change, return a token — **does not write** |
83
+ | `confirm_set` | execute, then wait for the device to settle and report what actually landed |
84
+
85
+ **Learning** — findings survive the session.
86
+
87
+ | Tool | Writes to |
88
+ |---|---|
89
+ | `add_docs_note` | that device type's own doc file |
90
+ | `add_preference` | `preferences.md`, served with every `get_docs` |
91
+
92
+ **Reporting** — `report_bug` turns something you hit into a ready-to-file
93
+ issue for this repository. It composes the report, strips identifiers
94
+ (API keys, serials, hostnames, site names) and returns a prefilled link.
95
+ Nothing is posted: you open the link, read exactly what would be
96
+ published, and submit it yourself under your own account. No token is
97
+ needed by anyone.
98
+
99
+ ---
100
+
101
+ ## Requirements
102
+
103
+ - Python 3.11+
104
+ - A Larnitech controller you administer, reachable via the Larnitech cloud
105
+ or on your LAN
106
+
107
+ ## Install
108
+
109
+ ```bash
110
+ pip install larnitech-mcp
111
+ ```
112
+
113
+ A virtual environment is worth using, since you'll point Claude Code at
114
+ that interpreter's absolute path:
115
+
116
+ ```bash
117
+ python -m venv ~/.venvs/larnitech
118
+ ~/.venvs/larnitech/bin/pip install larnitech-mcp # Windows: Scripts\pip.exe
119
+ ```
120
+
121
+ Your API keys, preferences, and any device notes the agent records live in
122
+ `~/.larnitech-mcp/`, outside the package, so upgrading never touches them.
123
+
124
+ To work on the server itself, install from a clone instead:
125
+
126
+ ```bash
127
+ git clone https://github.com/mpopovych-thinkhome/larnitech-mcp.git
128
+ cd larnitech-mcp
129
+ pip install -e .
130
+ ```
131
+
132
+ ## Where to get your API key
133
+
134
+ Both values come from **LT_Setup**, Larnitech's own configuration app, on
135
+ the installation you administer:
136
+
137
+ | What | Where in LT_Setup |
138
+ |---|---|
139
+ | API key | **Security → Show API key** |
140
+ | Serial number (cloud connection) | **General** |
141
+ | IP address (LAN connection) | **General**, or your router's DHCP table |
142
+ | WebSocket port (LAN, default `2041`) | **General → API → Websocket port** |
143
+
144
+ The key grants full read and write access to the installation. Treat it
145
+ like a password.
146
+
147
+ ## Connect a controller
148
+
149
+ Register it, then store the key:
150
+
151
+ ```bash
152
+ python -m larnitech_mcp add "Home" cloud --serial YOUR_SERIAL
153
+ python -m larnitech_mcp auth "Home"
154
+ ```
155
+
156
+ `auth` prompts with hidden input and writes the key to
157
+ `~/.larnitech-mcp/project_keys.json`. The key never passes through the chat
158
+ transcript this way — prefer it over the `object_set_key` tool, which works
159
+ but leaves the key in the session log.
160
+
161
+ For a controller on your LAN instead of via the cloud:
162
+
163
+ ```bash
164
+ python -m larnitech_mcp add "Home" local --host 192.168.1.50
165
+ ```
166
+
167
+ Check it works:
168
+
169
+ ```bash
170
+ python -m larnitech_mcp test "Home" # connect, authorize, count devices
171
+ python -m larnitech_mcp devices "Home" # full snapshot, counts per type
172
+ ```
173
+
174
+ ## Add to Claude Code
175
+
176
+ Add the server to the top-level `mcpServers` object in `~/.claude.json`,
177
+ using the absolute path to the venv's Python:
178
+
179
+ ```json
180
+ {
181
+ "mcpServers": {
182
+ "larnitech": {
183
+ "command": "/home/you/.venvs/larnitech/bin/python",
184
+ "args": ["-m", "larnitech_mcp", "serve"]
185
+ }
186
+ }
187
+ }
188
+ ```
189
+
190
+ On Windows the command is the `.exe`, with escaped backslashes:
191
+
192
+ ```json
193
+ "command": "C:\\Users\\you\\.venvs\\larnitech\\Scripts\\python.exe"
194
+ ```
195
+
196
+ It must be the top-level `mcpServers` key in `~/.claude.json` itself — a
197
+ `.mcp.json` placed inside the `~/.claude/` folder is never read. To scope
198
+ it to one project instead, put the same block in a `.mcp.json` at that
199
+ project's root. Restart Claude Code fully afterwards; closing the window
200
+ is not enough.
201
+
202
+ Other MCP clients work the same way — the server runs on stdio via
203
+ `python -m larnitech_mcp serve`.
204
+
205
+ ---
206
+
207
+ ## Safety model
208
+
209
+ **Reading is open. Writing is off until you turn it on, per controller,
210
+ from a terminal:**
211
+
212
+ ```bash
213
+ python -m larnitech_mcp allow-write "Home" on
214
+ ```
215
+
216
+ There is deliberately no tool for this — an agent cannot grant itself
217
+ write access, only tell you the command.
218
+
219
+ **Every write is two calls.** `set_device` validates the payload, reads
220
+ current state, and returns a preview plus a single-use token; it never
221
+ touches the controller. `confirm_set(token)` performs the write, waits for
222
+ the device to go quiet, then reports what actually landed, including
223
+ `unrequested_changes` — anything the controller altered on its own.
224
+
225
+ **Writes can be sequences,** because some types cannot be driven with one
226
+ frame:
227
+
228
+ ```json
229
+ [{"status": {"mode": "heat"}, "delay_after": 1.0},
230
+ {"status": {"state": "on"}}]
231
+ ```
232
+
233
+ Sending `mode` and `state` together loses the `state`: the controller
234
+ re-evaluates the channel after a mode change and overrides whatever
235
+ arrived behind it. The same applies to `vent` (`state` + `fan`) and to
236
+ clearing an `automation` before switching a channel off. `set_device`
237
+ rejects the combined forms and tells you the sequence to use instead.
238
+
239
+ **No key is ever returned by a tool,** and keys are masked out of error
240
+ messages.
241
+
242
+ ## Device documentation
243
+
244
+ `larnitech_mcp/docs/device-types/` holds one file per device type plus an
245
+ index, covering the API2 status keys, XML attributes, script-side byte
246
+ layout, and every quirk confirmed by live testing. `bugs.md` alongside it is
247
+ a numbered registry of confirmed vendor bugs that the type files reference.
248
+ Read them through `get_docs` rather than by path — that also picks up
249
+ anything you've added locally.
250
+
251
+ Notes the agent records with `add_docs_note` go to `~/.larnitech-mcp/docs/`,
252
+ not into the installed package, so they survive upgrades. Your copy wins on
253
+ read; everything you haven't edited still comes from the shipped set.
254
+
255
+ This is a working knowledge base, not a spec: entries say plainly when
256
+ something is confirmed live, observed but unexplained, or still unknown.
257
+ Corrections and additions are welcome — that is the most valuable kind of
258
+ contribution here.
259
+
260
+ ## Command reference
261
+
262
+ | Command | Effect |
263
+ |---|---|
264
+ | `add <name> cloud --serial S` | register a cloud controller |
265
+ | `add <name> local --host H [--port P]` | register a LAN controller |
266
+ | `auth <name>` | store its API key (hidden prompt) |
267
+ | `list` | configured controllers, key presence |
268
+ | `test <name>` | connect, authorize, count devices |
269
+ | `devices <name> [--full]` | full snapshot, decoded |
270
+ | `allow-write <name> on\|off` | enable or disable writes |
271
+ | `remove <name>` | drop a controller and its key |
272
+ | `serve` | run the MCP server on stdio |
273
+
274
+ ## Status
275
+
276
+ Beta. Reading, watching, and writing all work and have been exercised
277
+ against live hardware, but this has been tested against a limited set of
278
+ installations. Device types documented as unconfirmed genuinely are —
279
+ see `get_docs` output and the per-type files.
280
+
281
+ Not affiliated with or endorsed by Larnitech.
282
+
283
+ ## License
284
+
285
+ [MIT](LICENSE)
286
+
287
+ ## Contact
288
+
289
+ Mykhailo Popovych
290
+ - Telegram: [t.me/M_Popovych_ThinkHome](https://t.me/M_Popovych_ThinkHome)
291
+ - Phone (WhatsApp): +370 632 89 991, +380 99 333 99 96
292
+ - Email: [m.popovych@thinkhome.io](mailto:m.popovych@thinkhome.io)
@@ -0,0 +1,272 @@
1
+ # Larnitech MCP
2
+
3
+ <!-- mcp-name: io.github.mpopovych-thinkhome/larnitech-mcp -->
4
+
5
+ **Version 1.0.2 Beta** · [Changelog](CHANGELOG.md) · MIT licensed
6
+
7
+ An [MCP](https://modelcontextprotocol.io) server that lets an AI agent read
8
+ and control a [Larnitech](https://larnitech.com) smart-home installation over
9
+ the API2 protocol — lights, climate, blinds, sensors, meters.
10
+
11
+ It ships with a documented device-type reference built from live testing
12
+ against real controllers, so the agent looks up how a widget actually
13
+ behaves instead of guessing from key names. That matters more than it
14
+ sounds: on this platform writing `state: "closed"` to a gate is
15
+ acknowledged with `success: true` and then silently ignored, and several
16
+ climate types drop half of any two-key write. Those traps are documented,
17
+ checked before every write, and listed in this README's own safety section.
18
+
19
+ ---
20
+
21
+ ## What it does
22
+
23
+ **Reading** — open, no configuration beyond the API key.
24
+
25
+ | Tool | What it does |
26
+ |---|---|
27
+ | `list_objects` | configured controllers (never returns keys) |
28
+ | `check_connection` | connect, authorize, report device count |
29
+ | `list_devices` | full snapshot, filterable by area / type / name |
30
+ | `get_device` | status of one device by address |
31
+
32
+ **Understanding what came back** — statuses are type-specific and
33
+ occasionally not key/value at all.
34
+
35
+ | Tool | What it does |
36
+ |---|---|
37
+ | `get_docs()` | overview of every documented device type |
38
+ | `get_docs("AC")` | full detail for one type: status keys, enums, XML attributes, script byte layout, quirks |
39
+ | `get_docs("bugs")` | numbered registry of confirmed vendor bugs |
40
+ | `get_docs("protocol")` | API2 protocol reference |
41
+
42
+ Responses flag statuses that need care: an opaque `hex` blob, a
43
+ `malfunction` fault code in place of a normal reading, or an all-`null`
44
+ payload from a meter that missed its poll cycle — which means *no data*,
45
+ not zeros.
46
+
47
+ **Watching** — non-blocking, for "press the switch and tell me what moved".
48
+
49
+ | Tool | What it does |
50
+ |---|---|
51
+ | `watch_start` | begin watching; returns immediately |
52
+ | `watch_read` | drain what changed since the last read, per key `from`/`to` |
53
+ | `watch_stop` / `watch_list` | stop one / list active |
54
+
55
+ A watch keeps its own connection alive, so it survives the controller's
56
+ 5-minute idle timeout and can stay open across a long conversation.
57
+
58
+ **Writing** — off by default, two-phase, and never a single tool call.
59
+
60
+ | Tool | What it does |
61
+ |---|---|
62
+ | `set_device` | validate, preview the change, return a token — **does not write** |
63
+ | `confirm_set` | execute, then wait for the device to settle and report what actually landed |
64
+
65
+ **Learning** — findings survive the session.
66
+
67
+ | Tool | Writes to |
68
+ |---|---|
69
+ | `add_docs_note` | that device type's own doc file |
70
+ | `add_preference` | `preferences.md`, served with every `get_docs` |
71
+
72
+ **Reporting** — `report_bug` turns something you hit into a ready-to-file
73
+ issue for this repository. It composes the report, strips identifiers
74
+ (API keys, serials, hostnames, site names) and returns a prefilled link.
75
+ Nothing is posted: you open the link, read exactly what would be
76
+ published, and submit it yourself under your own account. No token is
77
+ needed by anyone.
78
+
79
+ ---
80
+
81
+ ## Requirements
82
+
83
+ - Python 3.11+
84
+ - A Larnitech controller you administer, reachable via the Larnitech cloud
85
+ or on your LAN
86
+
87
+ ## Install
88
+
89
+ ```bash
90
+ pip install larnitech-mcp
91
+ ```
92
+
93
+ A virtual environment is worth using, since you'll point Claude Code at
94
+ that interpreter's absolute path:
95
+
96
+ ```bash
97
+ python -m venv ~/.venvs/larnitech
98
+ ~/.venvs/larnitech/bin/pip install larnitech-mcp # Windows: Scripts\pip.exe
99
+ ```
100
+
101
+ Your API keys, preferences, and any device notes the agent records live in
102
+ `~/.larnitech-mcp/`, outside the package, so upgrading never touches them.
103
+
104
+ To work on the server itself, install from a clone instead:
105
+
106
+ ```bash
107
+ git clone https://github.com/mpopovych-thinkhome/larnitech-mcp.git
108
+ cd larnitech-mcp
109
+ pip install -e .
110
+ ```
111
+
112
+ ## Where to get your API key
113
+
114
+ Both values come from **LT_Setup**, Larnitech's own configuration app, on
115
+ the installation you administer:
116
+
117
+ | What | Where in LT_Setup |
118
+ |---|---|
119
+ | API key | **Security → Show API key** |
120
+ | Serial number (cloud connection) | **General** |
121
+ | IP address (LAN connection) | **General**, or your router's DHCP table |
122
+ | WebSocket port (LAN, default `2041`) | **General → API → Websocket port** |
123
+
124
+ The key grants full read and write access to the installation. Treat it
125
+ like a password.
126
+
127
+ ## Connect a controller
128
+
129
+ Register it, then store the key:
130
+
131
+ ```bash
132
+ python -m larnitech_mcp add "Home" cloud --serial YOUR_SERIAL
133
+ python -m larnitech_mcp auth "Home"
134
+ ```
135
+
136
+ `auth` prompts with hidden input and writes the key to
137
+ `~/.larnitech-mcp/project_keys.json`. The key never passes through the chat
138
+ transcript this way — prefer it over the `object_set_key` tool, which works
139
+ but leaves the key in the session log.
140
+
141
+ For a controller on your LAN instead of via the cloud:
142
+
143
+ ```bash
144
+ python -m larnitech_mcp add "Home" local --host 192.168.1.50
145
+ ```
146
+
147
+ Check it works:
148
+
149
+ ```bash
150
+ python -m larnitech_mcp test "Home" # connect, authorize, count devices
151
+ python -m larnitech_mcp devices "Home" # full snapshot, counts per type
152
+ ```
153
+
154
+ ## Add to Claude Code
155
+
156
+ Add the server to the top-level `mcpServers` object in `~/.claude.json`,
157
+ using the absolute path to the venv's Python:
158
+
159
+ ```json
160
+ {
161
+ "mcpServers": {
162
+ "larnitech": {
163
+ "command": "/home/you/.venvs/larnitech/bin/python",
164
+ "args": ["-m", "larnitech_mcp", "serve"]
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ On Windows the command is the `.exe`, with escaped backslashes:
171
+
172
+ ```json
173
+ "command": "C:\\Users\\you\\.venvs\\larnitech\\Scripts\\python.exe"
174
+ ```
175
+
176
+ It must be the top-level `mcpServers` key in `~/.claude.json` itself — a
177
+ `.mcp.json` placed inside the `~/.claude/` folder is never read. To scope
178
+ it to one project instead, put the same block in a `.mcp.json` at that
179
+ project's root. Restart Claude Code fully afterwards; closing the window
180
+ is not enough.
181
+
182
+ Other MCP clients work the same way — the server runs on stdio via
183
+ `python -m larnitech_mcp serve`.
184
+
185
+ ---
186
+
187
+ ## Safety model
188
+
189
+ **Reading is open. Writing is off until you turn it on, per controller,
190
+ from a terminal:**
191
+
192
+ ```bash
193
+ python -m larnitech_mcp allow-write "Home" on
194
+ ```
195
+
196
+ There is deliberately no tool for this — an agent cannot grant itself
197
+ write access, only tell you the command.
198
+
199
+ **Every write is two calls.** `set_device` validates the payload, reads
200
+ current state, and returns a preview plus a single-use token; it never
201
+ touches the controller. `confirm_set(token)` performs the write, waits for
202
+ the device to go quiet, then reports what actually landed, including
203
+ `unrequested_changes` — anything the controller altered on its own.
204
+
205
+ **Writes can be sequences,** because some types cannot be driven with one
206
+ frame:
207
+
208
+ ```json
209
+ [{"status": {"mode": "heat"}, "delay_after": 1.0},
210
+ {"status": {"state": "on"}}]
211
+ ```
212
+
213
+ Sending `mode` and `state` together loses the `state`: the controller
214
+ re-evaluates the channel after a mode change and overrides whatever
215
+ arrived behind it. The same applies to `vent` (`state` + `fan`) and to
216
+ clearing an `automation` before switching a channel off. `set_device`
217
+ rejects the combined forms and tells you the sequence to use instead.
218
+
219
+ **No key is ever returned by a tool,** and keys are masked out of error
220
+ messages.
221
+
222
+ ## Device documentation
223
+
224
+ `larnitech_mcp/docs/device-types/` holds one file per device type plus an
225
+ index, covering the API2 status keys, XML attributes, script-side byte
226
+ layout, and every quirk confirmed by live testing. `bugs.md` alongside it is
227
+ a numbered registry of confirmed vendor bugs that the type files reference.
228
+ Read them through `get_docs` rather than by path — that also picks up
229
+ anything you've added locally.
230
+
231
+ Notes the agent records with `add_docs_note` go to `~/.larnitech-mcp/docs/`,
232
+ not into the installed package, so they survive upgrades. Your copy wins on
233
+ read; everything you haven't edited still comes from the shipped set.
234
+
235
+ This is a working knowledge base, not a spec: entries say plainly when
236
+ something is confirmed live, observed but unexplained, or still unknown.
237
+ Corrections and additions are welcome — that is the most valuable kind of
238
+ contribution here.
239
+
240
+ ## Command reference
241
+
242
+ | Command | Effect |
243
+ |---|---|
244
+ | `add <name> cloud --serial S` | register a cloud controller |
245
+ | `add <name> local --host H [--port P]` | register a LAN controller |
246
+ | `auth <name>` | store its API key (hidden prompt) |
247
+ | `list` | configured controllers, key presence |
248
+ | `test <name>` | connect, authorize, count devices |
249
+ | `devices <name> [--full]` | full snapshot, decoded |
250
+ | `allow-write <name> on\|off` | enable or disable writes |
251
+ | `remove <name>` | drop a controller and its key |
252
+ | `serve` | run the MCP server on stdio |
253
+
254
+ ## Status
255
+
256
+ Beta. Reading, watching, and writing all work and have been exercised
257
+ against live hardware, but this has been tested against a limited set of
258
+ installations. Device types documented as unconfirmed genuinely are —
259
+ see `get_docs` output and the per-type files.
260
+
261
+ Not affiliated with or endorsed by Larnitech.
262
+
263
+ ## License
264
+
265
+ [MIT](LICENSE)
266
+
267
+ ## Contact
268
+
269
+ Mykhailo Popovych
270
+ - Telegram: [t.me/M_Popovych_ThinkHome](https://t.me/M_Popovych_ThinkHome)
271
+ - Phone (WhatsApp): +370 632 89 991, +380 99 333 99 96
272
+ - Email: [m.popovych@thinkhome.io](mailto:m.popovych@thinkhome.io)
@@ -0,0 +1,3 @@
1
+ """MCP server for Larnitech smart-home controllers (API2)."""
2
+
3
+ __version__ = "0.1.0"