Summary
The ControlByWeb X412 is a network-attached relay/IO module controllable via HTTP GET requests to state.xml/state.json and customState.xml/customState.json plus the Modbus/TCP function codes documented for the X-400 Series platform. It also supports SNMP, MQTT, Remote Services, and the optional ControlByWeb Cloud API.
Transport
protocols:
- tcp # inferred from source's TCP/IP connection description
addressing:
port: 80 # default web server port; source also shows configurable port 8000
base_url: "http://{device-ip}/state.xml"
auth:
type: basic # source documents Base64-encoded Authorization: Basic when User account is enabled
Traits
- powerable
- routable
- queryable
- levelable
Actions
# HTTP GET interface - works on both state.xml/json and customState.xml/json.
# Path variants: /state.xml, /state.json, /customState.xml, /customState.json
- id: relay_off
label: Turn Relay X OFF
kind: action
command: "GET /state.xml?relay{X}=0 HTTP/1.1"
params:
- name: X
type: integer
description: Relay number (1-based, e.g. relay1)
- id: relay_on
label: Turn Relay X ON
kind: action
command: "GET /state.xml?relay{X}=1 HTTP/1.1"
params:
- name: X
type: integer
description: Relay number (1-based)
- id: relay_pulse
label: Pulse Relay X
kind: action
command: "GET /state.xml?relay{X}=2 HTTP/1.1"
params:
- name: X
type: integer
description: Relay number (1-based); pulse duration defaults to 1.5 s
- id: relay_pulse_custom
label: Pulse Relay X for N seconds
kind: action
command: "GET /state.json?pulseTime{X}={seconds}&relay{X}=2 HTTP/1.1"
params:
- name: X
type: integer
description: Relay number (1-based)
- name: seconds
type: integer
description: Pulse duration in seconds; e.g. 5 or 15
- id: set_on_time
label: Set onTime X
kind: action
command: "GET /state.xml?onTime{X}={value} HTTP/1.1"
params:
- name: X
type: integer
description: Input/output number (1-based)
- name: value
type: number
description: Time in seconds (e.g. 0 to reset, 5 to set)
- id: set_total_on_time
label: Set totalOnTime X
kind: action
command: "GET /state.xml?totalOnTime{X}={value} HTTP/1.1"
params:
- name: X
type: integer
description: Input/output number (1-based)
- name: value
type: number
description: Time in seconds (e.g. 0 to reset, 5 to set)
- id: set_counter
label: Set counter X
kind: action
command: "GET /state.json?count{X}={value} HTTP/1.1"
params:
- name: X
type: integer
description: Counter number (1-based, e.g. count1)
- name: value
type: number
description: Counter value to set (e.g. 200)
- id: set_register
label: Set register X
kind: action
command: "GET /state.xml?register{X}={value} HTTP/1.1"
params:
- name: X
type: integer
description: Register number (1-based, e.g. register1)
- name: value
type: number
description: Register value (numeric, e.g. 10.5, 25)
- id: multi_command
label: Send multiple commands in one request
kind: action
command: "GET /state.xml?relay{X1}={v1}&relay{X2}={v2} HTTP/1.1"
params:
- name: args
type: string
description: One or more "ioNameX=value" pairs joined with &, e.g. relay1=1&relay2=0
- id: custom_state_set
label: Set IO via customState using configured name
kind: action
command: "GET /customState.xml?{camelCaseName}={value} HTTP/1.1"
params:
- name: camelCaseName
type: string
description: The configured camelCase IO name from customState.xml (e.g. myRegister1)
- name: value
type: string
description: Value to assign
- id: read_state_xml
label: Read current state (XML)
kind: query
command: "GET /state.xml HTTP/1.1"
params: []
- id: read_state_json
label: Read current state (JSON)
kind: query
command: "GET /state.json HTTP/1.1"
params: []
- id: read_custom_state_xml
label: Read custom state (XML)
kind: query
command: "GET /customState.xml HTTP/1.1"
params: []
- id: read_custom_state_json
label: Read custom state (JSON)
kind: query
command: "GET /customState.json HTTP/1.1"
params: []
- id: read_log_txt
label: Read data log file
kind: query
command: "GET /log.txt HTTP/1.1"
params: []
- id: erase_log_txt
label: Erase data log file
kind: action
command: "GET /log.txt?erase=1 HTTP/1.1"
params: []
- id: read_syslog
label: Read system log file
kind: query
command: "GET /syslog.txt HTTP/1.1"
params: []
- id: erase_syslog
label: Erase system log file
kind: action
command: "GET /syslog.txt?erase=1 HTTP/1.1"
params: []
# --- Modbus/TCP (slave) on port 502 ---
- id: modbus_read_coils
label: Modbus Read Coils (FC 01)
kind: query
command: "Modbus FC 0x01 - Read Relays and Digital IO configured as outputs"
params:
- name: start_address
type: integer
description: Per Modbus Address Table in Advanced Network setup
- name: coil_quantity
type: integer
description: Per Modbus Address Table; multiple outputs may be read at once
- id: modbus_read_discrete_inputs
label: Modbus Read Discrete Inputs (FC 02)
kind: query
command: "Modbus FC 0x02 - Read Digital Inputs and Digital IO configured as inputs"
params:
- name: start_address
type: integer
description: Per Modbus Address Table
- name: input_quantity
type: integer
description: Per Modbus Address Table
- id: modbus_read_holding_registers
label: Modbus Read Holding Registers (FC 03)
kind: query
command: "Modbus FC 0x03 - Read Vin, sensors, registers, counters, analog inputs (32-bit floats over register pairs)"
params:
- name: start_address
type: integer
description: Per Modbus Address Table; sensor values must be read in pairs of 2
- name: register_quantity
type: integer
description: Must be divisible by 2; values returned as IEEE 754 float (little- or big-endian per config)
- id: modbus_write_single_coil
label: Modbus Write Single Coil (FC 05)
kind: action
command: "Modbus FC 0x05 - 0x00 (Off) or 0xFF (On)"
params:
- name: start_address
type: integer
description: Per Modbus Address Table
- name: value
type: enum
description: 0x00 (Off) or 0xFF (On); response mirrors the requested state
- id: modbus_write_multiple_coils
label: Modbus Write Multiple Coils (FC 15)
kind: action
command: "Modbus FC 0x0F - Set one or more digital outputs/relays; 0x0000=off, 0xFFFF=on (or per-bit pattern like 0xF0)"
params:
- name: start_address
type: integer
description: Per Modbus Address Table
- name: output_quantity
type: integer
description: Quantity of coils; byte count = quantity / 8
- name: value_bytes
type: string
description: 0x0000-0xFFFF pattern of desired output states
- id: modbus_write_multiple_registers
label: Modbus Write Multiple Registers (FC 16)
kind: action
command: "Modbus FC 0x10 - Write 32-bit IEEE 754 floats in register pairs (endianness per Modbus setup)"
params:
- name: start_address
type: integer
description: Per Modbus Address Table
- name: register_quantity
type: integer
description: Must be divisible by 2
# --- PLC Addressing examples cited in source ---
- id: modbus_plc_read_relay1
label: PLC Addressing - Read relay 1
kind: query
command: "Modbus FC 0x01, address 0, PLC address 1"
params: []
- id: modbus_plc_write_relay1
label: PLC Addressing - Write relay 1
kind: action
command: "Modbus FC 0x05 (or 0x0F for multiple), address 0, PLC address 1"
params: []
- id: modbus_plc_pulse_relay1
label: PLC Addressing - Pulse relay 1 (32-bit float seconds)
kind: action
command: "Modbus FC 0x10, address 512-513, PLC address 40513-40514"
params:
- name: pulse_seconds
type: number
description: 32-bit float duration in seconds
- id: modbus_plc_read_input1
label: PLC Addressing - Read input 1
kind: query
command: "Modbus FC 0x02, address 1, PLC address 10002"
params: []
- id: modbus_plc_read_analog_input1
label: PLC Addressing - Read analog input 1
kind: query
command: "Modbus FC 0x03, address 4-5, PLC address 40005-40006, 32-bit float"
params: []
- id: modbus_plc_read_vin
label: PLC Addressing - Read Vin
kind: query
command: "Modbus FC 0x03, address 6-7, PLC address 40007-40008, 32-bit float"
params: []
- id: modbus_plc_read_temperature_sensor
label: PLC Addressing - Read temperature sensor
kind: query
command: "Modbus FC 0x03, address 8-9, PLC address 40009-40010, 32-bit float"
params: []
- id: modbus_plc_read_internal_register
label: PLC Addressing - Read internal register
kind: query
command: "Modbus FC 0x03, address 10-11, PLC address 40011-40012, 32-bit float; write via FC 0x10 same address"
params: []
- id: snmp_get_request
label: SNMP GetRequest
kind: query
command: "GetRequest"
params: UNRESOLVED
- id: snmp_get_next_request
label: SNMP GetNextRequest
kind: query
command: "GetNextRequest"
params: UNRESOLVED
- id: snmp_get_bulk_request
label: SNMP GetBulkRequest
kind: query
command: "GetBulkRequest"
params: UNRESOLVED
- id: snmp_set_request
label: SNMP SetRequest
kind: action
command: "SetRequest"
params: UNRESOLVED
- id: snmp_trap_pdu
label: SNMP Trap
kind: action
command: "Trap"
params: UNRESOLVED
- id: snmp_notification_pdu
label: SNMP Notification
kind: action
command: "Notification"
params: UNRESOLVED
- id: snmp_sys_descr
label: SNMP sysDescr
kind: query
command: "_system.sysDescr_"
params: UNRESOLVED
- id: snmp_sys_object_id
label: SNMP sysObjectID
kind: query
command: "_system.sysObjectID_"
params: UNRESOLVED
- id: snmp_sys_up_time
label: SNMP sysUpTime
kind: query
command: "_system.sysUpTime_"
params: UNRESOLVED
- id: snmp_sys_name
label: SNMP sysName
kind: query
command: "_system.sysName_"
params: UNRESOLVED
- id: cloud_read_state_json
label: Read state JSON through Cloud DAT URL
kind: query
command: "https://api.controlbyweb.cloud/{generated DAT url}/state.json"
params: UNRESOLVED
Feedbacks
- id: digital_input_state
type: enum
values: [off, on] # <digitalInputX>: 0=off, 1=on
query_command: "GET /state.xml HTTP/1.1"
- id: digital_io_state
type: enum
values: [off, on] # <digitalIOX>: 0=off, 1=on
query_command: "GET /state.xml HTTP/1.1"
- id: relay_state
type: enum
values: [off, on] # <relayX>: 0=coil off, 1=coil energized
query_command: "GET /state.xml HTTP/1.1"
- id: on_time
type: number # <onTimeX>: seconds since input last came on
query_command: "GET /state.xml HTTP/1.1"
- id: total_on_time
type: number # <totalOnTimeX>: cumulative on time in seconds
query_command: "GET /state.xml HTTP/1.1"
- id: count
type: number # <countX>: counter value
query_command: "GET /state.xml HTTP/1.1"
- id: frequency
type: number # <frequencyX>: frequency value
query_command: "GET /state.xml HTTP/1.1"
- id: analog_input
type: number # <analogInputX>: raw analog value
query_command: "GET /state.xml HTTP/1.1"
- id: vin
type: number # <vin>: scaled internal Vin measurement
query_command: "GET /state.xml HTTP/1.1"
- id: frequency_input
type: number # <frequencyInput>: X-420 frequency input value (UNRESOLVED: X412-specific applicability)
query_command: "GET /state.xml HTTP/1.1"
- id: register
type: number # <registerX>: register value
query_command: "GET /state.xml HTTP/1.1"
- id: utc_time
type: integer # <utcTime>: seconds since 1970-01-01
query_command: "GET /state.xml HTTP/1.1"
- id: timezone_offset
type: integer # <timezoneOffset>: local offset from utcTime
query_command: "GET /state.xml HTTP/1.1"
- id: serial_number
type: string # <serialNumber>: MAC-format device ID
query_command: "GET /state.xml HTTP/1.1"
- id: one_wire_sensor
type: string # <oneWireSensorX>: "x.x"=read error, otherwise numeric (with optional units via showUnits=1)
query_command: "GET /state.xml HTTP/1.1"
- id: modbus_error_code
type: string # Exception response: 0x81..0x90 + codes 0x01 (unsupported), 0x02 (bad address/quantity), 0x03 (padding/byte count out of range)
Variables
# Settable numeric parameters not otherwise enumerated as discrete actions.
- id: register_value
label: Internal register value
type: number
access: readwrite
notes: "Set via /state.xml?registerX=<value>; read back via /state.xml or Modbus FC 0x03 address 10-11 (PLC 40011-40012)."
- id: on_time_value
label: onTime value
type: number
access: readwrite
notes: "Set via /state.xml?onTimeX=<value> (seconds)."
- id: total_on_time_value
label: totalOnTime value
type: number
access: readwrite
notes: "Set via /state.xml?totalOnTimeX=<value> (seconds)."
- id: counter_value
label: Counter value
type: number
access: readwrite
notes: "Set via /state.json?countX=<value>."
- id: pulse_time
label: Pulse duration override
type: number
access: write
notes: "Set via /state.json?pulseTimeX=<seconds> before relayX=2 in same request."
- id: min_rec_refresh
label: Min Rec Refresh
type: string # appears in state.json examples; value not parsed
Events
- id: snmp_trap
type: notification
description: "SNMP traps (PDU Trap) sent on relay state change, sensor threshold trip, or supply voltage out of range. Configured as actions under Conditional and Scheduled tasks."
- id: snmp_notification
type: notification
description: "SNMP Notifications (SNMP v2c/v3) - trap variant requiring acknowledged response with retries."
- id: remote_services_state_push
type: notification
description: "When Remote Services is enabled and a logic event triggers 'send state', state.xml is pushed to the external server over the open TCP V1 connection."
- id: mqtt_publish
type: notification
description: "Device publishes IO/system state to the configured MQTT broker per Broker tab setup; payloads use tokens listed in Section 4.1.4."
Safety
confirmation_required_for: []
interlocks: []
# UNRESOLVED: source contains no explicit safety warnings, interlocks, or power-on sequencing procedures.
Notes
- The X412 is part of the ControlByWeb X-400 Series; this spec reflects the family-level behavior in the source. Per-device IO count (relays, digital inputs, analog inputs, etc.) and the precise Modbus address map depend on the X412 module's configuration and must be read via the device's Setup pages (Advanced Network > View Modbus Address Table).
- HTTP password handling: if the User account is enabled, the request uses a Base64-encoded Authorization: Basic header. The example decodes to none:webrelay; actual credentials are user-configured.
- Relay pulse semantics: relay{X}=2 triggers a pulse of the configured Pulse Duration (1.5 s default). pulseTime{X}=N overrides the duration for that single request only and must appear before relay{X}=2.
- Multiple commands may be combined in one URL via &. The same combining works on customState endpoints using configured camelCase names.
- Modbus/TCP: the device is a slave on port 502 (configurable). Modbus is disabled when the User account is enabled. Idle connections time out after 50 s without data; send periodic reads to keep them open. Only two simultaneous TCP socket connections are allowed.
- Modbus 32-bit floating-point values are IEEE 754; sensor values must be read in pairs of 2. Endianness is configurable under Advanced Network. Example: 81.25 °C is encoded as 0x800042A2 in little-endian.
- UNRESOLVED: Section 2.1.3 prints 0x16 for the relay-pulse and internal-register-write PLC examples, while Section 2.1.9 defines Write Multiple Registers as decimal function code 16 (0x10). The existing commands retain the formally documented 0x10.
- Modbus error framing: error responses use the original function code OR'd with 0x80 (e.g. 0x01 → 0x81). Common exception codes: 0x01 function not supported, 0x02 incorrect start address/quantity, 0x03 padding/byte-count out of range.
- Log endpoints (/log.txt, /syslog.txt) require setup username/password when accessing the system log; the data log requires the user password when the User account is enabled.
- The source documents MQTT v3.1.1 publish/subscribe plus Sparkplug B, and an optional ControlByWeb Cloud API using DAT URLs. No publish/subscribe topic semantics, DAT URL generation, or credential format are specified beyond the example URL.
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/2025/05/400-series-users-manual.pdf
- https://controlbyweb.com/wp-content/uploads/2025/04/x-412-v19.7.pdf
- https://controlbyweb.com/support/cbw-integration-manual/
- https://controlbyweb.com/support/manuals/x-400-users-manual/
retrieved_at: 2026-07-01T14:22:48.995Z
last_checked_at: 2026-10-07T11:17:47.097Z
Verification Summary
verdict: verified
checked_at: 2026-10-07T11:17:47.097Z
matched_actions: 58
action_count: 58
confidence: medium
summary: "All 58 action units match source commands and transport values are supported; the source's 0x16/0x015 PLC-example typos are reconciled by the spec to the formal 0x10/0x0F definitions. (4 unresolved item(s) noted in Known Gaps.)"
Known Gaps
- "Modbus address map is device-config-dependent (the source refers to a per-device \"View Modbus Address Table\" button). The exact coil/register/discrete-input addresses for a specific X412 configuration cannot be enumerated without that page."
- "X412-specific applicability)"
- "source contains no explicit safety warnings, interlocks, or power-on sequencing procedures."
- "Section 2.1.3 prints 0x16 for the relay-pulse and internal-register-write PLC examples, while Section 2.1.9 defines Write Multiple Registers as decimal function code 16 (0x10). The existing commands retain the formally documented 0x10."
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0