Summary

The ControlByWeb X320M is an Ethernet-connected I/O module that exposes its relays, digital inputs, analog inputs, registers, counters, and onboard sensors over several IP-based protocols. This spec covers the HTTP GET control interface (state.xml/json, customState.xml/json), Modbus/TCP slave on port 502, SNMP (v1/v2c/v3), and MQTT 3.1.1 with Sparkplug B, as documented in the ControlByWeb device integration manual.

Transport

protocols:
  - http
  - tcp
addressing:
  port: 80
  base_url: "http://<device-ip>/"  # e.g. http://192.168.1.2/
auth:
  type: basic  # optional: enabled if User account is enabled; otherwise no auth required
  scheme: HTTP Basic
  encoding: base64
  notes: "If User account is enabled, password is sent as Base64 in the Authorization header. Example decoded value: none:webrelay -> bm9uZTp3ZWJyZWxheQ==. The X-320M also accepts HTTP on a non-default port (e.g. 8000)."

Traits

# - routable        (relay/digital I/O output switching documented)
# - queryable       (state.xml/json read endpoints, Modbus input/coil/holding-register reads, SNMP Get/Trap)
# - levelable       (analog input, register values, pulse durations are settable as numeric values)

Actions

# HTTP GET command set (state.xml/json, customState.xml/json)
# Source: Section 1.1.2
- id: relay1_off
  label: Turn Relay 1 OFF
  kind: action
  command: "GET /state.xml?relay1=0 HTTP/1.1\r\n\r\n"
  params: []

- id: relay1_on
  label: Turn Relay 1 ON
  kind: action
  command: "GET /state.xml?relay1=1 HTTP/1.1\r\n\r\n"
  params: []

- id: relay1_pulse
  label: Pulse Relay 1 (preset duration)
  kind: action
  command: "GET /state.xml?relay1=2 HTTP/1.1\r\n\r\n"
  params: []

- id: relay2_off
  label: Turn Relay 2 OFF
  kind: action
  command: "GET /state.xml?relay2=0 HTTP/1.1\r\n\r\n"
  params: []

- id: relay2_on
  label: Turn Relay 2 ON
  kind: action
  command: "GET /state.xml?relay2=1 HTTP/1.1\r\n\r\n"
  params: []

- id: relay2_pulse
  label: Pulse Relay 2 (preset duration)
  kind: action
  command: "GET /state.xml?relay2=2 HTTP/1.1\r\n\r\n"
  params: []

- id: pulse_relay1_preset
  label: Pulse Relay 1 for preset 1.5s
  kind: action
  command: "GET /state.json?relay1=2 HTTP/1.1\r\n\r\n"
  params: []

- id: pulse_relay1_5s
  label: Pulse Relay 1 for 5 seconds
  kind: action
  command: "GET /state.json?pulseTime1=5&relay1=2 HTTP/1.1\r\n\r\n"
  params: []

- id: pulse_relay1_15s
  label: Pulse Relay 1 for 15 seconds
  kind: action
  command: "GET /state.json?pulseTime1=15&relay1=2 HTTP/1.1\r\n\r\n"
  params: []

- id: set_onTime1_0
  label: Reset onTime1 to 0
  kind: action
  command: "GET /state.xml?onTime1=0 HTTP/1.1\r\n\r\n"
  params: []

- id: set_onTime1_5
  label: Reset onTime1 to 5
  kind: action
  command: "GET /state.xml?onTime1=5 HTTP/1.1\r\n\r\n"
  params: []

- id: set_totalOnTime1_0
  label: Reset totalOnTime1 to 0
  kind: action
  command: "GET /state.xml?totalOnTime1=0 HTTP/1.1\r\n\r\n"
  params: []

- id: set_totalOnTime1_5
  label: Reset totalOnTime1 to 5
  kind: action
  command: "GET /state.xml?totalOnTime1=5 HTTP/1.1\r\n\r\n"
  params: []

- id: set_count1
  label: Set counter1 to value
  kind: action
  command: "GET /state.json?count1={value} HTTP/1.1\r\n\r\n"
  params:
    - name: value
      type: integer
      description: Counter value to set (e.g. 200)

- id: set_register1
  label: Set register1 to value
  kind: action
  command: "GET /state.xml?register1={value} HTTP/1.1\r\n\r\n"
  params:
    - name: value
      type: number
      description: Register value to set (e.g. 10.5)

