1

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.

Eric
  • 488
  • 1
  • 3
  • 10
  • 1
    Maybe see if config["access_token"] is defined, but also see if the server is receiving the request properly. Insert a console.log(config["access_token"]); and possibly wrap the ws.send in a try/catch for an error. But for WS monitor the messages in both ends best bet. – BGPHiJACK Feb 15 '21 at 18:57
  • Few guesses, but if you're inserting a "default" token make sure it's not escaping incorrectly and it can be JSON.stringify. – BGPHiJACK Feb 15 '21 at 18:59
  • @blanknamefornow Thanks for the suggestions. I appreciate it. I found the issue after looking closer at the ws.send. The serialize function had in incorrect reference. I had JSON.Stringify, it is supposed to be JSON.stringify. It's always the little things! Thank you. – Eric Feb 15 '21 at 19:11
  • No problem! Once you get comfortable it'd be the least of your worries. Cheers! – BGPHiJACK Feb 15 '21 at 19:40

1 Answers1

0

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...

Eric
  • 488
  • 1
  • 3
  • 10