homebridge-tasmota-control 1.4.0-beta.9 → 1.4.1-beta.1

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
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ## Changes
11
11
 
12
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,11 @@
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';
8
+ import Sensors from './src/sensors.js';
4
9
  import ImpulseGenerator from './src/impulsegenerator.js';
5
10
  import { PluginName, PlatformName } from './src/constants.js';
6
11
 
@@ -39,6 +44,12 @@ class tasmotaPlatform {
39
44
  }
40
45
 
41
46
  //log config
47
+ const url = `http://${host}/cm?cmnd=`;
48
+ const auth = device.auth || false;
49
+ const user = device.user || '';
50
+ const passwd = device.passwd || '';
51
+ const loadNameFromDevice = device.loadNameFromDevice || false;
52
+ const refreshInterval = device.refreshInterval * 1000 || 5000;
42
53
  const enableDebugMode = device.enableDebugMode || false;
43
54
  const disableLogDeviceInfo = device.disableLogDeviceInfo || false;
44
55
  const disableLogInfo = device.disableLogInfo || false;
@@ -46,55 +57,19 @@ class tasmotaPlatform {
46
57
  const disableLogWarn = device.disableLogWarn || false;
47
58
  const disableLogError = device.disableLogError || false;
48
59
  const debug = enableDebugMode ? log.info(`Device: ${host} ${deviceName}, debug: Did finish launching.`) : false;
49
- const config = {
60
+ const newConfig = {
50
61
  ...device,
51
62
  user: 'removed',
52
63
  passwd: 'removed'
53
64
  };
54
- const debug1 = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, Config: ${JSON.stringify(config, null, 2)}.`);
65
+ const debug1 = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, Config: ${JSON.stringify(newConfig, null, 2)}.`);
55
66
 
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}`;
60
-
61
- 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
- }
77
-
78
- //tasmota device
79
67
  try {
80
- const miElHvac = device.miElHvac ?? {};
81
- const tasmotaDevice = new TasmotaDevice(api, device, miElHvac, defaultHeatingSetTemperatureFile, defaultCoolingSetTemperatureFile);
82
- tasmotaDevice.on('publishAccessory', (accessory) => {
83
- api.publishExternalAccessories(PluginName, [accessory]);
84
- const emitLog = disableLogSuccess ? false : log.success(`Device: ${host} ${deviceName}, Published as external accessory.`);
68
+ //get device info
69
+ const deviceInfo = new DeviceInfo(url, auth, user, passwd, deviceName, loadNameFromDevice, enableDebugMode, refreshInterval);
70
+ deviceInfo.on('debug', (debug) => {
71
+ const emitLog = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, debug: ${debug}.`);
85
72
  })
86
- .on('devInfo', (devInfo) => {
87
- const emitLog = disableLogDeviceInfo ? false : log.info(devInfo);
88
- })
89
- .on('success', (success) => {
90
- const emitLog = disableLogSuccess ? false : log.success(`Device: ${host} ${deviceName}, ${success}.`);
91
- })
92
- .on('info', (info) => {
93
- const emitLog = disableLogInfo ? false : log.info(`Device: ${host} ${deviceName}, ${info}.`);
94
- })
95
- .on('debug', (debug) => {
96
- const emitLog = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, debug: ${debug}.`);
97
- })
98
73
  .on('warn', (warn) => {
99
74
  const emitLog = disableLogWarn ? false : log.warn(`Device: ${host} ${deviceName}, ${warn}.`);
100
75
  })
@@ -102,24 +77,98 @@ class tasmotaPlatform {
102
77
  const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}.`);
103
78
  });
104
79
 