- id: set_customState_register
  label: Set user-named register (customState)
  kind: action
  command: "GET /customState.xml?{customName}={value} HTTP/1.1\r\n\r\n"
  params:
    - name: customName
      type: string
      description: Camel-case I/O name from customState.xml (e.g. myRegister1)
    - name: value
      type: number
      description: Value to set

- id: multi_command_state
  label: Multi-arg state command
  kind: action
  command: "GET /state.json?relay1={r1}&relay2={r2} HTTP/1.1\r\n\r\n"
  params:
    - name: r1
      type: integer
      description: Relay 1 value (0=off, 1=on, 2=pulse)
    - name: r2
      type: integer
      description: Relay 2 value (0=off, 1=on, 2=pulse)

- id: read_state_xml
  label: Read state.xml
  kind: query
  command: "GET /state.xml HTTP/1.1\r\n\r\n"
  params: []

- id: read_state_json
  label: Read state.json
  kind: query
  command: "GET /state.json HTTP/1.1\r\n\r\n"
  params: []

- id: read_customState_xml
  label: Read customState.xml
  kind: query
  command: "GET /customState.xml HTTP/1.1\r\n\r\n"
  params: []

- id: read_customState_json
  label: Read customState.json
  kind: query
  command: "GET /customState.json HTTP/1.1\r\n\r\n"
  params: []

- id: read_log_txt
  label: Read data log
  kind: query
  command: "GET /log.txt HTTP/1.1\r\n\r\n"
  params: []

- id: erase_log_txt
  label: Erase data log
  kind: action
  command: "GET /log.txt?erase=1 HTTP/1.1\r\n\r\n"
  params: []

- id: read_syslog_txt
  label: Read system log
  kind: query
  command: "GET /syslog.txt HTTP/1.1\r\n\r\n"
  params: []

- id: erase_syslog_txt
  label: Erase system log
  kind: action
  command: "GET /syslog.txt?erase=1 HTTP/1.1\r\n\r\n"
  params: []

# Modbus/TCP slave (Section 2)
# The module listens on TCP port 502 and supports the function codes below.
# Exact coil/register addresses depend on the device's Modbus map (viewable in setup pages).
- id: modbus_read_coils
  label: Modbus Read Coils (Function Code 01)
  kind: query
  command: "Modbus/TCP function 0x01 on port 502"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: coil_count
      type: integer
      description: Number of coils to read; sum with start_address must not exceed max coil count

- id: modbus_read_discrete_inputs
  label: Modbus Read Discrete Inputs (Function Code 02)
  kind: query
  command: "Modbus/TCP function 0x02 on port 502"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: input_quantity
      type: integer
      description: Number of discrete inputs to read

- id: modbus_read_holding_registers
  label: Modbus Read Holding Registers (Function Code 03)
  kind: query
  command: "Modbus/TCP function 0x03 on port 502"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: register_count
      type: integer
      description: Must be divisible by 2 (32-bit IEEE 754 values); little- or big-endian per device config

- id: modbus_write_single_coil
  label: Modbus Write Single Coil (Function Code 05)
  kind: action
  command: "Modbus/TCP function 0x05 on port 502, value 0x00=Off, 0xFF=On"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: value
      type: integer
      description: 0 (off) or 0xFF (on)

- id: modbus_write_multiple_coils
  label: Modbus Write Multiple Coils (Function Code 15/0x0F)
  kind: action
  command: "Modbus/TCP function 0x0F on port 502, value 0x0000-0xFFFF"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: output_quantity
      type: integer
      description: Number of outputs
    - name: values
      type: integer
      description: Bit-packed value 0x0000-0xFFFF

- id: modbus_write_multiple_registers
  label: Modbus Write Multiple Registers (Function Code 16/0x10)
  kind: action
  command: "Modbus/TCP function 0x10 on port 502, IEEE 754 32-bit float pairs"
  params:
    - name: start_address
      type: integer
      description: Refer to Modbus map in setup pages
    - name: register_count
      type: integer
      description: Must be divisible by 2; little- or big-endian per device config
    - name: values
      type: number
      description: IEEE 754 32-bit float

# SNMP (Section 3)
# The device supports v1, v2c, v3; community strings default to "webrelay".
- id: snmp_get_system
  label: SNMP Get Standard RFC1213 objects
  kind: query
  command: "SNMP GetRequest for sysDescr, sysObjectID, sysUpTime, sysName"
  params: []

