Supereg / homebridge-http-switch

Powerful http switch for Homebridge: https://github.com/homebridge/homebridge
ISC License
219 stars 36 forks source link

Can't get a stateful switch to work :( #41

Closed teymur1988 closed 4 years ago

teymur1988 commented 4 years ago

Describe the bug Can't get a stateful switch to work. The logs report invalid header token. I have a relay which works with REST APIs. simply by sending a get request like: http:/IP/on http:/IP/off http:/IP/state http:/IP/toggle

Everyting works if doing from a browser or a command line. It also works if I use a stateless switch. But I need a stateful.

Version (output of npm list -g homebridge homebridge-http-switch)

Configuration


Your configuration goes in here
      {
          "accessory": "HTTP-SWITCH",
          "name": "Switch",

          "switchType": "stateful",

          "onUrl": "http://IP/on",
          "offUrl": "http://IP/off",

          "statusUrl": "http://IP/state"
        }  

**Additional context**
From logs:
[2019-11-19 0:34:30] [Switch] Error: Parse Error: Invalid header token
    at Socket.socketOnData (_http_client.js:456:22)
    at Socket.emit (events.js:210:5)
    at addChunk (_stream_readable.js:308:12)
    at readableAddChunk (_stream_readable.js:289:11)
    at Socket.Readable.push (_stream_readable.js:223:10)
    at TCP.onStreamRead (internal/stream_base_commons.js:182:23) {
  bytesParsed: 21,
  code: 'HPE_INVALID_HEADER_TOKEN',
  reason: 'Invalid header token',
  rawPacket: <Buffer 48 54 54 50 2f 31 2e 30 20 32 30 30 20 4f 4b 0d 0a 6c 77 49 50 2f 31 2e 34 2e 31 20 28 68 74 74 70 3a 2f 2f 73 61 76 61 6e 6e 61 68 2e 6e 6f 6e 67 6e ... 300 more bytes>
}
teymur1988 commented 4 years ago

Hi @Supereg Could you please help with this? I think my problem is related to the server response, because it responds "on" or "off" and this plugin doesn't understand that. I've tried using : { "accessory": "HTTP-SWITCH", "name": "L-Test", "switchType": "stateful", "onUrl": "http://10.10.10.54/on", "offUrl": "http://10.10.10.54/off", "statusUrl": "http://10.10.10.54/state", "statusPattern": "on" } But no joy.

Supereg commented 4 years ago

Is it still the issue with "Invalid header token". It seems that your http server returns some http headers which are not standardized. What I found out ready this: https://stackoverflow.com/questions/36628420/nodejs-request-hpe-invalid-header-token

teymur1988 commented 4 years ago

Hi Thanks for your reply! Yes it is invalid header token issue. This is what the device responds when statusUrl is sent to it. Is there anything wrong with this? Couldn't paste the html code here. Attached a file. status.txt

Supereg commented 4 years ago

This is what the device responds when statusUrl is sent to it. Is there anything wrong with this?

