# Node manifest: the typed contract a consumer wires against. # # No source package is shared between node repositories, so this file and the # schema written out below are the only contract a consumer has. `dora validate` # checks a dataflow against it without running the node, so CI needs no gamepad. # # `joy` is deliberately **untyped**. dora 1.0 has no standard joystick type and # the reading is an Arrow struct; annotating it with a std type would claim a # contract neither side keeps. The struct is documented instead. apiVersion: 1 name: dora-joy namespace: liria description: Publishes a gamepad's axes and buttons, raw and unshaped categories: [sensor] keywords: [joystick, gamepad, joy, controller, input, sdl, pygame, axes, buttons] runtime: python entrypoint: dora-joy # `dora build --uv` / `dora run --uv` rewrites this to `uv pip install .` and # gives the node its own managed venv under `.dora/python-envs/`, so the # dependencies it sees at runtime are the ones installed here. build: pip install . dora: ">=1.0" inputs: tick: required: true description: >- Publishes the latest reading. Wire to dora/timer/millis/20. The node stops when this input closes. A consumer's own loop is usually slower; ticking faster than it costs almost nothing and keeps the reading it acts on fresh. type: std/core/v1/UInt64 outputs: joy: description: >- One reading, as the Arrow struct `{axes: list, buttons: list, present: bool}`. `axes` and `buttons` are raw, exactly as SDL reports them: no deadzone, no response curve, no rescaling. A resting trigger reads -1.0 rather than 0.0, and a consumer that treats an axis as a stick without checking will find that out here. Shaping is a decision about what a reading *means* and belongs to whoever is acting on it. `present` is false when no pad is open — unplugged, never plugged in, or taken away by SDL mid-run. **An absent pad is not a centred pad**, and a consumer driving anything that moves must treat it as a stop rather than as zeroed axes. Published on **every** tick, changed or not, so the stream doubles as a heartbeat: silence means this node died, and nothing else. A consumer that acts on the reading should stop on silence as well as on `present: false`. env: JOYSTICK_INDEX: type: int default: 0 description: Which pad to open, when more than one is plugged in. Defaults to 0. example: | - id: joy git: https://gitea.liria.cl/liria/dora-joy.git tag: v0.1.1 build: pip install . path: dora-joy inputs: tick: dora/timer/millis/20 outputs: - joy