- id: snmp_set_io
  label: SNMP Set I/O value
  kind: action
  command: "SNMP SetRequest on module-specific OIDs (MIB from setup pages)"
  params:
    - name: community
      type: string
      description: Default "webrelay" for v1/v2c; v3 uses USM

- id: snmp_trap_relay
  label: SNMP Trap on relay state change
  kind: event
  command: "SNMP Trap (v1) / Notification (v2c/v3)"
  params: []

# Remote Services (Section 5)
- id: remote_services_connect
  label: Initiate Remote Services connection
  kind: action
  command: "Device-initiated TCP V1 connection to external server per Advanced Network settings; sends connection string + state.xml on interval"
  params: []

- id: remote_services_ack
  label: ACK response to Remote Services
  kind: action
  command: "Send three-character 'ACK' within 10 seconds of each connection string"
  params: []

# ControlByWeb Cloud (Section 6)
- id: cloud_state_json
  label: Read state via Cloud DAT URL (JSON)
  kind: query
  command: "GET https://api.controlbyweb.cloud/{datUrl}/state.json"
  params: []

- id: cloud_state_xml
  label: Read state via Cloud DAT URL (XML)
  kind: query
  command: "GET https://api.controlbyweb.cloud/{datUrl}/state.xml"
  params: []

- id: cloud_set_relays
  label: Set relays via Cloud DAT URL
  kind: action
  command: "GET https://api.controlbyweb.cloud/{datUrl}/state.json?relay1={r1}&relay2={r2}"
  params:
    - name: datUrl
      type: string
      description: Generated Cloud DAT URL
    - name: r1
      type: integer
      description: Relay 1 value
    - name: r2
      type: integer
      description: Relay 2 value

Feedbacks

# state.xml/json feedback values
- id: relay_state
  type: integer
  values: [0, 1]
  description: Relay output state (0=coil off, 1=coil energized)
  source_tag: "<relayX>" / "relayX"

- id: digital_input_state
  type: integer
  values: [0, 1]
  description: Digital input state (0=off, 1=on)
  source_tag: "<digitalInputX>" / "digitalInputX"

- id: digital_io_state
  type: integer
  values: [0, 1]
  description: Digital I/O state when configured as input
  source_tag: "<digitalIOX>" / "digitalIOX"

- id: analog_input_value
  type: number
  description: Analog input reading
  source_tag: "<analogInputX>" / "analogInputX"

- id: vin_value
  type: number
  description: Scaled internal Vin measurement
  source_tag: "<vin>" / "vin"

- id: frequency_input_value
  type: number
  description: Frequency input value (X-420 specific; UNRESOLVED for X320M)
  source_tag: "<frequencyInput>"

- id: on_time
  type: number
  description: Seconds input/relay was on since last coming on
  source_tag: "<onTimeX>" / "onTimeX"

- id: total_on_time
  type: number
  description: Total seconds input/relay has been on
  source_tag: "<totalOnTimeX>" / "totalOnTimeX"

- id: count_value
  type: number
  description: Counter value for input X
  source_tag: "<countX>" / "countX"

- id: frequency_value
  type: number
  description: Frequency for input X
  source_tag: "<frequencyX>" / "frequencyX"

- id: register_value
  type: number
  description: Value of register X
  source_tag: "<registerX>" / "registerX"

- id: one_wire_sensor_value
  type: string
  description: 1-Wire sensor reading; "x.x" indicates read failure
  source_tag: "<oneWireSensorX>" / "oneWireSensorX"

- id: utc_time
  type: integer
  description: Seconds since 1970-01-01 UTC
  source_tag: "<utcTime>" / "utcTime"

- id: timezone_offset
  type: integer
  description: Local timezone offset for utcTime
  source_tag: "<timezoneOffset>" / "timezoneOffset"

- id: serial_number
  type: string
  description: Device MAC address (00:0C:C8:xx:xx:xx format)
  source_tag: "<serialNumber>" / "serialNumber"

- id: modbus_coil_read
  type: integer
  description: Coils response bitfield (bit 0 = start address coil)
  source_tag: "Modbus FC 0x01 response"

- id: modbus_discrete_input_read
  type: integer
  description: Discrete input response bitfield
  source_tag: "Modbus FC 0x02 response"

