v0.16.1
This commit is contained in:
1 parent
ccf85a2fb8
commit
f376f0a7b6
32 files changed
+312
-114
No files matched your search
@@ -683,6 +683,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
// publishing the zone, but never overwrite manual device state while it follows
|
||||
// the house mode. Explicit per-zone heat/cool bypasses this branch above.
|
||||
if effective_mode == "off" {
|
||||
zone.heat_coasting = false;
|
||||
zone.effective_setpoint = None;
|
||||
zone.device_setpoint = None;
|
||||
zone.demand = false;
|
||||
@@ -709,6 +710,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
// controller stays physically OFF. This also makes gaps between schedule windows true
|
||||
// OFF periods instead of silently falling back to Comfort and recreating demand.
|
||||
if !zone_has_active_thermostat_intent(&zone, active_schedule, Utc::now()) {
|
||||
zone.heat_coasting = false;
|
||||
clear_compressor_pending(&mut zone, true);
|
||||
zone.effective_mode = "off".into();
|
||||
zone.effective_setpoint = None;
|
||||
@@ -802,6 +804,14 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
|
||||
let half = zone.hysteresis_for_mode(effective_mode) / 2.0;
|
||||
let previous_demand = zone.demand;
|
||||
let previous_heat_coasting = zone.heat_coasting;
|
||||
zone.heat_coasting = heat_coasting_next_state(
|
||||
zone.heat_coasting,
|
||||
effective_mode,
|
||||
temp,
|
||||
target,
|
||||
zone.heat_anticipation_c,
|
||||
);
|
||||
zone.demand = match effective_mode {
|
||||
"heat" => {
|
||||
if temp <= target - half {
|
||||
@@ -857,14 +867,17 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
"heat" => target + outdoor_assist,
|
||||
_ => target - demand_assist,
|
||||
};
|
||||
// Early heat coasting changes only GREE's requested temperature. It does NOT
|
||||
// toggle unit power or touch compressor protection/whole-multisplit OFF policy.
|
||||
let delivering_heat = zone.demand && !(effective_mode == "heat" && zone.heat_coasting);
|
||||
let standby_target = match effective_mode {
|
||||
"heat" => target - zone.standby_offset_c.max(0.5),
|
||||
_ => target + zone.standby_offset_c.max(0.5),
|
||||
};
|
||||
let desired_device_target = round_device_setpoint(
|
||||
effective_mode,
|
||||
zone.demand,
|
||||
if zone.demand {
|
||||
delivering_heat,
|
||||
if delivering_heat {
|
||||
active_target
|
||||
} else {
|
||||
standby_target
|
||||
@@ -878,7 +891,10 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
None
|
||||
};
|
||||
|
||||
let demand_changed = previous_demand != zone.demand;
|
||||
let demand_changed = previous_demand != zone.demand
|
||||
|| (effective_mode == "heat" && previous_heat_coasting != zone.heat_coasting);
|
||||
let previous_delivering_heat = previous_demand
|
||||
&& !(effective_mode == "heat" && previous_heat_coasting);
|
||||
let desired_fan = if night_active {
|
||||
let max_fan = settings.night_mode.max_fan_speed.clamp(1, 5);
|
||||
if zone.smart_fan {
|
||||
@@ -888,7 +904,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
temp,
|
||||
target,
|
||||
outdoor_assist_temperature,
|
||||
zone.demand,
|
||||
delivering_heat,
|
||||
),
|
||||
max_fan,
|
||||
))
|
||||
@@ -903,7 +919,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
temp,
|
||||
target,
|
||||
outdoor_assist_temperature,
|
||||
zone.demand,
|
||||
delivering_heat,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@@ -934,8 +950,8 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
let desired_quiet = smart_quiet_command(
|
||||
zone.smart_fan,
|
||||
quiet_supported,
|
||||
previous_demand,
|
||||
zone.demand,
|
||||
previous_delivering_heat,
|
||||
delivering_heat,
|
||||
device.quiet,
|
||||
settings.night_mode.enabled,
|
||||
night_active,
|
||||
@@ -1113,7 +1129,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
let fan_needs_command = desired_fan
|
||||
.map(|fan| fan != device.fan_speed)
|
||||
.unwrap_or(false)
|
||||
&& (zone.demand || demand_changed || core_needs_command || night_active);
|
||||
&& (delivering_heat || demand_changed || core_needs_command || (night_active && !zone.smart_fan));
|
||||
let needs_command = core_needs_command
|
||||
|| fan_needs_command
|
||||
|| desired_quiet
|
||||
@@ -1150,10 +1166,12 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
};
|
||||
zone.last_action_at = Some(transition_at);
|
||||
clear_compressor_pending(&mut zone, true);
|
||||
state.log("info", "zone.setpoint_modulation", &format!("Zone {} -> {:.1} C ({})", zone.name, desired_device_target, if zone.demand { "demand" } else { "standby" }), json!({
|
||||
state.log("info", "zone.setpoint_modulation", &format!("Zone {} -> {:.1} C ({})", zone.name, desired_device_target, if zone.heat_coasting { "coasting" } else if zone.demand { "demand" } else { "standby" }), json!({
|
||||
"zone_id": zone.id,
|
||||
"room_temperature": temp,
|
||||
"comfort_target": target,
|
||||
"heat_coasting": zone.heat_coasting,
|
||||
"heat_anticipation_c": zone.heat_anticipation_c,
|
||||
"device_target": desired_device_target,
|
||||
"mode": effective_mode,
|
||||
"preset": zone.active_preset,
|
||||
|
||||
Reference in new issue
Block a user