Compass

A heading-driven action ring. The device's live heading rotates a needle; you arrange action pucks around the ring; when the needle points at a puck (within tolerance) for dwell seconds, that puck's action fires. Point your phone at a thing to trigger it — no tapping. Pucks can be dragged around the ring to re-bearing them (firing pickup/place/layout), so the same control authors and runs.

The first spatial (Pattern-3) drag control: puck placements ride a bearing coordinate (CARSpatialPlacementState) and the whole ring speaks the Actions convention.

Type

"compass"

Heading (required)

The needle is driven by the control's own value — bind the compass sensor:

"sync": [{ "method": "sensor", "sensor": "heading" }]

The bound value is the heading in degrees (0 = N, clockwise). No compass hardware (e.g. the simulator)? Set a defaultValue (a number) to render a static needle for testing.

Sizing

Square control. In a mode: "flow" grid set controlHeight (e.g. 420); in a 2-D grid give it a real span. See Grid Dimensions.

Config (compassConfig)

Field Type Description
pucks array The action pucks (below).
tolerance number Half-angle (°) the needle must be within to "point at" a puck (default 15).
dwell number Seconds to hold before firing (default 1.2; 0 = instant on entry).
showCardinals bool N/E/S/W ticks (default true).
editable bool Drag pucks to re-bearing them (default true).
events object pickup / place / layout fired on puck drag.

Puck { id, label?, icon?, tint?, bearing, action?, locked? }

Field Type Description
bearing number Where it sits, degrees (0 = N, clockwise).
action object Fired on point-and-dwell. {{value}} = the puck id.
locked bool Can't be dragged (still activatable).

Events

Puck drag fires place { item, x, y, bearing } and layout { points: { id: {x,y,bearing} }, changed } (see DragEventActions). Puck activation fires that puck's own action.

Examples

Point-to-run scenes

{
  "type": "compass",
  "id": "action-ring",
  "position": [0, 0],
  "span": [7, 4],
  "controlHeight": 420,
  "label": "Action Ring",
  "defaultValue": 5,
  "compassConfig": {
    "tolerance": 20,
    "dwell": 1.0,
    "pucks": [
      { "id": "lights", "label": "Lights", "icon": "lightbulb.fill", "tint": "#FF9500", "bearing": 0,
        "action": { "method": "meshsocket", "mode": "send", "event": "route_msg",
                    "payload": { "target_id": "home", "type": "scene", "payload": { "name": "{{value}}" } } } },
      { "id": "music", "label": "Music", "icon": "music.note", "tint": "#FF2D55", "bearing": 90,
        "action": { "method": "meshsocket", "mode": "send", "event": "route_msg",
                    "payload": { "target_id": "home", "type": "scene", "payload": { "name": "{{value}}" } } } },
      { "id": "lock", "label": "Lock", "icon": "lock.fill", "tint": "#34C759", "bearing": 180,
        "action": { "method": "meshsocket", "mode": "send", "event": "route_msg",
                    "payload": { "target_id": "home", "type": "scene", "payload": { "name": "{{value}}" } } } }
    ]
  },
  "sync": [{ "method": "sensor", "sensor": "heading" }]
}

Try it: the bundled compass-demo layout has this ring (seeded to 5° so a needle shows in the simulator).

Accessibility

Under VoiceOver each puck reads its bearing, double-tap fires its action (no pointing needed), and the adjustable action (swipe up/down) rotates it 15° per step.