homebridge-tasmota-control 1.4.0-beta.8 → 1.4.0
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 +2 -1
- package/index.js +73 -30
- package/package.json +1 -1
- package/src/deviceinfo.js +77 -0
- package/src/fans.js +714 -0
- package/src/lights.js +735 -0
- package/src/mielhvac.js +1395 -0
- package/src/switches.js +624 -0
- package/src/tasmotadevice.js +0 -2534
package/CHANGELOG.md
CHANGED
|
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## Changes
|
|
11
11
|
|
|
12
|
-
- added support for
|
|
12
|
+
- added support for iFan, solved [#24](https://github.com/grzegorz914/homebridge-tasmota-control/issues/24)
|
|
13
|
+
- full code refactor
|
|
13
14
|
- config schema updated
|
|
14
15
|
- redme updated
|
|
15
16
|
- cleanup
|
package/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { mkdirSync, existsSync, writeFileSync } from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import deviceinfo from './src/deviceinfo.js';
|
|
4
|
+
import mielhvac from './src/mielhvac.js';
|
|
5
|
+
import switches from './src/switches.js';
|
|
6
|
+
import lights from './src/lights.js';
|
|
7
|
+
import fans from './src/fans.js';
|
|
4
8
|
import ImpulseGenerator from './src/impulsegenerator.js';
|
|
5
9
|
import { PluginName, PlatformName } from './src/constants.js';
|
|
6
10
|
|
|
@@ -39,6 +43,12 @@ class tasmotaPlatform {
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
//log config
|
|
46
|
+
const url = `http://${host}/cm?cmnd=`;
|
|
47
|
+
const auth = device.auth || false;
|
|
48
|
+
const user = device.user || '';
|
|
49
|
+
const passwd = device.passwd || '';
|
|
50
|
+
const loadNameFromDevice = device.loadNameFromDevice || false;
|
|
51
|
+
const refreshInterval = device.refreshInterval * 1000 || 5000;
|
|
42
52
|
const enableDebugMode = device.enableDebugMode || false;
|
|
43
53
|
const disableLogDeviceInfo = device.disableLogDeviceInfo || false;
|
|
44
54
|
const disableLogInfo = device.disableLogInfo || false;
|
|
@@ -46,40 +56,73 @@ class tasmotaPlatform {
|
|
|
46
56
|
const disableLogWarn = device.disableLogWarn || false;
|
|
47
57
|
const disableLogError = device.disableLogError || false;
|
|
48
58
|
const debug = enableDebugMode ? log.info(`Device: ${host} ${deviceName}, debug: Did finish launching.`) : false;
|
|
49
|
-
const
|
|
59
|
+
const newConfig = {
|
|
50
60
|
...device,
|
|
51
61
|
user: 'removed',
|
|
52
62
|
passwd: 'removed'
|
|
53
63
|
};
|
|
54
|
-
const debug1 = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, Config: ${JSON.stringify(
|
|
55
|
-
|
|
56
|
-
//check files exists, if not then create it
|
|
57
|
-
const postFix = device.host.split('.').join('');
|
|
58
|
-
const defaultHeatingSetTemperatureFile = `${prefDir}/defaultHeatingSetTemperature_${postFix}`;
|
|
59
|
-
const defaultCoolingSetTemperatureFile = `${prefDir}/defaultCoolingSetTemperature_${postFix}`;
|
|
64
|
+
const debug1 = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, Config: ${JSON.stringify(newConfig, null, 2)}.`);
|
|
60
65
|
|
|
61
66
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} catch (error) {
|
|
74
|
-
log.error(`Device: ${host} ${deviceName}, Prepare files error: ${error}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
67
|
+
//get device info
|
|
68
|
+
const deviceInfo = new deviceinfo(url, auth, user, passwd, deviceName, loadNameFromDevice, enableDebugMode, refreshInterval);
|
|
69
|
+
deviceInfo.on('debug', (debug) => {
|
|
70
|
+
const emitLog = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, debug: ${debug}.`);
|
|
71
|
+
})
|
|
72
|
+
.on('warn', (warn) => {
|
|
73
|
+
const emitLog = disableLogWarn ? false : log.warn(`Device: ${host} ${deviceName}, ${warn}.`);
|
|
74
|
+
})
|
|
75
|
+
.on('error', (error) => {
|
|
76
|
+
const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}.`);
|
|
77
|
+
});
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
const info = await deviceInfo.getInfo();
|
|
80
|
+
if (!info.serialNumber) {
|
|
81
|
+
log.warn(`Device: ${host} ${deviceName}, serial not found.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let deviceType;
|
|
86
|
+
switch (info.deviceType) {
|
|
87
|
+
case 0://mielhvac
|
|
88
|
+
//check files exists, if not then create it
|
|
89
|
+
try {
|
|
90
|
+
const postFix = device.host.split('.').join('');
|
|
91
|
+
info.defaultHeatingSetTemperatureFile = `${prefDir}/defaultHeatingSetTemperature_${postFix}`;
|
|
92
|
+
info.defaultCoolingSetTemperatureFile = `${prefDir}/defaultCoolingSetTemperature_${postFix}`;
|
|
93
|
+
const files = [
|
|
94
|
+
info.defaultHeatingSetTemperatureFile,
|
|
95
|
+
info.defaultCoolingSetTemperatureFile
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
files.forEach((file, index) => {
|
|
99
|
+
if (!existsSync(file)) {
|
|
100
|
+
const data = ['20', '23'][index]
|
|
101
|
+
writeFileSync(file, data);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
log.error(`Device: ${host} ${deviceName}, Prepare files error: ${error}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
deviceType = new mielhvac(api, device, info, refreshInterval);
|
|
110
|
+
break;
|
|
111
|
+
case 1://switches
|
|
112
|
+
deviceType = new switches(api, device, info, refreshInterval);
|
|
113
|
+
break;
|
|
114
|
+
case 2://lights
|
|
115
|
+
deviceType = new lights(api, device, info, refreshInterval);
|
|
116
|
+
break;
|
|
117
|
+
case 3://fans
|
|
118
|
+
deviceType = new fans(api, device, info, refreshInterval);
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
const emitLog = disableLogWarn ? false : log.warn(`Device: ${host} ${deviceName}, unknown device: ${info.deviceType}.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
deviceType.on('publishAccessory', (accessory) => {
|
|
83
126
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
84
127
|
const emitLog = disableLogSuccess ? false : log.success(`Device: ${host} ${deviceName}, Published as external accessory.`);
|
|
85
128
|
})
|
|
@@ -106,11 +149,11 @@ class tasmotaPlatform {
|
|
|
106
149
|
const impulseGenerator = new ImpulseGenerator();
|
|
107
150
|
impulseGenerator.on('start', async () => {
|
|
108
151
|
try {
|
|
109
|
-
const startDone = await
|
|
152
|
+
const startDone = await deviceType.start();
|
|
110
153
|
const stopImpulseGenerator = startDone ? await impulseGenerator.stop() : false;
|
|
111
154
|
|
|
112
155
|
//start impulse generator
|
|
113
|
-
const startImpulseGenerator = stopImpulseGenerator ? await
|
|
156
|
+
const startImpulseGenerator = stopImpulseGenerator ? await deviceType.startImpulseGenerator() : false
|
|
114
157
|
} catch (error) {
|
|
115
158
|
const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}, trying again.`);
|
|
116
159
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import EventEmitter from 'events';
|
|
3
|
+
import { ApiCommands, LightKeys } from './constants.js';
|
|
4
|
+
|
|
5
|
+
class DeviceInfo extends EventEmitter {
|
|
6
|
+
constructor(url, auth, user, passwd, deviceName, loadNameFromDevice, enableDebugMode, refreshInterval) {
|
|
7
|
+
super();
|
|
8
|
+
this.name = deviceName
|
|
9
|
+
this.loadNameFromDevice = loadNameFromDevice;
|
|
10
|
+
this.enableDebugMode = enableDebugMode;
|
|
11
|
+
|
|
12
|
+
//axios instance
|
|
13
|
+
this.axiosInstance = axios.create({
|
|
14
|
+
method: 'GET',
|
|
15
|
+
baseURL: url,
|
|
16
|
+
timeout: refreshInterval > 10000 ? 10000 : refreshInterval,
|
|
17
|
+
withCredentials: auth,
|
|
18
|
+
auth: {
|
|
19
|
+
username: user,
|
|
20
|
+
password: passwd
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getInfo() {
|
|
27
|
+
const debug = this.enableDebugMode ? this.emit('debug', `Requesting info`) : false;
|
|
28
|
+
try {
|
|
29
|
+
const deviceInfoData = await this.axiosInstance(ApiCommands.Status);
|
|
30
|
+
const deviceInfo = deviceInfoData.data ?? {};
|
|
31
|
+
const debug = this.enableDebugMode ? this.emit('debug', `Info: ${JSON.stringify(deviceInfo, null, 2)}`) : false;
|
|
32
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
33
|
+
|
|
34
|
+
//status
|
|
35
|
+
const friendlyNames = [];
|
|
36
|
+
const status = deviceInfo.Status ?? {};
|
|
37
|
+
const deviceName = this.loadNameFromDevice ? status.DeviceName ?? 'Unknown' : this.name;
|
|
38
|
+
const friendlyName = status.FriendlyName ?? [];
|
|
39
|
+
const relaysName = Array.isArray(friendlyName) ? friendlyName : [friendlyName];
|
|
40
|
+
for (const relayName of relaysName) {
|
|
41
|
+
const name = relayName ?? 'Unknown'
|
|
42
|
+
friendlyNames.push(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//status FWR
|
|
46
|
+
const statusFwr = deviceInfo.StatusFWR ?? {};
|
|
47
|
+
const firmwareRevision = statusFwr.Version ?? 'Unknown';
|
|
48
|
+
const modelName = statusFwr.Hardware ?? 'Unknown';
|
|
49
|
+
|
|
50
|
+
//status NET
|
|
51
|
+
const statusNet = deviceInfo.StatusNET ?? {};
|
|
52
|
+
const addressMac = statusNet.Mac ?? false;
|
|
53
|
+
|
|
54
|
+
//status SNS
|
|
55
|
+
const statusSns = deviceInfo.StatusSNS ?? {};
|
|
56
|
+
const statusSnsKeys = Object.keys(statusSns);
|
|
57
|
+
|
|
58
|
+
//status STS
|
|
59
|
+
const statusSts = deviceInfo.StatusSTS ?? {};
|
|
60
|
+
const statusStsKeys = Object.keys(statusSts);
|
|
61
|
+
const deviceType = statusSnsKeys.includes('MiElHVAC') ? 0 : statusStsKeys.some(key => LightKeys.includes(key)) ? 2 : statusStsKeys.includes('FanSpeed') ? 3 : 1;
|
|
62
|
+
const obj = {
|
|
63
|
+
deviceType: deviceType,
|
|
64
|
+
deviceName: deviceName,
|
|
65
|
+
friendlyNames: friendlyNames,
|
|
66
|
+
modelName: modelName,
|
|
67
|
+
serialNumber: addressMac,
|
|
68
|
+
firmwareRevision: firmwareRevision,
|
|
69
|
+
relaysCount: friendlyNames.length
|
|
70
|
+
};
|
|
71
|
+
return obj;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new Error(`Check info error: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export default DeviceInfo;
|