UNPKG

node-red-contrib-s-tech-automation-tools

Version:
257 lines (233 loc) 12.3 kB
module.exports = function(RED) { function STRegulator(config) { const node = this; RED.nodes.createNode(this, config); const server = RED.nodes.getNode(config.wbserver); const name = config.name; const locale = config.locale ? config.locale.split('-')[0] : 'en'; const driver = "S-Tech tools"; const basetopic = "/devices/"; const requestTopic = "/tmp/ST_items_list"; const responseTopic = basetopic + name; const metaenable = {}; const metatarget = {}; const metacurrent = {}; const metastate = {}; const metarelay = {}; const ParametrsNames = node.context().global.get("ParametrsNames"); const upto = {}; upto.en = " up to "; upto.ru = " до "; const to = {}; to.en = " to "; to.ru = " до "; var InitComplete = false; var device = {}; device.enable = null; device.target = null; device.current = 0; device.state = "off"; device.hysteresis = config.hysteresis; device.relay = 0; device.regultype = config.regultype; device.strategy = config.strategy; function SetAllMeta (){ var param; switch (device.regultype){ case "thermostat": param = "Temperature"; break; case "hydrostat": param = "Humidity"; break; default: param = "Temperature"; } metaenable.title = {"en":"Enable","ru":"Вкл."}; metaenable.readonly = false; metaenable.type = "switch"; metaenable.order = 1; metastate.title = {"en":ParametrsNames[param].state.en, "ru":ParametrsNames[param].state.ru}; metastate.readonly = true; metastate.type = "text"; metastate.order = 2; metacurrent.title = {"en":ParametrsNames[param].current.en, "ru":ParametrsNames[param].current.ru}; metacurrent.readonly = true; metacurrent.order = 3; metatarget.title = {"en":ParametrsNames[param].target.en, "ru":ParametrsNames[param].target.ru}; metatarget.min = config.min; metatarget.max = config.max; metatarget.readonly = false; metatarget.type = "range"; metatarget.order = 4; metarelay.title = {"en":ParametrsNames[param].relay.en, "ru":ParametrsNames[param].relay.ru}; metarelay.readonly = true; metarelay.type = "switch"; metarelay.order = 5; metacurrent.type = ParametrsNames[param].current.type; } server.on("online",()=>{ node.status({fill:"green",shape:"dot",text:"connect"}); }); server.on("offline",()=>{ node.status({fill:"red",shape:"dot",text:"no connect"}); }); function waitForMessage(timeout) { return new Promise((resolve, reject) => { var count = 0; const timer = setTimeout(() => { reject(new Error('Таймаут ожидания стартового сообщения')); }, timeout); function onMessage(receivedTopic, message) { const topicParts = receivedTopic.split('/'); if (topicParts[2] == name && topicParts.length == 5 && !(count == 2)) { if (topicParts[4] == "target") {device.target = message.toString(); count += 1; } if (topicParts[4] == "enable") {device.enable = message.toString(); count += 1; } if (count == 2) {clearTimeout(timer);} } } server.mqtt.on('message', onMessage); }); } async function requestStartValue() { try { server.publishToTopic(requestTopic, "get_value", false); await waitForMessage(500); // 500 мс таймаут } catch (error) { node.error(`Ошибка: ${error.message}`); device.target = config.target || config.min; device.enable = 0; node.log('Стартовое собщение не получено в течение 500 мс'); } } function WriteInitialValuesToMQTT() { var topic = basetopic + name; var meta = {}; meta.name = config.title; meta.title = {}; meta.title.ru = config.title; meta.driver = driver; server.publishToTopic(topic + "/meta/name", config.title, true); server.publishToTopic(topic + "/meta/driver", driver, true); server.publishToTopic(topic + "/meta", JSON.stringify(meta), true); server.publishToTopic(topic + "/controls/enable/meta", JSON.stringify(metaenable), true); server.publishToTopic(topic + "/controls/enable/on", device.enable.toString(), true); server.publishToTopic(topic + "/controls/target/meta", JSON.stringify(metatarget), true); server.publishToTopic(topic + "/controls/target/on", device.target.toString(), true); server.publishToTopic(topic + "/controls/current/meta", JSON.stringify(metacurrent), true); server.publishToTopic(topic + "/controls/state/meta", JSON.stringify(metastate), true); server.publishToTopic(topic + "/controls/relay/meta", JSON.stringify(metarelay), true); InitComplete = true; } function WriteValuesToMQTT() { var topic = basetopic + name; var statestr = ParametrsNames.Temperature.states.idle[locale]; if (device.strategy == "heating" && device.relay == 1 ) statestr = device.state.toString() + upto[locale] + device.target.toString() + "°C"; if (device.strategy == "cooling" && device.relay == 1 ) statestr = device.state.toString() + to[locale] + device.target.toString() + "°C"; if (device.strategy == "humidification" && device.relay == 1 ) statestr = device.state.toString() + upto[locale] + device.target.toString() + "%"; if (device.strategy == "drying" && device.relay == 1 ) statestr = device.state.toString() + to[locale] + device.target.toString() + "%"; if (device.enable == 0 ) statestr = device.state.toString(); node.status({fill:"green",shape:"dot", text:statestr}); if (config.invertrelay == true) device.relay = 1 - device.relay; server.publishToTopic(topic + "/controls/state", device.state.toString(), true); server.publishToTopic(topic + "/controls/target", device.target.toString(), true); server.publishToTopic(topic + "/controls/enable", device.enable.toString(), true); server.publishToTopic(topic + "/controls/relay", device.relay.toString(), true); server.publishToTopic(topic + "/controls/current", device.current.toString(), true); node.send({ payload: device.relay.toString() }); } this.on('input', (msg, send, done)=>{ if (InitComplete == false) return; if (typeof msg.payload != 'number'){ node.error("Неверный тип! msg.payload должен быть числом."); if (done) {done();} return; }; if (msg.payload == device.current){ node.debug("Значение не изменилось. Отмена обновления."); if (done) {done();} return; }; device.current = msg.payload; RegulatorLogic () WriteValuesToMQTT (); if (done) {done();} }); function RegulatorLogic () { if (device.enable == 0) { device.state = ParametrsNames.Temperature.states.off[locale]; device.relay = 0; } else switch (device.regultype) { case "thermostat": if (device.strategy == "heating"){ if ( Number(device.current) < Number(device.target) - Number(device.hysteresis)) { device.state = ParametrsNames.Temperature.states.head[locale]; device.relay = 1; break; } if ( Number(device.current) > Number(device.target) + Number(device.hysteresis)) { device.state = ParametrsNames.Temperature.states.idle[locale]; device.relay = 0; break; } }; if (device.strategy == "cooling") { if (Number(device.current) > Number(device.target) + Number(device.hysteresis)) { device.state = ParametrsNames.Temperature.states.cool[locale]; device.relay = 1; break; } if (Number(device.current) < Number(device.target) - Number(device.hysteresis)) { device.state = ParametrsNames.Temperature.states.idle[locale]; device.relay = 0; break; } }; break; case "hygrostat": if (device.strategy == "humidification"){ if ( Number(device.current) < Number(device.target) - Number(device.hysteresis)) { device.state = ParametrsNames.Humidity.states.humidification[locale]; device.relay = 1; break; } if ( Number(device.current) > Number(device.target) + Number(device.hysteresis)) { device.state = ParametrsNames.Humidity.states.idle[locale]; device.relay = 0; break; } }; if (device.strategy == "drying") { if (Number(device.current) > Number(device.target) + Number(device.hysteresis)) { device.state = ParametrsNames.Humidity.states.drying[locale]; device.relay = 1; break; } if (Number(device.current) < Number(device.target) - Number(device.hysteresis)) { device.state = ParametrsNames.Humidity.states.idle[locale]; device.relay = 0; break; } }; break; } } server.mqtt.on('message', (topic, message) => { if (InitComplete == false) return; const topicParts = topic.split('/'); if (topicParts[2] == name && topicParts[5] == "on") { if (topicParts[4] == "target") {device.target = message.toString(); RegulatorLogic (); WriteValuesToMQTT ();} if (topicParts[4] == "enable") {device.enable = message.toString(); RegulatorLogic (); WriteValuesToMQTT ();} } }); server.mqtt.subscribe(basetopic + name +"/#", function (err) { if (err) { node.error(`Ошибка подписки топик устройства ${basetopic + name +"/#"}: ${err.message}`); } else { node.log(`Успешно подписан на топик ${basetopic + name +"/#"}`); server.topicpush(basetopic + name +"/#"); SetAllMeta (); requestStartValue(); const checkInitComplete = setInterval(() => { if (!(device.target == null) && !(device.enable == null)) { clearInterval(checkInitComplete); WriteInitialValuesToMQTT(); } }, 100); } }); } RED.nodes.registerType("ST-Regulator", STRegulator); }