homebridge-melcloud-control 4.3.0-beta.1 → 4.3.0-beta.11

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.3.0-beta.1",
4
+ "version": "4.3.0-beta.11",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -26,7 +26,9 @@ class MelCloudAta extends EventEmitter {
26
26
  //set default values
27
27
  this.deviceData = {};
28
28
  this.headers = {};
29
+ this.hash = null;
29
30
  this.start = true;
31
+ this.socket = null;
30
32
 
31
33
  //lock flag
32
34
  this.locks = false;
@@ -52,6 +54,15 @@ class MelCloudAta extends EventEmitter {
52
54
  }
53
55
  }
54
56
 
57
+ cleanupSocket = () => {
58
+ if (this.heartbeat) {
59
+ clearInterval(this.heartbeat);
60
+ this.heartbeat = null;
61
+ }
62
+ this.socket = null;
63
+ this.socketConnected = false;
64
+ };
65
+
55
66
  async checkState() {
56
67
  try {
57
68
 
@@ -60,6 +71,7 @@ class MelCloudAta extends EventEmitter {
60
71
  if (!devicesData) return;
61
72
 
62
73
  this.headers = devicesData.Headers;
74
+ this.hash = devicesData.Hash;
63
75
  const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
64
76
  if (this.accountType === 'melcloudhome') {
65
77
  deviceData.Scenes = devicesData.Scenes ?? [];
@@ -73,34 +85,65 @@ class MelCloudAta extends EventEmitter {
73
85
  const temps = await this.functions.readData(this.defaultTempsFile, true);
74
86
  deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
75
87
  deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
76
- }
77
- if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
78
88
 
79
- if (this.start) {
80
- const hash = '2db32d6f-c19c-4b1f-a0dd-1915420a5152';
81
-
82
- const ws = new WebSocket(`wss://ws.melcloudhome.com/?hash=${hash}`, {
83
- headers: {
84
- 'Origin': 'https://melcloudhome.com',
85
- 'Pragma': 'no-cache',
86
- 'Cache-Control': 'no-cache'
87
- }
88
- });
89
- ws.on('open', () => {
90
- if (!this.logDebug) this.emit('warn', `Connected to MelCloudHome WebSocket`);
91
- this.start = false;
92
- })
93
- .on('message', (data) => {
94
- if (!this.logDebug) this.emit('warn', `Incoming message:', data.toString()`);
89
+ if (this.start) {
90
+ const socket = new WebSocket(`wss://ws.melcloudhome.com/?hash=${devicesData.Hash}`, {
91
+ headers: {
92
+ 'Origin': 'https://melcloudhome.com',
93
+ 'Pragma': 'no-cache',
94
+ 'Cache-Control': 'no-cache'
95
+ }
95
96
  })
96
- .on('close', () => {
97
- if (!this.logDebug) this.emit('warn', `Connection closed`);
98
- })
99
- .on('error', (error) => {
100
- if (!this.logDebug) this.emit('warn', `Connected error: ${error}`);
101
- });
102
- }
97
+ .on('error', (error) => {
98
+ if (this.logDebug) this.emit('debug', `Socket error: ${error}`);
99
+ socket.close();
100
+ })
101
+ .on('close', () => {
102
+ if (this.logDebug) this.emit('debug', `Socket closed`);
103
+ this.cleanupSocket();
104
+ })
105
+ .on('open', () => {
106
+ if (this.logDebug) this.emit('debug', `Plugin received heartbeat from TV`);
107
+
108
+ // connect to device success
109
+ this.socket = socket;
110
+ this.socketConnected = true;
111
+ this.emit('success', `Socket Connect Success`);
112
+ this.start = false;
113
+
114
+ // start heartbeat
115
+ this.heartbeat = setInterval(() => {
116
+ if (socket.readyState === socket.OPEN) {
117
+ if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
118
+ socket.ping();
119
+ }
120
+ }, 5000);
121
+ })
122
+ .on('pong', () => {
123
+ if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
124
+ })
125
+ .on('message', (message) => {
126
+ const parsedMessage = JSON.parse(message);
127
+ const messageType = parsedMessage.messageType;
128
+ const messageData = parsedMessage.Data;
129
+ const stringifyMessage = JSON.stringify(messageData, null, 2);
130
+ if (!this.logDebug) this.emit('warn', `Incoming message:', ${stringifyMessage}`);
103
131
 
132
+ switch (messageType) {
133
+ case 'unitStateChanged':
134
+ const unitId = messageData.id;
135
+ const unitType = messageData.unitType;
136
+ const settings = messageData.settings[0];
137
+ const name = settings.name;
138
+ const value = settings.value;
139
+ if (this.deviceId === unitId) deviceData.Device[name] = value;
140
+ break;
141
+ }
142
+ });
143
+ }
144
+
145
+ }
146
+ if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
104
147
 
105
148
  //device
106
149
  const serialNumber = deviceData.SerialNumber || '4.0.0';
@@ -23,6 +23,7 @@ class MelCloudHome extends EventEmitter {
23
23
  this.buildingsFile = buildingsFile;
24
24
  this.devicesFile = devicesFile;
25
25
  this.headers = {};
26
+ this.hash = null;
26
27
 
27
28
  this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
28
29
  .on('warn', warn => this.emit('warn', warn))
@@ -208,6 +209,7 @@ class MelCloudHome extends EventEmitter {
208
209
  devicesList.Devices = devices;
209
210
  devicesList.Scenes = scenes;
210
211
  devicesList.Headers = this.headers;
212
+ devicesList.Hash = this.hash;
211
213
 
212
214
  await this.functions.saveData(this.devicesFile, devicesList);
213
215
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
@@ -276,6 +278,21 @@ class MelCloudHome extends EventEmitter {
276
278
  page.setDefaultTimeout(GLOBAL_TIMEOUT);
277
279
  page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
278
280
 
281
+ // === CDP session (modern API) ===
282
+ const client = await page.createCDPSession();
283
+ await client.send('Network.enable')
284
+ client.on('Network.webSocketCreated', ({ url }) => {
285
+ try {
286
+ if (url.startsWith('wss://ws.melcloudhome.com/?hash=')) {
287
+ const params = new URL(url).searchParams;
288
+ this.hash = params.get('hash');
289
+ if (this.logDebug) this.emit('debug', `MelCloudHome WS hash detected: ${melcloudHash}`);
290
+ }
291
+ } catch (err) {
292
+ this.emit('error', `CDP WebSocketCreated handler error: ${err.message}`);
293
+ }
294
+ });
295
+
279
296
  try {
280
297
  await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
281
298
  } catch (error) {