1 unstable release

0.1.0 Oct 29, 2018


262 lines


Build Status Latest Version docs rustc 1.31+

This crate provides a state machine for an HVAC controller.

HVAC control systems regulate the operation of a heating and/or air conditioning system. Essentially, they turn on or off the heating, cooling, and air circulation as instructed by some other system–typically a thermostat.

This crate currently supports only single-stage HVAC implementations wherein the heating and cooling systems can be either on or off with no intermediate states of operation. Optional constraints on the minimum run and recovery time are supported for the heat, cool, and fan services.

This crate has no dependencies on the standard library or any other crates, making it easily used in standard applications as well as embedded targets leveraging #![no_std].


use hvac::prelude::*;

// create a new hvac controller with the
// following constraints:
// heat:
// - no min run time
// - min recover of 1 minute (60 sec)
// cool:
// - min run time of 5 minutes (300 sec)
// - min recover of 5 minutes (300 sec)
// fan:
// - no min run time
// - no min recovery
let mut hvac_controller = Hvac::default()
    .with_heat(None, Some(60))
    .with_cool(Some(300), Some(300))
    .with_fan(None, None);

// enable heat as soon as permissible
let state = hvac_controller.heat();

for i in 0..60 {
    // advance state machine to `i`
    // seconds elapsed
    let state = hvac_controller.tick(i);
    // even though we have called for
    // heat, it will not be enabled
    // until we have met our 60 second
    // minimum recovery time
    assert_eq!(state.service, None);
    // and since the fan is set to auto
    // by default, it remains disabled
    assert_eq!(state.fan, false);

// once the state machine is at
// 60 seconds elappsed...
let state = hvac_controller.tick(60);
// we have now met our minimum recover
// time and heat is enabled
assert_eq!(state.service, Some(HvacService::Heat));
// along with the fan
assert_eq!(state.fan, true);

// we can now call for cool
let state = hvac_controller.cool();
// and heat will be immediately disabled
// since we gave it no min run time but
// cool is not immediately enabled due
// to its 300 second recovery time
assert_eq!(state.service, None);
// fan is still set to auto and has no
// minimum run time, it is also disabled
assert_eq!(state.fan, false);

// advancing to cool's minimum recovery
// time will result in cool starting
let state = hvac_controller.tick(300);
assert_eq!(state.service, Some(HvacService::Cool));
// fan also starts again
assert_eq!(state.fan, true);

// we idle the system calls
let state = hvac_controller.idle();
// which has no immediate effect because
// of cool's min run time
assert_eq!(state.service, Some(HvacService::Cool));
assert_eq!(state.fan, true);

// we disable auto mode for the fan
let state = hvac_controller.fan_auto(false);
// which still has no immediate effect
assert_eq!(state.service, Some(HvacService::Cool));
assert_eq!(state.fan, true);

// until we advance another 300 seconds
// elapsed to meet cool's min run time
let state = hvac_controller.tick(600);
// now cool has stopped but fan
// continues with auto mode disabled
assert_eq!(state.service, None);
assert_eq!(state.fan, true);

// without a minimum run time, fan will
// immediately shut down when put back
// into auto mode
let state = hvac_controller.fan_auto(true);
assert_eq!(state.fan, false);


Licensed under either of the following, at your option:


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

No runtime deps