Can not get nodejs client websocket connection to work

Hopefully someone can help me out here, I’ve been trying to figure out where the problem is getting a nodejs server app to connect to HA as a websocket client. I’ve tried various libraries and different connection methods however the connection is always closed. I’m starting to wonder if it’s HA’s implementation of the websocket API. Also version 0.33 behaves differently than HA version 0.34 ( up to date tested on 0.40.2).

You can see in the below output that HA version 34 and 40 both receive the message sent and accept the auth msg with api_password, however after that the connection is closed and also I have to do this blindly instead of a handler for .on('message', ... as regardless of the version the message handler never gets invoked so I cannot switch on the message type. HA Version 0.33 throws a 404, I’m not sure it’s relevant, could be an implementation change but thought I’d include to be thorough.

I realize this that this sounds like it could be a problem with the websocket library in node, maybe it is, but hopefully someone has some tips here because I’m gonna go blind staring at this any longer ;o) I’ve included a docker-compose file to setup ha with the version wanted to test along with the config and the nodejs script, hopefully that will help a bit.

Steps to reproduce

  • Create the files with contents in this post
  • Create directory structure <root-folder>/homeassistant-docker/config
  • Paste configuration.yaml into <root-folder>/homeassistant-docker/config/configuration.yaml
  • Create <root-folder>/docker-compose.yaml
  • Create <root-folder>/websocket.js
  • cd to the <root-folder> run docker-compose up -d && docker-compose -f --tail
  • in another terminal cd to <root-folder> and run node websocket.js

Files mentioned in above steps

websocket.js (run npm install websocket first)

const util = require('util');
var client = new (require('websocket').client)();
client.on('connectFailed', (err) => console.log('**connectFailed: ', util.inspect(err)));
client.on('connect', function(conn) {
    console.log('**onConnect');

    conn.on('error', (error) => console.log('**onError: ', util.inspect(err)));
    conn.on('close', () => console.log('**onClose'));
    conn.on('message', (msg) => util.inspect(msg));

    const authObj = { type: 'auth', api_password: 'testing123' };
    conn.send(JSON.stringify(authObj));
});

client.connect('ws://localhost:8123/api/websocket');

configuration.yml

homeassistant:
    name: Home
    unit_system: metric
    time_zone: America/Los_Angeles

frontend:

http:
    api_password: !env_var API_PASS

logger:
    default: debug

docker-compose.yml

version: "3.1"

services:
    hass:
        image: homeassistant/home-assistant:0.40.2
        #image: homeassistant/home-assistant:0.34.0
        #image: homeassistant/home-assistant:0.33.0
        environment:
            API_PASS: testing123
        ports:
            - '8300:8300'
            - '8123:8123'
        volumes:
            - ./homeassistant-docker/config:/config
            - /etc/localtime:/etc/localtime:ro

Output

####0.40.2 (Same as 0.34.0)

  • From HA logs
hass_1      | 17-03-22 19:07:11 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139898631535360: Connected
hass_1      | 17-03-22 19:07:11 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139898631535360: Sending {'ha_version': '0.40.2', 'type': 'auth_required'}
hass_1      | 17-03-22 19:07:11 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139898631535360: Sending {'ha_version': '0.40.2', 'type': 'auth_ok'}
hass_1      | 17-03-22 19:07:11 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139898631535360: Connection closed by client
hass_1      | 17-03-22 19:07:11 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139898631535360: Closed connection
  • From Node:
⟴  node websocket.js
**onConnect
**onClose

####0.34.0

  • From HA logs
hass_1      | 17-03-22 15:12:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139942411936768: Connected
hass_1      | 17-03-22 15:12:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139942411936768: Sending {'ha_version': '0.35.0', 'type': 'auth_required'}
hass_1      | 17-03-22 15:12:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139942411936768: Sending {'ha_version': '0.35.0', 'type': 'auth_ok'}
hass_1      | 17-03-22 15:12:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139942411936768: Connection closed by client
hass_1      | 17-03-22 15:12:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 139942411936768: Closed connection
  • From Node:
⟴  node websocket.js
**onConnect
**onClose

####0.33

  • Nothing from HA logs
  • From running node below:

⟴  node websocket.js
**connectFailed:  Error: Server responded with a non-101 status: 404
Response Headers Follow:
content-type: text/plain; charset=utf-8
content-length: 14
date: Wed, 22 Mar 2017 19:02:42 GMT
server: Python/3.5 aiohttp/1.0.5

    at WebSocketClient.failHandshake (.../homeassistant-tests/ha-ws/node_modules/websocket/lib/WebSocketClient.js:326:32)
    at ClientRequest.<anonymous> (.../homeassistant-tests/ha-ws/node_modules/websocket/lib/WebSocketClient.js:265:18)
    at emitOne (events.js:96:13)
    at ClientRequest.emit (events.js:189:7)
    at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:522:21)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
    at Socket.socketOnData (_http_client.js:411:20)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:189:7)
    at readableAddChunk (_stream_readable.js:176:18)

