[AS-IS] Insteon web socket driver

One or the other, not both. You must change our the insteonserver.js file if you use the WS parent file for keypads.

I've heard from some that the keypad support works, but it's slow. I've heard from some that when you use the insteonserver.js file and the WS parent file with keypad support, that it doesn't create the child devices. The latter, I have experienced myself. However, I don't own a keypad. Had one for initial testing, but returned it when we were finished testing. Insteon keypads are not really my thing. I'm guessing (and this is a complete guess) that those that have it working have added devices with the two files that don't support keypads, so they get the child devices, then they swapped out the keypad files and got support for keypads. Just a hypothesis though. No way to test that without a keypad.

Maybe you don't need this for your keypads though... :man_shrugging:

Thanks. I might just link the keypad scenes manually. I am only using them for various zone controls with other Insteon devices. For example, all on/off, all dim, zone1 on/off, zone2 on/off. I can accomplish this with the local programming in multi-link mode.

Question: After configuring my Insteon devices on my Pi, I powered off the Pi and Hubitat continued controlling these devices via Google Home (which is my user-level UI for home control). Do I need the Pi running continuously in the background after setup? Would it make sense to configure a bunch of fake devices just to generate several childs for future use in the Hubitat interface? I ask because I only have one Pi and it is dedicated to Home Assistant which I will need again.

You will not get status update or be able to use input devices such as Insteon contact sensors, leak sensors or motion sensors without the node.js server and client listener running. Also cannot get new child devices without it running.

What you experienced was that part of the driver that Chris wrote to control device via http;// get commands. There are earlier drivers that do the same thing without node,js, but they don't give you status of the light, so things can get out of sync when you manually control switches if the node.js server isn't used.

Do you want a keypad? Ill take one out of my wall and send it to you. The keypad is the most important part of my insteon install. Unless there is a 8 button z-wave switch that is affordable. Ill send you one to test with and maybe get a more transparent way of switching insteon to hubitat. As a matter of fact I can send several switches, motion sensors etc to test with. I have bought many extra except key pads and as I replace old switches for z-wave I have plenty to offer if you can make a difference for easier and better migration. Eg. have a pie, download this, run it and start adding. PM me if your interested.

I’m not the developer of this driver or the node.js code. Would not know how to improve it. I tested all that I could and @cwwilson08 coded to the best of his abilities at the time. We tested, it worked.

The code is there for anyone that knows how to improve on it and has the motivation to do so. Sorry, but I cannot help make this into more than it is today.

1 Like

Hmm. Considering I need Home Assistant (w/ Insteon control panel) to configure Insteon devices and I have HE, wouldn't it make more sense to control these Insteon devices using Home Assistant Device Bridge?

Am I missing something?

It’s great that they can control the device settings, but it’s not as good for controlling the devices themselves. See my reply here for detail around that.

1 Like

Hello,

New user to the Hubitat community, picked one up in hopes of getting control back to my Inteson devices.

By using the documentation on these forms I was able to get Machine Rules created to control my dimmer switches but wasn't able to set it up as a Device to turn it on / off.

