-
Notifications
You must be signed in to change notification settings - Fork 0
Building Products
How finished devices — like the Ropener (curtain opener) and Glasscalibur (window opener) — are built on these boards without forking the firmware. Use the same pattern for your own device.
The rule: shared firmware exists once and is referenced, never copied (see docs/FIRMWARE-POLICY.md).
valar-core (shared firmware in this repo — Wi-Fi, API, OTA, TMC2209, buttons, tuning)
├── catalog boards: VAL3000 / VAL3100 / VAL3101 (thin wrappers in boards/)
├── Ropener (product repo — adds curtain cover, homing, gear math)
└── Glasscalibur (product repo — adds window cover; its own PCB)
Your product config lives in your own repo and includes the core by git tag:
packages:
valar_core:
url: https://github.com/Valar-Systems/valar-motion
ref: v0.2.0 # pin a tag — never float on main
files: [common/valar-core.yaml]
refresh: 1dPin ref: to a release tag so a core change never silently alters your build; bump it deliberately and re-test. Full working examples: docs/examples/ropener-val3100.yaml and docs/examples/glasscalibur.yaml.
Your config supplies the board identity and pin map via substitutions — chip (esp_board), the TMC2209 pins (pin_uart_tx/rx, pin_enn, pin_diag, pin_index), buttons (pin_btn1/2, pin_btn_wifi), names, and ota_repo/ota_asset/fw_version. Copy them from the matching wrapper in boards/ — see the pinout tables for what's on each board.
Custom PCB? A product on its own board (like Glasscalibur) still uses valar-core — supply your own pin map, and override the stepper hardware constants if they differ, e.g. stepper_rsense: "220 mOhm". Note that a different sense resistor changes the IRUN→amps mapping documented in the Web UI Reference (max motor current is 0.325 / (Rsense + 0.02) / √2 A RMS).
Wi-Fi + fallback hotspot + captive portal + Improv, the native Home Assistant API, both OTA paths with rollback safety, the grouped web UI, the TMC2209 stepper (id: driver), three debounced button entities, all the tuning controls and diagnostics from the Web UI Reference, and persisted settings.
A cover entity (curtain, window, blind) is usually the heart of it — a cover: that maps position to driver step targets. See the Ropener example for the minimal version.
Button behaviour: the core's buttons are deliberately bare — pins, debounce, and ids (btn1, btn2, btn3) with no actions attached (even Button 3's "Wi-Fi reset" name is only a label in the generic build). Your product attaches gestures with !extend:
binary_sensor:
- id: !extend btn1
on_click:
- cover.open: my_coverStall response: the core's default on_stall stops the motor and zeroes position. A product that homes with StallGuard (like the Ropener) overrides this hook with its own homing/position logic.
Scheduling: opt into the shared daily open/close schedule (fixed times or sunrise/sunset) by adding common/scheduling.yaml to your packages files: list. The mixin decides when; your product defines two scripts that decide how:
script:
- id: schedule_open
then: [ ... open your cover ... ]
- id: schedule_close
then: [ ... close your cover ... ]It adds its own "Schedule" group to the web UI (documented in the Web UI Reference).
OTA identity: set ota_repo to your product repo and give ota_asset a stable name — your field units will fetch <ota_asset>.ota.bin from your latest release forever. See OTA Updates.
- StallGuard homing (any board): drive toward the hard stop at moderate speed, let the stall zero the position, back off. Tune first — see StallGuard Tuning.
- Encoder homing (VAL3101): read the AS5600 for absolute position that survives power loss — no homing move needed within one shaft revolution, and it catches missed steps during travel. See the encoder section.