@homebridge-plugins/homebridge-tado 8.5.1-beta.1 → 8.5.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -2
- package/package.json +1 -1
- package/src/helper/handler.js +521 -531
package/src/helper/handler.js
CHANGED
|
@@ -3,9 +3,10 @@ import moment from 'moment';
|
|
|
3
3
|
import { writeFile, access, readFile } from 'fs/promises';
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
var delayTimer = {};
|
|
6
|
+
let settingState = false;
|
|
8
7
|
let tasksInitialized = false;
|
|
8
|
+
let lastGetStates = 0;
|
|
9
|
+
const delayTimer = {};
|
|
9
10
|
|
|
10
11
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
11
12
|
const aRefreshHistoryHandlers = [];
|
|
@@ -23,6 +24,7 @@ export async function getPersistedStates(storagePath) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export default (api, accessories, config, tado, telegram) => {
|
|
27
|
+
const statesIntervalTime = Math.max(config.polling, 30) * 1000;
|
|
26
28
|
const storagePath = api.user.storagePath();
|
|
27
29
|
|
|
28
30
|
async function setStates(accessory, accs, target, value) {
|
|
@@ -565,9 +567,11 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
565
567
|
} catch (err) {
|
|
566
568
|
errorHandler(err);
|
|
567
569
|
} finally {
|
|
568
|
-
//always update zone to set correct state in Apple Home
|
|
569
|
-
if (zoneUpdated) await updateZones(accessory.context.config.zoneId);
|
|
570
570
|
settingState = false;
|
|
571
|
+
//update zones to ensure correct state in Apple Home
|
|
572
|
+
const timeSinceLastGetStates = Date.now() - lastGetStates;
|
|
573
|
+
const statesIntervalTimeLeft = statesIntervalTime - timeSinceLastGetStates;
|
|
574
|
+
if (zoneUpdated && statesIntervalTimeLeft > 10_000) await updateZones();
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
577
|
|
|
@@ -746,7 +750,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
746
750
|
}
|
|
747
751
|
try {
|
|
748
752
|
//wait for fakegato history services to be loaded
|
|
749
|
-
await
|
|
753
|
+
await timeout(4000);
|
|
750
754
|
for (const refreshHistory of aRefreshHistoryHandlers) {
|
|
751
755
|
refreshHistory();
|
|
752
756
|
}
|
|
@@ -769,13 +773,14 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
769
773
|
tasksInitialized = true;
|
|
770
774
|
|
|
771
775
|
void getStates();
|
|
772
|
-
setInterval(() => getStates(),
|
|
776
|
+
setInterval(() => getStates(), statesIntervalTime);
|
|
773
777
|
|
|
774
778
|
void logCounter();
|
|
775
779
|
setInterval(() => logCounter(), 60 * 60 * 1000);
|
|
776
780
|
}
|
|
777
781
|
|
|
778
782
|
async function getStates() {
|
|
783
|
+
lastGetStates = Date.now();
|
|
779
784
|
let zoneStates = {};
|
|
780
785
|
try {
|
|
781
786
|
//ME
|
|
@@ -809,413 +814,382 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
809
814
|
}
|
|
810
815
|
|
|
811
816
|
async function updateMe() {
|
|
812
|
-
if (
|
|
813
|
-
Logger.debug('Polling User Info...', config.homeName);
|
|
817
|
+
if (settingState) return;
|
|
814
818
|
|
|
815
|
-
|
|
819
|
+
Logger.debug('Polling User Info...', config.homeName);
|
|
816
820
|
|
|
817
|
-
|
|
821
|
+
const me = await tado.getMe();
|
|
818
822
|
|
|
819
|
-
|
|
820
|
-
}
|
|
823
|
+
if (config.homeName !== me.homes[0].name) throw ('Cannot find requested home in the API!', config.homeName);
|
|
821
824
|
|
|
822
|
-
|
|
825
|
+
config.homeId = me.homes[0].id;
|
|
823
826
|
}
|
|
824
827
|
|
|
825
828
|
async function updateHome() {
|
|
826
|
-
if (
|
|
827
|
-
Logger.debug('Polling Home Info...', config.homeName);
|
|
829
|
+
if (settingState) return;
|
|
828
830
|
|
|
829
|
-
|
|
831
|
+
Logger.debug('Polling Home Info...', config.homeName);
|
|
830
832
|
|
|
831
|
-
|
|
833
|
+
const home = await tado.getHome(config.homeId);
|
|
832
834
|
|
|
833
|
-
|
|
835
|
+
if (!config.temperatureUnit) config.temperatureUnit = home.temperatureUnit || 'CELSIUS';
|
|
834
836
|
|
|
835
|
-
|
|
836
|
-
!config.geolocation ||
|
|
837
|
-
(config.geolocation && !config.geolocation.longitude) ||
|
|
838
|
-
!config.geolocation.latitude
|
|
839
|
-
) {
|
|
840
|
-
if (!home.geolocation) home.geolocation = {};
|
|
837
|
+
//config.skills = home.skills || []; //do we need this?
|
|
841
838
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
839
|
+
if (
|
|
840
|
+
!config.geolocation ||
|
|
841
|
+
(config.geolocation && !config.geolocation.longitude) ||
|
|
842
|
+
!config.geolocation.latitude
|
|
843
|
+
) {
|
|
844
|
+
if (!home.geolocation) home.geolocation = {};
|
|
848
845
|
|
|
849
|
-
|
|
846
|
+
config.geolocation = {
|
|
847
|
+
longitude: (home.geolocation.longitude || '').toString() || false,
|
|
848
|
+
latitude: (home.geolocation.latitude || '').toString() || false,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
850
851
|
}
|
|
851
852
|
|
|
852
|
-
async function updateZones(
|
|
853
|
-
|
|
854
|
-
if (!settingState || idToUpdate !== undefined) {
|
|
855
|
-
Logger.debug('Polling Zones...', config.homeName);
|
|
853
|
+
async function updateZones() {
|
|
854
|
+
if (settingState) return;
|
|
856
855
|
|
|
857
|
-
|
|
858
|
-
let inManualMode = 0;
|
|
859
|
-
let inOffMode = 0;
|
|
860
|
-
let inAutoMode = 0;
|
|
856
|
+
Logger.debug('Polling Zones...', config.homeName);
|
|
861
857
|
|
|
862
|
-
|
|
858
|
+
//CentralSwitch
|
|
859
|
+
let inManualMode = 0;
|
|
860
|
+
let inOffMode = 0;
|
|
861
|
+
let inAutoMode = 0;
|
|
863
862
|
|
|
864
|
-
|
|
865
|
-
const allZones = (await tado.getZones(config.homeId)) || [];
|
|
866
|
-
|
|
867
|
-
for (const [index, zone] of config.zones.entries()) {
|
|
868
|
-
allZones.forEach((zoneWithID) => {
|
|
869
|
-
if (zoneWithID.name === zone.name) config.zones[index].id = zoneWithID.id;
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
}
|
|
863
|
+
let zonesWithoutID = config.zones.filter((zone) => zone && !zone.id);
|
|
873
864
|
|
|
865
|
+
if (zonesWithoutID.length) {
|
|
874
866
|
const allZones = (await tado.getZones(config.homeId)) || [];
|
|
875
867
|
|
|
876
868
|
for (const [index, zone] of config.zones.entries()) {
|
|
877
869
|
allZones.forEach((zoneWithID) => {
|
|
878
|
-
if (zoneWithID.name === zone.name)
|
|
879
|
-
const heatAccessory = accessories.filter(
|
|
880
|
-
(acc) => acc && acc.displayName === config.homeName + ' ' + zone.name + ' Heater'
|
|
881
|
-
);
|
|
882
|
-
|
|
883
|
-
if (heatAccessory.length) heatAccessory[0].context.config.zoneId = zoneWithID.id;
|
|
884
|
-
|
|
885
|
-
config.zones[index].id = zoneWithID.id;
|
|
886
|
-
config.zones[index].battery = !config.zones[index].noBattery
|
|
887
|
-
? zoneWithID.devices.filter(
|
|
888
|
-
(device) =>
|
|
889
|
-
device &&
|
|
890
|
-
(zone.type === 'HEATING' || zone.type === 'AIR_CONDITIONING') &&
|
|
891
|
-
typeof device.batteryState === 'string' &&
|
|
892
|
-
!device.batteryState.includes('NORMAL')
|
|
893
|
-
).length
|
|
894
|
-
? zoneWithID.devices.filter((device) => device && !device.batteryState.includes('NORMAL'))[0]
|
|
895
|
-
.batteryState
|
|
896
|
-
: zoneWithID.devices.filter((device) => device && device.duties.includes('ZONE_LEADER'))[0].batteryState
|
|
897
|
-
: false;
|
|
898
|
-
config.zones[index].openWindowEnabled =
|
|
899
|
-
zoneWithID.openWindowDetection && zoneWithID.openWindowDetection.enabled ? true : false;
|
|
900
|
-
}
|
|
870
|
+
if (zoneWithID.name === zone.name) config.zones[index].id = zoneWithID.id;
|
|
901
871
|
});
|
|
902
872
|
}
|
|
873
|
+
}
|
|
903
874
|
|
|
904
|
-
|
|
905
|
-
if (idToUpdate !== undefined) {
|
|
906
|
-
zoneStates[idToUpdate] = await tado.getZoneState(config.homeId, idToUpdate);
|
|
907
|
-
zonesToUpdate = config.zones.filter(zone => zone.id === idToUpdate);
|
|
908
|
-
} else {
|
|
909
|
-
zoneStates = (await tado.getZoneStates(config.homeId))["zoneStates"];
|
|
910
|
-
zonesToUpdate = config.zones;
|
|
911
|
-
}
|
|
875
|
+
const allZones = (await tado.getZones(config.homeId)) || [];
|
|
912
876
|
|
|
913
|
-
|
|
914
|
-
|
|
877
|
+
for (const [index, zone] of config.zones.entries()) {
|
|
878
|
+
allZones.forEach((zoneWithID) => {
|
|
879
|
+
if (zoneWithID.name === zone.name) {
|
|
880
|
+
const heatAccessory = accessories.filter(
|
|
881
|
+
(acc) => acc && acc.displayName === config.homeName + ' ' + zone.name + ' Heater'
|
|
882
|
+
);
|
|
915
883
|
|
|
916
|
-
|
|
884
|
+
if (heatAccessory.length) heatAccessory[0].context.config.zoneId = zoneWithID.id;
|
|
885
|
+
|
|
886
|
+
config.zones[index].id = zoneWithID.id;
|
|
887
|
+
config.zones[index].battery = !config.zones[index].noBattery
|
|
888
|
+
? zoneWithID.devices.filter(
|
|
889
|
+
(device) =>
|
|
890
|
+
device &&
|
|
891
|
+
(zone.type === 'HEATING' || zone.type === 'AIR_CONDITIONING') &&
|
|
892
|
+
typeof device.batteryState === 'string' &&
|
|
893
|
+
!device.batteryState.includes('NORMAL')
|
|
894
|
+
).length
|
|
895
|
+
? zoneWithID.devices.filter((device) => device && !device.batteryState.includes('NORMAL'))[0]
|
|
896
|
+
.batteryState
|
|
897
|
+
: zoneWithID.devices.filter((device) => device && device.duties.includes('ZONE_LEADER'))[0].batteryState
|
|
898
|
+
: false;
|
|
899
|
+
config.zones[index].openWindowEnabled =
|
|
900
|
+
zoneWithID.openWindowDetection && zoneWithID.openWindowDetection.enabled ? true : false;
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
}
|
|
917
904
|
|
|
918
|
-
|
|
919
|
-
|
|
905
|
+
const zoneStates = (await tado.getZoneStates(config.homeId))["zoneStates"] ?? {};
|
|
906
|
+
const zonesToUpdate = config.zones;
|
|
920
907
|
|
|
921
|
-
|
|
908
|
+
for (const zone of zonesToUpdate) {
|
|
909
|
+
const zoneState = zoneStates[zone.id.toString()];
|
|
910
|
+
Logger.debug(`Update state of zone ${zone.id} to:`, zoneState);
|
|
922
911
|
|
|
923
|
-
|
|
924
|
-
if (zoneState.sensorDataPoints.insideTemperature) {
|
|
925
|
-
currentTemp =
|
|
926
|
-
config.temperatureUnit === 'FAHRENHEIT'
|
|
927
|
-
? zoneState.sensorDataPoints.insideTemperature.fahrenheit
|
|
928
|
-
: zoneState.sensorDataPoints.insideTemperature.celsius;
|
|
912
|
+
let currentState, targetState, currentTemp, targetTemp, humidity, active, battery, tempEqual;
|
|
929
913
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
config.temperatureUnit === 'FAHRENHEIT'
|
|
933
|
-
? zoneState.setting.temperature.fahrenheit
|
|
934
|
-
: zoneState.setting.temperature.celsius;
|
|
914
|
+
if (zoneState.setting.type === 'HEATING') {
|
|
915
|
+
battery = zone.battery === 'NORMAL' ? 100 : 10;
|
|
935
916
|
|
|
936
|
-
|
|
917
|
+
if (zoneState.sensorDataPoints.humidity) humidity = zoneState.sensorDataPoints.humidity.percentage;
|
|
937
918
|
|
|
938
|
-
|
|
939
|
-
|
|
919
|
+
//HEATING
|
|
920
|
+
if (zoneState.sensorDataPoints.insideTemperature) {
|
|
921
|
+
currentTemp =
|
|
922
|
+
config.temperatureUnit === 'FAHRENHEIT'
|
|
923
|
+
? zoneState.sensorDataPoints.insideTemperature.fahrenheit
|
|
924
|
+
: zoneState.sensorDataPoints.insideTemperature.celsius;
|
|
940
925
|
|
|
941
|
-
|
|
942
|
-
|
|
926
|
+
if (zoneState.setting.power === 'ON') {
|
|
927
|
+
targetTemp =
|
|
928
|
+
config.temperatureUnit === 'FAHRENHEIT'
|
|
929
|
+
? zoneState.setting.temperature.fahrenheit
|
|
930
|
+
: zoneState.setting.temperature.celsius;
|
|
943
931
|
|
|
944
|
-
|
|
945
|
-
} else {
|
|
946
|
-
//heating is switched off
|
|
947
|
-
currentState = 0;
|
|
948
|
-
targetState = 0;
|
|
949
|
-
active = 0;
|
|
950
|
-
}
|
|
932
|
+
tempEqual = Math.round(currentTemp) === Math.round(targetTemp);
|
|
951
933
|
|
|
952
|
-
if
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
}
|
|
934
|
+
//show as currently heating if current temp is lower than target temp, otherwise show as temp set
|
|
935
|
+
currentState = currentTemp < targetTemp ? 1 : 0;
|
|
956
936
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
(acc) =>
|
|
960
|
-
acc &&
|
|
961
|
-
(acc.context.config.subtype === 'zone-thermostat' ||
|
|
962
|
-
acc.context.config.subtype === 'zone-heatercooler' ||
|
|
963
|
-
acc.context.config.subtype === 'zone-heatercooler-ac')
|
|
964
|
-
);
|
|
937
|
+
//check if auto mode is enabled
|
|
938
|
+
targetState = zoneState.overlayType === null ? 3 : 1;
|
|
965
939
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
940
|
+
active = 1;
|
|
941
|
+
} else {
|
|
942
|
+
//heating is switched off
|
|
943
|
+
currentState = 0;
|
|
944
|
+
targetState = 0;
|
|
945
|
+
active = 0;
|
|
946
|
+
}
|
|
971
947
|
|
|
972
|
-
|
|
973
|
-
|
|
948
|
+
if (targetState === undefined && zoneState.overlayType === null) {
|
|
949
|
+
targetState = 3;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
974
952
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
953
|
+
//Thermostat/HeaterCooler
|
|
954
|
+
const thermoAccessory = accessories.filter(
|
|
955
|
+
(acc) =>
|
|
956
|
+
acc &&
|
|
957
|
+
(acc.context.config.subtype === 'zone-thermostat' ||
|
|
958
|
+
acc.context.config.subtype === 'zone-heatercooler' ||
|
|
959
|
+
acc.context.config.subtype === 'zone-heatercooler-ac')
|
|
960
|
+
);
|
|
978
961
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
let characteristicHumidity = api.hap.Characteristic.CurrentRelativeHumidity;
|
|
985
|
-
let characteristicUnit = api.hap.Characteristic.TemperatureDisplayUnits;
|
|
962
|
+
if (thermoAccessory.length) {
|
|
963
|
+
thermoAccessory.forEach((acc) => {
|
|
964
|
+
if (acc.displayName.includes(zone.name)) {
|
|
965
|
+
let serviceThermostat = acc.getService(api.hap.Service.Thermostat);
|
|
966
|
+
let serviceHeaterCooler = acc.getService(api.hap.Service.HeaterCooler);
|
|
986
967
|
|
|
987
|
-
|
|
988
|
-
|
|
968
|
+
let serviceBattery = acc.getService(api.hap.Service.BatteryService);
|
|
969
|
+
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
989
970
|
|
|
990
|
-
|
|
991
|
-
|
|
971
|
+
if (serviceBattery && zone.battery) {
|
|
972
|
+
serviceBattery.getCharacteristic(characteristicBattery).updateValue(battery);
|
|
973
|
+
}
|
|
992
974
|
|
|
993
|
-
|
|
994
|
-
|
|
975
|
+
if (serviceThermostat) {
|
|
976
|
+
let characteristicCurrentTemp = api.hap.Characteristic.CurrentTemperature;
|
|
977
|
+
let characteristicTargetTemp = api.hap.Characteristic.TargetTemperature;
|
|
978
|
+
let characteristicCurrentState = api.hap.Characteristic.CurrentHeatingCoolingState;
|
|
979
|
+
let characteristicTargetState = api.hap.Characteristic.TargetHeatingCoolingState;
|
|
980
|
+
let characteristicHumidity = api.hap.Characteristic.CurrentRelativeHumidity;
|
|
981
|
+
let characteristicUnit = api.hap.Characteristic.TemperatureDisplayUnits;
|
|
995
982
|
|
|
996
|
-
|
|
983
|
+
if (!isNaN(currentTemp)) {
|
|
984
|
+
acc.context.config.temperatureUnit = acc.context.config.temperatureUnit || config.temperatureUnit;
|
|
997
985
|
|
|
998
|
-
|
|
999
|
-
|
|
986
|
+
let isFahrenheit = serviceThermostat.getCharacteristic(characteristicUnit).value === 1;
|
|
987
|
+
let unitChanged = config.temperatureUnit !== acc.context.config.temperatureUnit;
|
|
1000
988
|
|
|
1001
|
-
|
|
1002
|
-
|
|
989
|
+
let cToF = (c) => Math.round((c * 9) / 5 + 32);
|
|
990
|
+
let fToC = (f) => Math.round(((f - 32) * 5) / 9);
|
|
1003
991
|
|
|
1004
|
-
|
|
1005
|
-
serviceThermostat.getCharacteristic(characteristicCurrentState).updateValue(currentState);
|
|
992
|
+
let newValue = unitChanged ? (isFahrenheit ? cToF(currentTemp) : fToC(currentTemp)) : currentTemp;
|
|
1006
993
|
|
|
1007
|
-
|
|
1008
|
-
serviceThermostat.getCharacteristic(characteristicTargetState).updateValue(targetState);
|
|
1009
|
-
|
|
1010
|
-
if (!isNaN(humidity) && serviceThermostat.testCharacteristic(characteristicHumidity))
|
|
1011
|
-
serviceThermostat.getCharacteristic(characteristicHumidity).updateValue(humidity);
|
|
994
|
+
serviceThermostat.getCharacteristic(characteristicCurrentTemp).updateValue(newValue);
|
|
1012
995
|
}
|
|
1013
996
|
|
|
1014
|
-
if (
|
|
1015
|
-
|
|
1016
|
-
let characteristicCurrentTemp = api.hap.Characteristic.CurrentTemperature;
|
|
1017
|
-
let characteristicTargetTempHeating = api.hap.Characteristic.HeatingThresholdTemperature;
|
|
1018
|
-
let characteristicTargetTempCooling = api.hap.Characteristic.CoolingThresholdTemperature;
|
|
1019
|
-
let characteristicCurrentState = api.hap.Characteristic.CurrentHeaterCoolerState;
|
|
1020
|
-
let characteristicTargetState = api.hap.Characteristic.TargetHeaterCoolerState;
|
|
1021
|
-
let characteristicActive = api.hap.Characteristic.Active;
|
|
997
|
+
if (!isNaN(targetTemp))
|
|
998
|
+
serviceThermostat.getCharacteristic(characteristicTargetTemp).updateValue(targetTemp);
|
|
1022
999
|
|
|
1023
|
-
|
|
1000
|
+
if (!isNaN(currentState))
|
|
1001
|
+
serviceThermostat.getCharacteristic(characteristicCurrentState).updateValue(currentState);
|
|
1024
1002
|
|
|
1025
|
-
|
|
1003
|
+
if (!isNaN(targetState))
|
|
1004
|
+
serviceThermostat.getCharacteristic(characteristicTargetState).updateValue(targetState);
|
|
1026
1005
|
|
|
1027
|
-
|
|
1006
|
+
if (!isNaN(humidity) && serviceThermostat.testCharacteristic(characteristicHumidity))
|
|
1007
|
+
serviceThermostat.getCharacteristic(characteristicHumidity).updateValue(humidity);
|
|
1008
|
+
}
|
|
1028
1009
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1010
|
+
if (serviceHeaterCooler) {
|
|
1011
|
+
let characteristicHumidity = api.hap.Characteristic.CurrentRelativeHumidity;
|
|
1012
|
+
let characteristicCurrentTemp = api.hap.Characteristic.CurrentTemperature;
|
|
1013
|
+
let characteristicTargetTempHeating = api.hap.Characteristic.HeatingThresholdTemperature;
|
|
1014
|
+
let characteristicTargetTempCooling = api.hap.Characteristic.CoolingThresholdTemperature;
|
|
1015
|
+
let characteristicCurrentState = api.hap.Characteristic.CurrentHeaterCoolerState;
|
|
1016
|
+
let characteristicTargetState = api.hap.Characteristic.TargetHeaterCoolerState;
|
|
1017
|
+
let characteristicActive = api.hap.Characteristic.Active;
|
|
1031
1018
|
|
|
1032
|
-
|
|
1033
|
-
serviceHeaterCooler.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1019
|
+
currentState = active ? (targetState === 3 || tempEqual ? 1 : currentState + 1) : 0;
|
|
1034
1020
|
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1021
|
+
targetState = 1;
|
|
1037
1022
|
|
|
1038
|
-
|
|
1039
|
-
serviceHeaterCooler.getCharacteristic(characteristicCurrentState).updateValue(currentState);
|
|
1023
|
+
if (!isNaN(active)) serviceHeaterCooler.getCharacteristic(characteristicActive).updateValue(active);
|
|
1040
1024
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1025
|
+
if (!isNaN(currentTemp))
|
|
1026
|
+
serviceHeaterCooler.getCharacteristic(characteristicCurrentTemp).updateValue(currentTemp);
|
|
1043
1027
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
} else {
|
|
1051
|
-
// Non-HEATING zones (AIR_CONDITIONING, HOT_WATER, etc.)
|
|
1052
|
-
battery = zone.battery === 'NORMAL' ? 100 : 10;
|
|
1028
|
+
if (!isNaN(targetTemp)) {
|
|
1029
|
+
serviceHeaterCooler.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1053
1030
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1031
|
+
serviceHeaterCooler.getCharacteristic(characteristicTargetTempCooling).updateValue(targetTemp);
|
|
1032
|
+
}
|
|
1057
1033
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
currentTemp =
|
|
1061
|
-
config.temperatureUnit === 'FAHRENHEIT'
|
|
1062
|
-
? zoneState.sensorDataPoints.insideTemperature.fahrenheit
|
|
1063
|
-
: zoneState.sensorDataPoints.insideTemperature.celsius;
|
|
1064
|
-
}
|
|
1034
|
+
if (!isNaN(currentState))
|
|
1035
|
+
serviceHeaterCooler.getCharacteristic(characteristicCurrentState).updateValue(currentState);
|
|
1065
1036
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1037
|
+
if (!isNaN(targetState))
|
|
1038
|
+
serviceHeaterCooler.getCharacteristic(characteristicTargetState).updateValue(targetState);
|
|
1068
1039
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
zoneState.setting.temperature !== null && zoneState.setting.temperature
|
|
1072
|
-
? config.temperatureUnit === 'FAHRENHEIT'
|
|
1073
|
-
? zoneState.setting.temperature.fahrenheit
|
|
1074
|
-
: zoneState.setting.temperature.celsius
|
|
1075
|
-
: undefined;
|
|
1076
|
-
|
|
1077
|
-
// Enhanced AC state handling
|
|
1078
|
-
if (zone.type === 'AIR_CONDITIONING') {
|
|
1079
|
-
const acMode = zoneState.setting.mode || 'COOL';
|
|
1080
|
-
tempEqual = currentTemp && targetTemp ? Math.abs(currentTemp - targetTemp) < 0.5 : false;
|
|
1081
|
-
|
|
1082
|
-
// Map AC modes to HomeKit states
|
|
1083
|
-
switch (acMode.toUpperCase()) {
|
|
1084
|
-
case 'HEAT':
|
|
1085
|
-
targetState = 1; // Heating
|
|
1086
|
-
currentState = tempEqual ? 1 : (currentTemp < targetTemp ? 2 : 1); // Idle or Heating
|
|
1087
|
-
break;
|
|
1088
|
-
case 'COOL':
|
|
1089
|
-
case 'AUTO':
|
|
1090
|
-
default:
|
|
1091
|
-
targetState = 2; // Cooling
|
|
1092
|
-
currentState = tempEqual ? 1 : (currentTemp > targetTemp ? 3 : 1); // Idle or Cooling
|
|
1093
|
-
break;
|
|
1040
|
+
if (!isNaN(humidity) && serviceHeaterCooler.testCharacteristic(characteristicHumidity))
|
|
1041
|
+
serviceHeaterCooler.getCharacteristic(characteristicHumidity).updateValue(humidity);
|
|
1094
1042
|
}
|
|
1095
|
-
} else {
|
|
1096
|
-
// Non-AC zones (HOT_WATER, etc.)
|
|
1097
|
-
currentState = zoneState.overlayType === null ? 1 : 2;
|
|
1098
|
-
targetState = 1;
|
|
1099
1043
|
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
// Non-HEATING zones (AIR_CONDITIONING, HOT_WATER, etc.)
|
|
1048
|
+
battery = zone.battery === 'NORMAL' ? 100 : 10;
|
|
1106
1049
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
acc.context.config.subtype === 'zone-heatercooler-ac')
|
|
1111
|
-
);
|
|
1112
|
-
const switchAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-switch');
|
|
1113
|
-
const faucetAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-faucet');
|
|
1050
|
+
if (zoneState.sensorDataPoints.humidity) {
|
|
1051
|
+
humidity = zoneState.sensorDataPoints.humidity.percentage;
|
|
1052
|
+
}
|
|
1114
1053
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1054
|
+
// Get current temperature from sensor data (same as HEATING zones)
|
|
1055
|
+
if (zoneState.sensorDataPoints.insideTemperature) {
|
|
1056
|
+
currentTemp =
|
|
1057
|
+
config.temperatureUnit === 'FAHRENHEIT'
|
|
1058
|
+
? zoneState.sensorDataPoints.insideTemperature.fahrenheit
|
|
1059
|
+
: zoneState.sensorDataPoints.insideTemperature.celsius;
|
|
1060
|
+
}
|
|
1119
1061
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1062
|
+
if (zoneState.setting.power === 'ON') {
|
|
1063
|
+
active = 1;
|
|
1064
|
+
|
|
1065
|
+
// Get target temperature from setting
|
|
1066
|
+
targetTemp =
|
|
1067
|
+
zoneState.setting.temperature !== null && zoneState.setting.temperature
|
|
1068
|
+
? config.temperatureUnit === 'FAHRENHEIT'
|
|
1069
|
+
? zoneState.setting.temperature.fahrenheit
|
|
1070
|
+
: zoneState.setting.temperature.celsius
|
|
1071
|
+
: undefined;
|
|
1072
|
+
|
|
1073
|
+
// Enhanced AC state handling
|
|
1074
|
+
if (zone.type === 'AIR_CONDITIONING') {
|
|
1075
|
+
const acMode = zoneState.setting.mode || 'COOL';
|
|
1076
|
+
tempEqual = currentTemp && targetTemp ? Math.abs(currentTemp - targetTemp) < 0.5 : false;
|
|
1077
|
+
|
|
1078
|
+
// Map AC modes to HomeKit states
|
|
1079
|
+
switch (acMode.toUpperCase()) {
|
|
1080
|
+
case 'HEAT':
|
|
1081
|
+
targetState = 1; // Heating
|
|
1082
|
+
currentState = tempEqual ? 1 : (currentTemp < targetTemp ? 2 : 1); // Idle or Heating
|
|
1083
|
+
break;
|
|
1084
|
+
case 'COOL':
|
|
1085
|
+
case 'AUTO':
|
|
1086
|
+
default:
|
|
1087
|
+
targetState = 2; // Cooling
|
|
1088
|
+
currentState = tempEqual ? 1 : (currentTemp > targetTemp ? 3 : 1); // Idle or Cooling
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
} else {
|
|
1092
|
+
// Non-AC zones (HOT_WATER, etc.)
|
|
1093
|
+
currentState = zoneState.overlayType === null ? 1 : 2;
|
|
1094
|
+
targetState = 1;
|
|
1095
|
+
}
|
|
1096
|
+
} else {
|
|
1097
|
+
active = 0;
|
|
1098
|
+
currentState = 0;
|
|
1099
|
+
targetTemp = undefined;
|
|
1100
|
+
targetState = zone.type === 'AIR_CONDITIONING' ? 2 : 1; // Default to cooling for AC, heating for others
|
|
1101
|
+
}
|
|
1126
1102
|
|
|
1127
|
-
|
|
1103
|
+
//Thermostat/HeaterCooler
|
|
1104
|
+
const heaterAccessory = accessories.filter(
|
|
1105
|
+
(acc) => acc && (acc.context.config.subtype === 'zone-heatercooler-boiler' ||
|
|
1106
|
+
acc.context.config.subtype === 'zone-heatercooler-ac')
|
|
1107
|
+
);
|
|
1108
|
+
const switchAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-switch');
|
|
1109
|
+
const faucetAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-faucet');
|
|
1128
1110
|
|
|
1129
|
-
|
|
1111
|
+
if (heaterAccessory.length) {
|
|
1112
|
+
heaterAccessory.forEach((acc) => {
|
|
1113
|
+
if (acc.displayName.includes(zone.name)) {
|
|
1114
|
+
let service = acc.getService(api.hap.Service.HeaterCooler);
|
|
1130
1115
|
|
|
1131
|
-
|
|
1116
|
+
let characteristicCurrentTemp = api.hap.Characteristic.CurrentTemperature;
|
|
1117
|
+
let characteristicActive = api.hap.Characteristic.Active;
|
|
1118
|
+
let characteristicCurrentState = api.hap.Characteristic.CurrentHeaterCoolerState;
|
|
1119
|
+
let characteristicTargetState = api.hap.Characteristic.TargetHeaterCoolerState;
|
|
1120
|
+
let characteristicTargetTempHeating = api.hap.Characteristic.HeatingThresholdTemperature;
|
|
1121
|
+
let characteristicTargetTempCooling = api.hap.Characteristic.CoolingThresholdTemperature;
|
|
1132
1122
|
|
|
1133
|
-
|
|
1134
|
-
if (!isNaN(currentTemp) || acc.context.currentTemp) {
|
|
1135
|
-
if (!isNaN(currentTemp)) acc.context.currentTemp = currentTemp; //store current temp in config
|
|
1123
|
+
service.getCharacteristic(characteristicActive).updateValue(active);
|
|
1136
1124
|
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1125
|
+
service.getCharacteristic(characteristicCurrentState).updateValue(currentState);
|
|
1139
1126
|
|
|
1140
|
-
|
|
1141
|
-
if (!isNaN(targetTemp)) {
|
|
1142
|
-
// For AC zones, set temperature based on the mode
|
|
1143
|
-
if (zone.type === 'AIR_CONDITIONING') {
|
|
1144
|
-
// Always set both characteristics but log which one is active
|
|
1145
|
-
service.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1146
|
-
service.getCharacteristic(characteristicTargetTempCooling).updateValue(targetTemp);
|
|
1147
|
-
} else {
|
|
1148
|
-
// Non-AC zones (like boiler/hot water)
|
|
1149
|
-
service.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1150
|
-
service.getCharacteristic(characteristicTargetTempCooling).updateValue(targetTemp);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1127
|
+
service.getCharacteristic(characteristicTargetState).updateValue(targetState);
|
|
1153
1128
|
|
|
1154
|
-
|
|
1129
|
+
// Set current temperature from sensor data
|
|
1130
|
+
if (!isNaN(currentTemp) || acc.context.currentTemp) {
|
|
1131
|
+
if (!isNaN(currentTemp)) acc.context.currentTemp = currentTemp; //store current temp in config
|
|
1155
1132
|
|
|
1156
|
-
|
|
1157
|
-
if (!isNaN(humidity) && service.testCharacteristic(api.hap.Characteristic.CurrentRelativeHumidity)) {
|
|
1158
|
-
service.getCharacteristic(api.hap.Characteristic.CurrentRelativeHumidity).updateValue(humidity);
|
|
1159
|
-
}
|
|
1133
|
+
service.getCharacteristic(characteristicCurrentTemp).updateValue(acc.context.currentTemp);
|
|
1160
1134
|
}
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
1135
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1136
|
+
// Set target temperature for both heating and cooling
|
|
1137
|
+
if (!isNaN(targetTemp)) {
|
|
1138
|
+
// For AC zones, set temperature based on the mode
|
|
1139
|
+
if (zone.type === 'AIR_CONDITIONING') {
|
|
1140
|
+
// Always set both characteristics but log which one is active
|
|
1141
|
+
service.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1142
|
+
service.getCharacteristic(characteristicTargetTempCooling).updateValue(targetTemp);
|
|
1143
|
+
} else {
|
|
1144
|
+
// Non-AC zones (like boiler/hot water)
|
|
1145
|
+
service.getCharacteristic(characteristicTargetTempHeating).updateValue(targetTemp);
|
|
1146
|
+
service.getCharacteristic(characteristicTargetTempCooling).updateValue(targetTemp);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1168
1149
|
|
|
1169
|
-
|
|
1150
|
+
// Fan speed polling removed for AIR_CONDITIONING zones
|
|
1170
1151
|
|
|
1171
|
-
|
|
1152
|
+
// Update humidity for all zones that support it
|
|
1153
|
+
if (!isNaN(humidity) && service.testCharacteristic(api.hap.Characteristic.CurrentRelativeHumidity)) {
|
|
1154
|
+
service.getCharacteristic(api.hap.Characteristic.CurrentRelativeHumidity).updateValue(humidity);
|
|
1172
1155
|
}
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
if (faucetAccessory.length) {
|
|
1177
|
-
faucetAccessory.forEach((acc) => {
|
|
1178
|
-
if (acc.displayName.includes(zone.name)) {
|
|
1179
|
-
let service = acc.getService(api.hap.Service.Valve);
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1180
1159
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1160
|
+
if (switchAccessory.length) {
|
|
1161
|
+
switchAccessory.forEach((acc) => {
|
|
1162
|
+
if (acc.displayName.includes(zone.name)) {
|
|
1163
|
+
let service = acc.getService(api.hap.Service.Switch);
|
|
1183
1164
|
|
|
1184
|
-
|
|
1165
|
+
let characteristic = api.hap.Characteristic.On;
|
|
1185
1166
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1167
|
+
service.getCharacteristic(characteristic).updateValue(active ? true : false);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1190
1170
|
}
|
|
1191
1171
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (tempAccessory.length) {
|
|
1196
|
-
tempAccessory.forEach((acc) => {
|
|
1172
|
+
if (faucetAccessory.length) {
|
|
1173
|
+
faucetAccessory.forEach((acc) => {
|
|
1197
1174
|
if (acc.displayName.includes(zone.name)) {
|
|
1198
|
-
let
|
|
1199
|
-
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
1175
|
+
let service = acc.getService(api.hap.Service.Valve);
|
|
1200
1176
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
}
|
|
1177
|
+
let characteristicActive = api.hap.Characteristic.Active;
|
|
1178
|
+
let characteristicInUse = api.hap.Characteristic.InUse;
|
|
1204
1179
|
|
|
1205
|
-
|
|
1206
|
-
let service = acc.getService(api.hap.Service.TemperatureSensor);
|
|
1207
|
-
let characteristic = api.hap.Characteristic.CurrentTemperature;
|
|
1180
|
+
service.getCharacteristic(characteristicActive).updateValue(active ? 1 : 0);
|
|
1208
1181
|
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1182
|
+
service.getCharacteristic(characteristicInUse).updateValue(active ? 1 : 0);
|
|
1211
1183
|
}
|
|
1212
1184
|
});
|
|
1213
1185
|
}
|
|
1186
|
+
}
|
|
1214
1187
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1188
|
+
//TemperatureSensor
|
|
1189
|
+
const tempAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-temperature');
|
|
1217
1190
|
|
|
1218
|
-
|
|
1191
|
+
if (tempAccessory.length) {
|
|
1192
|
+
tempAccessory.forEach((acc) => {
|
|
1219
1193
|
if (acc.displayName.includes(zone.name)) {
|
|
1220
1194
|
let serviceBattery = acc.getService(api.hap.Service.BatteryService);
|
|
1221
1195
|
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
@@ -1224,327 +1198,343 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1224
1198
|
serviceBattery.getCharacteristic(characteristicBattery).updateValue(battery);
|
|
1225
1199
|
}
|
|
1226
1200
|
|
|
1227
|
-
if (!isNaN(
|
|
1228
|
-
let service = acc.getService(api.hap.Service.
|
|
1229
|
-
let characteristic = api.hap.Characteristic.
|
|
1201
|
+
if (!isNaN(currentTemp)) {
|
|
1202
|
+
let service = acc.getService(api.hap.Service.TemperatureSensor);
|
|
1203
|
+
let characteristic = api.hap.Characteristic.CurrentTemperature;
|
|
1230
1204
|
|
|
1231
|
-
service.getCharacteristic(characteristic).updateValue(
|
|
1205
|
+
service.getCharacteristic(characteristic).updateValue(currentTemp);
|
|
1232
1206
|
}
|
|
1233
1207
|
}
|
|
1234
1208
|
});
|
|
1209
|
+
}
|
|
1235
1210
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
(acc) => acc && acc.context.config.subtype === 'zone-window-contact'
|
|
1239
|
-
);
|
|
1240
|
-
const windowSwitchAccessory = accessories.filter(
|
|
1241
|
-
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Open Window'
|
|
1242
|
-
);
|
|
1243
|
-
|
|
1244
|
-
if (windowContactAccessory.length) {
|
|
1245
|
-
windowContactAccessory.forEach((acc) => {
|
|
1246
|
-
if (acc.displayName.includes(zone.name)) {
|
|
1247
|
-
let serviceBattery = acc.getService(api.hap.Service.BatteryService);
|
|
1248
|
-
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
1211
|
+
//HumiditySensor
|
|
1212
|
+
const humidityAccessory = accessories.filter((acc) => acc && acc.context.config.subtype === 'zone-humidity');
|
|
1249
1213
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1214
|
+
humidityAccessory.forEach((acc) => {
|
|
1215
|
+
if (acc.displayName.includes(zone.name)) {
|
|
1216
|
+
let serviceBattery = acc.getService(api.hap.Service.BatteryService);
|
|
1217
|
+
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
1253
1218
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1219
|
+
if (serviceBattery && !isNaN(battery)) {
|
|
1220
|
+
serviceBattery.getCharacteristic(characteristicBattery).updateValue(battery);
|
|
1221
|
+
}
|
|
1256
1222
|
|
|
1257
|
-
|
|
1223
|
+
if (!isNaN(humidity)) {
|
|
1224
|
+
let service = acc.getService(api.hap.Service.HumiditySensor);
|
|
1225
|
+
let characteristic = api.hap.Characteristic.CurrentRelativeHumidity;
|
|
1258
1226
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
});
|
|
1227
|
+
service.getCharacteristic(characteristic).updateValue(humidity);
|
|
1228
|
+
}
|
|
1262
1229
|
}
|
|
1230
|
+
});
|
|
1263
1231
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1232
|
+
//WindowSensor
|
|
1233
|
+
const windowContactAccessory = accessories.filter(
|
|
1234
|
+
(acc) => acc && acc.context.config.subtype === 'zone-window-contact'
|
|
1235
|
+
);
|
|
1236
|
+
const windowSwitchAccessory = accessories.filter(
|
|
1237
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Open Window'
|
|
1238
|
+
);
|
|
1269
1239
|
|
|
1270
|
-
|
|
1240
|
+
if (windowContactAccessory.length) {
|
|
1241
|
+
windowContactAccessory.forEach((acc) => {
|
|
1242
|
+
if (acc.displayName.includes(zone.name)) {
|
|
1243
|
+
Logger.debug("Update window contact sensor.");
|
|
1244
|
+
|
|
1245
|
+
let serviceBattery = acc.getService(api.hap.Service.BatteryService);
|
|
1246
|
+
let characteristicBattery = api.hap.Characteristic.BatteryLevel;
|
|
1271
1247
|
|
|
1272
|
-
|
|
1248
|
+
if (serviceBattery && !isNaN(battery)) {
|
|
1249
|
+
serviceBattery.getCharacteristic(characteristicBattery).updateValue(battery);
|
|
1273
1250
|
}
|
|
1274
|
-
});
|
|
1275
|
-
}
|
|
1276
1251
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (zoneState.overlayType === null) inAutoMode += 1;
|
|
1252
|
+
let service = acc.getService(api.hap.Service.ContactSensor);
|
|
1253
|
+
let characteristic = api.hap.Characteristic.ContactSensorState;
|
|
1280
1254
|
|
|
1281
|
-
|
|
1255
|
+
let state = zoneState.openWindow || zoneState.openWindowDetected ? 1 : 0;
|
|
1282
1256
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1257
|
+
service.getCharacteristic(characteristic).updateValue(state);
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1286
1260
|
}
|
|
1287
1261
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1262
|
+
if (windowSwitchAccessory.length) {
|
|
1263
|
+
windowSwitchAccessory[0].services.forEach((switchService) => {
|
|
1264
|
+
if (switchService.subtype && switchService.subtype.includes(zone.name)) {
|
|
1265
|
+
Logger.debug("Update window switch accessory.");
|
|
1292
1266
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
if (service.subtype === 'Central') {
|
|
1296
|
-
let serviceSwitch = centralSwitchAccessory[0].getServiceById(api.hap.Service.Switch, service.subtype);
|
|
1297
|
-
let characteristicOn = api.hap.Characteristic.On;
|
|
1298
|
-
let characteristicAuto = api.hap.Characteristic.AutoThermostats;
|
|
1299
|
-
let characteristicOff = api.hap.Characteristic.OfflineThermostats;
|
|
1300
|
-
let characteristicManual = api.hap.Characteristic.ManualThermostats;
|
|
1267
|
+
let service = windowSwitchAccessory[0].getServiceById(api.hap.Service.Switch, switchService.subtype);
|
|
1268
|
+
let characteristic = api.hap.Characteristic.On;
|
|
1301
1269
|
|
|
1302
|
-
let state =
|
|
1270
|
+
let state = zone.openWindowEnabled ? true : false;
|
|
1303
1271
|
|
|
1304
|
-
|
|
1272
|
+
service.getCharacteristic(characteristic).updateValue(state);
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1305
1276
|
|
|
1306
|
-
|
|
1277
|
+
if (zoneState.setting.type === 'HEATING') {
|
|
1278
|
+
//CentralSwitch
|
|
1279
|
+
if (zoneState.overlayType === null) inAutoMode += 1;
|
|
1307
1280
|
|
|
1308
|
-
|
|
1281
|
+
if (zoneState.overlayType !== null && zoneState.setting.power === 'OFF') inOffMode += 1;
|
|
1309
1282
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
});
|
|
1283
|
+
if (zoneState.overlayType !== null && zoneState.setting.power === 'ON' && zoneState.overlay.termination)
|
|
1284
|
+
inManualMode += 1;
|
|
1313
1285
|
}
|
|
1314
1286
|
}
|
|
1287
|
+
|
|
1288
|
+
//CentralSwitch
|
|
1289
|
+
const centralSwitchAccessory = accessories.filter(
|
|
1290
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Central Switch'
|
|
1291
|
+
);
|
|
1292
|
+
|
|
1293
|
+
if (centralSwitchAccessory.length) {
|
|
1294
|
+
centralSwitchAccessory[0].services.forEach((service) => {
|
|
1295
|
+
if (service.subtype === 'Central') {
|
|
1296
|
+
let serviceSwitch = centralSwitchAccessory[0].getServiceById(api.hap.Service.Switch, service.subtype);
|
|
1297
|
+
let characteristicOn = api.hap.Characteristic.On;
|
|
1298
|
+
let characteristicAuto = api.hap.Characteristic.AutoThermostats;
|
|
1299
|
+
let characteristicOff = api.hap.Characteristic.OfflineThermostats;
|
|
1300
|
+
let characteristicManual = api.hap.Characteristic.ManualThermostats;
|
|
1301
|
+
|
|
1302
|
+
let state = (inManualMode || inAutoMode) !== 0;
|
|
1303
|
+
|
|
1304
|
+
Logger.debug(`Update central switch:`, { inAutoMode: inAutoMode, inManualMode: inManualMode, inOffMode: inOffMode, state: state });
|
|
1305
|
+
|
|
1306
|
+
serviceSwitch.getCharacteristic(characteristicAuto).updateValue(inAutoMode);
|
|
1307
|
+
serviceSwitch.getCharacteristic(characteristicManual).updateValue(inManualMode);
|
|
1308
|
+
serviceSwitch.getCharacteristic(characteristicOff).updateValue(inOffMode);
|
|
1309
|
+
serviceSwitch.getCharacteristic(characteristicOn).updateValue(state);
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1315
1313
|
return zoneStates;
|
|
1316
1314
|
}
|
|
1317
1315
|
|
|
1318
1316
|
async function updateMobileDevices() {
|
|
1319
|
-
if (
|
|
1320
|
-
Logger.debug('Polling MobileDevices...', config.homeName);
|
|
1317
|
+
if (settingState) return;
|
|
1321
1318
|
|
|
1322
|
-
|
|
1319
|
+
Logger.debug('Polling MobileDevices...', config.homeName);
|
|
1323
1320
|
|
|
1324
|
-
|
|
1325
|
-
(user) =>
|
|
1326
|
-
user &&
|
|
1327
|
-
user.context.config.subtype.includes('presence') &&
|
|
1328
|
-
user.displayName !== user.context.config.homeName + ' Anyone'
|
|
1329
|
-
);
|
|
1321
|
+
const mobileDevices = await tado.getMobileDevices(config.homeId);
|
|
1330
1322
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1323
|
+
const userAccessories = accessories.filter(
|
|
1324
|
+
(user) =>
|
|
1325
|
+
user &&
|
|
1326
|
+
user.context.config.subtype.includes('presence') &&
|
|
1327
|
+
user.displayName !== user.context.config.homeName + ' Anyone'
|
|
1328
|
+
);
|
|
1337
1329
|
|
|
1338
|
-
|
|
1330
|
+
const anyone = accessories.filter(
|
|
1331
|
+
(user) =>
|
|
1332
|
+
user &&
|
|
1333
|
+
user.context.config.subtype.includes('presence') &&
|
|
1334
|
+
user.displayName === user.context.config.homeName + ' Anyone'
|
|
1335
|
+
);
|
|
1339
1336
|
|
|
1340
|
-
|
|
1341
|
-
userAccessories.forEach((acc) => {
|
|
1342
|
-
if (acc.context.config.homeName + ' ' + device.name === acc.displayName) {
|
|
1343
|
-
let atHome = device.location && device.location.atHome ? 1 : 0;
|
|
1337
|
+
let activeUser = 0;
|
|
1344
1338
|
|
|
1345
|
-
|
|
1339
|
+
mobileDevices.forEach((device) => {
|
|
1340
|
+
userAccessories.forEach((acc) => {
|
|
1341
|
+
if (acc.context.config.homeName + ' ' + device.name === acc.displayName) {
|
|
1342
|
+
let atHome = device.location && device.location.atHome ? 1 : 0;
|
|
1346
1343
|
|
|
1347
|
-
|
|
1348
|
-
acc.getService(api.hap.Service.MotionSensor) || acc.getService(api.hap.Service.OccupancySensor);
|
|
1344
|
+
if (atHome) activeUser += 1;
|
|
1349
1345
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
: api.hap.Characteristic.OccupancyDetected;
|
|
1346
|
+
let service =
|
|
1347
|
+
acc.getService(api.hap.Service.MotionSensor) || acc.getService(api.hap.Service.OccupancySensor);
|
|
1353
1348
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1349
|
+
let characteristic = service.testCharacteristic(api.hap.Characteristic.MotionDetected)
|
|
1350
|
+
? api.hap.Characteristic.MotionDetected
|
|
1351
|
+
: api.hap.Characteristic.OccupancyDetected;
|
|
1352
|
+
|
|
1353
|
+
service.getCharacteristic(characteristic).updateValue(atHome);
|
|
1354
|
+
}
|
|
1357
1355
|
});
|
|
1356
|
+
});
|
|
1358
1357
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1358
|
+
if (anyone.length) {
|
|
1359
|
+
let service =
|
|
1360
|
+
anyone[0].getService(api.hap.Service.MotionSensor) || anyone[0].getService(api.hap.Service.OccupancySensor);
|
|
1362
1361
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1362
|
+
let characteristic = service.testCharacteristic(api.hap.Characteristic.MotionDetected)
|
|
1363
|
+
? api.hap.Characteristic.MotionDetected
|
|
1364
|
+
: api.hap.Characteristic.OccupancyDetected;
|
|
1366
1365
|
|
|
1367
|
-
|
|
1368
|
-
}
|
|
1366
|
+
service.getCharacteristic(characteristic).updateValue(activeUser ? 1 : 0);
|
|
1369
1367
|
}
|
|
1370
|
-
|
|
1371
|
-
return;
|
|
1372
1368
|
}
|
|
1373
1369
|
|
|
1374
1370
|
async function updateWeather() {
|
|
1375
|
-
if (
|
|
1376
|
-
const weatherTemperatureAccessory = accessories.filter(
|
|
1377
|
-
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Weather'
|
|
1378
|
-
);
|
|
1371
|
+
if (settingState) return;
|
|
1379
1372
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1373
|
+
const weatherTemperatureAccessory = accessories.filter(
|
|
1374
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Weather'
|
|
1375
|
+
);
|
|
1383
1376
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1377
|
+
const solarIntensityAccessory = accessories.filter(
|
|
1378
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Solar Intensity'
|
|
1379
|
+
);
|
|
1386
1380
|
|
|
1387
|
-
|
|
1381
|
+
if (weatherTemperatureAccessory.length || solarIntensityAccessory.length) {
|
|
1382
|
+
Logger.debug('Polling Weather...', config.homeName);
|
|
1388
1383
|
|
|
1389
|
-
|
|
1390
|
-
let tempUnit = config.temperatureUnit;
|
|
1391
|
-
let service = weatherTemperatureAccessory[0].getService(api.hap.Service.TemperatureSensor);
|
|
1392
|
-
let characteristic = api.hap.Characteristic.CurrentTemperature;
|
|
1384
|
+
const weather = await tado.getWeather(config.homeId);
|
|
1393
1385
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1386
|
+
if (weatherTemperatureAccessory.length && weather.outsideTemperature) {
|
|
1387
|
+
let tempUnit = config.temperatureUnit;
|
|
1388
|
+
let service = weatherTemperatureAccessory[0].getService(api.hap.Service.TemperatureSensor);
|
|
1389
|
+
let characteristic = api.hap.Characteristic.CurrentTemperature;
|
|
1396
1390
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1391
|
+
let temp =
|
|
1392
|
+
tempUnit === 'FAHRENHEIT' ? weather.outsideTemperature.fahrenheit : weather.outsideTemperature.celsius;
|
|
1399
1393
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
let brightness = weather.solarIntensity.percentage;
|
|
1394
|
+
service.getCharacteristic(characteristic).updateValue(temp);
|
|
1395
|
+
}
|
|
1403
1396
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1397
|
+
if (solarIntensityAccessory.length && weather.solarIntensity) {
|
|
1398
|
+
let state = weather.solarIntensity.percentage !== 0;
|
|
1399
|
+
let brightness = weather.solarIntensity.percentage;
|
|
1406
1400
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1401
|
+
solarIntensityAccessory[0].context.lightBulbState = state;
|
|
1402
|
+
solarIntensityAccessory[0].context.lightBulbBrightness = brightness;
|
|
1409
1403
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
let characteristicBrightness = api.hap.Characteristic.Brightness;
|
|
1404
|
+
let serviceLightbulb = solarIntensityAccessory[0].getService(api.hap.Service.Lightbulb);
|
|
1405
|
+
let serviceLightsensor = solarIntensityAccessory[0].getService(api.hap.Service.LightSensor);
|
|
1413
1406
|
|
|
1414
|
-
|
|
1407
|
+
if (serviceLightbulb) {
|
|
1408
|
+
let characteristicOn = api.hap.Characteristic.On;
|
|
1409
|
+
let characteristicBrightness = api.hap.Characteristic.Brightness;
|
|
1415
1410
|
|
|
1416
|
-
|
|
1417
|
-
} else {
|
|
1418
|
-
let characteristicLux = api.hap.Characteristic.CurrentAmbientLightLevel;
|
|
1411
|
+
serviceLightbulb.getCharacteristic(characteristicOn).updateValue(state);
|
|
1419
1412
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1413
|
+
serviceLightbulb.getCharacteristic(characteristicBrightness).updateValue(brightness);
|
|
1414
|
+
} else {
|
|
1415
|
+
let characteristicLux = api.hap.Characteristic.CurrentAmbientLightLevel;
|
|
1416
|
+
|
|
1417
|
+
serviceLightsensor
|
|
1418
|
+
.getCharacteristic(characteristicLux)
|
|
1419
|
+
.updateValue(brightness ? brightness * 1000 : 0.0001);
|
|
1424
1420
|
}
|
|
1425
1421
|
}
|
|
1426
1422
|
}
|
|
1427
|
-
|
|
1428
|
-
return;
|
|
1429
1423
|
}
|
|
1430
1424
|
|
|
1431
1425
|
async function updatePresence() {
|
|
1432
|
-
if (
|
|
1433
|
-
const presenceLockAccessory = accessories.filter(
|
|
1434
|
-
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Presence Lock'
|
|
1435
|
-
);
|
|
1426
|
+
if (settingState) return;
|
|
1436
1427
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1428
|
+
const presenceLockAccessory = accessories.filter(
|
|
1429
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Presence Lock'
|
|
1430
|
+
);
|
|
1439
1431
|
|
|
1440
|
-
|
|
1432
|
+
if (presenceLockAccessory.length) {
|
|
1433
|
+
Logger.debug('Polling PresenceLock...', config.homeName);
|
|
1441
1434
|
|
|
1442
|
-
|
|
1443
|
-
0: Home | true
|
|
1444
|
-
1: Away | true
|
|
1445
|
-
3: Off | false
|
|
1446
|
-
*/
|
|
1435
|
+
const presenceLock = await tado.getState(config.homeId);
|
|
1447
1436
|
|
|
1448
|
-
|
|
1437
|
+
/*
|
|
1438
|
+
0: Home | true
|
|
1439
|
+
1: Away | true
|
|
1440
|
+
3: Off | false
|
|
1441
|
+
*/
|
|
1449
1442
|
|
|
1450
|
-
|
|
1451
|
-
let serviceHomeSwitch = presenceLockAccessory[0].getServiceById(api.hap.Service.Switch, 'HomeSwitch');
|
|
1452
|
-
let serviceAwaySwitch = presenceLockAccessory[0].getServiceById(api.hap.Service.Switch, 'AwaySwitch');
|
|
1443
|
+
let state = presenceLock.presenceLocked ? (presenceLock.presence === 'AWAY' ? 1 : 0) : 3;
|
|
1453
1444
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1445
|
+
let serviceSecurity = presenceLockAccessory[0].getService(api.hap.Service.SecuritySystem);
|
|
1446
|
+
let serviceHomeSwitch = presenceLockAccessory[0].getServiceById(api.hap.Service.Switch, 'HomeSwitch');
|
|
1447
|
+
let serviceAwaySwitch = presenceLockAccessory[0].getServiceById(api.hap.Service.Switch, 'AwaySwitch');
|
|
1457
1448
|
|
|
1458
|
-
|
|
1449
|
+
if (serviceSecurity) {
|
|
1450
|
+
let characteristicCurrent = api.hap.Characteristic.SecuritySystemCurrentState;
|
|
1451
|
+
let characteristicTarget = api.hap.Characteristic.SecuritySystemTargetState;
|
|
1459
1452
|
|
|
1460
|
-
|
|
1461
|
-
} else if (serviceHomeSwitch || serviceAwaySwitch) {
|
|
1462
|
-
let characteristicOn = api.hap.Characteristic.On;
|
|
1453
|
+
serviceSecurity.getCharacteristic(characteristicCurrent).updateValue(state);
|
|
1463
1454
|
|
|
1464
|
-
|
|
1455
|
+
serviceSecurity.getCharacteristic(characteristicTarget).updateValue(state);
|
|
1456
|
+
} else if (serviceHomeSwitch || serviceAwaySwitch) {
|
|
1457
|
+
let characteristicOn = api.hap.Characteristic.On;
|
|
1465
1458
|
|
|
1466
|
-
|
|
1459
|
+
let homeState = !state ? true : false;
|
|
1467
1460
|
|
|
1468
|
-
|
|
1461
|
+
let awayState = state === 1 ? true : false;
|
|
1469
1462
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1463
|
+
serviceAwaySwitch.getCharacteristic(characteristicOn).updateValue(awayState);
|
|
1464
|
+
|
|
1465
|
+
serviceHomeSwitch.getCharacteristic(characteristicOn).updateValue(homeState);
|
|
1472
1466
|
}
|
|
1473
1467
|
}
|
|
1474
1468
|
}
|
|
1475
1469
|
|
|
1476
1470
|
async function updateRunningTime() {
|
|
1477
|
-
if (
|
|
1478
|
-
const centralSwitchAccessory = accessories.filter(
|
|
1479
|
-
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Central Switch'
|
|
1480
|
-
);
|
|
1471
|
+
if (settingState) return;
|
|
1481
1472
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1473
|
+
const centralSwitchAccessory = accessories.filter(
|
|
1474
|
+
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Central Switch'
|
|
1475
|
+
);
|
|
1484
1476
|
|
|
1485
|
-
|
|
1477
|
+
if (centralSwitchAccessory.length) {
|
|
1478
|
+
Logger.debug('Polling RunningTime...', config.homeName);
|
|
1486
1479
|
|
|
1487
|
-
|
|
1488
|
-
let fromDate =
|
|
1489
|
-
period === 'days'
|
|
1490
|
-
? moment().format('YYYY-MM-DD')
|
|
1491
|
-
: period === 'months'
|
|
1492
|
-
? moment().subtract(1, 'days').subtract(1, period).format('YYYY-MM-DD')
|
|
1493
|
-
: moment().add(1, 'months').startOf('month').subtract(1, period).format('YYYY-MM-DD');
|
|
1480
|
+
let periods = ['days', 'months', 'years'];
|
|
1494
1481
|
|
|
1495
|
-
|
|
1482
|
+
for (const period of periods) {
|
|
1483
|
+
let fromDate =
|
|
1484
|
+
period === 'days'
|
|
1485
|
+
? moment().format('YYYY-MM-DD')
|
|
1486
|
+
: period === 'months'
|
|
1487
|
+
? moment().subtract(1, 'days').subtract(1, period).format('YYYY-MM-DD')
|
|
1488
|
+
: moment().add(1, 'months').startOf('month').subtract(1, period).format('YYYY-MM-DD');
|
|
1496
1489
|
|
|
1497
|
-
|
|
1490
|
+
let toDate = period === 'years' ? moment().format('YYYY-MM-DD') : false;
|
|
1498
1491
|
|
|
1499
|
-
|
|
1492
|
+
let time = period.substring(0, period.length - 1);
|
|
1500
1493
|
|
|
1501
|
-
|
|
1502
|
-
let summaryInHours = runningTime.summary.totalRunningTimeInSeconds / 3600;
|
|
1494
|
+
const runningTime = await tado.getRunningTime(config.homeId, time, fromDate, toDate);
|
|
1503
1495
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
period === 'years'
|
|
1507
|
-
? api.hap.Characteristic.OverallHeatYear
|
|
1508
|
-
: period === 'months'
|
|
1509
|
-
? api.hap.Characteristic.OverallHeatMonth
|
|
1510
|
-
: api.hap.Characteristic.OverallHeatDay;
|
|
1496
|
+
if (runningTime && runningTime.summary) {
|
|
1497
|
+
let summaryInHours = runningTime.summary.totalRunningTimeInSeconds / 3600;
|
|
1511
1498
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1499
|
+
let serviceSwitch = centralSwitchAccessory[0].getServiceById(api.hap.Service.Switch, 'Central');
|
|
1500
|
+
let characteristic =
|
|
1501
|
+
period === 'years'
|
|
1502
|
+
? api.hap.Characteristic.OverallHeatYear
|
|
1503
|
+
: period === 'months'
|
|
1504
|
+
? api.hap.Characteristic.OverallHeatMonth
|
|
1505
|
+
: api.hap.Characteristic.OverallHeatDay;
|
|
1514
1506
|
|
|
1515
|
-
|
|
1507
|
+
serviceSwitch.getCharacteristic(characteristic).updateValue(summaryInHours);
|
|
1516
1508
|
}
|
|
1509
|
+
|
|
1510
|
+
await timeout(500);
|
|
1517
1511
|
}
|
|
1518
1512
|
}
|
|
1519
|
-
|
|
1520
|
-
return;
|
|
1521
1513
|
}
|
|
1522
1514
|
|
|
1523
1515
|
async function updateDevices() {
|
|
1524
|
-
if (
|
|
1525
|
-
Logger.debug('Polling Devices...', config.homeName);
|
|
1516
|
+
if (settingState) return;
|
|
1526
1517
|
|
|
1527
|
-
|
|
1518
|
+
Logger.debug('Polling Devices...', config.homeName);
|
|
1528
1519
|
|
|
1529
|
-
|
|
1530
|
-
(acc) => acc && acc.context.config.subtype === 'extra-childswitch'
|
|
1531
|
-
);
|
|
1520
|
+
const devices = await tado.getDevices(config.homeId);
|
|
1532
1521
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
let serviceChildLock = childLockAccessories[0].getServiceById(api.hap.Service.Switch, service.subtype);
|
|
1537
|
-
let characteristic = api.hap.Characteristic.On;
|
|
1522
|
+
const childLockAccessories = accessories.filter(
|
|
1523
|
+
(acc) => acc && acc.context.config.subtype === 'extra-childswitch'
|
|
1524
|
+
);
|
|
1538
1525
|
|
|
1539
|
-
|
|
1526
|
+
devices.forEach((device) => {
|
|
1527
|
+
childLockAccessories[0].services.forEach((service) => {
|
|
1528
|
+
if (device.serialNo === service.subtype) {
|
|
1529
|
+
let serviceChildLock = childLockAccessories[0].getServiceById(api.hap.Service.Switch, service.subtype);
|
|
1530
|
+
let characteristic = api.hap.Characteristic.On;
|
|
1540
1531
|
|
|
1541
|
-
|
|
1542
|
-
}
|
|
1543
|
-
});
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1532
|
+
let childLockEnabled = device.childLockEnabled || false;
|
|
1546
1533
|
|
|
1547
|
-
|
|
1534
|
+
serviceChildLock.getCharacteristic(characteristic).updateValue(childLockEnabled);
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
});
|
|
1548
1538
|
}
|
|
1549
1539
|
|
|
1550
1540
|
function errorHandler(err) {
|