Skip to content

Commit

Permalink
Add suspend until functionality (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
bramkragten authored Jun 12, 2020
1 parent 81cad99 commit 1dbf1b1
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 17 deletions.
78 changes: 62 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
getAuth,
createConnection,
subscribeEntities,
ERR_HASS_HOST_REQUIRED
ERR_HASS_HOST_REQUIRED,
} from "home-assistant-js-websocket";

async function connect() {
Expand All @@ -47,7 +47,7 @@ async function connect() {
}
}
const connection = await createConnection({ auth });
subscribeEntities(connection, ent => console.log(ent));
subscribeEntities(connection, (ent) => console.log(ent));
}

connect();
Expand Down Expand Up @@ -78,7 +78,7 @@ In certain instances `getAuth` will raise an error. These errors can be imported
// When bundling your application
import {
ERR_HASS_HOST_REQUIRED,
ERR_INVALID_AUTH
ERR_INVALID_AUTH,
} from "home-assistant-js-websocket";

// When using the UMD build
Expand Down Expand Up @@ -118,14 +118,60 @@ You can import them into your code as follows:
```javascript
import {
ERR_CANNOT_CONNECT,
ERR_INVALID_AUTH
ERR_INVALID_AUTH,
} from "home-assistant-js-websocket";
```

### Automatic reconnecting

The connection object will automatically try to reconnect to the server when the connection gets lost. On reconnect, it will automatically resubscribe the event listeners.

#### Suspend reconnection

If you don't want to automatically try to reconnect to the server when the connection is lost, you can pass a promise to wait for. The connection will try to reconnect after the promise is resolved.

```javascript
connection.suspendReconnectUntil(
new Promise((resolve) => {
// When you want to try to reconnect again, resolve the promise.
resolve();
})
);
```

#### Suspend connection

You can also actively close the connection and wait for a promise to resolve to reconnect again. This promise can be passed either with `suspendReconnectUntil` or with the `suspend` command itself.

If you don't provide a promise with either of these functions, an error will be thrown.

```javascript
connection.suspendReconnectUntil(
new Promise((resolve) => {
// When you want to try to reconnect again, resolve the promise.
resolve();
})
);
connection.suspend();
```

or

```javascript
connection.suspend(
new Promise((resolve) => {
// When you want to try to reconnect again, resolve the promise.
resolve();
})
);
```

#### Close connection

You can also close the connection, without any reconnect, with `connection.close()`.

#### Events

The `Connection` object implements three events related to the reconnecting logic.

| Event | Data | Description |
Expand Down Expand Up @@ -155,7 +201,7 @@ The function `subscribeEntities` will return an unsubscribe function.
import { subscribeEntities } from "home-assistant-js-websocket";

// conn is the connection from earlier.
subscribeEntities(conn, entities => console.log("New entities!", entities));
subscribeEntities(conn, (entities) => console.log("New entities!", entities));
```

You can also import the collection:
Expand All @@ -167,7 +213,7 @@ import { entitiesColl } from "home-assistant-js-websocket";
const coll = entitiesColl(connection);
console.log(coll.state);
await coll.refresh();
coll.subscribe(entities => console.log(entities));
coll.subscribe((entities) => console.log(entities));
```

### Config
Expand All @@ -180,7 +226,7 @@ The function `subscribeConfig` will return an unsubscribe function.
import { subscribeConfig } from "home-assistant-js-websocket";

// conn is the connection from earlier.
subscribeConfig(conn, config => console.log("New config!", config));
subscribeConfig(conn, (config) => console.log("New config!", config));
```

You can also import the collection:
Expand All @@ -192,7 +238,7 @@ import { configColl } from "home-assistant-js-websocket";
const coll = configColl(connection);
console.log(coll.state);
await coll.refresh();
coll.subscribe(config => console.log(config));
coll.subscribe((config) => console.log(config));
```

