Summary

This spec covers the Somfy Digital Network (SDN) RS-485 bus protocol as exposed by the TaHoma v1.2 hub for controlling Somfy wired motors and actuators (e.g. Ø30 DC, Ø50 AC/DC, Glydea, RS485 RTS transmitter). The source document describes a master/slave asynchronous serial bus with device addressing, configuration, control, and status messages. No transport-layer authentication is documented.

Transport

protocols:
  - serial
serial:
  baud_rate: 4800
  data_bits: 8
  parity: odd
  stop_bits: 1
  flow_control: none
# UNRESOLVED: physical bus is RS-485 (per source); a "serial" transport block
# captures line settings. RS-485 specifics (termination, bias, max nodes) not
# enumerated in source beyond 3-byte NodeID addressing.
# UNRESOLVED: data bit inversion is required: every byte is bitwise NOT before
# transmission. Example: 58h -> A7h on the bus.
# UNRESOLVED: source does not state RS-232 vs RS-485 explicitly inside the
# serial config table, but §3 repeatedly references "RS485" devices/bus.
auth:
  type: none  # inferred: no auth procedure in source

Traits

# - routable        (move-to-position / stop controls are the core action set)
# - queryable       (status requests POST back position/status/lock/UI state)
# - levelable       # not applicable - no volume/gain/brightness; position % is a routable parameter
# - powerable       # not applicable - motors are not power-cycled via SDN; line-power devices

Actions

# All actions are framed as SDN messages:
#   Byte1=MSG | Byte2=ACK/LEN | Byte3=NODE_TYPE | Byte4-6=SRC@ (LSB first) | Byte7-9=DST@ (LSB first) | Byte10..n-1=DATA | Byten=CHECKSUM (sum of all preceding bytes)
# Min frame = 11 bytes (no DATA). Max = 32 bytes. Bytes are bitwise-NOT before transmission.
# MASTER sets NODE_TYPE=0h. NodeID is 3 bytes. Broadcast = FFFFFFh. Group = DST 000000h with GroupID in SRC@.

# --- Device Management ---
- id: get_node_addr
  label: Get Node Address
  kind: query
  command: "40h"
  params: []
  notes: Broadcast request; replies may collide when many devices share the bus.

- id: set_group_addr
  label: Set Group Address
  kind: action
  command: "51h {group_index} {group_id_b0} {group_id_b1} {group_id_b2}"
  params:
    - name: group_index
      type: integer
      description: Entry in the group table (0-15)
    - name: group_id
      type: bytes
      description: 3-byte group address (24-bit)

- id: get_group_addr
  label: Get Group Address
  kind: query
  command: "41h {group_index}"
  params:
    - name: group_index
      type: integer
      description: Group table entry to query (0-15)

# --- Acknowledgement and Errors ---
- id: ack
  label: Acknowledge
  kind: feedback
  command: "7Fh"
  params: []
  notes: Returned by SLAVE when ACK bit set in the request and processing succeeded.

- id: nack
  label: Negative Acknowledge
  kind: feedback
  command: "6Fh {error_code}"
  params:
    - name: error_code
      type: enum
      description: "01h=Data out of range, 10h=Unknown message, 11h=Message Length Error, FFh=Busy"

# --- Device Information ---
- id: get_node_app_version
  label: Get Firmware Revision
  kind: query
  command: "74h"
  params: []

- id: set_node_label
  label: Set User Label
  kind: action
  command: "55h {label[16]}"
  params:
    - name: label
      type: string
      description: Always 16 characters; pad with spaces if shorter.

- id: get_node_label
  label: Get User Label
  kind: query
  command: "45h"
  params: []

