wirestudio 0.42.2__tar.gz → 0.44.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.42.2/wirestudio.egg-info → wirestudio-0.44.0}/PKG-INFO +4 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/README.md +2 -2
- {wirestudio-0.42.2 → wirestudio-0.44.0}/pyproject.toml +1 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_codec.py +9 -9
- wirestudio-0.44.0/tests/test_community.py +191 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_firmware_gen.py +13 -13
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_hardware_gate.py +31 -16
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_lorawan_api.py +9 -10
- wirestudio-0.42.2/tests/test_lorawan_lower.py → wirestudio-0.44.0/tests/test_lorawan_sugar_migration.py +46 -52
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_client.py +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_migrate.py +23 -2
- wirestudio-0.44.0/tests/test_rf_ook.py +100 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_targets.py +9 -8
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_workbench_boot.py +42 -12
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_workbench_serial.py +3 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/__init__.py +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/tools.py +11 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/app.py +9 -23
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/circuitpython.py +2 -6
- wirestudio-0.44.0/wirestudio/api/community.py +268 -0
- wirestudio-0.44.0/wirestudio/api/design_input.py +26 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/micropython.py +2 -6
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/workbench.py +5 -2
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/access-panel.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/actuator-harness.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/air-quality-station.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/analog-node.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atom-echo.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atom-lite.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atom-matrix.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atoms3-lcd.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atoms3-lite.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atoms3-onboard.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atomu-onboard.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/atomu.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/attic-logger.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/awning-control.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/bench-io.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/bl0906-mainmeter.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/blind-stepper.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/bluemotion.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/bluesonoff.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/button-toggles-light.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/c3-supermini.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/c6-supermini.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/co2-display.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/desk-climate.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/desk-matrix.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/distance-sensor.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/encoder-drives-stepper.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/energy-monitor.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/esp32-audio.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/esp32cam-wrover.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/esp32cam.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/flow-meter.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/garage-motion.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/grill-probe.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/guarded-button-light.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/heltec-v4-gnss-tracker.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/keypad.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/kitchen-scale.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/led-display.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/lorawan-battery-uplink.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/motion-turns-on-light.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/motor-position.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/multi-temp.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/nextion-thermostat.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/oled-knob.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/oled.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/output-hub.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/parking-distance.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/presence-media-node.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/presence-rf.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/rc522.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/room-climate.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/rs485-energy.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/s3-devkitc.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/s3-supermini.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/securitypanel.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/smart-plug-v1.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/smart-plug.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/solder-fan.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/spi-io-node.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/subghz-rfid.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/t-beam-lorawan.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/t-beam.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/temp-above-turns-on-fan.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/temp-turns-on-fan.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/tft-touch.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/ttgo-lora32.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/tuya-smart-plug.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/usbc-led-strip.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/vent-damper.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/wasserpir.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/weather-station.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/wemosgps.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/wled-sensor-strip.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/xiao-grove-climate.json +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/yaml_gen.py +35 -4
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/jlcpcb/__init__.py +2 -2
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/erc.py +2 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/netlist.py +2 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/pcb.py +2 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/render.py +2 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/remote_receiver.yaml +26 -4
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sx127x.yaml +22 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/mcp/hardware.py +10 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/migrate.py +107 -24
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/model.py +13 -32
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/schema/design.schema.json +14 -25
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/__init__.py +0 -15
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/api.py +16 -6
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/codec.py +2 -5
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/compile.py +2 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/firmware_gen.py +1 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/tasmota.py +8 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/wled.py +6 -7
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/boot.py +46 -18
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/client.py +10 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/gate.py +7 -16
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/images.py +1 -1
- {wirestudio-0.42.2 → wirestudio-0.44.0/wirestudio.egg-info}/PKG-INFO +4 -3
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio.egg-info/SOURCES.txt +5 -2
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio.egg-info/requires.txt +1 -0
- wirestudio-0.42.2/wirestudio/targets/lorawan/lower.py +0 -111
- {wirestudio-0.42.2 → wirestudio-0.44.0}/LICENSE +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/LICENSES.md +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/setup.cfg +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_active_design.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_agent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_agent_eval.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_agent_stream.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_api.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_api_auth.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_api_route.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_ascii_gen.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_build_backend.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_check_upgrade.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_chirpstack.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_circuitpython.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_compatibility.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_compile.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_component_corpus.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_connectors.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_coverage_baseline.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_design_events.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_designs.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_designs_seed.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_display_intent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_electrical.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_enclosure.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_enclosure_gate.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_enclosure_search.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_errors.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_esphome_dashboard.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_esphome_support.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_fleet.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_footprints.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_framework_baseline.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_intent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_inventory.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_jlcpcb.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_erc.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_fab.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_import.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_pcb.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_render.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_route.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_kicad_sch.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_library_check.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_library_hbridge.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_library_rtttl.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_lorawan.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_auth.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_hardware.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_jobs.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_resources.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_mcp_server.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_meshtastic.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_micropython.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_micropython_registers.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_modules.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_pin_protocols.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_pin_solver.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_printwarden.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_python_blocks.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_recommend.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_register_code.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_schema.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_schema_doc.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_schematics_gate.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_security_cors.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_seed_onboard.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_serve.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_sqlite_store.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_sqlite_stores.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_strict.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_tasmota.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_tbeam_lorawan_example.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_tools_add_bus.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_tools_add_component.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_tools_solve_pins.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_uart_frames.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_validate.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_wled.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_workbench.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_workbench_images.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_workbench_provision.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_ws2812_method.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/tests/test_yaml_gen.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/agent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/eval.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/eval_tasks.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/agent/session.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/__main__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/auth.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/meshtastic.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/schemas.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/api/serve.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/csp/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/csp/compatibility.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/csp/pin_solver.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/designs/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/designs/active.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/designs/events.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/designs/seed.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/designs/store.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/enclosure/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/enclosure/openscad.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/enclosure/search.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/enclosure/yapp/LICENSE +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/enclosure/yapp/YAPPgenerator_v3.scad +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/errors.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/esphome_dashboard/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/examples/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/fleet/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/fleet/client.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/__main__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/ascii_gen.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/circuitpython_gen.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/display_intent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/generate/micropython_gen.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/intent.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/buy.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/check.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/csv_io.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/match.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/inventory/store.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/jlcpcb/__main__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/jlcpcb/check.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/jlcpcb/client.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/fab.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/generator.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/import.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/importer.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/pcbnew_bridge.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/route.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/schematic.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/kicad/symbol_parser.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp01_1m.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-c3-devkitm-1.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-c3-supermini.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-c6-supermini.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-devkitc-v4.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-s3-devkitc-1.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-s3-supermini.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32-wrover-cam.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp32cam-ai-thinker.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/esp8285-1m.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/heltec-wifi-lora32-v2.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/heltec-wifi-lora32-v3.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/heltec-wifi-lora32-v4.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atom-echo.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atom-matrix.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atom.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atoms3-lite.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atoms3.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/m5stack-atomu.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/nodemcu-32s.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/nodemcu-v2.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/seeed-xiao-esp32c3.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/ttgo-lora32-v1.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/ttgo-lora32-v2.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/ttgo-t-beam.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/boards/wemos-d1-mini.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/check.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/adc.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ads1115.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ads1115_channel.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/aht10.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/apa102.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/axp192.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/battery_adc.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bh1750.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bl0906.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bme280.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bme680.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bmp180.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/bmp280.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/cc1101.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x02.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x03.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x04.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x05.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x06.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x07.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_1x08.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/connector_2x05_idc.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/cse7766.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ct_clamp.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/dht.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ds18b20.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/esp32_camera.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/esp32_rmt_led_strip.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/gpio_input.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/gpio_output.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/grove-aht20.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hbridge.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hbridge_mosfet.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hc-sr04.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hc-sr501.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hlw8012.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/htu21d.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/hx711.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/i2s_microphone.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ili9xxx.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ina219.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/lcd_pcf8574.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ld2410.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ld2420.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/led_indicator.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/level_shifter.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/max31855.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/max7219.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/max98357a.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mcp23008.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mcp23017.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mcp23s17.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mcp3008.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mcp3008_channel.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mhz19.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/modbus.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mosfet_low_side_driver.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mpu6050.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/mpu6886.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/nextion.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/npn_low_side_driver.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pca9685.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pcf8574.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pmsx003.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pn532.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/potentiometer.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pulse_counter.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pwm_fan.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/pzemac.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rc522.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rcwl-0516.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rdm6300.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/remote_transmitter.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rf_bridge.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rotary_encoder.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/rtttl.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/scd4x.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sdm_meter.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/servo.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sgp4x.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sht3xd.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sht4x.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/sn74hc595.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ssd1306.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/st7789.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tm1637.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tm1638.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tsl2561.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tuya.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tuya_sensor.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/tuya_switch.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/uart_gps.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/uln2003.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/usb_c_power_in.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/veml7700.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/vl53l0x.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/voltage_divider.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/wiegand.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/ws2812b.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/components/xpt2046.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/electrical.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/modules/env-display.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/modules/fan-controller.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/modules/motion-light.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/modules/oled-knob-13.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/library/modules/relay-4ch.yaml +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/mcp/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/mcp/auth.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/mcp/jobs.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/mcp/server.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/printwarden/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/printwarden/client.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/recommend/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/recommend/recommender.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/schema/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/seed.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/sqlite_store.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/base.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/build_backend.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/esphome.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/__main__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/build_local.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/chirpstack.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/templates/main.cpp.j2 +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/templates/platformio.ini.j2 +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/lorawan/workbench_provision.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/targets/wled_build.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/validate.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/__init__.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio/workbench/serial.py +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio.egg-info/dependency_links.txt +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.0}/wirestudio.egg-info/entry_points.txt +0 -0
- {wirestudio-0.42.2 → wirestudio-0.44.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.44.0
|
|
4
4
|
Summary: Agent-driven IoT device design tool that produces ESPHome YAML.
|
|
5
5
|
Author: wirestudio contributors
|
|
6
6
|
License: MIT
|
|
@@ -19,6 +19,7 @@ Requires-Dist: httpx>=0.27
|
|
|
19
19
|
Requires-Dist: slowapi>=0.1.9
|
|
20
20
|
Requires-Dist: mcp<3,>=2.0
|
|
21
21
|
Requires-Dist: websockets>=12
|
|
22
|
+
Requires-Dist: pillow>=10
|
|
22
23
|
Provides-Extra: dev
|
|
23
24
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
24
25
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
@@ -151,8 +152,8 @@ Tiers, in priority order:
|
|
|
151
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 |
|
|
152
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 |
|
|
153
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)) |
|
|
154
|
-
| **
|
|
155
|
-
| **
|
|
155
|
+
| **Verified** | LoRaWAN target | build RadioLib + LoRaWAN_ESP32 firmware for US915 radio boards, flash over WebSerial, provision against ChirpStack | every radio board's firmware, and designs carrying every component's LoRaWAN block, build on every PR that touches the target and daily ([gate](.github/workflows/lorawan-firmware.yml)); nightly, the hardware gate asserts a TTGO T-Beam running this firmware is on the bench, logging and seen by ChirpStack within the hour ([gate](docs/workbench.md#the-gate)). Validated end-to-end on a T-Beam and Heltec WiFi LoRa 32 V2 and V3 against live ChirpStack 4.17. The nightly gate does not reflash: each reflash costs a rejoin and a DevNonce |
|
|
156
|
+
| **Verified** | LoRaWAN external-component path | emit ESPHome YAML referencing `lorawan-for-esphome`, provision keys into `secrets.yaml`, build through fleet | the bundled examples that use it pass `esphome config` with the component fetched, every PR ([gate](.github/workflows/esphome-config.yml)); nightly, the hardware gate asserts three boards running it (Heltec WiFi LoRa 32 V4 and V2, TTGO T3) are on the bench, logging and seen by ChirpStack within the hour ([gate](docs/workbench.md#the-gate)). Validated end-to-end on the V4 (SX1262 + external PA): joined, uplinked and decoded against live ChirpStack. Payloads over 11 bytes need the data rate asserted per uplink — US915 DR0 caps at 11 and ADR drives to DR0 on a strong link |
|
|
156
157
|
| **Works (hardware-validated)** | Remote workbench | flash a board on a bench slot, and run the whole LoRaWAN bring-up (flash → register → key push → verify) headlessly | wire format verified against a reference bench; flashing and LoRaWAN bring-up exercised repeatedly on a live Pi bench with an ESP32-S3. Transport tests in `tests/test_workbench.py` use wire-level fakes; no automated live-bench gate |
|
|
157
158
|
| **Verified** | Tasmota target | emit a Tasmota device template (`/tasmota/template`) mapping solved pins to Tasmota GPIO function ids | function ids + per-chip layouts sourced from `tasmota_template.h`; unit tests pin the Sonoff S31 template convention for the smart-plug example |
|
|
158
159
|
| **Verified** | WLED target | emit WLED's `cfg.json` (`/wled/cfg`): LED outputs, buttons, the IR receiver, the relay and the bus pins, the runtime tier a stock release image reads at boot; sensors are usermods, compiled into the pinned release through a generated `platformio_override.ini` (`/wled/compile`, streamed) with their settings in `cfg.json`'s `um` section | keys and type codes sourced from `wled00/const.h` and `cfg.cpp`; goldens pin four examples in `tests/test_wled.py`; the usermod example builds against WLED v0.15.2 on every PR that touches the target and nightly ([gate](.github/workflows/wled-firmware.yml)) |
|
|
@@ -118,8 +118,8 @@ Tiers, in priority order:
|
|
|
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)) |
|
|
121
|
-
| **
|
|
122
|
-
| **
|
|
121
|
+
| **Verified** | LoRaWAN target | build RadioLib + LoRaWAN_ESP32 firmware for US915 radio boards, flash over WebSerial, provision against ChirpStack | every radio board's firmware, and designs carrying every component's LoRaWAN block, build on every PR that touches the target and daily ([gate](.github/workflows/lorawan-firmware.yml)); nightly, the hardware gate asserts a TTGO T-Beam running this firmware is on the bench, logging and seen by ChirpStack within the hour ([gate](docs/workbench.md#the-gate)). Validated end-to-end on a T-Beam and Heltec WiFi LoRa 32 V2 and V3 against live ChirpStack 4.17. The nightly gate does not reflash: each reflash costs a rejoin and a DevNonce |
|
|
122
|
+
| **Verified** | LoRaWAN external-component path | emit ESPHome YAML referencing `lorawan-for-esphome`, provision keys into `secrets.yaml`, build through fleet | the bundled examples that use it pass `esphome config` with the component fetched, every PR ([gate](.github/workflows/esphome-config.yml)); nightly, the hardware gate asserts three boards running it (Heltec WiFi LoRa 32 V4 and V2, TTGO T3) are on the bench, logging and seen by ChirpStack within the hour ([gate](docs/workbench.md#the-gate)). Validated end-to-end on the V4 (SX1262 + external PA): joined, uplinked and decoded against live ChirpStack. Payloads over 11 bytes need the data rate asserted per uplink — US915 DR0 caps at 11 and ADR drives to DR0 on a strong link |
|
|
123
123
|
| **Works (hardware-validated)** | Remote workbench | flash a board on a bench slot, and run the whole LoRaWAN bring-up (flash → register → key push → verify) headlessly | wire format verified against a reference bench; flashing and LoRaWAN bring-up exercised repeatedly on a live Pi bench with an ESP32-S3. Transport tests in `tests/test_workbench.py` use wire-level fakes; no automated live-bench gate |
|
|
124
124
|
| **Verified** | Tasmota target | emit a Tasmota device template (`/tasmota/template`) mapping solved pins to Tasmota GPIO function ids | function ids + per-chip layouts sourced from `tasmota_template.h`; unit tests pin the Sonoff S31 template convention for the smart-plug example |
|
|
125
125
|
| **Verified** | WLED target | emit WLED's `cfg.json` (`/wled/cfg`): LED outputs, buttons, the IR receiver, the relay and the bus pins, the runtime tier a stock release image reads at boot; sensors are usermods, compiled into the pinned release through a generated `platformio_override.ini` (`/wled/compile`, streamed) with their settings in `cfg.json`'s `um` section | keys and type codes sourced from `wled00/const.h` and `cfg.cpp`; goldens pin four examples in `tests/test_wled.py`; the usermod example builds against WLED v0.15.2 on every PR that touches the target and nightly ([gate](.github/workflows/wled-firmware.yml)) |
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from wirestudio.library import default_library
|
|
4
|
+
from wirestudio.migrate import migrate
|
|
4
5
|
from wirestudio.model import Design
|
|
5
6
|
from wirestudio.targets.lorawan import codec
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def _design(board_id: str, **lorawan) -> Design:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
name
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
10
|
+
"""`lorawan` may carry the retired gps / dht22 / oled blocks, which the
|
|
11
|
+
migrator turns into components."""
|
|
12
|
+
return Design.model_validate(migrate({
|
|
13
|
+
"schema_version": "0.3", "id": "d", "name": "D", "target": "lorawan",
|
|
14
|
+
"lorawan": lorawan,
|
|
15
|
+
"board": {"library_id": board_id, "mcu": "esp32"},
|
|
16
|
+
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
17
|
+
}).document)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def test_builtin_only_layout():
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Community designs: browse and import go through the catalogue's index
|
|
2
|
+
only, and share refuses what the catalogue's CI would."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import zipfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
import yaml
|
|
12
|
+
from fastapi.testclient import TestClient
|
|
13
|
+
|
|
14
|
+
from wirestudio.api.app import create_app
|
|
15
|
+
|
|
16
|
+
REPO = "moellere/wirestudio-designs"
|
|
17
|
+
RAW = f"https://raw.githubusercontent.com/{REPO}"
|
|
18
|
+
EXAMPLE = json.loads((Path(__file__).resolve().parent.parent / "wirestudio" / "examples"
|
|
19
|
+
/ "garage-motion.json").read_text())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _entry(slug, **over):
|
|
23
|
+
return {"slug": slug, "name": "Porch motion", "author": "octocat", "license": "CC-BY-4.0",
|
|
24
|
+
"design_url": f"{RAW}/main/designs/{slug}/design.json", **over}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def studio(monkeypatch):
|
|
29
|
+
monkeypatch.delenv("WIRESTUDIO_DESIGNS_REPO", raising=False)
|
|
30
|
+
files: dict[str, bytes] = {}
|
|
31
|
+
fetched: list[str] = []
|
|
32
|
+
|
|
33
|
+
async def fetch(url):
|
|
34
|
+
fetched.append(url)
|
|
35
|
+
if url not in files:
|
|
36
|
+
raise RuntimeError(f"404 {url}")
|
|
37
|
+
return files[url]
|
|
38
|
+
|
|
39
|
+
def publish(*entries, designs=None):
|
|
40
|
+
files[f"{RAW}/index/index.json"] = json.dumps(
|
|
41
|
+
{"version": 1, "generated": "2026-10-23T00:00:00+00:00", "designs": list(entries)}).encode()
|
|
42
|
+
for slug, doc in (designs or {}).items():
|
|
43
|
+
files[f"{RAW}/main/designs/{slug}/design.json"] = json.dumps(doc).encode()
|
|
44
|
+
|
|
45
|
+
app = TestClient(create_app(community_fetch=fetch))
|
|
46
|
+
return app, publish, fetched
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_status_names_the_catalogue(studio):
|
|
50
|
+
app, _, _ = studio
|
|
51
|
+
s = app.get("/community/status").json()
|
|
52
|
+
assert s["enabled"] and s["repo"] == REPO
|
|
53
|
+
assert s["upload_url"] == f"https://github.com/{REPO}/upload/main/designs"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_empty_repo_setting_turns_it_off(studio, monkeypatch):
|
|
57
|
+
app, _, _ = studio
|
|
58
|
+
monkeypatch.setenv("WIRESTUDIO_DESIGNS_REPO", "")
|
|
59
|
+
assert app.get("/community/status").json() == {"enabled": False}
|
|
60
|
+
assert app.get("/community/designs").status_code == 503
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_browse_lists_the_index_and_caches_it(studio):
|
|
64
|
+
app, publish, fetched = studio
|
|
65
|
+
publish(_entry("porch-motion"))
|
|
66
|
+
assert [d["slug"] for d in app.get("/community/designs").json()["designs"]] == ["porch-motion"]
|
|
67
|
+
app.get("/community/designs")
|
|
68
|
+
assert fetched.count(f"{RAW}/index/index.json") == 1
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_an_unreachable_index_is_a_502(studio):
|
|
72
|
+
app, _, _ = studio
|
|
73
|
+
r = app.get("/community/designs")
|
|
74
|
+
assert r.status_code == 502 and "index unavailable" in r.json()["detail"]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_import_stamps_the_origin(studio):
|
|
78
|
+
app, publish, _ = studio
|
|
79
|
+
publish(_entry("porch-motion"), designs={"porch-motion": {**EXAMPLE, "id": "porch-motion"}})
|
|
80
|
+
body = app.get("/community/designs/porch-motion").json()
|
|
81
|
+
assert body["design"]["origin"] == {"catalogue": REPO, "slug": "porch-motion",
|
|
82
|
+
"author": "octocat", "license": "CC-BY-4.0"}
|
|
83
|
+
assert app.post("/design/validate", json=body["design"]).status_code == 200
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_import_only_follows_urls_inside_the_catalogue(studio):
|
|
87
|
+
app, publish, fetched = studio
|
|
88
|
+
publish(_entry("evil", design_url="http://169.254.169.254/latest/meta-data"))
|
|
89
|
+
assert app.get("/community/designs/evil").status_code == 502
|
|
90
|
+
assert not any("169.254" in u for u in fetched)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_import_of_an_unknown_slug_is_a_404(studio):
|
|
94
|
+
app, publish, _ = studio
|
|
95
|
+
publish()
|
|
96
|
+
assert app.get("/community/designs/nope").status_code == 404
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _share(app, **over):
|
|
100
|
+
body = {"design": {**EXAMPLE, "id": "Porch Motion v2"}, "author": "@octocat", "tags": ["motion"], **over}
|
|
101
|
+
return app.post("/community/share", json=body)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_share_packs_the_folder_the_catalogue_expects(studio):
|
|
105
|
+
app, _, _ = studio
|
|
106
|
+
r = _share(app, notes="Built on a DevKitC.")
|
|
107
|
+
assert r.status_code == 200 and r.headers["content-type"] == "application/zip"
|
|
108
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
109
|
+
assert sorted(z.namelist()) == ["porch-motion-v2/README.md", "porch-motion-v2/design.json",
|
|
110
|
+
"porch-motion-v2/meta.yaml"]
|
|
111
|
+
assert json.loads(z.read("porch-motion-v2/design.json"))["id"] == "porch-motion-v2"
|
|
112
|
+
assert yaml.safe_load(z.read("porch-motion-v2/meta.yaml")) == {"author": "octocat", "tags": ["motion"]}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_share_credits_the_design_it_was_imported_from(studio):
|
|
116
|
+
app, _, _ = studio
|
|
117
|
+
origin = {"catalogue": REPO, "slug": "porch-motion", "author": "someone", "license": "CC-BY-4.0"}
|
|
118
|
+
r = _share(app, design={**EXAMPLE, "id": "porch-motion-v2", "origin": origin})
|
|
119
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
120
|
+
assert yaml.safe_load(z.read("porch-motion-v2/meta.yaml"))["derived_from"] == "porch-motion"
|
|
121
|
+
assert "origin" not in json.loads(z.read("porch-motion-v2/design.json"))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_resharing_your_own_design_does_not_derive_from_itself(studio):
|
|
125
|
+
app, _, _ = studio
|
|
126
|
+
origin = {"catalogue": REPO, "slug": "porch-motion", "author": "octocat", "license": "CC-BY-4.0"}
|
|
127
|
+
r = _share(app, design={**EXAMPLE, "id": "porch-motion", "origin": origin})
|
|
128
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
129
|
+
assert "derived_from" not in yaml.safe_load(z.read("porch-motion/meta.yaml"))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_share_refuses_a_literal_credential(studio):
|
|
133
|
+
app, _, _ = studio
|
|
134
|
+
design = json.loads(json.dumps(EXAMPLE))
|
|
135
|
+
design["fleet"]["secrets_ref"]["wifi_password"] = "hunter22"
|
|
136
|
+
r = _share(app, design=design)
|
|
137
|
+
assert r.status_code == 422 and "fleet.secrets_ref.wifi_password" in r.json()["detail"]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@pytest.mark.parametrize("over, match", [
|
|
141
|
+
({"author": "not a handle"}, "GitHub handle"),
|
|
142
|
+
({"tags": ["Has Space"]}, "tags"),
|
|
143
|
+
({"slug": "Bad_Slug"}, "not a usable name"),
|
|
144
|
+
])
|
|
145
|
+
def test_share_refuses_what_the_catalogue_would(studio, over, match):
|
|
146
|
+
app, _, _ = studio
|
|
147
|
+
r = _share(app, **over)
|
|
148
|
+
assert r.status_code == 422 and match in r.json()["detail"]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _jpeg(size=(3000, 2000), exif=True, fmt="JPEG") -> str:
|
|
152
|
+
import base64
|
|
153
|
+
|
|
154
|
+
from PIL import Image
|
|
155
|
+
|
|
156
|
+
img = Image.new("RGB", size, (200, 80, 40))
|
|
157
|
+
kwargs = {}
|
|
158
|
+
if exif:
|
|
159
|
+
e = Image.Exif()
|
|
160
|
+
e[0x010F] = "PhoneMaker"
|
|
161
|
+
e[0x0112] = 6 # Orientation: rotate 90 CW on display
|
|
162
|
+
kwargs["exif"] = e
|
|
163
|
+
buf = io.BytesIO()
|
|
164
|
+
img.save(buf, fmt, **kwargs)
|
|
165
|
+
return base64.b64encode(buf.getvalue()).decode()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_share_re_encodes_photos_without_metadata(studio):
|
|
169
|
+
from PIL import Image
|
|
170
|
+
|
|
171
|
+
app, _, _ = studio
|
|
172
|
+
r = _share(app, photos=[{"name": "IMG_0042.JPG", "data": _jpeg()},
|
|
173
|
+
{"name": "IMG_0042.JPG", "data": _jpeg(fmt="PNG", exif=False)}])
|
|
174
|
+
assert r.status_code == 200, r.text
|
|
175
|
+
z = zipfile.ZipFile(io.BytesIO(r.content))
|
|
176
|
+
names = sorted(n for n in z.namelist() if "/photos/" in n)
|
|
177
|
+
assert names == ["porch-motion-v2/photos/img-0042-2.jpg", "porch-motion-v2/photos/img-0042.jpg"]
|
|
178
|
+
with Image.open(io.BytesIO(z.read("porch-motion-v2/photos/img-0042.jpg"))) as img:
|
|
179
|
+
assert img.format == "JPEG" and not img.getexif()
|
|
180
|
+
assert img.size == (1365, 2048), "the EXIF rotation is applied before EXIF is dropped"
|
|
181
|
+
assert all(z.getinfo(n).file_size <= 1024 * 1024 for n in names)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_share_refuses_what_is_not_a_photo(studio):
|
|
185
|
+
import base64
|
|
186
|
+
|
|
187
|
+
app, _, _ = studio
|
|
188
|
+
r = _share(app, photos=[{"name": "notes.txt", "data": base64.b64encode(b"hello").decode()}])
|
|
189
|
+
assert r.status_code == 422 and "not a readable image" in r.json()["detail"]
|
|
190
|
+
r = _share(app, photos=[{"name": f"p{i}.jpg", "data": _jpeg((10, 10))} for i in range(5)])
|
|
191
|
+
assert r.status_code == 422 and "at most 4" in r.json()["detail"]
|
|
@@ -5,28 +5,28 @@ from pathlib import Path
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
from wirestudio.library import default_library
|
|
8
|
+
from wirestudio.migrate import migrate
|
|
8
9
|
from wirestudio.model import Design
|
|
9
10
|
from wirestudio.targets.lorawan.firmware_gen import generate_firmware, write_firmware
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def _design(board_id: str, **lorawan) -> Design:
|
|
13
|
-
|
|
14
|
+
"""A 0.3 design; `lorawan` may carry the retired gps / dht22 / oled
|
|
15
|
+
blocks, which the migrator turns into components."""
|
|
16
|
+
raw = {
|
|
17
|
+
"schema_version": "0.3", "id": "dev1", "name": "Dev 1",
|
|
18
|
+
"board": {"library_id": board_id, "mcu": "esp32"},
|
|
19
|
+
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
20
|
+
}
|
|
14
21
|
if lorawan:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return Design(
|
|
18
|
-
schema_version="0.1",
|
|
19
|
-
id="dev1",
|
|
20
|
-
name="Dev 1",
|
|
21
|
-
board={"library_id": board_id, "mcu": "esp32"},
|
|
22
|
-
power={"supply": "usb", "rail_voltage_v": 3.3},
|
|
23
|
-
**kw,
|
|
24
|
-
)
|
|
22
|
+
raw["target"] = "lorawan"
|
|
23
|
+
raw["lorawan"] = lorawan
|
|
24
|
+
return Design.model_validate(migrate(raw).document)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def _design_with(*library_ids: str) -> Design:
|
|
28
28
|
return Design.model_validate({
|
|
29
|
-
"schema_version": "0
|
|
29
|
+
"schema_version": "1.0", "id": "dev1", "name": "Dev 1", "target": "lorawan", "lorawan": {},
|
|
30
30
|
"board": {"library_id": "ttgo-lora32-v2", "mcu": "esp32"},
|
|
31
31
|
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
32
32
|
"components": [{"id": cid, "library_id": cid, "label": cid, "params": {}} for cid in library_ids],
|
|
@@ -180,7 +180,7 @@ def test_lorawan_matches_golden(lib):
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def test_synthesized_dht_emits_its_configured_pin(lib):
|
|
183
|
-
"""A DHT
|
|
183
|
+
"""A DHT from a retired `lorawan.dht22` block has no wiring, so the render
|
|
184
184
|
context sets `pin` to None. Jinja's `default()` only fires on *undefined*,
|
|
185
185
|
so the old template fell through to a literal "None" in the C++."""
|
|
186
186
|
src = generate_firmware(
|
|
@@ -258,13 +258,25 @@ def test_an_off_bench_device_without_a_dev_eui_is_rejected(tmp_path):
|
|
|
258
258
|
|
|
259
259
|
|
|
260
260
|
class FlashBench(FakeBench):
|
|
261
|
-
"""Records writes
|
|
262
|
-
|
|
261
|
+
"""Records writes. Like the real bench, the boot that follows a write
|
|
262
|
+
goes unrecorded; a reset afterwards prints what `boot_lines` scripts
|
|
263
|
+
for the framework written last."""
|
|
263
264
|
|
|
264
265
|
def __init__(self, slots, boot_lines, fail_write=False):
|
|
265
266
|
super().__init__(slots)
|
|
266
267
|
self.boot_lines, self.fail_write = boot_lines, fail_write
|
|
267
268
|
self.writes: list[dict] = []
|
|
269
|
+
self.resets: list[str] = []
|
|
270
|
+
self._written: dict[str, str] = {}
|
|
271
|
+
|
|
272
|
+
async def slot(self, label):
|
|
273
|
+
return next(s for s in self._slots if s.label == label)
|
|
274
|
+
|
|
275
|
+
async def reset(self, slot):
|
|
276
|
+
self.resets.append(slot)
|
|
277
|
+
self._lines[slot] = [{"ts": time.time(), "text": t}
|
|
278
|
+
for t in self.boot_lines.get(self._written.get(slot, ""), [])]
|
|
279
|
+
return {"ok": True}
|
|
268
280
|
|
|
269
281
|
async def flash(self, slot, images, chip="esp32", erase=False, baud=921600):
|
|
270
282
|
from wirestudio.workbench import WorkbenchUnavailable
|
|
@@ -272,8 +284,8 @@ class FlashBench(FakeBench):
|
|
|
272
284
|
if self.fail_write:
|
|
273
285
|
raise WorkbenchUnavailable("flash failed: A fatal error occurred: No serial data received.")
|
|
274
286
|
self.writes.append({"slot": slot, "images": images, "chip": chip, "erase": erase})
|
|
275
|
-
self.
|
|
276
|
-
|
|
287
|
+
self._written[slot] = images[0][1].decode()
|
|
288
|
+
self._lines[slot] = []
|
|
277
289
|
yield {"type": "log", "data": "Hash of data verified."}
|
|
278
290
|
yield {"type": "done", "ok": True, "slot": slot, "returncode": 0}
|
|
279
291
|
|
|
@@ -298,7 +310,6 @@ def fake_images(monkeypatch):
|
|
|
298
310
|
|
|
299
311
|
|
|
300
312
|
FLASH_CONFIG = {
|
|
301
|
-
"boot_capture_s": 0,
|
|
302
313
|
"devices": [{"slot": "SLOT2", "name": "spare S3", "board": "esp32-s3-devkitc-1",
|
|
303
314
|
"flash": ["esphome", "micropython", "circuitpython"]}],
|
|
304
315
|
}
|
|
@@ -307,7 +318,7 @@ BOOTS = {
|
|
|
307
318
|
"esphome": ["[I][app:117]: setup() finished successfully!"],
|
|
308
319
|
"micropython": ["MicroPython v1.26.1 on 2025-09-11; Generic ESP32S3 module with ESP32S3"],
|
|
309
320
|
"circuitpython": ["Adafruit CircuitPython 10.3.1 on 2026-09-14; Espressif ESP32 DevKitc V4 WROOM-32E with ESP32"],
|
|
310
|
-
"meshtastic": ["INFO | ??:??:?? 0
|
|
321
|
+
"meshtastic": ["INFO | ??:??:?? 0 Booted, wake cause 0 (boot count 1), reset_reason=reset"],
|
|
311
322
|
}
|
|
312
323
|
|
|
313
324
|
|
|
@@ -331,18 +342,22 @@ async def test_a_flash_device_skips_the_serial_stage(fake_images):
|
|
|
331
342
|
assert not [r for r in report.rows if r[0] == "serial"]
|
|
332
343
|
|
|
333
344
|
|
|
334
|
-
async def
|
|
335
|
-
"""
|
|
336
|
-
|
|
345
|
+
async def test_boot_is_read_from_a_reset_after_each_write(fake_images):
|
|
346
|
+
"""covbench, 2026-10-23: the boot esptool triggers is never recorded,
|
|
347
|
+
so without a reset ESPHome failed on a healthy board."""
|
|
337
348
|
report = gate.Report()
|
|
338
|
-
bench = FlashBench([FakeSlot("
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
await gate.run(cfg, report, client=bench)
|
|
349
|
+
bench = FlashBench([FakeSlot("SLOT2", chip="esp32s3")], BOOTS)
|
|
350
|
+
await gate.run(FLASH_CONFIG, report, client=bench)
|
|
351
|
+
assert bench.resets == ["SLOT2"] * 3
|
|
342
352
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
353
|
+
|
|
354
|
+
async def test_meshtastic_boots_on_its_marker(fake_images):
|
|
355
|
+
report = gate.Report()
|
|
356
|
+
bench = FlashBench([FakeSlot("SLOT3", chip="esp32")], BOOTS)
|
|
357
|
+
cfg = {"devices": [
|
|
358
|
+
{"slot": "SLOT3", "board": "ttgo-lora32-v2", "flash": ["meshtastic"]}]}
|
|
359
|
+
await gate.run(cfg, report, client=bench)
|
|
360
|
+
assert ("boot", "SLOT3/meshtastic", True) in [r[:3] for r in report.rows]
|
|
346
361
|
|
|
347
362
|
|
|
348
363
|
async def test_circuitpython_boots_on_its_banner(fake_images):
|
|
@@ -57,15 +57,14 @@ def client() -> TestClient:
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
def _design(board_id: str, **lorawan) -> dict:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
name
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
).model_dump(mode="json", exclude_none=True)
|
|
60
|
+
"""A 0.3 request body; `lorawan` may carry the retired gps / dht22 / oled
|
|
61
|
+
blocks, which the endpoints migrate into components."""
|
|
62
|
+
return {
|
|
63
|
+
"schema_version": "0.3", "id": "d", "name": "D", "target": "lorawan",
|
|
64
|
+
"lorawan": lorawan,
|
|
65
|
+
"board": {"library_id": board_id, "mcu": "esp32"},
|
|
66
|
+
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
67
|
+
}
|
|
69
68
|
|
|
70
69
|
|
|
71
70
|
def _seed_cache(tmp_path, design: dict, *, bin_bytes=b"FAKEBIN", log="cached log") -> str:
|
|
@@ -305,7 +304,7 @@ def test_provision_esphome_uses_esphome_profile_naming(client, monkeypatch):
|
|
|
305
304
|
assert fake.provisioned["device_profile_name"] == "wirestudio-esphome-us915-sub2"
|
|
306
305
|
# Codec is None on the new path -- the decoder is generated from
|
|
307
306
|
# design.lorawan.payload by a follow-up endpoint, not by the standalone
|
|
308
|
-
# codec.py (which
|
|
307
|
+
# codec.py (which packs the standalone path's components).
|
|
309
308
|
assert fake.provisioned["codec"] is None
|
|
310
309
|
|
|
311
310
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
"""`lorawan.gps` / `dht22` / `oled`
|
|
2
|
-
few connections.
|
|
3
|
-
|
|
4
|
-
payload and profile name."""
|
|
1
|
+
"""`lorawan.gps` / `dht22` / `oled` were sugar for a component, a bus and a
|
|
2
|
+
few connections. The 1.0 model drops them; the migrator rewrites a document
|
|
3
|
+
that still carries them into the components they stood for, so a design
|
|
4
|
+
written either way produces the same firmware, payload and profile name."""
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
|
+
from pydantic import ValidationError
|
|
8
9
|
|
|
9
10
|
from wirestudio.library import default_library
|
|
11
|
+
from wirestudio.migrate import CURRENT_SCHEMA_VERSION, _lower_lorawan_sugar, migrate
|
|
10
12
|
from wirestudio.model import Design
|
|
11
13
|
from wirestudio.targets import get_target
|
|
12
14
|
from wirestudio.targets.lorawan.codec import fields_for, generate_codec, profile_name
|
|
13
15
|
from wirestudio.targets.lorawan.firmware_gen import generate_firmware
|
|
14
|
-
from wirestudio.targets.lorawan.lower import lower_lorawan_sugar, sugar_in_use
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@pytest.fixture
|
|
@@ -19,14 +20,18 @@ def lib():
|
|
|
19
20
|
return default_library()
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def
|
|
23
|
-
return
|
|
24
|
-
"schema_version":
|
|
23
|
+
def _raw(board_id: str, *, lorawan: dict | None = None, version: str = "0.3", **extra) -> dict:
|
|
24
|
+
return {
|
|
25
|
+
"schema_version": version, "id": "d", "name": "D", "target": "lorawan",
|
|
25
26
|
"lorawan": lorawan if lorawan is not None else {},
|
|
26
27
|
"board": {"library_id": board_id, "mcu": "esp32"},
|
|
27
28
|
"power": {"supply": "usb", "rail_voltage_v": 3.3},
|
|
28
29
|
**extra,
|
|
29
|
-
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _design(board_id: str, **kw) -> Design:
|
|
34
|
+
return Design.model_validate(migrate(_raw(board_id, **kw)).document)
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
SUGAR = {"gps": {"rx_pin": "GPIO23", "tx_pin": "GPIO17"}, "dht22": {"pin": "GPIO13"}, "oled": {}}
|
|
@@ -59,12 +64,13 @@ EXPLICIT = {
|
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
assert
|
|
67
|
+
def test_the_blocks_become_the_explicit_design():
|
|
68
|
+
result = migrate(_raw("heltec-wifi-lora32-v3", lorawan=SUGAR))
|
|
69
|
+
assert result.applied == ["retire_lorawan_sugar"]
|
|
70
|
+
assert result.to_version == CURRENT_SCHEMA_VERSION == "1.0"
|
|
71
|
+
doc = result.document
|
|
72
|
+
assert {k: doc[k] for k in ("components", "buses", "connections")} == EXPLICIT
|
|
73
|
+
assert doc["lorawan"] == {}
|
|
68
74
|
|
|
69
75
|
|
|
70
76
|
def test_sugar_and_explicit_designs_generate_the_same_artifacts(lib):
|
|
@@ -78,12 +84,22 @@ def test_sugar_and_explicit_designs_generate_the_same_artifacts(lib):
|
|
|
78
84
|
assert {"lat", "lon", "temp_c", "humidity", "batt_mv"} <= set(names)
|
|
79
85
|
|
|
80
86
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
assert
|
|
85
|
-
assert
|
|
86
|
-
assert
|
|
87
|
+
def test_migration_leaves_the_input_alone_and_is_idempotent(lib):
|
|
88
|
+
raw = _raw("heltec-wifi-lora32-v3", lorawan=SUGAR)
|
|
89
|
+
doc = migrate(raw).document
|
|
90
|
+
assert raw["lorawan"]["gps"] and "components" not in raw
|
|
91
|
+
assert _lower_lorawan_sugar(doc, lib) is doc
|
|
92
|
+
assert migrate(doc).document is doc
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_a_0_3_design_without_the_blocks_only_changes_its_version():
|
|
96
|
+
raw = _raw("heltec-wifi-lora32-v3", lorawan={"region": "EU868"})
|
|
97
|
+
assert migrate(raw).document == {**raw, "schema_version": "1.0"}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_the_1_0_model_refuses_the_blocks():
|
|
101
|
+
with pytest.raises(ValidationError, match="gps"):
|
|
102
|
+
Design.model_validate(_raw("heltec-wifi-lora32-v3", lorawan=SUGAR, version="1.0"))
|
|
87
103
|
|
|
88
104
|
|
|
89
105
|
def test_a_block_whose_component_is_already_placed_is_dropped(lib):
|
|
@@ -93,54 +109,32 @@ def test_a_block_whose_component_is_already_placed_is_dropped(lib):
|
|
|
93
109
|
connections=[{"component_id": "env", "pin_role": "DATA",
|
|
94
110
|
"target": {"kind": "gpio", "pin": "GPIO4"}}],
|
|
95
111
|
)
|
|
96
|
-
|
|
97
|
-
assert [c.id for c in lowered.components] == ["env"]
|
|
98
|
-
assert lowered.lorawan.dht22 is None
|
|
112
|
+
assert [c.id for c in design.components] == ["env"]
|
|
99
113
|
assert "DHT dht(4, DHT22);" in generate_firmware(design, lib)["src/main.cpp"]
|
|
100
114
|
|
|
101
115
|
|
|
102
|
-
def test_ids_do_not_collide_with_what_the_design_has(
|
|
116
|
+
def test_ids_do_not_collide_with_what_the_design_has():
|
|
103
117
|
design = _design(
|
|
104
118
|
"heltec-wifi-lora32-v3", lorawan={"gps": {"rx_pin": "GPIO23", "tx_pin": "GPIO17"}},
|
|
105
119
|
components=[{"id": "gps", "library_id": "gpio_input", "label": "Button", "params": {}}],
|
|
106
120
|
buses=[{"id": "gps_uart", "type": "uart", "rx": "GPIO3", "tx": "GPIO1"}],
|
|
107
121
|
)
|
|
108
|
-
|
|
109
|
-
gps = next(c for c in lowered.components if c.library_id == "uart_gps")
|
|
122
|
+
gps = next(c for c in design.components if c.library_id == "uart_gps")
|
|
110
123
|
assert gps.id == "gps2"
|
|
111
|
-
assert {b.id for b in
|
|
124
|
+
assert {b.id for b in design.buses} == {"gps_uart", "gps_uart2"}
|
|
112
125
|
|
|
113
126
|
|
|
114
127
|
def test_onboard_gps_stays_synthesized_without_sugar(lib):
|
|
115
128
|
"""The T-Beam's GPS comes from the board file, not from a block; the
|
|
116
129
|
golden firmware for it does not move."""
|
|
117
130
|
design = _design("ttgo-t-beam")
|
|
118
|
-
assert
|
|
131
|
+
assert design.components == []
|
|
119
132
|
assert "GPSSerial.begin(9600, SERIAL_8N1, 34, 12)" in generate_firmware(design, lib)["src/main.cpp"]
|
|
120
133
|
|
|
121
134
|
|
|
122
|
-
def test_oled_disabled_is_no_oled(
|
|
123
|
-
|
|
124
|
-
assert lower_lorawan_sugar(design, lib).components == []
|
|
125
|
-
|
|
135
|
+
def test_oled_disabled_is_no_oled():
|
|
136
|
+
assert _design("heltec-wifi-lora32-v3", lorawan={"oled": {"enabled": False}}).components == []
|
|
126
137
|
|
|
127
|
-
def test_validate_names_each_lowered_block(lib):
|
|
128
|
-
warnings = get_target("lorawan").validate(_design("heltec-wifi-lora32-v3", lorawan=SUGAR), lib)
|
|
129
|
-
lowered = [w for w in warnings if w.code == "lorawan_sugar_lowered"]
|
|
130
|
-
assert [w.level for w in lowered] == ["info"] * 3
|
|
131
|
-
assert all(f"lorawan.{f}" in w.text for f, w in zip(("gps", "dht22", "oled"), lowered))
|
|
132
|
-
assert get_target("lorawan").validate(_design("heltec-wifi-lora32-v3", **EXPLICIT), lib) == []
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
on_console = {"gps": {"rx_pin": "GPIO3", "tx_pin": "GPIO1"}}
|
|
137
|
-
codes = {w.code for w in get_target("lorawan").validate(
|
|
138
|
-
_design("heltec-wifi-lora32-v2", lorawan=on_console), lib)}
|
|
139
|
-
assert "lorawan_gps_on_console_uart" in codes
|
|
140
|
-
explicit = _design(
|
|
141
|
-
"heltec-wifi-lora32-v2",
|
|
142
|
-
components=[{"id": "gnss", "library_id": "uart_gps", "label": "GNSS", "params": {}}],
|
|
143
|
-
buses=[{"id": "u", "type": "uart", "rx": "GPIO3", "tx": "GPIO1"}],
|
|
144
|
-
connections=[{"component_id": "gnss", "pin_role": "TX", "target": {"kind": "bus", "bus_id": "u"}}],
|
|
145
|
-
)
|
|
146
|
-
assert "lorawan_gps_on_console_uart" in {w.code for w in get_target("lorawan").validate(explicit, lib)}
|
|
139
|
+
def test_the_migrated_design_validates_clean(lib):
|
|
140
|
+
assert get_target("lorawan").validate(_design("heltec-wifi-lora32-v3", lorawan=SUGAR), lib) == []
|