105
- //create impulse generator
106
- const impulseGenerator = new ImpulseGenerator();
107
- impulseGenerator.on('start', async () => {
108
- try {
109
- const startDone = await tasmotaDevice.start();
110
- const stopImpulseGenerator = startDone ? await impulseGenerator.stop() : false;
111
-
112
- //start impulse generator
113
- const startImpulseGenerator = stopImpulseGenerator ? await tasmotaDevice.startImpulseGenerator() : false
114
- } catch (error) {
115
- const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}, trying again.`);
80
+ const info = await deviceInfo.getInfo();
81
+ if (!info.serialNumber) {
82
+ log.warn(`Device: ${host} ${deviceName}, serial not found.`);
83
+ return;
84
+ }
85
+
86
+ for (const type of info.deviceTypes) {
87
+ let deviceType;
88
+ switch (type) {
89
+ case 0://mielhvac
90
+ //check files exists, if not then create it
91
+ try {
92
+ const postFix = device.host.split('.').join('');
93
+ info.defaultHeatingSetTemperatureFile = `${prefDir}/defaultHeatingSetTemperature_${postFix}`;
94
+ info.defaultCoolingSetTemperatureFile = `${prefDir}/defaultCoolingSetTemperature_${postFix}`;
95
+ const files = [
96
+ info.defaultHeatingSetTemperatureFile,
97
+ info.defaultCoolingSetTemperatureFile
98
+ ];
99
+
100
+ files.forEach((file, index) => {
101
+ if (!existsSync(file)) {
102
+ const data = ['20', '23'][index]
103
+ writeFileSync(file, data);
104
+ }
105
+ });
106
+ } catch (error) {
107
+ log.error(`Device: ${host} ${deviceName}, Prepare files error: ${error}`);
108
+ return;
109
+ }
110
+
111
+ deviceType = new MiElHvac(api, device, info, refreshInterval);
112
+ break;
113
+ case 1: //switches
114
+ deviceType = new Switches(api, device, info, refreshInterval);
115
+ break;
116
+ case 2: //lights
117
+ deviceType = new Lights(api, device, info, refreshInterval);
118
+ break;
119
+ case 3: //fans
120
+ deviceType = new Fans(api, device, info, refreshInterval);
121
+ break;
122
+ case 4: //sensors
123
+ deviceType = new Sensors(api, device, info, refreshInterval);
124
+ break;
125
+ default:
126
+ const emitLog = disableLogWarn ? false : log.warn(`Device: ${host} ${deviceName}, unknown device: ${info.deviceType}.`);
127
+ return;
116
128
  }
117
- }).on('state', (state) => {
118
- const emitLog = !enableDebugMode ? false : state ? log.info(`Device: ${host} ${deviceName}, Start impulse generator started.`) : log.info(`Device: ${host} ${deviceName}, Start impulse generator stopped.`);
119
- });
120
129
 
121
- //start impulse generator
122
- await impulseGenerator.start([{ name: 'start', sampling: 45000 }]);
130
+ deviceType.on('publishAccessory', (accessory) => {
131
+ api.publishExternalAccessories(PluginName, [accessory]);
132
+ const emitLog = disableLogSuccess ? false : log.success(`Device: ${host} ${deviceName}, Published as external accessory.`);
133
+ })
134
+ .on('devInfo', (devInfo) => {
135
+ const emitLog = disableLogDeviceInfo ? false : log.info(devInfo);
136
+ })
137
+ .on('success', (success) => {
138
+ const emitLog = disableLogSuccess ? false : log.success(`Device: ${host} ${deviceName}, ${success}.`);
139
+ })
140
+ .on('info', (info) => {
141
+ const emitLog = disableLogInfo ? false : log.info(`Device: ${host} ${deviceName}, ${info}.`);
142
+ })
143
+ .on('debug', (debug) => {
144
+ const emitLog = !enableDebugMode ? false : log.info(`Device: ${host} ${deviceName}, debug: ${debug}.`);
145
+ })
146
+ .on('warn', (warn) => {
147
+ const emitLog = disableLogWarn ? false : log.warn(`Device: ${host} ${deviceName}, ${warn}.`);
148
+ })
149
+ .on('error', (error) => {
150
+ const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}.`);
151
+ });
152
+
153
+ //create impulse generator
154
+ const impulseGenerator = new ImpulseGenerator();
155
+ impulseGenerator.on('start', async () => {
156
+ try {
157
+ const startDone = await deviceType.start();
158
+ const stopImpulseGenerator = startDone ? await impulseGenerator.stop() : false;
159
+
160
+ //start impulse generator
161
+ const startImpulseGenerator = stopImpulseGenerator ? await deviceType.startImpulseGenerator() : false
162
+ } catch (error) {
163
+ const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, ${error}, trying again.`);
164
+ }
165
+ }).on('state', (state) => {
166
+ const emitLog = !enableDebugMode ? false : state ? log.info(`Device: ${host} ${deviceName}, Start impulse generator started.`) : log.info(`Device: ${host} ${deviceName}, Start impulse generator stopped.`);
167
+ });
168
+
169
+ //start impulse generator
170
+ await impulseGenerator.start([{ name: 'start', sampling: 45000 }]);
171
+ }
123
172
  } catch (error) {
124
173
  const emitLog = disableLogError ? false : log.error(`Device: ${host} ${deviceName}, Did finish launching error: ${error}.`);
125
174
  }
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.9",
4
+ "version": "1.4.1-beta.1",
5
5
  "description": "Homebridge plugin to control Tasmota flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/constants.js CHANGED
@@ -229,8 +229,7 @@ export const SensorKeys = [
229
229
  "TC74",
230
230
  "VEML7700",
231
231
  "PIR",
232
- "ENERGY",
233
- "MiElHVAC"
232
+ "ENERGY"
234
233
  ];
235
234
 
236
235
  export const SensorPropertiesKeys = [
@@ -0,0 +1,84 @@
1
+ import axios from 'axios';
2
+ import EventEmitter from 'events';
3
+ import { ApiCommands, LightKeys, SensorKeys } 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
+
62
+ //device types
63
+ const types = [];
64
+ const mielhvac = statusSnsKeys.includes('MiElHVAC') ? types.push(0) : false;
65
+ const lights = statusStsKeys.some(key => LightKeys.includes(key)) ? types.push(2) : false;
66
+ const fans = statusStsKeys.includes('FanSpeed') ? types.push(3) : false;
67
+ const sensors = statusSnsKeys.some(key => SensorKeys.includes(key)) ? types.push(4) : false;
68
+ const switches = !mielhvac && !lights && !fans && !sensors ? types.push(1) : false
69
+ const obj = {
70
+ deviceTypes: types,
71
+ deviceName: deviceName,
72
+ friendlyNames: friendlyNames,
73
+ modelName: modelName,
74
+ serialNumber: addressMac,
75
+ firmwareRevision: firmwareRevision,
76
+ relaysCount: friendlyNames.length
77
+ };
78
+ return obj;
79
+ } catch (error) {
80
+ throw new Error(`Check info error: ${error}`);
81
+ }
82
+ }
83
+ }
84
+ export default DeviceInfo;