diff --git a/package.json b/package.json index 452ef2d..92c4113 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vvzvlad/node-red-contrib-rn-combined-nodes", - "version": "0.2.9", + "version": "0.2.10", "description": "", "main": "index.js", "keywords": [ diff --git a/thermostat.js b/thermostat.js index 2c401fd..1d884ac 100644 --- a/thermostat.js +++ b/thermostat.js @@ -12,7 +12,47 @@ module.exports = function(RED) { let debug = "" + context.set("pwm_timer", null); + context.set("pwm_period", 30*60*1000); + context.set("pwm_tick", 1000); + function manage_pwm(duty_cycle_percent) { + if (duty_cycle_percent === -1) { + const pwm_timer = context.get("pwm_timer"); + if (pwm_timer !== null) { + clearInterval(pwm_timer); + context.set("pwm_timer", null); + } + return; + } + + const pwm_duty_cycle = Math.max(0, Math.min(1, duty_cycle_percent / 100)); + context.set("pwm_duty_cycle", pwm_duty_cycle); + + if (context.get("pwm_timer") === null) { + let cycle_start_time = Date.now(); + const pwm_tick = context.get("pwm_tick"); + + const pwm_timer = setInterval(() => { + const pwm_period = context.get("pwm_period"); + const elapsed_time = Date.now() - cycle_start_time; + const on_time = pwm_period * pwm_duty_cycle; + + if (elapsed_time >= pwm_period) { + cycle_start_time = Date.now(); + } + + if (elapsed_time < on_time) { + node.send([null, { payload: 1 }]); + } else { + node.send([null, { payload: 0 }]); + } + + }, pwm_tick); + + context.set("pwm_timer", pwm_timer); + } + } function debug_text(text) { if (text === "return_debug") { @@ -24,29 +64,6 @@ module.exports = function(RED) { } - function predict_temp_change(current_temp, time_interval) { - let last_temp = context.get('last_temp') || null - let last_temp_time = context.get('last_temp_time') || null - if (last_temp === null || last_temp_time === null) { - last_temp = current_temp - last_temp_time = new Date().getTime() - context.set('last_temp', last_temp) - context.set('last_temp_time', last_temp_time) - return 0 - } - - const temp_diff = current_temp - last_temp - const time_diff = (new Date().getTime() - last_temp_time) / 1000 - const rate_of_change = temp_diff / time_diff - - last_temp = current_temp - last_temp_time = new Date().getTime() - context.set('last_temp', last_temp) - context.set('last_temp_time', last_temp_time) - - return current_temp + rate_of_change * time_interval - } - node.on('input', function(msg) { @@ -74,31 +91,6 @@ module.exports = function(RED) { return } - if (control_mode === "auto-predict" && current_temp !== null && target_temp !== null) { - const predicted_temp = predict_temp_change(current_temp, 40) - if (window_state === "open" && window_mode === "cool") { - heater_status = 0 - debug_text("Heater: w-open-cool/off") - return - } - if (window_state === "open" && window_mode === "heat") { - heater_status = 1 - debug_text("Heater: w-open-heat/on") - return - } - - if (window_state === "close") { - if (predicted_temp < target_temp) { - heater_status = 1 - debug_text(`Heater: auto-predict/${"on"}`) - } else if (predicted_temp >= target_temp) { - heater_status = 0 - debug_text(`Heater: auto-predict/${"off"}`) - } - } - return - } - if (control_mode === "auto" && current_temp !== null && target_temp !== null) { if (window_state === "open" && window_mode === "cool") { heater_status = 0 @@ -124,18 +116,20 @@ module.exports = function(RED) { } return } + + if (control_mode === "pwm" && current_temp !== null && target_temp !== null) { + manage_pwm(context.get("pwm_duty_cycle")) + return + } } - - - debug_text(`W-mode: ${window_mode}`) let mqtt_control = topic.split('/') if (mqtt_control.length === 3 && mqtt_control[0] === 'control') { if (mqtt_control[2] === node.zone || mqtt_control[2] === node.zone + "_" + node.room || mqtt_control[2] === "all") { - if (mqtt_control[1] === "window_mode_control" || mqtt_control[1] === "heat_mode_control") { + if (mqtt_control[1] === "window_mode_control" || mqtt_control[1] === "heat_mode_control" || mqtt_control[1] === "pwm_control") { topic = mqtt_control[1] } else return @@ -148,11 +142,15 @@ module.exports = function(RED) { context.set("window_mode", window_mode) } - if (topic === "heat_mode_control" && (payload === "on" || payload === "off" || payload === "auto" || payload === "auto-predict")) { + if (topic === "heat_mode_control" && (payload === "on" || payload === "off" || payload === "auto" || payload === "pwm")) { control_mode = payload context.set("control_mode", control_mode) } + if (topic === "pwm_control") { + context.set("pwm_duty_cycle", payload) + } + debug_text(`W-state: ${window_state}`) if (topic === "window" && (payload === "open" || payload === "close")) { window_state = payload