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 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 Fan, solved [#24](https://github.com/grzegorz914/homebridge-tasmota-control/issues/24)
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 TasmotaDevice from './src/tasmotadevice.js';
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 config = {
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(config, null, 2)}.`);
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
- const files = [
63
- defaultHeatingSetTemperatureFile,
64
- defaultCoolingSetTemperatureFile
65
- ];
66
-
67
- files.forEach((file, index) => {
68
- if (!existsSync(file)) {
69
- const data = ['20', '23'][index]
70
- writeFileSync(file, data);
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
- //tasmota device
79
- try {
80
- const miElHvac = device.miElHvac ?? {};
81
- const tasmotaDevice = new TasmotaDevice(api, device, miElHvac, defaultHeatingSetTemperatureFile, defaultCoolingSetTemperatureFile);
82
- tasmotaDevice.on('publishAccessory', (accessory) => {
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 tasmotaDevice.start();
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 tasmotaDevice.startImpulseGenerator() : false
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Tasmota Control",
3
3
  "name": "homebridge-tasmota-control",
4
- "version": "1.4.0-beta.8",
4
+ "version": "1.4.0",
5
5
  "description": "Homebridge plugin to control Tasmota flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -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;