Skip to content

Commit f52949b

Browse files
committed
Implemented re-subscription if ws connection closes.
1 parent 31fb9eb commit f52949b

File tree

2 files changed

+55
-30
lines changed

2 files changed

+55
-30
lines changed

src/platformAccessory.ts

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import {
55
JSONPatchWsIncomingMessage,
66
JSONPatchWsOutgoingMessage,
77
PrincessHeaterAccessoryContext,
8-
PrincessHeaterStateWsIncomingMessage,
8+
PrincessHeaterStateWsIncomingMessage, ResponseWsIncomingMessage,
99
SubscribeWsOutgoingMessage,
10-
WsIncomingMessage,
10+
WsIncomingMessage, WsOutgoingMessage,
1111
} from './ws/types';
1212
import {WsAPIClient} from './ws';
1313
import {MessageType} from './ws/const';
@@ -53,12 +53,53 @@ export class HomewizardPrincessHeaterAccessory {
5353

5454
this.platform.log.debug('Subscribing to device updates:', this.accessory.context.device.name);
5555

56-
wsClient.send<SubscribeWsOutgoingMessage>({
56+
this.subscribe().catch(
57+
err => this.platform.log.error('Failed to subscribe to device ->', this.accessory.context.device.name, err),
58+
);
59+
}
60+
61+
subscribe(): Promise<ResponseWsIncomingMessage> {
62+
return this.wsClient.send<SubscribeWsOutgoingMessage>({
5763
type: MessageType.SubscribeDevice,
5864
device: this.accessory.context.device.identifier,
5965
});
6066
}
6167

68+
async jsonPatch(path: string, value: boolean | number, callback: CharacteristicSetCallback): Promise<ResponseWsIncomingMessage> {
69+
70+
const message = {
71+
type: MessageType.JSONPatch,
72+
device: this.accessory.context.device.identifier,
73+
patch: [{
74+
op: 'replace',
75+
path: path,
76+
value,
77+
}],
78+
};
79+
80+
const trySend = () => this.wsClient.send(message).then(m => {
81+
callback(null);
82+
return m;
83+
});
84+
85+
return trySend()
86+
.catch(err => {
87+
if (err.code === 400) {
88+
this.platform.log.warn('Error code 400. Might mean we need to re-subscribe ->', message, err);
89+
return this.subscribe().then(() => trySend());
90+
} else {
91+
this.platform.log.error('Failed to send jsonPatch message ->', message, err);
92+
callback(err);
93+
throw err;
94+
}
95+
})
96+
.catch(err => {
97+
this.platform.log.error('Failed to send jsonPatch message ->', message, err);
98+
callback(err);
99+
throw err;
100+
});
101+
}
102+
62103
onWsMessage(message: WsIncomingMessage) {
63104
if ('state' in message) {
64105
this.onStateMessage(message as PrincessHeaterStateWsIncomingMessage);
@@ -146,33 +187,17 @@ export class HomewizardPrincessHeaterAccessory {
146187

147188
this.platform.log.debug('Set Characteristic TargetHeatingCoolingState ->', value);
148189

149-
this.wsClient.send<JSONPatchWsOutgoingMessage>({
150-
type: MessageType.JSONPatch,
151-
device: this.accessory.context.device.identifier,
152-
patch: [{
153-
op: 'replace',
154-
path: '/state/power_on',
155-
value: value === this.platform.Characteristic.TargetHeatingCoolingState.HEAT,
156-
}],
157-
})
158-
.then(() => callback(null))
159-
.catch((err) => callback(err));
190+
this.jsonPatch(
191+
'/state/power_on',
192+
value === this.platform.Characteristic.TargetHeatingCoolingState.HEAT,
193+
callback,
194+
);
160195
}
161196

162197
setTargetTemperature(value: CharacteristicValue, callback: CharacteristicSetCallback) {
163198

164199
this.platform.log.debug('Set Characteristic TargetTemperature ->', value);
165200

166-
this.wsClient.send<JSONPatchWsOutgoingMessage>({
167-
type: MessageType.JSONPatch,
168-
device: this.accessory.context.device.identifier,
169-
patch: [{
170-
op: 'replace',
171-
path: '/state/target_temperature',
172-
value: value as number,
173-
}],
174-
})
175-
.then(() => callback(null))
176-
.catch((err) => callback(err));
201+
this.jsonPatch('/state/target_temperature', value as number, callback);
177202
}
178203
}

src/ws/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import WebSocket from 'ws';
2-
import {HelloWsOutgoingMessage, WsIncomingMessage, WsOutgoingMessage} from './types';
2+
import {HelloWsOutgoingMessage, ResponseWsIncomingMessage, WsIncomingMessage, WsOutgoingMessage} from './types';
33
import {MessageType, WS_URL} from './const';
44
import {EventEmitter} from 'events';
55
import {Logger} from 'homebridge';
@@ -21,7 +21,7 @@ export class WsAPIClient extends EventEmitter {
2121

2222
public async send<M extends WsOutgoingMessage>(
2323
message: Omit<M, 'message_id'>,
24-
): Promise<M> {
24+
): Promise<ResponseWsIncomingMessage> {
2525

2626
const ws = await this._getWebSocket();
2727
return this._send<M>(message, ws);
@@ -99,7 +99,7 @@ export class WsAPIClient extends EventEmitter {
9999
private async _send<M extends WsOutgoingMessage>(
100100
message: Omit<M, 'message_id'>,
101101
ws: WebSocket,
102-
): Promise<M> {
102+
): Promise<ResponseWsIncomingMessage> {
103103

104104
const sentMessage = await new Promise((res, rej) => {
105105
const messageId = ++this.lastMessageId;
@@ -127,7 +127,7 @@ export class WsAPIClient extends EventEmitter {
127127

128128
private _waitForResponse<M extends WsOutgoingMessage>(
129129
outgoingMessage: M,
130-
): Promise<M> {
130+
): Promise<ResponseWsIncomingMessage> {
131131
return new Promise((res, rej) => {
132132

133133
this.log.debug('Waiting for message response -> ', outgoingMessage);
@@ -145,7 +145,7 @@ export class WsAPIClient extends EventEmitter {
145145
this.off('message', onMessage);
146146
clearTimeout(timeout);
147147
if (incomingMessage.status === 200) {
148-
res(outgoingMessage);
148+
res(incomingMessage as ResponseWsIncomingMessage);
149149
} else {
150150
rej(incomingMessage);
151151
}

0 commit comments

Comments
 (0)