Nothing wrong with that (that's also just the http body).

The problem is the http header the server seems to return. Http headers are some special options which the browser can interpret. And the parsing of those headers seems to have become more strict in nodes v12.

teymur1988 commented 4 years ago

Is there anything I can do to get this fixed?

Supereg commented 4 years ago

Could you quickly confirm what version of node you are running, something like >= v12?

teymur1988 commented 4 years ago

Sure:

C:\Users\teymu>node -v v12.13.0

C:\Users\teymu>

Supereg commented 4 years ago

If that's true you could just add the following node command line argument --http-parser=legacy when you startup your homebridge instance. This will revert the parsing to the old way.

teymur1988 commented 4 years ago

You're probably better in this than I am. I'm running Homebridge on windows as you see. And I used nssm to create a service. Below is the argument from nssm. Where do I exactly paste --http-parser=legacy ??? I tried pasting it in cmd like homebridge -I --http-parser=legacy but it won't take it. Probably not right way of doing it

C:\Users\teymu\AppData\Roaming\npm\node_modules\homebridge-config-ui-x\dist\bin\hb-service.js run -I -U C:\Users\teymu.homebridge

Supereg commented 4 years ago

Oh yeah, I forgot, that homebridge brings its own executable (the argument would need to be passed to the node executable). Maybe I could add some property to the config file which tries to change the http parsing programmatically. Currently no time for that, but will come back to it.

teymur1988 commented 4 years ago

Thanks, will be waiting for that!

Supereg commented 4 years ago

Okay so the thing is, I could programmatically change the http parsing to the legacy one. However not in node v12 😅 So the best solution would be that you investigate where exactly homebridge is started (I don't know how nssm handles service definitions). The thing is that you are running homebridge-config-ui-x (?) and that it handles the startup of homebridge as far as I could identify. Some around this line you would probably somehow explicitly call node and pass the --http-parser=legacy option the the node process. You have no chance of changing the http headers of your http server I guess? Maybe you could reach out to somebody at homebridge-config-ui-x and ask how to pass command line options to the node process.

As time of writing there could also be another solution to the problem. You could pass the http-parser option via the environment variable NODE_OPTIONS. Check some tutorial for windows and set the variable NODE_OPTIONS to --http-parser=legacy

teymur1988 commented 4 years ago

Hello @Supereg Sorry been away for a while, couldn't get onto the web. Thanks very much for your reply. I erased that firmware from the device that was sending invalid header token. My new "problem" has still to do with getting the device status, now it's sending the correct headers, plus bunch of other info as well. I'll give an example down below. Could you please explain if it is possible at all to use the "StatusPattern" to parse this, and take the highlighted line value. I've looked at the regex, but found it rather difficult to implement.

So requesting status url looks like this: "http://device_ip:port/get_status.cgi?user=admin&pwd=password"

Response from the server is:

var alarm_mail=0; var alarm_audio=0; var alarm_temperture=0; var alarm_iolinkage=0; var alarm_ioout_level=0; var alarm_upload_interval=0; var alarm_presetsit=0; var alarm_snapshot=1; var alarm_record=0; var alarm_schedule_enable=0; var alarm_http=1; var alarm_http_url="url.com/trigger"; var alarm_schedule_sun_0=0; var alarm_schedule_sun_1=0; var alarm_schedule_sun_2=0; var alarm_schedule_mon_0=0; var alarm_schedule_mon_1=0; var alarm_schedule_mon_2=0; var alarm_schedule_tue_0=0; var alarm_schedule_tue_1=0; var alarm_schedule_tue_2=0; var alarm_schedule_wed_0=0; var alarm_schedule_wed_1=0; var alarm_schedule_wed_2=0; var alarm_schedule_thu_0=0; var alarm_schedule_thu_1=0; var alarm_schedule_thu_2=0; var alarm_schedule_fri_0=0; var alarm_schedule_fri_1=0; var alarm_schedule_fri_2=0; var alarm_schedule_sat_0=0; var alarm_schedule_sat_1=0; var alarm_schedule_sat_2=0; var alarm_note=1; var alarm_server=""; var alarm_user=""; var alarm_pwd=""; var alarm_port=0;

Supereg commented 4 years ago

A value of "statusPattern"="var alarm_audio=0;" should work (or "statusPattern"="var alarm_audio=1;", whatever you want it to be considered as 'on'). Regular expressions is pretty powerful when you want to match certain patterns. However in your case you can just use it without any special patterns and it is somewhat of a check if the given string is contained in the http request.

teymur1988 commented 4 years ago

Hi @Supereg , Thanks for your reply! I've tried the above solution before opening this discussion again, however the only difference I had was: "statusPattern": "var alarm_audio=1;", and it seemed to me that it didn't work. I've read all the wiki here, and of coursein my case this is pretty simple. Maybe sometime is needed to get the switch status updated. I'll play with the pullinterval.

Supereg commented 4 years ago

Could maybe post the relevant parts of your homebridge config for this plugin? So I can verify the property is placed at the right spot. Additionally enable the debug option in the plugin config. This will print all configured settings at startup and also what happens when comparing the pattern. This could help troubleshoot

teymur1988 commented 4 years ago

Hey I think I did manage to get it working! Thank you. I just set the pullInterval to 1 second, so for that to be more or less realtime. The reason is that the option I'm after isn't only controlled via homekit but from the device's app and I'm not the only one controlling it (there are some android guys as well). Here's the config part.

{ "accessory": "HTTP-SWITCH", "name": "Switch", "switchType": "stateful", "onUrl": "http://ip:port/set_alarm.cgi?user=admin&pwd=password&audio=1", "offUrl": "http://ip:port/set_alarm.cgi?user=admin&pwd=password&audio=0", "statusUrl": "http://ip:port/get_params.cgi?user=admin&pwd=password", "statusPattern": "var alarm_audio=1;", "pullInterval": 1000, "debug": true }

I want to thank you again for this cool plugin and for the quick help!