javascriptnode.jswshome-assistant

Javascript class with web socket prematurely ceasing execution


I am trying to build a JavaScript class that allows me to interact with my Home Assistant server via web sockets (ws library.) The script is intended to be executed in the node.js environment.

const WebSocket = require('ws');

class HomeAssistantWebSocket {
  constructor(config = {}) {
    this.config = config;
    this.initialize();
  }

  config;
  initialized = false;
  initializeErrors = [];
  authenticated = false;
  ws = null;

  authenticate = () => {
    let {
      config,
      ws,
      serialize
    } = this;

    console.log("Attempting to authenticate...");
    ws.send(serialize({
      "type": "auth",
      "access_token": config["access_token"]
    }));
    return true;
  }

  openConnection = () =>  {
    let {
      ws
    } = this;

    ws.on('open', () => {
      console.log("Connection opened!");
    });

    ws.on('message', (data) => {
      this.handleMessage(data);
    });
  }

  deserialize = (string) => {
    try {
      return JSON.parse(string);
    } catch (error) {
      return false;
    }
  }

  handleMessage = (data) => {
    let {
      authenticate,
      deserialize,
      ws
    } = this;

    data = deserialize(data);
    console.log(data);

    if(data["type"] === "auth_required") {
      authenticate();      
    }
    if (data["type"] === "auth_ok" && !this.authenticated) {
      this.authenticated = true;
      console.log("Successfully authenticated");
    }
    if (data["type"] === "auth_ok") {
      ws.send(JSON.stringify({
        "id": 20,
        "type": "subscribe_events",
      }));
    }
  }

  initialize = () => {
    let {
      config,
      initialized,
      initializeErrors,
    } = this;

    if (Object.keys(config).length < 1) {
      initializeErrors.push("No config present.");
    } else if (!config.hasOwnProperty("access_token") && typeof config["access_token"] === "string") {
      initializeErrors.push("Config must contain a valid access_token.");
    } else if (!config.hasOwnProperty("home_assistant_url") && typeof config["home_assistant_url"] === "string") {
      initializeErrors.push("Config must contain a valid home_assistant_url");
    }

    if (this.initializeErrors.length === 0) {
      this.ws = new WebSocket(config["home_assistant_url"]);
      this.openConnection();
      initialized = true;
      console.log("Attempting to open connection...");
    } else {
      console.log("Failed to initialize:");
      this.initializeErrors.forEach((e) => {
        console.log(e);
      });
    }
    return true;
  }

  serialize = (json) => {
    try {
      return JSON.Stringify(json);
    } catch (error) {
      return false;
    }
  }

}

const haWS = new HomeAssistantWebSocket({
  "access_token": "redacted_access_token",
  "home_assistant_url": "ws://homeassistant.local:8123/api/websocket"
});

I am running in to an issue where my code ceases execution after the authentication phase. My code prints the following in the console and then the script stops executing. No errors are present.

Connection opened!
{ type: 'auth_required', ha_version: '2021.2.3' }
Attempting to authenticate...

I have verified my code does properly connect to the web socket api and is communicating with the home assistant server. Does anyone see anything wrong with my code that would cause the script to stop execution/garbage collect the ws on message to prevent further messages from being received?

I have a very basic example working as expected outside of a class that makes it pass the authentication phase and leaves the socket open and receives data as expected. Any help would be greatly appreciated.


Solution

  • serialize = (json) => {
      try {
        return JSON.stringify(json);
      } catch (error) {
        return false;
      }
    }

    I found the issue in the serialize function. I had an improper reference to the JSON.stringify function. In my code it was JSON.Stringify. It should be JSON.stringify.

    It's always the little things...