### Services
Expand All @@ -205,7 +251,7 @@ The function `subscribeServices` will return an unsubscribe function.
import { subscribeServices } from "home-assistant-js-websocket";

// conn is the connection from earlier.
subscribeServices(conn, services => console.log("New services!", services));
subscribeServices(conn, (services) => console.log("New services!", services));
```

You can also import the collection:
Expand All @@ -217,7 +263,7 @@ import { servicesColl } from "home-assistant-js-websocket";
const coll = servicesColl(connection);
console.log(coll.state);
await coll.refresh();
coll.subscribe(services => console.log(services));
coll.subscribe((services) => console.log(services));
```

### Collections
Expand Down Expand Up @@ -266,11 +312,11 @@ function panelRegistered(state, event) {

// This will be merged with the existing state.
return {
panels: state.panels.concat(event.data.panel)
panels: state.panels.concat(event.data.panel),
};
}

const fetchPanels = conn => conn.sendMessagePromise({ type: "get_panels" });
const fetchPanels = (conn) => conn.sendMessagePromise({ type: "get_panels" });
const subscribeUpdates = (conn, store) =>
conn.subscribeEvents(store.action(panelRegistered), "panel_registered");

Expand All @@ -279,7 +325,7 @@ const panelsColl = getCollection(conn, "_pnl", fetchPanels, subscribeUpdates);
// Now use collection
console.log(panelsColl.state);
await panelsColl.refresh();
panelsColl.subscribe(panels => console.log("New panels!", panels));
panelsColl.subscribe((panels) => console.log("New panels!", panels));
```

Collections are useful to define if data is needed for initial data load. You can create a collection and have code on your page call it before you start rendering the UI. By the time UI is loaded, the data will be available to use.
Expand Down Expand Up @@ -374,7 +420,7 @@ import {
Auth,
createConnection,
subscribeEntities,
createLongLivedTokenAuth
createLongLivedTokenAuth,
} from "home-assistant-js-websocket";

(async () => {
Expand All @@ -384,7 +430,7 @@ import {
);

const connection = await createConnection({ auth });
subscribeEntities(connection, entities => console.log(entities));
subscribeEntities(connection, (entities) => console.log(entities));
})();
```

Expand All @@ -406,6 +452,6 @@ createConnection({
// TODO: Handle authentication with Home Assistant yourself :)

return ws;
}
},
});
```
22 changes: 21 additions & 1 deletion lib/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class Connection {
commands: Map<number, CommandInFlight>;
eventListeners: Map<string, ConnectionEventListener[]>;
closeRequested: boolean;
suspendReconnectPromise?: Promise<void>;
// @ts-ignore: incorrectly claiming it's not set in constructor.
socket: HaWebSocket;

Expand Down Expand Up @@ -166,6 +167,20 @@ export class Connection {
);
}

suspendReconnectUntil(suspendPromise: Promise<void>) {
this.suspendReconnectPromise = suspendPromise;
}

suspend(suspendPromise?: Promise<void>) {
if (suspendPromise) {
this.suspendReconnectPromise = suspendPromise;
}
if (!this.suspendReconnectPromise) {
throw new Error("Can't suspend without a suspend promise");
}
this.socket.close();
}

close() {
this.closeRequested = true;
this.socket.close();
Expand Down Expand Up @@ -305,7 +320,7 @@ export class Connection {
}
}

private _handleClose(ev: CloseEvent) {
private async _handleClose(ev: CloseEvent) {
// Reject in-flight sendMessagePromise requests
this.commands.forEach((info) => {
// We don't cancel subscribeEvents commands in flight
Expand Down Expand Up @@ -342,6 +357,11 @@ export class Connection {
}, Math.min(tries, 5) * 1000);
};

if (this.suspendReconnectPromise) {
await this.suspendReconnectPromise;
this.suspendReconnectPromise = undefined;
}

reconnect(0);
}

Expand Down

0 comments on commit 1dbf1b1

Please sign in to comment.