From cb0dabb883fedcd6987ecd58081334cc0bb2124a Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 02:40:52 +1100 Subject: [PATCH 01/13] wired zone sensor battery checks --- .gitignore | 3 ++ .vscode/settings.json | 2 +- config.schema.json | 19 ++++++++++++ src/hvac.ts | 6 ++-- src/hvacZone.ts | 3 +- src/masterControllerAccessory.ts | 8 ++--- src/platform.ts | 9 +++++- src/zoneControllerAccessory.ts | 50 ++++++++++++++++++-------------- 8 files changed, 69 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index aad6e46..6d07eb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +#test data +neo.json + # Ignore compiled code dist diff --git a/.vscode/settings.json b/.vscode/settings.json index 45d08a3..fabb386 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "files.eol": "\n", "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "editor.rulers": [ 140 ], "eslint.enable": true, diff --git a/config.schema.json b/config.schema.json index e7b8b03..bd86e7d 100644 --- a/config.schema.json +++ b/config.schema.json @@ -58,6 +58,25 @@ "required": false, "placeholder": "Leave Blank If You Have A Single Que System - Plugin Will Auto Discover" }, + "defineWiredSensors": { + "title": "Define Wired Zone Sensors to Disable Battery Checks", + "description": "All zones are assumed to be wireless by default with battery checks enabled", + "type": "boolean" + }, + "wiredSensors": { + "title": "Hardwired Zone Sensors", + "description": "Entering zone names here will disable battery checking on hardwired zones.", + "type": "array", + "required": false, + "condition": { + "functionBody": "return model.defineWiredSensors === true;" + }, + "items": { + "title": "Zone Name", + "description": "Name of Zone as Defined on Master Controller", + "type": "string" + } + }, "adjustThresholds": { "title": " Modify default heating cooling threshold temperatures", "description": "Cooling default min/max = 20/32. Heating default min/max = 10/26", diff --git a/src/hvac.ts b/src/hvac.ts index e1935b1..557abb0 100644 --- a/src/hvac.ts +++ b/src/hvac.ts @@ -33,7 +33,8 @@ export class HvacUnit { private readonly log: Logger, private readonly hbUserStoragePath: string, readonly zonesFollowMaster = true, - readonly zonesPushMaster = true) { + readonly zonesPushMaster = true, + readonly wiredZoneSensors: string[] = []) { this.name = name; } @@ -83,7 +84,8 @@ export class HvacUnit { if (targetInstance) { targetInstance.pushStatusUpdate(zone); } else { - this.zoneInstances.push(new HvacZone(this.log, this.apiInterface, zone)); + const zoneBatteryChecking = this.wiredZoneSensors.includes(zone.zoneName) ? true : false; + this.zoneInstances.push(new HvacZone(this.log, this.apiInterface, zone, zoneBatteryChecking)); } } return status; diff --git a/src/hvacZone.ts b/src/hvacZone.ts index c3fe655..6e3ca4e 100644 --- a/src/hvacZone.ts +++ b/src/hvacZone.ts @@ -22,6 +22,7 @@ export class HvacZone { private readonly log: Logger, readonly apiInterface: QueApi, zoneStatus: ZoneStatus, + readonly zoneBatteryChecking: boolean, ) { this.zoneName = zoneStatus.zoneName; @@ -42,7 +43,7 @@ export class HvacZone { this.zoneHumiditySensor = false; this.currentHumidity = 0; } else { - this.zoneHumiditySensor = false; + this.zoneHumiditySensor = true; this.currentHumidity = zoneStatus.currentHumidity; } } diff --git a/src/masterControllerAccessory.ts b/src/masterControllerAccessory.ts index 70eda25..4ff43bd 100644 --- a/src/masterControllerAccessory.ts +++ b/src/masterControllerAccessory.ts @@ -246,16 +246,16 @@ export class MasterControllerAccessory { async setFanMode(value: CharacteristicValue) { this.checkHvacComms(); switch (true) { - case (value <= 30): + case (+value <= 30): await this.platform.hvacInstance.setFanModeLow(); break; - case (value <= 60): + case (+value <= 60): await this.platform.hvacInstance.setFanModeMedium(); break; - case (value <= 90): + case (+value <= 90): await this.platform.hvacInstance.setFanModeHigh(); break; - case (value <= 100): + case (+value <= 100): await this.platform.hvacInstance.setFanModeAuto(); break; } diff --git a/src/platform.ts b/src/platform.ts index e49b0c5..df3efb6 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -20,6 +20,7 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { readonly userProvidedSerialNo: string = ''; readonly zonesFollowMaster: boolean = true; readonly zonesPushMaster: boolean = true; + readonly wiredZoneSensors: string[] = []; readonly hardRefreshInterval: number = 60000; readonly softRefreshInterval: number = 5000; readonly maxCoolingTemp: number = 32; @@ -54,6 +55,12 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { } else { this.zonesPushMaster = true; } + if (config['wiredZoneSensors']) { + this.wiredZoneSensors = config['wiredZoneSensors']; + this.log.debug('These zones are hardwired, battery checking is disabled', this.wiredZoneSensors); + } else { + this.wiredZoneSensors = []; + } if (config['refreshInterval']) { this.hardRefreshInterval = config['refreshInterval'] * 1000; this.log.debug('Auto refresh interval set to seconds', this.hardRefreshInterval/1000); @@ -122,7 +129,7 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { try { // Instantiate an instance of HvacUnit and connect the actronQueApi this.hvacInstance = new HvacUnit(this.clientName, this.log, this.api.user.storagePath(), - this.zonesFollowMaster, this.zonesPushMaster); + this.zonesFollowMaster, this.zonesPushMaster, this.wiredZoneSensors); let hvacSerial = ''; hvacSerial = await this.hvacInstance.actronQueApi(this.username, this.password, this.userProvidedSerialNo); // Make sure we have hvac master and zone data before adding devices diff --git a/src/zoneControllerAccessory.ts b/src/zoneControllerAccessory.ts index 66b403d..a17e050 100644 --- a/src/zoneControllerAccessory.ts +++ b/src/zoneControllerAccessory.ts @@ -8,7 +8,8 @@ export class ZoneControllerAccessory { private hvacService: Service; // some versions of the zone sensor do not support humidity private humidityService: Service | null; - private batteryService: Service; + // if sensors are hardwired it may be desirable to disable the battery service to avoid low battery alerts + private batteryService: Service | null; constructor( private readonly platform: ActronQuePlatform, @@ -29,10 +30,6 @@ export class ZoneControllerAccessory { // Set accessory display name, this is taken from discover devices in platform this.hvacService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); - // Get or create the humidity sensor service. - this.batteryService = this.accessory.getService(this.platform.Service.Battery) - || this.accessory.addService(this.platform.Service.Battery); - // Get or create the humidity sensor service if the zone sensor supports humidity readings if (this.zone.zoneHumiditySensor) { this.humidityService = this.accessory.getService(this.platform.Service.HumiditySensor) @@ -44,13 +41,20 @@ export class ZoneControllerAccessory { this.humidityService = null; } - // get battery low - this.batteryService.getCharacteristic(this.platform.Characteristic.StatusLowBattery) - .onGet(this.getBatteryStatus.bind(this)); - - // get battery level - this.batteryService.getCharacteristic(this.platform.Characteristic.BatteryLevel) - .onGet(this.getBatteryLevel.bind(this)); + // If the zone sensor is hardwired it may be desirable to disable the battery service to avoid low battery alerts + if (this.zone.zoneBatteryChecking) { + // Get or create the battery monitoring service. + this.batteryService = this.accessory.getService(this.platform.Service.Battery) + || this.accessory.addService(this.platform.Service.Battery); + // get battery low + this.batteryService.getCharacteristic(this.platform.Characteristic.StatusLowBattery) + .onGet(this.getBatteryStatus.bind(this)); + // get battery level + this.batteryService.getCharacteristic(this.platform.Characteristic.BatteryLevel) + .onGet(this.getBatteryLevel.bind(this)); + } else { + this.batteryService = null; + } // register handlers for device control, references the class methods that follow for Set and Get this.hvacService.getCharacteristic(this.platform.Characteristic.Active) @@ -101,8 +105,10 @@ export class ZoneControllerAccessory { this.hvacService.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.getCurrentTemperature()); this.hvacService.updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, this.getHeatingThresholdTemperature()); this.hvacService.updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, this.getCoolingThresholdTemperature()); - this.batteryService.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.getBatteryStatus()); - this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.getBatteryLevel()); + if (this.batteryService) { + this.batteryService.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.getBatteryStatus()); + this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.getBatteryLevel()); + } if (this.humidityService) { this.humidityService.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.getHumidity()); @@ -225,17 +231,17 @@ export class ZoneControllerAccessory { async setHeatingThresholdTemperature(value: CharacteristicValue) { this.checkHvacComms(); if (this.platform.hvacInstance.zonesPushMaster === true) { - if (value > this.zone.maxHeatSetPoint) { + if (+value > this.zone.maxHeatSetPoint) { await this.platform.hvacInstance.setHeatTemp(value as number); await this.platform.hvacInstance.getStatus(); - } else if (value < this.zone.minHeatSetPoint) { + } else if (+value < this.zone.minHeatSetPoint) { await this.platform.hvacInstance.setHeatTemp(value as number + 2); await this.platform.hvacInstance.getStatus(); } } else { - if (value > this.zone.maxHeatSetPoint) { + if (+value > this.zone.maxHeatSetPoint) { value = this.zone.maxHeatSetPoint; - } else if (value < this.zone.minHeatSetPoint) { + } else if (+value < this.zone.minHeatSetPoint) { value = this.zone.minHeatSetPoint; } } @@ -253,20 +259,20 @@ export class ZoneControllerAccessory { this.checkHvacComms(); if (this.platform.hvacInstance.zonesPushMaster === true) { this.platform.log.debug('zones push master is set to True'); - if (value > this.zone.maxCoolSetPoint) { + if (+value > this.zone.maxCoolSetPoint) { await this.platform.hvacInstance.setCoolTemp(value as number - 2); this.platform.log.debug(`Value is greater than MAX cool set point of ${this.zone.maxCoolSetPoint}, SETTING MASTER TO -> `, value); await this.platform.hvacInstance.getStatus(); - } else if (value < this.zone.minCoolSetPoint) { + } else if (+value < this.zone.minCoolSetPoint) { await this.platform.hvacInstance.setCoolTemp(value as number); this.platform.log.debug(`Value is less than MIN cool set point of ${this.zone.minCoolSetPoint}, SETTING MASTER TO -> `, value); await this.platform.hvacInstance.getStatus(); } } else { - if (value > this.zone.maxCoolSetPoint) { + if (+value > this.zone.maxCoolSetPoint) { value = this.zone.maxCoolSetPoint; this.platform.log.debug(`Value is greater than max cool set point of ${this.zone.maxCoolSetPoint}, CHANGING TO -> `, value); - } else if (value < this.zone.minCoolSetPoint) { + } else if (+value < this.zone.minCoolSetPoint) { value = this.zone.minCoolSetPoint; this.platform.log.debug(`Value is less than MIN cool set point of ${this.zone.minCoolSetPoint}, CHANGING TO -> `, value); } From 1e64abbe3fed1b482d9eac288f41c944a7b4bfe5 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 10:00:16 +1100 Subject: [PATCH 02/13] updated version to beta --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d5cb4d..1d41a83 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.7", + "version": "1.2.8-beta.0", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { From fceb76549b154c4fc04ab8b8d5a9c2e537444036 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 10:08:04 +1100 Subject: [PATCH 03/13] correct boolean result for zone batt check --- package.json | 2 +- src/hvac.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1d41a83..a661a79 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.8-beta.0", + "version": "1.2.8-beta.1", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { diff --git a/src/hvac.ts b/src/hvac.ts index 557abb0..9a674cb 100644 --- a/src/hvac.ts +++ b/src/hvac.ts @@ -84,7 +84,7 @@ export class HvacUnit { if (targetInstance) { targetInstance.pushStatusUpdate(zone); } else { - const zoneBatteryChecking = this.wiredZoneSensors.includes(zone.zoneName) ? true : false; + const zoneBatteryChecking = this.wiredZoneSensors.includes(zone.zoneName) ? false : true; this.zoneInstances.push(new HvacZone(this.log, this.apiInterface, zone, zoneBatteryChecking)); } } From 74bee28e2a01a076262626dba9c211a0767b6305 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 10:20:02 +1100 Subject: [PATCH 04/13] Correct schema naming for wired sensors --- config.schema.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.schema.json b/config.schema.json index bd86e7d..bfc00d0 100644 --- a/config.schema.json +++ b/config.schema.json @@ -58,18 +58,18 @@ "required": false, "placeholder": "Leave Blank If You Have A Single Que System - Plugin Will Auto Discover" }, - "defineWiredSensors": { + "defineWiredZoneSensors": { "title": "Define Wired Zone Sensors to Disable Battery Checks", "description": "All zones are assumed to be wireless by default with battery checks enabled", "type": "boolean" }, - "wiredSensors": { + "wiredZoneSensors": { "title": "Hardwired Zone Sensors", "description": "Entering zone names here will disable battery checking on hardwired zones.", "type": "array", "required": false, "condition": { - "functionBody": "return model.defineWiredSensors === true;" + "functionBody": "return model.defineWiredZoneSensors === true;" }, "items": { "title": "Zone Name", diff --git a/package.json b/package.json index a661a79..0d40266 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.8-beta.1", + "version": "1.2.8-beta.2", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { From 40a16dd8584779b3e1bca9a8409d1da17a4cc611 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 14:05:55 +1100 Subject: [PATCH 05/13] readme and schema updates --- README.md | 17 +++++++++++++++++ config.schema.json | 1 + package.json | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f3290a..be5e050 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ This is an 'almost' feature complete implementation of the Que platform in HomeK - Report battery level on zone sensors and get low battery alerts in the home app - Support for homebridge config UI + Fixes/Improvements in version 1.2.8 + - Provide option to disable battery checking for hard wired zone controllers (preventing erroneous low battery reports). Cached accessories will need to be removed for changes to take effect. + - Corrected error in humidity sensor detection that may have prevented humidity sensor from being added for zone sensors that support humidity reading. + Fixes/Improvements in version 1.2.7 - Allow master controller to also operate as a zone controller - Resolved issue with logic controlling "Zones Push Master" temp adjustments which was causing setting to fail on first attempt @@ -105,6 +109,10 @@ If you are not using the Homebridge config UI you can add the following to your             "zonesPushMaster": true | false, "refreshInterval": 60, "deviceSerial": "", + "wiredZoneSensors": [ + "Zone Name 1", + "Zone Name 2", + ], "maxCoolingTemp": 32, "minCoolingTemp": 20, "maxHeatingTemp": 26, @@ -181,6 +189,15 @@ default: "" In most cases you can exclude this option or leave it blank. If you only have a single air con system in your Que account the plugin will auto-discover the target device serial number. If you have multiple Que systems in your account you will need to specify which system you want to control by entering the serial number here. You can get your device serial numbers by logging in to que.actronair.com.au and looking at the list of authorised devices. +#### `wiredZoneSensors` + +type: array[string] (case sensitive) + +default: [] (empty array) + +An array of strings defining zone names utilising hardwired zone sensors. Zone names must match identically with the zone names configured within the Que controller. Be concious of case, spaces and any leading or trailing whitespace in the zone name. +Configuring zones as hardwired will prevent creation of a battery monitoring service and suppress erroneous low battery alerts for these sensors. + #### `maxCoolingTemp` type: number diff --git a/config.schema.json b/config.schema.json index bfc00d0..88d1dbd 100644 --- a/config.schema.json +++ b/config.schema.json @@ -74,6 +74,7 @@ "items": { "title": "Zone Name", "description": "Name of Zone as Defined on Master Controller", + "placeholder": "Enter zone name exactly as appears on controller - case sensitive", "type": "string" } }, diff --git a/package.json b/package.json index 0d40266..7077f34 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.8-beta.2", + "version": "1.2.8-beta.3", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { From 7508e60e8179c6fd6de51da1c45970c8e04613e4 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Wed, 17 Jan 2024 16:01:20 +1100 Subject: [PATCH 06/13] add failsafe normalisation for battery level greater than 100pc --- package.json | 2 +- src/hvacZone.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7077f34..f94c2a3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.8-beta.3", + "version": "1.2.8-beta.4", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { diff --git a/src/hvacZone.ts b/src/hvacZone.ts index 6e3ca4e..f259024 100644 --- a/src/hvacZone.ts +++ b/src/hvacZone.ts @@ -36,7 +36,8 @@ export class HvacZone { this.minCoolSetPoint = zoneStatus.minCoolSetPoint; this.currentHeatingSetTemp = zoneStatus.currentHeatingSetTemp; this.currentCoolingSetTemp = zoneStatus.currentCoolingSetTemp; - this.zoneSensorBattery = zoneStatus.zoneSensorBattery; + // wired sensors report battery level as 255; this prevents an error where battery level is set higher than 100 + this.zoneSensorBattery = zoneStatus.zoneSensorBattery > 100 ? 100 : zoneStatus.zoneSensorBattery; // some zone sensor versions do not report humidity if (zoneStatus.currentHumidity === 'notSupported') { @@ -57,7 +58,8 @@ export class HvacZone { this.minCoolSetPoint = zoneStatus.minCoolSetPoint; this.currentHeatingSetTemp = zoneStatus.currentHeatingSetTemp; this.currentCoolingSetTemp = zoneStatus.currentCoolingSetTemp; - this.zoneSensorBattery = zoneStatus.zoneSensorBattery; + // wired sensors report battery level as 255; this prevents an error where battery level is set higher than 100 + this.zoneSensorBattery = zoneStatus.zoneSensorBattery > 100 ? 100 : zoneStatus.zoneSensorBattery; this.currentHumidity = this.zoneHumiditySensor ? zoneStatus.currentHumidity as number : 0; } From ef6172fde0fbea594984a9c5e7bb1a7508883de6 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Thu, 18 Jan 2024 23:32:34 +1100 Subject: [PATCH 07/13] Support for running fan only --- .vscode/settings.json | 1 + config.schema.json | 5 ++ package.json | 2 +- src/fanOnlyMasterAccessory.ts | 127 +++++++++++++++++++++++++++++++ src/fanOnlyZoneAccessory.ts | 82 ++++++++++++++++++++ src/masterControllerAccessory.ts | 34 ++++++--- src/platform.ts | 59 ++++++++++++-- src/zoneControllerAccessory.ts | 14 +++- 8 files changed, 303 insertions(+), 21 deletions(-) create mode 100644 src/fanOnlyMasterAccessory.ts create mode 100644 src/fanOnlyZoneAccessory.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index fabb386..16eea8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "clientid", "cmds", "EHOSTDOWN", + "Fanv", "hvac", "refreshinterval", "Setpoint", diff --git a/config.schema.json b/config.schema.json index 88d1dbd..f1cdcfe 100644 --- a/config.schema.json +++ b/config.schema.json @@ -58,6 +58,11 @@ "required": false, "placeholder": "Leave Blank If You Have A Single Que System - Plugin Will Auto Discover" }, + "fanOnlyDevices": { + "title": "Create FAN ONLY devices for each zone", + "description": "Fan Only devices allow you to run the system in FAN mode", + "type": "boolean" + }, "defineWiredZoneSensors": { "title": "Define Wired Zone Sensors to Disable Battery Checks", "description": "All zones are assumed to be wireless by default with battery checks enabled", diff --git a/package.json b/package.json index f94c2a3..7314145 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.2.8-beta.4", + "version": "1.5.0-alpha.0", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { diff --git a/src/fanOnlyMasterAccessory.ts b/src/fanOnlyMasterAccessory.ts new file mode 100644 index 0000000..440afb5 --- /dev/null +++ b/src/fanOnlyMasterAccessory.ts @@ -0,0 +1,127 @@ +import { Service, PlatformAccessory, CharacteristicValue, HAPStatus } from 'homebridge'; +import { ClimateMode, FanMode, PowerState } from './types'; +import { ActronQuePlatform } from './platform'; + +// This class represents the master controller +export class FanOnlyMasterAccessory { + private fanService: Service; + + constructor( + private readonly platform: ActronQuePlatform, + private readonly accessory: PlatformAccessory, + ) { + + // set accessory information + this.accessory.getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Actron') + .setCharacteristic(this.platform.Characteristic.Model, this.platform.hvacInstance.type + ' FanOnlyMaster') + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.platform.hvacInstance.serialNo); + + // Get or create the fan service. + this.fanService = this.accessory.getService(this.platform.Service.Fanv2) + || this.accessory.addService(this.platform.Service.Fanv2); + + // Set accessory display name, this is taken from discover devices in platform + this.fanService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + + // register handlers for device control, references the class methods that follow for Set and Get + this.fanService.getCharacteristic(this.platform.Characteristic.Active) + .onSet(this.setPowerState.bind(this)) + .onGet(this.getPowerState.bind(this)); + + this.fanService.getCharacteristic(this.platform.Characteristic.RotationSpeed) + .onSet(this.setFanMode.bind(this)) + .onGet(this.getFanMode.bind(this)); + + setInterval(() => this.softUpdateDeviceCharacteristics(), this.platform.softRefreshInterval); + + } + + // SET's are async as these need to wait on API response then cache the return value on the hvac Class instance + // GET's run non async as this is a quick retrieval from the hvac class instance cache + // UPDATE is run Async as this polls the API first to confirm current cache state is accurate + async softUpdateDeviceCharacteristics() { + this.fanService.updateCharacteristic(this.platform.Characteristic.Active, this.getPowerState()); + this.fanService.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.getFanMode()); + } + + checkHvacComms() { + if (!this.platform.hvacInstance.cloudConnected) { + this.platform.log.error('Master Controller is offline. Check Master Controller Internet/Wifi connection'); + throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); + } + } + + async setPowerState(value: CharacteristicValue) { + this.checkHvacComms(); + switch (value) { + case 0: + await this.platform.hvacInstance.setPowerStateOff(); + break; + case 1: + await this.platform.hvacInstance.setPowerStateOn(); + await this.platform.hvacInstance.setClimateModeFan(); + break; + } + this.platform.log.debug('Set FanOnlyMaster Power State -> ', value); + } + + getPowerState(): CharacteristicValue { + // Check climate mode, if it is any value other than FAN then we are not in fan-only mode + // If it is FAN then we need to check if the system is powered on + let powerState: number; + const climateMode = this.platform.hvacInstance.climateMode; + switch (climateMode) { + case ClimateMode.FAN: + powerState = (this.platform.hvacInstance.powerState === PowerState.ON) ? 1 : 0; + break; + default: + powerState = 0; + } + this.platform.log.debug('Got FanOnlyMaster Power State -> ', powerState); + return powerState; + } + + async setFanMode(value: CharacteristicValue) { + this.checkHvacComms(); + switch (true) { + case (+value <= 10): + await this.platform.hvacInstance.setFanModeAuto(); + break; + case (+value <= 30): + await this.platform.hvacInstance.setFanModeLow(); + break; + case (+value <= 65): + await this.platform.hvacInstance.setFanModeMedium(); + break; + case (+value <= 100): + await this.platform.hvacInstance.setFanModeHigh(); + break; + } + this.platform.log.debug('Set FanOnlyMaster Fan Mode 1-10:Auto, 11-30:Low, 31-65:Medium, 66-100:High -> ', value); + } + + getFanMode(): CharacteristicValue { + let currentMode: number; + const fanMode = this.platform.hvacInstance.fanMode; + switch (fanMode) { + case FanMode.AUTO || FanMode.AUTO_CONT: + currentMode = 10; + break; + case FanMode.LOW || FanMode.LOW_CONT: + currentMode = 25; + break; + case FanMode.MEDIUM || FanMode.MEDIUM_CONT: + currentMode = 50; + break; + case FanMode.HIGH || FanMode.HIGH_CONT: + currentMode = 100; + break; + default: + currentMode = 0; + this.platform.log.debug('Failed To Get FanOnlyMaster Current Fan Mode -> ', fanMode); + } + this.platform.log.debug('Got FanOnlyMaster Current Fan Mode -> ', fanMode); + return currentMode; + } +} \ No newline at end of file diff --git a/src/fanOnlyZoneAccessory.ts b/src/fanOnlyZoneAccessory.ts new file mode 100644 index 0000000..8b6e8aa --- /dev/null +++ b/src/fanOnlyZoneAccessory.ts @@ -0,0 +1,82 @@ +import { Service, PlatformAccessory, CharacteristicValue, HAPStatus } from 'homebridge'; +import { ClimateMode } from './types'; +import { ActronQuePlatform } from './platform'; +import { HvacZone } from './hvacZone'; + +// This class represents the zone controller +export class FanOnlyZoneAccessory { + private fanService: Service; + + constructor( + private readonly platform: ActronQuePlatform, + private readonly accessory: PlatformAccessory, + private readonly zone: HvacZone, + ) { + + // set accessory information + this.accessory.getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Actron') + .setCharacteristic(this.platform.Characteristic.Model, this.platform.hvacInstance.type + ' FanOnlyZone') + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.zone.sensorId); + + // Get or create the fan service. + this.fanService = this.accessory.getService(this.platform.Service.Fanv2) + || this.accessory.addService(this.platform.Service.Fanv2); + + // Set accessory display name, this is taken from discover devices in platform + this.fanService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + + // register handlers for device control, references the class methods that follow for Set and Get + this.fanService.getCharacteristic(this.platform.Characteristic.Active) + .onSet(this.setEnableState.bind(this)) + .onGet(this.getEnableState.bind(this)); + + + setInterval(() => this.softUpdateDeviceCharacteristics(), this.platform.softRefreshInterval); + + } + + // SET's are async as these need to wait on API response then cache the return value on the hvac Class instance + // GET's run non async as this is a quick retrieval from the hvac class instance cache + // UPDATE is run Async as this polls the API first to confirm current cache state is accurate + async softUpdateDeviceCharacteristics() { + this.fanService.updateCharacteristic(this.platform.Characteristic.Active, this.getEnableState()); + } + + checkHvacComms() { + if (!this.platform.hvacInstance.cloudConnected) { + this.platform.log.error('Master Controller is offline. Check Master Controller Internet/Wifi connection'); + throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); + } + } + + async setEnableState(value: CharacteristicValue) { + this.checkHvacComms(); + switch (value) { + case 0: + await this.zone.setZoneDisable(); + break; + case 1: + await this.zone.setZoneEnable(); + await this.platform.hvacInstance.setClimateModeFan(); + break; + } + this.platform.log.debug(`Set FanOnlyZone ${this.zone.zoneName} Enable State -> `, value); + } + + getEnableState(): CharacteristicValue { + // Check climate mode, if it is any value other than FAN then we are not in fan-only mode + // If it is FAN then we need to check if the zone is enabled + let enableState: number; + const climateMode = this.platform.hvacInstance.climateMode; + switch (climateMode) { + case ClimateMode.FAN: + enableState = (this.zone.zoneEnabled === true) ? 1 : 0; + break; + default: + enableState = 0; + } + this.platform.log.debug(`Got FanOnlyZone ${this.zone.zoneName} Enable State -> `, enableState); + return enableState; + } +} \ No newline at end of file diff --git a/src/masterControllerAccessory.ts b/src/masterControllerAccessory.ts index 4ff43bd..5b9d43d 100644 --- a/src/masterControllerAccessory.ts +++ b/src/masterControllerAccessory.ts @@ -134,7 +134,13 @@ export class MasterControllerAccessory { } getPowerState(): CharacteristicValue { - const powerState = (this.platform.hvacInstance.powerState === PowerState.ON) ? 1 : 0; + // Report as powered off if the fan mode is set to FAN + let powerState: number; + if (this.platform.hvacInstance.climateMode === ClimateMode.FAN) { + powerState = 0; + } else { + powerState = (this.platform.hvacInstance.powerState === PowerState.ON) ? 1 : 0; + } // this.platform.log.debug('Got Master Power State -> ', powerState); return powerState; } @@ -195,6 +201,10 @@ export class MasterControllerAccessory { case ClimateMode.COOL: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; + // Setting climate mode to Auto when fan-only is running + case ClimateMode.FAN: + currentMode = this.platform.Characteristic.TargetHeaterCoolerState.AUTO; + break; default: currentMode = 0; this.platform.log.debug('Failed To Get Master Target Climate Mode -> ', climateMode); @@ -246,20 +256,20 @@ export class MasterControllerAccessory { async setFanMode(value: CharacteristicValue) { this.checkHvacComms(); switch (true) { + case (+value <= 10): + await this.platform.hvacInstance.setFanModeAuto(); + break; case (+value <= 30): await this.platform.hvacInstance.setFanModeLow(); break; - case (+value <= 60): + case (+value <= 65): await this.platform.hvacInstance.setFanModeMedium(); break; - case (+value <= 90): - await this.platform.hvacInstance.setFanModeHigh(); - break; case (+value <= 100): - await this.platform.hvacInstance.setFanModeAuto(); + await this.platform.hvacInstance.setFanModeHigh(); break; } - this.platform.log.debug('Set Master Fan Mode 91-100:Auto, 1-30:Low, 31-60:Medium, 61-90:High -> ', value); + this.platform.log.debug('Set Master Fan Mode 1-10:Auto, 11-30:Low, 31-65:Medium, 66-100:High -> ', value); } getFanMode(): CharacteristicValue { @@ -267,22 +277,22 @@ export class MasterControllerAccessory { const fanMode = this.platform.hvacInstance.fanMode; switch (fanMode) { case FanMode.AUTO || FanMode.AUTO_CONT: - currentMode = 100; + currentMode = 10; break; case FanMode.LOW || FanMode.LOW_CONT: - currentMode = 29; + currentMode = 25; break; case FanMode.MEDIUM || FanMode.MEDIUM_CONT: - currentMode = 59; + currentMode = 50; break; case FanMode.HIGH || FanMode.HIGH_CONT: - currentMode = 89; + currentMode = 100; break; default: currentMode = 0; this.platform.log.debug('Failed To Get Master Current Fan Mode -> ', fanMode); } - // this.platform.log.debug('Got Master Current Fan Mode -> ', fanMode); + this.platform.log.debug('Got Master Current Fan Mode -> ', fanMode); return currentMode; } } \ No newline at end of file diff --git a/src/platform.ts b/src/platform.ts index df3efb6..5912b0f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -3,6 +3,8 @@ import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, import { PLATFORM_NAME, PLUGIN_NAME } from './settings'; import { MasterControllerAccessory } from './masterControllerAccessory'; import { ZoneControllerAccessory } from './zoneControllerAccessory'; +import { FanOnlyMasterAccessory } from './fanOnlyMasterAccessory'; +import { FanOnlyZoneAccessory } from './fanOnlyZoneAccessory'; import { OutdoorUnitAccessory } from './outdoorUnitAccessory'; import { HvacUnit } from './hvac'; import { HvacZone } from './hvacZone'; @@ -20,6 +22,7 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { readonly userProvidedSerialNo: string = ''; readonly zonesFollowMaster: boolean = true; readonly zonesPushMaster: boolean = true; + readonly fanOnlyDevices: boolean = false; readonly wiredZoneSensors: string[] = []; readonly hardRefreshInterval: number = 60000; readonly softRefreshInterval: number = 5000; @@ -55,6 +58,12 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { } else { this.zonesPushMaster = true; } + if (config['fanOnlyDevices']) { + this.fanOnlyDevices = config['fanOnlyDevices']; + this.log.debug('Create Fan Only devices set to', this.fanOnlyDevices); + } else { + this.fanOnlyDevices = false; + } if (config['wiredZoneSensors']) { this.wiredZoneSensors = config['wiredZoneSensors']; this.log.debug('These zones are hardwired, battery checking is disabled', this.wiredZoneSensors); @@ -63,7 +72,7 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { } if (config['refreshInterval']) { this.hardRefreshInterval = config['refreshInterval'] * 1000; - this.log.debug('Auto refresh interval set to seconds', this.hardRefreshInterval/1000); + this.log.debug('Auto refresh interval set to seconds', this.hardRefreshInterval / 1000); } else { this.hardRefreshInterval = 60000; } @@ -156,11 +165,27 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { instance: zone, }); } + if (this.fanOnlyDevices) { + devices.push({ + type: 'fanOnlyMaster', + uniqueId: hvacSerial + '-fanOnly', + displayName: this.clientName + '-fanOnly', + instance: this.hvacInstance, + }); + for (const zone of this.hvacInstance.zoneInstances) { + devices.push({ + type: 'fanOnlyZone', + uniqueId: zone.sensorId + '-fanOnly', + displayName: zone.zoneName + '-fanOnly', + instance: zone, + }); + } + } this.log.debug('Discovered Devices \n', devices); // loop over the discovered devices and register each one if it has not already been registered for (const device of devices) { - // create uuid first then see if an accessory with the same uuid has already been registered and restored from - // the cached devices we stored in the `configureAccessory` method above + // create uuid first then see if an accessory with the same uuid has already been registered and restored from + // the cached devices we stored in the `configureAccessory` method above const uuid = this.api.hap.uuid.generate(device.uniqueId); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); @@ -177,26 +202,48 @@ export class ActronQuePlatform implements DynamicPlatformPlugin { this.log.info('Restoring Outdoor Unit accessory from cache:', existingAccessory.displayName); new OutdoorUnitAccessory(this, existingAccessory); - } else if (!existingAccessory && device.type === 'masterController'){ + } else if (existingAccessory && device.type === 'fanOnlyMaster') { + this.log.info('Restoring Fan Only Master accessory from cache:', existingAccessory.displayName); + new FanOnlyMasterAccessory(this, existingAccessory); + + } else if (existingAccessory && device.type === 'fanOnlyZone') { + this.log.info('Restoring Fan only Zone accessory from cache:', existingAccessory.displayName); + new FanOnlyZoneAccessory(this, existingAccessory, device.instance as HvacZone); + + } else if (!existingAccessory && device.type === 'masterController') { this.log.info('Adding new accessory:', device.displayName); const accessory = new this.api.platformAccessory(device.displayName, uuid); accessory.context.device = device; new MasterControllerAccessory(this, accessory); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); - } else if (!existingAccessory && device.type === 'zoneController'){ + } else if (!existingAccessory && device.type === 'zoneController') { this.log.info('Adding new accessory:', device.displayName); const accessory = new this.api.platformAccessory(device.displayName, uuid); accessory.context.device = device; new ZoneControllerAccessory(this, accessory, device.instance as HvacZone); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); - } else if (!existingAccessory && device.type === 'outdoorUnit'){ + } else if (!existingAccessory && device.type === 'outdoorUnit') { this.log.info('Adding new accessory:', device.displayName); const accessory = new this.api.platformAccessory(device.displayName, uuid); accessory.context.device = device; new OutdoorUnitAccessory(this, accessory); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); + + } else if (!existingAccessory && device.type === 'fanOnlyMaster') { + this.log.info('Adding new accessory:', device.displayName); + const accessory = new this.api.platformAccessory(device.displayName, uuid); + accessory.context.device = device; + new FanOnlyMasterAccessory(this, accessory); + this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); + + } else if (!existingAccessory && device.type === 'fanOnlyZone') { + this.log.info('Adding new accessory:', device.displayName); + const accessory = new this.api.platformAccessory(device.displayName, uuid); + accessory.context.device = device; + new FanOnlyZoneAccessory(this, accessory, device.instance as HvacZone); + this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } } catch (error) { diff --git a/src/zoneControllerAccessory.ts b/src/zoneControllerAccessory.ts index a17e050..ffaf4df 100644 --- a/src/zoneControllerAccessory.ts +++ b/src/zoneControllerAccessory.ts @@ -155,8 +155,18 @@ export class ZoneControllerAccessory { } getEnableState(): CharacteristicValue { - const enableState = (this.zone.zoneEnabled === true) ? 1 : 0; - // this.platform.log.debug(`Got Zone ${this.zone.zoneName} Enable State -> `, enableState); + // Check climate mode, if it is any value other than FAN then we are not in fan-only mode + // If it is FAN then we need to check if the zone is enabled + let enableState: number; + const climateMode = this.platform.hvacInstance.climateMode; + switch (climateMode) { + case ClimateMode.FAN: + enableState = 0; + break; + default: + enableState = (this.zone.zoneEnabled === true) ? 1 : 0; + } + this.platform.log.debug(`Got Zone ${this.zone.zoneName} Enable State -> `, enableState); return enableState; } From 4fb2d068e372e295cec7cd1fba6008e7e6e2ed2d Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Thu, 18 Jan 2024 23:50:36 +1100 Subject: [PATCH 08/13] return valid mode when fan only running. --- src/masterControllerAccessory.ts | 2 +- src/zoneControllerAccessory.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/masterControllerAccessory.ts b/src/masterControllerAccessory.ts index 5b9d43d..596f843 100644 --- a/src/masterControllerAccessory.ts +++ b/src/masterControllerAccessory.ts @@ -201,7 +201,7 @@ export class MasterControllerAccessory { case ClimateMode.COOL: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; - // Setting climate mode to Auto when fan-only is running + // Returning climate mode of Auto when fan-only is running case ClimateMode.FAN: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.AUTO; break; diff --git a/src/zoneControllerAccessory.ts b/src/zoneControllerAccessory.ts index ffaf4df..8d703ae 100644 --- a/src/zoneControllerAccessory.ts +++ b/src/zoneControllerAccessory.ts @@ -224,6 +224,10 @@ export class ZoneControllerAccessory { case ClimateMode.COOL: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; + // Returning climate mode of Auto when fan-only is running + case ClimateMode.FAN: + currentMode = this.platform.Characteristic.TargetHeaterCoolerState.AUTO; + break; default: currentMode = 0; this.platform.log.debug('Failed To Get Target Climate Mode -> ', climateMode); From 3d3e09ee8894bd09fe260e5615750d4454a44b86 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Thu, 18 Jan 2024 23:51:58 +1100 Subject: [PATCH 09/13] increment alpha ver --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7314145..3bad296 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.5.0-alpha.0", + "version": "1.5.0-alpha.1", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { From 4c882691df3b82c08d84903f94606fa40031f5a0 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Sat, 20 Jan 2024 21:30:40 +1100 Subject: [PATCH 10/13] additional logic to deal with switching from fan to cool and vice versa --- src/fanOnlyMasterAccessory.ts | 10 ++++++++-- src/fanOnlyZoneAccessory.ts | 10 ++++++++-- src/masterControllerAccessory.ts | 9 ++++++++- src/zoneControllerAccessory.ts | 9 ++++++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/fanOnlyMasterAccessory.ts b/src/fanOnlyMasterAccessory.ts index 440afb5..eb8e4bc 100644 --- a/src/fanOnlyMasterAccessory.ts +++ b/src/fanOnlyMasterAccessory.ts @@ -59,8 +59,14 @@ export class FanOnlyMasterAccessory { await this.platform.hvacInstance.setPowerStateOff(); break; case 1: - await this.platform.hvacInstance.setPowerStateOn(); - await this.platform.hvacInstance.setClimateModeFan(); + // If the fan only mode is not running, switch climate mode to fan instead of sending power on event + if (this.platform.hvacInstance.climateMode !== ClimateMode.FAN) { + await this.platform.hvacInstance.setClimateModeFan(); + } + // If power state is not ON - Turn on + if (this.platform.hvacInstance.powerState !== PowerState.ON) { + await this.platform.hvacInstance.setPowerStateOn(); + } break; } this.platform.log.debug('Set FanOnlyMaster Power State -> ', value); diff --git a/src/fanOnlyZoneAccessory.ts b/src/fanOnlyZoneAccessory.ts index 8b6e8aa..15a24ed 100644 --- a/src/fanOnlyZoneAccessory.ts +++ b/src/fanOnlyZoneAccessory.ts @@ -57,8 +57,14 @@ export class FanOnlyZoneAccessory { await this.zone.setZoneDisable(); break; case 1: - await this.zone.setZoneEnable(); - await this.platform.hvacInstance.setClimateModeFan(); + // If the fan only mode is not running, switch climate mode to fan instead of sending enable event + if (this.platform.hvacInstance.climateMode !== ClimateMode.FAN) { + await this.platform.hvacInstance.setClimateModeFan(); + } + // after checking mode, check if the state is enabled or not + if (this.zone.zoneEnabled === false) { + await this.zone.setZoneEnable(); + } break; } this.platform.log.debug(`Set FanOnlyZone ${this.zone.zoneName} Enable State -> `, value); diff --git a/src/masterControllerAccessory.ts b/src/masterControllerAccessory.ts index 596f843..0bfd9fe 100644 --- a/src/masterControllerAccessory.ts +++ b/src/masterControllerAccessory.ts @@ -127,7 +127,14 @@ export class MasterControllerAccessory { await this.platform.hvacInstance.setPowerStateOff(); break; case 1: - await this.platform.hvacInstance.setPowerStateOn(); + // If the fan only mode is running, switch climate mode to cool instead of sending power on event + if (this.platform.hvacInstance.climateMode === ClimateMode.FAN) { + await this.platform.hvacInstance.setClimateModeCool(); + } + // If power state is not ON - Turn on + if (this.platform.hvacInstance.powerState !== PowerState.ON) { + await this.platform.hvacInstance.setPowerStateOn(); + } break; } this.platform.log.debug('Set Master Power State -> ', value); diff --git a/src/zoneControllerAccessory.ts b/src/zoneControllerAccessory.ts index 8d703ae..1e41953 100644 --- a/src/zoneControllerAccessory.ts +++ b/src/zoneControllerAccessory.ts @@ -148,7 +148,14 @@ export class ZoneControllerAccessory { await this.zone.setZoneDisable(); break; case 1: - await this.zone.setZoneEnable(); + // If the fan only mode is running, switch climate mode to cool instead of sending enable event + if (this.platform.hvacInstance.climateMode === ClimateMode.FAN) { + await this.platform.hvacInstance.setClimateModeCool(); + } + // after checking mode, check if the state is enabled or not + if (this.zone.zoneEnabled === false) { + await this.zone.setZoneEnable(); + } break; } this.platform.log.debug(`Set Zone ${this.zone.zoneName} Enable State -> `, value); From 5825bc2dc956e5613c90a6cfb9708c0d7b7cb93a Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Sat, 20 Jan 2024 21:31:56 +1100 Subject: [PATCH 11/13] increment version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3bad296..b828805 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.5.0-alpha.1", + "version": "1.5.0-alpha.2", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": { From adfada5589d6bbfb45a8a58ee506da32cc0400ab Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Sat, 20 Jan 2024 21:45:13 +1100 Subject: [PATCH 12/13] climate mode reporting change for Fan Only --- src/masterControllerAccessory.ts | 4 ++-- src/zoneControllerAccessory.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/masterControllerAccessory.ts b/src/masterControllerAccessory.ts index 0bfd9fe..0f44953 100644 --- a/src/masterControllerAccessory.ts +++ b/src/masterControllerAccessory.ts @@ -208,9 +208,9 @@ export class MasterControllerAccessory { case ClimateMode.COOL: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; - // Returning climate mode of Auto when fan-only is running + // Returning climate mode of cool when fan-only is running case ClimateMode.FAN: - currentMode = this.platform.Characteristic.TargetHeaterCoolerState.AUTO; + currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; default: currentMode = 0; diff --git a/src/zoneControllerAccessory.ts b/src/zoneControllerAccessory.ts index 1e41953..9a4b0b6 100644 --- a/src/zoneControllerAccessory.ts +++ b/src/zoneControllerAccessory.ts @@ -231,9 +231,9 @@ export class ZoneControllerAccessory { case ClimateMode.COOL: currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; - // Returning climate mode of Auto when fan-only is running + // Returning climate mode of cool when fan-only is running case ClimateMode.FAN: - currentMode = this.platform.Characteristic.TargetHeaterCoolerState.AUTO; + currentMode = this.platform.Characteristic.TargetHeaterCoolerState.COOL; break; default: currentMode = 0; From 368e96c6d8963f6f2b2e5d40293160f67d33e8a9 Mon Sep 17 00:00:00 2001 From: Julian Greensmith Date: Sat, 20 Jan 2024 22:14:20 +1100 Subject: [PATCH 13/13] updates to readme --- README.md | 17 ++++++++++++++++- package.json | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be5e050..eccfb19 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ This is an 'almost' feature complete implementation of the Que platform in HomeK - Report battery level on zone sensors and get low battery alerts in the home app - Support for homebridge config UI +Fixes/Improvements in version 1.5.0 + - Now supports running in "Fan-Only" mode. An option has been added to the configuration to create a Fan accessory for each zone and the master controller (`Create FAN ONLY devices for each zone`). + Fixes/Improvements in version 1.2.8 - Provide option to disable battery checking for hard wired zone controllers (preventing erroneous low battery reports). Cached accessories will need to be removed for changes to take effect. - Corrected error in humidity sensor detection that may have prevented humidity sensor from being added for zone sensors that support humidity reading. @@ -66,7 +69,6 @@ New in version 1.1.0 Limitations - These options cannot be set via HomeKit: - Quiet Mode - - Fan Only Mode - Constant Fan Operation - Away Mode @@ -109,6 +111,7 @@ If you are not using the Homebridge config UI you can add the following to your             "zonesPushMaster": true | false, "refreshInterval": 60, "deviceSerial": "", + "fanOnlyDevices": true | false, "wiredZoneSensors": [ "Zone Name 1", "Zone Name 2", @@ -189,6 +192,18 @@ default: "" In most cases you can exclude this option or leave it blank. If you only have a single air con system in your Que account the plugin will auto-discover the target device serial number. If you have multiple Que systems in your account you will need to specify which system you want to control by entering the serial number here. You can get your device serial numbers by logging in to que.actronair.com.au and looking at the list of authorised devices. +### `fanOnlyDevices` + +type: boolean + +default: false + +Control creation of Fan Only accessories for each zone. When toggled on these accessories will put the system in Fan only mode. Fan speed can be controlled via the Master Controller accessory. +- 1-10% = Auto speed +- 11-30% = Low speed +- 31-65% = Medium speed +- 66-100% = High speed + #### `wiredZoneSensors` type: array[string] (case sensitive) diff --git a/package.json b/package.json index b828805..c6e88cc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": false, "displayName": "Homebridge Actron Que", "name": "homebridge-actron-que", - "version": "1.5.0-alpha.2", + "version": "1.5.0", "description": "Homebridge plugin for controlling Actron Que controller systems", "license": "Apache-2.0", "repository": {