wirestudio 0.44.0__tar.gz → 0.45.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.
- {wirestudio-0.44.0/wirestudio.egg-info → wirestudio-0.45.0}/PKG-INFO +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_api_route.py +6 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_community.py +63 -6
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_compatibility.py +83 -19
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad.py +23 -12
- wirestudio-0.45.0/tests/test_kicad_drc.py +112 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_pcb.py +35 -8
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_route.py +73 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_migrate.py +50 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/__init__.py +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/tools.py +55 -6
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/app.py +36 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/community.py +44 -7
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/csp/compatibility.py +37 -0
- wirestudio-0.45.0/wirestudio/kicad/drc.py +145 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/fab.py +3 -3
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/generator.py +27 -14
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/netlist.py +58 -13
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/pcb.py +29 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/pcbnew_bridge.py +5 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/route.py +138 -17
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/__init__.py +6 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/ttgo-lora32-v2.yaml +8 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/check.py +5 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/apa102.yaml +2 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bh1750.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/esp32_rmt_led_strip.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hc-sr501.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hlw8012.yaml +3 -3
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ili9xxx.yaml +3 -3
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ld2410.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/max98357a.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mcp23008.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mcp23017.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mcp23s17.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pca9685.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pcf8574.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pmsx003.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rc522.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rcwl-0516.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/remote_receiver.yaml +3 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/servo.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sn74hc595.yaml +4 -4
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ssd1306.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/st7789.yaml +3 -3
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sx127x.yaml +2 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tm1637.yaml +2 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/vl53l0x.yaml +2 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/wiegand.yaml +2 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ws2812b.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/xpt2046.yaml +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/mcp/server.py +12 -3
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/migrate.py +59 -2
- {wirestudio-0.44.0 → wirestudio-0.45.0/wirestudio.egg-info}/PKG-INFO +1 -1
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio.egg-info/SOURCES.txt +2 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/LICENSE +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/LICENSES.md +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/README.md +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/pyproject.toml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/setup.cfg +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_active_design.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_agent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_agent_eval.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_agent_stream.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_api.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_api_auth.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_ascii_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_build_backend.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_check_upgrade.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_chirpstack.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_circuitpython.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_codec.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_compile.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_component_corpus.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_connectors.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_coverage_baseline.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_design_events.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_designs.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_designs_seed.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_display_intent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_electrical.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_enclosure.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_enclosure_gate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_enclosure_search.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_errors.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_esphome_dashboard.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_esphome_support.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_firmware_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_fleet.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_footprints.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_framework_baseline.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_hardware_gate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_intent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_inventory.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_jlcpcb.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_erc.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_fab.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_import.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_render.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_kicad_sch.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_library_check.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_library_hbridge.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_library_rtttl.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_lorawan.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_lorawan_api.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_lorawan_sugar_migration.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_auth.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_client.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_hardware.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_jobs.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_resources.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_mcp_server.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_meshtastic.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_micropython.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_micropython_registers.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_modules.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_pin_protocols.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_pin_solver.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_printwarden.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_python_blocks.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_recommend.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_register_code.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_rf_ook.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_schema.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_schema_doc.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_schematics_gate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_security_cors.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_seed_onboard.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_serve.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_sqlite_store.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_sqlite_stores.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_strict.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_targets.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_tasmota.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_tbeam_lorawan_example.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_tools_add_bus.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_tools_add_component.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_tools_solve_pins.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_uart_frames.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_validate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_wled.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_workbench.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_workbench_boot.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_workbench_images.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_workbench_provision.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_workbench_serial.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_ws2812_method.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/tests/test_yaml_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/agent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/eval.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/eval_tasks.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/agent/session.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/__main__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/auth.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/circuitpython.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/design_input.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/meshtastic.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/micropython.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/schemas.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/serve.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/api/workbench.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/csp/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/csp/pin_solver.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/designs/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/designs/active.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/designs/events.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/designs/seed.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/designs/store.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/enclosure/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/enclosure/openscad.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/enclosure/search.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/enclosure/yapp/LICENSE +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/enclosure/yapp/YAPPgenerator_v3.scad +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/errors.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/esphome_dashboard/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/access-panel.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/actuator-harness.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/air-quality-station.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/analog-node.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atom-echo.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atom-lite.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atom-matrix.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atoms3-lcd.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atoms3-lite.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atoms3-onboard.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atomu-onboard.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/atomu.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/attic-logger.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/awning-control.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/bench-io.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/bl0906-mainmeter.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/blind-stepper.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/bluemotion.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/bluesonoff.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/button-toggles-light.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/c3-supermini.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/c6-supermini.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/co2-display.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/desk-climate.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/desk-matrix.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/distance-sensor.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/encoder-drives-stepper.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/energy-monitor.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/esp32-audio.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/esp32cam-wrover.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/esp32cam.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/flow-meter.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/garage-motion.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/grill-probe.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/guarded-button-light.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/heltec-v4-gnss-tracker.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/keypad.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/kitchen-scale.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/led-display.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/lorawan-battery-uplink.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/motion-turns-on-light.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/motor-position.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/multi-temp.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/nextion-thermostat.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/oled-knob.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/oled.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/output-hub.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/parking-distance.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/presence-media-node.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/presence-rf.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/rc522.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/room-climate.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/rs485-energy.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/s3-devkitc.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/s3-supermini.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/securitypanel.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/smart-plug-v1.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/smart-plug.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/solder-fan.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/spi-io-node.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/subghz-rfid.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/t-beam-lorawan.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/t-beam.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/temp-above-turns-on-fan.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/temp-turns-on-fan.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/tft-touch.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/ttgo-lora32.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/tuya-smart-plug.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/usbc-led-strip.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/vent-damper.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/wasserpir.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/weather-station.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/wemosgps.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/wled-sensor-strip.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/examples/xiao-grove-climate.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/fleet/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/fleet/client.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/__main__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/ascii_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/circuitpython_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/display_intent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/micropython_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/generate/yaml_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/intent.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/buy.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/check.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/csv_io.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/match.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/inventory/store.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/jlcpcb/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/jlcpcb/__main__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/jlcpcb/check.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/jlcpcb/client.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/erc.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/import.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/importer.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/render.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/schematic.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/kicad/symbol_parser.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp01_1m.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-c3-devkitm-1.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-c3-supermini.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-c6-supermini.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-devkitc-v4.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-s3-devkitc-1.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-s3-supermini.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32-wrover-cam.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp32cam-ai-thinker.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/esp8285-1m.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/heltec-wifi-lora32-v2.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/heltec-wifi-lora32-v3.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/heltec-wifi-lora32-v4.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atom-echo.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atom-matrix.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atom.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atoms3-lite.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atoms3.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/m5stack-atomu.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/nodemcu-32s.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/nodemcu-v2.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/seeed-xiao-esp32c3.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/ttgo-lora32-v1.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/ttgo-t-beam.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/boards/wemos-d1-mini.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/adc.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ads1115.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ads1115_channel.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/aht10.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/axp192.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/battery_adc.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bl0906.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bme280.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bme680.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bmp180.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/bmp280.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/cc1101.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x02.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x03.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x04.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x05.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x06.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x07.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_1x08.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/connector_2x05_idc.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/cse7766.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ct_clamp.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/dht.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ds18b20.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/esp32_camera.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/gpio_input.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/gpio_output.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/grove-aht20.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hbridge.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hbridge_mosfet.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hc-sr04.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/htu21d.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/hx711.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/i2s_microphone.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ina219.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/lcd_pcf8574.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/ld2420.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/led_indicator.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/level_shifter.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/max31855.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/max7219.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mcp3008.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mcp3008_channel.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mhz19.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/modbus.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mosfet_low_side_driver.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mpu6050.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/mpu6886.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/nextion.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/npn_low_side_driver.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pn532.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/potentiometer.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pulse_counter.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pwm_fan.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/pzemac.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rdm6300.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/remote_transmitter.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rf_bridge.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rotary_encoder.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/rtttl.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/scd4x.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sdm_meter.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sgp4x.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sht3xd.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/sht4x.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tm1638.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tsl2561.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tuya.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tuya_sensor.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/tuya_switch.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/uart_gps.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/uln2003.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/usb_c_power_in.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/veml7700.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/components/voltage_divider.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/electrical.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/modules/env-display.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/modules/fan-controller.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/modules/motion-light.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/modules/oled-knob-13.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/library/modules/relay-4ch.yaml +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/mcp/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/mcp/auth.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/mcp/hardware.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/mcp/jobs.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/model.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/printwarden/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/printwarden/client.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/recommend/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/recommend/recommender.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/schema/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/schema/design.schema.json +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/seed.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/sqlite_store.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/base.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/build_backend.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/esphome.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/__main__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/api.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/build_local.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/chirpstack.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/codec.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/compile.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/firmware_gen.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/templates/main.cpp.j2 +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/templates/platformio.ini.j2 +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/lorawan/workbench_provision.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/tasmota.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/wled.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/targets/wled_build.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/validate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/__init__.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/boot.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/client.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/gate.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/images.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio/workbench/serial.py +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio.egg-info/dependency_links.txt +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio.egg-info/entry_points.txt +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio.egg-info/requires.txt +0 -0
- {wirestudio-0.44.0 → wirestudio-0.45.0}/wirestudio.egg-info/top_level.txt +0 -0
|
@@ -99,3 +99,9 @@ def test_fab_gerbers_route_unavailable_is_503(client, monkeypatch):
|
|
|
99
99
|
def test_fab_status_reports_route(client):
|
|
100
100
|
body = client.get("/design/fab/status").json()
|
|
101
101
|
assert "route" in body and "route_reason" in body
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_route_bad_params_are_422(client, toolchain):
|
|
105
|
+
for query in ("keepout=1,2,3", "keepout=1,2,3,4,pads", "via_costs=-5", "passes=0"):
|
|
106
|
+
resp = client.post(f"/design/kicad/route?{query}", json=_design())
|
|
107
|
+
assert resp.status_code == 422, query
|
|
@@ -25,7 +25,14 @@ def _entry(slug, **over):
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@pytest.fixture
|
|
28
|
-
def
|
|
28
|
+
def user_library(tmp_path):
|
|
29
|
+
from wirestudio.library import Library, default_library
|
|
30
|
+
|
|
31
|
+
return Library(default_library().root, tmp_path / "user-library")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def studio(monkeypatch, user_library):
|
|
29
36
|
monkeypatch.delenv("WIRESTUDIO_DESIGNS_REPO", raising=False)
|
|
30
37
|
files: dict[str, bytes] = {}
|
|
31
38
|
fetched: list[str] = []
|
|
@@ -36,13 +43,15 @@ def studio(monkeypatch):
|
|
|
36
43
|
raise RuntimeError(f"404 {url}")
|
|
37
44
|
return files[url]
|
|
38
45
|
|
|
39
|
-
def publish(*entries, designs=None):
|
|
46
|
+
def publish(*entries, designs=None, components=None):
|
|
40
47
|
files[f"{RAW}/index/index.json"] = json.dumps(
|
|
41
48
|
{"version": 1, "generated": "2026-10-23T00:00:00+00:00", "designs": list(entries)}).encode()
|
|
42
49
|
for slug, doc in (designs or {}).items():
|
|
43
50
|
files[f"{RAW}/main/designs/{slug}/design.json"] = json.dumps(doc).encode()
|
|
51
|
+
for url, text in (components or {}).items():
|
|
52
|
+
files[url] = text.encode()
|
|
44
53
|
|
|
45
|
-
app = TestClient(create_app(community_fetch=fetch))
|
|
54
|
+
app = TestClient(create_app(library=user_library, community_fetch=fetch))
|
|
46
55
|
return app, publish, fetched
|
|
47
56
|
|
|
48
57
|
|
|
@@ -77,7 +86,7 @@ def test_an_unreachable_index_is_a_502(studio):
|
|
|
77
86
|
def test_import_stamps_the_origin(studio):
|
|
78
87
|
app, publish, _ = studio
|
|
79
88
|
publish(_entry("porch-motion"), designs={"porch-motion": {**EXAMPLE, "id": "porch-motion"}})
|
|
80
|
-
body = app.
|
|
89
|
+
body = app.post("/community/designs/porch-motion/import").json()
|
|
81
90
|
assert body["design"]["origin"] == {"catalogue": REPO, "slug": "porch-motion",
|
|
82
91
|
"author": "octocat", "license": "CC-BY-4.0"}
|
|
83
92
|
assert app.post("/design/validate", json=body["design"]).status_code == 200
|
|
@@ -86,14 +95,14 @@ def test_import_stamps_the_origin(studio):
|
|
|
86
95
|
def test_import_only_follows_urls_inside_the_catalogue(studio):
|
|
87
96
|
app, publish, fetched = studio
|
|
88
97
|
publish(_entry("evil", design_url="http://169.254.169.254/latest/meta-data"))
|
|
89
|
-
assert app.
|
|
98
|
+
assert app.post("/community/designs/evil/import").status_code == 502
|
|
90
99
|
assert not any("169.254" in u for u in fetched)
|
|
91
100
|
|
|
92
101
|
|
|
93
102
|
def test_import_of_an_unknown_slug_is_a_404(studio):
|
|
94
103
|
app, publish, _ = studio
|
|
95
104
|
publish()
|
|
96
|
-
assert app.
|
|
105
|
+
assert app.post("/community/designs/nope/import").status_code == 404
|
|
97
106
|
|
|
98
107
|
|
|
99
108
|
def _share(app, **over):
|
|
@@ -189,3 +198,51 @@ def test_share_refuses_what_is_not_a_photo(studio):
|
|
|
189
198
|
assert r.status_code == 422 and "not a readable image" in r.json()["detail"]
|
|
190
199
|
r = _share(app, photos=[{"name": f"p{i}.jpg", "data": _jpeg((10, 10))} for i in range(5)])
|
|
191
200
|
assert r.status_code == 422 and "at most 4" in r.json()["detail"]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
LED_DRIVER = (Path(__file__).resolve().parent / "fixtures" / "components" / "led_driver_npn.yaml").read_text()
|
|
204
|
+
LED_URL = f"{RAW}/main/designs/porch-led/components/led_driver_npn.yaml"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _led_design() -> dict:
|
|
208
|
+
doc = {**EXAMPLE, "id": "porch-led"}
|
|
209
|
+
doc["components"] = [*EXAMPLE["components"],
|
|
210
|
+
{"id": "status", "library_id": "led_driver_npn", "label": "Status LED", "params": {}}]
|
|
211
|
+
return doc
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _publish_led(publish):
|
|
215
|
+
publish(_entry("porch-led", user_components=[{"id": "led_driver_npn", "url": LED_URL}]),
|
|
216
|
+
designs={"porch-led": _led_design()}, components={LED_URL: LED_DRIVER})
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_import_installs_the_designs_user_components(studio, user_library):
|
|
220
|
+
app, publish, _ = studio
|
|
221
|
+
_publish_led(publish)
|
|
222
|
+
r = app.post("/community/designs/porch-led/import")
|
|
223
|
+
assert r.status_code == 200, r.text
|
|
224
|
+
assert r.json()["installed_components"] == ["led_driver_npn"]
|
|
225
|
+
assert user_library.component_source("led_driver_npn") == "user"
|
|
226
|
+
again = app.post("/community/designs/porch-led/import").json()
|
|
227
|
+
assert again["installed_components"] == [], "the same text is already here"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_import_never_overwrites_a_different_user_component(studio, user_library):
|
|
231
|
+
app, publish, _ = studio
|
|
232
|
+
_publish_led(publish)
|
|
233
|
+
user_library.save_component("led_driver_npn", LED_DRIVER.replace("NPN low-side LED driver", "mine", 1))
|
|
234
|
+
r = app.post("/community/designs/porch-led/import")
|
|
235
|
+
assert r.status_code == 409 and "different component 'led_driver_npn'" in r.json()["detail"]
|
|
236
|
+
assert "mine" in user_library.component_yaml("led_driver_npn")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_share_packs_the_user_components_the_design_uses(studio, user_library):
|
|
240
|
+
app, _, _ = studio
|
|
241
|
+
user_library.save_component("led_driver_npn", LED_DRIVER)
|
|
242
|
+
r = _share(app, design=_led_design())
|
|
243
|
+
assert r.status_code == 200, r.text
|
|
244
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
245
|
+
slug = "porch-led"
|
|
246
|
+
assert z.read(f"{slug}/components/led_driver_npn.yaml").decode() == LED_DRIVER
|
|
247
|
+
assert not [n for n in z.namelist() if "/components/" in n and "led_driver_npn" not in n], \
|
|
248
|
+
"bundled components are not packed"
|
|
@@ -58,31 +58,41 @@ def test_no_board_silently_returns_empty(lib):
|
|
|
58
58
|
assert check_pin_compatibility({"components": [], "connections": []}, lib) == []
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
d
|
|
67
|
-
|
|
61
|
+
def _with_lamp(d: dict, pin: str) -> dict:
|
|
62
|
+
"""garage-motion plus a relay/LED output the MCU drives, on `pin`."""
|
|
63
|
+
d["components"].append({"id": "lamp", "library_id": "gpio_output", "label": "Lamp", "params": {}})
|
|
64
|
+
d["connections"].append({"component_id": "lamp", "pin_role": "OUT",
|
|
65
|
+
"target": {"kind": "gpio", "pin": pin}})
|
|
66
|
+
return d
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _move_pir(d: dict, pin: str) -> dict:
|
|
68
70
|
for c in d["connections"]:
|
|
69
71
|
if c["component_id"] == "pir1" and c["pin_role"] == "OUT":
|
|
70
|
-
c["target"] = {"kind": "gpio", "pin":
|
|
71
|
-
|
|
72
|
+
c["target"] = {"kind": "gpio", "pin": pin}
|
|
73
|
+
return d
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_input_only_as_output_is_an_error(lib):
|
|
77
|
+
"""GPIO34 on ESP32-DevKitC-V4 is input-only; an output the MCU drives
|
|
78
|
+
there is a hard error."""
|
|
79
|
+
warnings = check_pin_compatibility(_with_lamp(_load("garage-motion"), "GPIO34"), lib)
|
|
72
80
|
errs = _by_code(warnings, "input_only_as_output")
|
|
73
81
|
assert len(errs) == 1
|
|
74
82
|
assert errs[0].severity == "error"
|
|
75
83
|
assert errs[0].pin == "GPIO34"
|
|
76
84
|
|
|
77
85
|
|
|
86
|
+
def test_a_sensor_output_on_an_input_only_pin_is_fine(lib):
|
|
87
|
+
"""A PIR's OUT is the MCU's input: GPIO34-39 are the usual place for it."""
|
|
88
|
+
warnings = check_pin_compatibility(_move_pir(_load("garage-motion"), "GPIO34"), lib)
|
|
89
|
+
assert warnings == []
|
|
90
|
+
|
|
91
|
+
|
|
78
92
|
def test_boot_high_pin_used_as_output_warns(lib):
|
|
79
93
|
"""ESP32 GPIO0 is boot_high (must be HIGH at boot). Driving it as a digital
|
|
80
94
|
output should warn."""
|
|
81
|
-
|
|
82
|
-
for c in d["connections"]:
|
|
83
|
-
if c["component_id"] == "pir1" and c["pin_role"] == "OUT":
|
|
84
|
-
c["target"] = {"kind": "gpio", "pin": "GPIO0"}
|
|
85
|
-
warnings = check_pin_compatibility(d, lib)
|
|
95
|
+
warnings = check_pin_compatibility(_with_lamp(_load("garage-motion"), "GPIO0"), lib)
|
|
86
96
|
boot = _by_code(warnings, "boot_strap_output")
|
|
87
97
|
assert len(boot) == 1
|
|
88
98
|
assert boot[0].severity == "warn"
|
|
@@ -92,16 +102,23 @@ def test_boot_high_pin_used_as_output_warns(lib):
|
|
|
92
102
|
def test_boot_low_pin_used_as_output_warns(lib):
|
|
93
103
|
"""ESP32 GPIO12 is boot_low (must be LOW at boot, controls flash voltage).
|
|
94
104
|
Output use should warn."""
|
|
95
|
-
|
|
96
|
-
for c in d["connections"]:
|
|
97
|
-
if c["component_id"] == "pir1" and c["pin_role"] == "OUT":
|
|
98
|
-
c["target"] = {"kind": "gpio", "pin": "GPIO12"}
|
|
99
|
-
warnings = check_pin_compatibility(d, lib)
|
|
105
|
+
warnings = check_pin_compatibility(_with_lamp(_load("garage-motion"), "GPIO12"), lib)
|
|
100
106
|
boot = _by_code(warnings, "boot_strap_output")
|
|
101
107
|
assert len(boot) == 1
|
|
102
108
|
assert "LOW at boot" in boot[0].message
|
|
103
109
|
|
|
104
110
|
|
|
111
|
+
@pytest.mark.parametrize("pin, level", [("GPIO0", "HIGH"), ("GPIO12", "LOW")])
|
|
112
|
+
def test_a_strap_pin_driven_by_a_sensor_warns(lib, pin, level):
|
|
113
|
+
"""A PIR holds its output at power-up, so on a strap pin it decides the
|
|
114
|
+
boot mode -- the hazard is the sensor's, even though the MCU reads it."""
|
|
115
|
+
warnings = check_pin_compatibility(_move_pir(_load("garage-motion"), pin), lib)
|
|
116
|
+
driven = _by_code(warnings, "boot_strap_driven")
|
|
117
|
+
assert len(driven) == 1 and driven[0].severity == "warn"
|
|
118
|
+
assert f"must be {level} at boot" in driven[0].message
|
|
119
|
+
assert _by_code(warnings, "boot_strap_output") == []
|
|
120
|
+
|
|
121
|
+
|
|
105
122
|
def test_boot_strap_pin_as_input_does_not_warn(lib):
|
|
106
123
|
"""Buttons wired to boot strap pins (e.g., Sonoff GPIO0 button) are read
|
|
107
124
|
as inputs; the validator should not flag this even though the pin is a
|
|
@@ -362,3 +379,50 @@ def test_a_reserved_bus_id_is_an_error(lib):
|
|
|
362
379
|
assert w.severity == "error"
|
|
363
380
|
assert w.component_id == "uart0"
|
|
364
381
|
assert "uart_bus0" in w.message
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
PART_DRIVEN = {
|
|
385
|
+
"hc-sr501": ["OUT"], "rcwl-0516": ["OUT"], "ld2410": ["OUT"], "remote_receiver": ["OUT"],
|
|
386
|
+
"hlw8012": ["CF", "CF1"], "sx127x": ["DIO0"], "wiegand": ["D0", "D1"],
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@pytest.mark.parametrize("library_id, roles", sorted(PART_DRIVEN.items()))
|
|
391
|
+
def test_part_driven_signals_are_mcu_inputs(lib, library_id, roles):
|
|
392
|
+
"""The audit's verdicts: these lines are outputs of the part, so inputs
|
|
393
|
+
of the MCU, and the part drives them."""
|
|
394
|
+
pins = {p.role: p for p in lib.component(library_id).electrical.pins}
|
|
395
|
+
for role in roles:
|
|
396
|
+
assert pins[role].kind == "digital_in" and pins[role].driven, f"{library_id}.{role}"
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def test_the_component_gate_refuses_driven_on_an_output(lib):
|
|
400
|
+
from wirestudio.library.check import check_component_yaml
|
|
401
|
+
|
|
402
|
+
text = (REPO_ROOT / "tests" / "fixtures" / "components" / "led_driver_npn.yaml").read_text()
|
|
403
|
+
assert check_component_yaml(text, lib).ok
|
|
404
|
+
bad = text.replace("kind: digital_out", "kind: digital_out, driven: true", 1)
|
|
405
|
+
assert bad != text
|
|
406
|
+
report = check_component_yaml(bad, lib)
|
|
407
|
+
assert not report.ok and any("`driven`" in e for e in report.errors)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _with_receivers(d: dict, n: int, **params) -> dict:
|
|
411
|
+
for i, pin in enumerate(("GPIO32", "GPIO33", "GPIO25", "GPIO26")[:n]):
|
|
412
|
+
d["components"].append({"id": f"rx{i}", "library_id": "remote_receiver",
|
|
413
|
+
"label": f"RX {i}", "params": dict(params)})
|
|
414
|
+
d["connections"].append({"component_id": f"rx{i}", "pin_role": "OUT",
|
|
415
|
+
"target": {"kind": "gpio", "pin": pin}})
|
|
416
|
+
return d
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def test_a_third_remote_receiver_on_a_classic_esp32_warns(lib):
|
|
420
|
+
assert _by_code(check_pin_compatibility(_with_receivers(_load("garage-motion"), 2), lib), "rmt_memory") == []
|
|
421
|
+
warns = _by_code(check_pin_compatibility(_with_receivers(_load("garage-motion"), 3), lib), "rmt_memory")
|
|
422
|
+
assert len(warns) == 1 and warns[0].component_id == "rx2"
|
|
423
|
+
assert "rmt_symbols" in warns[0].message
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def test_receivers_with_rmt_symbols_set_do_not_warn(lib):
|
|
427
|
+
d = _with_receivers(_load("garage-motion"), 3, rmt_symbols=128)
|
|
428
|
+
assert _by_code(check_pin_compatibility(d, lib), "rmt_memory") == []
|
|
@@ -14,12 +14,14 @@ The generator is pure text-out, so we lean on substring assertions +
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
|
+
import re
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
|
|
19
20
|
import pytest
|
|
20
21
|
|
|
21
22
|
from wirestudio.kicad import generate_skidl
|
|
22
23
|
from wirestudio.kicad.generator import _py_var
|
|
24
|
+
from wirestudio.kicad.netlist import build_netlist
|
|
23
25
|
from wirestudio.library import default_library
|
|
24
26
|
from wirestudio.model import Design
|
|
25
27
|
|
|
@@ -160,15 +162,24 @@ def test_rail_targets_become_named_nets(lib):
|
|
|
160
162
|
assert 'NET_PLUS__3V3 = Net("+3V3")' in script
|
|
161
163
|
|
|
162
164
|
|
|
163
|
-
def
|
|
164
|
-
"""
|
|
165
|
-
|
|
165
|
+
def test_bus_targets_land_on_a_net_per_line(lib):
|
|
166
|
+
"""A bus is a net per line: the BME280's SDA and SCL go to i2c0's SDA
|
|
167
|
+
and SCL nets, never one shared net. The symbol's I2C pins are SDI/SCK
|
|
168
|
+
(pin_map rewrites SDA->SDI, SCL->SCK)."""
|
|
166
169
|
script = generate_skidl(_design("garage-motion"), lib)
|
|
167
|
-
assert '
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
assert 'c_bme1["SDI"] +=
|
|
171
|
-
assert 'c_bme1["SCK"] +=
|
|
170
|
+
assert 'NET_BUS_i2c0_SDA = Net("BUS_i2c0_SDA")' in script
|
|
171
|
+
assert 'NET_BUS_i2c0_SCL = Net("BUS_i2c0_SCL")' in script
|
|
172
|
+
assert 'Net("BUS_i2c0")' not in script
|
|
173
|
+
assert 'c_bme1["SDI"] += NET_BUS_i2c0_SDA' in script
|
|
174
|
+
assert 'c_bme1["SCK"] += NET_BUS_i2c0_SCL' in script
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_uart_lines_cross_over(lib):
|
|
178
|
+
"""A UART part's TX is the MCU's RX line, and its RX the MCU's TX."""
|
|
179
|
+
nets = {n.name: {(p.ref, p.pin_role) for p in n.pads}
|
|
180
|
+
for n in build_netlist(_design("nextion-thermostat"), lib)}
|
|
181
|
+
assert nets["BUS_nextion_uart_RX"] == {("U1", "TX"), ("M1", "IO16")}
|
|
182
|
+
assert nets["BUS_nextion_uart_TX"] == {("U1", "RX"), ("M1", "IO17")}
|
|
172
183
|
|
|
173
184
|
|
|
174
185
|
def test_gpio_targets_share_one_declared_net(lib):
|
|
@@ -187,8 +198,8 @@ def test_board_pins_join_gpio_rail_and_bus_nets(lib):
|
|
|
187
198
|
the module's VDD, and 5V has no module pin, which the script says."""
|
|
188
199
|
script = generate_skidl(_design("garage-motion"), lib)
|
|
189
200
|
assert 'board["IO13"] += NET_GPIO_GPIO13 # GPIO13' in script
|
|
190
|
-
assert 'board["IO21"] +=
|
|
191
|
-
assert 'board["IO22"] +=
|
|
201
|
+
assert 'board["IO21"] += NET_BUS_i2c0_SDA # GPIO21' in script
|
|
202
|
+
assert 'board["IO22"] += NET_BUS_i2c0_SCL # GPIO22' in script
|
|
192
203
|
assert 'board["VDD"] += NET_PLUS__3V3 # 3V3' in script
|
|
193
204
|
assert 'board["GND"] += GND # GND' in script
|
|
194
205
|
assert "# 5V: no pin on RF_Module:ESP32-WROOM-32" in script
|
|
@@ -198,8 +209,8 @@ def test_board_pins_use_the_boards_own_role_names(lib):
|
|
|
198
209
|
"""A D1 mini design names pins D1/D2; the pin_map turns them into the
|
|
199
210
|
symbol's SCL/D1 and SDA/D2."""
|
|
200
211
|
script = generate_skidl(_design("room-climate"), lib)
|
|
201
|
-
assert 'board["SDA/D2"]
|
|
202
|
-
assert 'board["SCL/D1"]
|
|
212
|
+
assert re.search(r'board\["SDA/D2"\] \+= NET_BUS_\w+_SDA ', script)
|
|
213
|
+
assert re.search(r'board\["SCL/D1"\] \+= NET_BUS_\w+_SCL ', script)
|
|
203
214
|
assert 'board["3V3"] += NET_PLUS__3V3' in script
|
|
204
215
|
|
|
205
216
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Routed DRC: kicad-cli's report summarised for people, stored with the
|
|
2
|
+
routed board, and served by the API and route_pcb.
|
|
3
|
+
|
|
4
|
+
kicad-cli itself runs in CI's pcb-route gate, which also checks this path
|
|
5
|
+
reports the airwires of an unrouted board; here its JSON comes from a
|
|
6
|
+
fixture captured from KiCad 8 on the unrouted garage-motion board."""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from wirestudio.kicad import drc as drc_mod
|
|
15
|
+
from wirestudio.kicad.drc import WAIVED, summarize
|
|
16
|
+
from wirestudio.kicad.route import routed_drc
|
|
17
|
+
|
|
18
|
+
FIXTURE = json.loads((Path(__file__).parent / "fixtures" / "drc_unrouted_garage_motion.json").read_text())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_summary_leads_with_the_airwires():
|
|
22
|
+
s = summarize(FIXTURE)
|
|
23
|
+
assert s["unconnected"] == 7 and s["ok"] is False
|
|
24
|
+
assert s["groups"][0]["type"] == "unconnected_items"
|
|
25
|
+
first = s["groups"][0]["violations"][0]["items"][0]
|
|
26
|
+
assert first["description"].startswith("Pad ") and first["pos"]["x"] is not None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_footprint_inherent_types_are_waived_not_failed():
|
|
30
|
+
s = summarize(FIXTURE)
|
|
31
|
+
drill = next(g for g in s["groups"] if g["type"] == "drill_out_of_range")
|
|
32
|
+
assert drill["waived"] and drill["count"] == 12 and drill["reason"] == WAIVED["drill_out_of_range"]
|
|
33
|
+
assert s["errors"] == 0, "the only errors besides airwires are waived"
|
|
34
|
+
assert [g["waived"] for g in s["groups"]][-1] is True, "waived groups sort last"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_a_routed_board_with_a_short_is_not_ok():
|
|
38
|
+
report = {"violations": [{"type": "shorting_items", "severity": "error",
|
|
39
|
+
"description": "Items shorting two nets", "items": []}],
|
|
40
|
+
"unconnected_items": []}
|
|
41
|
+
s = summarize(report)
|
|
42
|
+
assert s["ok"] is False and s["errors"] == 1 and s["unconnected"] == 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_a_clean_routed_board_is_ok():
|
|
46
|
+
report = {"violations": [{"type": "silk_overlap", "severity": "warning", "description": "x", "items": []}],
|
|
47
|
+
"unconnected_items": []}
|
|
48
|
+
s = summarize(report)
|
|
49
|
+
assert s["ok"] is True and s["warnings"] == 0 and s["waived"] == 1
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture
|
|
53
|
+
def slot(tmp_path):
|
|
54
|
+
key = "0123456789abcdef"
|
|
55
|
+
(tmp_path / key).mkdir()
|
|
56
|
+
(tmp_path / key / "routed.kicad_pcb").write_text("(kicad_pcb (segment (net 1)))")
|
|
57
|
+
return tmp_path, key
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_routed_drc_runs_once_and_is_stored(slot, monkeypatch):
|
|
61
|
+
cache, key = slot
|
|
62
|
+
calls = []
|
|
63
|
+
monkeypatch.setattr(drc_mod, "drc_status", lambda: {"available": True, "reason": None})
|
|
64
|
+
monkeypatch.setattr(drc_mod, "run_drc", lambda text: calls.append(text) or summarize(FIXTURE))
|
|
65
|
+
first = routed_drc(key, cache_dir=cache)
|
|
66
|
+
second = routed_drc(key, cache_dir=cache)
|
|
67
|
+
assert first == second and first["unconnected"] == 7
|
|
68
|
+
assert len(calls) == 1 and (cache / key / "drc.json").is_file()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_routed_drc_without_kicad_cli_is_none(slot, monkeypatch):
|
|
72
|
+
cache, key = slot
|
|
73
|
+
monkeypatch.setattr(drc_mod, "drc_status", lambda: {"available": False, "reason": "no"})
|
|
74
|
+
assert routed_drc(key, cache_dir=cache) is None
|
|
75
|
+
assert routed_drc("ffffffffffffffff", cache_dir=cache) is None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_the_api_serves_the_routed_drc(slot, monkeypatch):
|
|
79
|
+
from fastapi.testclient import TestClient
|
|
80
|
+
|
|
81
|
+
from wirestudio.api.app import create_app
|
|
82
|
+
|
|
83
|
+
cache, key = slot
|
|
84
|
+
monkeypatch.setenv("WIRESTUDIO_ROUTE_CACHE", str(cache))
|
|
85
|
+
monkeypatch.setattr(drc_mod, "drc_status", lambda: {"available": True, "reason": None})
|
|
86
|
+
monkeypatch.setattr(drc_mod, "run_drc", lambda text: summarize(FIXTURE))
|
|
87
|
+
app = TestClient(create_app())
|
|
88
|
+
body = app.get(f"/design/kicad/route/{key}/drc").json()
|
|
89
|
+
assert body["unconnected"] == 7 and body["groups"][0]["type"] == "unconnected_items"
|
|
90
|
+
assert app.get("/design/kicad/route/ffffffffffffffff/drc").status_code == 404
|
|
91
|
+
assert app.get("/design/kicad/route/nope/drc").status_code == 422
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_route_pcb_brief_drops_item_detail():
|
|
95
|
+
from wirestudio.agent.tools import _drc_brief
|
|
96
|
+
|
|
97
|
+
brief = _drc_brief(summarize(FIXTURE))
|
|
98
|
+
unconnected = brief["groups"][0]
|
|
99
|
+
assert unconnected == {"type": "unconnected_items", "severity": "error", "count": 7, "waived": False,
|
|
100
|
+
"examples": ["Missing connection between items"] * 3}
|
|
101
|
+
assert "examples" not in brief["groups"][-1], "waived groups carry counts only"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_route_pcb_refuses_bad_parameters():
|
|
105
|
+
from wirestudio.agent.tools import _run_route_pcb
|
|
106
|
+
from wirestudio.library import default_library
|
|
107
|
+
|
|
108
|
+
design = json.loads((Path(__file__).resolve().parent.parent / "wirestudio/examples/garage-motion.json").read_text())
|
|
109
|
+
out = _run_route_pcb(design, default_library(), keepouts=[{"x": 0, "y": 0, "w": 0, "h": 1}])
|
|
110
|
+
assert out["ok"] is False and "bad route parameters" in out["error"]
|
|
111
|
+
out = _run_route_pcb(design, default_library(), via_costs=-1)
|
|
112
|
+
assert out["ok"] is False and "bad route parameters" in out["error"]
|
|
@@ -43,10 +43,12 @@ def _design(name: str) -> Design:
|
|
|
43
43
|
|
|
44
44
|
def test_build_netlist_groups_rails_and_buses(lib):
|
|
45
45
|
nets = {n.name: n for n in build_netlist(_design("garage-motion"), lib)}
|
|
46
|
-
assert {"GND", "+5V", "+3V3", "
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
assert {"GND", "+5V", "+3V3", "BUS_i2c0_SDA", "BUS_i2c0_SCL"} <= set(nets)
|
|
47
|
+
assert "BUS_i2c0" not in nets
|
|
48
|
+
sda = {(p.component_id, p.pin_role) for p in nets["BUS_i2c0_SDA"].pads}
|
|
49
|
+
scl = {(p.component_id, p.pin_role) for p in nets["BUS_i2c0_SCL"].pads}
|
|
50
|
+
assert sda == {("bme1", "SDA"), ("__board__", "IO21")}
|
|
51
|
+
assert scl == {("bme1", "SCL"), ("__board__", "IO22")}
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
def test_build_netlist_refs_agree_with_assign_refs(lib):
|
|
@@ -85,6 +87,30 @@ def test_pcb_starts_correctly_and_is_paren_balanced(lib):
|
|
|
85
87
|
assert '(layer "Edge.Cuts")' in pcb and "gr_rect" in pcb
|
|
86
88
|
|
|
87
89
|
|
|
90
|
+
@libs_required
|
|
91
|
+
def test_pcb_pours_gnd_on_the_bottom_inside_the_outline(lib):
|
|
92
|
+
pcb = generate_kicad_pcb(_design("garage-motion"), lib)
|
|
93
|
+
gnd = re.search(r'\(net (\d+) "GND"\)', pcb).group(1)
|
|
94
|
+
zones = re.findall(r"^\t\(zone\n(.*?)^\t\)$", pcb, re.S | re.M)
|
|
95
|
+
assert len(zones) == 1
|
|
96
|
+
zone = zones[0]
|
|
97
|
+
assert f"(net {gnd})" in zone and '(layer "B.Cu")' in zone
|
|
98
|
+
assert "(connect_pads yes" in zone
|
|
99
|
+
assert "(filled_polygon" not in pcb # the route step fills it
|
|
100
|
+
ox0, oy0, ox1, oy1 = map(float, re.search(
|
|
101
|
+
r"\(gr_rect\n\t\t\(start ([\d.]+) ([\d.]+)\)\n\t\t\(end ([\d.]+) ([\d.]+)\)", pcb).groups())
|
|
102
|
+
pts = [tuple(map(float, p)) for p in re.findall(r"\(xy ([\d.]+) ([\d.]+)\)", zone)]
|
|
103
|
+
assert all(ox0 < x < ox1 and oy0 < y < oy1 for x, y in pts)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@libs_required
|
|
107
|
+
def test_pcb_without_a_gnd_net_has_no_pour(lib):
|
|
108
|
+
design = _design("garage-motion")
|
|
109
|
+
design.connections = [c for c in design.connections
|
|
110
|
+
if not (c.target.kind == "rail" and c.target.rail.lower() == "gnd")]
|
|
111
|
+
assert not re.search(r"^\t\(zone$", generate_kicad_pcb(design, lib), re.M)
|
|
112
|
+
|
|
113
|
+
|
|
88
114
|
@libs_required
|
|
89
115
|
def test_pcb_embeds_board_and_component_footprints(lib):
|
|
90
116
|
pcb = generate_kicad_pcb(_design("garage-motion"), lib)
|
|
@@ -96,11 +122,12 @@ def test_pcb_embeds_board_and_component_footprints(lib):
|
|
|
96
122
|
@libs_required
|
|
97
123
|
def test_pcb_binds_pin_mapped_pad_to_net(lib):
|
|
98
124
|
"""The BME280's VCC role -> VDD pin -> a real footprint pad, carrying the
|
|
99
|
-
+3V3 net; SDA/SCL land on the I2C bus
|
|
100
|
-
number -> pad resolution end to end."""
|
|
125
|
+
+3V3 net; SDA/SCL land on the I2C bus's line nets. Exercises symbol
|
|
126
|
+
pin-name -> number -> pad resolution end to end."""
|
|
101
127
|
pcb = generate_kicad_pcb(_design("garage-motion"), lib)
|
|
102
128
|
assert re.search(r'\(net \d+ "\+3V3"\)', pcb)
|
|
103
|
-
assert re.search(r'\(net \d+ "
|
|
129
|
+
assert re.search(r'\(net \d+ "BUS_i2c0_SDA"\)', pcb)
|
|
130
|
+
assert re.search(r'\(net \d+ "BUS_i2c0_SCL"\)', pcb)
|
|
104
131
|
|
|
105
132
|
|
|
106
133
|
@libs_required
|
|
@@ -228,7 +255,7 @@ def test_build_netlist_binds_board_pins(lib):
|
|
|
228
255
|
nets = {n.name: n for n in build_netlist(_design("garage-motion"), lib)}
|
|
229
256
|
board = {(p.pin_role, name) for name, n in nets.items() for p in n.pads if p.ref == "M1"}
|
|
230
257
|
assert ("IO13", "GPIO_GPIO13") in board
|
|
231
|
-
assert ("IO21", "
|
|
258
|
+
assert ("IO21", "BUS_i2c0_SDA") in board and ("IO22", "BUS_i2c0_SCL") in board
|
|
232
259
|
assert ("VDD", "+3V3") in board
|
|
233
260
|
assert ("GND", "GND") in board
|
|
234
261
|
assert all(p.component_id == "__board__" for n in nets.values() for p in n.pads if p.ref == "M1")
|
|
@@ -14,7 +14,9 @@ from pathlib import Path
|
|
|
14
14
|
import pytest
|
|
15
15
|
|
|
16
16
|
from wirestudio.kicad.route import (
|
|
17
|
+
Keepout,
|
|
17
18
|
RouteError,
|
|
19
|
+
RouteParams,
|
|
18
20
|
RouteUnavailable,
|
|
19
21
|
route_board,
|
|
20
22
|
route_cache_key,
|
|
@@ -76,7 +78,9 @@ def test_cache_key_folds_in_board_and_passes():
|
|
|
76
78
|
a = route_cache_key(UNROUTED)
|
|
77
79
|
assert a == route_cache_key(UNROUTED)
|
|
78
80
|
assert a != route_cache_key(ROUTED)
|
|
79
|
-
assert a != route_cache_key(UNROUTED, max_passes=5)
|
|
81
|
+
assert a != route_cache_key(UNROUTED, RouteParams(max_passes=5))
|
|
82
|
+
assert a != route_cache_key(UNROUTED, RouteParams(via_costs=500))
|
|
83
|
+
assert a != route_cache_key(UNROUTED, RouteParams(keepouts=(Keepout(1, 1, 2, 2),)))
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
def test_route_events_roundtrip(toolchain, tmp_path):
|
|
@@ -142,3 +146,71 @@ def test_route_board_returns_text_or_raises(toolchain, tmp_path):
|
|
|
142
146
|
_script(tmp_path / "fake-java", 'echo "could not route"\n')
|
|
143
147
|
with pytest.raises(RouteError, match="could not route"):
|
|
144
148
|
route_board(ROUTED, cache_dir=tmp_path / "c2")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_a_routed_board_carries_its_drc_and_a_cache_hit_reuses_it(toolchain, tmp_path, monkeypatch):
|
|
152
|
+
from wirestudio.kicad import drc
|
|
153
|
+
|
|
154
|
+
runs = []
|
|
155
|
+
monkeypatch.setattr(drc, "drc_status", lambda: {"available": True, "reason": None})
|
|
156
|
+
monkeypatch.setattr(drc, "run_drc", lambda text: runs.append(text) or {"ok": True, "unconnected": 0})
|
|
157
|
+
cache = tmp_path / "cache"
|
|
158
|
+
first = list(route_events(UNROUTED, cache_dir=cache))[-1]
|
|
159
|
+
again = list(route_events(UNROUTED, cache_dir=cache))[-1]
|
|
160
|
+
assert first["drc"] == again["drc"] == {"ok": True, "unconnected": 0}
|
|
161
|
+
assert again["cache_hit"] and len(runs) == 1
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_keepout_parse_and_validation():
|
|
165
|
+
assert Keepout.parse("1, 2, 3, 4") == Keepout(1, 2, 3, 4)
|
|
166
|
+
assert Keepout.parse("1,2,3,4,vias") == Keepout(1, 2, 3, 4, tracks=False, vias=True)
|
|
167
|
+
assert Keepout.parse("1,2,3,4,tracks") == Keepout(1, 2, 3, 4, tracks=True, vias=False)
|
|
168
|
+
for bad in ("1,2,3", "1,2,3,4,pads", "a,2,3,4", "1,2,0,4", "1,2,3,-1"):
|
|
169
|
+
with pytest.raises(ValueError):
|
|
170
|
+
Keepout.parse(bad)
|
|
171
|
+
with pytest.raises(ValueError):
|
|
172
|
+
Keepout(0, 0, 1, 1, tracks=False, vias=False)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_route_params_validation():
|
|
176
|
+
for kwargs in ({"max_passes": 0}, {"max_passes": 201}, {"via_costs": -1}, {"via_costs": 10_001}):
|
|
177
|
+
with pytest.raises(ValueError):
|
|
178
|
+
RouteParams(**kwargs)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_apply_adds_rule_areas_inside_the_board():
|
|
182
|
+
board = RouteParams(keepouts=(Keepout(1, 2, 3, 4, vias=False),)).apply(UNROUTED)
|
|
183
|
+
assert board.count("(") == board.count(")")
|
|
184
|
+
assert board.rstrip().endswith(")")
|
|
185
|
+
assert "(keepout (tracks not_allowed) (vias allowed)" in board
|
|
186
|
+
assert "(xy 1 2) (xy 4 2) (xy 4 6) (xy 1 6)" in board
|
|
187
|
+
assert RouteParams().apply(UNROUTED) == UNROUTED
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_freerouting_args():
|
|
191
|
+
default = RouteParams().freerouting_args()
|
|
192
|
+
assert "--usage_and_diagnostic_data.disable_analytics=true" in default
|
|
193
|
+
assert not any("via_costs" in a for a in default)
|
|
194
|
+
assert "--router.scoring.via_costs=800" in RouteParams(via_costs=800).freerouting_args()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_params_reach_freerouting_and_the_board(toolchain, tmp_path):
|
|
198
|
+
seen = tmp_path / "seen"
|
|
199
|
+
_script(
|
|
200
|
+
tmp_path / "fake-java",
|
|
201
|
+
f'echo "$@" > {seen}/args\necho session > "$6"\n',
|
|
202
|
+
)
|
|
203
|
+
_script(
|
|
204
|
+
tmp_path / "fake-python",
|
|
205
|
+
'case "$2" in\n'
|
|
206
|
+
"probe) echo 8.0.9 ;;\n"
|
|
207
|
+
f'dsn) cp "$3" {seen}/board; echo dsn > "$4" ;;\n'
|
|
208
|
+
'ses) printf \'%s\' \'' + ROUTED.replace("\n", "") + '\' > "$3" ;;\n'
|
|
209
|
+
"esac\n",
|
|
210
|
+
)
|
|
211
|
+
seen.mkdir()
|
|
212
|
+
params = RouteParams(via_costs=1500, keepouts=(Keepout(5, 5, 2, 2),))
|
|
213
|
+
done = list(route_events(UNROUTED, params, cache_dir=tmp_path / "cache"))[-1]
|
|
214
|
+
assert done["ok"] is True
|
|
215
|
+
assert "--router.scoring.via_costs=1500" in (seen / "args").read_text()
|
|
216
|
+
assert "wirestudio keepout" in (seen / "board").read_text()
|