Files
parking_solution/wiki/entities/dingtian-relay.md
T
julian 1b55e2034d Dingtian relay driver — resolves the ticket-first entry blocker
The Dingtian board's inputs are independent of its relays (configurable), so a
button on an input can report to the host WITHOUT auto-firing a relay — solving
the access-controller-button-flow blocker the UHPPOTE/ZKTeco couldn't.

packages/devices:
- access-dingtian.ts: `dingtian` access driver implementing AccessControlDevice
  (relay pulse/latch via UDP string protocol :60001), InputDevice (read inputs +
  poll-based press/release events, active-LOW), and the new PreconditionDevice.
- PreconditionDevice capability on the interface: a device can report config it
  requires for parking and optionally fix it. Dingtian checks input_link_relay
  via the HTTP config API and can disable it.
- httpPort config field — the web/config API port is separate from UDP control
  (this unit uses 8080, not the default 80).
- Register dingtian; export driver objects from the package.

Verified on real hardware (DT-R004 @ 10.0.10.172): status read, relay pulse,
input events; disabled input_link_relay via the driver, then confirmed pressing
inputs fires NO relay (0000) — host-in-the-loop entry works.

Config-write gotcha recorded: config_set.cgi requires "command":"setconfig"
injected after "status" (GET omits it) or the POST silently no-ops.

apps/server/scripts/dingtian-test.mjs: status / watch / pulse hardware test.
wiki: dingtian-relay verified; button-flow marked RESOLVED; index + log.
2026-06-14 14:15:00 +02:00

72 lines
4.1 KiB
Markdown

---
type: entity
tags: [parking, hardware, access-control, relay]
sources: []
updated: 2026-06-15
---
# Dingtian Relay Controller
A network **relay + input** board (the unit on hand is the **4-channel** variant: 4 relays + 4
inputs). Chosen to drive the entry/exit lane because — unlike the [[uhppote-controller]] — its
**inputs are independent of its relays**, which solves the [[access-controller-button-flow]]
blocker (a button on an input does not auto-open a relay; the host decides).
SDK: `dingtian/4ch/sdk_v2_0_0/` (programming manual, examples). MIT-compatible use; no vendor
runtime needed.
## ⚠️ The one gotcha: `input_link_relay`
By **default the device links each input to auto-fire its matching relay** (`input_link_relay: 1`,
`on_action_on: [[0],[1],…]` in the config) — i.e. the *same* auto-open problem as the UHPPOTE.
The difference: **it is configurable.** Set `input_link_relay: 0` (or clear the action mappings)
so an input only *reports* and the host commands the relay. **This config step is mandatory** for
the ticket-first entry flow. See [[autonomous-direction]].
## Protocol (Dingtian string — what we use)
Transport options: UDP/TCP string, UDP binary, HTTP CGI, Modbus, MQTT. We use **HTTP + UDP** —
see [[dingtian-vs-mqtt]].
- **Relay control — UDP ASCII, port 60001:** `1`+ch = ON, `2`+ch = OFF, `T`+ch = toggle.
Pulse/jog `11*` (default 500 ms), delay `11:30` (30 s then off), flash `11F5`. `X` = all relays.
Intent-only pulse for a barrier = `pulseOpen` ([[barrier-not-a-door]]).
- **Status / inputs — send `00`** → `「relays」:「inputs」:「count」`, e.g. **`0000:1111:4`** (4ch:
relays off, inputs high). `0` = OFF/Low, `1` = ON/High. Poll-based.
- **Input push — `input_link_url`:** device **HTTP POSTs to a host URL on input change** — the
push path for button events without a broker.
- **Discovery:** UDP multicast `224.0.2.11:60000`, send `\x05\xAA` (devices reply). Defaults:
IP `192.168.1.100`, UDP `60000` (binary) / `60001` (string).
- Binary protocol (port 60000) adds optional **password** + multicast; bitmask relay/input maps.
## Driver & config API
The `dingtian` driver ([[device-registry]]) implements three capabilities:
`AccessControlDevice` (relay pulse/latch over UDP), `InputDevice` (read inputs + poll-based
press/release events ~50 ms), and `PreconditionDevice` (below). Config fields include a separate
**`httpPort`** — the device's web/config API is on a configurable HTTP port (this unit: **8080**,
not the default 80), distinct from the UDP control port 60001.
### Precondition: input_link_relay must be OFF
The driver reads the device's JSON config (`GET /api/v2/config.cgi`) and **checks
`input_link_relay`**; if enabled it reports a fixable issue, and `fixPreconditions()` writes the
correction (`POST /api/v2/config_set.cgi`) — setting the flag to 0 and clearing `on_action_on`,
preserving everything else (network, etc.). This is the generic [[device-registry|precondition]]
capability: the app doesn't own full device config (that's the vendor web UI), only the few
settings our flow depends on.
> **Write gotcha (cost real debugging):** the GET config payload **omits** a `"command"` field, but
> the set endpoint **requires `"command":"setconfig"`** injected right after `"status"`. Without it
> the POST returns/looks like success but silently does nothing (and the device may reset). With it,
> POST returns `{"status":0}` and the change sticks. JSON node order must be preserved.
## Status — VERIFIED on hardware (DT-R004, sw V3.1.5461A, 10.0.10.172)
- ✅ status read (`0000:1111:4`), relay pulse, input press/release events (active-LOW, idle HIGH).
- ✅ **`input_link_relay` disabled via the driver** → confirmed: pressing an input now reports the
event and **fires NO relay** (`0000` after presses). The [[access-controller-button-flow]] blocker
is **solved** — host-in-the-loop entry (`button → host → ticket → host opens relay`) works.
- ⬜ Next: input HTTP-push endpoint (device `input_link_url` → backend), and wiring the entry flow
(input event → print ticket → `pulseOpen`). Polling works today; push is the lower-latency path.