# Climate regulator

Process scheduler

Zonal climate regulator - can be used as a thermostat for a specific zone heating or cooling. Output should be provided to a climate regulator hub.

Climate regulator
AT
OT
PRE
DWC
COM
PRO
TT
TD

# Inputs

IDAbbrevNameTypeDefaultDescription
air_tempATAir temperatureNUMBER0Current air temperature
outside_tempOTOutside temperatureNUMBER-1000Current outside temperature
presencePREPresenceBOOLEANfalseExtends the comfort temperature period when high. Switches from eco to comfort after a specified configuration parameter.
dwcDWCDoor window contactBOOLEANfalseIf active longer than configured duration: 1) switches off heating for open door or window if outside temperature is lower than target, 2) switches off cooling if outside temperature is higher than target
comfortCOMComfort modeBOOLEANfalseActivates the comfort temperature mode until the next schedule period change.
protectionPROProtection modeBOOLEANfalseActivates the protection temperature mode to protect the building from frost or overheat.

# Outputs

IDAbbrevNameTypeDefaultDescription
target_tempTTTarget temperatureNUMBER0The output target temperature
temp_deviationTDTemperature deviationNUMBER0Output expressing the heating or cooling requirement expressed as a difference between a current and a target temperature. Positive for heating. Negative for cooling.

# Configuration

IDNameTypeDefaultUnitDescription
thermal_modeThermal modeENUM0Defines the thermal mode: Heating and cooling, Heating only, Cooling only

Details:

Values: Heating and cooling, Heating only, Cooling only
operation_modeOperation modeENUM0Defines the operation mode: Schedule, Manual

Details:

Values: Schedule, Manual
scheduleScheduleSCHEDULE0Schedule to use for schedule based operation mode

Details:

Subtype: CLIMATE
Visible whenoperation_mode = Schedule
comfort_heating_tempComfort heating temperatureNUMBER21°CComfort heating temperature

Details:

Visible whenthermal_mode = Heating and cooling, Heating only
comfort_cooling_temp
comfort_cooling_tempComfort cooling temperatureNUMBER25°CComfort cooling temperature

Details:

Visible whenthermal_mode = Heating and cooling, Cooling only
comfort_heating_temp
eco_heating_temp_diffEco heating temperature diffNUMBER2°CEco heating temperature diff. below the comfort temp.

Details:

Visible whenthermal_mode = Heating and cooling, Heating only
≥ 0
eco_cooling_temp_diffEco cooling temperature diffNUMBER3°CEco cooling temperature diff. above the comfort temp.

Details:

Visible whenthermal_mode = Heating and cooling, Cooling only
≥ 0
frost_protection_tempFrost protection temperatureNUMBER10°CFrost protection temperature. If the temperature is below this temperature, the heating is activated to protect the building.

Details:

Visible whenthermal_mode = Heating and cooling, Heating only
≥ 5
< comfort_heating_temp
overheat_protection_tempOverheat protection temperatureNUMBER40°COverheat protection temperature. If the temperature is above this temperature, the cooling is activated to protect the building.

Details:

Visible whenthermal_mode = Heating and cooling, Cooling only
comfort_cooling_temp
dwc_delayDWC delay in secondsNUMBER30sDoor, window contact delay in seconds. Prevents short door, window opening to interrupt the climate control.

Details:

> 0
presence_extension_durationPresence extension durationNUMBER1800sComfort temperature extension duration (in seconds) on presence detection.

Details:

Visible whenoperation_mode = Schedule
≥ 0

# State

IDNameTypeDefaultUnitDescription
dwc_state_change_tsLast DWC state change timestampNUMBER0
dwc_stateCurrent DWC stateBOOLEANfalseUsed to prevent DWC duration extension on re-sending the same input
dwc_lockDWC lock stateBOOLEANfalseDWC lock state
last_activation_tsLast execution timestampNUMBER0sLast execution timestamp in seconds from epoch
next_activation_tsNext execution timestampNUMBER0sNext execution timestamp in seconds from epoch

# Source Code

View Volang source

extern fn std::next_activation_at() {
    operation_mode = config::get("operation_mode") 
    if (operation_mode == 0) { // schedule
        schedule_id = config::get("schedule")
        return schedule::get_next_activation_ts(schedule_id)
    }
    return 0
}

