Skip to content

Commit

Permalink
fix: Wait until HA is in running state to emit events
Browse files Browse the repository at this point in the history
Closes #246
  • Loading branch information
zachowj committed Jun 23, 2020
1 parent 5ffef62 commit 92cdb64
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 51 deletions.
18 changes: 17 additions & 1 deletion lib/base-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ class BaseNode {
};

if (this.websocketClient) {
if (this.websocketClient.isHomeAssistantRunning) {
connectionStatus = {
shape: 'dot',
fill: 'green',
text: 'running',
};
return connectionStatus;
}

switch (this.connectionState) {
case this.websocketClient.CONNECTING:
connectionStatus = {
Expand All @@ -193,7 +202,7 @@ class BaseNode {
case this.websocketClient.CONNECTED:
connectionStatus = {
shape: 'dot',
fill: 'green',
fill: 'yellow',
text: 'node-red:common.status.connected',
};
break;
Expand Down Expand Up @@ -262,6 +271,13 @@ class BaseNode {
return this.websocketClient && this.websocketClient.connectionState;
}

get isHomeAssistantRunning() {
return (
this.websocketClient.isConnected &&
this.websocketClient.isHomeAssistantRunning
);
}

getPrettyDate() {
return new Date().toLocaleDateString('en-US', {
month: 'short',
Expand Down
44 changes: 16 additions & 28 deletions lib/events-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,19 @@ class EventsNode extends BaseNode {
this.integrationErrorMessage =
'Node-RED custom integration needs to be installed in Home Assistant for this node to function correctly.';

this.addEventClientListener(
'ha_client:close',
this.onHaEventsClose.bind(this)
// Setup event listeners
const events = {
'ha_client:close': this.onHaEventsClose,
'ha_client:open': this.onHaEventsOpen,
'ha_client:error': this.onHaEventsError,
'ha_client:connecting': this.onHaEventsConnecting,
updateNodeStatus: this.onHaEventsUpdateStatus,
integration: this.onHaIntegration,
'ha_client:running': this.onHaEventsRunning,
};
Object.entries(events).forEach(([event, callback]) =>
this.addEventClientListener(event, callback.bind(this))
);
this.addEventClientListener(
'ha_client:open',
this.onHaEventsOpen.bind(this)
);
this.addEventClientListener(
'ha_client:error',
this.onHaEventsError.bind(this)
);
this.addEventClientListener(
'ha_client:connecting',
this.onHaEventsConnecting.bind(this)
);
this.addEventClientListener(
'updateNodeStatus',
this.onHaEventsUpdateStatus.bind(this)
);
this.addEventClientListener(
`ha_events:config_update`,
this.onHaConfigUpdate.bind(this)
);
this.addEventClientListener(
`integration`,
this.onHaIntegration.bind(this)
);

this.updateConnectionStatus();
}

Expand Down Expand Up @@ -90,6 +74,10 @@ class EventsNode extends BaseNode {
this.updateConnectionStatus();
}

onHaEventsRunning() {
this.updateConnectionStatus();
}

onHaEventsError(err) {
if (err.message) this.error(err.message);
}
Expand Down
68 changes: 51 additions & 17 deletions lib/ha-websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class HaWebsocket extends EventEmitter {
this.subscribedEvents = new Set();
this.unsubCallback = {};
this.integrationVersion = 0;
this.isHomeAssistantRunning = false;

this.setMaxListeners(0);
}
Expand Down Expand Up @@ -92,30 +93,53 @@ class HaWebsocket extends EventEmitter {
this.emit('ha_client:connected');

// Client events
this.client.addEventListener('ready', this.onClientOpen.bind(this));
this.client.addEventListener(
'disconnected',
this.onClientClose.bind(this)
);
this.client.addEventListener(
'reconnect-error',
this.onClientError.bind(this)
const events = {
ready: this.onClientOpen,
disconnected: this.onClientClose,
'reconnect-error': this.onClientError,
};
Object.entries(events).forEach(([event, callback]) =>
this.client.addEventListener(event, callback.bind(this))
);
this.onStatesLoadedAndRunning();

// Home Assistant Events
homeassistant.subscribeConfig(this.client, (config) =>
this.onClientConfigUpdate(config)
);
homeassistant.subscribeEntities(this.client, (ent) =>
this.onClientStates(ent)
);
homeassistant.subscribeServices(this.client, (ent) =>
this.onClientServices(ent)
);
homeassistant.subscribeConfig(this.client, (config) =>
this.onClientConfigUpdate(config)
);

return true;
}

async getUser() {
return homeassistant.getUser(this.client);
}

onHomeAssistantRunning() {
if (!this.isHomeAssistantRunning) {
this.isHomeAssistantRunning = true;
this.emit('ha_client:running');
}
}

onStatesLoadedAndRunning() {
const statesLoaded = new Promise((resolve, reject) => {
this.once('ha_client:states_loaded', resolve);
});
const homeAssinstantRunning = new Promise((resolve, reject) => {
this.once('ha_client:running', resolve);
});
Promise.all([statesLoaded, homeAssinstantRunning]).then(([states]) => {
this.emit('ha_client:initial_connection_ready', states);
});
}

async subscribeEvents(events) {
const currentEvents = new Set(Object.values(events));

Expand Down Expand Up @@ -206,6 +230,12 @@ class HaWebsocket extends EventEmitter {
return;
}

// Don't emit events if HA is not in a running state. Keep functioning
// the same as HA prior to vesrion 0.111.0.
if (!this.isHomeAssistantRunning) {
return;
}

if (msg) {
const eventType = msg.event_type;
const entityId = msg.data && msg.data.entity_id;
Expand Down Expand Up @@ -254,8 +284,14 @@ class HaWebsocket extends EventEmitter {
}

async onClientConfigUpdate(config) {
this.integrationVersion = 0;
if (config.components.includes('nodered')) {
// Prior to HA 0.111.0 state didn't exist
if (config.state === undefined || config.state === 'RUNNING') {
this.onHomeAssistantRunning();
}
if (
config.components.includes('nodered') &&
this.integrationVersion === 0
) {
try {
this.integrationVersion = await this.send({
type: 'nodered/version',
Expand All @@ -267,12 +303,14 @@ class HaWebsocket extends EventEmitter {

onClientOpen() {
this.integrationVersion = 0;
this.isHomeAssistantRunning = false;
this.connectionState = HaWebsocket.CONNECTED;
this.emit('ha_client:open');
}

onClientClose() {
this.integrationVersion = 0;
this.isHomeAssistantRunning = false;
this.connectionState = HaWebsocket.DISCONNECTED;
this.emit('ha_client:close');

Expand Down Expand Up @@ -307,10 +345,6 @@ class HaWebsocket extends EventEmitter {
}
}

async getUser() {
return homeassistant.getUser(this.client);
}

async getStates(entityId, forceRefresh = false) {
if (Object.keys(this.states).length === 0 || forceRefresh) {
// TODO: handle forceRefresh and empty state object
Expand Down
5 changes: 5 additions & 0 deletions nodes/events-all/events-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ module.exports = function (RED) {
this.clientEvent('connecting');
}

onHaEventsRunning() {
super.onHaEventsRunning();
this.clientEvent('running');
}

onHaEventsError(err) {
super.onHaEventsError(err);
if (err) {
Expand Down
2 changes: 1 addition & 1 deletion nodes/events-state-changed/events-state-changed.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = function (RED) {
this.onDeploy();
} else {
this.addEventClientListener(
'ha_client:states_loaded',
'ha_client:initial_connection_ready',
this.onStatesLoaded.bind(this)
);
}
Expand Down
6 changes: 4 additions & 2 deletions nodes/poll-state/poll-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ module.exports = function (RED) {

if (this.nodeConfig.outputinitially) {
this.addEventClientListener(
'ha_client:states_loaded',
'ha_client:initial_connection_ready',
this.onTimer.bind(this)
);
}
Expand All @@ -72,7 +72,9 @@ module.exports = function (RED) {
}

async onTimer(triggered = false) {
if (!this.isConnected || this.isEnabled === false) return;
if (!this.isHomeAssistantRunning || this.isEnabled === false) {
return;
}

const pollState = await this.nodeConfig.server.homeAssistant.getStates(
this.nodeConfig.entity_id
Expand Down
4 changes: 2 additions & 2 deletions nodes/trigger-state/trigger-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ module.exports = function (RED) {

if (this.nodeConfig.outputinitially) {
// Here for when the node is deploy without the server config being deployed
if (this.isConnected) {
if (this.isHomeAssistantRunning) {
this.onDeploy();
} else {
this.addEventClientListener(
'ha_client:states_loaded',
'ha_client:initial_connection_ready',
this.onStatesLoaded.bind(this)
);
}
Expand Down

0 comments on commit 92cdb64

Please sign in to comment.