@homebridge-plugins/homebridge-tado 6.0.14
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 +152 -0
- package/LICENSE +21 -0
- package/README.md +560 -0
- package/config.schema.json +878 -0
- package/homebridge-ui/public/css/style.css +25 -0
- package/homebridge-ui/public/images/tado_logo.png +0 -0
- package/homebridge-ui/public/index.html +118 -0
- package/homebridge-ui/public/js/main.js +1582 -0
- package/homebridge-ui/public/js/modules/compareVersions.min.js +1 -0
- package/homebridge-ui/public/js/modules/jquery.min.js +2 -0
- package/homebridge-ui/public/js/modules/progressbar.min.js +6 -0
- package/homebridge-ui/public/js/progressbars.js +48 -0
- package/homebridge-ui/public/js/schema.js +864 -0
- package/homebridge-ui/server.js +80 -0
- package/images/tado_logo.png +0 -0
- package/index.js +14 -0
- package/package.json +66 -0
- package/src/accessories/airquality.js +56 -0
- package/src/accessories/contact.js +124 -0
- package/src/accessories/faucet.js +63 -0
- package/src/accessories/heatercooler.js +333 -0
- package/src/accessories/humidity.js +90 -0
- package/src/accessories/lightbulb.js +59 -0
- package/src/accessories/lightsensor.js +40 -0
- package/src/accessories/motion.js +79 -0
- package/src/accessories/occupancy.js +45 -0
- package/src/accessories/security.js +79 -0
- package/src/accessories/switch.js +261 -0
- package/src/accessories/temperature.js +95 -0
- package/src/accessories/thermostat.js +337 -0
- package/src/helper/handler.js +1467 -0
- package/src/helper/logger.js +51 -0
- package/src/helper/telegram.js +60 -0
- package/src/platform.js +337 -0
- package/src/tado/tado-api.js +404 -0
- package/src/tado/tado-config.js +1032 -0
- package/src/types/custom.js +264 -0
- package/src/types/eve.js +337 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const { HomebridgePluginUiServer } = require('@homebridge/plugin-ui-utils');
|
|
2
|
+
const { RequestError } = require('@homebridge/plugin-ui-utils');
|
|
3
|
+
|
|
4
|
+
const TadoApi = require('../src/tado/tado-api.js');
|
|
5
|
+
|
|
6
|
+
class UiServer extends HomebridgePluginUiServer {
|
|
7
|
+
constructor () {
|
|
8
|
+
|
|
9
|
+
super();
|
|
10
|
+
|
|
11
|
+
this.onRequest('/authenticate', this.authenticate.bind(this));
|
|
12
|
+
this.onRequest('/exec', this.exec.bind(this));
|
|
13
|
+
this.onRequest('/reset', this.reset.bind(this));
|
|
14
|
+
|
|
15
|
+
this.tado = false;
|
|
16
|
+
|
|
17
|
+
this.ready();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
authenticate(config){
|
|
21
|
+
|
|
22
|
+
this.tado = new TadoApi('Config UI X', { username: config.username, password: config.password });
|
|
23
|
+
|
|
24
|
+
return;
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
reset(){
|
|
29
|
+
|
|
30
|
+
this.tado = false;
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async exec(payload){
|
|
37
|
+
|
|
38
|
+
if(this.tado){
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
|
|
42
|
+
console.log('Executing /' + payload.dest);
|
|
43
|
+
|
|
44
|
+
let value1, value2, value3;
|
|
45
|
+
|
|
46
|
+
if(payload.data){
|
|
47
|
+
if(typeof payload.data === 'object'){
|
|
48
|
+
value1 = payload.data[0];
|
|
49
|
+
value2 = payload.data[1];
|
|
50
|
+
value3 = payload.data[2];
|
|
51
|
+
} else {
|
|
52
|
+
value1 = payload.data;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const data = await this.tado[payload.dest](value1, value2, value3);
|
|
57
|
+
|
|
58
|
+
return data;
|
|
59
|
+
|
|
60
|
+
} catch(err){
|
|
61
|
+
|
|
62
|
+
console.log(err);
|
|
63
|
+
|
|
64
|
+
throw new RequestError(err.message);
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
} else {
|
|
69
|
+
|
|
70
|
+
throw new RequestError('API not initialized!');
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
(() => {
|
|
79
|
+
return new UiServer;
|
|
80
|
+
})();
|
|
Binary file
|
package/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v6
|
|
3
|
+
*
|
|
4
|
+
* @url https://github.com/homebridge-plugins/homebridge-tado
|
|
5
|
+
* @author SeydX <seyd55@outlook.de>
|
|
6
|
+
*
|
|
7
|
+
**/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
module.exports = function (homebridge) {
|
|
12
|
+
let TadoPlatform = require('./src/platform.js')(homebridge);
|
|
13
|
+
homebridge.registerPlatform('@homebridge-plugins/homebridge-tado', 'TadoPlatform', TadoPlatform, true);
|
|
14
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@homebridge-plugins/homebridge-tado",
|
|
3
|
+
"version": "6.0.14",
|
|
4
|
+
"description": "Homebridge plugin for controlling Tado devices",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"funding": [
|
|
7
|
+
{
|
|
8
|
+
"type": "paypal",
|
|
9
|
+
"url": "https://paypal.me/seydx"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "kofi",
|
|
13
|
+
"url": "https://ko-fi.com/seydx"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "github",
|
|
17
|
+
"url": "https://github.com/sponsors/seydx"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"lint": "eslint --fix ."
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/homebridge-plugins/homebridge-tado.git"
|
|
26
|
+
},
|
|
27
|
+
"author": "SeydX (https://github.com/homebridge-plugins/homebridge-tado)",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/homebridge-plugins/homebridge-tado/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/homebridge-plugins/homebridge-tado#readme",
|
|
33
|
+
"keywords": [
|
|
34
|
+
"tado",
|
|
35
|
+
"thermostat",
|
|
36
|
+
"homebridge",
|
|
37
|
+
"homebridge-plugin",
|
|
38
|
+
"plugin",
|
|
39
|
+
"boiler",
|
|
40
|
+
"presence",
|
|
41
|
+
"heat"
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=12.18.3",
|
|
45
|
+
"homebridge": "^1.1.6"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@homebridge/plugin-ui-utils": "^0.0.19",
|
|
49
|
+
"fakegato-history": "^0.6.1",
|
|
50
|
+
"form-data": "^4.0.0",
|
|
51
|
+
"fs-extra": "^10.0.0",
|
|
52
|
+
"got": "^11.8.2",
|
|
53
|
+
"moment": "^2.29.1",
|
|
54
|
+
"simple-oauth2": "^4.2.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@babel/core": "7.14.3",
|
|
58
|
+
"@babel/eslint-parser": "7.14.4",
|
|
59
|
+
"@babel/eslint-plugin": "7.13.16",
|
|
60
|
+
"eslint": "^7.27.0",
|
|
61
|
+
"eslint-config-prettier": "^8.3.0",
|
|
62
|
+
"eslint-plugin-import": "^2.23.4",
|
|
63
|
+
"eslint-plugin-prettier": "^3.4.0",
|
|
64
|
+
"prettier": "^2.3.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Logger = require('../helper/logger.js');
|
|
4
|
+
|
|
5
|
+
class AirQualityAccessory {
|
|
6
|
+
constructor(api, accessory, accessories, tado) {
|
|
7
|
+
this.api = api;
|
|
8
|
+
this.accessory = accessory;
|
|
9
|
+
this.accessories = accessories;
|
|
10
|
+
|
|
11
|
+
this.tado = tado;
|
|
12
|
+
|
|
13
|
+
this.getService();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
17
|
+
// Services
|
|
18
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
19
|
+
|
|
20
|
+
getService() {
|
|
21
|
+
let service = this.accessory.getService(this.api.hap.Service.AirQualitySensor);
|
|
22
|
+
|
|
23
|
+
if (!service) {
|
|
24
|
+
Logger.info('Adding AirQualitySensor service', this.accessory.displayName);
|
|
25
|
+
service = this.accessory.addService(
|
|
26
|
+
this.api.hap.Service.AirQualitySensor,
|
|
27
|
+
this.accessory.displayName,
|
|
28
|
+
this.accessory.context.config.subtype
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.PM10Density))
|
|
33
|
+
service.addCharacteristic(this.api.hap.Characteristic.PM10Density);
|
|
34
|
+
|
|
35
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.PM2_5Density))
|
|
36
|
+
service.addCharacteristic(this.api.hap.Characteristic.PM2_5Density);
|
|
37
|
+
|
|
38
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.NitrogenDioxideDensity))
|
|
39
|
+
service.addCharacteristic(this.api.hap.Characteristic.NitrogenDioxideDensity);
|
|
40
|
+
|
|
41
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.OzoneDensity))
|
|
42
|
+
service.addCharacteristic(this.api.hap.Characteristic.OzoneDensity);
|
|
43
|
+
|
|
44
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.SulphurDioxideDensity))
|
|
45
|
+
service.addCharacteristic(this.api.hap.Characteristic.SulphurDioxideDensity);
|
|
46
|
+
|
|
47
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.CarbonMonoxideLevel))
|
|
48
|
+
service.addCharacteristic(this.api.hap.Characteristic.CarbonMonoxideLevel);
|
|
49
|
+
|
|
50
|
+
service.getCharacteristic(this.api.hap.Characteristic.CarbonMonoxideLevel).setProps({
|
|
51
|
+
minStep: 0.01,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = AirQualityAccessory;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Logger = require('../helper/logger.js');
|
|
4
|
+
|
|
5
|
+
const moment = require('moment');
|
|
6
|
+
|
|
7
|
+
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
8
|
+
|
|
9
|
+
class ContactAccessory {
|
|
10
|
+
constructor(api, accessory, accessories, tado, deviceHandler, FakeGatoHistoryService) {
|
|
11
|
+
this.api = api;
|
|
12
|
+
this.accessory = accessory;
|
|
13
|
+
this.accessories = accessories;
|
|
14
|
+
this.FakeGatoHistoryService = FakeGatoHistoryService;
|
|
15
|
+
|
|
16
|
+
this.deviceHandler = deviceHandler;
|
|
17
|
+
this.tado = tado;
|
|
18
|
+
|
|
19
|
+
this.getService();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
23
|
+
// Services
|
|
24
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
25
|
+
|
|
26
|
+
async getService() {
|
|
27
|
+
let service = this.accessory.getService(this.api.hap.Service.ContactSensor);
|
|
28
|
+
let serviceSwitch = this.accessory.getService(this.api.hap.Service.Switch);
|
|
29
|
+
|
|
30
|
+
if (serviceSwitch) {
|
|
31
|
+
Logger.info('Removing Switch service', this.accessory.displayName);
|
|
32
|
+
this.accessory.removeService(serviceSwitch);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!service) {
|
|
36
|
+
Logger.info('Adding ContactSensor service', this.accessory.displayName);
|
|
37
|
+
service = this.accessory.addService(
|
|
38
|
+
this.api.hap.Service.ContactSensor,
|
|
39
|
+
this.accessory.displayName,
|
|
40
|
+
this.accessory.context.config.subtype
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let batteryService = this.accessory.getService(this.api.hap.Service.BatteryService);
|
|
45
|
+
|
|
46
|
+
if (!this.accessory.context.config.noBattery && this.accessory.context.config.type === 'HEATING') {
|
|
47
|
+
if (!batteryService) {
|
|
48
|
+
Logger.info('Adding Battery service', this.accessory.displayName);
|
|
49
|
+
batteryService = this.accessory.addService(this.api.hap.Service.BatteryService);
|
|
50
|
+
}
|
|
51
|
+
batteryService.setCharacteristic(
|
|
52
|
+
this.api.hap.Characteristic.ChargingState,
|
|
53
|
+
this.api.hap.Characteristic.ChargingState.NOT_CHARGEABLE
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
if (batteryService) {
|
|
57
|
+
Logger.info('Removing Battery service', this.accessory.displayName);
|
|
58
|
+
this.accessory.removeService(batteryService);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.LastActivation))
|
|
63
|
+
service.addCharacteristic(this.api.hap.Characteristic.LastActivation);
|
|
64
|
+
|
|
65
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.TimesOpened))
|
|
66
|
+
service.addCharacteristic(this.api.hap.Characteristic.TimesOpened);
|
|
67
|
+
|
|
68
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.ResetTotal))
|
|
69
|
+
service.addCharacteristic(this.api.hap.Characteristic.ResetTotal);
|
|
70
|
+
|
|
71
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.OpenDuration))
|
|
72
|
+
service.addCharacteristic(this.api.hap.Characteristic.OpenDuration);
|
|
73
|
+
|
|
74
|
+
if (!service.testCharacteristic(this.api.hap.Characteristic.ClosedDuration))
|
|
75
|
+
service.addCharacteristic(this.api.hap.Characteristic.ClosedDuration);
|
|
76
|
+
|
|
77
|
+
service.getCharacteristic(this.api.hap.Characteristic.ResetTotal).onSet((value) => {
|
|
78
|
+
Logger.info(value + ': Resetting FakeGato..', this.accessory.displayName);
|
|
79
|
+
|
|
80
|
+
const now = Math.round(new Date().valueOf() / 1000);
|
|
81
|
+
const epoch = Math.round(new Date('2001-01-01T00:00:00Z').valueOf() / 1000);
|
|
82
|
+
|
|
83
|
+
service.getCharacteristic(this.api.hap.Characteristic.ResetTotal).updateValue(now - epoch);
|
|
84
|
+
|
|
85
|
+
this.accessory.context.timesOpened = 0;
|
|
86
|
+
|
|
87
|
+
service
|
|
88
|
+
.getCharacteristic(this.api.hap.Characteristic.TimesOpened)
|
|
89
|
+
.updateValue(this.accessory.context.timesOpened);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.historyService = new this.FakeGatoHistoryService('door', this.accessory, {
|
|
93
|
+
storage: 'fs',
|
|
94
|
+
path: this.api.user.storagePath(),
|
|
95
|
+
disableTimer: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await timeout(250); //wait for historyService to load
|
|
99
|
+
|
|
100
|
+
service
|
|
101
|
+
.getCharacteristic(this.api.hap.Characteristic.ContactSensorState)
|
|
102
|
+
.on(
|
|
103
|
+
'change',
|
|
104
|
+
this.deviceHandler.changedStates.bind(this, this.accessory, this.historyService, this.accessory.displayName)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
this.refreshHistory(service);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
refreshHistory(service) {
|
|
111
|
+
let state = service.getCharacteristic(this.api.hap.Characteristic.ContactSensorState).value;
|
|
112
|
+
|
|
113
|
+
this.historyService.addEntry({
|
|
114
|
+
time: moment().unix(),
|
|
115
|
+
status: state ? 1 : 0,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
this.refreshHistory(service);
|
|
120
|
+
}, 10 * 60 * 1000);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = ContactAccessory;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Logger = require('../helper/logger.js');
|
|
4
|
+
|
|
5
|
+
class FaucetAccessory {
|
|
6
|
+
constructor(api, accessory, accessories, tado, deviceHandler) {
|
|
7
|
+
this.api = api;
|
|
8
|
+
this.accessory = accessory;
|
|
9
|
+
this.accessories = accessories;
|
|
10
|
+
|
|
11
|
+
this.deviceHandler = deviceHandler;
|
|
12
|
+
this.tado = tado;
|
|
13
|
+
|
|
14
|
+
this.getService();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
18
|
+
// Services
|
|
19
|
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
|
20
|
+
|
|
21
|
+
getService() {
|
|
22
|
+
let service = this.accessory.getService(this.api.hap.Service.Valve);
|
|
23
|
+
let serviceSwitch = this.accessory.getService(this.api.hap.Service.Switch);
|
|
24
|
+
let serviceHeaterCooler = this.accessory.getService(this.api.hap.Service.HeaterCooler);
|
|
25
|
+
let serviceThermostat = this.accessory.getService(this.api.hap.Service.Thermostat);
|
|
26
|
+
|
|
27
|
+
if (serviceSwitch) {
|
|
28
|
+
Logger.info('Removing Switch service', this.accessory.displayName);
|
|
29
|
+
this.accessory.removeService(serviceSwitch);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (serviceThermostat) {
|
|
33
|
+
Logger.info('Removing Thermostat service', this.accessory.displayName);
|
|
34
|
+
this.accessory.removeService(serviceThermostat);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (serviceHeaterCooler) {
|
|
38
|
+
Logger.info('Removing HeaterCooler service', this.accessory.displayName);
|
|
39
|
+
this.accessory.removeService(serviceHeaterCooler);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!service) {
|
|
43
|
+
Logger.info('Adding Faucet service', this.accessory.displayName);
|
|
44
|
+
service = this.accessory.addService(
|
|
45
|
+
this.api.hap.Service.Valve,
|
|
46
|
+
this.accessory.displayName,
|
|
47
|
+
this.accessory.context.config.subtype
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
service
|
|
52
|
+
.setCharacteristic(this.api.hap.Characteristic.Name, this.accessory.displayName)
|
|
53
|
+
.setCharacteristic(this.api.hap.Characteristic.IsConfigured, this.api.hap.Characteristic.IsConfigured.CONFIGURED)
|
|
54
|
+
.setCharacteristic(this.api.hap.Characteristic.StatusFault, this.api.hap.Characteristic.StatusFault.NO_FAULT)
|
|
55
|
+
.setCharacteristic(this.api.hap.Characteristic.ValveType, this.api.hap.Characteristic.ValveType.WATER_FAUCET);
|
|
56
|
+
|
|
57
|
+
service
|
|
58
|
+
.getCharacteristic(this.api.hap.Characteristic.Active)
|
|
59
|
+
.onSet(this.deviceHandler.setStates.bind(this, this.accessory, this.accessories, 'State'));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = FaucetAccessory;
|