home-assistant-js-websocket
Advanced tools
Comparing version 5.11.3 to 5.12.0
@@ -46,2 +46,7 @@ import { ERR_CONNECTION_LOST } from "./errors.js"; | ||
suspend(): void; | ||
/** | ||
* Reconnect the websocket connection. | ||
* @param force discard old socket instead of gracefully closing it. | ||
*/ | ||
reconnect(force?: boolean): void; | ||
close(): void; | ||
@@ -48,0 +53,0 @@ /** |
@@ -10,2 +10,101 @@ /** | ||
constructor(socket, options) { | ||
this._handleMessage = (event) => { | ||
const message = JSON.parse(event.data); | ||
if (DEBUG) { | ||
console.log("Received", message); | ||
} | ||
const info = this.commands.get(message.id); | ||
switch (message.type) { | ||
case "event": | ||
if (info) { | ||
info.callback(message.event); | ||
} | ||
else { | ||
console.warn(`Received event for unknown subscription ${message.id}. Unsubscribing.`); | ||
this.sendMessagePromise(messages.unsubscribeEvents(message.id)); | ||
} | ||
break; | ||
case "result": | ||
// No info is fine. If just sendMessage is used, we did not store promise for result | ||
if (info) { | ||
if (message.success) { | ||
info.resolve(message.result); | ||
// Don't remove subscriptions. | ||
if (!("subscribe" in info)) { | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
else { | ||
info.reject(message.error); | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
break; | ||
case "pong": | ||
if (info) { | ||
info.resolve(); | ||
this.commands.delete(message.id); | ||
} | ||
else { | ||
console.warn(`Received unknown pong response ${message.id}`); | ||
} | ||
break; | ||
default: | ||
if (DEBUG) { | ||
console.warn("Unhandled message", message); | ||
} | ||
} | ||
}; | ||
this._handleClose = async () => { | ||
// Reject in-flight sendMessagePromise requests | ||
this.commands.forEach((info) => { | ||
// We don't cancel subscribeEvents commands in flight | ||
// as we will be able to recover them. | ||
if (!("subscribe" in info)) { | ||
info.reject(messages.error(ERR_CONNECTION_LOST, "Connection lost")); | ||
} | ||
}); | ||
if (this.closeRequested) { | ||
return; | ||
} | ||
this.fireEvent("disconnected"); | ||
// Disable setupRetry, we control it here with auto-backoff | ||
const options = Object.assign(Object.assign({}, this.options), { setupRetry: 0 }); | ||
const reconnect = (tries) => { | ||
setTimeout(async () => { | ||
if (DEBUG) { | ||
console.log("Trying to reconnect"); | ||
} | ||
try { | ||
const socket = await options.createSocket(options); | ||
this.setSocket(socket); | ||
} | ||
catch (err) { | ||
if (this._queuedMessages) { | ||
const queuedMessages = this._queuedMessages; | ||
this._queuedMessages = undefined; | ||
for (const msg of queuedMessages) { | ||
if (msg.reject) { | ||
msg.reject(ERR_CONNECTION_LOST); | ||
} | ||
} | ||
} | ||
if (err === ERR_INVALID_AUTH) { | ||
this.fireEvent("reconnect-error", err); | ||
} | ||
else { | ||
reconnect(tries + 1); | ||
} | ||
} | ||
}, Math.min(tries, 5) * 1000); | ||
}; | ||
if (this.suspendReconnectPromise) { | ||
await this.suspendReconnectPromise; | ||
this.suspendReconnectPromise = undefined; | ||
// For the first retry after suspend, we will queue up | ||
// all messages. | ||
this._queuedMessages = []; | ||
} | ||
reconnect(0); | ||
}; | ||
// connection options | ||
@@ -35,4 +134,4 @@ // - setupRetry: amount of ms to retry when unable to connect on initial setup | ||
this.socket = socket; | ||
socket.addEventListener("message", (ev) => this._handleMessage(ev)); | ||
socket.addEventListener("close", (ev) => this._handleClose(ev)); | ||
socket.addEventListener("message", this._handleMessage); | ||
socket.addEventListener("close", this._handleClose); | ||
if (oldSocket) { | ||
@@ -94,2 +193,16 @@ const oldCommands = this.commands; | ||
} | ||
/** | ||
* Reconnect the websocket connection. | ||
* @param force discard old socket instead of gracefully closing it. | ||
*/ | ||
reconnect(force = false) { | ||
if (!force) { | ||
this.socket.close(); | ||
return; | ||
} | ||
this.socket.removeEventListener("message", this._handleMessage); | ||
this.socket.removeEventListener("close", this._handleClose); | ||
this.socket.close(); | ||
this._handleClose(); | ||
} | ||
close() { | ||
@@ -196,101 +309,2 @@ this.closeRequested = true; | ||
} | ||
_handleMessage(event) { | ||
const message = JSON.parse(event.data); | ||
if (DEBUG) { | ||
console.log("Received", message); | ||
} | ||
const info = this.commands.get(message.id); | ||
switch (message.type) { | ||
case "event": | ||
if (info) { | ||
info.callback(message.event); | ||
} | ||
else { | ||
console.warn(`Received event for unknown subscription ${message.id}. Unsubscribing.`); | ||
this.sendMessagePromise(messages.unsubscribeEvents(message.id)); | ||
} | ||
break; | ||
case "result": | ||
// No info is fine. If just sendMessage is used, we did not store promise for result | ||
if (info) { | ||
if (message.success) { | ||
info.resolve(message.result); | ||
// Don't remove subscriptions. | ||
if (!("subscribe" in info)) { | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
else { | ||
info.reject(message.error); | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
break; | ||
case "pong": | ||
if (info) { | ||
info.resolve(); | ||
this.commands.delete(message.id); | ||
} | ||
else { | ||
console.warn(`Received unknown pong response ${message.id}`); | ||
} | ||
break; | ||
default: | ||
if (DEBUG) { | ||
console.warn("Unhandled message", message); | ||
} | ||
} | ||
} | ||
async _handleClose(ev) { | ||
// Reject in-flight sendMessagePromise requests | ||
this.commands.forEach((info) => { | ||
// We don't cancel subscribeEvents commands in flight | ||
// as we will be able to recover them. | ||
if (!("subscribe" in info)) { | ||
info.reject(messages.error(ERR_CONNECTION_LOST, "Connection lost")); | ||
} | ||
}); | ||
if (this.closeRequested) { | ||
return; | ||
} | ||
this.fireEvent("disconnected"); | ||
// Disable setupRetry, we control it here with auto-backoff | ||
const options = Object.assign(Object.assign({}, this.options), { setupRetry: 0 }); | ||
const reconnect = (tries) => { | ||
setTimeout(async () => { | ||
if (DEBUG) { | ||
console.log("Trying to reconnect"); | ||
} | ||
try { | ||
const socket = await options.createSocket(options); | ||
this.setSocket(socket); | ||
} | ||
catch (err) { | ||
if (this._queuedMessages) { | ||
const queuedMessages = this._queuedMessages; | ||
this._queuedMessages = undefined; | ||
for (const msg of queuedMessages) { | ||
if (msg.reject) { | ||
msg.reject(ERR_CONNECTION_LOST); | ||
} | ||
} | ||
} | ||
if (err === ERR_INVALID_AUTH) { | ||
this.fireEvent("reconnect-error", err); | ||
} | ||
else { | ||
reconnect(tries + 1); | ||
} | ||
} | ||
}, Math.min(tries, 5) * 1000); | ||
}; | ||
if (this.suspendReconnectPromise) { | ||
await this.suspendReconnectPromise; | ||
this.suspendReconnectPromise = undefined; | ||
// For the first retry after suspend, we will queue up | ||
// all messages. | ||
this._queuedMessages = []; | ||
} | ||
reconnect(0); | ||
} | ||
_genCmdId() { | ||
@@ -297,0 +311,0 @@ return ++this.commandId; |
@@ -170,2 +170,91 @@ (function (global, factory) { | ||
constructor(socket, options) { | ||
this._handleMessage = (event) => { | ||
const message = JSON.parse(event.data); | ||
const info = this.commands.get(message.id); | ||
switch (message.type) { | ||
case "event": | ||
if (info) { | ||
info.callback(message.event); | ||
} | ||
else { | ||
console.warn(`Received event for unknown subscription ${message.id}. Unsubscribing.`); | ||
this.sendMessagePromise(unsubscribeEvents(message.id)); | ||
} | ||
break; | ||
case "result": | ||
// No info is fine. If just sendMessage is used, we did not store promise for result | ||
if (info) { | ||
if (message.success) { | ||
info.resolve(message.result); | ||
// Don't remove subscriptions. | ||
if (!("subscribe" in info)) { | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
else { | ||
info.reject(message.error); | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
break; | ||
case "pong": | ||
if (info) { | ||
info.resolve(); | ||
this.commands.delete(message.id); | ||
} | ||
else { | ||
console.warn(`Received unknown pong response ${message.id}`); | ||
} | ||
break; | ||
} | ||
}; | ||
this._handleClose = async () => { | ||
// Reject in-flight sendMessagePromise requests | ||
this.commands.forEach((info) => { | ||
// We don't cancel subscribeEvents commands in flight | ||
// as we will be able to recover them. | ||
if (!("subscribe" in info)) { | ||
info.reject(error(ERR_CONNECTION_LOST, "Connection lost")); | ||
} | ||
}); | ||
if (this.closeRequested) { | ||
return; | ||
} | ||
this.fireEvent("disconnected"); | ||
// Disable setupRetry, we control it here with auto-backoff | ||
const options = Object.assign(Object.assign({}, this.options), { setupRetry: 0 }); | ||
const reconnect = (tries) => { | ||
setTimeout(async () => { | ||
try { | ||
const socket = await options.createSocket(options); | ||
this.setSocket(socket); | ||
} | ||
catch (err) { | ||
if (this._queuedMessages) { | ||
const queuedMessages = this._queuedMessages; | ||
this._queuedMessages = undefined; | ||
for (const msg of queuedMessages) { | ||
if (msg.reject) { | ||
msg.reject(ERR_CONNECTION_LOST); | ||
} | ||
} | ||
} | ||
if (err === ERR_INVALID_AUTH) { | ||
this.fireEvent("reconnect-error", err); | ||
} | ||
else { | ||
reconnect(tries + 1); | ||
} | ||
} | ||
}, Math.min(tries, 5) * 1000); | ||
}; | ||
if (this.suspendReconnectPromise) { | ||
await this.suspendReconnectPromise; | ||
this.suspendReconnectPromise = undefined; | ||
// For the first retry after suspend, we will queue up | ||
// all messages. | ||
this._queuedMessages = []; | ||
} | ||
reconnect(0); | ||
}; | ||
// connection options | ||
@@ -195,4 +284,4 @@ // - setupRetry: amount of ms to retry when unable to connect on initial setup | ||
this.socket = socket; | ||
socket.addEventListener("message", (ev) => this._handleMessage(ev)); | ||
socket.addEventListener("close", (ev) => this._handleClose(ev)); | ||
socket.addEventListener("message", this._handleMessage); | ||
socket.addEventListener("close", this._handleClose); | ||
if (oldSocket) { | ||
@@ -254,2 +343,16 @@ const oldCommands = this.commands; | ||
} | ||
/** | ||
* Reconnect the websocket connection. | ||
* @param force discard old socket instead of gracefully closing it. | ||
*/ | ||
reconnect(force = false) { | ||
if (!force) { | ||
this.socket.close(); | ||
return; | ||
} | ||
this.socket.removeEventListener("message", this._handleMessage); | ||
this.socket.removeEventListener("close", this._handleClose); | ||
this.socket.close(); | ||
this._handleClose(); | ||
} | ||
close() { | ||
@@ -353,91 +456,2 @@ this.closeRequested = true; | ||
} | ||
_handleMessage(event) { | ||
const message = JSON.parse(event.data); | ||
const info = this.commands.get(message.id); | ||
switch (message.type) { | ||
case "event": | ||
if (info) { | ||
info.callback(message.event); | ||
} | ||
else { | ||
console.warn(`Received event for unknown subscription ${message.id}. Unsubscribing.`); | ||
this.sendMessagePromise(unsubscribeEvents(message.id)); | ||
} | ||
break; | ||
case "result": | ||
// No info is fine. If just sendMessage is used, we did not store promise for result | ||
if (info) { | ||
if (message.success) { | ||
info.resolve(message.result); | ||
// Don't remove subscriptions. | ||
if (!("subscribe" in info)) { | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
else { | ||
info.reject(message.error); | ||
this.commands.delete(message.id); | ||
} | ||
} | ||
break; | ||
case "pong": | ||
if (info) { | ||
info.resolve(); | ||
this.commands.delete(message.id); | ||
} | ||
else { | ||
console.warn(`Received unknown pong response ${message.id}`); | ||
} | ||
break; | ||
} | ||
} | ||
async _handleClose(ev) { | ||
// Reject in-flight sendMessagePromise requests | ||
this.commands.forEach((info) => { | ||
// We don't cancel subscribeEvents commands in flight | ||
// as we will be able to recover them. | ||
if (!("subscribe" in info)) { | ||
info.reject(error(ERR_CONNECTION_LOST, "Connection lost")); | ||
} | ||
}); | ||
if (this.closeRequested) { | ||
return; | ||
} | ||
this.fireEvent("disconnected"); | ||
// Disable setupRetry, we control it here with auto-backoff | ||
const options = Object.assign(Object.assign({}, this.options), { setupRetry: 0 }); | ||
const reconnect = (tries) => { | ||
setTimeout(async () => { | ||
try { | ||
const socket = await options.createSocket(options); | ||
this.setSocket(socket); | ||
} | ||
catch (err) { | ||
if (this._queuedMessages) { | ||
const queuedMessages = this._queuedMessages; | ||
this._queuedMessages = undefined; | ||
for (const msg of queuedMessages) { | ||
if (msg.reject) { | ||
msg.reject(ERR_CONNECTION_LOST); | ||
} | ||
} | ||
} | ||
if (err === ERR_INVALID_AUTH) { | ||
this.fireEvent("reconnect-error", err); | ||
} | ||
else { | ||
reconnect(tries + 1); | ||
} | ||
} | ||
}, Math.min(tries, 5) * 1000); | ||
}; | ||
if (this.suspendReconnectPromise) { | ||
await this.suspendReconnectPromise; | ||
this.suspendReconnectPromise = undefined; | ||
// For the first retry after suspend, we will queue up | ||
// all messages. | ||
this._queuedMessages = []; | ||
} | ||
reconnect(0); | ||
} | ||
_genCmdId() { | ||
@@ -444,0 +458,0 @@ return ++this.commandId; |
@@ -5,3 +5,3 @@ { | ||
"sideEffects": false, | ||
"version": "5.11.3", | ||
"version": "5.12.0", | ||
"description": "Home Assistant websocket client", | ||
@@ -8,0 +8,0 @@ "source": "lib/index.ts", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
153446
3150