Guide
#Heating and air conditioning (HVAC)
This chapter models room temperature control with a thermostat, heating actuator, thermoelectric valve, and window contact. The heating or cooling power is assumed to be available: there is no boiler, heat pump, or water circuit. Simulation time is compressed so a change that would take hours in a building becomes visible in minutes.
#Rooms and thermal model
Each entry in rooms stores a room's temperature, outside temperature, and thermal time constant. Temperature moves toward the outside temperature while heating or cooling equipment adds or removes heat. A radiator's powerK is not a power in watts: it is the temperature difference, in K, that the radiator alone could maintain above the outside temperature with its valve fully open. An open window multiplies the heat loss by eight. The default timeConstantMs is 300 seconds in the accelerated model.
"rooms": [
{ "id": "living", "name": "Living room", "temperatureC": 18, "outsideTemperatureC": 5 }
]Assign a thermostat, window contact, or temperature sensor to a room with "room": "living". A radiator channel uses "equipment": { "type": "radiator", "room": "living" }. The Rooms panel shows temperature, setpoints, valve positions, and window state. roomAction(roomId, "window" | "outside", value) offers the same controls from JavaScript. An outdoor temperature entered on a weather station also becomes the outside temperature of every room, unless its setsRoomOutdoorTemperature parameter is false.
The physical window and the bus are distinct. Opening the window in the Rooms panel changes the heat loss and makes the window contact send a telegram. A telegram written directly to a thermostat window object, for example from the USB interface panel, changes the thermostat mode but does not open the physical window; this can represent a contact fault.
#Relevant DPTs
| DPT | Size | Meaning |
|---|---|---|
| 9.001 | 2 bytes | Temperature in °C, encoded as a KNX two-byte float. |
| 9.002 | 2 bytes | Temperature difference in K. |
| 20.102 | 1 byte | HVAC mode: 0 auto, 1 comfort, 2 standby, 3 economy, 4 building protection. |
| 1.019 | 1 bit | Window state; 1 means open. |
| 1.009 | 1 bit | Open/closed; 1 means closed. |
| 1.100 | 1 bit | 1 means heating, 0 means cooling. |
| 5.001 | 1 byte | Continuous control value from 0 to 100%. |
A DPT 9.001 value uses two payload bytes after the APCI. Until an object receives or measures a value, a 9.xxx object can remain unknown.
#Room thermostat
roomThermostat/v1 measures a room and controls heating or cooling. Its display shows measured temperature, mode, setpoint, and demand. Optional buttons and digital inputs affect presence and setpoint objects.
The thermostat includes a setpoint manager with a fixed policy. This policy is a choice of the model, not a rule of DPT 20.102. The mode is selected by priority: an open window requests building protection; otherwise presence requests comfort; otherwise the hvacMode preselection applies. Auto mode uses comfort in this model. A heating comfort setpoint defaults to comfortC: 21. Standby and economy lower it through standbyShiftK and economyShiftK; frost protection defaults to frostProtectionC: 7. Cooling uses deadZoneK between heating and cooling setpoints and a high-temperature protection setpoint.
For PI control, controlType: "pi" uses proportionalBandK and integralTimeMs. It sends a DPT 5.001 value on heatingValue or coolingValue when the change reaches valueSendDeltaPct; a one-bit heatingSwitch or coolingSwitch output uses PWM over pwmCycleMs. For two-point control, controlType: "twoPoint" switches a one-bit output at the setpoint and restarts below the hysteresisK threshold.
| Port | DPT | Direction | Purpose |
|---|---|---|---|
actualTemp |
9.001 | Send | Measured temperature. |
externalTemp |
9.001 | Receive | Replaces the internal sensor. With externalTempTimeoutMs, the internal sensor is used again when no value arrives within that time. |
baseSetpoint |
9.001 | Receive | Base comfort setpoint. |
setpointShift |
9.002 | Receive | Setpoint offset. |
setpointStatus |
9.001 | Send | Current setpoint. |
hvacMode / hvacModeStatus |
20.102 | Receive / send | Requested and current mode. |
presence |
1.018 or 1.001 | Receive | Presence requests comfort. |
window |
1.019, 1.001, or 1.009 | Receive | Open window requests protection; with 1.009, 0 means open. |
heatCool / heatCoolStatus |
1.100 | Receive / send | Heating or cooling selection. |
heatingValue / coolingValue |
5.001 | Send | Continuous control value. |
heatingSwitch / coolingSwitch |
1.001 | Send | One-bit control or PWM. |
#Heating actuator and valve
heatingActuator/v1 drives a thermoelectric valve. A continuous value command is converted to PWM using cycleMs; a one-bit switch command is applied directly. valveType tells the actuator whether its valve is normally closed or normally open; the radiator's normallyOpen parameter describes the valve actually installed. When they disagree, the valve opens when no heat is requested, and a configuration warning is shown above the diagram. Without a fresh command for monitoringMs, the actuator uses emergencyPct and reports a fault. Its valueStatus object reports the applied control value. When monitoring is enabled, the thermostat's valueCyclicMs should send often enough to prevent a false fault.
A radiator load opens and closes its valve over openingTimeMs, then heats or cools the assigned room. Short PWM cycles can prevent the valve from opening fully, so choose a cycle appropriate to the modeled travel time. A switching actuator can also control a radiator with two-point regulation.
{
"id": "heat",
"address": "1.1.3",
"kind": "heatingActuator",
"behavior": "heatingActuator/v1",
"objects": [
{ "id": "value", "ga": "3/0/1", "dpt": "5.001", "port": "value", "channel": "h1", "flags": { "W": true, "T": false } },
{ "id": "status", "ga": "3/4/4", "dpt": "5.001", "port": "valueStatus", "channel": "h1", "flags": { "W": false, "T": true } }
],
"channels": [{
"id": "h1", "label": "H1 living room",
"parameters": { "cycleMs": 20000 },
"equipment": { "type": "radiator", "room": "living" }
}]
}#Sensors
windowContact/v1 sends changes to its contact object. The transmitted value always follows the DPT: with DPT 1.019 or 1.001, 1 means open; with DPT 1.009, 1 means closed. contactType describes the physical contact (normally open by default) and invert tells the input to interpret a normally closed contact; when they disagree, open and closed are reported the wrong way round and a configuration warning is shown. By default it also sends its initial state after startDelayMs, so a thermostat can learn that a window starts open. Set sendOnStart: false to suppress that telegram. temperatureSensor/v1 transmits room temperature when it changes and, optionally, at a fixed interval.
#Example
The room-heating example combines PI control in the living room, two-point control in the bedroom, a window contact, and a central mode set through the USB interface panel.
<bus-diagram>
<script type="application/json">
{
"formatVersion": 2,
"title": "Room-by-room heating control",
"description": "Two rooms are heated. In the living room, a PI thermostat sends a DPT 5.001 control value to a heating actuator, which drives a thermoelectric valve with PWM. The bedroom uses two-point (one-bit) control through a switching actuator. Open the living-room window in the Rooms panel: the contact switches the thermostat to frost protection. Write central mode 3/2/0 (DPT 20.102) from the USB interface panel; the thermostat Presence key restores comfort mode.",
"lines": [{ "address": "1.1", "name": "Heating example" }],
"rooms": [
{ "id": "livingRoom", "name": "Living room", "temperatureC": 18 },
{ "id": "bedroom", "name": "Bedroom", "temperatureC": 17 }
],
"groupAddresses": [
{ "address": "3/0/1", "name": "Living-room heating control", "dpt": "5.001" },
{ "address": "3/0/2", "name": "Bedroom heating control", "dpt": "1.001" },
{ "address": "3/1/1", "name": "Living-room base setpoint", "dpt": "9.001" },
{ "address": "3/2/0", "name": "Central mode", "dpt": "20.102" },
{ "address": "3/2/1", "name": "Living-room presence", "dpt": "1.018" },
{ "address": "3/3/1", "name": "Living-room window", "dpt": "1.019" },
{ "address": "3/4/1", "name": "Living-room temperature", "dpt": "9.001" },
{ "address": "3/4/2", "name": "Living-room current setpoint", "dpt": "9.001" },
{ "address": "3/4/3", "name": "Living-room current mode", "dpt": "20.102" },
{ "address": "3/4/4", "name": "Living-room valve status", "dpt": "5.001" },
{ "address": "3/4/5", "name": "Bedroom temperature", "dpt": "9.001" }
],
"devices": [
{
"id": "usbInterface",
"name": "USB interface",
"address": "1.1.255",
"kind": "interface",
"behavior": "usbInterface/v1",
"objects": []
},
{
"id": "livingThermostat",
"name": "Living-room thermostat",
"address": "1.1.1",
"kind": "thermostat",
"behavior": "roomThermostat/v1",
"room": "livingRoom",
"objects": [
{
"id": "temp",
"name": "Measured temperature",
"ga": "3/4/1",
"dpt": "9.001",
"port": "actualTemp",
"flags": { "W": false, "T": true }
},
{
"id": "base",
"name": "Base setpoint",
"ga": "3/1/1",
"dpt": "9.001",
"port": "baseSetpoint",
"flags": { "W": true, "T": false }
},
{
"id": "mode",
"name": "Mode (preset)",
"ga": "3/2/0",
"dpt": "20.102",
"port": "hvacMode",
"flags": { "W": true, "T": false }
},
{
"id": "presence",
"name": "Presence",
"ga": "3/2/1",
"dpt": "1.018",
"port": "presence",
"flags": { "W": true, "T": true }
},
{
"id": "win",
"name": "Window",
"ga": "3/3/1",
"dpt": "1.019",
"port": "window",
"flags": { "W": true, "T": false }
},
{
"id": "sp",
"name": "Current setpoint",
"ga": "3/4/2",
"dpt": "9.001",
"port": "setpointStatus",
"flags": { "W": false, "T": true }
},
{
"id": "ms",
"name": "Current mode",
"ga": "3/4/3",
"dpt": "20.102",
"port": "hvacModeStatus",
"flags": { "W": false, "T": true }
},
{
"id": "val",
"name": "Heating control",
"ga": "3/0/1",
"dpt": "5.001",
"port": "heatingValue",
"flags": { "W": false, "T": true }
}
],
"buttons": [
{
"id": "presence",
"label": "Presence",
"icon": "presence",
"press": { "object": "presence", "value": "toggle" },
"led": "presence"
}
],
"inputs": [
{
"id": "setpoint",
"type": "number",
"label": "Setpoint °C",
"object": "base",
"min": 15,
"max": 28,
"step": 0.5
}
]
},
{
"id": "windowContact",
"name": "Living-room window contact",
"address": "1.1.2",
"kind": "binaryInput",
"behavior": "windowContact/v1",
"room": "livingRoom",
"objects": [
{
"id": "c",
"name": "Living-room window",
"ga": "3/3/1",
"dpt": "1.019",
"port": "contact",
"flags": { "W": false, "T": true }
}
]
},
{
"id": "heatingActuator",
"name": "Heating actuator",
"address": "1.1.3",
"kind": "heatingActuator",
"behavior": "heatingActuator/v1",
"objects": [
{
"id": "v1",
"name": "H1 control value",
"ga": "3/0/1",
"dpt": "5.001",
"port": "value",
"channel": "h1",
"flags": { "W": true, "T": false }
},
{
"id": "s1",
"name": "H1 control status",
"ga": "3/4/4",
"dpt": "5.001",
"port": "valueStatus",
"channel": "h1",
"flags": { "W": false, "T": true }
}
],
"channels": [
{
"id": "h1",
"label": "H1 living room",
"equipment": { "type": "radiator", "room": "livingRoom" }
}
]
},
{
"id": "bedroomThermostat",
"name": "Bedroom thermostat",
"address": "1.1.4",
"kind": "thermostat",
"behavior": "roomThermostat/v1",
"parameters": { "controlType": "twoPoint", "comfortC": 19 },
"room": "bedroom",
"objects": [
{
"id": "temp",
"name": "Measured temperature",
"ga": "3/4/5",
"dpt": "9.001",
"port": "actualTemp",
"flags": { "W": false, "T": true }
},
{
"id": "mode",
"name": "Mode (preset)",
"ga": "3/2/0",
"dpt": "20.102",
"port": "hvacMode",
"flags": { "W": true, "T": false }
},
{
"id": "sw",
"name": "One-bit heating control",
"ga": "3/0/2",
"dpt": "1.001",
"port": "heatingSwitch",
"flags": { "W": false, "T": true }
}
]
},
{
"id": "switchActuator",
"name": "Switching actuator",
"address": "1.1.5",
"kind": "switchActuator",
"behavior": "switchActuator/v1",
"objects": [
{
"id": "a",
"name": "Bedroom radiator",
"ga": "3/0/2",
"dpt": "1.001",
"port": "switch",
"channel": "a",
"flags": { "W": true, "T": false }
}
],
"channels": [
{
"id": "a",
"label": "Bedroom actuator",
"equipment": { "type": "radiator", "room": "bedroom", "parameters": { "powerK": 20 } }
}
]
}
],
"options": { "speed": 1, "filterTables": false }
}
</script>
</bus-diagram>{
"formatVersion": 2,
"title": "Room-by-room heating control",
"description": "Two rooms are heated. In the living room, a PI thermostat sends a DPT 5.001 control value to a heating actuator, which drives a thermoelectric valve with PWM. The bedroom uses two-point (one-bit) control through a switching actuator. Open the living-room window in the Rooms panel: the contact switches the thermostat to frost protection. Write central mode 3/2/0 (DPT 20.102) from the USB interface panel; the thermostat Presence key restores comfort mode.",
"lines": [{ "address": "1.1", "name": "Heating example" }],
"rooms": [
{ "id": "livingRoom", "name": "Living room", "temperatureC": 18 },
{ "id": "bedroom", "name": "Bedroom", "temperatureC": 17 }
],
"groupAddresses": [
{ "address": "3/0/1", "name": "Living-room heating control", "dpt": "5.001" },
{ "address": "3/0/2", "name": "Bedroom heating control", "dpt": "1.001" },
{ "address": "3/1/1", "name": "Living-room base setpoint", "dpt": "9.001" },
{ "address": "3/2/0", "name": "Central mode", "dpt": "20.102" },
{ "address": "3/2/1", "name": "Living-room presence", "dpt": "1.018" },
{ "address": "3/3/1", "name": "Living-room window", "dpt": "1.019" },
{ "address": "3/4/1", "name": "Living-room temperature", "dpt": "9.001" },
{ "address": "3/4/2", "name": "Living-room current setpoint", "dpt": "9.001" },
{ "address": "3/4/3", "name": "Living-room current mode", "dpt": "20.102" },
{ "address": "3/4/4", "name": "Living-room valve status", "dpt": "5.001" },
{ "address": "3/4/5", "name": "Bedroom temperature", "dpt": "9.001" }
],
"devices": [
{
"id": "usbInterface",
"name": "USB interface",
"address": "1.1.255",
"kind": "interface",
"behavior": "usbInterface/v1",
"objects": []
},
{
"id": "livingThermostat",
"name": "Living-room thermostat",
"address": "1.1.1",
"kind": "thermostat",
"behavior": "roomThermostat/v1",
"room": "livingRoom",
"objects": [
{
"id": "temp",
"name": "Measured temperature",
"ga": "3/4/1",
"dpt": "9.001",
"port": "actualTemp",
"flags": { "W": false, "T": true }
},
{
"id": "base",
"name": "Base setpoint",
"ga": "3/1/1",
"dpt": "9.001",
"port": "baseSetpoint",
"flags": { "W": true, "T": false }
},
{
"id": "mode",
"name": "Mode (preset)",
"ga": "3/2/0",
"dpt": "20.102",
"port": "hvacMode",
"flags": { "W": true, "T": false }
},
{
"id": "presence",
"name": "Presence",
"ga": "3/2/1",
"dpt": "1.018",
"port": "presence",
"flags": { "W": true, "T": true }
},
{
"id": "win",
"name": "Window",
"ga": "3/3/1",
"dpt": "1.019",
"port": "window",
"flags": { "W": true, "T": false }
},
{
"id": "sp",
"name": "Current setpoint",
"ga": "3/4/2",
"dpt": "9.001",
"port": "setpointStatus",
"flags": { "W": false, "T": true }
},
{
"id": "ms",
"name": "Current mode",
"ga": "3/4/3",
"dpt": "20.102",
"port": "hvacModeStatus",
"flags": { "W": false, "T": true }
},
{
"id": "val",
"name": "Heating control",
"ga": "3/0/1",
"dpt": "5.001",
"port": "heatingValue",
"flags": { "W": false, "T": true }
}
],
"buttons": [
{
"id": "presence",
"label": "Presence",
"icon": "presence",
"press": { "object": "presence", "value": "toggle" },
"led": "presence"
}
],
"inputs": [
{
"id": "setpoint",
"type": "number",
"label": "Setpoint °C",
"object": "base",
"min": 15,
"max": 28,
"step": 0.5
}
]
},
{
"id": "windowContact",
"name": "Living-room window contact",
"address": "1.1.2",
"kind": "binaryInput",
"behavior": "windowContact/v1",
"room": "livingRoom",
"objects": [
{
"id": "c",
"name": "Living-room window",
"ga": "3/3/1",
"dpt": "1.019",
"port": "contact",
"flags": { "W": false, "T": true }
}
]
},
{
"id": "heatingActuator",
"name": "Heating actuator",
"address": "1.1.3",
"kind": "heatingActuator",
"behavior": "heatingActuator/v1",
"objects": [
{
"id": "v1",
"name": "H1 control value",
"ga": "3/0/1",
"dpt": "5.001",
"port": "value",
"channel": "h1",
"flags": { "W": true, "T": false }
},
{
"id": "s1",
"name": "H1 control status",
"ga": "3/4/4",
"dpt": "5.001",
"port": "valueStatus",
"channel": "h1",
"flags": { "W": false, "T": true }
}
],
"channels": [
{
"id": "h1",
"label": "H1 living room",
"equipment": { "type": "radiator", "room": "livingRoom" }
}
]
},
{
"id": "bedroomThermostat",
"name": "Bedroom thermostat",
"address": "1.1.4",
"kind": "thermostat",
"behavior": "roomThermostat/v1",
"parameters": { "controlType": "twoPoint", "comfortC": 19 },
"room": "bedroom",
"objects": [
{
"id": "temp",
"name": "Measured temperature",
"ga": "3/4/5",
"dpt": "9.001",
"port": "actualTemp",
"flags": { "W": false, "T": true }
},
{
"id": "mode",
"name": "Mode (preset)",
"ga": "3/2/0",
"dpt": "20.102",
"port": "hvacMode",
"flags": { "W": true, "T": false }
},
{
"id": "sw",
"name": "One-bit heating control",
"ga": "3/0/2",
"dpt": "1.001",
"port": "heatingSwitch",
"flags": { "W": false, "T": true }
}
]
},
{
"id": "switchActuator",
"name": "Switching actuator",
"address": "1.1.5",
"kind": "switchActuator",
"behavior": "switchActuator/v1",
"objects": [
{
"id": "a",
"name": "Bedroom radiator",
"ga": "3/0/2",
"dpt": "1.001",
"port": "switch",
"channel": "a",
"flags": { "W": true, "T": false }
}
],
"channels": [
{
"id": "a",
"label": "Bedroom actuator",
"equipment": { "type": "radiator", "room": "bedroom", "parameters": { "powerK": 20 } }
}
]
}
],
"options": { "speed": 1, "filterTables": false }
}
#Model limits
The thermal model uses one time constant per room. It does not simulate room-to-room heat transfer, solar gains, equipment inertia, heat production (boiler, heat pump, water circuit, summer or winter changeover), fan coils, or BACnet. The thermostat has no internal time program: schedules come from the bus, for example a weekly time switch that sends HVAC modes. Humidity and CO₂ are values entered on an air quality sensor; ventilation drives a fan load but does not change the room temperature or air quality. A radiator with emitter: "cooling" removes heat without any condensation model or dew-point protection; use it only as a simplified cooling emitter. Auto mode selects comfort; a bus telegram can change the mode preselection. The operating mode is selected with a DPT 20.102 object only: the separate forced-mode object and the one-bit mode objects (comfort, night, frost protection) offered by many room controllers are not modeled.