extern fn process() {
    channel = input::channel()
    value = input::value()
    now = time::uptime()

    // inputs
    air_temp = input::get("air_temp")
    floor_temp = input::get("floor_temp")
    outside_temp = input::get("outside_temp")
    presence = input::get("presence")
    dwc = input::get("dwc")
    comfort_forced = input::get("comfort")
    protection_forced = input::get("protection")

    // configs
    thermal_mode = config::get("thermal_mode") // 0=heating and cooling, 1=heating only, 2=cooling only
    operation_mode = config::get("operation_mode") // 0=schedule, 1=manual
    comfort_heating_temp = config::get("comfort_heating_temp")
    comfort_cooling_temp = config::get("comfort_cooling_temp")
    eco_heating_temp_diff = config::get("eco_heating_temp_diff")
    eco_cooling_temp_diff = config::get("eco_cooling_temp_diff")
    frost_protection_temp = config::get("frost_protection_temp")
    overheat_protection_temp = config::get("overheat_protection_temp")
    dwc_delay_sec = config::get("dwc_delay")
    presence_extension_duration = config::get("presence_extension_duration")


    last_dwc_state = state::get("dwc_state")
    if (channel == "dwc") {
        // on DWC switch
        if ((value and !last_dwc_state) or (!value and last_dwc_state)) {
            callback::clear()
            callback::set(dwc_delay_sec * 1000 + 5, "process")
            state::set("dwc_state_change_ts", now)
            state::set("dwc_state", value)
        }
    }
    last_dwc_ts = state::get("dwc_state_change_ts")
    dwc_duration_sec = (now - last_dwc_ts) / 1000.0

    fn round2decimal(temp_deviation) {
        return math::round(100.0 * temp_deviation) / 100.0
    }

    // define:
    // - comfort mode: 0=eco, 1=comfort, 2=protection
    // - target temperatures for heating and cooling
    comfort_mode = 0
    target_heating_temp = 0
    target_cooling_temp = 0

    if (operation_mode == 0) { // schedule
        schedule_id = config::get("schedule")
        comfort_mode = schedule::get_entry_type(schedule_id)
    }

    if (comfort_mode == 1 or comfort_forced) { // comfort
        target_heating_temp = comfort_heating_temp
        target_cooling_temp = comfort_cooling_temp
    } else if (comfort_mode == 2 or protection_forced) { // protection
        target_heating_temp = frost_protection_temp
        target_cooling_temp = overheat_protection_temp
    } else { // no entry - default: eco
        target_heating_temp = comfort_heating_temp - eco_heating_temp_diff
        target_cooling_temp = comfort_cooling_temp + eco_cooling_temp_diff
    }

    // define:
    // - chosen thermal mode: 0=idle, 1=heating, 2=cooling
    // - temperature deviation
    chosen_thermal_mode = 0 // idle
    target_temp = air_temp
    temp_deviation = 0.0
    outside_temp_helps = false
    outside_temp_sensor = outside_temp != -1000
    if (air_temp < target_heating_temp and (thermal_mode == 0 or thermal_mode == 1)) {
        chosen_thermal_mode = 1 // heating
        target_temp = target_heating_temp
        temp_deviation = target_temp - air_temp // positive
        outside_temp_helps = outside_temp_sensor and (outside_temp > target_heating_temp)
    }
    if (air_temp > target_cooling_temp and (thermal_mode == 0 or thermal_mode == 2)) {
        chosen_thermal_mode = 2 // cooling
        target_temp = target_cooling_temp
        temp_deviation = target_temp - air_temp // negative
        outside_temp_helps = outside_temp_sensor and (outside_temp < target_cooling_temp)
    }

    if (!outside_temp_helps and (dwc and dwc_duration_sec > dwc_delay_sec)) {
        state::set("dwc_lock", true)
    }
    if (state::get("dwc_lock") and !dwc and dwc_duration_sec > dwc_delay_sec) {
        state::set("dwc_lock", false)
    }
    dwc_lock_active = state::get("dwc_lock")

    protective_action_required = air_temp < frost_protection_temp or air_temp > overheat_protection_temp
    if (dwc_lock_active and !protective_action_required) {
        output::set("temp_deviation", round2decimal(0.0))
        output::set("target_temp", round2decimal(target_temp))
        return
    }

    output::set("temp_deviation", round2decimal(temp_deviation))
    output::set("target_temp", round2decimal(target_temp))

}

process()

# Climate Schedules

The Climate Regulator uses a Climate-type schedule to define temperature setpoints across a 24-hour daily cycle. Each schedule entry carries a numeric target temperature; the regulator compares it against measured values to drive heating or cooling outputs.

Different temperature profiles for weekdays, weekends, or custom situations (away, party, etc.) are achieved by combining the schedule with Operation Modes rather than hard-coding days of the week.

For details on configuring Climate schedules in the visual editor see Voldeno Studio – Schedules.

Zonal climate regulator - can be used as a thermostat for a specific zone heating or cooling. Output should be provided to a climate regulator hub.