Skip to main content

Relay Semantics

Relay-driven hardware (gates, garage doors, locks, lighting contactors) reports and accepts a raw contact state — open or closed. What that contact means depends on the equipment and the subsystem. GEM centralizes this translation so drivers never hardcode it.

The translation helpers

Two server helpers map between raw relay state and logical zone state:

  • relayToZoneState(relayState, zone) — turn a contact reading into a zone state (e.g. "this gate is open").
  • zoneToRelayCommand(cmd, zone) — turn a zone command into the relay action that achieves it.

Drivers route relay handling through these helpers rather than reasoning about contacts themselves.

Default mappings by subsystem

The default assumes a normally-open relay (relay closed = the active/energized state). From there the meaning is subsystem-specific:

BehaviorSubsystemsMapping (relay closed →)
Invertedgates, garages, doors/locks, liftsopen / unlocked
Directlights, fans, poweron
Directshadesopen
Directsecurityarmed

Override precedence

A site can override the default per zone or per subsystem. Precedence, most specific first:

  1. Zone relay_states attribute (JSON)
  2. Subsystem relay_states attribute
  3. Built-in subsystem defaults (above)

A legacy normally_closed boolean is also honored. Note that normally_open is the default and a no-op — only normally_closed inverts the contact interpretation.

:::tip Drivers: never hardcode contact meaning If you're writing a relay driver, always go through relayToZoneState / zoneToRelayCommand. Hardcoding "closed = on" breaks the moment a site wires a gate normally-closed or maps a contact differently. See Driver Development. :::