wirestudio 0.45.0__tar.gz → 0.46.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.45.0/wirestudio.egg-info → wirestudio-0.46.0}/PKG-INFO +5 -4
- {wirestudio-0.45.0 → wirestudio-0.46.0}/README.md +4 -3
- wirestudio-0.46.0/tests/test_board_check.py +205 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_community.py +141 -2
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_firmware_gen.py +73 -5
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad.py +6 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_erc.py +29 -5
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_pcb.py +16 -0
- wirestudio-0.46.0/tests/test_lorawan_fragments.py +279 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_server.py +3 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/__init__.py +1 -1
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/agent.py +1 -1
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/tools.py +80 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/app.py +54 -9
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/community.py +140 -27
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/schemas.py +18 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/erc.py +19 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/generator.py +3 -1
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/netlist.py +27 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/__init__.py +94 -48
- wirestudio-0.46.0/wirestudio/library/board_check.py +376 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-c3-devkitm-1.yaml +24 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-s3-devkitc-1.yaml +31 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/check.py +1 -1
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bl0906.yaml +164 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/cse7766.yaml +58 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ld2410.yaml +56 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ld2420.yaml +64 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/max31855.yaml +38 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mcp23008.yaml +1 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mcp23017.yaml +1 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mcp23s17.yaml +1 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mcp3008.yaml +17 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mcp3008_channel.yaml +22 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mhz19.yaml +36 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/modbus.yaml +54 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pcf8574.yaml +1 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pmsx003.yaml +55 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pzemac.yaml +71 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sdm_meter.yaml +78 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sn74hc595.yaml +1 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/uart_gps.yaml +2 -1
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/mcp/server.py +41 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/codec.py +13 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/firmware_gen.py +99 -57
- {wirestudio-0.45.0 → wirestudio-0.46.0/wirestudio.egg-info}/PKG-INFO +5 -4
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio.egg-info/SOURCES.txt +3 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/LICENSE +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/LICENSES.md +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/pyproject.toml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/setup.cfg +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_active_design.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_agent.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_agent_eval.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_agent_stream.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_api.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_api_auth.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_api_route.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_ascii_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_build_backend.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_check_upgrade.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_chirpstack.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_circuitpython.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_codec.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_compatibility.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_compile.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_component_corpus.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_connectors.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_coverage_baseline.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_design_events.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_designs.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_designs_seed.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_display_intent.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_electrical.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_enclosure.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_enclosure_gate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_enclosure_search.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_errors.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_esphome_dashboard.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_esphome_support.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_fleet.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_footprints.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_framework_baseline.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_hardware_gate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_intent.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_inventory.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_jlcpcb.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_drc.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_fab.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_import.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_render.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_route.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_kicad_sch.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_library_check.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_library_hbridge.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_library_rtttl.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_lorawan.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_lorawan_api.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_lorawan_sugar_migration.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_auth.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_client.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_hardware.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_jobs.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_mcp_resources.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_meshtastic.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_micropython.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_micropython_registers.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_migrate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_modules.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_pin_protocols.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_pin_solver.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_printwarden.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_python_blocks.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_recommend.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_register_code.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_rf_ook.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_schema.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_schema_doc.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_schematics_gate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_security_cors.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_seed_onboard.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_serve.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_sqlite_store.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_sqlite_stores.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_strict.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_targets.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_tasmota.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_tbeam_lorawan_example.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_tools_add_bus.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_tools_add_component.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_tools_solve_pins.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_uart_frames.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_validate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_wled.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_workbench.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_workbench_boot.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_workbench_images.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_workbench_provision.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_workbench_serial.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_ws2812_method.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/tests/test_yaml_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/eval.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/eval_tasks.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/agent/session.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/__main__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/auth.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/circuitpython.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/design_input.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/meshtastic.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/micropython.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/serve.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/api/workbench.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/csp/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/csp/compatibility.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/csp/pin_solver.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/designs/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/designs/active.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/designs/events.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/designs/seed.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/designs/store.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/enclosure/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/enclosure/openscad.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/enclosure/search.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/enclosure/yapp/LICENSE +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/enclosure/yapp/YAPPgenerator_v3.scad +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/errors.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/esphome_dashboard/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/access-panel.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/actuator-harness.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/air-quality-station.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/analog-node.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atom-echo.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atom-lite.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atom-matrix.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atoms3-lcd.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atoms3-lite.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atoms3-onboard.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atomu-onboard.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/atomu.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/attic-logger.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/awning-control.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/bench-io.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/bl0906-mainmeter.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/blind-stepper.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/bluemotion.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/bluesonoff.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/button-toggles-light.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/c3-supermini.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/c6-supermini.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/co2-display.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/desk-climate.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/desk-matrix.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/distance-sensor.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/encoder-drives-stepper.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/energy-monitor.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/esp32-audio.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/esp32cam-wrover.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/esp32cam.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/flow-meter.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/garage-motion.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/grill-probe.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/guarded-button-light.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/heltec-v4-gnss-tracker.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/keypad.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/kitchen-scale.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/led-display.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/lorawan-battery-uplink.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/motion-turns-on-light.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/motor-position.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/multi-temp.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/nextion-thermostat.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/oled-knob.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/oled.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/output-hub.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/parking-distance.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/presence-media-node.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/presence-rf.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/rc522.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/room-climate.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/rs485-energy.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/s3-devkitc.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/s3-supermini.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/securitypanel.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/smart-plug-v1.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/smart-plug.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/solder-fan.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/spi-io-node.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/subghz-rfid.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/t-beam-lorawan.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/t-beam.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/temp-above-turns-on-fan.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/temp-turns-on-fan.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/tft-touch.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/ttgo-lora32.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/tuya-smart-plug.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/usbc-led-strip.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/vent-damper.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/wasserpir.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/weather-station.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/wemosgps.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/wled-sensor-strip.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/examples/xiao-grove-climate.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/fleet/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/fleet/client.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/__main__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/ascii_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/circuitpython_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/display_intent.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/micropython_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/generate/yaml_gen.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/intent.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/buy.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/check.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/csv_io.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/match.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/inventory/store.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/jlcpcb/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/jlcpcb/__main__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/jlcpcb/check.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/jlcpcb/client.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/drc.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/fab.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/import.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/importer.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/pcb.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/pcbnew_bridge.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/render.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/route.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/schematic.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/kicad/symbol_parser.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp01_1m.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-c3-supermini.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-c6-supermini.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-devkitc-v4.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-s3-supermini.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32-wrover-cam.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp32cam-ai-thinker.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/esp8285-1m.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/heltec-wifi-lora32-v2.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/heltec-wifi-lora32-v3.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/heltec-wifi-lora32-v4.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atom-echo.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atom-matrix.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atom.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atoms3-lite.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atoms3.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/m5stack-atomu.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/nodemcu-32s.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/nodemcu-v2.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/seeed-xiao-esp32c3.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/ttgo-lora32-v1.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/ttgo-lora32-v2.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/ttgo-t-beam.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/boards/wemos-d1-mini.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/adc.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ads1115.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ads1115_channel.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/aht10.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/apa102.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/axp192.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/battery_adc.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bh1750.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bme280.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bme680.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bmp180.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/bmp280.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/cc1101.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x02.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x03.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x04.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x05.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x06.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x07.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_1x08.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/connector_2x05_idc.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ct_clamp.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/dht.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ds18b20.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/esp32_camera.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/esp32_rmt_led_strip.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/gpio_input.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/gpio_output.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/grove-aht20.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hbridge.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hbridge_mosfet.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hc-sr04.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hc-sr501.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hlw8012.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/htu21d.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/hx711.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/i2s_microphone.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ili9xxx.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ina219.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/lcd_pcf8574.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/led_indicator.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/level_shifter.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/max7219.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/max98357a.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mosfet_low_side_driver.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mpu6050.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/mpu6886.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/nextion.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/npn_low_side_driver.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pca9685.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pn532.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/potentiometer.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pulse_counter.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/pwm_fan.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rc522.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rcwl-0516.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rdm6300.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/remote_receiver.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/remote_transmitter.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rf_bridge.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rotary_encoder.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/rtttl.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/scd4x.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/servo.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sgp4x.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sht3xd.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sht4x.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ssd1306.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/st7789.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/sx127x.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tm1637.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tm1638.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tsl2561.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tuya.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tuya_sensor.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/tuya_switch.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/uln2003.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/usb_c_power_in.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/veml7700.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/vl53l0x.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/voltage_divider.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/wiegand.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/ws2812b.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/components/xpt2046.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/electrical.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/modules/env-display.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/modules/fan-controller.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/modules/motion-light.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/modules/oled-knob-13.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/library/modules/relay-4ch.yaml +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/mcp/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/mcp/auth.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/mcp/hardware.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/mcp/jobs.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/migrate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/model.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/printwarden/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/printwarden/client.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/recommend/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/recommend/recommender.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/schema/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/schema/design.schema.json +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/seed.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/sqlite_store.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/base.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/build_backend.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/esphome.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/__main__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/api.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/build_local.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/chirpstack.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/compile.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/templates/main.cpp.j2 +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/templates/platformio.ini.j2 +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/lorawan/workbench_provision.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/tasmota.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/wled.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/targets/wled_build.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/validate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/__init__.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/boot.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/client.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/gate.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/images.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio/workbench/serial.py +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio.egg-info/dependency_links.txt +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio.egg-info/entry_points.txt +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio.egg-info/requires.txt +0 -0
- {wirestudio-0.45.0 → wirestudio-0.46.0}/wirestudio.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wirestudio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.46.0
|
|
4
4
|
Summary: Agent-driven IoT device design tool that produces ESPHome YAML.
|
|
5
5
|
Author: wirestudio contributors
|
|
6
6
|
License: MIT
|
|
@@ -147,8 +147,8 @@ Tiers, in priority order:
|
|
|
147
147
|
| **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` |
|
|
148
148
|
| **Verified** | Fleet handoff | push YAML to `fleet-for-esphome` ha-addon, optional compile + log relay | round-trip tests in `tests/test_fleet.py` |
|
|
149
149
|
| **Verified** | KiCad schematic | write a KiCad 8 `.kicad_sch` the studio draws itself (board, peripherals, subcircuits drawn as circuits from their library layouts; symbols embedded), plus a SKiDL script for the same design | every bundled example builds a KiCad netlist against the pinned upstream symbol libraries, every PR ([gate](.github/workflows/kicad-schematic.yml)) — no unresolved symbols or pins; kicad-cli reads the netlist back from every written sheet's geometry and it must match the design's, then `kicad-cli sch erc` runs on all of them against a baseline of the violation types the writer cannot avoid ([gate](.github/workflows/kicad-render.yml)). Parts KiCad ships no symbol for (sensor/module breakouts) render as labeled generic headers |
|
|
150
|
-
| **Verified** | KiCad PCB layout | emit a placed, unrouted `.kicad_pcb` — footprints, nets, ratsnest, edge cuts | every bundled example emits a structurally sound board, every PR ([gate](.github/workflows/pcb-layout.yml)); a DRC tier opens each board in real KiCad ([gate](.github/workflows/pcb-drc.yml)) |
|
|
151
|
-
| **Verified** | Fab outputs | JLCPCB upload bundle — BOM, CPL, Gerber + drill (`/design/fab/*`) | CPL positions match the `.kicad_pcb` placement; the DRC tier smoke-tests the Gerber export.
|
|
150
|
+
| **Verified** | KiCad PCB layout | emit a placed, unrouted `.kicad_pcb` — footprints, nets, ratsnest, edge cuts, and a GND pour on the bottom layer that the route step fills | every bundled example emits a structurally sound board, every PR ([gate](.github/workflows/pcb-layout.yml)); a DRC tier opens each board in real KiCad ([gate](.github/workflows/pcb-drc.yml)) |
|
|
151
|
+
| **Verified** | Fab outputs | JLCPCB upload bundle — BOM, CPL, Gerber + drill (`/design/fab/*`) | CPL positions match the `.kicad_pcb` placement; the DRC tier smoke-tests the Gerber export. `?route=true` routes the board first; without it Gerbers carry pads but no tracks or pour (`is_routed` flags it). The routed gate checks a routed board's B.Cu Gerber carries the GND pour ([gate](.github/workflows/pcb-route.yml)) |
|
|
152
152
|
| **Verified** | Subcircuits + building blocks | a component built from discrete parts (five bundled blocks, two H-bridges) expands into the schematic, PCB, BOM, CPL and inventory check; part values are parametric; each block's `verify.checks` rule runs at `validate` with the real params and rails | goldens for every subcircuit example pin YAML + BOM; `component_check` fails a block whose defaults break its own rule (`tests/test_electrical.py`); the schematic and PCB gates cover the expanded parts like any other |
|
|
153
153
|
| **Verified** | Inventory check | cross-check a design's BOM against the drawer (library modules + discrete parts), propose same-package substitutes ranked by pinout, package, rating headroom and stock, accept one or all of them as `part_overrides` that every fab output honours, pick list by location, buy list priced on JLCPCB | matching, substitution and coverage tests in `tests/test_inventory.py` against the real drawer CSV; JLCPCB pricing is mocked and degrades to an unpriced list when the parts API is down |
|
|
154
154
|
| **Verified** | Parametric enclosure | OpenSCAD `.scad` from board mount-hole metadata, built on [YAPP_Box](https://github.com/mrWheel/YAPP_Box): base and lid with standoffs, port cutouts, a ridge and snap joins | every enclosure-capable board renders through real OpenSCAD to a non-empty, manifold (closed, printable) solid, every PR ([gate](.github/workflows/enclosure-render.yml)) |
|
|
@@ -160,12 +160,13 @@ Tiers, in priority order:
|
|
|
160
160
|
| **Works (lighter checks)** | Meshtastic flashing + config | proxy the official release factory image (`/meshtastic/firmware`) for the radio boards, flash it via the unified WebSerial dialog, then push region, modem preset, owner and primary channel as protobufs over the same port | endpoint tests with mocked upstream; board-to-variant map checked against `meshtastic/firmware` variants; config push tested against a fake device; no live-flash gate |
|
|
161
161
|
| **Works (lighter checks)** | MicroPython flashing + main.py | proxy the official release image for the board's chip (`/micropython/firmware`; every library board maps to a generic port image, ESP8266 included), flash via the unified dialog, generate `main.py` from the design over the `machine` API (`/micropython/code`) and push it through the raw REPL over the same serial port | endpoint tests with a captured download page; every board's starter and every example's main.py parse as Python; the raw-REPL push is tested against a fake board; the micropython.org page format could not be fetched from the build session, so the first live flash validates it; no live-flash gate |
|
|
162
162
|
| **Verified** | Component authoring | write a component YAML in the studio or through `component_create`; the same gate as `component_check` (schema, subcircuit nets, template render, KiCad symbols / pins / footprint) before it lands in the user library | a user-authored component goes through the gate and its synthetic design through upstream `esphome config`, every PR ([gate](.github/workflows/esphome-config.yml)); the gate rejects every file in a corpus of bad components for the named reason (`tests/test_component_corpus.py`); KiCad resolution runs only where the toolchain is present |
|
|
163
|
+
| **Verified** | Board authoring | write a board YAML in the studio or through `board_create`; the same gate as `board_check` (schema, every bus / onboard / radio pin against `gpio_capabilities`, KiCad symbol pins and footprint, enclosure geometry, ESPHome's board table, an ESPHome render with the onboard peripherals) before it lands in the user library | every bundled board passes the gate (`tests/test_board_check.py`); a user-authored board with its onboard peripherals goes through upstream `esphome config`, every PR ([gate](.github/workflows/esphome-config.yml)); the pinout is not checked against the datasheet |
|
|
163
164
|
| **Verified** | ESPHome dashboard compile | push the rendered YAML to a running ESPHome Device Builder (`/esphome/push`), start a compile there, stream the log and serve the firmware, with the same job shapes as the fleet path | nightly, against a live Device Builder (ESPHome 2026.10.0) open and password-protected: push, compile over the WebSocket API, fetch the image, with the client's wire assumptions asserted ([dashboard](.github/workflows/esphome-compile.yml)); client + route tests against a fake of the same in `tests/test_esphome_dashboard.py` |
|
|
164
165
|
| **Works (lighter checks)** | CircuitPython flashing | proxy the official release image (`/circuitpython/firmware`) for ESP32/S3/C3/C6 boards, flash via the unified dialog, serve a generated starter `code.py` (`/circuitpython/code`) | endpoint tests with mocked upstream; every board-id mapping verified against downloads.circuitpython.org; generated starters parse as valid Python; no live-flash gate |
|
|
165
166
|
| **Verified** | MCP server | 53 tools over the Model Context Protocol: 31 design / library / inventory / fab, plus 22 that reach hardware — workbench flash and boot verification, LoRaWAN compile / provision / activation, fleet and dashboard push + build status, and job polling for the long ones | the `mcp` package's own client connects to the mounted server over Streamable HTTP with the bearer token, lists the tools, drives a design from `create_design` to `render` and reads a resource, every PR (`tests/test_mcp_client.py`); tool / auth / resource tests in `tests/test_mcp_*.py`; the hardware tools drive the real clients against wire-level fakes, and were exercised against a live bench, ChirpStack and fleet during bring-up |
|
|
166
167
|
| **Experimental** | Thingiverse search relay | rank community models for a board | smoke-tested; depends on a third-party search API that ranks unevenly |
|
|
167
168
|
| **Verified** | Agent (Claude tool-using) | natural-language design driving | scored nightly against the 28-task list in `wirestudio/agent/eval_tasks.yaml`: the design it hands back passes `esphome config`, carries the asked-for change, and the turn stayed within its tool-call budget ([gate](.github/workflows/agent-eval.yml)); every task's reference solution passes the same scorer on every PR (`tests/test_agent_eval.py`). The nightly run drives the studio through Claude Code over MCP with the `CLAUDE_CODE_OAUTH_TOKEN` secret (a subscription); the embedded agent is scored too when `ANTHROPIC_API_KEY` is set. Each driver's first scored run sets its gate |
|
|
168
|
-
| **Verified** | PCB autorouting | Freerouting roundtrip — board → Specctra DSN → routed → SES import; SSE route endpoint, `route_pcb` MCP/agent tool, web-UI Route button, `?route=true` fab exports, `-pcb` image variant | representative examples route with zero unconnected items and
|
|
169
|
+
| **Verified** | PCB autorouting | Freerouting roundtrip — board → Specctra DSN → routed → SES import, GND pour filled; via cost and keepout rectangles as parameters; KiCad's DRC of the routed board reported back; SSE route endpoint, `route_pcb` MCP/agent tool, web-UI Route button, `?route=true` fab exports, `-pcb` image variant | representative examples route with zero unconnected items, a filled pour and a clean routed DRC; a high via cost must remove vias and a keepout must clear the track under it ([gate](.github/workflows/pcb-route.yml)) |
|
|
169
170
|
|
|
170
171
|
The **Verified** tier is the bar the project is asking to be judged
|
|
171
172
|
on. Everything else is offered with the caveat that's spelled out in
|
|
@@ -113,8 +113,8 @@ Tiers, in priority order:
|
|
|
113
113
|
| **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` |
|
|
114
114
|
| **Verified** | Fleet handoff | push YAML to `fleet-for-esphome` ha-addon, optional compile + log relay | round-trip tests in `tests/test_fleet.py` |
|
|
115
115
|
| **Verified** | KiCad schematic | write a KiCad 8 `.kicad_sch` the studio draws itself (board, peripherals, subcircuits drawn as circuits from their library layouts; symbols embedded), plus a SKiDL script for the same design | every bundled example builds a KiCad netlist against the pinned upstream symbol libraries, every PR ([gate](.github/workflows/kicad-schematic.yml)) — no unresolved symbols or pins; kicad-cli reads the netlist back from every written sheet's geometry and it must match the design's, then `kicad-cli sch erc` runs on all of them against a baseline of the violation types the writer cannot avoid ([gate](.github/workflows/kicad-render.yml)). Parts KiCad ships no symbol for (sensor/module breakouts) render as labeled generic headers |
|
|
116
|
-
| **Verified** | KiCad PCB layout | emit a placed, unrouted `.kicad_pcb` — footprints, nets, ratsnest, edge cuts | every bundled example emits a structurally sound board, every PR ([gate](.github/workflows/pcb-layout.yml)); a DRC tier opens each board in real KiCad ([gate](.github/workflows/pcb-drc.yml)) |
|
|
117
|
-
| **Verified** | Fab outputs | JLCPCB upload bundle — BOM, CPL, Gerber + drill (`/design/fab/*`) | CPL positions match the `.kicad_pcb` placement; the DRC tier smoke-tests the Gerber export.
|
|
116
|
+
| **Verified** | KiCad PCB layout | emit a placed, unrouted `.kicad_pcb` — footprints, nets, ratsnest, edge cuts, and a GND pour on the bottom layer that the route step fills | every bundled example emits a structurally sound board, every PR ([gate](.github/workflows/pcb-layout.yml)); a DRC tier opens each board in real KiCad ([gate](.github/workflows/pcb-drc.yml)) |
|
|
117
|
+
| **Verified** | Fab outputs | JLCPCB upload bundle — BOM, CPL, Gerber + drill (`/design/fab/*`) | CPL positions match the `.kicad_pcb` placement; the DRC tier smoke-tests the Gerber export. `?route=true` routes the board first; without it Gerbers carry pads but no tracks or pour (`is_routed` flags it). The routed gate checks a routed board's B.Cu Gerber carries the GND pour ([gate](.github/workflows/pcb-route.yml)) |
|
|
118
118
|
| **Verified** | Subcircuits + building blocks | a component built from discrete parts (five bundled blocks, two H-bridges) expands into the schematic, PCB, BOM, CPL and inventory check; part values are parametric; each block's `verify.checks` rule runs at `validate` with the real params and rails | goldens for every subcircuit example pin YAML + BOM; `component_check` fails a block whose defaults break its own rule (`tests/test_electrical.py`); the schematic and PCB gates cover the expanded parts like any other |
|
|
119
119
|
| **Verified** | Inventory check | cross-check a design's BOM against the drawer (library modules + discrete parts), propose same-package substitutes ranked by pinout, package, rating headroom and stock, accept one or all of them as `part_overrides` that every fab output honours, pick list by location, buy list priced on JLCPCB | matching, substitution and coverage tests in `tests/test_inventory.py` against the real drawer CSV; JLCPCB pricing is mocked and degrades to an unpriced list when the parts API is down |
|
|
120
120
|
| **Verified** | Parametric enclosure | OpenSCAD `.scad` from board mount-hole metadata, built on [YAPP_Box](https://github.com/mrWheel/YAPP_Box): base and lid with standoffs, port cutouts, a ridge and snap joins | every enclosure-capable board renders through real OpenSCAD to a non-empty, manifold (closed, printable) solid, every PR ([gate](.github/workflows/enclosure-render.yml)) |
|
|
@@ -126,12 +126,13 @@ Tiers, in priority order:
|
|
|
126
126
|
| **Works (lighter checks)** | Meshtastic flashing + config | proxy the official release factory image (`/meshtastic/firmware`) for the radio boards, flash it via the unified WebSerial dialog, then push region, modem preset, owner and primary channel as protobufs over the same port | endpoint tests with mocked upstream; board-to-variant map checked against `meshtastic/firmware` variants; config push tested against a fake device; no live-flash gate |
|
|
127
127
|
| **Works (lighter checks)** | MicroPython flashing + main.py | proxy the official release image for the board's chip (`/micropython/firmware`; every library board maps to a generic port image, ESP8266 included), flash via the unified dialog, generate `main.py` from the design over the `machine` API (`/micropython/code`) and push it through the raw REPL over the same serial port | endpoint tests with a captured download page; every board's starter and every example's main.py parse as Python; the raw-REPL push is tested against a fake board; the micropython.org page format could not be fetched from the build session, so the first live flash validates it; no live-flash gate |
|
|
128
128
|
| **Verified** | Component authoring | write a component YAML in the studio or through `component_create`; the same gate as `component_check` (schema, subcircuit nets, template render, KiCad symbols / pins / footprint) before it lands in the user library | a user-authored component goes through the gate and its synthetic design through upstream `esphome config`, every PR ([gate](.github/workflows/esphome-config.yml)); the gate rejects every file in a corpus of bad components for the named reason (`tests/test_component_corpus.py`); KiCad resolution runs only where the toolchain is present |
|
|
129
|
+
| **Verified** | Board authoring | write a board YAML in the studio or through `board_create`; the same gate as `board_check` (schema, every bus / onboard / radio pin against `gpio_capabilities`, KiCad symbol pins and footprint, enclosure geometry, ESPHome's board table, an ESPHome render with the onboard peripherals) before it lands in the user library | every bundled board passes the gate (`tests/test_board_check.py`); a user-authored board with its onboard peripherals goes through upstream `esphome config`, every PR ([gate](.github/workflows/esphome-config.yml)); the pinout is not checked against the datasheet |
|
|
129
130
|
| **Verified** | ESPHome dashboard compile | push the rendered YAML to a running ESPHome Device Builder (`/esphome/push`), start a compile there, stream the log and serve the firmware, with the same job shapes as the fleet path | nightly, against a live Device Builder (ESPHome 2026.10.0) open and password-protected: push, compile over the WebSocket API, fetch the image, with the client's wire assumptions asserted ([dashboard](.github/workflows/esphome-compile.yml)); client + route tests against a fake of the same in `tests/test_esphome_dashboard.py` |
|
|
130
131
|
| **Works (lighter checks)** | CircuitPython flashing | proxy the official release image (`/circuitpython/firmware`) for ESP32/S3/C3/C6 boards, flash via the unified dialog, serve a generated starter `code.py` (`/circuitpython/code`) | endpoint tests with mocked upstream; every board-id mapping verified against downloads.circuitpython.org; generated starters parse as valid Python; no live-flash gate |
|
|
131
132
|
| **Verified** | MCP server | 53 tools over the Model Context Protocol: 31 design / library / inventory / fab, plus 22 that reach hardware — workbench flash and boot verification, LoRaWAN compile / provision / activation, fleet and dashboard push + build status, and job polling for the long ones | the `mcp` package's own client connects to the mounted server over Streamable HTTP with the bearer token, lists the tools, drives a design from `create_design` to `render` and reads a resource, every PR (`tests/test_mcp_client.py`); tool / auth / resource tests in `tests/test_mcp_*.py`; the hardware tools drive the real clients against wire-level fakes, and were exercised against a live bench, ChirpStack and fleet during bring-up |
|
|
132
133
|
| **Experimental** | Thingiverse search relay | rank community models for a board | smoke-tested; depends on a third-party search API that ranks unevenly |
|
|
133
134
|
| **Verified** | Agent (Claude tool-using) | natural-language design driving | scored nightly against the 28-task list in `wirestudio/agent/eval_tasks.yaml`: the design it hands back passes `esphome config`, carries the asked-for change, and the turn stayed within its tool-call budget ([gate](.github/workflows/agent-eval.yml)); every task's reference solution passes the same scorer on every PR (`tests/test_agent_eval.py`). The nightly run drives the studio through Claude Code over MCP with the `CLAUDE_CODE_OAUTH_TOKEN` secret (a subscription); the embedded agent is scored too when `ANTHROPIC_API_KEY` is set. Each driver's first scored run sets its gate |
|
|
134
|
-
| **Verified** | PCB autorouting | Freerouting roundtrip — board → Specctra DSN → routed → SES import; SSE route endpoint, `route_pcb` MCP/agent tool, web-UI Route button, `?route=true` fab exports, `-pcb` image variant | representative examples route with zero unconnected items and
|
|
135
|
+
| **Verified** | PCB autorouting | Freerouting roundtrip — board → Specctra DSN → routed → SES import, GND pour filled; via cost and keepout rectangles as parameters; KiCad's DRC of the routed board reported back; SSE route endpoint, `route_pcb` MCP/agent tool, web-UI Route button, `?route=true` fab exports, `-pcb` image variant | representative examples route with zero unconnected items, a filled pour and a clean routed DRC; a high via cost must remove vias and a keepout must clear the track under it ([gate](.github/workflows/pcb-route.yml)) |
|
|
135
136
|
|
|
136
137
|
The **Verified** tier is the bar the project is asking to be judged
|
|
137
138
|
on. Everything else is offered with the caveat that's spelled out in
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Checking and creating library boards at runtime."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from fastapi.testclient import TestClient
|
|
8
|
+
|
|
9
|
+
from wirestudio.api.app import create_app
|
|
10
|
+
from wirestudio.library import Library
|
|
11
|
+
from wirestudio.library.board_check import check_board_yaml, create_board
|
|
12
|
+
|
|
13
|
+
BUNDLED = Path(__file__).resolve().parent.parent / "wirestudio" / "library"
|
|
14
|
+
DRAFT = (Path(__file__).resolve().parent / "fixtures" / "boards" / "lolin-s2-mini.yaml").read_text()
|
|
15
|
+
DEVKIT = (BUNDLED / "boards" / "esp32-devkitc-v4.yaml").read_text()
|
|
16
|
+
C3 = (BUNDLED / "boards" / "esp32-c3-devkitm-1.yaml").read_text()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def lib(tmp_path):
|
|
21
|
+
return Library(BUNDLED, tmp_path / "user")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def client(tmp_path):
|
|
26
|
+
return TestClient(create_app(library=Library(BUNDLED, tmp_path / "user")))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _errors(text: str, lib: Library) -> str:
|
|
30
|
+
return " | ".join(check_board_yaml(text, lib).errors)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_every_bundled_board_passes(lib):
|
|
34
|
+
for path in sorted((BUNDLED / "boards").glob("*.yaml")):
|
|
35
|
+
report = check_board_yaml(path.read_text(), lib)
|
|
36
|
+
assert report.ok, (path.stem, report.errors)
|
|
37
|
+
assert report.exists == "bundled"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_draft_passes_and_renders_its_onboard_parts(lib):
|
|
41
|
+
report = check_board_yaml(DRAFT, lib)
|
|
42
|
+
assert report.ok, report.errors
|
|
43
|
+
assert report.library_id == "lolin-s2-mini" and report.exists == ""
|
|
44
|
+
assert any("gpio_output, gpio_input" in v for v in report.verified)
|
|
45
|
+
assert any("34.3 x 25.4" in v for v in report.verified)
|
|
46
|
+
assert any("no kicad: block" in w for w in report.warnings)
|
|
47
|
+
assert any("datasheet" in n for n in report.not_checked)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_unparseable_and_invalid_files_are_reported(lib):
|
|
51
|
+
assert "not valid YAML" in _errors("id: [", lib)
|
|
52
|
+
assert "top level must be a mapping" in _errors("- a", lib)
|
|
53
|
+
assert "platformio_board" in _errors(DRAFT.replace("platformio_board: lolin_s2_mini\n", ""), lib)
|
|
54
|
+
assert "names the file" in _errors(DRAFT.replace("id: lolin-s2-mini", "id: Lolin S2"), lib)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_platform_fields_must_agree(lib):
|
|
58
|
+
assert "not an ESP32 variant" in _errors(DRAFT.replace("chip_variant: esp32s2", "chip_variant: esp32s9"), lib)
|
|
59
|
+
assert "needs mcu esp32" in _errors(DRAFT.replace("mcu: esp32\n", "mcu: esp8266\n"), lib)
|
|
60
|
+
assert "not an ESPHome platform" in _errors(DRAFT.replace("chip_variant: esp32s2", "chip_variant: stm32"), lib)
|
|
61
|
+
assert "framework" in _errors(DRAFT.replace("framework: arduino", "framework: zephyr"), lib)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_every_named_pin_must_be_a_gpio(lib):
|
|
65
|
+
errors = _errors(DRAFT.replace("sda: GPIO33", "sda: GPIO34").replace("pin: GPIO15", "pin: GPIO14"), lib)
|
|
66
|
+
assert "default_buses.i2c.sda: GPIO34 is not in gpio_capabilities" in errors
|
|
67
|
+
assert "onboard_peripherals.led.pin: GPIO14 is not in gpio_capabilities" in errors
|
|
68
|
+
assert "no scl pin" in _errors(DRAFT.replace(", scl: GPIO35", ""), lib)
|
|
69
|
+
assert "unknown bus type" in _errors(DRAFT.replace(" spi: {", " qspi: {"), lib)
|
|
70
|
+
assert "no GND rail" in _errors(DRAFT.replace(' - {name: "GND", voltage: 0.0}\n', ""), lib)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_an_input_only_pin_cannot_drive_a_bus_line(lib):
|
|
74
|
+
text = DRAFT.replace("GPIO33: [gpio, pwm, i2c_sda]", "GPIO33: [gpio, input_only]")
|
|
75
|
+
assert "GPIO33 is input_only and cannot drive sda" in _errors(text, lib)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_radio_pins_are_cross_checked(lib):
|
|
79
|
+
heltec = (BUNDLED / "boards" / "heltec-wifi-lora32-v2.yaml").read_text()
|
|
80
|
+
assert check_board_yaml(heltec, lib).ok
|
|
81
|
+
broken = heltec.replace("pins: {cs: GPIO18", "pins: {cs: GPIO99")
|
|
82
|
+
assert broken != heltec
|
|
83
|
+
assert "radio.pins.cs: GPIO99" in _errors(broken, lib)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_enclosure_geometry_stays_on_the_pcb(lib):
|
|
87
|
+
off = DRAFT.replace("offset_mm: 8.95", "offset_mm: 20.0")
|
|
88
|
+
assert "runs off the 25.4 mm short_a edge" in _errors(off, lib)
|
|
89
|
+
edge = DRAFT.replace("edge: short_a", "edge: top")
|
|
90
|
+
assert "edge 'top'" in _errors(edge, lib)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_pin_map_is_checked_against_the_symbol(lib, tmp_path, monkeypatch):
|
|
94
|
+
symdir = tmp_path / "symbols"
|
|
95
|
+
symdir.mkdir()
|
|
96
|
+
pins = " ".join(
|
|
97
|
+
f'(pin passive line (at 0 0 0) (length 2.54) (name "Pin_{n}") (number "{n}"))' for n in range(1, 31))
|
|
98
|
+
(symdir / "Connector_Generic.kicad_sym").write_text(
|
|
99
|
+
'(kicad_symbol_lib (version 20231120) (generator kicad_symbol_editor)'
|
|
100
|
+
f' (symbol "Conn_02x15_Odd_Even" {pins}))')
|
|
101
|
+
monkeypatch.setenv("KICAD8_SYMBOL_DIR", str(symdir))
|
|
102
|
+
report = check_board_yaml(C3, lib)
|
|
103
|
+
assert report.ok, report.errors
|
|
104
|
+
assert any("has every pin_map pin" in v for v in report.verified)
|
|
105
|
+
|
|
106
|
+
assert "has no pin '31'" in _errors(C3.replace('GPIO1: "19"', 'GPIO1: "31"'), lib)
|
|
107
|
+
assert "GPIO1 and GPIO0 share pin 17, which would short them" in _errors(C3.replace('GPIO1: "19"', 'GPIO1: "17"'), lib)
|
|
108
|
+
assert "no symbol 'Conn_02x20'" in _errors(C3.replace("Conn_02x15_Odd_Even", "Conn_02x20"), lib)
|
|
109
|
+
assert "symbol library 'RF_Module' not found" in _errors(DEVKIT, lib)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_create_saves_into_the_user_library_only(lib, tmp_path):
|
|
113
|
+
report = create_board(DRAFT, lib)
|
|
114
|
+
assert report.ok and report.saved.endswith("user/boards/lolin-s2-mini.yaml")
|
|
115
|
+
assert Path(report.saved).read_text() == DRAFT
|
|
116
|
+
assert lib.board_source("lolin-s2-mini") == "user"
|
|
117
|
+
assert lib.board("lolin-s2-mini").chip_variant == "esp32s2"
|
|
118
|
+
assert "lolin-s2-mini" in [b.id for b in lib.list_boards()]
|
|
119
|
+
|
|
120
|
+
again = create_board(DRAFT, lib)
|
|
121
|
+
assert not again.ok and any("already exists" in e for e in again.errors)
|
|
122
|
+
assert create_board(DRAFT, lib, overwrite=True).ok
|
|
123
|
+
|
|
124
|
+
bundled = create_board(DEVKIT, lib)
|
|
125
|
+
assert not bundled.ok and any("bundled board" in e for e in bundled.errors)
|
|
126
|
+
assert not (tmp_path / "user" / "boards" / "esp32-devkitc-v4.yaml").exists()
|
|
127
|
+
|
|
128
|
+
with pytest.raises(PermissionError):
|
|
129
|
+
lib.delete_board("esp32-devkitc-v4")
|
|
130
|
+
lib.delete_board("lolin-s2-mini")
|
|
131
|
+
assert lib.board_source("lolin-s2-mini") == ""
|
|
132
|
+
with pytest.raises(FileNotFoundError):
|
|
133
|
+
lib.board("lolin-s2-mini")
|
|
134
|
+
|
|
135
|
+
assert not create_board(DRAFT, Library(BUNDLED)).ok # no user dir configured
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_a_bundled_board_wins_over_a_user_file(lib, tmp_path):
|
|
139
|
+
(tmp_path / "user" / "boards").mkdir(parents=True)
|
|
140
|
+
(tmp_path / "user" / "boards" / "esp32-devkitc-v4.yaml").write_text(DRAFT.replace("lolin-s2-mini", "esp32-devkitc-v4"))
|
|
141
|
+
assert lib.board_source("esp32-devkitc-v4") == "bundled"
|
|
142
|
+
assert lib.board("esp32-devkitc-v4").chip_variant == "esp32"
|
|
143
|
+
assert [b.id for b in lib.list_boards()].count("esp32-devkitc-v4") == 1
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_user_board_flows_into_a_design(lib):
|
|
147
|
+
from wirestudio.generate.yaml_gen import render_yaml
|
|
148
|
+
from wirestudio.model import Design
|
|
149
|
+
|
|
150
|
+
assert create_board(DRAFT, lib).ok
|
|
151
|
+
design = Design.model_validate({
|
|
152
|
+
"schema_version": "0.1", "id": "t", "name": "t",
|
|
153
|
+
"board": {"library_id": "lolin-s2-mini", "mcu": "esp32"},
|
|
154
|
+
"power": {"supply": "usb-5v", "rail_voltage_v": 5.0, "budget_ma": 500},
|
|
155
|
+
"components": [{"id": "led", "library_id": "gpio_output", "label": "LED"}],
|
|
156
|
+
"buses": [], "requirements": [], "warnings": [],
|
|
157
|
+
"connections": [
|
|
158
|
+
{"component_id": "led", "pin_role": "OUT", "target": {"kind": "gpio", "pin": "GPIO15"}},
|
|
159
|
+
],
|
|
160
|
+
})
|
|
161
|
+
rendered = render_yaml(design, lib)
|
|
162
|
+
assert "board: lolin_s2_mini" in rendered and "variant: ESP32S2" in rendered
|
|
163
|
+
assert "pin: GPIO15" in rendered
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_api_round_trip(client):
|
|
167
|
+
check = client.post("/library/boards/check", json={"yaml": DRAFT})
|
|
168
|
+
assert check.status_code == 200 and check.json()["ok"]
|
|
169
|
+
|
|
170
|
+
created = client.post("/library/boards", json={"yaml": DRAFT})
|
|
171
|
+
assert created.status_code == 201 and created.json()["saved"]
|
|
172
|
+
listed = {b["id"]: b for b in client.get("/library/boards").json()}
|
|
173
|
+
assert listed["lolin-s2-mini"]["source"] == "user"
|
|
174
|
+
assert listed["esp32-devkitc-v4"]["source"] == "bundled"
|
|
175
|
+
assert client.get("/library/boards/lolin-s2-mini").json()["chip_variant"] == "esp32s2"
|
|
176
|
+
assert client.get("/library/boards/lolin-s2-mini/yaml").text == DRAFT
|
|
177
|
+
|
|
178
|
+
assert client.post("/library/boards", json={"yaml": DRAFT}).status_code == 409
|
|
179
|
+
assert client.post("/library/boards", json={"yaml": DEVKIT}).status_code == 409
|
|
180
|
+
bad = client.post("/library/boards", json={"yaml": DRAFT.replace("pin: GPIO15", "pin: GPIO14")})
|
|
181
|
+
assert bad.status_code == 422 and not bad.json()["ok"]
|
|
182
|
+
|
|
183
|
+
assert client.delete("/library/boards/esp32-devkitc-v4").status_code == 409
|
|
184
|
+
assert client.delete("/library/boards/lolin-s2-mini").status_code == 200
|
|
185
|
+
assert client.delete("/library/boards/lolin-s2-mini").status_code == 404
|
|
186
|
+
assert "lolin-s2-mini" not in {b["id"] for b in client.get("/library/boards").json()}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_agent_tools(lib):
|
|
190
|
+
from wirestudio.agent.tools import _run_board_check, _run_board_create, _run_board_delete, _run_list_boards
|
|
191
|
+
|
|
192
|
+
assert _run_board_check({}, lib, yaml=DRAFT)["ok"]
|
|
193
|
+
assert _run_board_create({}, lib, yaml=DRAFT)["saved"]
|
|
194
|
+
sources = {b["library_id"]: b["source"] for b in _run_list_boards({}, lib)["boards"]}
|
|
195
|
+
assert sources["lolin-s2-mini"] == "user"
|
|
196
|
+
assert _run_board_delete({}, lib, library_id="lolin-s2-mini")["ok"]
|
|
197
|
+
assert not _run_board_delete({}, lib, library_id="esp32-devkitc-v4")["ok"]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_platformio_board_is_looked_up_in_esphome(lib):
|
|
201
|
+
pytest.importorskip("esphome")
|
|
202
|
+
assert any("lolin_s2_mini is an ESPHome esp32 board" in v for v in check_board_yaml(DRAFT, lib).verified)
|
|
203
|
+
unknown = check_board_yaml(DRAFT.replace("lolin_s2_mini", "no_such_board"), lib)
|
|
204
|
+
assert unknown.ok and any("not in ESPHome's esp32 board list" in w for w in unknown.warnings)
|
|
205
|
+
assert "is an esp32c3 board in ESPHome, not esp32s2" in _errors(DRAFT.replace("lolin_s2_mini", "esp32-c3-devkitm-1"), lib)
|
|
@@ -43,9 +43,10 @@ def studio(monkeypatch, user_library):
|
|
|
43
43
|
raise RuntimeError(f"404 {url}")
|
|
44
44
|
return files[url]
|
|
45
45
|
|
|
46
|
-
def publish(*entries, designs=None, components=None):
|
|
46
|
+
def publish(*entries, designs=None, components=None, library=None):
|
|
47
47
|
files[f"{RAW}/index/index.json"] = json.dumps(
|
|
48
|
-
{"version": 1, "generated": "2026-10-23T00:00:00+00:00", "designs": list(entries)
|
|
48
|
+
{"version": 1, "generated": "2026-10-23T00:00:00+00:00", "designs": list(entries),
|
|
49
|
+
**(library or {})}).encode()
|
|
49
50
|
for slug, doc in (designs or {}).items():
|
|
50
51
|
files[f"{RAW}/main/designs/{slug}/design.json"] = json.dumps(doc).encode()
|
|
51
52
|
for url, text in (components or {}).items():
|
|
@@ -246,3 +247,141 @@ def test_share_packs_the_user_components_the_design_uses(studio, user_library):
|
|
|
246
247
|
assert z.read(f"{slug}/components/led_driver_npn.yaml").decode() == LED_DRIVER
|
|
247
248
|
assert not [n for n in z.namelist() if "/components/" in n and "led_driver_npn" not in n], \
|
|
248
249
|
"bundled components are not packed"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
S2_MINI = (Path(__file__).resolve().parent / "fixtures" / "boards" / "lolin-s2-mini.yaml").read_text()
|
|
253
|
+
LIB_LED_URL = f"{RAW}/main/library/components/led_driver_npn/component.yaml"
|
|
254
|
+
LIB_S2_URL = f"{RAW}/main/library/boards/lolin-s2-mini/board.yaml"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _publish_library(publish, **over):
|
|
258
|
+
publish(library={
|
|
259
|
+
"components": [{"id": "led_driver_npn", "name": "NPN LED driver", "author": "octocat",
|
|
260
|
+
"license": "MIT", "url": LIB_LED_URL, **over}],
|
|
261
|
+
"boards": [{"id": "lolin-s2-mini", "name": "LOLIN S2 Mini", "author": "octocat",
|
|
262
|
+
"license": "MIT", "url": LIB_S2_URL}],
|
|
263
|
+
}, components={LIB_LED_URL: LED_DRIVER, LIB_S2_URL: S2_MINI})
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def test_library_lists_entries_and_what_is_here(studio, user_library):
|
|
267
|
+
app, publish, _ = studio
|
|
268
|
+
_publish_library(publish)
|
|
269
|
+
user_library.save_component("led_driver_npn", LED_DRIVER)
|
|
270
|
+
body = app.get("/community/library").json()
|
|
271
|
+
assert [(e["id"], e["installed"]) for e in body["components"]] == [("led_driver_npn", "user")]
|
|
272
|
+
assert [(e["id"], e["installed"]) for e in body["boards"]] == [("lolin-s2-mini", "")]
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_an_index_without_library_entries_lists_none(studio):
|
|
276
|
+
app, publish, _ = studio
|
|
277
|
+
publish(_entry("porch-motion"))
|
|
278
|
+
body = app.get("/community/library").json()
|
|
279
|
+
assert body["components"] == [] and body["boards"] == []
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_install_runs_the_gate_and_is_idempotent(studio, user_library):
|
|
283
|
+
app, publish, _ = studio
|
|
284
|
+
_publish_library(publish)
|
|
285
|
+
r = app.post("/community/library/boards/lolin-s2-mini/install")
|
|
286
|
+
assert r.status_code == 200 and r.json()["installed"] is True
|
|
287
|
+
assert user_library.board_source("lolin-s2-mini") == "user"
|
|
288
|
+
assert app.post("/community/library/boards/lolin-s2-mini/install").json()["installed"] is False
|
|
289
|
+
r = app.post("/community/library/components/led_driver_npn/install")
|
|
290
|
+
assert r.status_code == 200 and user_library.component_source("led_driver_npn") == "user"
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_install_refuses_conflicts_and_foreign_urls(studio, user_library):
|
|
294
|
+
app, publish, _ = studio
|
|
295
|
+
_publish_library(publish)
|
|
296
|
+
user_library.save_component("led_driver_npn", LED_DRIVER.replace("NPN low-side LED driver", "mine", 1))
|
|
297
|
+
r = app.post("/community/library/components/led_driver_npn/install")
|
|
298
|
+
assert r.status_code == 409 and "different component" in r.json()["detail"]
|
|
299
|
+
assert "mine" in user_library.component_yaml("led_driver_npn")
|
|
300
|
+
assert app.post("/community/library/components/nope/install").status_code == 404
|
|
301
|
+
assert app.post("/community/library/modules/x/install").status_code == 404
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def test_install_only_follows_urls_inside_the_catalogue(studio, user_library):
|
|
305
|
+
app, publish, fetched = studio
|
|
306
|
+
_publish_library(publish, url="https://evil.example/led.yaml")
|
|
307
|
+
r = app.post("/community/library/components/led_driver_npn/install")
|
|
308
|
+
assert r.status_code == 502 and "points outside" in r.json()["detail"]
|
|
309
|
+
assert "https://evil.example/led.yaml" not in fetched
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_install_refuses_an_entry_whose_file_names_another_id(studio, user_library):
|
|
313
|
+
app, publish, _ = studio
|
|
314
|
+
publish(library={"boards": [{"id": "lolin-s2", "url": LIB_S2_URL}]}, components={LIB_S2_URL: S2_MINI})
|
|
315
|
+
r = app.post("/community/library/boards/lolin-s2/install")
|
|
316
|
+
assert r.status_code == 502 and "declares id 'lolin-s2-mini'" in r.json()["detail"]
|
|
317
|
+
assert user_library.board_source("lolin-s2-mini") == ""
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_install_of_a_broken_entry_is_reported_not_saved(studio, user_library):
|
|
321
|
+
app, publish, _ = studio
|
|
322
|
+
broken = S2_MINI.replace("pin: GPIO15", "pin: GPIO14")
|
|
323
|
+
publish(library={"boards": [{"id": "lolin-s2-mini", "url": LIB_S2_URL}]}, components={LIB_S2_URL: broken})
|
|
324
|
+
r = app.post("/community/library/boards/lolin-s2-mini/install")
|
|
325
|
+
assert r.status_code == 502 and "GPIO14 is not in gpio_capabilities" in r.json()["detail"]
|
|
326
|
+
assert user_library.board_source("lolin-s2-mini") == ""
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _share_library(app, **over):
|
|
330
|
+
body = {"kind": "boards", "id": "lolin-s2-mini", "author": "@octocat", "tags": ["esp32s2"],
|
|
331
|
+
"source": "https://www.wemos.cc/en/latest/s2/s2_mini.html", **over}
|
|
332
|
+
return app.post("/community/library/share", json=body)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_share_library_packs_the_entry_folder(studio, user_library):
|
|
336
|
+
app, _, _ = studio
|
|
337
|
+
user_library.save_board("lolin-s2-mini", S2_MINI)
|
|
338
|
+
r = _share_library(app, notes="Pinout from the WEMOS docs.")
|
|
339
|
+
assert r.status_code == 200, r.text
|
|
340
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
341
|
+
assert sorted(z.namelist()) == ["lolin-s2-mini/README.md", "lolin-s2-mini/board.yaml", "lolin-s2-mini/meta.yaml"]
|
|
342
|
+
assert z.read("lolin-s2-mini/board.yaml").decode() == S2_MINI
|
|
343
|
+
assert yaml.safe_load(z.read("lolin-s2-mini/meta.yaml")) == {
|
|
344
|
+
"author": "octocat", "tags": ["esp32s2"], "source": "https://www.wemos.cc/en/latest/s2/s2_mini.html"}
|
|
345
|
+
|
|
346
|
+
user_library.save_component("led_driver_npn", LED_DRIVER)
|
|
347
|
+
r = _share_library(app, kind="components", id="led_driver_npn", source="")
|
|
348
|
+
assert sorted(zipfile.ZipFile(io.BytesIO(r.content)).namelist()) == [
|
|
349
|
+
"led_driver_npn/component.yaml", "led_driver_npn/meta.yaml"]
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def test_share_library_refuses_what_is_not_yours_or_not_clean(studio, user_library):
|
|
353
|
+
app, _, _ = studio
|
|
354
|
+
assert _share_library(app, id="esp32-devkitc-v4").status_code == 422
|
|
355
|
+
user_library.save_board("lolin-s2-mini", S2_MINI)
|
|
356
|
+
assert "GitHub handle" in _share_library(app, author="not a handle").json()["detail"]
|
|
357
|
+
assert "https://" in _share_library(app, source="ftp://x").json()["detail"]
|
|
358
|
+
assert _share_library(app, kind="widgets").status_code == 404
|
|
359
|
+
user_library.save_board("lolin-s2-mini", S2_MINI.replace("pin: GPIO15", "pin: GPIO14"))
|
|
360
|
+
assert "no longer passes its gate" in _share_library(app).json()["detail"]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _s2_design() -> dict:
|
|
364
|
+
return {**EXAMPLE, "id": "s2-blink",
|
|
365
|
+
"board": {"library_id": "lolin-s2-mini", "mcu": "esp32"},
|
|
366
|
+
"components": [{"id": "led", "library_id": "gpio_output", "label": "LED", "params": {}}],
|
|
367
|
+
"buses": [],
|
|
368
|
+
"connections": [{"component_id": "led", "pin_role": "OUT", "target": {"kind": "gpio", "pin": "GPIO15"}}]}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def test_design_share_and_import_carry_a_user_board(studio, user_library):
|
|
372
|
+
app, publish, _ = studio
|
|
373
|
+
user_library.save_board("lolin-s2-mini", S2_MINI)
|
|
374
|
+
r = _share(app, design=_s2_design())
|
|
375
|
+
assert r.status_code == 200, r.text
|
|
376
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
377
|
+
assert z.read("s2-blink/boards/lolin-s2-mini.yaml").decode() == S2_MINI
|
|
378
|
+
|
|
379
|
+
user_library.delete_board("lolin-s2-mini")
|
|
380
|
+
url = f"{RAW}/main/designs/s2-blink/boards/lolin-s2-mini.yaml"
|
|
381
|
+
publish(_entry("s2-blink", user_boards=[{"id": "lolin-s2-mini", "url": url}]),
|
|
382
|
+
designs={"s2-blink": _s2_design()}, components={url: S2_MINI})
|
|
383
|
+
r = app.post("/community/designs/s2-blink/import")
|
|
384
|
+
assert r.status_code == 200, r.text
|
|
385
|
+
assert r.json()["installed_boards"] == ["lolin-s2-mini"]
|
|
386
|
+
assert user_library.board_source("lolin-s2-mini") == "user"
|
|
387
|
+
|
|
@@ -7,6 +7,7 @@ import pytest
|
|
|
7
7
|
from wirestudio.library import default_library
|
|
8
8
|
from wirestudio.migrate import migrate
|
|
9
9
|
from wirestudio.model import Design
|
|
10
|
+
from wirestudio.targets.lorawan.codec import decode_js, fields_for
|
|
10
11
|
from wirestudio.targets.lorawan.firmware_gen import generate_firmware, write_firmware
|
|
11
12
|
|
|
12
13
|
|
|
@@ -230,14 +231,11 @@ def test_ci_component_designs_carry_every_lorawan_block(lib):
|
|
|
230
231
|
assert built | board_built == {c.id for c in lib.list_components() if c.lorawan is not None}
|
|
231
232
|
for design in designs:
|
|
232
233
|
main = generate_firmware(design, lib)["src/main.cpp"]
|
|
233
|
-
for
|
|
234
|
-
|
|
235
|
-
assert field.cpp_expr in main
|
|
234
|
+
for field in fields_for(design, lib):
|
|
235
|
+
assert field["cpp_expr"] in main
|
|
236
236
|
|
|
237
237
|
|
|
238
238
|
def test_i2c_sensor_blocks_bring_up_wire_and_report_scaled_fields(lib):
|
|
239
|
-
from wirestudio.targets.lorawan.codec import fields_for
|
|
240
|
-
|
|
241
239
|
design = _design_with("bme280", "sht4x")
|
|
242
240
|
names = [f["name"] for f in fields_for(design, lib)]
|
|
243
241
|
assert {"bme280_temp_c", "bme280_humidity", "bme280_pressure", "sht4x_temp_c"} <= set(names)
|
|
@@ -245,3 +243,73 @@ def test_i2c_sensor_blocks_bring_up_wire_and_report_scaled_fields(lib):
|
|
|
245
243
|
assert "Wire.begin(" in main
|
|
246
244
|
assert "bme280Ok = bme280.begin(0x76, &Wire);" in main
|
|
247
245
|
assert "(int16_t)(bme280TempC * 100.0)" in main
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _wired(components, buses, connections) -> Design:
|
|
249
|
+
return Design.model_validate({
|
|
250
|
+
"schema_version": "1.0", "id": "dev1", "name": "Dev 1", "target": "lorawan", "lorawan": {},
|
|
251
|
+
"board": {"library_id": "ttgo-lora32-v2", "mcu": "esp32"},
|
|
252
|
+
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
253
|
+
"components": [{"id": i, "library_id": c, "label": i, "params": p} for i, c, p in components],
|
|
254
|
+
"buses": buses,
|
|
255
|
+
"connections": [{"component_id": c, "pin_role": r, "target": t} for c, r, t in connections],
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _uart(bus_id, rx, tx, baud=9600):
|
|
260
|
+
return {"id": bus_id, "type": "uart", "rx": rx, "tx": tx, "baud_rate": baud}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _on(bus_id):
|
|
264
|
+
return {"kind": "bus", "bus_id": bus_id}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_each_uart_bus_gets_its_own_hardware_uart(lib):
|
|
268
|
+
parts = [("co2", "mhz19", {}), ("pm", "pmsx003", {})]
|
|
269
|
+
buses = [_uart("u1", "GPIO16", "GPIO17"), _uart("u2", "GPIO32", "GPIO33")]
|
|
270
|
+
conns = [("co2", "TX", _on("u1")), ("co2", "RX", _on("u1")), ("pm", "TX", _on("u2")), ("pm", "RX", _on("u2"))]
|
|
271
|
+
main = generate_firmware(_wired(parts, buses, conns), lib)["src/main.cpp"]
|
|
272
|
+
assert "HardwareSerial co2Serial(1);" in main and "HardwareSerial pmSerial(2);" in main
|
|
273
|
+
assert "co2Serial.begin(9600, SERIAL_8N1, 16, 17);" in main
|
|
274
|
+
third = _wired(parts + [("radar", "ld2410", {})], buses + [_uart("u3", "GPIO4", "GPIO25", 256000)],
|
|
275
|
+
conns + [("radar", "TX", _on("u3")), ("radar", "RX", _on("u3"))])
|
|
276
|
+
with pytest.raises(ValueError, match="hardware UART"):
|
|
277
|
+
generate_firmware(third, lib)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_a_uart_bus_keeps_its_parity(lib):
|
|
281
|
+
design = _wired([("plug", "cse7766", {})], [{**_uart("u1", "GPIO16", "GPIO17", 4800), "parity": "EVEN"}],
|
|
282
|
+
[("plug", "TX", _on("u1"))])
|
|
283
|
+
assert "plugSerial.begin(4800, SERIAL_8E1, 16, 17);" in generate_firmware(design, lib)["src/main.cpp"]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_spi_parts_share_the_radio_bus(lib):
|
|
287
|
+
spi = lib.board("ttgo-lora32-v2").default_buses["spi"]
|
|
288
|
+
conns = [("tc", "CLK", _on("s")), ("tc", "MISO", _on("s")), ("tc", "CS", {"kind": "gpio", "pin": "GPIO25"})]
|
|
289
|
+
main = generate_firmware(_wired([("tc", "max31855", {})], [{"id": "s", "type": "spi", **spi}], conns),
|
|
290
|
+
lib)["src/main.cpp"]
|
|
291
|
+
assert "#include <SPI.h>" in main and "SPI.begin(5, 19, 27);" in main
|
|
292
|
+
elsewhere = {"id": "s", "type": "spi", "clk": "GPIO14", "miso": "GPIO12", "mosi": "GPIO13"}
|
|
293
|
+
with pytest.raises(ValueError, match="radio's bus"):
|
|
294
|
+
generate_firmware(_wired([("tc", "max31855", {})], [elsewhere], conns), lib)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def test_parts_on_a_hub_read_through_it_and_name_their_fields(lib):
|
|
298
|
+
spi = lib.board("ttgo-lora32-v2").default_buses["spi"]
|
|
299
|
+
parts = [("adc", "mcp3008", {}), ("ch0", "mcp3008_channel", {"number": 0}),
|
|
300
|
+
("ch5", "mcp3008_channel", {"number": 5, "reference_voltage": "5.0V"}),
|
|
301
|
+
("bus", "modbus", {}), ("mains", "pzemac", {"address": 2})]
|
|
302
|
+
conns = [("adc", r, _on("s")) for r in ("CLK", "MOSI", "MISO")] + [
|
|
303
|
+
("adc", "CS", {"kind": "gpio", "pin": "GPIO2"}),
|
|
304
|
+
("ch0", "HUB", {"kind": "component", "component_id": "adc"}),
|
|
305
|
+
("ch5", "HUB", {"kind": "component", "component_id": "adc"}),
|
|
306
|
+
("bus", "UART", _on("u1")), ("bus", "FLOW", {"kind": "gpio", "pin": "GPIO4"}),
|
|
307
|
+
("mains", "HUB", {"kind": "component", "component_id": "bus"})]
|
|
308
|
+
design = _wired(parts, [{"id": "s", "type": "spi", **spi}, _uart("u1", "GPIO16", "GPIO17")], conns)
|
|
309
|
+
main = generate_firmware(design, lib)["src/main.cpp"]
|
|
310
|
+
assert "ch5Mv = (uint16_t)(adcRead(5) * 5.0 * 1000.0 / 1023.0 + 0.5);" in main
|
|
311
|
+
assert "busRead(2, 0x04, 0, 10, _d)" in main
|
|
312
|
+
assert "digitalWrite(4, HIGH); // RS485 driver on" in main
|
|
313
|
+
names = [f["name"] for f in fields_for(design, lib)]
|
|
314
|
+
assert {"ch0_voltage", "ch5_voltage", "mains_voltage", "mains_energy"} <= set(names)
|
|
315
|
+
assert "ch5_voltage" in decode_js(fields_for(design, lib))
|
|
@@ -318,3 +318,9 @@ def test_subcircuit_refs_do_not_disturb_other_prefixes(lib):
|
|
|
318
318
|
assert refs[part_key("bridge", "q_hi_a")] == "Q1"
|
|
319
319
|
assert refs[part_key("bridge", "r_gate_a")] == "R1"
|
|
320
320
|
assert refs["feedback"] == "U1"
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def test_expander_pads_join_the_expander_pin_net(lib):
|
|
324
|
+
script = generate_skidl(_design("keypad"), lib)
|
|
325
|
+
assert 'Net("kpd_hub_GP0")' in script
|
|
326
|
+
assert 'c_kpd_hub["P0"] += Net.fetch("kpd_hub_GP0")' in script
|
|
@@ -21,9 +21,9 @@ REPORT = {
|
|
|
21
21
|
"violations": [
|
|
22
22
|
{"type": "pin_not_driven", "severity": "error", "description": "Input Power pin not driven by any Output Power pins",
|
|
23
23
|
"items": [{"description": "Symbol U1 Pin 3 [VCC, Power input]"}], "excluded": False},
|
|
24
|
-
{"type": "
|
|
24
|
+
{"type": "global_label_dangling", "severity": "warning", "description": "Pin not connected",
|
|
25
25
|
"items": [{"description": "Symbol J2 Pin 4"}], "excluded": False},
|
|
26
|
-
{"type": "
|
|
26
|
+
{"type": "global_label_dangling", "severity": "warning", "description": "Pin not connected",
|
|
27
27
|
"items": [{"description": "Symbol J2 Pin 5"}], "excluded": True},
|
|
28
28
|
{"type": "endpoint_off_grid", "severity": "error", "description": "Pins of type Output and Output are connected",
|
|
29
29
|
"items": [{"description": "Symbol U1 Pin 7"}, {"description": "Symbol U2 Pin 2"}], "excluded": False},
|
|
@@ -51,7 +51,7 @@ def _writer_ok(monkeypatch):
|
|
|
51
51
|
def test_summarize_flattens_counts_and_drops_exclusions():
|
|
52
52
|
out = summarize(REPORT)
|
|
53
53
|
assert out["kicad_version"] == "8.0.9"
|
|
54
|
-
assert out["counts"] == {"
|
|
54
|
+
assert out["counts"] == {"global_label_dangling": 1, "endpoint_off_grid": 1, "pin_not_driven": 1}
|
|
55
55
|
assert out["errors"] == 2 and out["warnings"] == 1
|
|
56
56
|
assert out["violations"][0]["items"] == ["Symbol U1 Pin 3 [VCC, Power input]"]
|
|
57
57
|
assert summarize({}) == {"kicad_version": None, "violations": [], "counts": {}, "errors": 0, "warnings": 0}
|
|
@@ -103,11 +103,11 @@ def _load_script():
|
|
|
103
103
|
def test_gate_judges_against_the_baseline():
|
|
104
104
|
mod = _load_script()
|
|
105
105
|
allowed = mod.load_baseline()
|
|
106
|
-
assert {"pin_not_driven", "
|
|
106
|
+
assert {"pin_not_driven", "global_label_dangling"} <= set(allowed)
|
|
107
107
|
results = {"garage-motion": summarize(REPORT)}
|
|
108
108
|
failures, stale = mod.judge(results, allowed, check_stale=True)
|
|
109
109
|
assert failures == ["garage-motion: [error] endpoint_off_grid: Pins of type Output and Output are connected (Symbol U1 Pin 7)"]
|
|
110
|
-
assert stale == sorted(set(allowed) - {"pin_not_driven", "
|
|
110
|
+
assert stale == sorted(set(allowed) - {"pin_not_driven", "global_label_dangling"})
|
|
111
111
|
only_power = {"x": summarize({"sheets": [{"path": "/", "violations": [REPORT["sheets"][0]["violations"][0]]}]})}
|
|
112
112
|
failures, stale = mod.judge(only_power, allowed, check_stale=True)
|
|
113
113
|
assert failures == [] and stale == sorted(set(allowed) - {"pin_not_driven"})
|
|
@@ -119,3 +119,27 @@ def test_gate_checks_every_example_by_default():
|
|
|
119
119
|
paths = mod.example_paths([])
|
|
120
120
|
assert len(paths) == len(list(mod.EXAMPLES_DIR.glob("*.json"))) > 50
|
|
121
121
|
assert mod.example_paths(["garage-motion"]) == [mod.EXAMPLES_DIR / "garage-motion.json"]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_run_erc_checks_the_sheet_inside_a_project(monkeypatch, garage_motion_design, library, tmp_path):
|
|
125
|
+
"""kicad-cli reports every embedded symbol's library as not configured
|
|
126
|
+
unless the sheet sits in a project whose sym-lib-table names it."""
|
|
127
|
+
from wirestudio.kicad import erc as E
|
|
128
|
+
|
|
129
|
+
(tmp_path / "Sensor.kicad_sym").write_text("(kicad_symbol_lib)")
|
|
130
|
+
monkeypatch.setattr(E, "default_symbol_dirs", lambda: [tmp_path])
|
|
131
|
+
monkeypatch.setattr(R.shutil, "which", lambda exe: "/usr/bin/" + exe)
|
|
132
|
+
_writer_ok(monkeypatch)
|
|
133
|
+
monkeypatch.setattr(R, "generate_kicad_sch", lambda d, lib: '(kicad_sch (symbol (lib_id "Sensor:BME280")) (symbol (lib_id "power:GND")))')
|
|
134
|
+
seen = {}
|
|
135
|
+
|
|
136
|
+
def run(cmd, **kw):
|
|
137
|
+
sch = Path(cmd[-1])
|
|
138
|
+
seen["table"] = (sch.parent / "sym-lib-table").read_text()
|
|
139
|
+
seen["pro"] = sch.with_suffix(".kicad_pro").is_file()
|
|
140
|
+
return _fake_run(cmd, **kw)
|
|
141
|
+
monkeypatch.setattr(subprocess, "run", run)
|
|
142
|
+
run_erc(garage_motion_design, library)
|
|
143
|
+
assert seen["pro"]
|
|
144
|
+
assert f'(name "Sensor")(type "KiCad")(uri "{tmp_path / "Sensor.kicad_sym"}")' in seen["table"]
|
|
145
|
+
assert '"power"' not in seen["table"] # no such library here, so no row
|