WattBox WB-250-IPW-2 and Telnet

Hey, folks! Thought I would make my weekend project a telnet driver for a WattBox I have behind my TV in the living room.

I started hacking away and ran into problems almost immediately. Namely, the telnet command always times out. I tried the raw socket driver and verified connectivity to the device, which also illustrated that line endings were LF-only, since it sent back the hex characters.

An nc session with the device looks like this:

Please Login to Continue
Username: ernie
Password: ********
Successfully Logged In!
?OutletName
?OutletName={LG Gallery OLED},{Outlet 2}
?Model
?Model=WB-250-IPW-2
?Firmware
?Firmware=1.2.0.6

And the driver code I hacked together so far looks like the below. I'm hoping I'm doing something silly, since there is next to no documentation on the telnet interface. It consistently hangs at the telnetConnect call for 30 seconds, then errors with java.net.SocketTimeoutException.

metadata {
  definition (name: "WattBox", namespace: "ernie", author: "Ernie Miller", ocfDeviceType: "oic.d.switch") {
    capability "Actuator"
    capability "Configuration"
    capability "Refresh"
    capability "Switch"

    command "connectTelnet"
    command "disconnectTelnet"
    command "sendMsg", ["String"]
  }
}

preferences {
  input "ip", "text", title: "IP / Hostname", required: true
  input "port", "number", title: "Port (23)", required: true, defaultValue: 23
  input "username", "text", title: "Username", required: true
  input "password", "text", title: "Password", required: true
}

def parse(String message) {
  log.info message
}

def telnetStatus(String message) {
	log.info "telnetStatus - error: $message"
}

def connectTelnet() {
  log.debug "Connecting to telnet: $host"
  telnetConnect([termChars: [10]], ip, port as int, username, password)
}

def disconnectTelnet() {
  log.debug "Resetting telnet session"
  telnetClose()
}

def sendMsg(String message) {
  log.debug "Sending: $message"
  return new hubitat.device.HubAction(message, hubitat.device.Protocol.TELNET)
}

take a look at my email or ups status via telnet.. it is async you need to have a fx called.

you need a parse fx that gets called automatically fater connect

Sorry, maybe I’m missing something, but I do have a parse method.

try it from windows command line does it work

I’m on a Mac, but yes. I can telnet to the device successfully, as shared in the initial post.

well try some debuging for username port etc. maybe it is case sensitive or your convesion to int is not working.. if you notice my code is differnt.

i also dont know what termchars 10 is.. take a look at my connect string.. my guess is yours is not correct.

Hey, appreciate the attempt to help.

termChars is an option that (with this value) tells telnetConnect that only a line feed, not a carriage return/line feed, terminates a line. It’s documented elsewhere in this community.

Again, appreciate the attempt to help, but I came here after exhausting all the options, including just hardcoding all the parameters. Also, I’ve checked to make sure that it can at least connect to other ports (even those that don’t speak telnet) and will error differently in those cases.

At this point I’m fairly sure there are some specifics in the telnetConnect implementation that I’m just unaware of. Another undocumented option, perhaps, or maybe it’s waiting for a specific keyword to trigger sending the username, instead of just pushing username/password down the line and waiting for the telnet daemon to read it.

I’ve been programming for too many decades to be so bold as to say I’m not doing something obviously wrong, here, but just know I’ve tried many things that seemed like obvious possibilities, to me.

1 Like

Yep, sure enough -- I got the idea to run a "telnet" server on my MBP this morning via nc -l <ip> 8888 and configure my driver to talk to that.

I tried sending the driver any old string, then tried Username: , and only once I sent the specific string login: did it send the username over the wire.

I'm wondering if perhaps there's an option for the prompt string that's been added since @bravenel made this post a couple of years ago.

What I have managed to do as a workaround is to login with null, null and immediately push the username and password onto the buffer, which does seem to get me logged in OK. I suspect, due to how telnet is supposed to work, this would generally be sufficient to login to all devices that read a username and password in that order.

1 Like

Not for login prompt per se. There is one more parameter available for telnetConnet:

telnetConnect(ip, 23, "nwk", "", ignoreList:["QSE>"])

This is an example for logging into Lutron GrafikEye QS. Notice the ignoreList optional parameter. That eats line-level prompts.

2 Likes

In case anyone's curious to see the "working" (or at least work-in-progress) code, here it is. My intent, if I keep playing around with this, is to make this a parent driver and each outlet a component, exposing them as a switch device. The telnet API for these things sends unsolicited status change messages, which should allow parsing and sending out to each device to update status. Since I've never actually done any of the stuff I just mentioned above, should be fun.

/**
* WattBox Telnet Driver
*
* Copyright 2020 Ernie Miller
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

metadata {
  definition (name: "WattBox", namespace: "ernie", author: "Ernie Miller", ocfDeviceType: "oic.d.switch") {
    capability "Actuator"
    capability "Configuration"
    capability "Refresh"
    capability "Switch"

    attribute "lastResponse", "string"

    command "reconnectTelnet"
    command "sendMessage", ["String"]
  }
}

preferences {
  input "ip", "text", title: "IP / Hostname", required: true
  input "port", "number", title: "Port (23)", required: true, defaultValue: 23
  input "username", "text", title: "Username", required: true
  input "password", "text", title: "Password", required: true
}

def installed() {
  initialize()
}

def updated() {
  initialize()
}

def initialize() {
  log.debug "Connecting to telnet: $ip"
  telnetConnect([termChars: [10]], ip, port as int, null, null)
  sendMessage(username)
  sendMessage(password)
}

def parse(String message) {
  log.info message
  createEvent(name: "lastResponse", value: message)
}

def telnetStatus(String message) {
	log.debug "telnetStatus: $message"
	if (message == "receive error: Stream is closed") {
		log.error "Telnet connection dropped..."
		initialize()
	} else {
    log.error "Unknown telnetStatus message"
	}
}

def reconnectTelnet() {
  log.debug "Resetting telnet session"
  telnetClose()
  initialize()
}

def sendMessage(String message) {
  log.debug "Sending: $message"
  sendHubCommand(
    new hubitat.device.HubAction(message, hubitat.device.Protocol.TELNET)
  )
}

Thanks for the quick reply! I think this workaround I found will do the trick. I’d be curious to see if you find that the built-in drivers similarly can pre-buffer the login/pass without doing the expect-style behavior, as it would definitely be a nice usability improvement in the behavior of telnetConnect (though admittedly, even if built-in drivers behaved well, with the breadth of devices out there I could understand not wanting to make a potentially-breaking change to the API)

So, that went more quickly than anticipated, and was kind of fun. Thanks again for the quick response, @bravenel!