Created
January 5, 2020 20:16
-
-
Save bdwilson/4fa4621fd2ba782e6a037dd5a26c921b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Neo Smart Controller | |
* | |
* Calls URIs with HTTP GET for shade open/close/stop/favourite using the Neo Smart Controller | |
* | |
* Based on the Hubitat community driver httpGetSwitch | |
*/ | |
metadata { | |
definition(name: "Neo Smart Controller-alpha", namespace: "bigrizzo", author: "bigrizz", importUrl: "https://raw.githubusercontent.com/bdwilson/hubitatDrivers/master/NeoSmart.groovy") { | |
capability "WindowShade" | |
capability "Switch" | |
capability "Actuator" | |
capability "ChangeLevel" | |
capability "Switch Level" | |
command "stop" | |
command "favorite" | |
command "up" | |
command "down" | |
} | |
} | |
preferences { | |
section("URIs") { | |
input "blindCode", "text", title: "Blind or Room Code (from Neo App)", required: true | |
input "controllerID", "text", title: "Controller ID (from Neo App)", required: true | |
input "controllerIP", "text", title: "Controller IP Address (from Neo App)", required: true | |
input "timeToClose", "number", title: "Time in seconds it takes to close the blinds completely", required: true | |
input "timeToFav", "number", title: "Time in seconds it takes to reach your favorite setting when closing the blinds", required: true | |
input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true | |
} | |
} | |
def date() { | |
//def date = new Date().getTime().toString().drop(6) | |
def origdate = new Date().getTime().toString().drop(6) // API docs say only 7 chars | |
def random = Math.random().toString().reverse().take(4) // get four random #'s | |
log.debug "origdate: ${origdate} random: ${random}" | |
def date = origdate.toInteger() + random.toInteger() // add 4 random #'s to millisecs | |
//if (logEnable) log.debug "Using ${date}" | |
return date | |
} | |
def get(url,state) { | |
log.debug "Call to ${url}; setting ${state}" | |
try { | |
httpGet(url) { resp -> | |
if (resp.success) { | |
sendEvent(name: "windowShade", value: "${state}", isStateChange: true) | |
} | |
if (logEnable) | |
if (resp.data) log.debug "${resp.data}" | |
} | |
} catch (Exception e) { | |
log.warn "Call to ${url} failed: ${e.message}" | |
} | |
} | |
def logsOff() { | |
log.warn "debug logging disabled..." | |
device.updateSetting("logEnable", [value: "false", type: "bool"]) | |
} | |
def installed() { | |
log.info "installed..." | |
if (!controllerID || !controllerIP || !blindCode || !timeToClose || !timeToFav) { | |
log.error "Please make sure controller ID, IP, blind/room code, time to close and time to favorite are configured." | |
} | |
log.warn "debug logging is: ${logEnable == true}" | |
if (logEnable) runIn(1800, logsOff) | |
} | |
def updated() { | |
log.info "updated..." | |
if (!controllerID || !controllerIP || !blindCode || !timeToClose || !timeToFav) { | |
log.error "Please make sure controller ID, IP, blind/room code, time to close and time to favorite are configured." | |
} | |
log.warn "debug logging is: ${logEnable == true}" | |
if (logEnable) runIn(1800, logsOff) | |
} | |
def parse(String description) { | |
if (logEnable) log.debug(description) | |
} | |
def up() { | |
startLevelChange("up") | |
} | |
def down() { | |
startLevelChange("down") | |
} | |
def on() { | |
open() | |
} | |
def off() { | |
close() | |
} | |
def close() { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-dn&id=" + controllerID + "&hash=" + date() | |
if (logEnable) log.debug "Sending close GET request to ${url}" | |
sendEvent(name: "windowShade", value: "closing", isStateChange: true) | |
UpdateTimeRunning() | |
if (state.lastCmd != "stop") { // someone went from open to close without stopping. how do we deal? | |
} | |
get(url,"closed") | |
state.level=0 | |
state.secs=timeToClose // this is being set before we know it's closed... | |
state.lastCmd = close | |
sendEvent(name: "level", value: "${state.level}", isStateChange: true) | |
} | |
def open() { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-up&id=" + controllerID + "&hash=" + date() | |
if (logEnable) log.debug "Sending open GET request to ${url}" | |
sendEvent(name: "windowShade", value: "opening", isStateChange: true) | |
UpdateTimeRunning() | |
if (state.lastCmd != "stop") { // someone went from close to open without stopping. how do we deal? | |
} | |
get(url,"open") | |
state.level=100 | |
state.secs=0 | |
state.lastCmd = open | |
sendEvent(name: "level", value: "${state.level}", isStateChange: true) | |
} | |
def stop() { | |
// todo, it would be nice if we could start a timer when someone opens/closes so if they stop we have an idea of where the shade is | |
// then we could reflect it by setting the level here... | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-sp&id=" + controllerID + "&hash=" + date() | |
if (logEnable) log.debug "Sending stop GET request to ${url}" | |
get(url,"partially open") | |
UpdateTimeRunning() | |
state.lastCmd = stop | |
state.level=100-((state.secs/timeToClose)*100) | |
} | |
def UpdateTimeRunning() { | |
def now = new Date().getTime() | |
if (state.stateChangeTime) { | |
def timeRunning = now.toInteger()-state.stateChangeTime.toInteger() | |
log.debug "Found that ${now} - ${state.stateChangeTime} = ${timeRunning}" | |
def timeRunningSecs = timeRunning/1000 // convert MS to Secs | |
} | |
// if state.stateChangeTime is beyond how long it takes to open/close completely | |
// then we'll assume it's already been successful at that open/close command. | |
if (timeRunningSecs > timeToClose) { | |
log.debug "Found that ${timeRunningSecs} was greater than ${timeToClose}" | |
log.debug "Resetting state.stateChangeTime = ${now}, state.timeRan=0" | |
state.stateChangeTime = now | |
state.timeRan=0 | |
} else if ((state.stateChangeTime < now) && (timeRunningSecs)) { | |
//def timeRunning = now - state.stateChangeTime.toInteger() | |
state.timeRan = timeRunningSecs | |
log.debug "UpdateTimeRunning- Time Running: ${timeRunningSecs} Old State.secs: ${state.secs}" | |
if ((state.secs != 0) && (state.secs != timeToClose)) { // | |
if (state.lastCmd == "close") { | |
state.secs=timeRunningSecs + state.secs | |
} else if (state.lastCmd == "open") { | |
state.secs=state.secs - timeRunningSecs | |
} | |
} | |
log.debug "UpdateTimeRunning- Time Running: ${timeRunningSecs} New State.secs: ${state.secs}" | |
} else { | |
log.debug "Setting state.stateChangeTime = ${now}" | |
state.stateChangeTime = now | |
} | |
} | |
def stopPosition() { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-sp&id=" + controllerID + "&hash=" + date() | |
if (logEnable) log.debug "Sending stop GET request to ${url}" | |
get(url,"partially open") | |
sendEvent(name: "level", value: "${state.newposition}", isStateChange: true) | |
log.debug "Stopped ${state.newposition} ${state.difference}" | |
state.level=state.newposition | |
state.difference=0 | |
} | |
def runAndStop() { | |
if (state.direction == "up") { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-up&id=" + controllerID + "&hash=" + date() | |
} else { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-dn&id=" + controllerID + "&hash=" + date() | |
} | |
//if (logEnable) log.debug "Adjusting ${direction} to ${position} for ${secs} request to ${url}" | |
log.debug "Adjusting ${state.direction} to ${state.newposition} for ${state.difference} request to ${url}" | |
get(url,"partially open") | |
runInMillis(state.difference.toInteger(),stopPosition) | |
} | |
def favorite() { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-gp&id=" + controllerID + "&hash=" + date() | |
if (logEnable) log.debug "Sending favorite GET request to ${url}" | |
state.secs=timeToFav | |
state.level=100-((timeToFav/timeToClose)*100) | |
get(url,"partially open") | |
sendEvent(name: "level", value: "${state.level}", isStateChange: true) | |
} | |
def setPosition(position) { // timeToClose= closed/down, 0=open/up | |
//log.debug "Opening to ${blindCode} to ${position}" | |
secs = timeToClose-((position/100)*timeToClose) // get percentage based on how long it takes to open. | |
log.debug "Setting Position for ${blindCode} to ${position} (maps to travel time from open of ${secs} secs)" | |
if (secs >= timeToClose) { | |
secs = timeToClose | |
} | |
if (position != state.level) { | |
state.lastCmd="partial" | |
log.debug "Position: ${position} StateLevel: ${state.level} StateSecs: ${state.secs} Secs: ${secs} Last Cmd: ${partial}" | |
if ((position > state.level) || (state.secs > secs)) { // requested location is more closed than current. | |
if (position == 100) { | |
open() | |
state.level=100 | |
state.secs=0 | |
} else { | |
pos = ((state.secs - secs) * 1000) //-2000 | |
if (pos < 1000) { | |
pos = 1000 | |
} | |
state.direction = "up" | |
state.difference = pos | |
state.newposition = position | |
state.secs = secs | |
log.debug "Opening... Stopping at ${pos} secs" | |
runInMillis(10,runAndStop) | |
} | |
} else { // location is less closed than current | |
if (position == 0) { | |
close() | |
state.level=0 | |
state.secs=timeToClose | |
} else { | |
def pos = ((secs - state.secs)*1000) //-2000 | |
if (pos < 1000) { | |
pos = 1000 | |
} | |
state.direction = "down" | |
state.difference = pos | |
state.newposition = position | |
state.secs = secs | |
log.debug "Closing... Stopping at ${pos} secs" | |
runInMillis(10,runAndStop) | |
} | |
} | |
} | |
} | |
def startLevelChange(direction) { | |
// https://github.com/hubitat/HubitatPublic/blob/master/examples/drivers/genericComponentDimmer.groovy | |
if (direction == "up") { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-mu&id=" + controllerID + "&hash=" + date() | |
} else { | |
url = "http://" + controllerIP + ":8838/neo/v1/transmit?command=" + blindCode + "-md&id=" + controllerID + "&hash=" + date() | |
} | |
if (logEnable) log.debug "Sending startLevel Change ${direction} GET request to ${url}" | |
get(url,"partially open") | |
} | |
def setLevel(level) { | |
setPosition(level) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment