Supereg / homebridge-http-switch

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

Basic telnet support? #52

Closed maelstromchanneled closed 4 years ago

maelstromchanneled commented 4 years ago

Is your feature request related to a problem? Please describe. I have a device on my intranet that accepts commands via telnet. Some of the commands are simple toggles (no response), while others return state. I'd like to use the http-switch but with single telnet commands to implement the toggle and state sniffing protocols.

Describe the solution you'd like

{
  "accessory": "HTTP-SWITCH", "name": "TOGGLE1", "switchType": "stateful",
  "onUrl": "telnet://xxx.xxx.xxx.xxx/ON-COMMAND",
  "offUrl": "telnet://xxx.xxx.xxx.xxx/OFF-COMMAND",
  "statusUrl": "telnet://xxx.xxx.xxx.xxx/STATE-REQUEST-COMMAND",
  "statusPattern": "STATE-ON-PATTERN"
}   

Where "ON-COMMAND", "OFF-COMMAND", and "STATE-REQUEST-COMMAND" are some ASCII sequences to pass via telnet. For the statusUrl, the telnet server would return a status that should match "STATE-ON-PATTERN".

Thanks a ton in advance!

Supereg commented 4 years ago

I don't know if I would want to add a telnet client into this plugin. But anyways, I currently don't have the time to do it. But I'm glad to review any Pull request of someone giving this a shot.

maelstromchanneled commented 4 years ago

I am a programmer but haven't developed in Node before, so let me take a little time to see if I am up for this challenge. Your code is very clean so at first glance it seems doable.

Supereg commented 4 years ago

http-switch is based on homebridge-http-base. It maybe useful to implement it there. But I'm still not sure if I want my HTTTP devices doing other stuff than HTTP.

maelstromchanneled commented 4 years ago

Ok, I ended up implementing a basic HTTP to TELNET bridge using python. I'll include the code and configs here in case people are interest.

First, here is the python code [http_telnet_bridge.py] (note that this server is configured to run on port 8000 of localhost):

import time
import telnetlib
import re
import urlparse
from BaseHTTPServer import HTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler

class telnet_message_handler( BaseHTTPRequestHandler ) :

   HOST = "xxx.xxx.xxx.xxx"   # this is the address of the device to telnet to
   WAIT = 0.2                 # this is the wait time in seconds for the device to respond

   def telnet_message_query( self, query ) :
      tn = telnetlib.Telnet(self.HOST)
      tn.write(query+'\n')
      time.sleep(self.WAIT)
      message = re.sub( '[\r,\n]', ' ', tn.read_some() )
      tn.close()
      return message

   def do_GET(self) :
      parsed_path = urlparse.urlparse(self.path)
      query = parsed_path.query
      if len(query) > 0 :
         message = self.telnet_message_query( query )
      else :
         message = ''
      self.send_response(200)
      self.end_headers()
      self.wfile.write(message)
      print( '[HTTP TELNET BRIDGE] <' + query + '> ==> <' + message + '>' )
      return

LOCALHOST = ""
LOCALPORT = 8000

print( '[HTTP TELNET BRIDGE] starting server' )
server_address = (LOCALHOST, LOCALPORT)
server = HTTPServer( server_address, telnet_message_handler )
server.serve_forever()

Next, I put http_telnet_bridge.py in the HomeBridge root directory and added this line to the startup script:

python http_telnet_bridge.py &

In config.json, the homebridge-http-switch entry looks like this:

    {
      "accessory": "HTTP-SWITCH",
      "name": "TELNETSWITCH1",
      "switchType": "stateful",
      "pullInterval" : 3000,
      "onUrl":     "http://localhost:8000/?ON-COMMAND",
      "offUrl":    "http://localhost:8000/?OFF-COMMAND",
      "statusUrl": "http://localhost:8000/?STATE-REQUEST-COMMAND",
      "statusPattern": "STATE-ON-PATTERN"
    }   

This effectively solves my need. Would be good to know if there is a way to formalize this within the HomeBridge plugin ecosystem.

Cheers!

maelstromchanneled commented 4 years ago

I've formally documented the code here