wirestudio 0.10.0__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 (173) hide show
  1. wirestudio-0.10.0/LICENSE +21 -0
  2. wirestudio-0.10.0/PKG-INFO +563 -0
  3. wirestudio-0.10.0/README.md +540 -0
  4. wirestudio-0.10.0/pyproject.toml +57 -0
  5. wirestudio-0.10.0/setup.cfg +4 -0
  6. wirestudio-0.10.0/tests/test_active_design.py +304 -0
  7. wirestudio-0.10.0/tests/test_agent.py +355 -0
  8. wirestudio-0.10.0/tests/test_api.py +356 -0
  9. wirestudio-0.10.0/tests/test_ascii_gen.py +141 -0
  10. wirestudio-0.10.0/tests/test_compatibility.py +307 -0
  11. wirestudio-0.10.0/tests/test_design_events.py +139 -0
  12. wirestudio-0.10.0/tests/test_designs.py +180 -0
  13. wirestudio-0.10.0/tests/test_designs_seed.py +185 -0
  14. wirestudio-0.10.0/tests/test_enclosure.py +126 -0
  15. wirestudio-0.10.0/tests/test_enclosure_search.py +211 -0
  16. wirestudio-0.10.0/tests/test_fleet.py +463 -0
  17. wirestudio-0.10.0/tests/test_kicad.py +232 -0
  18. wirestudio-0.10.0/tests/test_kicad_import.py +167 -0
  19. wirestudio-0.10.0/tests/test_library_rtttl.py +91 -0
  20. wirestudio-0.10.0/tests/test_mcp_auth.py +122 -0
  21. wirestudio-0.10.0/tests/test_mcp_resources.py +206 -0
  22. wirestudio-0.10.0/tests/test_mcp_server.py +249 -0
  23. wirestudio-0.10.0/tests/test_pin_solver.py +283 -0
  24. wirestudio-0.10.0/tests/test_recommend.py +265 -0
  25. wirestudio-0.10.0/tests/test_schema.py +34 -0
  26. wirestudio-0.10.0/tests/test_serve.py +82 -0
  27. wirestudio-0.10.0/tests/test_tools_add_bus.py +90 -0
  28. wirestudio-0.10.0/tests/test_yaml_gen.py +653 -0
  29. wirestudio-0.10.0/wirestudio/__init__.py +3 -0
  30. wirestudio-0.10.0/wirestudio/agent/__init__.py +8 -0
  31. wirestudio-0.10.0/wirestudio/agent/agent.py +313 -0
  32. wirestudio-0.10.0/wirestudio/agent/session.py +65 -0
  33. wirestudio-0.10.0/wirestudio/agent/tools.py +518 -0
  34. wirestudio-0.10.0/wirestudio/api/__init__.py +3 -0
  35. wirestudio-0.10.0/wirestudio/api/__main__.py +45 -0
  36. wirestudio-0.10.0/wirestudio/api/app.py +884 -0
  37. wirestudio-0.10.0/wirestudio/api/schemas.py +236 -0
  38. wirestudio-0.10.0/wirestudio/api/serve.py +73 -0
  39. wirestudio-0.10.0/wirestudio/csp/__init__.py +20 -0
  40. wirestudio-0.10.0/wirestudio/csp/compatibility.py +414 -0
  41. wirestudio-0.10.0/wirestudio/csp/pin_solver.py +521 -0
  42. wirestudio-0.10.0/wirestudio/designs/__init__.py +11 -0
  43. wirestudio-0.10.0/wirestudio/designs/active.py +36 -0
  44. wirestudio-0.10.0/wirestudio/designs/events.py +108 -0
  45. wirestudio-0.10.0/wirestudio/designs/seed.py +200 -0
  46. wirestudio-0.10.0/wirestudio/designs/store.py +129 -0
  47. wirestudio-0.10.0/wirestudio/enclosure/__init__.py +30 -0
  48. wirestudio-0.10.0/wirestudio/enclosure/openscad.py +195 -0
  49. wirestudio-0.10.0/wirestudio/enclosure/search.py +217 -0
  50. wirestudio-0.10.0/wirestudio/examples/__init__.py +1 -0
  51. wirestudio-0.10.0/wirestudio/examples/attic-logger.json +82 -0
  52. wirestudio-0.10.0/wirestudio/examples/awning-control.json +158 -0
  53. wirestudio-0.10.0/wirestudio/examples/bl0906-mainmeter.json +67 -0
  54. wirestudio-0.10.0/wirestudio/examples/bluemotion.json +97 -0
  55. wirestudio-0.10.0/wirestudio/examples/bluesonoff.json +77 -0
  56. wirestudio-0.10.0/wirestudio/examples/desk-climate.json +69 -0
  57. wirestudio-0.10.0/wirestudio/examples/desk-matrix.json +72 -0
  58. wirestudio-0.10.0/wirestudio/examples/distance-sensor.json +119 -0
  59. wirestudio-0.10.0/wirestudio/examples/esp32-audio.json +124 -0
  60. wirestudio-0.10.0/wirestudio/examples/garage-motion.json +90 -0
  61. wirestudio-0.10.0/wirestudio/examples/keypad.json +86 -0
  62. wirestudio-0.10.0/wirestudio/examples/multi-temp.json +98 -0
  63. wirestudio-0.10.0/wirestudio/examples/nextion-thermostat.json +86 -0
  64. wirestudio-0.10.0/wirestudio/examples/oled.json +87 -0
  65. wirestudio-0.10.0/wirestudio/examples/parking-distance.json +75 -0
  66. wirestudio-0.10.0/wirestudio/examples/rc522.json +132 -0
  67. wirestudio-0.10.0/wirestudio/examples/room-climate.json +81 -0
  68. wirestudio-0.10.0/wirestudio/examples/rs485-energy.json +72 -0
  69. wirestudio-0.10.0/wirestudio/examples/securitypanel.json +151 -0
  70. wirestudio-0.10.0/wirestudio/examples/smart-plug-v1.json +84 -0
  71. wirestudio-0.10.0/wirestudio/examples/smart-plug.json +87 -0
  72. wirestudio-0.10.0/wirestudio/examples/ttgo-lora32.json +136 -0
  73. wirestudio-0.10.0/wirestudio/examples/tuya-smart-plug.json +97 -0
  74. wirestudio-0.10.0/wirestudio/examples/wasserpir.json +79 -0
  75. wirestudio-0.10.0/wirestudio/examples/weather-station.json +95 -0
  76. wirestudio-0.10.0/wirestudio/examples/wemosgps.json +101 -0
  77. wirestudio-0.10.0/wirestudio/fleet/__init__.py +4 -0
  78. wirestudio-0.10.0/wirestudio/fleet/client.py +266 -0
  79. wirestudio-0.10.0/wirestudio/generate/__init__.py +4 -0
  80. wirestudio-0.10.0/wirestudio/generate/__main__.py +48 -0
  81. wirestudio-0.10.0/wirestudio/generate/ascii_gen.py +114 -0
  82. wirestudio-0.10.0/wirestudio/generate/yaml_gen.py +321 -0
  83. wirestudio-0.10.0/wirestudio/kicad/__init__.py +5 -0
  84. wirestudio-0.10.0/wirestudio/kicad/generator.py +331 -0
  85. wirestudio-0.10.0/wirestudio/kicad/import.py +11 -0
  86. wirestudio-0.10.0/wirestudio/kicad/importer.py +232 -0
  87. wirestudio-0.10.0/wirestudio/kicad/symbol_parser.py +152 -0
  88. wirestudio-0.10.0/wirestudio/library/__init__.py +198 -0
  89. wirestudio-0.10.0/wirestudio/library/boards/esp01_1m.yaml +30 -0
  90. wirestudio-0.10.0/wirestudio/library/boards/esp32-c3-devkitm-1.yaml +53 -0
  91. wirestudio-0.10.0/wirestudio/library/boards/esp32-devkitc-v4.yaml +83 -0
  92. wirestudio-0.10.0/wirestudio/library/boards/esp32-s3-devkitc-1.yaml +77 -0
  93. wirestudio-0.10.0/wirestudio/library/boards/esp32-wrover-cam.yaml +71 -0
  94. wirestudio-0.10.0/wirestudio/library/boards/esp32cam-ai-thinker.yaml +78 -0
  95. wirestudio-0.10.0/wirestudio/library/boards/esp8285-1m.yaml +45 -0
  96. wirestudio-0.10.0/wirestudio/library/boards/m5stack-atom.yaml +49 -0
  97. wirestudio-0.10.0/wirestudio/library/boards/m5stack-atoms3.yaml +67 -0
  98. wirestudio-0.10.0/wirestudio/library/boards/nodemcu-32s.yaml +72 -0
  99. wirestudio-0.10.0/wirestudio/library/boards/nodemcu-v2.yaml +60 -0
  100. wirestudio-0.10.0/wirestudio/library/boards/ttgo-lora32-v1.yaml +91 -0
  101. wirestudio-0.10.0/wirestudio/library/boards/ttgo-t-beam.yaml +75 -0
  102. wirestudio-0.10.0/wirestudio/library/boards/wemos-d1-mini.yaml +67 -0
  103. wirestudio-0.10.0/wirestudio/library/components/adc.yaml +71 -0
  104. wirestudio-0.10.0/wirestudio/library/components/ads1115.yaml +61 -0
  105. wirestudio-0.10.0/wirestudio/library/components/ads1115_channel.yaml +69 -0
  106. wirestudio-0.10.0/wirestudio/library/components/aht10.yaml +63 -0
  107. wirestudio-0.10.0/wirestudio/library/components/apa102.yaml +66 -0
  108. wirestudio-0.10.0/wirestudio/library/components/bh1750.yaml +54 -0
  109. wirestudio-0.10.0/wirestudio/library/components/bl0906.yaml +97 -0
  110. wirestudio-0.10.0/wirestudio/library/components/bme280.yaml +51 -0
  111. wirestudio-0.10.0/wirestudio/library/components/bmp180.yaml +53 -0
  112. wirestudio-0.10.0/wirestudio/library/components/bmp280.yaml +61 -0
  113. wirestudio-0.10.0/wirestudio/library/components/cc1101.yaml +77 -0
  114. wirestudio-0.10.0/wirestudio/library/components/cse7766.yaml +58 -0
  115. wirestudio-0.10.0/wirestudio/library/components/dht.yaml +54 -0
  116. wirestudio-0.10.0/wirestudio/library/components/ds18b20.yaml +63 -0
  117. wirestudio-0.10.0/wirestudio/library/components/esp32_camera.yaml +103 -0
  118. wirestudio-0.10.0/wirestudio/library/components/esp32_rmt_led_strip.yaml +59 -0
  119. wirestudio-0.10.0/wirestudio/library/components/gpio_input.yaml +61 -0
  120. wirestudio-0.10.0/wirestudio/library/components/gpio_output.yaml +51 -0
  121. wirestudio-0.10.0/wirestudio/library/components/hc-sr04.yaml +55 -0
  122. wirestudio-0.10.0/wirestudio/library/components/hc-sr501.yaml +52 -0
  123. wirestudio-0.10.0/wirestudio/library/components/hlw8012.yaml +79 -0
  124. wirestudio-0.10.0/wirestudio/library/components/htu21d.yaml +53 -0
  125. wirestudio-0.10.0/wirestudio/library/components/hx711.yaml +60 -0
  126. wirestudio-0.10.0/wirestudio/library/components/ili9xxx.yaml +78 -0
  127. wirestudio-0.10.0/wirestudio/library/components/lcd_pcf8574.yaml +59 -0
  128. wirestudio-0.10.0/wirestudio/library/components/ld2420.yaml +57 -0
  129. wirestudio-0.10.0/wirestudio/library/components/max31855.yaml +58 -0
  130. wirestudio-0.10.0/wirestudio/library/components/max7219.yaml +62 -0
  131. wirestudio-0.10.0/wirestudio/library/components/max98357a.yaml +46 -0
  132. wirestudio-0.10.0/wirestudio/library/components/mcp23008.yaml +50 -0
  133. wirestudio-0.10.0/wirestudio/library/components/mcp23017.yaml +50 -0
  134. wirestudio-0.10.0/wirestudio/library/components/modbus.yaml +62 -0
  135. wirestudio-0.10.0/wirestudio/library/components/mpu6050.yaml +72 -0
  136. wirestudio-0.10.0/wirestudio/library/components/nextion.yaml +73 -0
  137. wirestudio-0.10.0/wirestudio/library/components/pcf8574.yaml +60 -0
  138. wirestudio-0.10.0/wirestudio/library/components/pulse_counter.yaml +70 -0
  139. wirestudio-0.10.0/wirestudio/library/components/rc522.yaml +51 -0
  140. wirestudio-0.10.0/wirestudio/library/components/rcwl-0516.yaml +49 -0
  141. wirestudio-0.10.0/wirestudio/library/components/rdm6300.yaml +43 -0
  142. wirestudio-0.10.0/wirestudio/library/components/rf_bridge.yaml +41 -0
  143. wirestudio-0.10.0/wirestudio/library/components/rotary_encoder.yaml +76 -0
  144. wirestudio-0.10.0/wirestudio/library/components/rtttl.yaml +52 -0
  145. wirestudio-0.10.0/wirestudio/library/components/sdm_meter.yaml +82 -0
  146. wirestudio-0.10.0/wirestudio/library/components/sht3xd.yaml +58 -0
  147. wirestudio-0.10.0/wirestudio/library/components/ssd1306.yaml +55 -0
  148. wirestudio-0.10.0/wirestudio/library/components/st7789.yaml +72 -0
  149. wirestudio-0.10.0/wirestudio/library/components/sx127x.yaml +90 -0
  150. wirestudio-0.10.0/wirestudio/library/components/tm1638.yaml +56 -0
  151. wirestudio-0.10.0/wirestudio/library/components/tsl2561.yaml +66 -0
  152. wirestudio-0.10.0/wirestudio/library/components/tuya.yaml +66 -0
  153. wirestudio-0.10.0/wirestudio/library/components/tuya_sensor.yaml +60 -0
  154. wirestudio-0.10.0/wirestudio/library/components/tuya_switch.yaml +53 -0
  155. wirestudio-0.10.0/wirestudio/library/components/uart_gps.yaml +41 -0
  156. wirestudio-0.10.0/wirestudio/library/components/vl53l0x.yaml +72 -0
  157. wirestudio-0.10.0/wirestudio/library/components/ws2812b.yaml +53 -0
  158. wirestudio-0.10.0/wirestudio/library/components/xpt2046.yaml +65 -0
  159. wirestudio-0.10.0/wirestudio/mcp/__init__.py +20 -0
  160. wirestudio-0.10.0/wirestudio/mcp/auth.py +114 -0
  161. wirestudio-0.10.0/wirestudio/mcp/server.py +528 -0
  162. wirestudio-0.10.0/wirestudio/model.py +153 -0
  163. wirestudio-0.10.0/wirestudio/recommend/__init__.py +8 -0
  164. wirestudio-0.10.0/wirestudio/recommend/recommender.py +223 -0
  165. wirestudio-0.10.0/wirestudio/schema/__init__.py +1 -0
  166. wirestudio-0.10.0/wirestudio/schema/design.schema.json +213 -0
  167. wirestudio-0.10.0/wirestudio/validate.py +26 -0
  168. wirestudio-0.10.0/wirestudio.egg-info/PKG-INFO +563 -0
  169. wirestudio-0.10.0/wirestudio.egg-info/SOURCES.txt +171 -0
  170. wirestudio-0.10.0/wirestudio.egg-info/dependency_links.txt +1 -0
  171. wirestudio-0.10.0/wirestudio.egg-info/entry_points.txt +3 -0
  172. wirestudio-0.10.0/wirestudio.egg-info/requires.txt +14 -0
  173. wirestudio-0.10.0/wirestudio.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 esphome-studio contributors
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,563 @@
1
+ Metadata-Version: 2.1
2
+ Name: wirestudio
3
+ Version: 0.10.0
4
+ Summary: Agent-driven IoT device design tool that produces ESPHome YAML.
5
+ Author: wirestudio contributors
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: pydantic>=2.6
11
+ Requires-Dist: pyyaml>=6.0
12
+ Requires-Dist: jsonschema>=4.21
13
+ Requires-Dist: jinja2>=3.1
14
+ Requires-Dist: fastapi>=0.110
15
+ Requires-Dist: uvicorn[standard]>=0.27
16
+ Requires-Dist: anthropic>=0.40
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: slowapi>=0.1.9
19
+ Requires-Dist: mcp>=1.27
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8.0; extra == "dev"
22
+ Requires-Dist: ruff>=0.4; extra == "dev"
23
+
24
+ # wirestudio
25
+
26
+ Agent-driven IoT device design tool. Describe a goal (or pick parts);
27
+ get ESPHome YAML, an ASCII wiring diagram, and a BOM that compile
28
+ under upstream ESPHome.
29
+
30
+ Produces ESPHome configs but is not affiliated with the ESPHome
31
+ project — see [`weirded/fleet-for-esphome`](https://github.com/weirded/fleet-for-esphome)
32
+ for the OTA-deploy companion this studio's **Push to fleet** flow
33
+ talks to.
34
+
35
+ ## Status
36
+
37
+ `v0.9.0` — first tagged release. The studio has wide surface area
38
+ (YAML, schematic, enclosure, agent, fleet handoff, web UI) and a
39
+ narrow set of things actually verified against upstream tools. This
40
+ section is honest about which is which, ordered by how much it
41
+ matters that it works.
42
+
43
+ Tiers, in priority order:
44
+
45
+ | Tier | Area | What it does | Verified by |
46
+ |---|---|---|---|
47
+ | **Verified** | ESPHome YAML production | render `design.json` → ESPHome YAML | `esphome config` passes on every bundled example, every PR ([gate](.github/workflows/esphome-config.yml)); nightly `esphome compile` smoke against a representative example ([compile](.github/workflows/esphome-compile.yml)) |
48
+ | **Verified** | CSP pin solver + compat checker | assign legal pins, surface boot-strap / ADC2-WiFi / voltage / locked-pin issues | unit tests + property checks in `tests/test_pin_solver.py` + `tests/test_compatibility.py` |
49
+ | **Verified** | Fleet handoff | push YAML to `fleet-for-esphome` ha-addon, optional compile + log relay | round-trip tests in `tests/test_fleet.py` |
50
+ | **Works (lighter checks)** | KiCad schematic | emit a SKiDL Python script the user runs locally | unit tests assert the script is well-formed Python with expected nets; **not** verified by opening in KiCad |
51
+ | **Works (lighter checks)** | Parametric enclosure | OpenSCAD `.scad` from board mount-hole metadata | unit tests + manual-print iteration; not verified by an OpenSCAD parser in CI |
52
+ | **Experimental** | Thingiverse search relay | rank community models for a board | smoke-tested; depends on a third-party search API that ranks unevenly |
53
+ | **Experimental** | Agent (Claude tool-using) | natural-language design driving | works in practice; tool surface is small; no auto-eval against task list yet |
54
+ | **Deferred** | KiCad PCB layout | Freerouting + Gerber + JLCPCB CPL/BOM | 1.0+, not started |
55
+
56
+ The **Verified** tier is the bar the project is asking to be judged
57
+ on. Everything else is offered with the caveat that's spelled out in
58
+ the table.
59
+
60
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the bar a change has to
61
+ clear before merging, [`CHANGELOG.md`](CHANGELOG.md) for per-release
62
+ deltas, and [`START.md`](START.md) for the longer-form design notes.
63
+
64
+ Tested against ESPHome **`==2025.12.7`** (pinned in
65
+ `.github/workflows/esphome-config.yml` + bumped deliberately). When
66
+ that pin moves, this line moves with it.
67
+
68
+ ## What it does
69
+
70
+ - **Design.** Web UI inspector (board, fleet metadata, components,
71
+ buses, connections, requirements, warnings). Add components by
72
+ picking a capability (**Add by function**) — the recommender ranks
73
+ library matches against use cases. Drag-and-drop pinout for
74
+ component-to-board pin assignment. Pin locks per role. Bus editor
75
+ with rename propagation + inline compatibility warnings. USB
76
+ bootstrap from a plugged-in ESP via WebSerial + esptool-js. Saved
77
+ designs at `designs/<id>.json` with a **Saved** tab + **New design**
78
+ dialog.
79
+ - **Validate.** CSP pin solver assigns every unbound connection with
80
+ capability-aware fallback (boot strap pins de-prioritised; ADC1
81
+ preferred over ADC2 on classic ESP32). Port-compatibility checker
82
+ flags input-only-as-output errors, boot-strap risks, serial console
83
+ reuse, voltage limits, ADC2/WiFi conflicts, locked-pin mismatches.
84
+ Strict mode (header toggle) promotes warn/error compat to render
85
+ errors as a pre-deploy gate.
86
+ - **Generate.** Pure functions over `design.json` + the static
87
+ library produce ESPHome YAML, ASCII wiring diagrams + BOM, a
88
+ parametric OpenSCAD enclosure (`.scad`), and a SKiDL Python script
89
+ the user runs locally to produce a `.kicad_sch`. Bundled examples
90
+ pinned as goldens.
91
+ - **Deploy.** **Push to fleet** ships the YAML to a running
92
+ [`weirded/fleet-for-esphome`](https://github.com/weirded/fleet-for-esphome)
93
+ ha-addon over Bearer-token HTTP; optional `compile: true` enqueues
94
+ an OTA build with live log streaming (Server-Sent Events). **Strict
95
+ mode** refuses the push when warn/error compat issues remain.
96
+ - **Discover enclosures.** Generate a parametric `.scad` shell from
97
+ the board's mount-hole + USB-port metadata, or search community
98
+ models on Thingiverse (`THINGIVERSE_API_KEY`).
99
+ - **Self-host.** Single multi-arch Docker image
100
+ (`linux/amd64` + `linux/arm64`). FastAPI serves API + SPA from one
101
+ process. Kubernetes manifest, docker-compose recipe, and an nginx
102
+ production layout in [`deploy/`](deploy/).
103
+
104
+ ## Quickstart
105
+
106
+ ### Docker (single-image deployment)
107
+
108
+ ```sh
109
+ docker run --rm -p 8765:8765 \
110
+ -e ANTHROPIC_API_KEY=sk-ant-... \
111
+ -v wirestudio-data:/data \
112
+ ghcr.io/moellere/wirestudio:v0.9.0
113
+ ```
114
+
115
+ Open <http://localhost:8765>. The image bundles the FastAPI server +
116
+ the built web UI in one process; `/api/*` is the JSON API,
117
+ `/` is the SPA. `/data` holds the agent's session log + saved
118
+ designs across upgrades.
119
+
120
+ Available tags:
121
+
122
+ | Tag | What it tracks |
123
+ |---|---|
124
+ | `:v0.9.0` / `:0.9.0` / `:0.9` / `:latest` | the v0.9.0 release |
125
+ | `:main` | latest commit on `main` (rolling) |
126
+ | `:sha-<short>` | a specific commit |
127
+
128
+ All four feature-gating env vars are optional — the studio runs
129
+ without any of them, just with the corresponding feature turned off:
130
+
131
+ | Env var | What it gates |
132
+ |---|---|
133
+ | `ANTHROPIC_API_KEY` | the agent (`/agent/*` endpoints + the chat sidebar) |
134
+ | `FLEET_URL` + `FLEET_TOKEN` | fleet-for-esphome push (`/fleet/*`) |
135
+ | `THINGIVERSE_API_KEY` | enclosure search (`/enclosure/search`) |
136
+
137
+ For Kubernetes, see [`deploy/k8s.yaml`](deploy/k8s.yaml). For an
138
+ nginx-front compose recipe, see [`deploy/README.md`](deploy/README.md).
139
+
140
+ ### CLI
141
+
142
+ ```sh
143
+ pip install -e .[dev]
144
+ python -m wirestudio.generate examples/garage-motion.json
145
+ ```
146
+
147
+ Prints rendered YAML and the ASCII wiring block to stdout. To write to files:
148
+
149
+ ```sh
150
+ python -m wirestudio.generate examples/garage-motion.json \
151
+ --out-yaml build/garage-motion.yaml \
152
+ --out-ascii build/garage-motion.txt
153
+ ```
154
+
155
+ ### HTTP API
156
+
157
+ ```sh
158
+ python -m wirestudio.api # localhost:8765
159
+ python -m wirestudio.api --reload # dev mode (auto-reload on edits)
160
+ ```
161
+
162
+ Browse the auto-generated OpenAPI docs at <http://127.0.0.1:8765/docs>.
163
+
164
+ To enable the **agent** endpoints (`/agent/turn`, `/agent/sessions/{id}`),
165
+ export an Anthropic API key before starting the server:
166
+
167
+ ```sh
168
+ export ANTHROPIC_API_KEY=sk-ant-...
169
+ python -m wirestudio.api
170
+ ```
171
+
172
+ Without a key, `/agent/status` reports `available: false` and the agent
173
+ sidebar in the UI shows a friendly notice instead of trying to talk.
174
+
175
+ To enable the **fleet handoff** (`/fleet/push` and the **Push to fleet**
176
+ header button), point the API at a running fleet-for-esphome ha-addon:
177
+
178
+ ```sh
179
+ export FLEET_URL=http://homeassistant.local:8765
180
+ export FLEET_TOKEN=$(grep -oP '(?<=token: )\S+' .../addon/secrets.yaml)
181
+ python -m wirestudio.api
182
+ ```
183
+
184
+ `GET /fleet/status` reports `available: true` when both env vars are set
185
+ and the addon answers a probe; otherwise the UI surfaces the specific
186
+ reason (URL missing, unauthorized, unreachable).
187
+
188
+ ### MCP server
189
+
190
+ The studio's design-editing tools are also exposed over the
191
+ [Model Context Protocol](https://modelcontextprotocol.io) at `/mcp`.
192
+ Point Claude Code or Claude Desktop at the daemon and the model drives
193
+ the studio on your Claude subscription — no Anthropic key, no studio-side
194
+ token spend. See [`docs/MCP.md`](docs/MCP.md) for the end-to-end setup.
195
+
196
+ ### Web UI (dev)
197
+
198
+ ```sh
199
+ # In one terminal:
200
+ python -m wirestudio.api
201
+
202
+ # In another:
203
+ cd web && npm install && npm run dev
204
+ ```
205
+
206
+ Open <http://localhost:5173>. Vite proxies `/api/*` to the studio API,
207
+ so no CORS plumbing in dev. The same web UI is served at `/` from the
208
+ production Docker image; the dev server is only useful when you're
209
+ editing UI code yourself.
210
+
211
+ Inspector surfaces:
212
+
213
+ - **Design pane** — board picker, fleet metadata (device_name, tags,
214
+ secrets refs), requirements, warnings, components list (add /
215
+ remove with auto-wiring), buses (add / rename / edit pin slots /
216
+ remove), per-bus + design-level compatibility warnings.
217
+ - **Component-instance pane** — params (form generated from each
218
+ library entry's `params_schema`), connections (per-row editor with
219
+ rail / gpio / bus / expander_pin / component target kinds),
220
+ Form ⇄ Pinout view toggle for drag-and-drop pin assignment, 🔓/🔒
221
+ per-row pin lock.
222
+
223
+ Header buttons: **New design**, **Reset**, **Save**, **Download JSON**,
224
+ **Solve pins**, **strict** (toggle), **Connect device** (USB
225
+ bootstrap), **Add by function** (capability picker), **Schematic**
226
+ (KiCad export), **Enclosure** (parametric `.scad` + Thingiverse
227
+ search), **Push to fleet**.
228
+
229
+ Useful endpoints:
230
+
231
+ | Method | Path | What it does |
232
+ |---|---|---|
233
+ | `GET` | `/library/boards` | summaries of every board in the library |
234
+ | `GET` | `/library/boards/{id}` | full board, including pinout |
235
+ | `GET` | `/library/components?category=&use_case=&bus=` | filtered component summaries |
236
+ | `GET` | `/library/components/{id}` | full component, including ESPHome template |
237
+ | `GET` | `/library/use_cases` | distinct capabilities across the library, with counts; powers the **Add by function** picker |
238
+ | `POST` | `/library/recommend` | rank library components against a free-text or capability query |
239
+ | `POST` | `/design/validate` | parse a `design.json`, return summary or 422 |
240
+ | `POST` | `/design/render` | parse + render a `design.json` to `{yaml, ascii}` |
241
+ | `POST` | `/design/enclosure/openscad` | generate a parametric `.scad` shell for the design's board |
242
+ | `POST` | `/design/kicad/schematic` | generate a SKiDL Python script the user runs locally to produce a `.kicad_sch` |
243
+ | `GET` | `/enclosure/search?library_id=...&query=...` | search community-uploaded enclosure models (Thingiverse) |
244
+ | `GET` | `/enclosure/search/status` | per-source availability + configure hints |
245
+ | `GET` | `/examples` | list bundled examples |
246
+ | `GET` | `/examples/{id}` | fetch an example as raw `design.json` |
247
+ | `GET` | `/fleet/status` | check whether `FLEET_URL` + `FLEET_TOKEN` reach a fleet-for-esphome ha-addon |
248
+ | `POST` | `/fleet/push` | render `design.json` and push it as `<device_name>.yaml` (optionally `compile: true`) |
249
+ | `GET` | `/fleet/jobs/{run_id}/log?offset=N` | poll the addon's build log for a compile run; returns `{log, offset, finished}` |
250
+ | `GET` | `/fleet/jobs/{run_id}/log/stream` | Server-Sent Events relay over the same log endpoint; ~300ms cadence, exits with `event: done` when the build finishes |
251
+
252
+ The HTTP API is a thin layer over the studio's pure-function modules
253
+ (`wirestudio.generate`, `wirestudio.csp`, `wirestudio.recommend`, `wirestudio.fleet`,
254
+ `wirestudio.enclosure`, `wirestudio.kicad`). Server state is limited to the
255
+ agent session log + the saved-design store — both file-backed under
256
+ `/data` (via `SESSIONS_DIR` / `DESIGNS_DIR`). Permissive CORS for
257
+ `localhost:5173` / `localhost:3000` so the dev Vite server can hit
258
+ it without a proxy.
259
+
260
+ ## Examples
261
+
262
+ | Example | Board | What it is |
263
+ |---|---|---|
264
+ | [`garage-motion.json`](examples/garage-motion.json) | ESP32-DevKitC-V4 | PIR + BME280 (temp/humidity/pressure) over I2C |
265
+ | [`awning-control.json`](examples/awning-control.json) | WeMos D1 Mini | Cover controller — 4 limit switches + buttons via MCP23008 expander, 2 GPIO relays, dual-PWM motor drive |
266
+ | [`wasserpir.json`](examples/wasserpir.json) | WeMos D1 Mini | Single PIR with a scheduled nightly reboot |
267
+ | [`oled.json`](examples/oled.json) | WeMos D1 Mini | SSD1306 status display rendering time, date, IP |
268
+ | [`bluemotion.json`](examples/bluemotion.json) | WeMos D1 Mini | PIR + WS2812B NeoPixel; motion lights the LED |
269
+ | [`distance-sensor.json`](examples/distance-sensor.json) | NodeMCU v2 | HC-SR04 ultrasonic + WS2812B NeoPixel; LED color tracks distance |
270
+ | [`securitypanel.json`](examples/securitypanel.json) | WeMos D1 Mini | 12 door/window/motion sensors via MCP23017 expander, RTTTL piezo, GPIO siren |
271
+ | [`rc522.json`](examples/rc522.json) | WeMos D1 Mini | MFRC522 RFID reader (SPI), NeoPixel status LED, RTTTL piezo, manual button |
272
+ | [`esp32-audio.json`](examples/esp32-audio.json) | NodeMCU-32S | I2S audio (MAX98357A DAC) + ST7789V SPI dashboard display, Arduino framework |
273
+ | [`bluesonoff.json`](examples/bluesonoff.json) | ESP-01S 1MB | Sonoff Basic relay; front button (boot strap pin) toggles a single GPIO relay |
274
+ | [`wemosgps.json`](examples/wemosgps.json) | WeMos D1 Mini | UART GPS module — lat/lon/altitude/speed/satellites + runtime baud-rate selector |
275
+ | [`ttgo-lora32.json`](examples/ttgo-lora32.json) | TTGO LoRa32 V1 | ESP32 + onboard SX1276 LoRa radio + onboard SSD1306 OLED + battery ADC, ESP-IDF |
276
+ | [`multi-temp.json`](examples/multi-temp.json) | WeMos D1 Mini | Two DS18B20 temp sensors sharing a single 1-wire bus + an RCWL-0516 microwave motion sensor |
277
+ | [`room-climate.json`](examples/room-climate.json) | WeMos D1 Mini | BH1750 ambient-light + AHT20 temp/humidity on one I2C bus |
278
+ | [`desk-climate.json`](examples/desk-climate.json) | ESP32-C3-DevKitM-1 | Sensirion SHT3x precision temp/humidity over I2C |
279
+ | [`parking-distance.json`](examples/parking-distance.json) | NodeMCU v2 | VL53L0X laser ToF distance (indoor parking-spot indicator) |
280
+ | [`keypad.json`](examples/keypad.json) | WeMos D1 Mini | 8 buttons read through a PCF8574 GPIO expander over I2C |
281
+ | [`smart-plug.json`](wirestudio/examples/smart-plug.json) | ESP8285 1MB | Athom-style smart plug — relay + button + CSE7766 AC power metering over UART 4800 8E1 |
282
+ | [`smart-plug-v1.json`](wirestudio/examples/smart-plug-v1.json) | ESP8285 1MB | Older Athom v1 / Sonoff POW R1 plug — same topology with the HLW8012 / BL0937 3-pin pulse meter |
283
+ | [`desk-matrix.json`](wirestudio/examples/desk-matrix.json) | ESP32-DevKitC | 8x8 WS2812 matrix driven by the ESP32 RMT peripheral (no bit-banging) |
284
+ | [`rs485-energy.json`](wirestudio/examples/rs485-energy.json) | ESP32-DevKitC-V4 | Eastron SDM230 single-phase energy meter via Modbus RTU (UART2 + MAX485 transceiver, GPIO5 drives DE+RE) |
285
+ | [`bl0906-mainmeter.json`](wirestudio/examples/bl0906-mainmeter.json) | ESP32-DevKitC-V4 | BL0906 6-channel CT-clamp energy monitor over UART2 (Athom EM6-style whole-home sub-metering) |
286
+ | [`nextion-thermostat.json`](wirestudio/examples/nextion-thermostat.json) | ESP32-DevKitC-V4 | Nextion HMI thermostat panel — display on UART2 + SHT3xD temp/humidity on default I2C |
287
+ | [`tuya-smart-plug.json`](wirestudio/examples/tuya-smart-plug.json) | ESP8285 1MB | Tuya-MCU smart plug — relay (DP 1) + power (DP 17) + energy (DP 18) over UART 9600; logger off UART0 |
288
+ | [`weather-station.json`](wirestudio/examples/weather-station.json) | ESP32-DevKitC-V4 | BMP280 barometer + HTU21D temp/humidity + TSL2561 lux on one shared I2C bus |
289
+ | [`attic-logger.json`](wirestudio/examples/attic-logger.json) | WeMos D1 Mini | DHT22 single-wire temp/humidity + legacy BMP180 I2C barometer |
290
+
291
+ Generated artifacts for each are pinned as goldens in
292
+ [`tests/golden/`](tests/golden/). For a per-component / per-board view of
293
+ which library entries are exercised by these examples, see
294
+ [`docs/library-coverage.md`](docs/library-coverage.md) (regenerate with
295
+ `python scripts/coverage_matrix.py`).
296
+
297
+ ## Architecture
298
+
299
+ ```
300
+ design.json ── single source of truth (JSON-Schema-validated)
301
+
302
+
303
+ ┌─ wirestudio.model pydantic models mirroring the schema
304
+ ├─ wirestudio.library loads boards/ + components/ YAML
305
+ ├─ wirestudio.generate design + library → ESPHome YAML + ASCII
306
+ ├─ wirestudio.csp pin solver + port-compatibility checker
307
+ ├─ wirestudio.recommend deterministic capability ranking
308
+ ├─ wirestudio.agent Claude tool-using agent + session store
309
+ ├─ wirestudio.designs file-backed designs/<id>.json store
310
+ ├─ wirestudio.fleet fleet-for-esphome HTTP client
311
+ ├─ wirestudio.enclosure parametric OpenSCAD + Thingiverse search
312
+ ├─ wirestudio.kicad SKiDL Python script emitter
313
+ └─ wirestudio.api FastAPI HTTP layer (mounts everything above)
314
+ serve.py adds the production wrapper:
315
+ API at /api/*, web bundle at /
316
+ ```
317
+
318
+ Generators are pure functions of `design.json` + the static library — no
319
+ artifact-to-document round-trips. Library files in `library/components/`
320
+ carry the electrical metadata ESPHome doesn't (pin roles, voltage ranges,
321
+ current draw, decoupling caps, pull-up requirements) plus a Jinja2 template
322
+ that renders the ESPHome YAML for that component, an `enclosure:` block
323
+ the OpenSCAD generator reads, and a `kicad:` block the schematic exporter
324
+ reads.
325
+
326
+ ## Library
327
+
328
+ Currently shipped:
329
+
330
+ **Boards** (`library/boards/`)
331
+ - `esp32-devkitc-v4` — ESP32 DevKitC V4 (ESP32-WROOM-32, 4MB flash)
332
+ - `nodemcu-32s` — NodeMCU-32S (ESP32-WROOM-32, marks I2S-capable pins)
333
+ - `ttgo-lora32-v1` — LilyGO TTGO LoRa32 V1 (ESP32 + onboard SX1276 + onboard SSD1306)
334
+ - `ttgo-t-beam` — LilyGO TTGO T-Beam v1.x (ESP32 + onboard SX1276 + NEO-6M GPS + AXP192 PMIC + 18650)
335
+ - `esp32-c3-devkitm-1` — ESP32-C3-DevKitM-1 (single-core RISC-V, USB-Serial-JTAG, onboard WS2812)
336
+ - `esp32-s3-devkitc-1` — ESP32-S3-DevKitC-1 (dual-core Xtensa, native USB, onboard WS2812)
337
+ - `esp32cam-ai-thinker` — AI-Thinker ESP32-CAM (ESP32-WROVER-B + OV2640 + microSD)
338
+ - `esp32-wrover-cam` — ESP32-WROVER-CAM (Freenove-style, OV2640 with the WROVER pinout)
339
+ - `m5stack-atom` — M5Stack Atom Lite / Echo (ESP32-PICO-D4, 24mm cube, onboard SK6812)
340
+ - `m5stack-atoms3` — M5Stack AtomS3 (ESP32-S3 + onboard 0.85" 128×128 ST7789 + IMU)
341
+ - `wemos-d1-mini` — WeMos D1 Mini (ESP-12F module, ESP8266)
342
+ - `nodemcu-v2` — NodeMCU v2 (ESP-12E/F module, ESP8266, breaks out RX/TX/MISO/MOSI as D9-D12)
343
+ - `esp01_1m` — ESP-01S 1MB module / Sonoff Basic-class devices
344
+ - `esp8285-1m` — Generic ESP8285 1MB SoC (Athom / Sonoff Basic R3+ / Tuya smart plugs)
345
+
346
+ **Components** (`library/components/`)
347
+
348
+ _Environmental sensors:_
349
+ - `bme280` — Bosch temperature/humidity/pressure sensor (I2C)
350
+ - `bmp180` — Bosch BMP180/BMP085 barometric pressure + temperature (I2C)
351
+ - `bmp280` — Bosch temperature/pressure sensor (I2C, no humidity)
352
+ - `dht` — DHT11 / DHT22 / AM2302 temperature + humidity (single-wire)
353
+ - `htu21d` — TE Connectivity HTU21D temperature + humidity (I2C; covers Si7021 / SHT2x)
354
+ - `sht3xd` — Sensirion SHT3x / SHT4x precision temp + humidity (I2C; modern default)
355
+ - `aht10` — Aosong AHT10 / AHT20 cheap temp + humidity (I2C; AliExpress weather modules)
356
+ - `ds18b20` — Dallas DS18B20 1-Wire temperature sensor (single-pin bus + 4.7kΩ pull-up)
357
+ - `bh1750` — BH1750FVI ambient light sensor in lux (I2C; GY-30 / GY-302 modules)
358
+
359
+ _Specialty sensors:_
360
+ - `max31855` — Maxim K-type thermocouple amplifier (SPI; -270..+1372°C)
361
+ - `hx711` — AVIA 24-bit load-cell ADC (custom 2-wire serial)
362
+ - `tsl2561` — AMS ambient light sensor (lux, I2C)
363
+ - `mpu6050` — InvenSense 6-axis IMU (3-axis accel + 3-axis gyro + die temp, I2C)
364
+
365
+ _Presence / distance:_
366
+ - `hc-sr04` — ultrasonic distance sensor (4-pin: VCC, GND, TRIGGER, ECHO)
367
+ - `hc-sr501` — PIR motion sensor (used as a generic PIR)
368
+ - `rcwl-0516` — microwave doppler motion sensor (low-power PIR alternative)
369
+ - `ld2420` — Hi-Link LD2420 24GHz mmWave presence sensor (UART)
370
+ - `vl53l0x` — STMicro VL53L0X laser time-of-flight distance (I2C; indoor up to ~1.2m)
371
+
372
+ _RFID / radios:_
373
+ - `rc522` — MFRC522 RFID reader (SPI, singleton)
374
+ - `rdm6300` — RDM6300 125kHz EM4100 RFID reader (UART, singleton)
375
+ - `sx127x` — Semtech SX1276/SX1278 LoRa radio (SPI, singleton)
376
+ - `cc1101` — TI CC1101 sub-GHz transceiver (SPI, singleton)
377
+ - `rf_bridge` — Sonoff RF Bridge 433MHz EFM8 module (UART, singleton)
378
+
379
+ _Displays:_
380
+ - `ssd1306` — 128×64 OLED (I2C)
381
+ - `st7789` — Sitronix ST7789V color TFT (SPI write-only)
382
+ - `ili9xxx` — ILI9341 / ILI9486 / ILI9488 SPI TFT
383
+ - `lcd_pcf8574` — HD44780 16x2 / 20x4 LCD via PCF8574 I2C backpack
384
+ - `tm1638` — TM1638 8-digit 7-segment + 8 LEDs + 8 buttons combo
385
+ - `max7219` — MAX7219 7-segment / 8x8 LED matrix driver (SPI)
386
+
387
+ _Touch / input:_
388
+ - `xpt2046` — XPT2046 resistive touchscreen controller (SPI)
389
+ - `rotary_encoder` — Quadrature rotary encoder (KY-040 style)
390
+
391
+ _IO expanders + ADC hubs:_
392
+ - `mcp23008` — 8-bit I2C GPIO expander (Microchip)
393
+ - `mcp23017` — 16-bit I2C GPIO expander (Microchip)
394
+ - `pcf8574` — NXP PCF8574 / PCF8575 8-/16-bit I2C GPIO expander (cheap, weak open-drain)
395
+ - `ads1115` — TI 4-channel 16-bit ADC (I2C) hub; rescues ESP32 designs from the ADC2/WiFi conflict
396
+ - `ads1115_channel` — one logical reading on an ADS1115 hub (multiplexer + gain + update_interval per channel)
397
+
398
+ _Generic IO:_
399
+ - `gpio_input` — generic binary_sensor on a GPIO or expander pin (buttons, limit switches, door/window/motion sensors)
400
+ - `gpio_output` — generic switch on a GPIO or expander pin (relays, indicators)
401
+ - `adc` — generic analog input (battery monitoring, potentiometers, LDRs)
402
+ - `pulse_counter` — pulse counter / tachometer (RPM, flow, energy meters)
403
+
404
+ _Light / audio / camera:_
405
+ - `ws2812b` — WS2812B / SK6812 addressable RGB LED (1-wire NeoPixel; bit-banged or ESP8266-DMA)
406
+ - `esp32_rmt_led_strip` — same WS2812 / SK6812 silicon, ESP32 RMT-driven (preferred on ESP32 / S2 / S3 / C3)
407
+ - `apa102` — APA102 / SK9822 addressable RGB strip (DotStar, SPI-style)
408
+ - `max98357a` — Maxim Class-D mono I2S amp + DAC
409
+ - `rtttl` — piezo buzzer + RTTTL melody player (PWM output)
410
+ - `esp32_camera` — ESP32 OV2640 / OV7670 / OV5640 camera
411
+
412
+ _Power metering:_
413
+ - `cse7766` — Chipsea AC voltage / current / power / energy over UART 4800 8E1 (Athom v2/c3 + Sonoff plugs)
414
+ - `hlw8012` — HLW8012 / BL0937 / CSE7759 AC power meter via 3-pin pulse interface (older Athom v1 + Sonoff POW R1)
415
+ - `bl0906` — Belling 6-channel AC energy meter over UART 19200 (Athom EM6 / whole-home sub-metering)
416
+ - `modbus` + `sdm_meter` — Modbus RTU bus (RS485 via MAX485 transceiver) + Eastron SDM120/220/230/630 single/three-phase DIN-rail meter
417
+
418
+ _Vendor bridges:_
419
+ - `tuya` + `tuya_switch` + `tuya_sensor` — Tuya MCU UART bridge plus per-datapoint switch and sensor platforms (smart plugs / switches / climate gadgets that ship with an ESP8266-class radio talking to a separate Tuya MCU)
420
+
421
+ _Displays (HMI):_
422
+ - `nextion` — Nextion HMI smart display over UART 9600 (T/K/P series; .tft uploaded separately via Nextion Editor)
423
+
424
+ _Location:_
425
+ - `uart_gps` — generic UART GPS module (NEO-6M / NEO-8M)
426
+
427
+ The `gpio_input` / `gpio_output` components and the `kind: expander_pin`
428
+ connection target together let downstream platforms hang off any expander
429
+ without bloating `esphome_extras`. See `examples/securitypanel.json` for a
430
+ 12-sensor MCP23017 wiring or `examples/awning-control.json` for a mix of
431
+ expander inputs and outputs.
432
+
433
+ The library now spans the device classes used across the
434
+ [`moellere/esphome`](https://github.com/moellere/esphome) device
435
+ configurations (camera boards, mmWave presence, sub-GHz radios,
436
+ character LCDs, touchscreens, generic ADC and pulse counters,
437
+ addressable LEDs, RTTTL piezo, RFID variants, and the M5Stack /
438
+ ESP32-C3 / ESP32-S3 board family). It will keep growing as new device
439
+ configs land. See
440
+ [`START.md` § Library sourcing strategy](START.md#library-sourcing-strategy)
441
+ for the hybrid plan.
442
+
443
+ ## Layout
444
+
445
+ ```
446
+ schema/ JSON Schema for design.json (source of truth)
447
+ library/boards/ board manifests (pinout, rails, framework, enclosure, kicad)
448
+ library/components/ component manifests (electrical + ESPHome + enclosure + kicad)
449
+ wirestudio/ python package — see Architecture above for the module map
450
+ web/ React 19 + Vite + Tailwind v4 SPA
451
+ examples/ bundled design.json files (every one pinned by goldens)
452
+ tests/ pytest + golden artifacts; vitest tests under web/src
453
+ deploy/ k8s.yaml, docker-compose.yml, nginx.conf for self-hosting
454
+ Dockerfile multi-stage build for the published GHCR image
455
+ .github/workflows/ GHA workflow that publishes ghcr.io/.../wirestudio
456
+ scripts/ dev helpers (currently: examples → `esphome config` gate)
457
+ CHANGELOG.md per-release feature deltas
458
+ START.md vision, decisions, phase plan
459
+ CLAUDE.md working conventions for both Claude and humans
460
+ CONTRIBUTING.md substantive bar a change has to clear (the YAML gate, etc.)
461
+ ```
462
+
463
+ ## Tests
464
+
465
+ ```sh
466
+ python -m pytest # ~297 cases, ~10s
467
+ python -m ruff check . # lint
468
+ cd web && npx vitest run # ~125 cases, ~5s (vitest + jsdom)
469
+ pip install 'esphome==2025.12.7'
470
+ python scripts/check_examples.py # the YAML gate -- every example through `esphome config`
471
+ python scripts/check_examples.py --compile garage-motion # the compile-smoke; slow (~10min cold)
472
+ ```
473
+
474
+ The `esphome config` gate is the headline test: it renders every
475
+ bundled example through the studio and runs upstream ESPHome's own
476
+ validator against the output. Anything the studio emits has to
477
+ round-trip through that gate, and the GitHub Actions workflow
478
+ ([`.github/workflows/esphome-config.yml`](.github/workflows/esphome-config.yml))
479
+ runs it on every PR. A nightly compile-smoke
480
+ ([`.github/workflows/esphome-compile.yml`](.github/workflows/esphome-compile.yml))
481
+ goes one level deeper -- it runs `esphome compile` against a
482
+ representative example so we catch upstream toolchain / codegen
483
+ regressions even when no code has changed.
484
+
485
+ To run the same gate before every push, install the pre-commit
486
+ hooks once:
487
+
488
+ ```sh
489
+ pip install pre-commit
490
+ pre-commit install --hook-type pre-push
491
+ ```
492
+
493
+ After that, `git push` runs the gate locally and aborts on failure.
494
+
495
+ Golden tests pin the generator output for every bundled example.
496
+ Regenerate goldens with the CLI when output legitimately changes;
497
+ commit the new files in the same diff as the code change. The web
498
+ suite covers `lib/design.ts` plus React components (BusList,
499
+ ConnectionForm, EnclosureDialog, Inspector, CapabilityPickerDialog,
500
+ PinoutView, PushToFleetDialog, SchematicDialog) via React Testing
501
+ Library + jsdom; network surfaces are mocked at the api/client
502
+ boundary so the suite stays offline.
503
+
504
+ The GitHub Actions workflow runs the YAML gate + the full suite +
505
+ multi-arch image build on every PR + merge to main.
506
+
507
+ ## Roadmap
508
+
509
+ Reorganised by priority — what's worth working on next, ordered by
510
+ how much it raises the floor on whether the studio is actually
511
+ useful. The previous "ship more surface area" roadmap is preserved
512
+ in [`CHANGELOG.md`](CHANGELOG.md) (per-release deltas) and
513
+ [`START.md`](START.md) (decisions + phase scope).
514
+
515
+ **Priority 1 — YAML production correctness.** *Active.* The single
516
+ non-negotiable bar: every artifact the studio emits round-trips
517
+ through upstream `esphome config`. Done so far: `esphome config` CI
518
+ gate over every bundled example; pinned ESPHome version called out
519
+ in this README + workflow; CONTRIBUTING.md establishes the gate as
520
+ the merge bar. Next: real `esphome compile` smoke for one example;
521
+ component-coverage matrix (which components have an example that
522
+ validates) so additions are forced through the gate.
523
+
524
+ **Priority 2 — Wiring schema correctness.** *Verified-light.* SKiDL
525
+ emitter + 100% library `kicad:` coverage shipped. Honest gap: the
526
+ output is unit-tested as Python text, not opened in KiCad. Next:
527
+ container-side KiCad CLI in CI to actually open + render the
528
+ generated schematic; pin-solver property tests on randomized
529
+ designs; compatibility-checker fuzzing.
530
+
531
+ **Priority 3 — Enclosures.** *Lower priority.* Parametric OpenSCAD
532
+ generator + Thingiverse search relay shipped. Open question: keep
533
+ investing here, or outsource to e.g.
534
+ [YAPP_Box](https://github.com/mrWheel/YAPP_Box) and integrate
535
+ instead of reimplementing? Decision deferred until P1 + P2 are
536
+ tighter.
537
+
538
+ **Priority 4 — PCB layout.** *Deferred to 1.0+.* No work in flight;
539
+ not adding surface here until P1 is rock solid.
540
+
541
+ **Plumbing — already shipped.** API (`0.2`), web UI (`0.3` +
542
+ `0.6+`), USB bootstrap (`0.4`), agent (`0.5` + streaming), CSP
543
+ solver (`0.6`), fleet handoff (`0.7`), enclosure (`0.8`), KiCad
544
+ schematic (`0.9`), Docker single-image deploy + K8s manifest. See
545
+ [`CHANGELOG.md`](CHANGELOG.md) for the per-release feature deltas.
546
+
547
+ **Future** — multi-writer state backend so the studio can run as a
548
+ HA replica; agent eval harness against a task list; ESPHome version
549
+ matrix in CI (last 2 stables) so we can call out which components
550
+ work where.
551
+
552
+ ## Contributing
553
+
554
+ [`CONTRIBUTING.md`](CONTRIBUTING.md) is the substantive bar — what
555
+ "working" means for the artifacts the studio produces, including
556
+ the `esphome config` gate every PR has to clear. [`CLAUDE.md`](CLAUDE.md)
557
+ covers the prose / commit / comment conventions (concise, no emojis,
558
+ default-to-no-comments, boundary-only validation, no premature
559
+ abstraction).
560
+
561
+ ## License
562
+
563
+ MIT. See [`LICENSE`](LICENSE).