- id: modbus_holding_register_read
  type: number
  description: IEEE 754 32-bit float (pair of 16-bit registers)
  source_tag: "Modbus FC 0x03 response"

Variables

# Numeric settable values explicitly documented in the source
- id: register1
  type: number
  description: General-purpose register (state.xml?register1=)
  command: "GET /state.xml?register1={value}"

- id: count1
  type: integer
  description: Counter value (state.json?count1=)
  command: "GET /state.json?count1={value}"

- id: onTime1
  type: integer
  description: On-time counter (seconds); resettable to 0
  command: "GET /state.xml?onTime1={value}"

- id: totalOnTime1
  type: integer
  description: Total on-time counter (seconds); resettable to 0
  command: "GET /state.xml?totalOnTime1={value}"

- id: pulseTime1
  type: integer
  description: One-shot pulse duration in seconds (must precede relay1=2 argument)
  command: "GET /state.json?pulseTime1={value}&relay1=2"

Events

# Unsolicited notifications documented in source
- id: snmp_trap_relay_change
  source: SNMP
  description: Trap sent when a relay changes state
  pdu: Trap (v1) / Notification (v2c, v3)

- id: snmp_trap_sensor_threshold
  source: SNMP
  description: Trap sent when a configured sensor threshold is reached
  pdu: Trap / Notification

- id: snmp_trap_vin_range
  source: SNMP
  description: Trap sent when supply voltage goes out of configured range
  pdu: Trap / Notification

- id: mqtt_publish
  source: MQTT
  description: I/O state published to configured MQTT broker
  pdu: MQTT 3.1.1 PUBLISH

- id: mqtt_sparkplug_b
  source: MQTT
  description: Sparkplug B structured topic + payload
  pdu: Sparkplug B

Macros

# UNRESOLVED: no explicit multi-step sequences documented in source

Safety

confirmation_required_for: []
interlocks: []
# UNRESOLVED: source does not document safety warnings, interlocks, or power-on sequencing.
# Modbus is automatically disabled whenever the User account is enabled (no password mechanism in Modbus/TCP).

Notes

  • Source manual covers the ControlByWeb X-400 Series at large. X320M-specific I/O counts, register addresses, and coil counts must be confirmed against the X320M setup pages (Advanced Network > "View Modbus Address Table") and the device-generated MIB file.
  • HTTP control uses plain GET requests; no keep-alive or special framing required.
  • Modbus/TCP idle timeout is 50 seconds; send a periodic read to keep the socket open. The device accepts at most 2 concurrent Modbus/TCP sockets.
  • Error responses prepend 0x80 to the original function code (e.g. 0x01 -> 0x81) plus an exception code byte.
  • 32-bit sensor/analog values in Modbus FC 0x03 use IEEE 754; endianness is configurable on the device.
  • Unconfigured sensor reads return 0xFFFFFFFF (NaN).
  • MQTT payload tokens (Section 4.1.4) are placeholders for use in publish payloads: ${mac}, ${ver}, ${ser}, ${uptime}, ${ip}, ${port}, ${httpsport}, ${dateTime}, ${name}, ${model}, ${clientID}, ${digitalInput1..4}, ${relay1..4}, ${vin}, ${register1}.
  • ControlByWeb Cloud DAT URL version 2.0 is reserved for ControlByWeb.Cloud and not for third-party use.

Provenance

source_domains:
  - controlbyweb.com
source_urls:
  - https://controlbyweb.com/wp-content/uploads/2025/05/cbw-integration-and-protocols-manual.pdf
  - https://controlbyweb.com/wp-content/uploads/2024/01/X-320M_manual_v1.5.pdf
  - https://controlbyweb.com/support/cbw-integration-manual/
retrieved_at: 2026-06-30T15:03:30.812Z
last_checked_at: 2026-07-07T11:14:48.388Z

Verification Summary

verdict: verified
checked_at: 2026-07-07T11:14:48.388Z
matched_actions: 39
action_count: 39
confidence: medium
summary: "All 39 spec actions have literal matches in source; transport parameters verified. (4 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "source describes the X-400 Series family at large; X320M-specific I/O counts and capabilities should be confirmed against the X320M datasheet."
- "no explicit multi-step sequences documented in source"
- "source does not document safety warnings, interlocks, or power-on sequencing."
- "pulseTime1 argument must precede the relay1=2 argument on the same request line."

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