I believe I should be using the instructions in this document to accomplish this. I had a few questions for clarification (wasn't able to find the answers in the comments, they are probably pretty basic questions):

  1. If I have the hubitat hub do I still require another device to run the Insteon Server Node.js?
  2. In the section for "Installing the Insteon WS Parent and Child device drivers", I am stuck at where I could find the link for the actual Parent and Child Driver code to copy?

Thanks for the assistance.

Yes.

You can get them from @SmartHomePrimer's GitHub repository; available here:

1 Like

Thank you!

1 Like

I am having trouble with adding a motion sensor. I don't see any support for type "motionsensor" in the insteonserver.js file so I think it is being ignored in my config.json file. This is how I have it set up in the config file:

{
"name": "Bonus Room Motion Sensor",
"deviceID": "2D3DA0",
"dimmable": "no",
"deviceType": "motionsensor"
}

Maybe I have the wrong version of insteonserver.js?

So sorry for the long delay in responding. Please try removing "dimmable": "no", from your config.json file. I just checked how I have my contact sensor setup and it doesn't include the dimmable type for that device. Save and restart the server and client listener.

I do not personally own an Insteon motion sensor, so I've no way to test this. Motion sensors were included in the original Homecontroller code that sits at the base of all this, so it technically should work, but I cannot recall if we ever tested it with this specific integration. I am personally considering just retiring my one Insteon contact sensor. I thought I would go ahead and use it, because it was working perfectly as a sensor for closed physical contacts, but now that I've converted it back to using the internal reed switch, I find I have to restart the server and client listener every week or so. Not worth it to me and I have several more Aqara and Mijia contact sensors that are rock solid using Home Assistant with Home Assistant Device Bridge to bring them back into HE.

Looks like we may have either abandoned the attempt because we didn't have a motion sensor to test with, or just forgot to finish adding it. Part of the code was in the ws parent file, but not all of it, and as you mentioned, nothing was in the insteonserver.js file. I'm not sure I did it right, but give these a try and see if they work for you.

Insteonserver.js (with motion sensor - no keypad support)

'use strict'
var Insteon = require('home-controller').Insteon
var hub = new Insteon()
var express = require('express')
var app = express()
var fs = require('fs')
var _ = require('underscore')

var websocket = require('ws')
var wss = new websocket.Server({port: 8080})

var configFile = fs.readFileSync('./config.json')
var configJSON = JSON.parse(configFile)
var platformIndex = configJSON.platforms.findIndex(function(item){return item.platform =='InsteonLocal'})
var config = configJSON.platforms[platformIndex]

InsteonServer()

function InsteonServer() {
	var devices = config.devices
	var deviceIDs = []
	var deviceJSON = []

	devices.forEach(function(device){
		deviceIDs.push(device.deviceID)
	})

	devices.forEach(function(device){
		var devJSON = {name: device.name, deviceID: device.deviceID, dimmable: device.dimmable, deviceType: device.deviceType}
		deviceJSON.push(devJSON)
	})

	var host = config.host
	var port = config.port
	var user = config.user
	var pass = config.pass
	var model = config.model
	var server_port = config.server_port || 3000

	var hubConfig = {
		host: host,
		port: port,
		user: user,
		password: pass
	}

	connectToHub()
	init()

	app.get('/light/:id/on', function(req, res) {
		var id = req.params.id.toUpperCase()
		hub.light(id).turnOn().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/light/:id/off', function(req, res) {
		var id = req.params.id.toUpperCase()
		hub.light(id).turnOff().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/light/:id/faston', function(req, res) {
		var id = req.params.id.toUpperCase()
		hub.light(id).turnOnFast().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/light/:id/fastoff', function(req, res) {
		var id = req.params.id.toUpperCase()
		hub.light(id).turnOffFast().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/light/:id/status', function(req, res) {
		var id = req.params.id
		hub.light(id).level(function(err, level) {
			res.json({
				'level': level
			})
		})
	})

	app.get('/light/:id/level/:targetLevel', function(req, res) {
		var id = req.params.id
		var targetLevel = req.params.targetLevel

		hub.light(id).level(targetLevel).then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/scene/:group/on', function(req, res) {
		var group = parseInt(req.params.group)
		hub.sceneOn(group).then(function(status) {
			if (status.aborted) {
				res.sendStatus(404)
			}
			if (status.completed) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/scene/:group/off', function(req, res) {
		var group = parseInt(req.params.group)
		hub.sceneOff(group).then(function(status) {
			if (status.aborted) {
				res.sendStatus(404)
			}
			if (status.completed) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/links', function(req, res) {
		hub.links(function(err, links) {
			res.json(links)
		})
	})

	app.get('/links/:id', function(req, res) {
		var id = req.params.id
		hub.links(id, function(err, links) {
			res.json(links)
		})
	})

	app.get('/info/:id', function(req, res) {
		var id = req.params.id
		hub.info(id, function(err, info) {
			res.json(info)
		})
	})

	app.get('/iolinc/:id/relay_on', function(req, res) {
		var id = req.params.id
		hub.ioLinc(id).relayOn().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/iolinc/:id/relay_off', function(req, res) {
		var id = req.params.id
		hub.ioLinc(id).relayOff().then(function(status) {
			if (status.response) {
				res.sendStatus(200)

			} else {
				res.sendStatus(404)
			}
		})
	})

	app.get('/iolinc/:id/sensor_status', function(req, res) {
		var id = req.params.id
		hub.ioLinc(id).status(function(err, status) {
			res.json(status.sensor)
		})
	})

	app.get('/iolinc/:id/relay_status', function(req, res) {
		var id = req.params.id
		hub.ioLinc(id).status(function(err, status) {
			res.json(status.relay)
		})
	})

	app.listen(server_port)

	function connectToHub() {
		console.log('Model: ' + model)

		if (model == '2245') {
			console.log('Connecting to Insteon Model 2245 Hub...')
			hub.httpClient(hubConfig, function() {
				console.log('Connected to Insteon Model 2245 Hub...')
				connectedToHub = true
			})
		} else if (model == '2243') {
			console.log('Connecting to Insteon "Hub Pro" Hub...')
			connectingToHub = true
			hub.serial('/dev/ttyS4',{baudRate:19200}, function() {
				console.log('Connected to Insteon "Hub Pro" Hub...')
				connectedToHub = true

			})
		} else if (model == '2242') {
			console.log('Connecting to Insteon Model 2242 Hub...')
			hub.connect(host, function() {
				console.log('Connected to Insteon Model 2242 Hub...')
				connectedToHub = true
			})
		} else {
			console.log('Connecting to Insteon PLM...')
			hub.serial(host,{baudRate:19200}, function() {
				console.log('Connected to Insteon PLM...')
				connectedToHub = true
			})
		}
	}

	function init() {
		console.log('Initiating websocket...')
		var message

		wss.on('connection', function (ws) {
			console.log('Client connected to websocket')
			ws.isAlive = true

			ws.on('close', function(){
				console.log('Websocket closed by client')
				ws.isAlive = false
			})

			ws.send('Connected to Insteon Server')

			ws.on('message', function (message) {
				if(message == 'getDevices'){
					console.log(deviceJSON)
					if(ws.isAlive){ws.send(JSON.stringify(deviceJSON))}
				}
			})

			devices.forEach(function(device){
				switch (device.deviceType) {
				case 'doorsensor':
				case 'windowsensor':
				case 'contactsensor':
				case 'motionsensor':


					device.door = hub.door(device.deviceID)

					device.door.on('opened', function(){
						console.log('Got open for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'open'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					device.door.on('closed', function(){
						console.log('Got closed for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'closed'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					break

					case 'leaksensor':
					device.leak = hub.leak(device.deviceID)

					device.leak.on('dry', function(){
						console.log('Got dry for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'dry'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					device.leak.on('wet', function(){
						console.log('Got wet for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'wet'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					break

					case 'motionsensor':
					device.motion = hub.motion(device.deviceID)

					device.motion.on('inactive', function(){
						console.log('Got inactive for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'inactive'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					device.motion.on('active', function(){
						console.log('Got active for ' + device.name)
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 'active'}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					break

				case 'switch':
					device.light = hub.light(device.deviceID)

					device.light.on('turnOn', function (group, level) {
						console.log(device.name + ' turned on')
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: level}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					device.light.on('turnOff', function () {
						console.log(device.name + ' turned off')
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: 0}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})

					break

				case 'lightbulb':
				case 'dimmer':
					device.light = hub.light(device.deviceID)
					device.light.level().then(function(level) {
						message = {name: device.name, id: device.deviceID, deviceType: device.deviceType, state: level}
						if(ws.isAlive){ws.send(JSON.stringify(message))}
					})
					break
				}
			})

			eventListener()

			function eventListener() {
				console.log('Insteon event listener started...')

				hub.on('command', function(data) {

					if (typeof data.standard !== 'undefined') {
						//console.log('Received command for ' + data.standard.id)

						var info = JSON.stringify(data)
						var id = data.standard.id.toUpperCase()
						var command1 = data.standard.command1
						var command2 = data.standard.command2
						var messageType = data.standard.messageType

						var isDevice = _.contains(deviceIDs, id, 0)
						var message

						if (isDevice) {
							var foundDevices = devices.filter(function(item) {
								return item.deviceID == id
							})

							console.log('Found ' + foundDevices.length + ' accessories matching ' + id)
							console.log('Hub command: ' + info)

							for (var i = 0, len = foundDevices.length; i < len; i++) {
								var foundDevice = foundDevices[i]
								console.log('Got event for ' + foundDevice.name + ' (' + foundDevice.deviceID + ')')

								switch (foundDevice.deviceType) {
								case 'lightbulb':
								case 'dimmer':
									if (command1 == '19' || command1 == '03' || command1 == '04' || (command1 == '00' && command2 != '00') || (command1 == '06' && messageType == '1')) { //19 = status
										var level_int = parseInt(command2, 16) * (100 / 255)
										var level = Math.ceil(level_int)

										console.log('Got updated status for ' + foundDevice.name)
										message = {name: foundDevice.name, id: foundDevice.deviceID, deviceType: foundDevice.deviceType, state: level}
										if(ws.isAlive){ws.send(JSON.stringify(message))}
									}

									if (command1 == 11) { //11 = on
										var level_int = parseInt(command2, 16)*(100/255)
										var level = Math.ceil(level_int)

										console.log('Got on event for ' + foundDevice.name)
										message = {name: foundDevice.name, id: foundDevice.deviceID, deviceType: foundDevice.deviceType, state: level}
										if(ws.isAlive){ws.send(JSON.stringify(message))}
									}

									if (command1 == 12) { //fast on
										console.log('Got fast on event for ' + foundDevice.name)
										message = {name: foundDevice.name, id: foundDevice.deviceID, deviceType: foundDevice.deviceType, state: 100}
										if(ws.isAlive){ws.send(JSON.stringify(message))}
									}

									if (command1 == 13 || command1 == 14) { //13 = off, 14= fast off
										if (command1 == 13) {
											console.log('Got off event for ' + foundDevice.name)
										} else {console.log('Got fast off event for ' + foundDevice.name)}
										message = {name: foundDevice.name, id: foundDevice.deviceID, deviceType: foundDevice.deviceType, state: 0}
										if(ws.isAlive){ws.send(JSON.stringify(message))}
									}

									if (command1 == 18) { //stop dimming
										console.log('Got dim event for ' + foundDevice.name)
										foundDevice.light.level().then(function(level) {
											message = {name: foundDevice.name, id: foundDevice.deviceID, deviceType: foundDevice.deviceType, state: level}
											if(ws.isAlive){ws.send(JSON.stringify(message))}
										})
									}

									break
								}
							}
						}
					}
				})
			}
		}
		)}
}

ws parent (with motion sensor - no keypad support)

/**
 *  Insteon WS Parent
 *
 *  Copyright 2019 Chris Wilson
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  
 *  
 *
 *  Original Author     : ethomasii@gmail.com
 *  Creation Date       : 2013-12-08
 *
 *  Rewritten by        : idealerror
 *  Last Modified Date  : 2016-12-13 
 *
 *  Rewritten by        : kuestess
 *  Last Modified Date  : 2017-09-30
 * 
 *  Hubitat port by    @cwwilson08
 *  Last Modified Date  : 2019-06-24
 *
 * 
 *  Changelog:
 *  2019-06-24: Merge functions of original Insteon Direct dimmer code with WS so all calls are completed by parent device
 *  2019-06-23: Convert to async http calls - major code cleanup
 *  2019-06-23: Utilize ogiewon's websocket reconncet code
 *  2018-10-01: Added ability to disable auto refresh in driver
 *  2018-09-14: Added Fast ON/OFF & Refresh setting in driver
 *  2018-09-09: Initial release for Hubitat Elevation Hub
 *  2016-12-13: Added polling for Hub2
 *  2016-12-13: Added background refreshing every 3 minutes
 *  2016-11-21: Added refresh/polling functionality
 *  2016-10-15: Added full dimming functions
 *  2016-10-01: Redesigned interface tiles
 * 
 */


import hubitat.helper.InterfaceUtils
import groovy.json.JsonSlurper

metadata {
definition (name: "Insteon WS Parent", namespace: "cw", author: "Chris Wilson") {
    capability "Initialize"
    capability "Switch"
    attribute "connection", ""
    
}
}

preferences {
input("ip", "text", title: "Websocket IP Address", description: "Websocket IP Address", required: true)
	input("wsPort", "text", title: "WS Port", description: "Websocket Port", required: true)
    input("host", "text", title: "URL", description: "The URL of your Insteon Hub (without http:// example: my.hub.com ")
    input("instPort", "text", title: "Insteon Hub Port", description: "The insteon hub port.")
    input("username", "text", title: "Username", description: "The hub username (found in app)")
    input("password", "text", title: "Password", description: "The hub password (found in app)")
    input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
}


def parse(String description) {
    if (logEnable)log.debug "data from websocketparsed here"
    if (logEnable)log.debug "description: ${description}"
    if (description.startsWith("Connected to Insteon Server")) {
    sendEvent(name: "connection", value: "connected")
		return
   }
	

	def jsonSlurper = new JsonSlurper()
	def msg = jsonSlurper.parseText(description)

if (msg.dimmable){
    createDevices(msg)
    return
}

if (msg.id != null){
    if(state.childDevice.contains(msg.id)){
	child = getChildDevice(msg.id)
       // log.debug "Got event for Device ${child} with a value of ${msg.state}"   
if (msg.deviceType == "motionsensor") {childMotion(child, msg.state)}
if (msg.deviceType == "contactsensor") {childContact(child, msg.state)}
if (msg.deviceType == "dimmer") {childDimmer(child, msg.state)}
if (msg.deviceType == "lightbulb") {childDimmer(child, msg.state)}        
if (msg.deviceType == "leaksensor") {childLeakSensor(child, msg.state)}

                                          } 
 }
}

def childMotion(child, state){
if (logEnable) log.debug "child motion = ${child}"
if (logEnable) log.debug "child state = ${state}"
child.sendEvent(name: "motion", value: state)}

                                                          
def childContact(child, state){
if (logEnable) log.debug "child conact = ${child}"
if (logEnable) log.debug "child state = ${state}"
child.sendEvent(name: "contact", value: state)}

def childLeakSensor(child, state){
if (logEnable) log.debug "child leak = ${child}"
if (logEnable)log.debug "child state = ${state}"
child.sendEvent(name: "water", value: state)}


def childDimmer(child, state){
if (state == 0){
    child.sendEvent(name: "level", value: state)
    child.sendEvent(name: "switch", value: "off")
}
   
if (state > 0){
   if (logEnable) log.debug state
    child.sendEvent(name: "level", value: state)
    child.sendEvent(name: "switch", value: "on")
}
}


def initialize() {
   log.info "initialize() called"
   if (!ip) {
    log.warn "Please enter an IP"
    return
}

	try {
		
   
		InterfaceUtils.webSocketConnect(device, "ws://${ip}:${wsPort}")
} 
catch(e) {
    if (logEnable) log.debug "initialize error: ${e.message}"
    log.error "WebSocket connect failed"
    sendEvent(name: "connection", value: "disconnected")
}

}

def updated() {
log.info "updated() called"
//Unschedule any existing schedules
unschedule()

//Create a 30 minute timer for debug logging
if (logEnable) runIn(1800,logsOff)

//Connect the webSocket
    initialize()
    runIn(2, listChild)
    runIn(4, getDevices)
    
}

def logsOff(){
log.warn "debug logging disabled..."
device.updateSetting("logEnable",[value:"false",type:"bool"])
}


def getDevices() {
	if (logEnable) log.debug "getDevices sent"
	sendMsg ("getDevices")
}


def createDevices(msg){ msg.each { it->
    namespace = "cw"
    if (it.deviceType == "motionsensor") {type = "Insteon Motion Child"}
	if (it.deviceType == "contactsensor") {type = "Insteon Contact Child"}
    if (it.deviceType == "dimmer") {type = "Insteon Dimmer Child"}
    if (it.deviceType == "leaksensor") {type = "Insteon Leak Child"}
    if (it.deviceType == "lightbulb") {type = "Insteon Dimmer Child"}
    if (logEnable)log.debug type
    if (logEnable)log.debug it.deviceID
    if (logEnable)log.debug it.name
    if(!state.childDevice.contains(it.deviceID)){
    log.debug "creating device: ${it.name}"
	addChildDevice (namespace, type, it.deviceID, [label: it.name, isComponent: false, name: type])
}

}
listChild()

}
def listChild (){
def myMap =[]
	if (logEnable)log.debug "listChild called"
	childDevices.each{ it ->
        if (logEnable)log.debug "child: ${it.deviceNetworkId}"
        myMap << it.deviceNetworkId
        
        
}
if (logEnable)log.debug myMap
state.childDevice = myMap

}

def sendMsg(String s) {
InterfaceUtils.sendWebSocketMessage(device, s)
}



def webSocketStatus(String status){
if (logEnable)log.debug "webSocketStatus- ${status}"

if(status.startsWith('failure: ')) {
    log.warn("failure message from web socket ${status}")
    sendEvent(name: "connection", value: "disconnected")
    reconnectWebSocket()
} 
else if(status == 'status: open') {
    sendEvent(name: "connection", value: "connected")    
    log.info "websocket is open"
    if (logEnable)log.debug "Resetting reconnect delay"
    // success! reset reconnect delay
    pauseExecution(1000)
    state.reconnectDelay = 1
} 
else if (status == "status: closing"){
    log.warn "WebSocket connection closing."
    sendEvent(name: "connection", value: "disconnected")
} 
else {
    log.warn "WebSocket error, reconnecting."
    reconnectWebSocket()
}
}

def reconnectWebSocket() {
// first delay is 2 seconds, doubles every time
state.reconnectDelay = (state.reconnectDelay ?: 1) * 2
// don't let delay get too crazy, max it out at 10 minutes
if(state.reconnectDelay > 600) state.reconnectDelay = 600

   
runIn(state.reconnectDelay, initialize)
}


////control functions here


def setLevel(value, deviceid) {

    if (logEnable)log.debug "setLevel >> value: $value"
    
    // Max is 255
    def percent = value / 100
    def realval = percent * 255
    def valueaux = realval as Integer
    def level = Math.max(Math.min(valueaux, 255), 0)
    if (level > 0) {
        sendEvent(name: "switch", value: "on")
    } else {
        sendEvent(name: "switch", value: "off")
    }
    if (logEnable)log.debug "dimming value is $valueaux"
    if (logEnable)log.debug "dimming to $level"
    dim(level,value, deviceid)
}

def setLevel(value, rate, deviceid) {

    if (logEnable)log.debug "setLevel >> value: $value"
    
    // Max is 255
    def percent = value / 100
    def realval = percent * 255
    def valueaux = realval as Integer
    def level = Math.max(Math.min(valueaux, 255), 0)
    if (level > 0) {
        sendEvent(name: "switch", value: "on")
    } else {
        sendEvent(name: "switch", value: "off")
    }
    if (logEnable)log.debug "dimming value is $valueaux"
    if (logEnable)log.debug "dimming to $level"
    dim(level,value)
}

def dim(level, real, deviceid) {
    String hexlevel = level.toString().format( '%02x', level.toInteger() )
   
    if (logEnable)log.debug "Dimming Device: ${deviceid} to hex $hexlevel"
    sendCmd("11",hexlevel, deviceid)
}

def sendCmd(num, level, deviceid){
    
    def requestParams = [ uri: "http://${settings.username}:${settings.password}@${settings.host}:${settings.instPort}//3?0262${deviceid}0F${num}${level}=I=3" ]
    if(logEnable)log.debug requestParams
    if(logEnable)log.debug "Sending command to Device: ${device} with parameters Command: ${num} Level ${level}"
	asynchttpGet("cmdHandler", requestParams)
}

def cmdHandler(resp, data) {
	if(resp.getStatus() == 200 || resp.getStatus() == 207) {
        if (logEnable) "Command Sent successfully"
    
        } else {
        
		    log.error "Error sending command to device"
	}
}

motion sensor child

/**
 *  Insteon Motion Child
 *
 *  Copyright 2022 Logname
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  
 *
 
 * 
 */

metadata {
	definition (name: "Insteon Motion Child", namespace: "ln", author: "logname") {
		
		capability "Motion Sensor"
        command "active"
        command "inactive"
        			
	}   
}


def installed() {
}

def open() {
    sendEvent(name: "motion", value: "active")
}

def close() {
    sendEvent(name: "motion", value: "inactive")
}

[Update 6/7/2022] Insteon servers are back online. Hurry up and make your hub and device changes!

1 Like

What?!!!!! She lives!!!

1 Like

It might not last. Insteon users should get while the gettin's good, then go local for life.

1 Like

OK, so I tried to get this running on a Pi had lying around.
when Itry to strat the insteonserver.js I get the following error.
Model: 2242
Connecting to Insteon Model 2242 Hub...
Initiating websocket...
Connected to Insteon Model 2242 Hub...
(node:1303) UnhandledPromiseRejectionWarning: ReferenceError: connectedToHub is not defined at /usr/local/lib/node_modules/insteon-server/insteonserver.js:232:20
Any clues ??

I tried a little google help, but it's greek to me. My guess here is there's something not right in the insteonserver.js file. Maybe a formatting issue if you copied and pasted, spacing, missing brackets, etc. I would try getting the file again and replace with the one you have. I use Atom text editor. I would highly recommend that so you don't get formatting issues, especially if you're on an older Mac, that can be an issue.

Hope this help.

tl;dr
2242 Hub Working

You can send and receive Insteon HTTP messages on port 25105 on the 2242 hubs, without including the user name and password. The hubs still work with other home automation programs using port 9761 but that port does not work for this project.

Connecting on port 9761 did not work on either of my 2242 hubs. Both work with HouseLinc and Home Assistant using the default 2242 config (port 9761) but both hubs (V2.0 and V2.2) also have port 25105 printed on the label.

Port 25105 seems to be an early-version browser port for these hubs. The original "Insteon for Hub" app uses an email address for the user name. I could not clear this, even with a hard reset. The "@" character in the user name causes problems.

Note: If you have a default user/password (admin/hubID) on your hub, first try defining it as a 2245 in config.json. It looks like it should work but it did not for me because the Insteon browser user name (email address) included the @ character.

How-to:
Verify connection to the 2242 hub using curl. Determine if your hub requires a user name and password.
curl http://192.168.1.100:25105/buffstatus.xml
or
curl http://user:password@192.168.1.100:25105/buffstatus.xml

If the hub does not require a user/password, modify the insteonserver.js file as follows:

Locate this variable definition:
var hubConfig = {
host: host,
port: port,
user: user,
password: pass
}

Define a new variable:
var hub2242Config = {
host: host,
port: port
}

Locate the connect function for the 2242 hub. It should look like this.
} else if (model == '2242') {
console.log('Connecting to Insteon Model 2242 Hub...')
hub.connect(host, function() {
console.log('Connected to Insteon Model 2242 Hub...')
connectedToHub = true
})

Change the function to use the same HTTP connect method as the 2245 hub but use the hub2242Config variable.
} else if (model == '2242') {
console.log('Connecting to Insteon Model 2242 Hub...')
hub.httpClient(hub2242Config, function() {
console.log('Connected to Insteon Model 2242 Hub...')
connectedToHub = true
})

Please post if you have made this work on the 2242 hub in a different way.

2 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.