diff --git a/controller/Equipment.ts b/controller/Equipment.ts index b4da30ae..396f3a7f 100644 --- a/controller/Equipment.ts +++ b/controller/Equipment.ts @@ -75,10 +75,11 @@ interface IPoolSystem { export class PoolSystem implements IPoolSystem { public _hasChanged: boolean = false; public isReady: boolean = false; + private _startOCPTimer: NodeJS.Timeout; constructor() { this.cfgPath = path.posix.join(process.cwd(), '/data/poolConfig.json'); } - public getAvailableControllerTypes(include:string[] = ['easytouch', 'intellitouch', 'intellicenter', 'nixie']) { + public getAvailableControllerTypes(include:string[] = ['easytouch', 'intellitouch', 'intellicenter', 'suntouch', 'nixie']) { let arr = []; if (include.indexOf('easytouch')>=0) arr.push({ type: 'easytouch', name: 'EasyTouch', @@ -124,7 +125,14 @@ export class PoolSystem implements IPoolSystem { { val: 7, name: 'i10D', part: '523029Z', desc: 'IntelliCenter i10D', bodies: 2, valves: 2, circuits: 11, shared: false, dual: true, chlorinators: 2, chemControllers: 2 }, ] }); - if (include.indexOf('nixie')>=0) arr.push({ + if (include.indexOf('suntouch') >= 0) arr.push({ + type: 'suntouch', name: 'SunTouch', + models: [ + { val: 41, name: 'stshared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }, + { val: 40, name: 'stsingle', part: '520819', desc: 'Pool or Spa controller', bodies: 2, valves: 4, circuits: 5, single: true, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 } + ] + }) + if (include.indexOf('nixie') >= 0) arr.push({ type: 'nixie', name: 'Nixie', canChange: true, models: [ { val: 0, name: 'nxp', part: 'NXP', desc: 'Nixie Single Body', bodies: 1, shared: false, dual: false }, @@ -143,7 +151,9 @@ export class PoolSystem implements IPoolSystem { if (this.controllerType === 'unknown' || typeof this.controllerType === 'undefined') { // Delay for 7.5 seconds to give any OCPs a chance to start emitting messages. logger.info(`Listening for any installed OCPs`); - setTimeout(() => { self.initNixieController(); }, 7500); + if (this._startOCPTimer) clearTimeout(this._startOCPTimer); + this._startOCPTimer = null; + this._startOCPTimer = setTimeout(() => { self.initNixieController(); }, 7500); } else this.initNixieController(); @@ -217,6 +227,15 @@ export class PoolSystem implements IPoolSystem { } public resetSystem() { + logger.info(`Resetting System to initial defaults`); + (async () => { + await this.board.closeAsync(); + logger.info(`Closed ${this.controllerType} board`); + this.controllerType = ControllerType.Unknown; + })(); + + /* + let self = this; if (this.controllerType === ControllerType.Nixie) { (async () => { await this.board.closeAsync(); @@ -225,6 +244,10 @@ export class PoolSystem implements IPoolSystem { await board.initNixieBoard(); })(); state.status = 0; + + logger.info(`Listening for any installed OCPs`); + setTimeout(() => { self.initNixieController(); }, 7500); + } else { conn.pauseAll(); @@ -237,6 +260,7 @@ export class PoolSystem implements IPoolSystem { this.board = BoardFactory.fromControllerType(ControllerType.Unknown, this); setTimeout(function () { state.status = 0; conn.resumeAll(); }, 0); } + */ } public get anslq25ControllerType(): ControllerType { return this.data.anslq25.controllerType as ControllerType; } public set anslq25ControllerType(val: ControllerType) { @@ -261,12 +285,17 @@ export class PoolSystem implements IPoolSystem { // Only go in here if there is a change to the controller type. this.resetData(); // Clear the configuration data. state.resetData(); // Clear the state data. + state.status = 0; // We are performing a re-initialize. this.data.controllerType = val; EquipmentStateMessage.initDefaults(); // We are actually changing the config so lets clear out all the data. this.board = BoardFactory.fromControllerType(val, this); - if (this.data.controllerType === ControllerType.Unknown) setTimeout(() => { self.initNixieController(); }, 7500); + if (this.data.controllerType === ControllerType.Unknown) { + if (this._startOCPTimer) clearTimeout(this._startOCPTimer); + this._startOCPTimer = null; + this._startOCPTimer = setTimeout(() => { self.initNixieController(); }, 7500); + } } } public resetData() { diff --git a/controller/boards/NixieBoard.ts b/controller/boards/NixieBoard.ts index 9b8a66ad..31a4d6e5 100644 --- a/controller/boards/NixieBoard.ts +++ b/controller/boards/NixieBoard.ts @@ -437,10 +437,11 @@ export class NixieBoard extends SystemBoard { sys.board.heaters.updateHeaterServices(); state.cleanupState(); logger.info(`${sys.equipment.model} control board initialized`); - state.status = sys.board.valueMaps.controllerStatus.transform(1, 100); + //state.status = sys.board.valueMaps.controllerStatus.transform(1, 100); state.mode = sys.board.valueMaps.panelModes.encode('auto'); // At this point we should have the start of a board so lets check to see if we are ready or if we are stuck initializing. await setTimeout(5000); + state.status = sys.board.valueMaps.controllerStatus.transform(1, 100); await self.processStatusAsync(); } catch (err) { state.status = 255; logger.error(`Error Initializing Nixie Control Panel ${err.message}`); } } diff --git a/controller/boards/SunTouchBoard.ts b/controller/boards/SunTouchBoard.ts index 7852d51b..334e3a45 100644 --- a/controller/boards/SunTouchBoard.ts +++ b/controller/boards/SunTouchBoard.ts @@ -31,7 +31,7 @@ export class SunTouchBoard extends EasyTouchBoard { constructor(system: PoolSystem) { super(system); // graph chain to EasyTouchBoard constructor. this.valueMaps.expansionBoards = new byteValueMap([ - [41, { name: 'shared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }], + [41, { name: 'stshared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }], [40, { name: 'stsingle', part: '520819', desc: 'Pool or Spa controller', bodies: 2, valves: 4, circuits: 5, single: true, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }] ]); this._statusInterval = -1; diff --git a/controller/comms/messages/status/EquipmentStateMessage.ts b/controller/comms/messages/status/EquipmentStateMessage.ts index a25bd23e..13b0a5ce 100644 --- a/controller/comms/messages/status/EquipmentStateMessage.ts +++ b/controller/comms/messages/status/EquipmentStateMessage.ts @@ -121,8 +121,12 @@ export class EquipmentStateMessage { // Start over because we didn't have communication before but we now do. This will fall into the if // below so that it goes through the intialization process. In this case we didn't see an OCP when we started // but there clearly is one now. - sys.controllerType = ControllerType.Unknown; - state.status = 0; + (async () => { + await sys.board.closeAsync(); + logger.info(`Closed ${sys.controllerType} board`); + sys.controllerType = ControllerType.Unknown; + state.status = 0; + })(); } if (!state.isInitialized) { msg.isProcessed = true; diff --git a/web/interfaces/mqttInterface.ts b/web/interfaces/mqttInterface.ts index b0b6cb1c..3b08417a 100644 --- a/web/interfaces/mqttInterface.ts +++ b/web/interfaces/mqttInterface.ts @@ -68,12 +68,12 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { // make sure status is up to date immediately // especially in the case of a re-connect this.bindEvent("controller", state.controllerState); - } catch (err) { logger.error(err); } + } catch (err) { logger.error(`Error connecting to MQTT Broker ${this.cfg.name} ${err.message}`); } }); this.client.on('reconnect', () => { try { logger.info(`Re-connecting to MQTT broker ${this.cfg.name}`); - } catch (err) { logger.error(err); } + } catch (err) { logger.error(`Error reconnecting to MQTT Brokder ${this.cfg.name} ${err.message}`); } }); this.client.on('error', (error) => { @@ -374,7 +374,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { } } catch (err) { - logger.error(err); + logger.error(`Error binding MQTT event ${evt}: ${err.message}`); } } // This needed to be refactored so we could extract it from an anonymous function. We want to be able to unbind @@ -415,7 +415,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { try { if(typeof isOn !== 'undefined') await sys.board.circuits.setCircuitStateAsync(id, isOn); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; } case 'features': @@ -423,7 +423,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { try { if (typeof isOn !== 'undefined') await sys.board.features.setFeatureStateAsync(id, isOn); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; } case 'lightgroups': @@ -431,7 +431,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { try { if (typeof isOn !== 'undefined') await sys.board.circuits.setLightGroupStateAsync(id, isOn); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; } case 'circuitgroups': @@ -439,7 +439,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { try { if (typeof isOn !== 'undefined') await sys.board.circuits.setCircuitGroupStateAsync(id, isOn); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; } default: @@ -459,14 +459,14 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { try { await sys.board.circuits.toggleCircuitStateAsync(id); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'features': case 'feature': try { await sys.board.features.toggleFeatureStateAsync(id); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; default: logger.warn(`MQTT: Inbound topic ${topics[topics.length - 1]} not matched to event ${topics[topics.length - 2].toLowerCase()}. Message ${msg} `) @@ -489,7 +489,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { } } } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'coolsetpoint': try { @@ -506,7 +506,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { } } } - } catch (err) { logger.error(err); } + } catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'setpoint': try { @@ -530,7 +530,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { } } } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'heatmode': try { @@ -553,39 +553,39 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings { } let tbody = await sys.board.bodies.setHeatModeAsync(body, mode); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'chlorinator': try { let schlor = await sys.board.chlorinator.setChlorAsync(msg); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'chemcontroller': try { await sys.board.chemControllers.setChemControllerAsync(msg); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'settheme': try { let theme = await state.circuits.setLightThemeAsync(parseInt(msg.id, 10), sys.board.valueMaps.lightThemes.encode(msg.theme)); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'temp': case 'temps': try { await sys.board.system.setTempsAsync(msg); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; case 'tempsensor': case 'tempsensors': try { await sys.board.system.setTempSensorsAsync(msg); } - catch (err) { logger.error(err); } + catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); } break; default: logger.silly(`MQTT: Inbound MQTT topic not matched: ${topic}: ${message.toString()}`)