# --- Device Configuration: HMI Management ---
- id: set_local_ui
  label: Set Local UI Lock State
  kind: action
  command: "17h {function} {ui_index} {priority}"
  params:
    - name: function
      type: enum
      description: "00h=Enable/Unlock, 01h=Disable/Lock"
    - name: ui_index
      type: enum
      description: "00h=All, 01h=DCT input, 02h=Local stimuli, 03h=Local Radio (BT), 04h=Touch Motion, 05h=LEDs"
    - name: priority
      type: integer
      description: 0-255; higher = higher priority. Lock re-set/removed only at >= existing lock level.

- id: get_local_ui
  label: Get Local UI Lock State
  kind: query
  command: "27h {ui_index}"
  params:
    - name: ui_index
      type: integer
      description: "01h..UI_MAX"

# --- Device Configuration: Intermediate Positions ---
- id: set_motor_ip
  label: Set Intermediate Position
  kind: action
  command: "15h {function} {ip_index} {value_lo} {value_hi}"
  params:
    - name: function
      type: enum
      description: "00h=Delete IP, 01h=Set IP at current position, 03h=Set IP at % position, 04h=Divide full range into N equal IPs"
    - name: ip_index
      type: integer
      description: 1-16
    - name: value
      type: integer
      description: Position % (function 03h) or IP count (function 04h); ignored otherwise.

- id: get_motor_ip
  label: Get Intermediate Position
  kind: query
  command: "25h {ip_index}"
  params:
    - name: ip_index
      type: integer
      description: 1-16

# --- Device Configuration: Speed Adjustment (DC motors only) ---
- id: set_motor_rolling_speed
  label: Set Motor Rolling Speed
  kind: action
  command: "13h {up_speed} {down_speed} {slow_speed}"
  params:
    - name: up_speed
      type: integer
      description: Speed (rpm) during UP movement. Range is per motor datasheet.
    - name: down_speed
      type: integer
      description: Speed (rpm) during DOWN movement. Range is per motor datasheet.
    - name: slow_speed
      type: integer
      description: Speed (rpm) for adjustment movements. Range is per motor datasheet.

- id: get_motor_rolling_speed
  label: Get Motor Rolling Speed
  kind: query
  command: "23h"
  params: []

# --- Device Configuration: Network Lock ---
- id: set_network_lock
  label: Set Network Lock
  kind: action
  command: "16h {function} {priority}"
  params:
    - name: function
      type: enum
      description: "00h=Unlock, 01h=Lock, 03h=Save lock across power cycle, 04h=Do not save lock across power cycle"
    - name: priority
      type: integer
      description: 0-255. Required for 00h/01h; ignored for 03h/04h.

- id: get_network_lock
  label: Get Network Lock
  kind: query
  command: "26h"
  params: []

# --- Device Control ---
- id: ctrl_moveto
  label: Move To Position
  kind: action
  command: "03h {function} {position_lo} {position_hi} {reserved}"
  params:
    - name: function
      type: enum
      description: "00h=Move to DOWN limit, 01h=Move to UP limit, 02h=Move to IP, 04h=Move to % position"
    - name: position
      type: integer
      description: IP index (0-15) for function 02h, or % (0-100) for function 04h. Ignored for 00h/01h.
    - name: reserved
      type: bytes
      description: 8-bit reserved field.

- id: ctrl_stop
  label: Stop
  kind: action
  command: "02h {reserved}"
  params:
    - name: reserved
      type: bytes
      description: 8-bit reserved field.
  notes: Motor stops immediately without speed ramp-down.

# --- Device Status ---
- id: get_motor_position
  label: Get Motor Position
  kind: query
  command: "0Ch"
  params: []

- id: get_motor_status
  label: Get Motor Status
  kind: query
  command: "0Eh"
  params: []

Feedbacks

- id: motor_position
  type: object
  description: |
    POST_MOTOR_POSITION (0Dh) reply. Fields:
    Position_pulse (16-bit, UP_LIMIT..DOWN_LIMIT),
    Position_percentage (8-bit, 0..100),
    Reserved (8-bit),
    IP (8-bit, 1..IP_MAX, FFh if not at an IP).

