Bump version to 0.1.11
This commit is contained in:
parent
e11f156961
commit
08f73c1b58
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vvzvlad/node-red-contrib-rn-combined-nodes",
|
"name": "@vvzvlad/node-red-contrib-rn-combined-nodes",
|
||||||
"version": "0.1.10",
|
"version": "0.1.11",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -15,7 +15,8 @@
|
|||||||
},
|
},
|
||||||
"node-red": {
|
"node-red": {
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"thermostat": "thermostat.js"
|
"thermostat": "thermostat.js",
|
||||||
|
"thermostat-analyzer": "thermostat-analyzer.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"author": "vvzvlad",
|
"author": "vvzvlad",
|
||||||
|
31
thermostat-analyzer.html
Normal file
31
thermostat-analyzer.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('c-thermostat-analyzer',{
|
||||||
|
category: 'Combined RN',
|
||||||
|
color: '#F3B567',
|
||||||
|
paletteLabel: 'Th-analyzer',
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
room: {value:"", required:true},
|
||||||
|
zone: {value:"", required:true}
|
||||||
|
},
|
||||||
|
inputs: 1,
|
||||||
|
outputs: 3,
|
||||||
|
outputLabels: ["heater","stats"],
|
||||||
|
icon: "font-awesome/fa-snowflake-o",
|
||||||
|
label: function() {
|
||||||
|
return this.name||"Th-analyzer";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="c-thermostat-analyzer">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="c-thermostat-analyzer">
|
||||||
|
<p>c-thermostat/p>
|
||||||
|
</script>
|
||||||
|
|
218
thermostat-analyzer.js
Normal file
218
thermostat-analyzer.js
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function thermostat_analyzer(config) {
|
||||||
|
RED.nodes.createNode(this,config);
|
||||||
|
this.room = config.room;
|
||||||
|
this.zone = config.zone;
|
||||||
|
var node = this;
|
||||||
|
var context = node.context();
|
||||||
|
|
||||||
|
|
||||||
|
const HEAT_ON_THRESHOLD = 0.2
|
||||||
|
const HEAT_OFF_THRESHOLD = 0.2
|
||||||
|
let debug = ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function debug_text(text) {
|
||||||
|
if (text === "return_debug") {
|
||||||
|
let debug_tmp = debug
|
||||||
|
debug = ""
|
||||||
|
return debug_tmp
|
||||||
|
}
|
||||||
|
debug = debug ? `${debug}, ${text}` : text
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
let topic = msg.topic
|
||||||
|
let payload = msg.payload
|
||||||
|
|
||||||
|
let control_mode = context.get("control_mode") || "auto" //auto, on, off
|
||||||
|
let current_temp = context.get("current_temp")
|
||||||
|
let target_temp = context.get("target_temp") || 19
|
||||||
|
let window_state = context.get("window_state") || "close" //open, close
|
||||||
|
let window_mode = context.get("window_mode") || "cool" //heat, cool, off
|
||||||
|
let heater_status = null
|
||||||
|
|
||||||
|
|
||||||
|
function logic() {
|
||||||
|
if (control_mode === "off") {
|
||||||
|
heater_status = 0
|
||||||
|
debug_text("Heater: force/off")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control_mode === "on") {
|
||||||
|
heater_status = 1
|
||||||
|
debug_text("Heater: force/on")
|
||||||
|
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
|
||||||
|
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 (current_temp <= target_temp - HEAT_ON_THRESHOLD) {
|
||||||
|
heater_status = 1
|
||||||
|
debug_text(`Heater: auto/${"on"}`)
|
||||||
|
} else if (current_temp >= target_temp + HEAT_OFF_THRESHOLD) {
|
||||||
|
heater_status = 0
|
||||||
|
debug_text(`Heater: auto/${"off"}`)
|
||||||
|
} else {
|
||||||
|
debug_text(`Heater: auto/${"hyst"}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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") {
|
||||||
|
topic = mqtt_control[1]
|
||||||
|
}
|
||||||
|
else return
|
||||||
|
}
|
||||||
|
else return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic === "window_mode_control" && (payload === "cool" || payload === "heat" || payload === "off")) {
|
||||||
|
window_mode = payload
|
||||||
|
context.set("window_mode", window_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic === "heat_mode_control" && (payload === "on" || payload === "off" || payload === "auto" || payload === "auto-predict")) {
|
||||||
|
control_mode = payload
|
||||||
|
context.set("control_mode", control_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_text(`W-state: ${window_state}`)
|
||||||
|
if (topic === "window" && (payload === "open" || payload === "close")) {
|
||||||
|
window_state = payload
|
||||||
|
context.set("window_state", window_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic === "target") {
|
||||||
|
target_temp = parseFloat(msg.payload)
|
||||||
|
context.set("target_temp", target_temp)
|
||||||
|
debug_text(`Current: ${current_temp}`)
|
||||||
|
debug_text(`Target: ${target_temp}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.topic === "current") {
|
||||||
|
current_temp = parseFloat(msg.payload)
|
||||||
|
context.set("current_temp", current_temp)
|
||||||
|
debug_text(`Current: ${current_temp}`)
|
||||||
|
debug_text(`Target: ${target_temp}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
logic()
|
||||||
|
|
||||||
|
let extended_stats = {
|
||||||
|
"control_mode": control_mode,
|
||||||
|
"current_temp": current_temp,
|
||||||
|
"target_temp": target_temp,
|
||||||
|
"window_state": window_state,
|
||||||
|
"window_mode": window_mode,
|
||||||
|
"heater_status": heater_status,
|
||||||
|
"HEAT_ON_THRESHOLD": HEAT_ON_THRESHOLD,
|
||||||
|
"HEAT_OFF_THRESHOLD": HEAT_OFF_THRESHOLD,
|
||||||
|
}
|
||||||
|
|
||||||
|
node.status({ text: debug_text("return_debug") })
|
||||||
|
|
||||||
|
let msg_heater_status = { payload: heater_status }
|
||||||
|
let msg_extended_stats = { payload: extended_stats, topic: node.zone + "_" + node.room }
|
||||||
|
|
||||||
|
if (heater_status === null || typeof heater_status === "undefined") {
|
||||||
|
msg_heater_status = null
|
||||||
|
}
|
||||||
|
|
||||||
|
node.send([msg_heater_status, msg_extended_stats]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("c-thermostat-analyzer", thermostat_analyzer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -173,10 +173,8 @@ module.exports = function(RED) {
|
|||||||
debug_text(`Target: ${target_temp}`)
|
debug_text(`Target: ${target_temp}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logic()
|
logic()
|
||||||
|
|
||||||
|
|
||||||
let extended_stats = {
|
let extended_stats = {
|
||||||
"control_mode": control_mode,
|
"control_mode": control_mode,
|
||||||
"current_temp": current_temp,
|
"current_temp": current_temp,
|
||||||
@ -188,12 +186,10 @@ module.exports = function(RED) {
|
|||||||
"HEAT_OFF_THRESHOLD": HEAT_OFF_THRESHOLD,
|
"HEAT_OFF_THRESHOLD": HEAT_OFF_THRESHOLD,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
node.status({ text: debug_text("return_debug") })
|
node.status({ text: debug_text("return_debug") })
|
||||||
|
|
||||||
let msg_heater_status = { payload: heater_status }
|
let msg_heater_status = { payload: heater_status }
|
||||||
let msg_extended_stats = { payload: extended_stats, room: node.room, zone: node.zone }
|
let msg_extended_stats = { payload: extended_stats, topic: node.zone + "_" + node.room }
|
||||||
|
|
||||||
if (heater_status === null || typeof heater_status === "undefined") {
|
if (heater_status === null || typeof heater_status === "undefined") {
|
||||||
msg_heater_status = null
|
msg_heater_status = null
|
||||||
|
Loading…
Reference in New Issue
Block a user