An additional piece of information, I was able to get the actual error dumped to the console testing the above examples against 0.40.2. I see this error

{
    description: "Control frames must not be fragmented."
    reasonCode: 1002
}

(edit)
and a bit more from the node side:

Wed, 22 Mar 2017 23:56:30 GMT websocket:connection processReceivedData
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection -- protocol error
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  read
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  _read
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket Event  'resume'
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection handleSocketResume: socket resume event
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection processReceivedData
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection -- insufficient data for frame
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  read
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection drop
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection Forcefully dropping connection. skipCloseFrame: undefined, code: 1002, description: Control frames must not be fragmented.
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection sendCloseFrame state: open, reasonCode: 1002, description: Control frames must not be fragmented.
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection sendFrame
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  write
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  _write
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  _writeGeneric
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  _unrefTimer
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  end
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket Event  'prefinish'
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection ||| Socket method called:  read
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection clearCloseTimer
Wed, 22 Mar 2017 23:56:30 GMT websocket:connection Emitting WebSocketConnection close event

hopefully there are some js peeps on here or a dev that knows enough on the python side as I’m lacking in that area

Guess it’s just me then… Well I found a workaround if anyone needs to a nodejs event parser with input from home assistant. I found the ServerSent Events implementation docs for HA and wrote a quick and dirty lib for it. Maybe it’ll help someone else, no idea what’s going on with the websockets but this workaround is super easy and works well in my limited testing.

I pushed up to Github: HA-Events and if interested can see the examples there or just npm install AYapejian/ha-events

Saw your thread here, struggling with the same issue - can’t establish the connection, it disconnects directly.

I’ve tested against one RPI3 installation (running Python 3.4) and one running in docker on Ubuntu (running Python 3.6), same version of HA on both (53.1) and aiohttp (2.2.5). I cannot connect to the docker instance.

I found this thread https://github.com/node-red/node-red/issues/1246 (last comment) where the guy runs HA in Docker on Synology NAS, and can confirm it’s the same issue for me:

  • The RPI3 instance sends two packages: 1) connect ack, then 2) payload with HA version and “auth_ok” (I tested without api password).
  • The docker instance sends one package with both combined.

Testing with Simple Websocket Client addon to Chrome seems to handle this anyway, as well as Chrome itself (web UI uses websocket) and I can connect fine to both.

I’ve also tested with npm package wscat since it’s using the same node ws package, it connects fine to RPI3 instance but gives the following error for docker instance:

error: Error: RSV2 and RSV3 must be clear

So - the response from HA running in Docker is garbled and will cause issues with connecting. Not sure what to do with that information though… As you said, it’s possible to use the event stream but your HA-Events is not published anymore. Node-RED has an eventsource input node that can be used, but it doesn’t contain exactly the same information as the websocket data (for instance; missing domain).

Did you get further on this topic, @RubenRybnik ?

Hey, yea I haven’t looked further into the websocket issue. My node package is published under node-home-assistant on git now. If it’s not an exact fit for you at least should give some an idea on setting it up. I’ve been using it in my node-red module and it seems to be working well. They could both use some polish but maybe you’ll get use out of it.

If you stumbled upon an answer for the websocket issue I’d be very interested in hearing about it :wink:

I am running into this exact issue. I would really like to set up a Node server that acts as an interface between my clients and HASS, but I keep getting the error “Control frames must not be fragmented.” when trying to connect via WS.

I am using this library to connect to a WS via Node: https://github.com/theturtle32/WebSocket-Node

To be honest I’ve not really found much of a reason to investigate the web socket issue further. Server Sent Events has been working solidly for me for awhile now. Like I mentioned previously you can use my lib I linked above ( which now has a CLI utility for querying home assistant for various info, but still is a lib you can include in your own projects ). Or just re-use my connection code here https://github.com/AYapejian/node-home-assistant/blob/master/lib/ha-events.js

The only down side is no bi-directional communication without websockets but I just send those with a normal REST api call.

I agree there’s little reason to investigate this further. I ended up solving my predicament by settling for only using web sockets client side for subscribing to event changes. Any time I want to call a service, I just post to my interface first and then post to my HASS instance (all via REST). It works out well.