UNPKG

homebridge-rinnai-touch-platform

Version:

Homebridge Plugin to control heating/cooling via a Rinnai Touch WiFi Module

499 lines 21.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HomeAssistantFormat = void 0; const RinnaiService_1 = require("../rinnai/RinnaiService"); class HomeAssistantFormat { constructor(platform, client) { this.platform = platform; this.client = client; this.subTopics = new Map(); this.topicPayloads = new Map(); this.modeMap = { 'heat': RinnaiService_1.OperatingModes.HEATING, 'cool': RinnaiService_1.OperatingModes.COOLING, 'evap': RinnaiService_1.OperatingModes.EVAPORATIVE_COOLING, }; this.prefix = this.platform.settings.mqtt.topicPrefix ? `${this.platform.settings.mqtt.topicPrefix}/` : ''; this.subTopics .set(`${this.prefix}hvac/fan_mode/set`, this.setHvacFanMode.bind(this)) .set(`${this.prefix}hvac/fan_speed/set`, this.setHvacFanSpeed.bind(this)) .set(`${this.prefix}hvac/mode/set`, this.setHvacMode.bind(this)) .set(`${this.prefix}hvac/temperature/set`, this.setHvacTemperature.bind(this)) .set(`${this.prefix}switch/zone/a/set`, this.setSwitchZone.bind(this)) .set(`${this.prefix}switch/zone/b/set`, this.setSwitchZone.bind(this)) .set(`${this.prefix}switch/zone/c/set`, this.setSwitchZone.bind(this)) .set(`${this.prefix}switch/zone/d/set`, this.setSwitchZone.bind(this)) .set(`${this.prefix}switch/heat/set`, this.setSwitchMode.bind(this)) .set(`${this.prefix}switch/cool/set`, this.setSwitchMode.bind(this)) .set(`${this.prefix}switch/evap/set`, this.setSwitchMode.bind(this)) .set(`${this.prefix}switch/fan/set`, this.setSwitchFan.bind(this)) .set(`${this.prefix}switch/manual/set`, this.setSwitchManual.bind(this)) .set(`${this.prefix}switch/manual/a/set`, this.setSwitchManual.bind(this)) .set(`${this.prefix}switch/manual/b/set`, this.setSwitchManual.bind(this)) .set(`${this.prefix}switch/manual/c/set`, this.setSwitchManual.bind(this)) .set(`${this.prefix}switch/manual/d/set`, this.setSwitchManual.bind(this)) .set(`${this.prefix}switch/pump/set`, this.setSwitchPump.bind(this)); // Publish on status change if (this.platform.settings.mqtt.publishStatusChanged) { this.platform.service.session.on('status', () => { this.publishTopics(); }); this.platform.temperatureService.on('temperature_change', () => { this.publishTopics(); }); } } get subscriptionTopics() { return [...this.subTopics.keys()]; } process(topic, payload) { this.platform.log.debug(this.constructor.name, 'process', topic, payload); try { const setValue = this.subTopics.get(topic); if (setValue) { setValue(topic, payload); } } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setHvacFanMode(topic, payload) { this.platform.log.debug(this.constructor.name, 'setHvacFanMode', topic, payload); try { if (!this.platform.service.getFanState()) { this.platform.log.warn('MQTT: Setting fan mode only supported for "fan_only" mode'); await this.publishTopics(); return; } let fanSpeed; switch (payload) { case 'low': fanSpeed = 5; break; case 'medium': fanSpeed = 10; break; case 'high': fanSpeed = 15; break; default: this.platform.log.warn(`MQTT: Invalid fan mode '${payload}' in payload`); await this.publishTopics(); return; } await this.platform.service.setFanSpeed(fanSpeed); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setHvacFanSpeed(topic, payload) { this.platform.log.debug(this.constructor.name, 'setHvacFanSpeed', topic, payload); try { if (!this.platform.service.getFanState()) { this.platform.log.warn('MQTT: Setting fan speed only supported for "fan_only" mode'); await this.publishTopics(); return; } const fanSpeed = parseInt(payload); if (isNaN(fanSpeed)) { this.platform.log.warn(`MQTT: Invalid fan speed specified: ${payload}`); return; } if (fanSpeed < 0 || fanSpeed > 16) { this.platform.log.warn(`MQTT: Fan speed ${fanSpeed} not between 1 and 16`); return; } await this.platform.service.setFanSpeed(fanSpeed); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setHvacMode(topic, payload) { this.platform.log.debug(this.constructor.name, 'setHvacMode', topic, payload); try { switch (payload) { case 'fan_only': await this.platform.service.setPowerState(false); await this.platform.service.setFanState(true); break; case 'off': await this.platform.service.setPowerState(false); await this.platform.service.setFanState(false); break; case 'heat': await this.platform.service.setFanState(false); await this.platform.service.setOperatingMode(RinnaiService_1.OperatingModes.HEATING); await this.platform.service.setPowerState(true); break; case 'cool': await this.platform.service.setFanState(false); await this.platform.service.setOperatingMode(this.platform.service.getHasEvaporative() ? RinnaiService_1.OperatingModes.EVAPORATIVE_COOLING : RinnaiService_1.OperatingModes.COOLING); await this.platform.service.setPowerState(true); break; default: this.platform.log.warn(`MQTT: Invalid mode '${payload}' in payload`); await this.publishTopics(); return; } } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setHvacTemperature(topic, payload) { this.platform.log.debug(this.constructor.name, 'setHvacTemperature', topic, payload); try { if (!this.platform.service.getPowerState() || this.platform.service.getFanState()) { this.platform.log.warn('MQTT: Setting temperature only supported for "heat" and "cool" modes'); await this.publishTopics(); return; } const json = JSON.parse(payload); if (typeof json === 'object') { for (const zone in json) { if (this.isValidTemperature(json[zone])) { await this.platform.service.setSetPointTemperature(json[zone], zone); } else { await this.publishTopics(); } } } else { if (this.isValidTemperature(json)) { await this.platform.service.setSetPointTemperature(json); } else { await this.publishTopics(); } } } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } isValidTemperature(temp) { this.platform.log.debug(this.constructor.name, 'isValidTemperature', temp); const value = parseInt(`${temp}`); if (isNaN(value)) { this.platform.log.warn(`MQTT: Invalid temperature specified: ${temp}`); return false; } if (value < 0 || value > 30) { this.platform.log.warn(`MQTT: Temperature ${temp} not between 0 and 30`); return false; } return true; } async setSwitchZone(topic, payload) { this.platform.log.debug(this.constructor.name, 'setSwitchZone', topic, payload); try { const zone = this.getTopicComponent(topic, -2).toUpperCase(); const value = payload.toLowerCase() === 'on'; await this.platform.service.setUserEnabled(value, zone); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setSwitchMode(topic, payload) { this.platform.log.debug(this.constructor.name, 'setSwitchMode', topic, payload); try { const mode = this.getTopicComponent(topic, -2).toLowerCase(); const operatingMode = this.modeMap[mode]; const state = payload.toLowerCase() === 'on'; if (this.platform.service.getFanState()) { await this.platform.service.setFanState(false); } if (state) { await this.platform.service.setOperatingMode(operatingMode); await this.platform.service.setPowerState(true); } else { await this.platform.service.setPowerState(false); } } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setSwitchFan(topic, payload) { this.platform.log.debug(this.constructor.name, 'setSwitchFan', topic, payload); try { const state = payload.toLowerCase() === 'on'; if (this.platform.service.getOperatingMode() === RinnaiService_1.OperatingModes.EVAPORATIVE_COOLING) { if (state && !this.platform.service.getPowerState()) { await this.platform.service.setPowerState(true); } await this.platform.service.setControlMode(RinnaiService_1.ControlModes.MANUAL); } else { if (state && this.platform.service.getPowerState()) { await this.platform.service.setPowerState(false); } } await this.platform.service.setFanState(state); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setSwitchManual(topic, payload) { this.platform.log.debug(this.constructor.name, 'setSwitchManual', topic, payload); try { if (!this.platform.service.getPowerState() && !this.platform.service.getFanState()) { this.platform.log.warn('MQTT: Setting manual operation not supported for "off" mode'); await this.publishTopics(); return; } let zone = this.getTopicComponent(topic, -2).toUpperCase(); zone = zone.length !== 1 ? 'U' : zone; const state = payload.toLowerCase() === 'on' ? RinnaiService_1.ControlModes.MANUAL : RinnaiService_1.ControlModes.AUTO; this.platform.service.setControlMode(state, zone); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } async setSwitchPump(topic, payload) { this.platform.log.debug(this.constructor.name, 'setSwitchPump', topic, payload); try { if (this.platform.service.getOperatingMode() !== RinnaiService_1.OperatingModes.EVAPORATIVE_COOLING) { this.platform.log.warn('MQTT: Setting pump state only supported when in "Evaporative Cooling" mode'); await this.publishTopics(); return; } if (!this.platform.service.getPowerState()) { this.platform.log.warn('MQTT: Setting pump state not supported for "off" mode'); await this.publishTopics(); return; } if (this.platform.service.getControlMode() !== RinnaiService_1.ControlModes.MANUAL) { this.platform.log.warn('MQTT: Setting pump state not supported for "auto" operation'); await this.publishTopics(); return; } const state = payload.toLowerCase() === 'on'; this.platform.service.setPumpState(state); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } getTopicComponent(topic, index) { const components = topic.split('/'); if (index >= 0) { return components[index]; } return components[components.length + index]; } async publishTopics() { this.platform.log.debug(this.constructor.name, 'publishTopics'); try { this.publishHvacAction(); this.publishHvacCurrentTemperature(); this.publishHvacFanMode(); this.publishHvacFanSpeed(); this.publishHvacMode(); this.publishHvacTemperature(); this.publishSwitchZone(); this.publishSwitchMode(); this.publishSwitchFan(); this.publishSwitchManual(); this.publishSwitchPump(); } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } publishHvacAction() { this.platform.log.debug(this.constructor.name, 'publishHvacAction'); const payload = {}; for (const zone of this.platform.service.getZonesInstalled()) { if (!this.platform.service.getUserEnabled(zone)) { payload[zone] = 'off'; continue; } if (this.platform.service.getFanState()) { payload[zone] = 'fan'; continue; } if (!this.platform.service.getPowerState()) { payload[zone] = 'off'; continue; } if (!this.platform.service.getAutoEnabled(zone)) { payload[zone] = 'idle'; continue; } payload[zone] = this.platform.service.getOperatingMode() === RinnaiService_1.OperatingModes.HEATING ? 'heating' : 'cooling'; } this.publish('hvac/action/get', JSON.stringify(payload)); } publishHvacCurrentTemperature() { this.platform.log.debug(this.constructor.name, 'publishHvacCurrentTemperature'); const payload = {}; for (const zone of this.platform.service.getZonesInstalled()) { const temperature = this.platform.temperatureService.getTemperature(zone); if (temperature !== undefined) { payload[zone] = temperature; } } this.publish('hvac/current_temperature/get', JSON.stringify(payload)); } publishHvacFanMode() { this.platform.log.debug(this.constructor.name, 'publishHvacFanMode'); const fanSpeed = this.platform.service.getFanSpeed(); let payload = 'low'; if (fanSpeed > 5) { payload = 'medium'; } if (fanSpeed > 10) { payload = 'high'; } this.publish('hvac/fan_mode/get', payload); } publishHvacFanSpeed() { this.platform.log.debug(this.constructor.name, 'publishHvacFanSpeed'); const payload = this.platform.service.getFanSpeed(); this.publish('hvac/fan_speed/get', String(payload)); } publishHvacMode() { this.platform.log.debug(this.constructor.name, 'publishHvacMode'); let payload; if (this.platform.service.getFanState()) { payload = 'fan_only'; } else if (!this.platform.service.getPowerState()) { payload = 'off'; } else if (this.platform.service.getOperatingMode() === RinnaiService_1.OperatingModes.HEATING) { payload = 'heat'; } else { payload = 'cool'; } this.publish('hvac/mode/get', payload); } publishHvacTemperature() { var _a, _b; this.platform.log.debug(this.constructor.name, 'publishHvacTemperature'); let payload; if (this.platform.service.getHasMultiSetPoint()) { payload = {}; for (const zone of this.platform.service.getZonesInstalled()) { if (this.platform.service.getSetPointTemperature(zone) !== undefined) { payload[zone] = (_a = this.platform.service.getSetPointTemperature(zone)) !== null && _a !== void 0 ? _a : 0; } } } else { payload = (_b = this.platform.service.getSetPointTemperature()) !== null && _b !== void 0 ? _b : 0; } this.publish('hvac/temperature/get', JSON.stringify(payload)); } publishSwitchZone() { this.platform.log.debug(this.constructor.name, 'publishSwitchZone'); const zonesInstalled = this.platform.service.getZonesInstalled(); for (const zone of ['A', 'B', 'C', 'D']) { if (zonesInstalled.includes(zone)) { const payload = this.platform.service.getUserEnabled(zone) ? 'on' : 'off'; this.publish(`switch/zone/${zone.toLowerCase()}/get`, payload); } else { this.publish(`switch/zone/${zone.toLowerCase()}/get`, 'off'); } } } publishSwitchMode() { this.platform.log.debug(this.constructor.name, 'publishSwitchMode'); let payload; for (const mode of ['heat', 'cool', 'evap']) { if (this.platform.service.getPowerState()) { payload = this.platform.service.getOperatingMode() === this.modeMap[mode] ? 'on' : 'off'; } else { payload = 'off'; } this.publish(`switch/${mode}/get`, payload); } } publishSwitchFan() { this.platform.log.debug(this.constructor.name, 'publishSwitchFan'); const payload = this.platform.service.getFanState() ? 'on' : 'off'; this.publish('switch/fan/get', payload); } publishSwitchManual() { this.platform.log.debug(this.constructor.name, 'publishSwitchManual'); if (this.platform.service.getHasMultiSetPoint() && this.platform.service.getOperatingMode() !== RinnaiService_1.OperatingModes.EVAPORATIVE_COOLING) { for (const zone of this.platform.service.getZonesInstalled()) { if (this.platform.service.getControlMode(zone)) { const payload = this.platform.service.getControlMode(zone) === RinnaiService_1.ControlModes.MANUAL ? 'on' : 'off'; this.publish(`switch/manual/${zone.toLowerCase()}/get`, payload); } } } else { const payload = this.platform.service.getControlMode() === RinnaiService_1.ControlModes.MANUAL ? 'on' : 'off'; this.publish('switch/manual/get', payload); } } publishSwitchPump() { this.platform.log.debug(this.constructor.name, 'publishSwitchPump'); const payload = this.platform.service.getPumpState() ? 'on' : 'off'; this.publish('switch/pump/get', payload); } async publish(topic, payload) { this.platform.log.debug(this.constructor.name, 'publish', topic, payload); try { if (payload === this.topicPayloads.get(topic) && !this.platform.settings.mqtt.publishAll) { return; } this.topicPayloads.set(topic, payload); await this.client.publish(`${this.prefix}${topic}`, payload, { retain: true }); if (this.platform.settings.mqtt.showMqttEvents) { this.platform.log.info(`MQTT: Publish: ${this.prefix}${topic}, Payload: ${payload}`); } } catch (error) { if (error instanceof Error) { this.platform.log.error(error.message); } } } } exports.HomeAssistantFormat = HomeAssistantFormat; //# sourceMappingURL=HomeAssistantFormat.js.map