- id: motor_status
  type: object
  description: |
    POST_MOTOR_STATUS (0Fh) reply. Fields:
    Status (00h Stopped, 01h Running, 02h Blocked, 03h Locked),
    Direction (00h Going DOWN, 01h Going UP, FFh Unknown),
    Source (00h Internal, 01h Network, 02h Local UI),
    Cause (00h Target reached, 01h Explicit command, 02h Wink, 20h Obstacle, 21h Over-current, 22h Thermal, 30h Run time exceeded, 32h Timeout, FFh Reset/PowerUp).

- id: group_address
  type: object
  description: |
    POST_GROUP_ADDR (61h) reply. GroupIndex (8-bit) + GroupID (24-bit).

- id: node_app_version
  type: object
  description: |
    POST_NODE_APP_VERSION (75h) reply. App_Reference (24-bit) + App_IndexLetter (8-bit ASCII 41h..5Ah) + App_IndexNumber (8-bit) + Reserved (8-bit).

- id: node_label
  type: string
  description: |
    POST_NODE_LABEL (65h) reply. 16-character string.

- id: local_ui_state
  type: object
  description: |
    POST_LOCAL_UI (37h) reply. UI_Index, Status (00h Enabled/Unlocked, 01h Disabled/Locked), Source_Addr (24-bit), Priority (8-bit).

- id: motor_ip
  type: object
  description: |
    POST_MOTOR_IP (35h) reply. IP_index (1..16), Reserved (16-bit), IP_position_percentage (0..100, FFh if not set).

- id: motor_rolling_speed
  type: object
  description: |
    POST_MOTOR_ROLLING_SPEED (33h) reply. UP_Speed, DOWN_Speed, Slow_Speed (8-bit each, units per motor datasheet).

- id: network_lock_state
  type: object
  description: |
    POST_NETWORK_LOCK (36h) reply. Status (00h Unlocked, 01h Locked), Source_Addr (24-bit), Priority (8-bit), Saved (00h not restored, 01h restored on power cycle).

- id: nack_error
  type: enum
  values: [data_out_of_range, unknown_message, length_error, busy]
  description: |
    ErrorCode from NACK (6Fh). 01h/10h/11h/FFh respectively.

Variables

- id: intermediate_position
  description: User-defined motor preset position as percentage of full travel (0..100). Up to 16 IPs per motor.
- id: motor_rolling_speed
  description: Per-direction RPM (UP/DOWN/Slow). Range varies by motor - see device datasheet.
- id: network_lock_priority
  description: 0..255 priority gating whether network CTRL messages execute while a lock is active.
- id: local_ui_priority
  description: 0..255 priority for a given UI lock; higher than existing lock required to re-lock/unlock.

Events

# UNRESOLVED: source describes only solicited POST_xxx replies; no unsolicited
# asynchronous notifications are documented.

Macros

# UNRESOLVED: no multi-step sequences are explicitly defined in the source.
# Operators may compose CTRL_MOVETO + GET_MOTOR_POSITION as a "move and verify"
# pattern, but the source does not prescribe this.

Safety

confirmation_required_for: []
interlocks:
  - description: "When SET_LOCAL_UI UI_Index=00h, priority must be >= highest existing lock level or NACK(LOW_PRIORITY) is returned."
  - description: "When a per-item UI_Index lock is set, priority must be >= that item's lock level or NACK(LOW_PRIORITY) is returned."
  - description: "When NETWORK_LOCK is active, only CTRL_NETWORK_LOCK with equal or higher priority is accepted; all other movement messages (CTRL_xxx, SET_MOTOR_LIMITS, SET_TILT_LIMITS) are rejected with NACK(NODE_IS_LOCKED)."
  - description: "Motor status 02h (Blocked) and Cause codes 20h/21h/22h/30h indicate thermal protection, obstacle, over-current, or run-time exceeded; controller should treat as fault state."
