Summary
X-400 Series IP-enabled remote I/O controller with relay outputs, digital/analog inputs, and optional sensors. Control via HTTP GET requests (state.xml/json), Modbus/TCP on port 502, SNMP (v1/v2c/v3), or MQTT/Sparkplug B (v3.1.1). No native REST semantics — commands passed as query parameters on XML/JSON endpoints. Optional ControlByWeb Cloud DAT URLs provide remote access without port forwarding.
Transport
protocols:
- http
- modbus
- snmp
- mqtt
addressing:
base_url: http://{ip}/state.xml # http only; https for cloud DAT URLs
modbus_port: 502 # stated: "open a connection with the module on port 502 (configurable under Advanced Network tab)"
http_port: 80 # inferred default: source references "not port 80" implying 80 is default
auth:
type: none # inferred: no-password mode supported in source
# NOTE: optional user/password auth via Base64 encoded Authorization header (HTTP).
# Password encoding: Base64("name:password") - example "none:webrelay" → "bm9uZTp3ZWJyZWxheQ=="
# NOTE: Modbus disabled whenever User account enabled (Modbus/TCP has no password mechanism).
# NOTE: SNMP v1/v2c use community strings (default read+write "webrelay"); SNMP v3 uses USM (auth+privacy protocols).
Traits
- powerable # relay on/off/pulse commands present
- routable # multi-output control present
- queryable # state.xml/json + Modbus read FCs + SNMP Get present
- levelable # pulseTime parameter for variable pulse duration; register float set
Actions
# ── HTTP / XML / JSON control (state.xml, state.json, customState.xml/json) ──
- id: set_relay
label: Set Relay State
kind: action
command: "state.xml?relay{relay}={value}"
params:
- name: relay
type: integer
description: Relay number (1-based)
- name: value
type: integer
description: 0=off, 1=on, 2=pulse
note: Replace state.xml with state.json or customState.xml/json equivalently.
- id: pulse_relay
label: Pulse Relay with Custom Duration
kind: action
command: "state.json?pulseTime{relay}={pulseTime}&relay{relay}=2"
params:
- name: relay
type: integer
description: Relay number (1-based)
- name: pulseTime
type: integer
description: Pulse duration in seconds (overrides preset, not stored)
note: pulseTime arg MUST precede relayX=2 in query string.
- id: set_on_time
label: Set On Time
kind: action
command: "state.xml?onTime{relay}={value}"
params:
- name: relay
type: integer
description: Input/relay number (1-based)
- name: value
type: number
description: On time in seconds
- id: set_total_on_time
label: Set Total On Time
kind: action
command: "state.xml?totalOnTime{relay}={value}"
params:
- name: relay
type: integer
description: Input/relay number (1-based)
- name: value
type: number
description: Total on time in seconds
- id: set_counter
label: Set Counter
kind: action
command: "state.json?count{counter}={value}"
params:
- name: counter
type: integer
description: Counter number (1-based)
- name: value
type: number
description: Counter value
- id: set_register
label: Set Register Value
kind: action
command: "state.xml?register{register}={value}"
params:
- name: register
type: integer
description: Register number (1-based)
- name: value
type: number
description: Register value (floating point, e.g. 10.5)
- id: erase_data_log
label: Erase Data Log (log.txt)
kind: action
command: "http://{ip}/log.txt?erase=1"
params: []
note: Requires user password if User account enabled.
- id: erase_syslog
label: Erase System Log (syslog.txt)
kind: action
command: "http://{ip}/syslog.txt?erase=1"
params: []
note: Requires setup username/password.
- id: custom_state_set
label: Set I/O via customState (CamelCase name)
kind: action
command: "customState.xml?{camelName}={value}"
params:
- name: camelName
type: string
description: Camel case of user-configurable I/O name (e.g. myRegister1)
- name: value
type: number
description: Value to set
note: Reference customState.xml for exact tag names per configured I/O.
# ── Modbus/TCP function codes (slave; port 502) ──
- id: modbus_read_coils
label: Modbus Read Coils (FC 01)
kind: query
command: "0x01"
params:
- name: start_address
type: integer
description: Starting coil address (refer to Modbus map in setup pages)
- name: quantity
type: integer
description: Number of coils to read
note: Read relays and digital I/O configured as outputs. Error FC 0x81.
- id: modbus_read_discrete_inputs
label: Modbus Read Discrete Inputs (FC 02)
kind: query
command: "0x02"
params:
- name: start_address
type: integer
description: Starting input address (refer to Modbus map)
- name: quantity
type: integer
description: Number of inputs to read
note: Read digital inputs / digital I/O as inputs. Error FC 0x82.
- id: modbus_read_holding_registers
label: Modbus Read Holding Registers (FC 03)
kind: query
command: "0x03"
params:
- name: start_address
type: integer
description: Starting register address (refer to Modbus map)
- name: quantity
type: integer
description: Register count; MUST be divisible by 2 (32-bit float pairs)
note: Read Vin, sensors, registers, counters, analog inputs. IEEE 754 floats, little- or big-endian per config. NaN (0xFFFFFFFF) if sensor absent. Error FC 0x83.
- id: modbus_write_single_coil
label: Modbus Write Single Coil (FC 05)
kind: action
command: "0x05"
params:
- name: address
type: integer
description: Coil address (refer to Modbus map)
- name: value
type: integer
description: 0x00=Off, 0xFF=On
note: Control one digital output/relay. Response mirrors 0x00 or 0xFF. Error FC 0x85.
- id: modbus_write_multiple_coils
label: Modbus Write Multiple Coils (FC 15 / 0x0F)
kind: action
command: "0x0F"
params:
- name: start_address
type: integer
description: Starting output address
- name: quantity
type: integer
description: Number of outputs to affect
- name: byte_count
type: integer
description: Quantity divided by 8
- name: values
type: integer
description: Digital I/O value byte(s), 0x0000-0xFFFF
note: 0xFFFF=ON up to 16, 0x0000=OFF, 0xF0=off first 4 / on 5-8. Error FC 0x8F.
- id: modbus_write_multiple_registers
label: Modbus Write Multiple Registers (FC 16 / 0x10)
kind: action
command: "0x10"
params:
- name: start_address
type: integer
description: Starting register address
- name: quantity
type: integer
description: Register count; MUST be divisible by 2
- name: values
type: number
description: IEEE 754 floating-point values (endianness per config)
note: Set registers / analog outputs / pulse duration (e.g. Pulse Relay 1 as 32-bit float at addr 512-513). Error FC 0x90.
# ── SNMP PDUs (v1/v2c/v3) ──
- id: snmp_get
label: SNMP GetRequest
kind: query
command: "GetRequest"
params:
- name: oid
type: string
description: Object identifier from generated MIB file
note: Community string required (default "webrelay") for v1/v2c; USM creds for v3.
- id: snmp_get_next
label: SNMP GetNextRequest
kind: query
command: "GetNextRequest"
params:
- name: oid
type: string
description: Object identifier
note: Walks to next object in MIB.
- id: snmp_get_bulk
label: SNMP GetBulkRequest
kind: query
command: "GetBulkRequest"
params:
- name: oid
type: string
description: Object identifier
note: Bulk transfer; supported PDU per Section 3.1.
- id: snmp_set
label: SNMP SetRequest
kind: action
command: "SetRequest"
params:
- name: oid
type: string
description: Object identifier from generated MIB file
- name: value
type: string
description: Value to write
note: Write community string required (default "webrelay") for v1/v2c.
Feedbacks
- id: relay_state
label: Relay State
type: enum
values:
- "0" # off (coil off)
- "1" # on (coil energized)
- id: digital_input_state
label: Digital Input State
type: enum
values:
- "0" # off (voltage not applied)
- "1" # on (voltage applied)
- id: digital_io_state
label: Digital I/O State (configurable direction)
type: enum
values:
- "0" # off (voltage not applied)
- "1" # on (voltage applied)
description: digitalIOX - digital I/O configurable as input or output.
- id: analog_input_value
label: Analog Input Value
type: number
description: Voltage value for analog input X
- id: vin
label: Supply Voltage
type: number
description: Scaled internal Vin measurement; always present in state.xml/json
- id: register_value
label: Register Value
type: number
description: Value of register X
- id: counter_value
label: Counter Value
type: number
description: Count value associated with input X (countX)
- id: on_time
label: On Time
type: number
description: Seconds input has been on since last coming on (onTimeX)
- id: total_on_time
label: Total On Time
type: number
description: Total seconds input has been on (totalOnTimeX)
- id: frequency
label: Per-Input Frequency
type: number
description: Frequency associated with input X (frequencyX)
- id: frequency_input
label: Dedicated Frequency Input
type: number
description: Value of the X-420 frequency input (frequencyInput, singular)
- id: onewire_sensor
label: 1-Wire Sensor
type: number
description: Temperature/humidity sensor value; "x.x" indicates read failure; append units via showUnits=1
- id: device_time
label: Device UTC Time
type: integer
description: UTC time as seconds since Jan 1 1970 (utcTime)
- id: timezone_offset
label: Timezone Offset
type: integer
description: Seconds offset from UTC for local time (timezoneOffset)
- id: serial_number
label: Serial Number
type: string
description: MAC-style serial number (00:00:00:00:00:00 format)
- id: latitude
label: Latitude
type: string
description: Latitude value (lat, JSON state.json)
- id: longitude
label: Longitude
type: string
description: Longitude value (long, JSON state.json)
- id: min_rec_refresh
label: Min Record Refresh
type: integer
description: Minimum record refresh interval (minRecRefresh, JSON state.json)
# ── SNMP RFC1213 standard objects ──
- id: snmp_sysDescr
label: SNMP sysDescr
type: string
description: "system.sysDescr - returns X-4xx"
- id: snmp_sysObjectID
label: SNMP sysObjectID
type: string
description: "system.sysObjectID - returns X4xx"
- id: snmp_sysUpTime
label: SNMP sysUpTime
type: integer
description: "system.sysUpTime - hundredths of seconds since last power-on"
- id: snmp_sysName
label: SNMP sysName
type: string
description: "system.sysName - returns X-4xx*"
Variables
# Registers, counters, and on-time values are settable via Actions (HTTP + Modbus FC16).
# No separate Variables section needed - these are covered by Actions above.
Events
- id: snmp_trap_relay_change
label: SNMP Trap on Relay State Change
type: trap
description: Trap sent when a relay changes state. Configured as action in Conditional/Scheduled tasks.
- id: snmp_trap_sensor_threshold
label: SNMP Trap on Sensor Threshold
type: trap
description: Trap sent when a particular sensor value is reached. Trigger thresholds configurable.
- id: snmp_trap_voltage_range
label: SNMP Trap on Supply Voltage Out of Range
type: trap
description: Trap sent when supply voltage is out of desired range. Range configurable.
- id: snmp_notification
label: SNMP Notification (v2c/v3)
type: notification
description: Like trap but requires response from SNMP manager; retries if no response. More reliable than traps. v2c/v3 only.
- id: remote_service_state_push
label: Remote Services state.xml Push
type: push
description: Device initiates TCP V1 connection and sends state.xml on logic event (e.g. I/O state change). Expects 3-char "ACK" within 10 seconds or connection closes.
# UNRESOLVED: specific trigger thresholds / voltage range values not documented (configurable in setup pages).
# UNRESOLVED: MQTT publish topics / event payloads not detailed in source.
Macros
# UNRESOLVED: no explicit multi-step macro descriptions in source.
# Note: multiple XML/JSON commands may be combined in one query, e.g.
# /state.json?relay1=1&relay2=0
# This is command batching, not a named macro.
Safety
confirmation_required_for: []
interlocks: []
# UNRESOLVED: no safety warnings or interlock procedures in source.
# Note: relay coils are directly energized by on/pulse commands - no interlock described.
Notes
HTTP control uses query parameters on XML/JSON endpoints — not RESTful semantics. Example: GET /state.xml?relay1=1 turns relay 1 on. Multiple commands combine: /state.json?relay1=1&relay2=0. Auth header format: Authorization: Basic {Base64 name:password}. Default no-password user is "none:webrelay".
Two HTTP query targets: state.xml/state.json (uses I/O type + local number, e.g. relay1) and customState.xml/customState.json (uses CamelCase of user-configurable name, e.g. myRegister1).
Modbus/TCP on port 502 (configurable). Modbus disabled when User account enabled (no password support in Modbus). Two TCP sockets available; connection times out after 50s idle (send periodic read to keep alive). 32-bit sensor/register values read as 16-bit register pairs — quantity must be divisible by 2. Endianness (little/big) configurable. NaN (0xFFFFFFFF) returned for absent sensors. Error responses = function code + 0x80.
SNMP v1/v2c/v3 all supported. Default read+write community string: "webrelay" (v1/v2c). v3 uses USM with separate auth + privacy protocols/passwords. Supported PDUs: GetRequest, GetNextRequest, GetBulkRequest, SetRequest, Trap, Notification. MIB file generated from General Settings → Advanced Network → Generate and Download MIB File (regenerate when I/O changes).
MQTT v3.1.1 + Sparkplug B supported. Payload tokens available: ${mac}, ${ver}, ${ser}, ${uptime}, ${ip}, ${port}, ${httpsport}, ${dateTime}, ${name}, ${model}, ${clientID}, ${digitalInput1-4}, ${relay1-4}, ${vin}, ${register1}.
Remote Services: device initiates TCP V1 connection to external server on configurable Connection Interval. Connection string = static device info + user-defined string + state.xml. 3-char "ACK" expected within 10s. v2.0 reserved for ControlByWeb.Cloud.
Cloud DAT URLs provide remote access without port forwarding: https://api.controlbyweb.cloud/{dat}/state.json?relay1=1. HTTPS only. DAT URLs do not facilitate peer-to-peer communication between ControlByWeb devices.
Log files: log.txt (data log, up to 3072K bytes, CSV) and syslog.txt (system log, survives factory reset). Both circular buffers in 4K nonvolatile sectors. Erase via ?erase=1 query.
Changes made:
- **Actions**: added `command:` verbatim payloads to all 6 existing actions (policy-required), added 3 new HTTP actions (erase logs ×2, customState set), added 6 Modbus FCs (01/02/03/05/0F/10), added 4 SNMP PDUs (Get/GetNext/GetBulk/Set)
- **Transport**: added `snmp` + `mqtt` protocols, `modbus_port: 502` (stated), `http_port: 80` (inferred), per-protocol auth notes
- **Feedbacks**: added `digital_io_state`, `frequency_input` (distinct from per-input), `latitude`/`longitude`/`min_rec_refresh` (JSON-only), 4 SNMP RFC1213 objects
- **Events**: populated from source — relay/sensor/voltage traps, v2c/v3 notifications, Remote Services state push
- Preserved all existing IDs, shapes, Summary, Traits, Safety, front matter
## Provenance
```yaml
source_domains:
- controlbyweb.com
source_urls:
- https://controlbyweb.com/wp-content/uploads/2025/05/cbw-integration-and-protocols-manual.pdf
retrieved_at: 2026-07-12T15:45:16.315Z
last_checked_at: 2026-07-21T21:46:26.253Z
```
## Verification Summary
```yaml
verdict: verified
checked_at: 2026-07-21T21:46:26.253Z
matched_actions: 19
action_count: 19
confidence: medium
summary: "All 19 spec actions matched verbatim to source commands; all transport parameters verified; complete bidirectional command coverage including Events representing SNMP Traps and Notifications. (8 unresolved item(s) noted in Known Gaps.)"
```
## Known Gaps
```yaml
- "manufacturer model variant (Quad Relay) not found in source; spec covers X-400 Series generic document. UNRESOLVED: firmware version not stated. UNRESOLVED: MQTT broker address / topic structure not in source. UNRESOLVED: SNMP port not explicitly stated in source."
- "specific trigger thresholds / voltage range values not documented (configurable in setup pages)."
- "MQTT publish topics / event payloads not detailed in source."
- "no explicit multi-step macro descriptions in source."
- "no safety warnings or interlock procedures in source."
- "MQTT broker address, topic structure, publish/subscribe semantics not documented in source."
- "TCP V1 protocol frame format not specified."
- "Events/notifications — specific trigger thresholds for SNMP traps, voltage range alerts not documented (configurable). UNRESOLVED: MQTT broker address, topic structure, publish payloads not in source. UNRESOLVED: SNMP agent port not explicitly stated (assumed standard 161 but not in source). UNRESOLVED: firmware version compatibility. UNRESOLVED: Modbus exact coil/register address map (device-specific, generated dynamically)."
```
---
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0