This is not true. There was no function to poll the MyQ servers for the door status in the old app/driver.
There was in my version (I know these things, because I wrote these things...)
You wrote the old Hubitat version?
Can you point to me the section of the app that polls the MyQ server for the door status?
/**
* MyQ Lite
*
* Copyright 2017 Jason Mok/Brian Beaird/Barry Burke/Thomas Howard
*
* 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.
*
* Last Updated : 04/08/2019
* SmartApp version: 3.0.2
* Door device version: 2.2.5
* Door-no-sensor device version: 1.1.9
* Light device version: not provided/tested.
*/
definition(
name: "MyQ Lite",
namespace: "tchoward",
author: "Jason Mok/Brian Beaird/Barry Burke/Thomas Howard/Brian Wilson",
description: "Integrate MyQ with Hubitat",
category: "SmartThings Labs",
iconUrl: "https://raw.githubusercontent.com/brbeaird/SmartThings_MyQ/master/icons/myq.png",
iconX2Url: "https://raw.githubusercontent.com/brbeaird/SmartThings_MyQ/master/icons/myq@2x.png",
iconX3Url: "https://raw.githubusercontent.com/brbeaird/SmartThings_MyQ/master/icons/myq@3x.png"
)
preferences {
page(name: "prefLogIn", title: "MyQ")
page(name: "prefListDevices", title: "MyQ")
page(name: "prefSensor1", title: "MyQ")
page(name: "prefSensor2", title: "MyQ")
page(name: "prefSensor3", title: "MyQ")
page(name: "prefSensor4", title: "MyQ")
page(name: "prefSensor5", title: "MyQ")
page(name: "prefSensor6", title: "MyQ")
page(name: "summary", title: "MyQ")
}
/* Preferences */
def prefLogIn() {
state.previousVersion = state.thisSmartAppVersion
if (state.previousVersion == null){
state.previousVersion = 0;
}
state.thisSmartAppVersion = "3.0.2"
state.installMsg = ""
def showUninstall = username != null && password != null
return dynamicPage(name: "prefLogIn", title: "Connect to MyQ", nextPage:"prefListDevices", uninstall:showUninstall, install: false) {
section("Login Credentials"){
input("username", "email", title: "Username", description: "MyQ Username (email address)")
input("password", "password", title: "Password", description: "MyQ password")
}
section("Gateway Brand"){
input(name: "brand_t", title: "Gateway Brand", type: "enum", , options: [[1:"Liftmaster"],[2:"Chamberlain"],[3:"Craftsman"]] )
}
settings.brand = getBrandName(settings.brand_t)
}
}
def prefListDevices() {
getVersionInfo(0, 0);
getSelectedDevices("lights")
if (forceLogin()) {
def doorList = getDoorList()
if ((state.doorList) || (state.lightList)){
ifDebug(state.doorList)
def nextPage = "prefSensor1"
if (!state.doorList){nextPage = "summary"} //Skip to summary if there are no doors to handle
return dynamicPage(name: "prefListDevices", title: "Devices", nextPage:nextPage, install:false, uninstall:true) {
if (state.doorList) {
section("Select which garage door/gate to use") {
input("doors", "enum", required:false, multiple:true, options:state.doorList)
input("priority", "enum", title: "Default Message Priority (Blank = NORMAL):", description: "", defaultValue: "0", options:[["-1":"LOW"], ["0":"NORMAL"], ["1":"HIGH"]])
}
}
if (state.lightList) {
section("Select which lights to use"){
input(name: "lights", type: "enum", required:false, multiple:true, metadata:[values:state.lightList])
}
}
}
}else {
def devList = getDeviceList()
return dynamicPage(name: "prefListDevices", title: "Error!", install:false, uninstall:true) {
section(""){
paragraph "Could not find any supported device(s). Please report to author about these devices: " + devList
}
}
}
} else {
return dynamicPage(name: "prefListDevices", title: "Error!", install:false, uninstall:true) {
section(""){
paragraph "The username or password you entered is incorrect. Try again. "
}
}
}
}
def prefSensor1() {
ifDebug("Doors chosen: " + doors)
//Set defaults
def nextPage = "summary"
def titleText = ""
//Determine if we have multiple doors and need to send to another page
if (doors instanceof String){ //simulator seems to just make a single door a string. For that reason we have this weird check.
ifDebug("Single door detected (string).")
titleText = "Select Sensors for Door 1 (" + state.data[doors].name + ")"
}
else if (doors.size() == 1){
ifDebug("Single door detected (array).")
titleText = "Select Sensors for Door 1 (" + state.data[doors[0]].name + ")"
}
else{
ifDebug("Multiple doors detected.")
nextPage = "prefSensor2"
titleText = "OPTIONAL: Select Sensors for Door 1 (" + state.data[doors[0]].name + ")"
}
return dynamicPage(name: "prefSensor1", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
paragraph "Optional: If you have sensors on this door, select them below. A sensor allows the device type to know whether the door is open or closed, which helps the device function " +
"as a switch you can turn on (to open) and off (to close)."
input(name: "door1Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: false, multiple: false)
input(name: "door1Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have separate additional On and Off push button devices created. This is recommened if you have no sensors but still want a way to open/close the " +
"garage from SmartTiles and other interfaces like Google Home that can't function with the built-in open/close capability. See wiki for more details."
input "prefDoor1PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def prefSensor2() {
def nextPage = "summary"
def titleText = "Sensors for Door 2 (" + state.data[doors[1]].name + ")"
if (doors.size() > 2){
nextPage = "prefSensor3"
}
return dynamicPage(name: "prefSensor2", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
input(name: "door2Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: false, multiple: false)
input(name: "door2Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have extra on and off push button devices created. This is recommened if you have no sensors but still want a way to open/close the garage from SmartTiles."
input "prefDoor2PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def prefSensor3() {
def nextPage = "summary"
def titleText = "Sensors for Door 3 (" + state.data[doors[2]].name + ")"
if (doors.size() > 3){
nextPage = "prefSensor4"
}
return dynamicPage(name: "prefSensor3", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
input(name: "door3Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: false, multiple: false)
input(name: "door3Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have extra on and off push button devices created. This is recommened if you have no sensors but still want a way to open/close the garage from SmartTiles."
input "prefDoor3PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def prefSensor4() {
def nextPage = "summary"
def titleText = "Contact Sensor for Door 4 (" + state.data[doors[3]].name + ")"
if (doors.size() > 4){
nextPage = "prefSensor5"
}
return dynamicPage(name: "prefSensor4", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
input(name: "door4Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: "false", multiple: "false")
input(name: "door4Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have extra on and off push button devices created. This is recommened if you have no sensors but still want a way to open/close the garage from SmartTiles."
input "prefDoor4PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def prefSensor5() {
def nextPage = "summary"
def titleText = "Contact Sensor for Door 5 (" + state.data[doors[4]].name + ")"
if (doors.size() > 5){
nextPage = "prefSensor6"
}
return dynamicPage(name: "prefSensor5", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
input(name: "door5Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: "false", multiple: "false")
input(name: "door5Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have extra on and off push button devices created. This is recommened if you have no sensors but still want a way to open/close the garage from SmartTiles."
input "prefDoor5PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def prefSensor6() {
def nextPage = "summary"
def titleText = "Contact Sensor for Door 6 (" + state.data[doors[5]].name + ")"
return dynamicPage(name: "prefSensor6", title: "Optional Sensors and Push Buttons", nextPage:nextPage, install:false, uninstall:true) {
section(titleText){
input(name: "door6Sensor", title: "Contact Sensor", type: "capability.contactSensor", required: "false", multiple: "false")
input(name: "door6Acceleration", title: "Acceleration Sensor", type: "capability.accelerationSensor", required: false, multiple: false)
}
section("Create separate on/off push buttons?"){
paragraph "Choose the option below to have extra on and off push button devices created. This is recommened if you have no sensors but still want a way to open/close the garage from SmartTiles."
input "prefDoor6PushButtons", "bool", required: false, title: "Create on/off push buttons?"
}
}
}
def summary() {
state.installMsg = ""
state.isDebug = isDebug
initialize()
versionCheck()
return dynamicPage(name: "summary", title: "Summary", install:true, uninstall:true) {
section("Installation Details:"){
paragraph state.installMsg
paragraph state.versionWarning
}
section("") {
input "isDebug", "bool", title: "Enable Debug Logging", required: false, multiple: false, defaultValue: false, submitOnChange: true
}
}
}
/* Initialization */
def installed() {
}
def updated() {
ifDebug("Updated...")
if (state.previousVersion != state.thisSmartAppVersion){
getVersionInfo(state.previousVersion, state.thisSmartAppVersion);
}
}
def uninstalled() {
getVersionInfo(state.previousVersion, 0);
}
def initialize() {
unsubscribe()
ifDebug("Initializing...")
login()
state.sensorMap = [:]
// Get initial device status in state.data
state.polling = [ last: 0, rescheduler: now() ]
state.data = [:]
// Create selected devices
def doorsList = getDoorList()
def lightsList = state.lightList
if (doors != null){
def firstDoor = doors[0]
//Handle single door (sometimes it's just a dumb string thanks to the simulator)
if (doors instanceof String)
firstDoor = doors
//Create door devices
createChilDevices(firstDoor, door1Sensor, doorsList[firstDoor], prefDoor1PushButtons)
if (doors[1]) createChilDevices(doors[1], door2Sensor, doorsList[doors[1]], prefDoor2PushButtons)
if (doors[2]) createChilDevices(doors[2], door3Sensor, doorsList[doors[2]], prefDoor3PushButtons)
if (doors[3]) createChilDevices(doors[3], door4Sensor, doorsList[doors[3]], prefDoor4PushButtons)
if (doors[4]) createChilDevices(doors[4], door4Sensor, doorsList[doors[4]], prefDoor5PushButtons)
if (doors[5]) createChilDevices(doors[5], door4Sensor, doorsList[doors[5]], prefDoor6PushButtons)
}
//Create light devices
def selectedLights = getSelectedDevices("lights")
selectedLights.each {
ifDebug("Checking for existing light: " + it)
if (!getChildDevice(it)) {
ifDebug("Creating child light device: " + it)
try{
addChildDevice("brbeaird", "MyQ Light Controller", it, getHubID(), ["name": lightsList[it]])
state.installMsg = state.installMsg + lightsList[it] + ": created light device. \r\n\r\n"
}
catch(com.hubitat.app.exception.UnknownDeviceTypeException e)
{
ifDebug("Error! " + e)
state.installMsg = state.installMsg + lightsList[it] + ": problem creating light device. Check your IDE to make sure the brbeaird : MyQ Light Controller device handler is installed and published. \r\n\r\n"
}
}
else{
ifDebug("Light device already exists: " + it)
state.installMsg = state.installMsg + lightsList[it] + ": light device already exists. \r\n\r\n"
}
}
// Remove unselected devices
def selectedDevices = [] + getSelectedDevices("doors") + getSelectedDevices("lights")
getChildDevices().each{
//Modify DNI string for the extra pushbuttons to make sure they don't get deleted unintentionally
def DNI = it?.deviceNetworkId
DNI = DNI.replace(" Opener", "")
DNI = DNI.replace(" Closer", "")
if (!(DNI in selectedDevices)){
ifDebug("found device to delete: " + it)
try{
deleteChildDevice(it.deviceNetworkId)
} catch (e){
sendPush("Warning: unable to delete door or button - " + it + "- you'll need to manually remove it.")
ifDebug("Error trying to delete device " + it + " - " + e)
ifDebug("Device is likely in use in a Routine, or SmartApp (make sure and check SmarTiles!).")
}
}
}
//Create subscriptions
if (door1Sensor)
subscribe(door1Sensor, "contact", sensorHandler)
if (door2Sensor)
subscribe(door2Sensor, "contact", sensorHandler)
if (door3Sensor)
subscribe(door3Sensor, "contact", sensorHandler)
if (door4Sensor)
subscribe(door4Sensor, "contact", sensorHandler)
if (door5Sensor)
subscribe(door5Sensor, "contact", sensorHandler)
if (door6Sensor)
subscribe(door6Sensor, "contact", sensorHandler)
if (door1Acceleration)
subscribe(door1Acceleration, "acceleration", sensorHandler)
if (door2Acceleration)
subscribe(door2Acceleration, "acceleration", sensorHandler)
if (door3Acceleration)
subscribe(door3Acceleration, "acceleration", sensorHandler)
if (door4Acceleration)
subscribe(door4Acceleration, "acceleration", sensorHandler)
if (door5Acceleration)
subscribe(door5Acceleration, "acceleration", sensorHandler)
if (door6Acceleration)
subscribe(door6Acceleration, "acceleration", sensorHandler)
//Set initial values
if (door1Sensor)
syncDoorsWithSensors()
}
def createChilDevices(door, sensor, doorName, prefPushButtons){
ifDebug("In CreateChild")
if (door){
//Has door's child device already been created?
def existingDev = getChildDevice(door)
def existingType = existingDev?.typeName
if (existingDev){
ifDebug("Child already exists for " + doorName + ". Sensor name is: " + sensor)
state.installMsg = state.installMsg + doorName + ": door device already exists. \r\n\r\n"
if ((!sensor) && existingType == "MyQ Garage Door Opener"){
ifDebug("Type needs updating to non-sensor version")
//existingDev.deviceType = "MyQ Garage Door Opener-NoSensor2"
state.installMsg = state.installMsg + doorName + ": needs to be changed to No-sensor version. Go remove Sensor Version Driver then re-install." + "\r\n\r\n"
}
if (sensor && existingType == "MyQ Garage Door Opener-NoSensor"){
ifDebug("Type needs updating to sensor version")
//existingDev.deviceType = "MyQ Garage Door Opener"
state.installMsg = state.installMsg + doorName + ": needs to be changed to Sensor version. Go remove No-Sensor Version Driver then re-install." + "\r\n\r\n"
}
}
else{
ifDebug("Creating child door device " + door)
if (sensor){
try{
ifDebug("Creating door with sensor")
addChildDevice("tchoward", "MyQ Garage Door Opener", door, getHubID(), ["name": doorName])
state.installMsg = state.installMsg + doorName + ": created door device (sensor version) \r\n\r\n"
}
catch(com.hubitat.app.exception.UnknownDeviceTypeException e)
{
ifDebug("Error! " + e)
state.installMsg = state.installMsg + doorName + ": problem creating door device (sensor type). Check your IDE to make sure the tchoward : MyQ Garage Door Opener device handler is installed and published. \r\n\r\n"
}
}
else{
try{
ifDebug("Creating door with no sensor")
addChildDevice("tchoward", "MyQ Garage Door Opener-NoSensor", door, getHubID(), ["name": doorName])
state.installMsg = state.installMsg + doorName + ": created door device (no-sensor version) \r\n\r\n"
}
catch(com.hubitat.app.exception.UnknownDeviceTypeException e)
{
ifDebug("Error! " + e)
state.installMsg = state.installMsg + doorName + ": problem creating door device (no-sensor type). Check your IDE to make sure the tchoward : MyQ Garage Door Opener-NoSensor device handler is installed and published. \r\n\r\n"
}
}
}
//Create push button devices
if (prefPushButtons){
def existingOpenButtonDev = getChildDevice(door + " Opener")
def existingCloseButtonDev = getChildDevice(door + " Closer")
if (!existingOpenButtonDev){
try{
def openButton = addChildDevice("smartthings", "Momentary Button Tile", door + " Opener", getHubID(), [name: doorName + " Opener", label: doorName + " Opener"])
state.installMsg = state.installMsg + doorName + ": created push button device. \r\n\r\n"
subscribe(openButton, "momentary.pushed", doorButtonOpenHandler)
}
catch(com.hubitat.app.exception.UnknownDeviceTypeException e)
{
ifDebug("Error! " + e)
state.installMsg = state.installMsg + doorName + ": problem creating push button device. Check your IDE to make sure the smartthings : Momentary Button Tile device handler is installed and published. \r\n\r\n"
}
}
else{
subscribe(existingOpenButtonDev, "momentary.pushed", doorButtonOpenHandler)
state.installMsg = state.installMsg + doorName + ": push button device already exists. Subscription recreated. \r\n\r\n"
ifDebug("subscribed to button: " + existingOpenButtonDev)
}
if (!existingCloseButtonDev){
try{
def closeButton = addChildDevice("smartthings", "Momentary Button Tile", door + " Closer", getHubID(), [name: doorName + " Closer", label: doorName + " Closer"])
subscribe(closeButton, "momentary.pushed", doorButtonCloseHandler)
}
catch(com.hubitat.app.exception.UnknownDeviceTypeException e)
{
ifDebug("Error! " + e)
}
}
else{
subscribe(existingCloseButtonDev, "momentary.pushed", doorButtonCloseHandler)
}
}
//Cleanup defunct push button devices if no longer wanted
else{
def pushButtonIDs = [door + " Opener", door + " Closer"]
ifDebug("ID's to look for: " + pushButtonIDs)
def devsToDelete = getChildDevices().findAll { pushButtonIDs.contains(it.deviceNetworkId)}
ifDebug("button devices to delete: " + devsToDelete)
devsToDelete.each{
ifDebug("deleting button: " + it)
try{
deleteChildDevice(it.deviceNetworkId)
} catch (e){
//sendPush("Warning: unable to delete virtual on/off push button - you'll need to manually remove it.")
state.installMsg = state.installMsg + "Warning: unable to delete virtual on/off push button - you'll need to manually remove it. \r\n\r\n"
ifDebug("Error trying to delete button " + it + " - " + e)
ifDebug("Button is likely in use in a Routine, or SmartApp (make sure and check SmarTiles!).")
}
}
}
}
}
def syncDoorsWithSensors(child){
def firstDoor = doors[0]
//Handle single door (sometimes it's just a dumb string thanks to the simulator)
if (doors instanceof String)
firstDoor = doors
def doorDNI = null
if (child) { // refresh only the requesting door (makes things a bit more efficient if you have more than 1 door
doorDNI = child.device.deviceNetworkId
switch (doorDNI) {
case firstDoor:
updateDoorStatus(firstDoor, door1Sensor, door1Acceleration, door1ThreeAxis, child)
break
case doors[1]:
updateDoorStatus(doors[1], door2Sensor, door2Acceleration, door2ThreeAxis, child)
break
case doors[2]:
updateDoorStatus(doors[2], door3Sensor, door3Acceleration, door3ThreeAxis, child)
break
case doors[3]:
updateDoorStatus(doors[3], door4Sensor, door4Acceleration, door4ThreeAxis, child)
break
case doors[4]:
updateDoorStatus(doors[4], door5Sensor, door5Acceleration, door5ThreeAxis, child)
break
case doors[5]:
updateDoorStatus(doors[5], door6Sensor, door6Acceleration, door6ThreeAxis, child)
}
} else { // refresh ALL the doors
if (firstDoor) updateDoorStatus(firstDoor, door1Sensor, door1Acceleration, door1ThreeAxis, null)
if (doors[1]) updateDoorStatus(doors[1], door2Sensor, door2Acceleration, door2ThreeAxis, null)
if (doors[2]) updateDoorStatus(doors[2], door3Sensor, door3Acceleration, door3ThreeAxis, null)
if (doors[3]) updateDoorStatus(doors[3], door4Sensor, door4Acceleration, door4ThreeAxis, null)
if (doors[4]) updateDoorStatus(doors[4], door5Sensor, door5Acceleration, door5ThreeAxis, null)
if (doors[5]) updateDoorStatus(doors[5], door6Sensor, door6Acceleration, door6ThreeAxis, null)
}
}
def updateDoorStatus(doorDNI, sensor, acceleration, threeAxis, child){
//Get door to update and set the new value
def doorToUpdate = getChildDevice(doorDNI)
def doorName = state.data[doorDNI].name
def value = "unknown"
def moving = "unknown"
def door = doorToUpdate.latestValue("door")
if (acceleration) moving = acceleration.latestValue("acceleration")
if (sensor) value = sensor.latestValue("contact")
if (moving == "active") {
if (value == "open") {
if (door != "opening") value = "closing" else value = "opening" // if door is "open" or "waiting" change to "closing", else it must be "opening"
} else if (value == "closed") {
if (door != "closing") value = "opening" else value = "closed"
}
} else if (moving == "inactive") {
if (door == "closing") {
if (value == "open") { // just stopped but door is still open
value = "stopped"
}
}
}
doorToUpdate.updateDeviceStatus(value)
doorToUpdate.updateDeviceSensor("${sensor} is ${sensor?.currentContact}")
ifDebug("Door: " + doorName + ": Updating with status - " + value + " - from sensor " + sensor)
if (acceleration) {
doorToUpdate.updateDeviceMoving("${acceleration} is ${moving}")
ifDebug("Door: " + doorName + ": Updating with status - " + moving + " - from sensor " + acceleration)
}
//Get latest activity timestamp for the sensor (data saved for up to a week)
def eventsSinceYesterday = sensor.eventsSince(new Date() - 7)
def latestEvent = eventsSinceYesterday[0]?.date
def timeStampLogText = "Door: " + doorName + ": Updating timestamp to: " + latestEvent + " - from sensor " + sensor
if (!latestEvent) //If the door has been inactive for more than a week, timestamp data will be null. Keep current value in that case.
timeStampLogText = "Door: " + doorName + ": Null timestamp detected " + " - from sensor " + sensor + " . Keeping current value."
else
doorToUpdate.updateDeviceLastActivity(latestEvent)
ifDebug(timeStampLogText)
}
def refresh(child){
def door = child.device.deviceNetworkId
def doorName = state.data[door].name
ifDebug("refresh called from " + doorName + ' (' + door + ')')
syncDoorsWithSensors(child)
}
def sensorHandler(evt) {
ifDebug("Sensor change detected: Event name " + evt.name + " value: " + evt.value + " deviceID: " + evt.deviceId)
//If we're seeing vibration sensor values, ignore them if it's been more than 30 seconds after a command was sent.
// This keeps us from seeing phantom entries from overly-sensitive sensors
if (evt.value == "active" || evt.value == "inactive"){
if (state.lastCommandSent == null || state.lastCommandSent > now()-30000){
return 0;
}
}
switch (evt.deviceId) {
case door1Sensor.id:
case door1Acceleration?.id:
def firstDoor = doors[0]
if (doors instanceof String) firstDoor = doors
updateDoorStatus(firstDoor, door1Sensor, door1Acceleration, door1ThreeAxis, null)
break
case door2Sensor?.id:
case door2Acceleration?.id:
updateDoorStatus(doors[1], door2Sensor, door2Acceleration, door2ThreeAxis, null)
break
case door3Sensor?.id:
case door3Acceleration?.id:
updateDoorStatus(doors[2], door3Sensor, door3Acceleration, door3ThreeAxis, null)
break
case door4Sensor?.id:
case door4Acceleration?.id:
updateDoorStatus(doors[3], door4Sensor, door4Acceleration, door4ThreeAxis, null)
break
case door5Sensor?.id:
case door5Acceleration?.id:
updateDoorStatus(doors[4], door5Sensor, door5Acceleration, door5ThreeAxis, null)
break
case door6Sensor?.id:
case door6Acceleration?.id:
updateDoorStatus(doors[5], door6Sensor, door6Acceleration, door6ThreeAxis, null)
break
default:
syncDoorsWithSensors()
}
}
def doorButtonOpenHandler(evt) {
ifDebug("Door open button push detected: Event name " + evt.name + " value: " + evt.value + " deviceID: " + evt.deviceId + " DNI: " + evt.getDevice().deviceNetworkId)
def doorDeviceDNI = evt.getDevice().deviceNetworkId
doorDeviceDNI = doorDeviceDNI.replace(" Opener", "")
def doorDevice = getChildDevice(doorDeviceDNI)
ifDebug("Opening door.")
doorDevice.openPrep()
sendCommand(doorDevice, "desireddoorstate", 1)
}
def doorButtonCloseHandler(evt) {
ifDebug("Door close button push detected: Event name " + evt.name + " value: " + evt.value + " deviceID: " + evt.deviceId + " DNI: " + evt.getDevice().deviceNetworkId)
def doorDeviceDNI = evt.getDevice().deviceNetworkId
doorDeviceDNI = doorDeviceDNI.replace(" Closer", "")
def doorDevice = getChildDevice(doorDeviceDNI)
ifDebug("Closing door.")
doorDevice.closePrep()
sendCommand(doorDevice, "desireddoorstate", 0)
}
def getSelectedDevices( settingsName ) {
def selectedDevices = []
(!settings.get(settingsName))?:((settings.get(settingsName)?.getAt(0)?.size() > 1) ? settings.get(settingsName)?.each { selectedDevices.add(it) } : selectedDevices.add(settings.get(settingsName)))
return selectedDevices
}
private getBrandName(val){
switch (val) {
case 1:
return "Liftmaster"
break
case 2:
return "Chamberlain"
break
case 3:
return "Craftsman"
break
}
}
/* Access Management */
private forceLogin() {
//Reset token and expiry
state.session = [ brandID: 0, brandName: settings.brand, securityToken: null, expiration: 0 ]
state.polling = [ last: 0, rescheduler: now() ]
state.data = [:]
def a = doLogin();
ifDebug('a: ' + a)
return a;
}
private login() { return (!(state.session.expiration > now())) ? doLogin() : true }
private doLogin() {
ifDebug(doLoginStart)
def success = false;
apiPostLogin("/api/v4/User/Validate", [ username: settings.username, password: settings.password ]) { response ->
ifDebug("got login response: " + response + " : " + response.data.SecurityToken+" :: "+response.status)
if (response.status == 200) {
if (response.data.SecurityToken != null) {
//state.session.brandID = response.data.BrandId
//state.session.brandName = response.data.BrandName
state.session.securityToken = response.data.SecurityToken
state.session.expiration = now() + 150000
ifDebug("doLoginDone")
success = true;
//return true
} else {
//return false
}
} else {
//return false
}
}
return success;
}
// Listing all the garage doors you have in MyQ
private getDoorList() {
state.doorList = [:]
state.lightList = [:]
def deviceList = [:]
apiGet("/api/v4/userdevicedetails/get", []) { response ->
if (response.status == 200) {
//sendAlert("response data: " + response.data.Devices)
response.data.Devices.each { device ->
// 2 = garage door, 5 = gate, 7 = MyQGarage(no gateway), 17 = Garage Door Opener WGDO
if (device.MyQDeviceTypeId == 2||device.MyQDeviceTypeId == 5||device.MyQDeviceTypeId == 7||device.MyQDeviceTypeId == 17) {
ifDebug("Found door: " + device.MyQDeviceId)
def dni = [ app.id, "GarageDoorOpener", device.MyQDeviceId ].join('|')
def description = ''
def doorState = ''
def updatedTime = ''
device.Attributes.each {
if (it.AttributeDisplayName=="desc") //deviceList[dni] = it.Value
{
description = it.Value
}
if (it.AttributeDisplayName=="doorstate") {
doorState = it.Value
updatedTime = it.UpdatedTime
}
}
//Sometimes MyQ has duplicates. Check and see if we've seen this door before
def doorToRemove = ""
state.data.each { doorDNI, door ->
if (door.name == description){
ifDebug("Duplicate door detected. Checking to see if this one is newer...")
//If this instance is newer than the duplicate, pull the older one back out of the array
if (door.lastAction < updatedTime){
ifDebug("Yep, this one is newer.")
doorToRemove = door
}
//If this door is the older one, clear out the description so it will be ignored
else{
ifDebug("Nope, this one is older. Stick with what we've got.")
description = ""
}
}
}
if (doorToRemove){
ifDebug("Removing older duplicate.")
state.data.remove(door)
state.doorList.remove(door)
}
//Ignore any doors with blank descriptions
if (description != ''){
ifDebug("Storing door info: " + description + "type: " + device.MyQDeviceTypeId + " status: " + doorState + " type: " + device.MyQDeviceTypeName)
deviceList[dni] = description
state.doorList[dni] = description
state.data[dni] = [ status: doorState, lastAction: updatedTime, name: description, type: device.MyQDeviceTypeId ]
}
else{
ifDebug("Door " + device.MyQDeviceId + " has blank desc field. This is unusual...")
}
}
//Lights!
if (device.MyQDeviceTypeId == 3) {
ifDebug("Found light: " + device.MyQDeviceId)
def dni = [ app.id, "LightController", device.MyQDeviceId ].join('|')
def description = ''
def lightState = ''
def updatedTime = ''
device.Attributes.each {
if (it.AttributeDisplayName=="desc") //deviceList[dni] = it.Value
{
description = it.Value
}
if (it.AttributeDisplayName=="lightstate") {
lightState = it.Value
updatedTime = it.UpdatedTime
}
}
//Ignore any lights with blank descriptions
if (description != ''){
ifDebug("Storing light info: " + description + "type: " + device.MyQDeviceTypeId + " status: " + doorState + " type: " + device.MyQDeviceTypeName)
deviceList[dni] = description
state.lightList[dni] = description
state.data[dni] = [ status: lightState, lastAction: updatedTime, name: description, type: device.MyQDeviceTypeId ]
}
}
}
}
}
return deviceList
}
private getDeviceList() {
def deviceList = []
apiGet("/api/v4/userdevicedetails/get", []) { response ->
if (response.status == 200) {
response.data.Devices.each { device ->
ifDebug("MyQDeviceTypeId : " + device.MyQDeviceTypeId.toString())
if (!(device.MyQDeviceTypeId == 1||device.MyQDeviceTypeId == 2||device.MyQDeviceTypeId == 3||device.MyQDeviceTypeId == 5||device.MyQDeviceTypeId == 7)) {
device.Attributes.each {
def description = ''
def doorState = ''
def updatedTime = ''
if (it.AttributeDisplayName=="desc") //deviceList[dni] = it.Value
description = it.Value
//Ignore any doors with blank descriptions
if (description != ''){
ifDebug("found device: " + description)
deviceList.add( device.MyQDeviceTypeId.toString() + "|" + device.TypeID )
}
}
}
}
}
}
return deviceList
}
def getHubID(){
//def hubID
//def hubs = location.hubs.findAll{ it.type == com.hubitat.device.HubType.PHYSICAL }
//log.debug "hub count: ${hubs.size()}"
//log.debug "hubID: ${hubID}"
return 1234//hubs[0].id
}
/* api connection */
// get URL
private getApiURL() {
if (settings.brand == "Craftsman") {
return "https://craftexternal.myqdevice.com"
} else {
return "https://myqexternal.myqdevice.com"
}
}
private getApiAppID() {
if (settings.brand == "Craftsman") {
return "eU97d99kMG4t3STJZO/Mu2wt69yTQwM0WXZA5oZ74/ascQ2xQrLD/yjeVhEQccBZ"
} else {
return "NWknvuBd7LoFHfXmKNMBcgajXtZEgKUh4V7WNzMidrpUUluDpVYVZx+xT4PCM5Kx"
}
}
// HTTP GET call
private apiGet(apiPath, apiQuery = [], callback = {}) {
// set up query
def myHeaders = ""
apiQuery = [ appId: getApiAppID() ] + apiQuery
if (state.session.securityToken) {
apiQuery = apiQuery + [SecurityToken: state.session.securityToken ]
myHeaders = [ "SecurityToken": state.session.securityToken,
"MyQApplicationId": getApiAppID() ]
}
try {
httpGet([ uri: getApiURL(), path: apiPath, headers: myHeaders, query: apiQuery ]) { response -> callback(response) }
} catch (SocketException e) {
//sendAlert("API Error: $e")
ifDebug("API Error: $e")
}
}
// HTTP PUT call
private apiPut(apiPath, apiBody = [], callback = {}) {
// set up body
apiBody = [ ApplicationId: getApiAppID() ] + apiBody
if (state.session.securityToken) { apiBody = apiBody + [SecurityToken: state.session.securityToken ] }
def myHeaders = ""
// set up query
def apiQuery = [ appId: getApiAppID() ]
if (state.session.securityToken) {
apiQuery = apiQuery + [SecurityToken: state.session.securityToken ]
myHeaders = [ "SecurityToken": state.session.securityToken,
"MyQApplicationId": getApiAppID() ]
}
try {
httpPut([ uri: getApiURL(), path: apiPath, headers: myHeaders, contentType: "application/json; charset=utf-8", body: apiBody, query: apiQuery ]) { response -> callback(response) }
} catch (SocketException e) {
ifDebug("API Error: $e")
}
}
// HTTP POST call
private apiPostLogin(apiPath, apiBody = [], callback = {}) {
// set up body
apiBody = apiBody
def myHeaders = [ "User-Agent": "Chamberlain/3.73",
"BrandId": "2",
"ApiVersion": "4.1",
"Culture": "en",
"MyQApplicationId": getApiAppID() ]
try {
httpPost([ uri: getApiURL(), path: apiPath, headers: myHeaders, contentType: "application/json; charset=utf-8", body: apiBody ]) { response -> callback(response) }
} catch (SocketException e) {
ifDebug("API Error: $e")
}
}
// Get Device ID
def getChildDeviceID(child) {
return child.device.deviceNetworkId.split("\\|")[2]
}
// Get single device status
def getDeviceStatus(child) {
return state.data[child.device.deviceNetworkId].status
}
// Get single device last activity
def getDeviceLastActivity(child) {
return state.data[child.device.deviceNetworkId].lastAction.toLong()
}
// Send command to start or stop
def sendCommand(child, attributeName, attributeValue) {
state.lastCommandSent = now()
if (login()) {
//Send command
apiPut("/api/v4/deviceattribute/putdeviceattribute", [ MyQDeviceId: getChildDeviceID(child), AttributeName: attributeName, AttributeValue: attributeValue ])
if ((attributeName == "desireddoorstate") && (attributeValue == 0)) { // if we are closing, check if we have an Acceleration sensor, if so, "waiting" until it moves
def firstDoor = doors[0]
if (doors instanceof String) firstDoor = doors
def doorDNI = child.device.deviceNetworkId
switch (doorDNI) {
case firstDoor:
if (door1Sensor){if (door1Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
case doors[1]:
if (door2Sensor){if (door2Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
case doors[2]:
if (door3Sensor){if (door3Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
case doors[3]:
if (door4Sensor){if (door4Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
case doors[4]:
if (door5Sensor){if (door5Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
case doors[5]:
if (door6Sensor){if (door6Acceleration) child.updateDeviceStatus("waiting") else child.updateDeviceStatus("closing")}
break
}
}
return true
}
}
def getVersionInfo(oldVersion, newVersion){
def params = [
uri: 'http://www.fantasyaftermath.com/getMyQVersion/' + oldVersion + '/' + newVersion,
contentType: 'application/json'
]
asynchttpGet('responseHandlerMethod', params)
}
def responseHandlerMethod(response, data) {
if (response.hasError()) {
log.error "response has error: $response.errorMessage"
} else {
def results = response.json
state.latestSmartAppVersion = results.SmartApp;
state.latestDoorVersion = results.DoorDevice;
state.latestDoorNoSensorVersion = results.DoorDeviceNoSensor;
state.latestLightVersion = results.LightDevice;
}
ifDebug("previousVersion: " + state.previousVersion)
ifDebug("installedVersion: " + state.thisSmartAppVersion)
ifDebug("latestVersion: " + state.latestSmartAppVersion)
ifDebug("doorVersion: " + state.latestDoorVersion)
ifDebug("doorNoSensorVersion: " + state.latestDoorNoSensorVersion)
ifDebug("lightVersion: " + state.latestLightVersion)
}
def versionCheck(){
state.versionWarning = ""
state.thisDoorVersion = ""
state.thisDoorNoSensorVersion = ""
state.thisLightVersion = ""
state.versionWarning = ""
def usesDoorDev = false
def usesDoorNoSensorDev = false
def usesLightControllerDev = false
getChildDevices().each { childDevice ->
try {
def devType = childDevice.getTypeName()
if (devType != "Momentary Button Tile"){
if (devType == "MyQ Garage Door Opener"){
usesDoorDev = true
state.thisDoorVersion = childDevice.showVersion()
}
if (devType == "MyQ Garage Door Opener-NoSensor"){
usesDoorNoSensorDev = true
state.thisDoorNoSensorVersion = childDevice.showVersion()
}
if (devType == "MyQ Light Controller"){
usesLightControllerDev = true
state.thisLightVersion = childDevice.showVersion()
}
}
} catch (MissingPropertyException e) {
ifDebug("API Error: $e")
}
}
if (state.thisSmartAppVersion != state.latestSmartAppVersion) {
state.versionWarning = state.versionWarning + "Your SmartApp version (" + state.thisSmartAppVersion + ") is not the latest version (" + state.latestSmartAppVersion + ")\n\n"
}
if (usesDoorDev && state.thisDoorVersion != state.latestDoorVersion) {
state.versionWarning = state.versionWarning + "Your MyQ Door device version (" + state.thisDoorVersion + ") is not the latest version (" + state.latestDoorVersion + ")\n\n"
}
if (usesDoorNoSensorDev && state.thisDoorNoSensorVersion != state.latestDoorNoSensorVersion) {
state.versionWarning = state.versionWarning + "Your MyQ Door (No-sensor) device version (" + state.thisDoorNoSensorVersion + ") is not the latest version (" + state.latestDoorNoSensorVersion + ")\n\n"
}
if (usesLightControllerDev && state.thisLightVersion != state.latestLightVersion) {
state.versionWarning = state.versionWarning + "Your MyQ Light Controller device version (" + state.thisLightVersion + ") is not the latest version (" + state.latestLightVersion + ")\n\n"
}
ifDebug(state.versionWarning)
}
private ifDebug(msg) {
if (msg && state.isDebug) log.debug 'MyQ Lite: ' + msg
}
Ryan - I am not sure why you want to engage in this sort of meaningless banter, especially since you can see my name right in the "author: " field of MyQ Lite.
But yes:
- I ported the version I have been using over to Hubitat,
- from the version I ported from copy-ninja and brbeaird on SmartThings
- which originally used polling to determine when the door was opening/closing/stopped.
- brbeard added the tilt-sensor support for open/close, along with the lights support (and more recently, the Lock version)
- I personally added the acceleration sensor support for recognizing when the door was moving, which is/was the only way to know that something other than SmartThings had requested the door to close (for example)
- and despite all the gnashing of teeth, my version IS STILL ABLE to poll the MyQ servers (for testing purposes only, these days, but it isn't blocked)
So yeah, the OP's previous version most likely didn't use polling (unless he had a copy of my code).
BTW - I'm surprised that he was able to work using only the acceleration sensor - I'm doing a little testing on my re-addition of that capability to MyQ Lite to see if I can restore that operational mode reliably.
And no, I won't post the polling code (old or new versions), because Chamberlain has made it clear that this is unacceptable behavior for applications using their API.
So, the previous version did not poll the servers for the door status. Thank you for agreeing with me.
I am not the one who started this....you claimed something that was incorrect and I corrected you. The previous Hubitat version of this app/driver did not support the polling feature. Period. Why you are trying to claim otherwise is a mystery to me.
There is no "Hubitat" version of MyQ Lite - only hacks of @brbeairds' SmartThings version. And if Brian Beaird hadn't figured out/chased down why it stopped working recently, the so-called "Hubitat" version would still not be working.
A true "Hubitat" version would have learned from Mr. Beaird what changed, and then added it back into the "Hubitat" code base. Instead, it's just another hack port of the SmartThings version (and there are more than one HE port of brbeaird's version).
That said, yes - I made a mistake about the prior (Hubitat) version that the OP was using. I'm an old man, and I've probably forgotten more about programming for the MyQ than most people will ever actually know.
I see you've been around HE a while - I guess that gives you the right to chastise me for my mistakes.
So be it - have a nice day.
There was in fact a hubitat version of myQ lite.
And NONE of them used the poll feature. But hey...what do I know.
As I said, one of the MyQ ports actually used polling. It just wasn't made available publicly...
But you are the all-being, omnipotent resident expert on all things MyQ on HE, so who am I to disagree with you?
Then how would anyone have it. And why would you refer to it as the 'old version' then? I feel like I'm taking crazy pills over here. You're agreeing with me but saying I'm wrong.
I'm the not the expert on anything....but I have been on the forum for a while and I think I've read every thread on MyQ garage doors. So, if you want to insult me, go ahead.
No insult intended on my part, but you continue to hammer on the "I was right" screw with such unnecessary vehemence and arrogance...
I said that I made a mistake - I thought I had shared my port, but realized later that I didn't (because I didn't want to share the polling version).
Let's just drop it... I still have testing to do to get the accelerating sensor working again.
The only reason for someone to do that is if someone else is insisting he/she is wrong. If you stop insisting I was wrong, I'll gladly put down the hammer.
On mine either....but don't tell me something I know to not be true. I don't know much but I know what I know.
Gladly.
I was wondering when someone would get the sensor working again in Hubitat. The only reason it was disabled in the first place was I believe MyQ blocked the Smartthings IP address from polling... which is no longer an issue with Hubitat.
any update it looks like the current version still doesnt poll.