{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bus-diagram.local/schema/scenario-v2.schema.json",
  "title": "ScenarioV2",
  "description": "BusDiagram scenario, format 2. Generated by `npm run schema`.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "formatVersion",
    "lines",
    "devices"
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Schema path for editor completion; ignored by BusDiagram."
    },
    "formatVersion": {
      "const": 2,
      "description": "Format version 2. Without this field, the file is read as legacy format 1."
    },
    "title": {
      "type": "string",
      "description": "Title displayed in the toolbar."
    },
    "description": {
      "type": "string",
      "description": "Description displayed below the toolbar."
    },
    "lines": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/line"
      },
      "description": "Twisted-pair lines. Multiple lines add line couplers and main lines; multiple areas add a backbone."
    },
    "clock": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "start"
      ],
      "description": "Simulated clock: local date and time at simulation start, and acceleration. Clock masters, time switches, and time windows use it.",
      "properties": {
        "start": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2})?$",
          "description": "Local date and time at start, such as 2026-09-28T21:58:00 (no time zone)."
        },
        "speed": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 3600,
          "default": 1,
          "description": "Clock seconds per simulated second; 60 makes one minute pass per second."
        }
      }
    },
    "rooms": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/room"
      },
      "description": "Rooms: thermal state shared by assigned thermostats, window contacts, and radiators."
    },
    "topology": {
      "type": "object",
      "description": "Topology levels and coupler settings. Without this field, a main line appears with two lines in an area and a backbone with two areas.",
      "additionalProperties": false,
      "properties": {
        "backbone": {
          "type": "boolean",
          "description": "Backbone line 0.0 and area couplers A.0.0, even with one area."
        },
        "mainLines": {
          "type": "boolean",
          "description": "Area main lines A.0 and line couplers A.L.0, even with one line."
        },
        "ip": {
          "enum": [
            "areaCouplers",
            "lineCouplers"
          ],
          "description": "IP network: KNXnet/IP routers replace area couplers (areaCouplers, A.0.0) or line couplers (lineCouplers, A.L.0). Devices with medium IP connect to it."
        },
        "areas": {
          "type": "array",
          "description": "Area names.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "address"
            ],
            "properties": {
              "address": {
                "type": "integer",
                "minimum": 1,
                "maximum": 15,
                "description": "Area number."
              },
              "name": {
                "type": "string",
                "description": "Area name displayed in the diagram, for example “Building A”."
              }
            }
          }
        },
        "couplers": {
          "type": "array",
          "description": "Group-telegram routing settings for couplers and routers; filtering is the default.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "address"
            ],
            "properties": {
              "address": {
                "type": "string",
                "pattern": "^([0-9]|1[0-5])\\.([0-9]|1[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
                "description": "Coupler address: A.L.0 for a line or A.0.0 for an area."
              },
              "name": {
                "type": "string",
                "description": "Display name."
              },
              "down": {
                "enum": [
                  "filter",
                  "route",
                  "block"
                ],
                "description": "Primary-to-secondary direction: filter by association table, route everything, or block everything."
              },
              "up": {
                "enum": [
                  "filter",
                  "route",
                  "block"
                ],
                "description": "Secondary → primary line: filter, route, or block."
              }
            }
          }
        }
      }
    },
    "ipRouter": {
      "type": "object",
      "description": "Legacy single-router IP network setting; prefer topology.ip. The address must belong to a coupler (A.L.0 or A.0.0).",
      "additionalProperties": false,
      "required": [
        "address"
      ],
      "properties": {
        "address": {
          "type": "string",
          "pattern": "^([0-9]|1[0-5])\\.([0-9]|1[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
          "description": "Router individual address."
        },
        "name": {
          "type": "string",
          "description": "Display name; defaults to “KNX/IP router”."
        }
      }
    },
    "groupAddresses": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/groupAddress"
      },
      "description": "Declared group addresses: names and DPTs displayed in the monitor and inherited by associated objects."
    },
    "devices": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/device"
      },
      "description": "Devices in cable order on their line; order determines propagation delay."
    },
    "options": {
      "type": "object",
      "description": "Initial simulation settings.",
      "additionalProperties": false,
      "properties": {
        "speed": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Initial speed (1 = normal simulated time, 0.35 = slower)."
        },
        "filterTables": {
          "type": "boolean",
          "description": "Show coupler filter tables on startup."
        }
      }
    }
  },
  "$defs": {
    "dpt": {
      "description": "Datapoint type: a simulated DPT, or another standard DPT that passive and display devices show without simulating it.",
      "anyOf": [
        {
          "oneOf": [
            {
              "const": "1.001",
              "description": "Switch"
            },
            {
              "const": "1.002",
              "description": "Boolean"
            },
            {
              "const": "1.003",
              "description": "Enable"
            },
            {
              "const": "1.005",
              "description": "Alarm"
            },
            {
              "const": "1.007",
              "description": "Step"
            },
            {
              "const": "1.008",
              "description": "Up/Down"
            },
            {
              "const": "1.009",
              "description": "Open/Closed"
            },
            {
              "const": "1.010",
              "description": "Start/Stop"
            },
            {
              "const": "1.011",
              "description": "State"
            },
            {
              "const": "1.012",
              "description": "Invert"
            },
            {
              "const": "1.017",
              "description": "Trigger"
            },
            {
              "const": "1.018",
              "description": "Occupation"
            },
            {
              "const": "1.019",
              "description": "Window/Door"
            },
            {
              "const": "1.100",
              "description": "Heating/Cooling"
            },
            {
              "const": "2.001",
              "description": "Priority control"
            },
            {
              "const": "3.007",
              "description": "Relative dimming"
            },
            {
              "const": "5.001",
              "description": "Percentage"
            },
            {
              "const": "5.004",
              "description": "Percentage (0–255)"
            },
            {
              "const": "5.010",
              "description": "Counter"
            },
            {
              "const": "17.001",
              "description": "Scene number"
            },
            {
              "const": "20.102",
              "description": "HVAC mode"
            },
            {
              "const": "9.001",
              "description": "Temperature"
            },
            {
              "const": "9.002",
              "description": "Temperature difference"
            },
            {
              "const": "9.004",
              "description": "Lux"
            },
            {
              "const": "9.005",
              "description": "Wind speed"
            },
            {
              "const": "9.007",
              "description": "Humidity"
            },
            {
              "const": "9.008",
              "description": "Air quality"
            },
            {
              "const": "9.024",
              "description": "Power (kW)"
            },
            {
              "const": "9.028",
              "description": "Wind speed (km/h)"
            },
            {
              "const": "7.600",
              "description": "Colour temperature"
            },
            {
              "const": "10.001",
              "description": "Time of day"
            },
            {
              "const": "11.001",
              "description": "Date"
            },
            {
              "const": "13.010",
              "description": "Active energy"
            },
            {
              "const": "13.013",
              "description": "Active energy (kWh)"
            },
            {
              "const": "14.056",
              "description": "Power"
            }
          ]
        },
        {
          "type": "string",
          "pattern": "^\\d{1,3}\\.\\d{3}$",
          "description": "DPT shown but not simulated (passive/v1 and display/v1 objects only): its value stays unknown or raw."
        }
      ]
    },
    "line": {
      "type": "object",
      "description": "TP line.",
      "additionalProperties": false,
      "required": [
        "address"
      ],
      "properties": {
        "address": {
          "type": "string",
          "pattern": "^([1-9]|1[0-5])\\.([1-9]|1[0-5])$",
          "description": "Line address in area.line form, such as 1.1; values 1–15. Main lines and backbone come from topology."
        },
        "name": {
          "type": "string",
          "description": "Name displayed under the bus, for example “Example line”."
        },
        "extension": {
          "type": "object",
          "description": "Line extension: repeater or segment coupler to a downstream segment.",
          "additionalProperties": false,
          "required": [
            "address"
          ],
          "properties": {
            "address": {
              "type": "string",
              "pattern": "^([0-9]|1[0-5])\\.([0-9]|1[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
              "description": "Extension individual address on the line, such as 2.1.64."
            },
            "mode": {
              "enum": [
                "repeater",
                "segmentCoupler"
              ],
              "description": "repeater: forward without filtering; segmentCoupler: use a filter table."
            },
            "switchable": {
              "type": "boolean",
              "description": "Show a repeater/segment-coupler selector in the toolbar."
            },
            "powerSupply": {
              "$ref": "#/$defs/powerSupply",
              "description": "Power supply of the downstream segment; each segment needs its own."
            }
          }
        },
        "powerSupply": {
          "$ref": "#/$defs/powerSupply",
          "description": "Bus power supply of the line, shown on the diagram."
        }
      }
    },
    "powerSupply": {
      "type": "object",
      "description": "KNX power supply with choke. Shown on the diagram; the bus load is not computed.",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "description": "Name, such as a product reference."
        },
        "currentMa": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Rated current in mA, such as 160, 320, 640, or 1280."
        }
      }
    },
    "groupAddress": {
      "type": "object",
      "description": "Declared group address.",
      "additionalProperties": false,
      "required": [
        "address"
      ],
      "properties": {
        "address": {
          "type": "string",
          "pattern": "^([0-9]|[12][0-9]|3[01])/[0-7]/([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
          "description": "Three-level group address (main/middle/subgroup), such as 1/1/1."
        },
        "name": {
          "type": "string",
          "description": "Purpose shown in the monitor and tooltips."
        },
        "dpt": {
          "$ref": "#/$defs/dpt",
          "description": "DPT inherited by objects that do not declare their own."
        }
      }
    },
    "object": {
      "type": "object",
      "description": "Communication object of a device.",
      "additionalProperties": false,
      "required": [
        "id",
        "ga",
        "port",
        "flags"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID unique within the device; buttons, inputs, and indicators may refer to it."
        },
        "name": {
          "type": "string",
          "description": "Name shown on the device card; defaults to the ID."
        },
        "ga": {
          "description": "Associated group addresses: the first is the sending address; others are receive-only. An empty list leaves the object unassociated.",
          "anyOf": [
            {
              "type": "string",
              "pattern": "^([0-9]|[12][0-9]|3[01])/[0-7]/([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
            },
            {
              "type": "array",
              "uniqueItems": true,
              "items": {
                "type": "string",
                "pattern": "^([0-9]|[12][0-9]|3[01])/[0-7]/([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
              }
            }
          ]
        },
        "dpt": {
          "$ref": "#/$defs/dpt",
          "description": "Object DPT; defaults to the DPT of its first declared group address."
        },
        "port": {
          "oneOf": [
            {
              "const": "input",
              "description": "Object written by a button or numeric input, then transmitted."
            },
            {
              "const": "display",
              "description": "Object that receives and displays a value, such as an indicator or supervisor."
            },
            {
              "const": "switch",
              "description": "Channel switch command (0 = off, 1 = on)."
            },
            {
              "const": "status",
              "description": "Relay status feedback, sent after each effective change."
            },
            {
              "const": "move",
              "description": "Shutter: 0 raises toward 0%, 1 lowers toward 100%."
            },
            {
              "const": "stopStep",
              "description": "Shutter: stop when moving; otherwise step by stepPct (0 up, 1 down)."
            },
            {
              "const": "positionCommand",
              "description": "Shutter target position in percent."
            },
            {
              "const": "positionStatus",
              "description": "Actuator's estimated shutter position, sent after stopping."
            },
            {
              "const": "scene",
              "description": "Scene number (bus byte 0–63 represents scenes 1–64); without a channel, apply to all channels."
            },
            {
              "const": "forced",
              "description": "Priority override (2 = force off, 3 = force on, 0/1 = end override)."
            },
            {
              "const": "dim",
              "description": "Relative dimming (3.007): direction bit and step size 1–7; 0 stops dimming."
            },
            {
              "const": "value",
              "description": "Brightness value in percent (5.001); zero switches off."
            },
            {
              "const": "valueStatus",
              "description": "Brightness value feedback (5.001), sent after a transition."
            },
            {
              "const": "error",
              "description": "Lamp or ballast fault in a DALI group (1.005)."
            },
            {
              "const": "broadcastSwitch",
              "description": "Switch all groups with a DALI broadcast."
            },
            {
              "const": "broadcastValue",
              "description": "Set all group levels with a DALI broadcast."
            },
            {
              "const": "generalError",
              "description": "Fault on the DALI line across all groups (1.005)."
            },
            {
              "const": "actualTemp",
              "description": "Thermostat: measured temperature (9.001), sent on change."
            },
            {
              "const": "externalTemp",
              "description": "Thermostat: temperature from an external sensor (9.001), replacing the internal sensor."
            },
            {
              "const": "baseSetpoint",
              "description": "Thermostat: base heating comfort setpoint (9.001)."
            },
            {
              "const": "setpointShift",
              "description": "Thermostat: offset from the base setpoint (9.002, in K)."
            },
            {
              "const": "setpointStatus",
              "description": "Thermostat: current setpoint (9.001)."
            },
            {
              "const": "hvacMode",
              "description": "Thermostat: selected mode (20.102: 0 auto, 1 comfort, 2 standby, 3 economy, 4 protection)."
            },
            {
              "const": "hvacModeStatus",
              "description": "Thermostat: current mode (20.102)."
            },
            {
              "const": "presence",
              "description": "Thermostat: presence requests comfort mode."
            },
            {
              "const": "window",
              "description": "Thermostat: an open window requests priority protection mode."
            },
            {
              "const": "heatCool",
              "description": "Thermostat: 1 selects heating, 0 selects cooling (1.100)."
            },
            {
              "const": "heatCoolStatus",
              "description": "Thermostat: current heating or cooling mode."
            },
            {
              "const": "heatingValue",
              "description": "Thermostat: continuous heating control value (5.001)."
            },
            {
              "const": "heatingSwitch",
              "description": "Thermostat: one-bit heating command (two-point or PWM)."
            },
            {
              "const": "coolingValue",
              "description": "Thermostat: continuous cooling control value (5.001)."
            },
            {
              "const": "coolingSwitch",
              "description": "Thermostat: one-bit cooling command."
            },
            {
              "const": "fault",
              "description": "Heating actuator: missing control value triggers emergency mode (1.005)."
            },
            {
              "const": "contact",
              "description": "Window contact: 1.019 or 1.001 uses 1 for open; 1.009 uses 1 for closed."
            },
            {
              "const": "temperature",
              "description": "Sensor: room temperature (9.001)."
            },
            {
              "const": "power",
              "description": "power"
            },
            {
              "const": "energy",
              "description": "energy"
            },
            {
              "const": "totalPower",
              "description": "totalPower"
            },
            {
              "const": "powerLimit",
              "description": "powerLimit"
            },
            {
              "const": "slatCommand",
              "description": "slatCommand"
            },
            {
              "const": "slatStatus",
              "description": "slatStatus"
            },
            {
              "const": "windAlarm",
              "description": "windAlarm"
            },
            {
              "const": "brightness",
              "description": "brightness"
            },
            {
              "const": "colourTemperature",
              "description": "colourTemperature"
            },
            {
              "const": "colourTemperatureStatus",
              "description": "colourTemperatureStatus"
            },
            {
              "const": "logicIn",
              "description": "logicIn"
            },
            {
              "const": "enable",
              "description": "enable"
            },
            {
              "const": "time",
              "description": "time"
            },
            {
              "const": "logicOut",
              "description": "logicOut"
            },
            {
              "const": "wind",
              "description": "wind"
            },
            {
              "const": "outdoorTemp",
              "description": "outdoorTemp"
            },
            {
              "const": "sunProtection",
              "description": "sunProtection"
            },
            {
              "const": "humidity",
              "description": "humidity"
            },
            {
              "const": "co2",
              "description": "co2"
            },
            {
              "const": "co2Alarm",
              "description": "co2Alarm"
            },
            {
              "const": "humidityAlarm",
              "description": "humidityAlarm"
            },
            {
              "const": "ventilation",
              "description": "ventilation"
            },
            {
              "const": "date",
              "description": "date"
            },
            {
              "const": "output",
              "description": "output"
            },
            {
              "const": "command",
              "description": "command"
            }
          ],
          "description": "Object role in the device's behavior."
        },
        "channel": {
          "type": "string",
          "description": "Channel ID within the device; required by actuator ports."
        },
        "value": {
          "type": [
            "number",
            "null"
          ],
          "description": "Initial value (zero by default; unknown for a display port)."
        },
        "flags": {
          "type": "object",
          "description": "Modeled flags: W write, T transmit, R read, U update. C communication is always enabled.",
          "additionalProperties": false,
          "required": [
            "W",
            "T"
          ],
          "properties": {
            "W": {
              "type": "boolean",
              "description": "Accept a received GroupValueWrite; otherwise the telegram is ignored and the value stays unchanged."
            },
            "T": {
              "type": "boolean",
              "description": "Allow the behavior to transmit this object; otherwise its value may change locally without a telegram."
            },
            "R": {
              "type": "boolean",
              "description": "Answer a GroupValueRead received on any of the object's addresses, on its sending address; enabled by default for status ports."
            },
            "U": {
              "type": "boolean",
              "description": "Apply a received GroupValueResponse as an update; enabled by default for display ports."
            }
          }
        }
      }
    },
    "button": {
      "type": "object",
      "description": "Button drawn on the device card; use press or a short/long pair.",
      "additionalProperties": false,
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID unique among buttons and inputs on this device."
        },
        "label": {
          "type": "string",
          "description": "Label on the button; for example “Key 2 · Down” displays Key 2 prominently."
        },
        "icon": {
          "oneOf": [
            {
              "const": "on",
              "description": "I: on"
            },
            {
              "const": "off",
              "description": "O: off"
            },
            {
              "const": "toggle",
              "description": "I/O: toggle"
            },
            {
              "const": "up",
              "description": "▲: up"
            },
            {
              "const": "down",
              "description": "▼: down"
            },
            {
              "const": "updown",
              "description": "▲▼: shutter on one key"
            },
            {
              "const": "scene",
              "description": "☰: scene"
            },
            {
              "const": "presence",
              "description": "◉: presence"
            },
            {
              "const": "clock",
              "description": "◷: clock"
            },
            {
              "const": "dimUp",
              "description": "☼+: brighten"
            },
            {
              "const": "dimDown",
              "description": "☼−: dim"
            }
          ],
          "description": "Icon inferred from the action when omitted."
        },
        "press": {
          "type": "object",
          "description": "Action taken immediately on press; cannot be combined with short/long.",
          "additionalProperties": false,
          "required": [
            "object",
            "value"
          ],
          "properties": {
            "object": {
              "type": "string",
              "description": "ID of an object on the same device (input port, with a group address)."
            },
            "value": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "const": "toggle"
                }
              ],
              "description": "Value within the object's DPT range, or “toggle” to invert a one-bit local object value."
            }
          }
        },
        "short": {
          "type": "object",
          "description": "Action on release before the long-press threshold.",
          "additionalProperties": false,
          "required": [
            "object",
            "value"
          ],
          "properties": {
            "object": {
              "type": "string",
              "description": "ID of an object on the same device (input port, with a group address)."
            },
            "value": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "const": "toggle"
                }
              ],
              "description": "Value within the object's DPT range, or “toggle” to invert a one-bit local object value."
            }
          }
        },
        "long": {
          "type": "object",
          "description": "Action after the long-press threshold (0.5 s by default).",
          "additionalProperties": false,
          "required": [
            "object",
            "value"
          ],
          "properties": {
            "object": {
              "type": "string",
              "description": "ID of an object on the same device (input port, with a group address)."
            },
            "value": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "const": "toggle"
                }
              ],
              "description": "Value within the object's DPT range, or “toggle” to invert a one-bit local object value."
            }
          }
        },
        "release": {
          "type": "object",
          "description": "Action on release after a long press, such as stopping DPT 3.007 dimming.",
          "additionalProperties": false,
          "required": [
            "object",
            "value"
          ],
          "properties": {
            "object": {
              "type": "string",
              "description": "ID of an object on the same device (input port, with a group address)."
            },
            "value": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "const": "toggle"
                }
              ],
              "description": "Value within the object's DPT range, or “toggle” to invert a one-bit local object value."
            }
          }
        },
        "led": {
          "type": "string",
          "description": "Object whose value lights the button indicator."
        }
      },
      "anyOf": [
        {
          "required": [
            "press"
          ]
        },
        {
          "required": [
            "short"
          ]
        },
        {
          "required": [
            "long"
          ]
        }
      ],
      "not": {
        "anyOf": [
          {
            "required": [
              "press",
              "short"
            ]
          },
          {
            "required": [
              "press",
              "long"
            ]
          }
        ]
      }
    },
    "input": {
      "type": "object",
      "description": "Numeric input drawn on the device card.",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "object"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID unique among buttons and inputs on this device."
        },
        "type": {
          "const": "number",
          "description": "The only input type is number."
        },
        "label": {
          "type": "string",
          "description": "Label displayed above the input."
        },
        "object": {
          "type": "string",
          "description": "Input object with a group address; written and transmitted on submission."
        },
        "min": {
          "type": "number",
          "description": "Minimum value; defaults to the DPT minimum."
        },
        "max": {
          "type": "number",
          "description": "Maximum value; defaults to the DPT maximum."
        },
        "step": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Input step; defaults to 1."
        }
      }
    },
    "room": {
      "type": "object",
      "description": "Room thermal model: heat loss toward outside temperature plus equipment output; an open window increases loss.",
      "additionalProperties": false,
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID referenced by room assignments."
        },
        "name": {
          "type": "string",
          "description": "Display name; defaults to the ID."
        },
        "temperatureC": {
          "type": "number",
          "minimum": -30,
          "maximum": 60,
          "description": "Initial indoor temperature in °C; defaults to 20."
        },
        "outsideTemperatureC": {
          "type": "number",
          "minimum": -30,
          "maximum": 50,
          "description": "Outside temperature in °C; defaults to 5."
        },
        "windowOpen": {
          "type": "boolean",
          "description": "Window initially open."
        },
        "timeConstantMs": {
          "type": "integer",
          "minimum": 1000,
          "maximum": 86400000,
          "description": "Thermal loss time constant in simulated milliseconds; defaults to 300,000 ms."
        }
      }
    },
    "equipment": {
      "type": "object",
      "description": "Non-KNX equipment connected to a channel output.",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "anyOf": [
            {
              "enum": [
                "lamp",
                "shutter",
                "dimmableLamp",
                "daliGroup",
                "radiator",
                "fan",
                "appliance"
              ]
            },
            {
              "type": "string",
              "pattern": "^[A-Za-z][\\w.-]*(/v\\d+)?$"
            }
          ],
          "description": "Equipment type: lamp, shutter, or a registered extension type."
        },
        "view": {
          "type": "string",
          "description": "View type; defaults to the equipment type, such as an extension's ledStrip view."
        },
        "room": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "Heated or cooled room ID from rooms; required for a radiator."
        },
        "parameters": {
          "type": "object",
          "description": "Equipment parameters, such as a shutter's actualTravelTimeMs."
        },
        "initialState": {
          "type": "object",
          "description": "Initial physical state, such as a shutter's positionPct."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "type": {
                "const": "lamp"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "powerW": {
                    "title": "Rated power",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100000,
                    "default": 60,
                    "description": "Electrical power (W) drawn while the lamp is on; used by metering actuators. Any switched load can be represented with a matching power."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "on": {
                    "title": "On at start",
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "shutter"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "actualTravelTimeMs"
                ],
                "properties": {
                  "actualTravelTimeMs": {
                    "title": "Actual travel time",
                    "unit": "ms",
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "description": "Actual time for one complete travel (ms): downwards, and upwards unless actualTravelTimeUpMs is set."
                  },
                  "actualTravelTimeUpMs": {
                    "title": "Actual travel time up",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "description": "Actual time for one complete upward travel (ms), when it differs from the downward time."
                  },
                  "slatTravelMs": {
                    "title": "Actual slat rotation time",
                    "unit": "ms",
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "Venetian blind: time for the slats to turn from open to closed (ms). The motor first turns the slats, then moves the blind. 0 describes a roller shutter without slats."
                  },
                  "wiringReversed": {
                    "title": "Motor wired in reverse",
                    "expert": true,
                    "type": "boolean",
                    "default": false,
                    "description": "Physical wiring with the up and down wires swapped: the motor turns opposite to the command. The actuator's “Inverted wiring” parameter compensates it."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "positionPct": {
                    "title": "Actual position at start",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  },
                  "slatPct": {
                    "title": "Actual slat angle at start",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  }
                }
              }
            },
            "required": [
              "type",
              "parameters"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "dimmableLamp"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "powerW": {
                    "title": "Rated power",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100000,
                    "default": 40,
                    "description": "Electrical power (W) at full level; the drawn power is proportional to the level."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "levelPct": {
                    "title": "Initial level",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "daliGroup"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "ballasts": {
                    "title": "Number of ballasts",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 16,
                    "default": 2,
                    "description": "Number of ballasts (luminaires) in the group."
                  },
                  "firstAddress": {
                    "title": "First short address",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 63,
                    "default": 0,
                    "description": "Short DALI address of the first ballast; subsequent ballasts use consecutive addresses."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "levelPct": {
                    "title": "Initial level",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  },
                  "failed": {
                    "title": "Faulty ballasts",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 16,
                    "default": 0,
                    "description": "Number of ballasts initially reporting a lamp or ballast fault."
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "radiator"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "openingTimeMs": {
                    "title": "Valve travel time",
                    "unit": "ms",
                    "type": "integer",
                    "minimum": 1000,
                    "default": 8000,
                    "description": "Time for the valve to open fully (ms). Real valves take about 3 minutes; this is compressed like room time and should be short relative to the PWM period."
                  },
                  "normallyOpen": {
                    "title": "Valve open when de-energised",
                    "type": "boolean",
                    "default": false,
                    "description": "Normally open valve: applying power closes it."
                  },
                  "powerK": {
                    "title": "Heating effect (K)",
                    "type": "number",
                    "minimum": 1,
                    "maximum": 60,
                    "default": 25,
                    "description": "Temperature difference (K) from outdoors that this radiator can maintain alone with the valve fully open."
                  },
                  "emitter": {
                    "title": "Emitter",
                    "type": "string",
                    "enum": [
                      "heating",
                      "cooling"
                    ],
                    "default": "heating",
                    "description": "heating warms the room; cooling cools it, for example a fan coil with condensate drainage. Humidity and condensation are not modeled."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "openPct": {
                    "title": "Initial opening",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "fan"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "powerW": {
                    "title": "Rated power",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100000,
                    "default": 50,
                    "description": "Electrical power (W) at full speed; the drawn power is proportional to the speed."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "levelPct": {
                    "title": "Initial level",
                    "unit": "%",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "appliance"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "powerW": {
                    "title": "Rated power",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100000,
                    "default": 2000,
                    "description": "Electrical power (W) drawn while the appliance is powered."
                  }
                }
              },
              "initialState": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "on": {
                    "title": "On at start",
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        }
      ]
    },
    "channel": {
      "type": "object",
      "description": "Actuator output channel; declare hardware channels even when unused.",
      "additionalProperties": false,
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID unique within the device and referenced by its communication objects."
        },
        "label": {
          "type": "string",
          "description": "Load label, such as L1; use “unused” for an unconnected output."
        },
        "parameters": {
          "type": "object",
          "description": "Behavior-specific channel parameters, such as timer or travel time."
        },
        "initialState": {
          "type": "object",
          "description": "Initial application state of the channel."
        },
        "equipment": {
          "anyOf": [
            {
              "$ref": "#/$defs/equipment"
            },
            {
              "type": "null"
            }
          ],
          "description": "Connected equipment, or null for a free output without a drawn load."
        },
        "scenes": {
          "type": "object",
          "description": "Scene presets: scene number 1–64 maps to a relay state or shutter position.",
          "propertyNames": {
            "pattern": "^([1-9]|[1-5][0-9]|6[0-4])$"
          },
          "additionalProperties": {
            "type": "number"
          }
        }
      }
    },
    "device": {
      "type": "object",
      "description": "KNX device with an individual address, objects, buttons, inputs, and channels.",
      "additionalProperties": false,
      "required": [
        "id",
        "kind",
        "behavior",
        "objects"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "ID unique within the scenario."
        },
        "name": {
          "type": "string",
          "description": "Name shown on the device card; defaults to the ID."
        },
        "address": {
          "type": "string",
          "pattern": "^([0-9]|1[0-5])\\.([0-9]|1[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
          "description": "Individual address in area.line.device form; its line must be declared."
        },
        "kind": {
          "type": "string",
          "description": "Device grouping and rendering: pushButton, switchActuator, shutterActuator, sensor, supervisor, generic…"
        },
        "behavior": {
          "anyOf": [
            {
              "enum": [
                "pushButton/v1",
                "switchActuator/v1",
                "shutterActuator/v1",
                "display/v1",
                "passive/v1",
                "presenceDetector/v1",
                "usbInterface/v1",
                "dimmerActuator/v1",
                "daliGateway/v1",
                "roomThermostat/v1",
                "heatingActuator/v1",
                "windowContact/v1",
                "temperatureSensor/v1",
                "logicGate/v1",
                "weatherStation/v1",
                "airQualitySensor/v1",
                "clockMaster/v1",
                "timeSwitch/v1",
                "systemGateway/v1",
                "energyMeter/v1"
              ]
            },
            {
              "type": "string",
              "pattern": "^[A-Za-z][\\w.-]*(/v\\d+)?$"
            }
          ],
          "description": "Versioned ID of the device's registered behavior."
        },
        "parameters": {
          "type": "object",
          "description": "Device parameters defined by its behavior."
        },
        "medium": {
          "enum": [
            "TP",
            "IP"
          ],
          "description": "Communication medium: TP by default, or IP on the topology.ip network; supervisors default to IP."
        },
        "room": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*$",
          "description": "Assigned room ID; thermostats, window contacts, and temperature sensors observe that room."
        },
        "inFilterTables": {
          "type": "boolean",
          "description": "false: do not include this virtual device in coupler filter tables."
        },
        "downstream": {
          "type": "boolean",
          "description": "Device on the downstream segment of a line extension."
        },
        "description": {
          "type": "string",
          "description": "Explanation shown when selecting the device card."
        },
        "objects": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/object"
          },
          "description": "Communication objects in device-card order."
        },
        "buttons": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/button"
          },
          "description": "Keys (push button, sensor)."
        },
        "inputs": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/input"
          },
          "description": "Numeric inputs."
        },
        "channels": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/channel"
          },
          "description": "Actuator channels (outputs)."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "pushButton/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "longPressMs": {
                    "title": "Long press duration",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 10000,
                    "default": 500,
                    "description": "Press duration that counts as a long press (ms), for keys with short and long actions."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "input",
                          "description": "object sent by a key or input"
                        },
                        {
                          "const": "display",
                          "description": "object receiving a value (indicator or feedback)"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "switchActuator/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "meterIntervalMs": {
                    "title": "Metering send interval",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 1000,
                    "default": 5000,
                    "description": "Cyclic sending of power and energy values (ms), for channels with metering objects."
                  },
                  "powerSendDeltaW": {
                    "title": "Power send threshold",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 10,
                    "description": "Send a power value when it changes by at least this many watts."
                  },
                  "energyTimeScale": {
                    "title": "Energy time scale",
                    "expert": true,
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "default": 60,
                    "description": "Energy counting speed: 60 counts one simulated second as one minute, so that the counter moves visibly; 1 counts real time."
                  },
                  "powerLimitW": {
                    "title": "Total power limit",
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "description": "Total power (W) of the actuator outputs at which the powerLimit object is set, for load shedding (0 disables it)."
                  },
                  "sheddingTimeMs": {
                    "title": "Minimum shedding time",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 20000,
                    "description": "A shed output is switched on again after this time, if its command still requests it; it is shed again if the limit is still exceeded."
                  },
                  "powerLimitHysteresisW": {
                    "title": "Power limit hysteresis",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 50,
                    "description": "The powerLimit object is reset below limit − hysteresis (W)."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "timerMs": {
                          "title": "Timer",
                          "unit": "ms",
                          "type": [
                            "integer",
                            "null"
                          ],
                          "exclusiveMinimum": 0,
                          "default": null,
                          "description": "Timer duration (ms); null disables the timer."
                        },
                        "timerRetrigger": {
                          "title": "Timer retriggering",
                          "expert": true,
                          "type": "string",
                          "enum": [
                            "restart",
                            "none",
                            "add"
                          ],
                          "default": "restart",
                          "description": "When 1 is received during a timer: restart begins a full period, none ignores it, and add extends the period (up to five times)."
                        },
                        "timerWarningMs": {
                          "title": "Switch-off warning",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Time before the timer ends when the output briefly opens for one second as a warning (0 disables the warning)."
                        },
                        "timerOffAllowed": {
                          "title": "Early switch-off by 0",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "Allow a 0 write to switch off before the timer ends; otherwise ignore it."
                        },
                        "loadShedding": {
                          "title": "Shed on power limit",
                          "type": "boolean",
                          "default": false,
                          "description": "Switch this output off while the total power limit of the actuator is exceeded (load shedding)."
                        },
                        "relayMode": {
                          "title": "Relay operating mode",
                          "type": "string",
                          "enum": [
                            "normallyOpen",
                            "normallyClosed"
                          ],
                          "default": "normallyOpen",
                          "description": "normallyOpen closes the contact when the channel is on; normallyClosed closes it when the channel is off, so the load is powered while the switching state is 0. Status feedback reports the switching state, not the contact."
                        },
                        "statusDelayMs": {
                          "title": "Status feedback delay",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 300,
                          "description": "Delay before sending status feedback (ms)."
                        },
                        "afterForcing": {
                          "title": "End of forcing",
                          "expert": true,
                          "type": "string",
                          "enum": [
                            "lastCommand",
                            "previous",
                            "unchanged",
                            "on",
                            "off",
                            "toggle"
                          ],
                          "default": "lastCommand",
                          "description": "State after a priority override ends: follow the latest command received during the override, restore the previous state, keep the forced state, switch on, switch off, or invert the forced state."
                        }
                      }
                    },
                    "initialState": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "on": {
                          "title": "On at start",
                          "type": "boolean",
                          "default": false,
                          "description": "Switching state of the channel when the simulation starts."
                        }
                      }
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "switch",
                          "description": "Channel switch command (0 = off, 1 = on)."
                        },
                        {
                          "const": "status",
                          "description": "Relay status feedback, sent after each effective change."
                        },
                        {
                          "const": "scene",
                          "description": "Scene number (bus byte 0–63 represents scenes 1–64); without a channel, apply to all channels."
                        },
                        {
                          "const": "forced",
                          "description": "Priority override (2 = force off, 3 = force on, 0/1 = end override)."
                        },
                        {
                          "const": "power",
                          "description": "electrical power drawn by the load of the channel: W with 14.056, kW with 9.024"
                        },
                        {
                          "const": "energy",
                          "description": "active energy counted for the channel, sent cyclically: Wh with 13.010, kWh with 13.013"
                        },
                        {
                          "const": "totalPower",
                          "description": "sum of the power of all channels: W with 14.056, kW with 9.024"
                        },
                        {
                          "const": "powerLimit",
                          "description": "1 when the total power reaches powerLimitW, 0 after hysteresis (load shedding)"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "shutterActuator/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "maxProperties": 0
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "estimatedTravelTimeMs"
                      ],
                      "properties": {
                        "estimatedTravelTimeMs": {
                          "title": "Configured travel time",
                          "unit": "ms",
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "description": "Travel time configured in the actuator (ms): downwards, and upwards unless estimatedTravelTimeUpMs is set."
                        },
                        "estimatedTravelTimeUpMs": {
                          "title": "Configured travel time up",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "description": "Upward travel time configured in the actuator (ms), when it differs from the downward time; many actuators take separate times because a shutter rises more slowly than it falls."
                        },
                        "startDelayMs": {
                          "title": "Start delay",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 300,
                          "description": "Delay before starting or reversing motion (ms)."
                        },
                        "statusDelayMs": {
                          "title": "Position feedback delay",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 300,
                          "description": "Delay before sending position feedback (ms)."
                        },
                        "slatTravelMs": {
                          "title": "Configured slat rotation time",
                          "unit": "ms",
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Venetian blind: time configured for the slats to turn from open to closed (ms). A movement first turns the slats; a short press at rest turns them by slatStepPct. 0 for a roller shutter without slats."
                        },
                        "slatStepPct": {
                          "title": "Slat step",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 1,
                          "maximum": 100,
                          "default": 20,
                          "description": "Slat angle change for a short press at rest (%)."
                        },
                        "stepPct": {
                          "title": "Stop/step increment",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 0,
                          "description": "Position step for a stop/step command received at rest by a shutter without slats (%). 0 (default): no movement, as in the KNX stop/step function, where the step turns slats; some actuators move a roller shutter by this amount instead."
                        },
                        "endSupplementPct": {
                          "title": "End-of-travel supplement",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 0,
                          "maximum": 50,
                          "default": 0,
                          "description": "Extra travel time toward 0% or 100%, as a percentage of full travel. The motor runs even if the estimate is already at the limit; the end stop recalibrates the estimate (often 5–10% on a real actuator)."
                        },
                        "invertOutput": {
                          "title": "Inverted wiring",
                          "expert": true,
                          "type": "boolean",
                          "default": false,
                          "description": "Invert the up and down outputs to compensate a motor wired in reverse (shutter parameter wiringReversed). Without such wiring, enabling it makes the shutter move opposite to the commands."
                        }
                      }
                    },
                    "initialState": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "estimatedPositionPct": {
                          "title": "Estimated position at start",
                          "unit": "%",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 0,
                          "description": "Position the actuator assumes at start (0 = fully open, 100 = fully closed); the connected shutter starts at its own position."
                        },
                        "estimatedSlatPct": {
                          "title": "Estimated slat angle at start",
                          "unit": "%",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 0,
                          "description": "Slat angle the actuator assumes at start (venetian blinds)."
                        }
                      }
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "move",
                          "description": "Shutter: 0 raises toward 0%, 1 lowers toward 100%."
                        },
                        {
                          "const": "stopStep",
                          "description": "Shutter: stop when moving; otherwise step by stepPct (0 up, 1 down)."
                        },
                        {
                          "const": "positionCommand",
                          "description": "Shutter target position in percent."
                        },
                        {
                          "const": "positionStatus",
                          "description": "Actuator's estimated shutter position, sent after stopping."
                        },
                        {
                          "const": "scene",
                          "description": "Scene number (bus byte 0–63 represents scenes 1–64); without a channel, apply to all channels."
                        },
                        {
                          "const": "slatCommand",
                          "description": "slat angle (0 % open, 100 % closed) of a venetian blind"
                        },
                        {
                          "const": "slatStatus",
                          "description": "estimated slat angle, sent after stopping"
                        },
                        {
                          "const": "windAlarm",
                          "description": "1 raises the shutter and locks it against other commands; 0 releases it in place"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "display/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "maxProperties": 0
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "display",
                          "description": "Object that receives and displays a value, such as an indicator or supervisor."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "passive/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "maxProperties": 0
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "input",
                          "description": "Object written by a button or numeric input, then transmitted."
                        },
                        {
                          "const": "display",
                          "description": "Object that receives and displays a value, such as an indicator or supervisor."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "presenceDetector/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "holdMs": {
                    "title": "Hold time",
                    "unit": "ms",
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "default": 10000,
                    "description": "Time with no detected presence before sending 0 (ms); restarted on each detection."
                  },
                  "retrigger": {
                    "title": "Hold time restarted by a detection",
                    "expert": true,
                    "type": "boolean",
                    "default": true,
                    "description": "Each activation restarts the timer; otherwise it runs from the first activation."
                  },
                  "sendOnEnd": {
                    "title": "Send 0 at the end",
                    "expert": true,
                    "type": "boolean",
                    "default": true,
                    "description": "Send 0 when the timer expires; otherwise the detector sends only 1 and the actuator handles switch-off."
                  },
                  "brightnessThresholdLux": {
                    "title": "Switch-on brightness threshold",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "description": "A detection switches on only while the entered brightness is below this value (lx); an active presence is still extended. 0 disables the threshold."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "input",
                          "description": "output object: 1 on detection, 0 when the timer expires"
                        },
                        {
                          "const": "brightness",
                          "description": "measured brightness (lx), entered by the reader and sent; there is no light model"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "usbInterface/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "groupAddresses": {
                    "title": "Group addresses assigned to the interface",
                    "type": "string",
                    "default": "",
                    "description": "Group addresses assigned to this interface in the project, separated by spaces or commas. Coupler filter tables include them, so telegrams on these addresses cross couplers to and from the interface. Without them, a coupler filters an address that is not used on the interface side."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "maxItems": 0
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "dimmerActuator/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "maxProperties": 0
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "onLevel": {
                          "title": "Switch-on value",
                          "type": "string",
                          "enum": [
                            "fixed",
                            "last"
                          ],
                          "default": "fixed",
                          "description": "Level when switched on: fixed uses onLevelPct; last restores the level before switch-off."
                        },
                        "onLevelPct": {
                          "title": "Switch-on level",
                          "unit": "%",
                          "type": "number",
                          "minimum": 1,
                          "maximum": 100,
                          "default": 100,
                          "description": "Level reached when a value of 1 is received (fixed mode)."
                        },
                        "dimTimeMs": {
                          "title": "Dimming time",
                          "unit": "ms",
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "default": 5000,
                          "description": "Time for a 0–100% relative dimming transition (DPT 3.007); smaller steps take less time."
                        },
                        "switchFadeMs": {
                          "title": "Fade on switching",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Transition time when switching on or off (0 means immediate)."
                        },
                        "valueFadeMs": {
                          "title": "Fade on value",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Transition time to a received value (5.001) or scene preset."
                        },
                        "minLevelPct": {
                          "title": "Minimum level",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 0,
                          "maximum": 50,
                          "default": 1,
                          "description": "Minimum relative dimming level."
                        },
                        "maxLevelPct": {
                          "title": "Maximum level",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 50,
                          "maximum": 100,
                          "default": 100,
                          "description": "Upper limit for relative dimming and received values."
                        },
                        "dimSwitchesOn": {
                          "title": "Switch on by dimming",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "An increase command turns on a channel that is off."
                        },
                        "dimSwitchesOff": {
                          "title": "Switch off by dimming",
                          "expert": true,
                          "type": "boolean",
                          "default": false,
                          "description": "A decrease command may dim to off; otherwise it stops at the minimum level."
                        },
                        "valueSwitchesOn": {
                          "title": "Switch on by brightness value",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "A brightness value above 0 turns on a channel that is off; otherwise the value is ignored while the channel is off."
                        },
                        "valueSwitchesOff": {
                          "title": "Switch off by brightness value",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "A brightness value of 0 turns the channel off; otherwise the channel dims to its minimum level."
                        },
                        "statusDelayMs": {
                          "title": "Status feedback delay",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 300,
                          "description": "Delay before sending status feedback (ms), after a transition completes."
                        },
                        "minColourK": {
                          "title": "Warmest colour temperature",
                          "expert": true,
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 2700,
                          "description": "Lowest colour temperature (K) of a tunable white channel; lower requests are limited to it."
                        },
                        "maxColourK": {
                          "title": "Coldest colour temperature",
                          "expert": true,
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 6500,
                          "description": "Highest colour temperature (K) of a tunable white channel; higher requests are limited to it."
                        }
                      }
                    },
                    "initialState": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "levelPct": {
                          "title": "Initial level",
                          "unit": "%",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 0,
                          "description": "Level of the output when the simulation starts; 0 is off."
                        },
                        "colourTemperatureK": {
                          "title": "Initial colour temperature",
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 4000,
                          "description": "Colour temperature (K) at start, for a channel with a colourTemperature object."
                        }
                      }
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "switch",
                          "description": "Channel switch command (0 = off, 1 = on)."
                        },
                        {
                          "const": "dim",
                          "description": "Relative dimming (3.007): direction bit and step size 1–7; 0 stops dimming."
                        },
                        {
                          "const": "value",
                          "description": "Brightness value in percent (5.001); zero switches off."
                        },
                        {
                          "const": "status",
                          "description": "Relay status feedback, sent after each effective change."
                        },
                        {
                          "const": "valueStatus",
                          "description": "Brightness value feedback (5.001), sent after a transition."
                        },
                        {
                          "const": "scene",
                          "description": "Scene number (bus byte 0–63 represents scenes 1–64); without a channel, apply to all channels."
                        },
                        {
                          "const": "colourTemperature",
                          "description": "colour temperature setpoint (K) of a tunable white channel"
                        },
                        {
                          "const": "colourTemperatureStatus",
                          "description": "applied colour temperature (K)"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "daliGateway/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "pollMs": {
                    "title": "Ballast polling",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 500,
                    "default": 2000,
                    "description": "Interval between ballast status polls for faults."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "onLevel": {
                          "title": "Switch-on value",
                          "type": "string",
                          "enum": [
                            "fixed",
                            "last"
                          ],
                          "default": "fixed",
                          "description": "Level when switched on: fixed uses onLevelPct; last restores the level before switch-off."
                        },
                        "onLevelPct": {
                          "title": "Switch-on level",
                          "unit": "%",
                          "type": "number",
                          "minimum": 1,
                          "maximum": 100,
                          "default": 100,
                          "description": "Level reached when a value of 1 is received (fixed mode)."
                        },
                        "dimTimeMs": {
                          "title": "Dimming time",
                          "unit": "ms",
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "default": 5000,
                          "description": "Time for a 0–100% relative dimming transition (DPT 3.007); smaller steps take less time."
                        },
                        "switchFadeMs": {
                          "title": "Fade on switching",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Transition time when switching on or off (0 means immediate)."
                        },
                        "valueFadeMs": {
                          "title": "Fade on value",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Transition time to a received value (5.001) or scene preset."
                        },
                        "minLevelPct": {
                          "title": "Minimum level",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 0,
                          "maximum": 50,
                          "default": 1,
                          "description": "Minimum relative dimming level."
                        },
                        "maxLevelPct": {
                          "title": "Maximum level",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 50,
                          "maximum": 100,
                          "default": 100,
                          "description": "Upper limit for relative dimming and received values."
                        },
                        "dimSwitchesOn": {
                          "title": "Switch on by dimming",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "An increase command turns on a channel that is off."
                        },
                        "dimSwitchesOff": {
                          "title": "Switch off by dimming",
                          "expert": true,
                          "type": "boolean",
                          "default": false,
                          "description": "A decrease command may dim to off; otherwise it stops at the minimum level."
                        },
                        "valueSwitchesOn": {
                          "title": "Switch on by brightness value",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "A brightness value above 0 turns on a channel that is off; otherwise the value is ignored while the channel is off."
                        },
                        "valueSwitchesOff": {
                          "title": "Switch off by brightness value",
                          "expert": true,
                          "type": "boolean",
                          "default": true,
                          "description": "A brightness value of 0 turns the channel off; otherwise the channel dims to its minimum level."
                        },
                        "statusDelayMs": {
                          "title": "Status feedback delay",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 300,
                          "description": "Delay before sending status feedback (ms), after a transition completes."
                        },
                        "minColourK": {
                          "title": "Warmest colour temperature",
                          "expert": true,
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 2700,
                          "description": "Lowest colour temperature (K) of a tunable white channel; lower requests are limited to it."
                        },
                        "maxColourK": {
                          "title": "Coldest colour temperature",
                          "expert": true,
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 6500,
                          "description": "Highest colour temperature (K) of a tunable white channel; higher requests are limited to it."
                        }
                      }
                    },
                    "initialState": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "levelPct": {
                          "title": "Initial level",
                          "unit": "%",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 0,
                          "description": "Level of the output when the simulation starts; 0 is off."
                        },
                        "colourTemperatureK": {
                          "title": "Initial colour temperature",
                          "type": "integer",
                          "minimum": 1000,
                          "maximum": 10000,
                          "default": 4000,
                          "description": "Colour temperature (K) at start, for a channel with a colourTemperature object."
                        }
                      }
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "switch",
                          "description": "Channel switch command (0 = off, 1 = on)."
                        },
                        {
                          "const": "dim",
                          "description": "Relative dimming (3.007): direction bit and step size 1–7; 0 stops dimming."
                        },
                        {
                          "const": "value",
                          "description": "Brightness value in percent (5.001); zero switches off."
                        },
                        {
                          "const": "status",
                          "description": "Relay status feedback, sent after each effective change."
                        },
                        {
                          "const": "valueStatus",
                          "description": "Brightness value feedback (5.001), sent after a transition."
                        },
                        {
                          "const": "scene",
                          "description": "Scene number (bus byte 0–63 represents scenes 1–64); without a channel, apply to all channels."
                        },
                        {
                          "const": "error",
                          "description": "lamp or ballast fault in the group"
                        },
                        {
                          "const": "broadcastSwitch",
                          "description": "Switch all groups with a DALI broadcast."
                        },
                        {
                          "const": "broadcastValue",
                          "description": "Set all group levels with a DALI broadcast."
                        },
                        {
                          "const": "generalError",
                          "description": "Fault on the DALI line across all groups (1.005)."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "roomThermostat/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "controlType": {
                    "title": "Control type",
                    "type": "string",
                    "enum": [
                      "pi",
                      "twoPoint"
                    ],
                    "default": "pi",
                    "description": "pi uses proportional-integral control with a continuous value (5.001) or PWM (1 bit); twoPoint uses on/off control with hysteresis."
                  },
                  "comfortC": {
                    "title": "Base setpoint",
                    "type": "number",
                    "minimum": 5,
                    "maximum": 35,
                    "default": 21,
                    "description": "Initial heating comfort setpoint (°C); the baseSetpoint object can replace it."
                  },
                  "standbyShiftK": {
                    "title": "Standby setback",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 2,
                    "description": "Standby setpoint offset (K): lower for heating, higher for cooling."
                  },
                  "economyShiftK": {
                    "title": "Economy setback",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 15,
                    "default": 4,
                    "description": "Economy or night setpoint offset (K): lower for heating, higher for cooling."
                  },
                  "frostProtectionC": {
                    "title": "Frost protection setpoint",
                    "type": "number",
                    "minimum": 3,
                    "maximum": 15,
                    "default": 7,
                    "description": "Heating setpoint in protection mode (°C)."
                  },
                  "heatProtectionC": {
                    "title": "Heat protection setpoint",
                    "expert": true,
                    "type": "number",
                    "minimum": 25,
                    "maximum": 45,
                    "default": 35,
                    "description": "Cooling setpoint in protection mode (°C)."
                  },
                  "deadZoneK": {
                    "title": "Dead zone",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 3,
                    "description": "Difference between heating and cooling comfort setpoints (K), for example 21 °C and 24 °C."
                  },
                  "minSetpointC": {
                    "title": "Minimum setpoint",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "maximum": 30,
                    "default": 5,
                    "description": "Lowest base setpoint accepted (°C); lower received or entered values are raised to it."
                  },
                  "maxSetpointC": {
                    "title": "Maximum setpoint",
                    "expert": true,
                    "type": "number",
                    "minimum": 10,
                    "maximum": 50,
                    "default": 35,
                    "description": "Highest base setpoint accepted (°C); higher received or entered values are lowered to it."
                  },
                  "hysteresisK": {
                    "title": "Hysteresis",
                    "type": "number",
                    "minimum": 0.1,
                    "maximum": 5,
                    "default": 0.5,
                    "description": "Two-point control switches on again when temperature differs from the setpoint by this amount (K)."
                  },
                  "proportionalBandK": {
                    "title": "Proportional band",
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 10,
                    "default": 2,
                    "description": "Temperature difference (K) that produces 100% output from the proportional term."
                  },
                  "integralTimeMs": {
                    "title": "Integral time",
                    "unit": "ms",
                    "type": "integer",
                    "minimum": 0,
                    "default": 120000,
                    "description": "PI integral time: a constant error doubles the proportional response after this duration (0 selects P control). Real installations use 60–240 minutes; the default follows compressed simulated room time."
                  },
                  "pwmCycleMs": {
                    "title": "PWM cycle time",
                    "unit": "ms",
                    "type": "integer",
                    "minimum": 1000,
                    "default": 20000,
                    "description": "PWM cycle period for a 1-bit control object (real installations typically use 10–20 minutes)."
                  },
                  "controlPeriodMs": {
                    "title": "Calculation period",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 100,
                    "default": 1000,
                    "description": "Interval at which the controller recalculates its control value from the measured temperature."
                  },
                  "valueSendDeltaPct": {
                    "title": "Send on change",
                    "unit": "%",
                    "type": "number",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 5,
                    "description": "Send the control value when it changes by at least this many percentage points."
                  },
                  "externalTempTimeoutMs": {
                    "title": "External temperature timeout",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "If no externalTemp value arrives within this time, control uses the internal sensor again until a new value arrives (0 disables monitoring)."
                  },
                  "valueCyclicMs": {
                    "title": "Cyclic sending of control value",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "Periodically resend the control value (0 disables it); needed when the actuator monitors incoming values."
                  },
                  "temperatureSendDeltaK": {
                    "title": "Temperature send threshold",
                    "type": "number",
                    "minimum": 0.1,
                    "maximum": 5,
                    "default": 0.2,
                    "description": "Send measured temperature when it changes by at least this amount (K)."
                  },
                  "temperatureCyclicMs": {
                    "title": "Cyclic sending of temperature",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "Also send the measured temperature at this interval, even without change; 0 disables cyclic sending."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "actualTemp",
                          "description": "Thermostat: measured temperature (9.001), sent on change."
                        },
                        {
                          "const": "externalTemp",
                          "description": "replaces the internal sensor after a value is received"
                        },
                        {
                          "const": "baseSetpoint",
                          "description": "Thermostat: base heating comfort setpoint (9.001)."
                        },
                        {
                          "const": "setpointShift",
                          "description": "base setpoint = configured setpoint + offset"
                        },
                        {
                          "const": "setpointStatus",
                          "description": "Thermostat: current setpoint (9.001)."
                        },
                        {
                          "const": "hvacMode",
                          "description": "Thermostat: selected mode (20.102: 0 auto, 1 comfort, 2 standby, 3 economy, 4 protection)."
                        },
                        {
                          "const": "hvacModeStatus",
                          "description": "Thermostat: current mode (20.102)."
                        },
                        {
                          "const": "presence",
                          "description": "1 = presence: comfort mode"
                        },
                        {
                          "const": "window",
                          "description": "open window (1.019 / 1.001: 1 = open; 1.009: 0 = open): protection mode, highest priority"
                        },
                        {
                          "const": "heatCool",
                          "description": "1 = heating, 0 = cooling"
                        },
                        {
                          "const": "heatCoolStatus",
                          "description": "Thermostat: current heating or cooling mode."
                        },
                        {
                          "const": "heatingValue",
                          "description": "continuous control value from 0 to 100%"
                        },
                        {
                          "const": "heatingSwitch",
                          "description": "two-point control, or PWM under PI control"
                        },
                        {
                          "const": "coolingValue",
                          "description": "Thermostat: continuous cooling control value (5.001)."
                        },
                        {
                          "const": "coolingSwitch",
                          "description": "Thermostat: one-bit cooling command."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "heatingActuator/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "maxProperties": 0
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "valveType": {
                          "title": "Valve direction of action",
                          "type": "string",
                          "enum": [
                            "normallyClosed",
                            "normallyOpen"
                          ],
                          "default": "normallyClosed",
                          "description": "normallyClosed: valve closed without power (common); normallyOpen: valve open without power, so the output is inverted."
                        },
                        "cycleMs": {
                          "title": "PWM cycle time",
                          "unit": "ms",
                          "type": "integer",
                          "minimum": 1000,
                          "default": 20000,
                          "description": "PWM period for a continuous control value: powered time equals value × period (real installations typically use 10–20 minutes)."
                        },
                        "monitoringMs": {
                          "title": "Control value monitoring",
                          "unit": "ms",
                          "expert": true,
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "If no control value arrives within this time, enter fallback mode and report a fault (0 disables monitoring)."
                        },
                        "emergencyPct": {
                          "title": "Emergency control value",
                          "unit": "%",
                          "expert": true,
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "default": 30,
                          "description": "Control value applied in fallback mode."
                        }
                      }
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "value",
                          "description": "Brightness value in percent (5.001); zero switches off."
                        },
                        {
                          "const": "switch",
                          "description": "two-point or thermostat PWM control"
                        },
                        {
                          "const": "valueStatus",
                          "description": "Brightness value feedback (5.001), sent after a transition."
                        },
                        {
                          "const": "fault",
                          "description": "missing control value: fallback program"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "windowContact/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "contactType": {
                    "title": "Contact type",
                    "expert": true,
                    "type": "string",
                    "enum": [
                      "normallyOpen",
                      "normallyClosed"
                    ],
                    "default": "normallyOpen",
                    "description": "Physical contact: a normally open contact is closed while the window is closed; a normally closed contact is open while the window is closed."
                  },
                  "invert": {
                    "title": "Invert input",
                    "expert": true,
                    "type": "boolean",
                    "default": false,
                    "description": "Interpret the electrical input for a normally closed contact. The transmitted value always follows the DPT (1.019: 1 = open; 1.009: 1 = closed)."
                  },
                  "sendOnStart": {
                    "title": "Send on start",
                    "type": "boolean",
                    "default": true,
                    "description": "Send contact state when the simulation starts, as after bus power returns, so a thermostat can detect an already open window."
                  },
                  "startDelayMs": {
                    "title": "Start-up send delay",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 1000,
                    "description": "Delay after the simulation starts before the contact state is sent."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "contact",
                          "description": "1.019 / 1.001: 1 means open; 1.009: 1 means closed"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "temperatureSensor/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "sendDeltaK": {
                    "title": "Send on change",
                    "type": "number",
                    "minimum": 0.1,
                    "maximum": 5,
                    "default": 0.2,
                    "description": "Temperature change (K) that triggers a transmission."
                  },
                  "cyclicMs": {
                    "title": "Cyclic sending",
                    "unit": "ms",
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "Periodic retransmission (0 disables it)."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "temperature",
                          "description": "Sensor: room temperature (9.001)."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "logicGate/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "operation": {
                    "title": "Operation",
                    "type": "string",
                    "enum": [
                      "and",
                      "or",
                      "xor",
                      "not"
                    ],
                    "default": "and",
                    "description": "Logic function applied to the inputs; an input with no value yet counts as 0."
                  },
                  "activeFrom": {
                    "title": "Time window from",
                    "type": "string",
                    "default": "",
                    "description": "Start of a daily time window (HH:MM). With activeTo and a time object, the output is 1 only inside the window; the window may cross midnight."
                  },
                  "activeTo": {
                    "title": "Time window to",
                    "type": "string",
                    "default": "",
                    "description": "End of the daily time window (HH:MM), excluded."
                  },
                  "sendOnChangeOnly": {
                    "title": "Send on change only",
                    "expert": true,
                    "type": "boolean",
                    "default": true,
                    "description": "Send the result only when it differs from the last transmitted value."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "logicIn",
                          "description": "one-bit input of the logic function"
                        },
                        {
                          "const": "enable",
                          "description": "0 blocks the output; 1 enables it again and sends the current result"
                        },
                        {
                          "const": "time",
                          "description": "time received from a clock master, used by the time window"
                        },
                        {
                          "const": "logicOut",
                          "description": "result of the logic function"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "weatherStation/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "windThreshold": {
                    "title": "Wind alarm threshold",
                    "type": "number",
                    "minimum": 0,
                    "default": 10,
                    "description": "Wind speed (m/s) at which the wind alarm is set."
                  },
                  "windHysteresis": {
                    "title": "Wind alarm hysteresis",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 2,
                    "description": "The alarm is reset when the wind speed falls below threshold − hysteresis (m/s)."
                  },
                  "brightnessThreshold": {
                    "title": "Sun protection threshold",
                    "type": "number",
                    "minimum": 0,
                    "default": 40000,
                    "description": "Brightness (lux) at which sun protection is requested."
                  },
                  "setsRoomOutdoorTemperature": {
                    "title": "Outdoor temperature applies to the rooms",
                    "type": "boolean",
                    "default": true,
                    "description": "An entered outdoor temperature also becomes the outdoor temperature of every room in the thermal model, so that the building reacts to it."
                  },
                  "brightnessHysteresis": {
                    "title": "Sun protection hysteresis",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 5000,
                    "description": "The request is reset when brightness falls below threshold − hysteresis (lux)."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "wind",
                          "description": "measured wind speed, sent when entered: m/s with 9.005, km/h with 9.028 (thresholds stay in m/s)"
                        },
                        {
                          "const": "brightness",
                          "description": "measured brightness (lux), sent when entered"
                        },
                        {
                          "const": "outdoorTemp",
                          "description": "measured outdoor temperature (°C), sent when entered"
                        },
                        {
                          "const": "windAlarm",
                          "description": "1 when the wind threshold is reached, 0 after hysteresis"
                        },
                        {
                          "const": "sunProtection",
                          "description": "1 when the brightness threshold is reached, 0 after hysteresis"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "airQualitySensor/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "co2AlarmPpm": {
                    "title": "CO₂ alarm threshold",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 1500,
                    "description": "CO₂ concentration (ppm) at which the CO₂ alarm is set."
                  },
                  "co2AlarmHysteresisPpm": {
                    "title": "CO₂ alarm hysteresis",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 100,
                    "description": "The CO₂ alarm is reset below threshold − hysteresis (ppm).",
                    "expert": true
                  },
                  "humidityAlarmPct": {
                    "title": "Humidity alarm threshold",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 70,
                    "description": "Relative humidity (%) at which the humidity alarm is set."
                  },
                  "humidityAlarmHysteresisPct": {
                    "title": "Humidity alarm hysteresis",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 5,
                    "description": "The humidity alarm is reset below threshold − hysteresis (%).",
                    "expert": true
                  },
                  "step1Ppm": {
                    "title": "Threshold step 0 ↔ 1",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 800,
                    "description": "CO₂ concentration (ppm) between ventilation steps 0 and 1."
                  },
                  "step2Ppm": {
                    "title": "Threshold step 1 ↔ 2",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 1000,
                    "description": "CO₂ concentration (ppm) between ventilation steps 1 and 2."
                  },
                  "step3Ppm": {
                    "title": "Threshold step 2 ↔ 3",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 1200,
                    "description": "CO₂ concentration (ppm) between ventilation steps 2 and 3."
                  },
                  "stepHysteresisPpm": {
                    "title": "Step hysteresis",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000,
                    "default": 50,
                    "description": "A step changes only above threshold + hysteresis or below threshold − hysteresis (ppm).",
                    "expert": true
                  },
                  "step0Pct": {
                    "title": "Control value step 0",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 0,
                    "description": "Ventilation control value (%) sent in step 0.",
                    "expert": true
                  },
                  "step1Pct": {
                    "title": "Control value step 1",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 33,
                    "description": "Ventilation control value (%) sent in step 1.",
                    "expert": true
                  },
                  "step2Pct": {
                    "title": "Control value step 2",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 66,
                    "description": "Ventilation control value (%) sent in step 2.",
                    "expert": true
                  },
                  "step3Pct": {
                    "title": "Control value step 3",
                    "type": "number",
                    "unit": "%",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 100,
                    "description": "Ventilation control value (%) sent in step 3.",
                    "expert": true
                  },
                  "minStepTimeMs": {
                    "title": "Minimum time per step",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "A step is kept at least this long before the next change (0 disables it)."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "temperature",
                          "description": "measured temperature (°C), sent when entered"
                        },
                        {
                          "const": "humidity",
                          "description": "measured relative humidity (%), sent when entered: 2-byte float with 9.007, one byte with 5.001"
                        },
                        {
                          "const": "co2",
                          "description": "measured CO₂ concentration (ppm), sent when entered"
                        },
                        {
                          "const": "co2Alarm",
                          "description": "1 when the CO₂ alarm threshold is reached, 0 after hysteresis"
                        },
                        {
                          "const": "humidityAlarm",
                          "description": "1 when the humidity alarm threshold is reached, 0 after hysteresis"
                        },
                        {
                          "const": "ventilation",
                          "description": "control value (%) of the current ventilation step"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "clockMaster/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "sendPeriodMin": {
                    "title": "Send period",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1440,
                    "default": 1,
                    "description": "Clock minutes between two broadcasts, aligned on the clock (1 = every full minute); 0 sends only at start and after the clock is set."
                  },
                  "sendOnStart": {
                    "title": "Send on start",
                    "type": "boolean",
                    "default": true,
                    "description": "Send time and date shortly after the simulation starts."
                  },
                  "startDelayMs": {
                    "title": "Start-up send delay",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 0,
                    "default": 1000,
                    "description": "Delay after the simulation starts before time and date are sent."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "time",
                          "description": "day of week and time of the simulated clock"
                        },
                        {
                          "const": "date",
                          "description": "date of the simulated clock"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "timeSwitch/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "program": {
                    "title": "Weekly program",
                    "type": "string",
                    "default": "",
                    "description": "Switching points separated by semicolons: days, time, and value, for example “Mon-Fri 07:00 = 1; Sat,Sun 09:00 = 1; Daily 22:30 = 0”. Days: Mon … Sun, ranges (Mon-Fri), lists (Sat,Sun), or Daily."
                  },
                  "sendOnStart": {
                    "title": "Send current state on start",
                    "type": "boolean",
                    "default": true,
                    "description": "At start, and after the clock is set, send the value of the most recent switching point."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "output",
                          "description": "object that receives the programmed values"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "systemGateway/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "system": {
                    "title": "Other system",
                    "type": "string",
                    "default": "Modbus",
                    "description": "Name of the system on the other side of the gateway, shown on the device card and in the event log, such as Modbus, BACnet, or M-Bus."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "maxProperties": 0
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "value",
                          "description": "value read in the other system (temperature, state, meter…), sent on KNX when entered"
                        },
                        {
                          "const": "command",
                          "description": "value received from KNX and forwarded to the other system (mode, setpoint, boost…)"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "behavior": {
                "const": "energyMeter/v1"
              }
            },
            "required": [
              "behavior"
            ]
          },
          "then": {
            "properties": {
              "parameters": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "meterIntervalMs": {
                    "title": "Metering send interval",
                    "unit": "ms",
                    "expert": true,
                    "type": "integer",
                    "minimum": 1000,
                    "default": 5000,
                    "description": "Cyclic sending of power and energy values (ms)."
                  },
                  "powerSendDeltaW": {
                    "title": "Power send threshold",
                    "expert": true,
                    "type": "number",
                    "minimum": 0,
                    "default": 10,
                    "description": "Send a power value when it changes by at least this many watts."
                  },
                  "energyTimeScale": {
                    "title": "Energy time scale",
                    "expert": true,
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "default": 60,
                    "description": "Energy counting speed: 60 counts one simulated second as one minute, so that the counter moves visibly; 1 counts real time."
                  }
                }
              },
              "channels": {
                "items": {
                  "properties": {
                    "parameters": {
                      "type": "object",
                      "maxProperties": 0
                    },
                    "initialState": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "powerW": {
                          "title": "Measured power at start",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1000000,
                          "default": 0,
                          "description": "Power (W) drawn by the circuit when the simulation starts; a numeric input on the power object changes it."
                        },
                        "energyWh": {
                          "title": "Meter index at start",
                          "type": "number",
                          "minimum": 0,
                          "default": 0,
                          "description": "Energy (Wh) already counted when the simulation starts."
                        }
                      }
                    }
                  }
                }
              },
              "objects": {
                "items": {
                  "properties": {
                    "port": {
                      "oneOf": [
                        {
                          "const": "power",
                          "description": "power measured on the circuit: W with 14.056, kW with 9.024; a numeric input on this object enters the measured power"
                        },
                        {
                          "const": "energy",
                          "description": "energy counted on the circuit, sent cyclically: Wh with 13.010, kWh with 13.013"
                        },
                        {
                          "const": "totalPower",
                          "description": "sum of the measured circuits: W with 14.056, kW with 9.024"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      ]
    }
  }
}