# UNRESOLVED: source does not document any external interlocks (e.g. E-stop, safety
# contactors) - only protocol-level priority gating.

Notes

  • Byte inversion: Every data byte is bitwise-NOTed before transmission. Example: byte 0x58 goes on the bus as 0xA7. Same for the address and checksum fields.
  • Bit order: Least significant bit sent first within each byte.
  • Frame delimiters: No sync/header byte. A frame ends when the bus is idle for Tfree (3 ms).
  • Timing constraints:
    • Tc <= 1 ms max gap between consecutive bytes in a frame.
    • Treq >= 10 ms idle time before the master can start a new request.
    • Trep = 5..255 ms (partially randomized) slave reply delay after bus activity.
  • Addressing modes:
    • Point-to-point: SRC@ = sender NodeID, DST@ = target NodeID.
    • Group: SRC@ = GroupID, DST@ = 000000h.
    • Broadcast: SRC@ = NodeID, DST@ = FFFFFFh.
    • NodeType filtering: set DST NODE_TYPE to target a family (02h=Ø30 DC, 05h=RS485 RTS, 06h=Glydea, 07h=Ø50 AC, 08h=Ø50 DC, 09h=Ø40 AC).
  • ACK strategy: Source strongly recommends requesting ACKs (set ACK bit in Byte 2) and implementing retry on NACK or ACK timeout. Avoid ACKs in group/broadcast mode to limit collision risk.
  • Length field (LEN): 0..31, equals the number of DATA bytes in the message.
  • CHECKSUM: sum of Byte 1..Byte n-2 (basic additive checksum, no error correction).
  • NodeType table (from §3.2.2): 02h=Ø30 DC Serie RS485, 05h=RS485 RTS transmitter, 06h=Glydea RS485, 07h=Ø50 AC Serie RS485, 08h=Ø50 DC Serie RS485, 09h=Ø40 AC Serie RS485 (not yet available).
  • Master NodeType: Always 0h.
  • Per-source caveat (NACK §6.1.3): "Above mentioned NACK values are implemented in all products" — other error codes may exist on specific products.
  • MOVETO function 04h: Position is 0..100, treated as % of full travel.
  • MOTOR_IP function 04h: Value = desired IP count; sets the first N IPs to evenly-spaced positions (e.g. N=2 → IP1=33%, IP2=66%).
<br>

## Provenance

```yaml
source_domains:
  - service.somfy.com
source_urls:
  - https://service.somfy.com/downloads/bui_v4/sdn-integration-guide--preliminary.pdf
retrieved_at: 2026-04-29T08:47:14.635Z
last_checked_at: 2026-06-02T22:14:52.221Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T22:14:52.221Z
matched_actions: 20
action_count: 20
confidence: medium
summary: "All 20 spec actions traced to source (dip-safe re-verify). (11 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "TaHoma-specific gateway configuration (LAN/HTTP API, cloud pairing) is not covered by the supplied source."
- "source describes SDN bus protocol generically; TaHoma-specific bindings (which NodeIDs TaHoma emulates) not stated."
- "physical bus is RS-485 (per source); a \"serial\" transport block"
- "data bit inversion is required: every byte is bitwise NOT before"
- "source does not state RS-232 vs RS-485 explicitly inside the"
- "source describes only solicited POST_xxx replies; no unsolicited"
- "no multi-step sequences are explicitly defined in the source."
- "source does not document any external interlocks (e.g. E-stop, safety"
- "TaHoma v1.2-specific gateway configuration (LAN REST API, cloud pairing, Zigbee/RTS bridges) is not in the supplied source."
- "device-specific speed ranges (\"See Technical Datasheet\") and the master NodeID value used by TaHoma on the SDN bus are not in the source."
- "no firmware version compatibility range is stated; firmware field is left empty."

From the AI4AV catalog (https://ai4av.net) · ODbL-1.0