diff --git a/dist/MaxThermostat.js b/dist/MaxThermostat.js new file mode 100644 index 0000000..ab0fd23 --- /dev/null +++ b/dist/MaxThermostat.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const MqttDispatcher_1 = require("./MqttDispatcher"); +const AHomematicItem_1 = require("./AHomematicItem"); +// import { SwitchExport, ExportType } from './Export' +const WINDOW_OPEN_TEMPERATURE = 4.5; +class MaxThermostat extends AHomematicItem_1.AHomematicItem { + // Thermostat: homegear/instance1/set/3/1/SET_TEMPERATURE + constructor(floor, room, item, label, hmId, windowContacts) { + super(floor, room, item, label, hmId); + this.temperatureTopic = `${this.topicFirstPart}/temperature`; + this.temperatureFeedbackTopic = `${this.topicFirstPart}/temperature/feedback`; + this.deviceFeedbackTopic = `${this.deviceTopicPre}/1/SET_TEMPERATURE`; + this.actionTopic = `${this.actionTopicPre}/1/SET_TEMPERATURE`; + this.subscribeTopics = [ + this.temperatureTopic, + this.deviceFeedbackTopic + ]; + this.windowOpen = false; + this.windowContactMap = {}; + windowContacts.forEach((windowContact) => { + this.subscribeTopics.push(windowContact.getStateFeedbackTopic()); + this.windowContactMap[windowContact.getStateFeedbackTopic()] = { windowContact: windowContact, state: 'unknown' }; + }); + } + processMessage(topic, payload) { + if ((topic == this.temperatureTopic) || (topic == this.deviceFeedbackTopic)) { + this.temperature = parseFloat(payload); + } + else if (topic in this.windowContactMap) { + this.windowContactMap[topic].state = payload; + this.windowOpen = false; + Object.values(this.windowContactMap).forEach((w) => { + if (w.state == 'OPEN') { + this.windowOpen = true; + } + }); + } + if (!this.windowOpen) { + MqttDispatcher_1.mqttHandler.send(this.temperatureFeedbackTopic, `${this.temperature}`); + MqttDispatcher_1.mqttHandler.send(this.actionTopic, `${this.temperature}`); + } + else { + MqttDispatcher_1.mqttHandler.send(this.temperatureFeedbackTopic, `${WINDOW_OPEN_TEMPERATURE}`); + MqttDispatcher_1.mqttHandler.send(this.actionTopic, `${WINDOW_OPEN_TEMPERATURE}`); + } + } +} +exports.MaxThermostat = MaxThermostat; +//# sourceMappingURL=MaxThermostat.js.map \ No newline at end of file diff --git a/dist/MaxWindowContact.js b/dist/MaxWindowContact.js new file mode 100644 index 0000000..2cf0a99 --- /dev/null +++ b/dist/MaxWindowContact.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const MqttDispatcher_1 = require("./MqttDispatcher"); +const AHomematicItem_1 = require("./AHomematicItem"); +// import { SwitchExport, ExportType } from './Export' +class MaxWindowContact extends AHomematicItem_1.AHomematicItem { + getStateFeedbackTopic() { + return this.stateFeedbackTopic; + } + constructor(floor, room, item, label, hmId) { + super(floor, room, item, label, hmId); + this.stateTopic = `${this.topicFirstPart}/state`; + this.stateFeedbackTopic = `${this.topicFirstPart}/state/feedback`; + this.deviceFeedbackTopic = `${this.deviceTopicPre}/1/STATE`; + this.subscribeTopics = [ + this.stateTopic, + this.deviceFeedbackTopic + ]; + } + processMessage(topic, payload) { + this.state = payload; + MqttDispatcher_1.mqttHandler.send(this.stateFeedbackTopic, this.state); + } +} +exports.MaxWindowContact = MaxWindowContact; +//# sourceMappingURL=MaxWindowContact.js.map \ No newline at end of file diff --git a/dist/MqttDispatcher.js b/dist/MqttDispatcher.js index 30f7f45..8cfa26c 100644 --- a/dist/MqttDispatcher.js +++ b/dist/MqttDispatcher.js @@ -70,18 +70,18 @@ class MqttHandler { return found; } send(topic, payload, internalFirst = false) { - let sent = false; - if (internalFirst) { - logger.info(`Try internal sending: ${topic}`); - sent = this.processMessage(topic, payload); - } - if (!sent) { - logger.info(`External sending required: ${topic}`); - this.mqttClient.publish(topic, payload); - } - else { - logger.info(`Internally delivered: ${topic}`); - } + //let sent = false + //if (internalFirst) { + // logger.info(`Try internal sending: ${topic}`) + // sent = this.processMessage(topic, payload) + //} + //if (! sent) { + logger.info(`External sending required: ${topic}`); + let options = { retain: true, qos: 0 }; + this.mqttClient.publish(topic, payload, options); + //} else { + // logger.info(`Internally delivered: ${topic}`) + //} } } exports.mqttHandler = new MqttHandler(); diff --git a/dist/UrlSwitchItem.js b/dist/UrlSwitchItem.js new file mode 100644 index 0000000..6d27dc8 --- /dev/null +++ b/dist/UrlSwitchItem.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const MqttDispatcher_1 = require("./MqttDispatcher"); +const AItem_1 = require("./AItem"); +const Export_1 = require("./Export"); +const http = require("http"); +class UrlSwitchItem extends AItem_1.AItem { + getStateTopic() { + return this.stateTopic; + } + getStateFeedbackTopic() { + return this.stateFeedbackTopic; + } + constructor(floor, room, item, label, onUrl, offUrl, type = 'bulb') { + super(floor, room, item, label); + this.stateTopic = `${this.topicFirstPart}/state`; + this.subscribeTopics = [this.stateTopic]; + this.stateFeedbackTopic = `${this.topicFirstPart}/state/feedback`; + this.actionTopic = 'IoT/Mqtt433Gateway/Message'; + this.state = 'OFF'; + this.oldState = undefined; + this.onUrl = onUrl; + this.offUrl = offUrl; + this.type = type; + } + exportItem() { + return Export_1.SwitchExport(this.itemId, this.label, this.stateTopic, this.stateFeedbackTopic, this.type); + } + processMessage(topic, payload) { + this.state = payload; + MqttDispatcher_1.mqttHandler.send(this.stateFeedbackTopic, this.state); + if (this.state != this.oldState) { + if (this.state == 'ON') { + http.get(this.onUrl); + } + else { + http.get(this.offUrl); + } + this.oldState = this.state; + } + } +} +exports.UrlSwitchItem = UrlSwitchItem; +//# sourceMappingURL=UrlSwitchItem.js.map \ No newline at end of file diff --git a/dist/main.js b/dist/main.js index cc4eef0..b48995e 100644 --- a/dist/main.js +++ b/dist/main.js @@ -13,6 +13,8 @@ const HomematicSwitchItem_1 = require("./HomematicSwitchItem"); const Forwarder_1 = require("./Forwarder"); const Scene_1 = require("./Scene"); const MaxEcoSwitch_1 = require("./MaxEcoSwitch"); +const MaxThermostat_1 = require("./MaxThermostat"); +const MaxWindowContact_1 = require("./MaxWindowContact"); logger.info("Dispatcher starting"); let allLabeledItems = new Array(); // Anna ----------------------------------------------------------------------------------------------------- @@ -154,6 +156,12 @@ let testForwarder = new Forwarder_1.Forwarder('Gnd', 'Hallway', 'TestForwarder', testForwarder.start(); let testScene = new Scene_1.LightScene('Gnd', 'Hallway', 'TestScene', 'TestScene', [aquariumLight, annaBedLight], [matthiasStandLights, matthiasBedLight]); testScene.start(); +let windowContact1 = new MaxWindowContact_1.MaxWindowContact('Gnd', 'Bathroom', 'WindowContact1', 'Fenster Bad unten', 2); +windowContact1.start(); +let windowContact2 = new MaxWindowContact_1.MaxWindowContact('Gnd', 'Bathroom', 'WindowContact2', 'Fenster Bad unten', 20); +windowContact2.start(); +let thermostat1 = new MaxThermostat_1.MaxThermostat('Gnd', 'Bathroom', 'Thermostat', 'Thermostat Bad unten', 3, [windowContact1, windowContact2]); +thermostat1.start(); // ---------------------------------------------------------------------------------------------------------- // Homekit export let homekitObject = {}; diff --git a/src/MaxThermostat.ts b/src/MaxThermostat.ts index 4588fae..cf716a4 100644 --- a/src/MaxThermostat.ts +++ b/src/MaxThermostat.ts @@ -7,14 +7,18 @@ import { MaxWindowContact } from './MaxWindowContact'; const WINDOW_OPEN_TEMPERATURE = 4.5 +type WindowContactHolder = { + windowContact : MaxWindowContact + state : string +} + export class MaxThermostat extends AHomematicItem { private actionTopic: string private deviceFeedbackTopic: string private temperatureFeedbackTopic: string private temperatureTopic: string private temperature: number - private windowContacts: MaxWindowContact[] - private windowContactTopics: string[] + private windowContactMap: { [key:string]: WindowContactHolder } private windowOpen: boolean // Thermostat: homegear/instance1/set/3/1/SET_TEMPERATURE @@ -28,32 +32,34 @@ export class MaxThermostat extends AHomematicItem { this.temperatureTopic, this.deviceFeedbackTopic ] - this.windowContacts = windowContacts this.windowOpen = false - this.windowContactTopics = [] - this.windowContacts.forEach((windowContact) => { + this.windowContactMap = {} + windowContacts.forEach((windowContact) => { this.subscribeTopics.push(windowContact.getStateFeedbackTopic()) - this.windowContactTopics.push(windowContact.getStateFeedbackTopic()) + this.windowContactMap[windowContact.getStateFeedbackTopic()] = { windowContact: windowContact, state: 'unknown' } }) } processMessage(topic: string, payload: string) : void { - if (topic == this.temperatureTopic) { + if ((topic == this.temperatureTopic) || (topic == this.deviceFeedbackTopic)) { this.temperature = parseFloat(payload) - mqttHandler.send(this.temperatureFeedbackTopic, `${this.temperature}`) - mqttHandler.send(this.actionTopic, `${this.temperature}`) - } else if (topic == this.deviceFeedbackTopic) { - this.temperature = parseFloat(payload) - mqttHandler.send(this.temperatureFeedbackTopic, `${this.temperature}`) - } else if (this.windowContactTopics.indexOf(topic) >= 0) { - if (payload == 'CLOSED') { - - } else { - - } + } else if (topic in this.windowContactMap) { + this.windowContactMap[topic].state = payload + this.windowOpen = false + Object.values(this.windowContactMap).forEach((w) => { + if (w.state == 'OPEN') { + this.windowOpen = true + } + }) } - + if (! this.windowOpen) { + mqttHandler.send(this.temperatureFeedbackTopic, `${this.temperature}`) + mqttHandler.send(this.actionTopic, `${this.temperature}`) + } else { + mqttHandler.send(this.temperatureFeedbackTopic, `${WINDOW_OPEN_TEMPERATURE}`) + mqttHandler.send(this.actionTopic, `${WINDOW_OPEN_TEMPERATURE}`) + } } diff --git a/src/MqttDispatcher.ts b/src/MqttDispatcher.ts index 2b03cc8..18e8861 100644 --- a/src/MqttDispatcher.ts +++ b/src/MqttDispatcher.ts @@ -2,6 +2,7 @@ import * as logger from './log' import * as Mqtt from 'mqtt' import * as fs from 'fs' import * as config from './config' +import { IClientPublishOptions } from 'mqtt'; @@ -97,7 +98,8 @@ class MqttHandler { //} //if (! sent) { logger.info(`External sending required: ${topic}`) - this.mqttClient.publish(topic, payload) + let options : IClientPublishOptions = { retain: true, qos: 0 } + this.mqttClient.publish(topic, payload, options) //} else { // logger.info(`Internally delivered: ${topic}`) //} diff --git a/src/main.ts b/src/main.ts index c90692e..167f8e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,6 +15,8 @@ import { HomematicSwitchItem } from './HomematicSwitchItem' import { Forwarder } from './Forwarder' import { LightScene } from './Scene' import { MaxEcoSwitch } from './MaxEcoSwitch' +import { MaxThermostat } from './MaxThermostat' +import { MaxWindowContact } from './MaxWindowContact' logger.info("Dispatcher starting") @@ -211,6 +213,15 @@ let testScene = new LightScene('Gnd', 'Hallway', 'TestScene', 'TestScene', testScene.start() +let windowContact1 = new MaxWindowContact('Gnd', 'Bathroom', 'WindowContact1', 'Fenster Bad unten', 2) +windowContact1.start() +let windowContact2 = new MaxWindowContact('Gnd', 'Bathroom', 'WindowContact2', 'Fenster Bad unten', 20) +windowContact2.start() + +let thermostat1 = new MaxThermostat('Gnd', 'Bathroom', 'Thermostat', 'Thermostat Bad unten', 3, [windowContact1, windowContact2]) +thermostat1.start() + + // ---------------------------------------------------------------------------------------------------------- // Homekit export let homekitObject : { [key:string]:{} } = {}