/* TP-Link TAPO plug, switches, lights, hub, and hub sensors.
Copyright Dave Gutheinz
License: https://github.com/DaveGut/HubitatActive/blob/master/KasaDevices/License.md
Supports: Tapo Plugs and Switches, New and Matter Kasa Plugs and Switches.
=================================================================================================*/
metadata {
definition (name: "TpLink Plug", namespace: nameSpace(), author: "Dave Gutheinz",
singleThreaded: true,
importUrl: "https://raw.githubusercontent.com/DaveGut/tpLink_Hubitat/main/Drivers/tpLink_plug.groovy")
{ }
preferences {
commonPreferences()
}
}
def installed() {
Map logData = [method: "installed", commonInstalled: commonInstalled()]
state.eventType = "digital"
logInfo(logData)
}
def updated() {
Map logData = [method: "updated", commonUpdated: commonUpdated()]
logInfo(logData)
}
def parse_get_device_info(result, data) {
switchParse(result)
}
// ~~~~~ start include (392) davegut.tpLinkCapConfiguration ~~~~~
library ( // library marker davegut.tpLinkCapConfiguration, line 1
name: "tpLinkCapConfiguration", // library marker davegut.tpLinkCapConfiguration, line 2
namespace: "davegut", // library marker davegut.tpLinkCapConfiguration, line 3
author: "Compied by Dave Gutheinz", // library marker davegut.tpLinkCapConfiguration, line 4
description: "Hubitat capability Configuration.", // library marker davegut.tpLinkCapConfiguration, line 5
category: "utilities", // library marker davegut.tpLinkCapConfiguration, line 6
documentationLink: "" // library marker davegut.tpLinkCapConfiguration, line 7
) // library marker davegut.tpLinkCapConfiguration, line 8
capability "Configuration" // library marker davegut.tpLinkCapConfiguration, line 10
def configure() { // library marker davegut.tpLinkCapConfiguration, line 12
String devIp = getDataValue("devIp") // library marker davegut.tpLinkCapConfiguration, line 13
Map logData = [method: "configure", devIp: devIp] // library marker davegut.tpLinkCapConfiguration, line 14
def cmdData = "0200000101e51100095c11706d6f58577b22706172616d73223a7b227273615f6b6579223a222d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d5c6e4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b43415145416d684655445279687367797073467936576c4d385c6e54646154397a61586133586a3042712f4d6f484971696d586e2b736b4e48584d525a6550564134627532416257386d79744a5033445073665173795679536e355c6e6f425841674d303149674d4f46736350316258367679784d523871614b33746e466361665a4653684d79536e31752f564f2f47474f795436507459716f384e315c6e44714d77373563334b5a4952387a4c71516f744657747239543337536e50754a7051555a7055376679574b676377716e7338785a657a78734e6a6465534171765c6e3167574e75436a5356686d437931564d49514942576d616a37414c47544971596a5442376d645348562f2b614a32564467424c6d7770344c7131664c4f6a466f5c6e33737241683144744a6b537376376a624f584d51695666453873764b6877586177717661546b5658382f7a4f44592b2f64684f5374694a4e6c466556636c35585c6e4a514944415141425c6e2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d5c6e227d7d" // library marker davegut.tpLinkCapConfiguration, line 15
try { // library marker davegut.tpLinkCapConfiguration, line 16
if (getDataValue("power")) { // library marker davegut.tpLinkCapConfiguration, line 17
sendFindCmd(devIp, "20004", cmdData, "configure2", 5) // library marker davegut.tpLinkCapConfiguration, line 18
} else { // library marker davegut.tpLinkCapConfiguration, line 19
sendFindCmd(devIp, "20002", cmdData, "configure2", 5) // library marker davegut.tpLinkCapConfiguration, line 20
} // library marker davegut.tpLinkCapConfiguration, line 21
logInfo(logData) // library marker davegut.tpLinkCapConfiguration, line 22
} catch (err) { // library marker davegut.tpLinkCapConfiguration, line 23
def parentChecked = parent.tpLinkCheckForDevices(5) // library marker davegut.tpLinkCapConfiguration, line 24
logData << [status: "FAILED", error: err, parentChecked: parentChecked] // library marker davegut.tpLinkCapConfiguration, line 25
logWarn(logData) // library marker davegut.tpLinkCapConfiguration, line 26
configure3() // library marker davegut.tpLinkCapConfiguration, line 27
} // library marker davegut.tpLinkCapConfiguration, line 28
} // library marker davegut.tpLinkCapConfiguration, line 29
def configure2(response) { // library marker davegut.tpLinkCapConfiguration, line 31
Map logData = [method: "configure2"] // library marker davegut.tpLinkCapConfiguration, line 32
def respData = parseLanMessage(response) // library marker davegut.tpLinkCapConfiguration, line 33
String hubDni = device.getDeviceNetworkId() // library marker davegut.tpLinkCapConfiguration, line 34
logData << [dni: respData.mac, hubDni: hubDni] // library marker davegut.tpLinkCapConfiguration, line 35
def parentChecked = false // library marker davegut.tpLinkCapConfiguration, line 36
if (respData.mac != hubDni) { // library marker davegut.tpLinkCapConfiguration, line 37
logData << [status: "device/ip not found", action: "parentCheck", // library marker davegut.tpLinkCapConfiguration, line 38
parentChecked: parent.tpLinkCheckForDevices(5)] // library marker davegut.tpLinkCapConfiguration, line 39
} else { // library marker davegut.tpLinkCapConfiguration, line 40
logData << [status: "device/ip found"] // library marker davegut.tpLinkCapConfiguration, line 41
} // library marker davegut.tpLinkCapConfiguration, line 42
configure3() // library marker davegut.tpLinkCapConfiguration, line 43
logInfo(logData) // library marker davegut.tpLinkCapConfiguration, line 44
} // library marker davegut.tpLinkCapConfiguration, line 45
def configure3() { // library marker davegut.tpLinkCapConfiguration, line 46
Map logData = [method: "configure3"] // library marker davegut.tpLinkCapConfiguration, line 47
logData <<[updateDeviceData: updateDeviceData(true)] // library marker davegut.tpLinkCapConfiguration, line 48
logData << [deviceHandshake: deviceHandshake()] // library marker davegut.tpLinkCapConfiguration, line 49
if (getDataValue("protocol") != "camera") { // library marker davegut.tpLinkCapConfiguration, line 50
runEvery3Hours("deviceHandshake") // library marker davegut.tpLinkCapConfiguration, line 51
logData << [handshakeInterval: "3 Hours"] // library marker davegut.tpLinkCapConfiguration, line 52
} // library marker davegut.tpLinkCapConfiguration, line 53
runIn(5, refresh) // library marker davegut.tpLinkCapConfiguration, line 54
logInfo(logData) // library marker davegut.tpLinkCapConfiguration, line 55
} // library marker davegut.tpLinkCapConfiguration, line 56
def deviceHandshake() { // library marker davegut.tpLinkCapConfiguration, line 58
def protocol = getDataValue("protocol") // library marker davegut.tpLinkCapConfiguration, line 59
Map logData = [method: "deviceHandshake", protocol: protocol] // library marker davegut.tpLinkCapConfiguration, line 60
if (protocol == "KLAP") { // library marker davegut.tpLinkCapConfiguration, line 61
klapHandshake(getDataValue("baseUrl"), parent.localHash) // library marker davegut.tpLinkCapConfiguration, line 62
} else if (protocol == "camera") { // library marker davegut.tpLinkCapConfiguration, line 63
Map hsInput = [url: getDataValue("baseUrl"), user: parent.userName, // library marker davegut.tpLinkCapConfiguration, line 64
pwd: parent.encPasswordCam] // library marker davegut.tpLinkCapConfiguration, line 65
cameraHandshake(hsInput) // library marker davegut.tpLinkCapConfiguration, line 66
} else if (protocol == "AES") { // library marker davegut.tpLinkCapConfiguration, line 67
aesHandshake() // library marker davegut.tpLinkCapConfiguration, line 68
} else if (protocol == "vacAes") { // library marker davegut.tpLinkCapConfiguration, line 69
vacAesHandshake(getDataValue("baseUrl"), parent.userName, parent.encPasswordVac) // library marker davegut.tpLinkCapConfiguration, line 70
} else { // library marker davegut.tpLinkCapConfiguration, line 71
logData << [ERROR: "Protocol not supported"] // library marker davegut.tpLinkCapConfiguration, line 72
logWarn(logData) // library marker davegut.tpLinkCapConfiguration, line 73
} // library marker davegut.tpLinkCapConfiguration, line 74
return logData // library marker davegut.tpLinkCapConfiguration, line 75
} // library marker davegut.tpLinkCapConfiguration, line 76
// ~~~~~ end include (392) davegut.tpLinkCapConfiguration ~~~~~
// ~~~~~ start include (381) davegut.tpLinkCapSwitch ~~~~~
library ( // library marker davegut.tpLinkCapSwitch, line 1
name: "tpLinkCapSwitch", // library marker davegut.tpLinkCapSwitch, line 2
namespace: "davegut", // library marker davegut.tpLinkCapSwitch, line 3
author: "Compied by Dave Gutheinz", // library marker davegut.tpLinkCapSwitch, line 4
description: "Hubitat capability Switch methods", // library marker davegut.tpLinkCapSwitch, line 5
category: "utilities", // library marker davegut.tpLinkCapSwitch, line 6
documentationLink: "" // library marker davegut.tpLinkCapSwitch, line 7
) // library marker davegut.tpLinkCapSwitch, line 8
capability "Switch" // library marker davegut.tpLinkCapSwitch, line 10
def on() { setPower(true) } // library marker davegut.tpLinkCapSwitch, line 12
def off() { setPower(false) } // library marker davegut.tpLinkCapSwitch, line 14
def setPower(onOff) { // library marker davegut.tpLinkCapSwitch, line 16
state.eventType = "digital" // library marker davegut.tpLinkCapSwitch, line 17
logDebug("setPower: [device_on: ${onOff}]") // library marker davegut.tpLinkCapSwitch, line 18
List requests = [[ // library marker davegut.tpLinkCapSwitch, line 19
method: "set_device_info", // library marker davegut.tpLinkCapSwitch, line 20
params: [device_on: onOff]]] // library marker davegut.tpLinkCapSwitch, line 21
requests << [method: "get_device_info"] // library marker davegut.tpLinkCapSwitch, line 22
sendDevCmd(requests, "setPower", "parseUpdates") // library marker davegut.tpLinkCapSwitch, line 23
} // library marker davegut.tpLinkCapSwitch, line 24
def switchParse(result) { // library marker davegut.tpLinkCapSwitch, line 26
Map logData = [method: "switchParse"] // library marker davegut.tpLinkCapSwitch, line 27
if (result.device_on != null) { // library marker davegut.tpLinkCapSwitch, line 28
def onOff = "off" // library marker davegut.tpLinkCapSwitch, line 29
if (result.device_on == true) { onOff = "on" } // library marker davegut.tpLinkCapSwitch, line 30
if (device.currentValue("switch") != onOff) { // library marker davegut.tpLinkCapSwitch, line 31
sendEvent(name: "switch", value: onOff, type: state.eventType) // library marker davegut.tpLinkCapSwitch, line 32
if (getDataValue("isEm") == "true" && !pollInterval.contains("sec")) { // library marker davegut.tpLinkCapSwitch, line 33
runIn(4, refresh) // library marker davegut.tpLinkCapSwitch, line 34
} // library marker davegut.tpLinkCapSwitch, line 35
} // library marker davegut.tpLinkCapSwitch, line 36
state.eventType = "physical" // library marker davegut.tpLinkCapSwitch, line 37
logData << [switch: onOff] // library marker davegut.tpLinkCapSwitch, line 38
} // library marker davegut.tpLinkCapSwitch, line 39
logDebug(logData) // library marker davegut.tpLinkCapSwitch, line 40
} // library marker davegut.tpLinkCapSwitch, line 41
// ~~~~~ end include (381) davegut.tpLinkCapSwitch ~~~~~
// ~~~~~ start include (380) davegut.tpLinkCapEngMon ~~~~~
library ( // library marker davegut.tpLinkCapEngMon, line 1
name: "tpLinkCapEngMon", // library marker davegut.tpLinkCapEngMon, line 2
namespace: "davegut", // library marker davegut.tpLinkCapEngMon, line 3
author: "Compied by Dave Gutheinz", // library marker davegut.tpLinkCapEngMon, line 4
description: "Hubitat Energy Monitor methods", // library marker davegut.tpLinkCapEngMon, line 5
category: "utilities", // library marker davegut.tpLinkCapEngMon, line 6
documentationLink: "" // library marker davegut.tpLinkCapEngMon, line 7
) // library marker davegut.tpLinkCapEngMon, line 8
capability "EnergyMeter" // library marker davegut.tpLinkCapEngMon, line 10
capability "PowerMeter" // library marker davegut.tpLinkCapEngMon, line 11
attribute "past30Energy", "number" // library marker davegut.tpLinkCapEngMon, line 12
attribute "past7Energy", "number" // library marker davegut.tpLinkCapEngMon, line 13
def emUpdated() { // library marker davegut.tpLinkCapEngMon, line 15
Map logData = [emDataUpdate: "30 mins"] // library marker davegut.tpLinkCapEngMon, line 16
runEvery30Minutes(getEmData) // library marker davegut.tpLinkCapEngMon, line 17
getEmData() // library marker davegut.tpLinkCapEngMon, line 18
return logData // library marker davegut.tpLinkCapEngMon, line 19
} // library marker davegut.tpLinkCapEngMon, line 20
def powerPoll() { // library marker davegut.tpLinkCapEngMon, line 22
List requests = [[method: "get_current_power"]] // library marker davegut.tpLinkCapEngMon, line 23
sendDevCmd(requests, "powerPoll", "parseUpdates") // library marker davegut.tpLinkCapEngMon, line 24
} // library marker davegut.tpLinkCapEngMon, line 25
def parse_get_current_power(result, data) { // library marker davegut.tpLinkCapEngMon, line 27
Map logData = [method: "parse_get_current_power", data: data, // library marker davegut.tpLinkCapEngMon, line 28
power: result.current_power] // library marker davegut.tpLinkCapEngMon, line 29
updateAttr("power", result.current_power) // library marker davegut.tpLinkCapEngMon, line 30
logDebug(logData) // library marker davegut.tpLinkCapEngMon, line 31
} // library marker davegut.tpLinkCapEngMon, line 32
def getEmData() { // library marker davegut.tpLinkCapEngMon, line 34
List requests = [[method: "get_device_usage"]] // library marker davegut.tpLinkCapEngMon, line 35
sendDevCmd(requests, "getEmData", "parseUpdates") // library marker davegut.tpLinkCapEngMon, line 36
} // library marker davegut.tpLinkCapEngMon, line 37
def parse_get_device_usage(result, data) { // library marker davegut.tpLinkCapEngMon, line 39
Map logData = [method: "parse_get_device_usage", data: data] // library marker davegut.tpLinkCapEngMon, line 40
def usage = result.power_usage // library marker davegut.tpLinkCapEngMon, line 41
updateAttr("energy", usage.today) // library marker davegut.tpLinkCapEngMon, line 42
updateAttr("past30Energy", usage.past30) // library marker davegut.tpLinkCapEngMon, line 43
updateAttr("past7Energy", usage.past7) // library marker davegut.tpLinkCapEngMon, line 44
logData << [energy:usage.today, past30Energy: usage.past30, past7Energy: usage.past7] // library marker davegut.tpLinkCapEngMon, line 45
logDebug(logData) // library marker davegut.tpLinkCapEngMon, line 46
} // library marker davegut.tpLinkCapEngMon, line 47
// ~~~~~ end include (380) davegut.tpLinkCapEngMon ~~~~~
// ~~~~~ start include (384) davegut.tpLinkCommon ~~~~~
library ( // library marker davegut.tpLinkCommon, line 1
name: "tpLinkCommon", // library marker davegut.tpLinkCommon, line 2
namespace: "davegut", // library marker davegut.tpLinkCommon, line 3
author: "Compied by Dave Gutheinz", // library marker davegut.tpLinkCommon, line 4
description: "Common driver methods including capability Refresh and Configuration methods", // library marker davegut.tpLinkCommon, line 5
category: "utilities", // library marker davegut.tpLinkCommon, line 6
documentationLink: "" // library marker davegut.tpLinkCommon, line 7
) // library marker davegut.tpLinkCommon, line 8
capability "Refresh" // library marker davegut.tpLinkCommon, line 10
attribute "commsError", "string" // library marker davegut.tpLinkCommon, line 11
def commonPreferences() { // library marker davegut.tpLinkCommon, line 13
List pollOptions = ["5 sec", "10 sec", "1 min", "5 min", "15 min", "30 min"] // library marker davegut.tpLinkCommon, line 14
input ("pollInterval", "enum", title: "Poll Interval", // library marker davegut.tpLinkCommon, line 15
options: pollOptions, defaultValue: "30 min") // library marker davegut.tpLinkCommon, line 16
if (getDataValue("hasLed") == "true") { // library marker davegut.tpLinkCommon, line 17
input ("ledRule", "enum", title: "LED Mode (if night mode, set type and times in phone app)", // library marker davegut.tpLinkCommon, line 18
options: ["always", "never", "night_mode"], defaultValue: "always") // library marker davegut.tpLinkCommon, line 19
} // library marker davegut.tpLinkCommon, line 20
input ("rebootDev", "bool", title: "Reboot Device", defaultValue: false) // library marker davegut.tpLinkCommon, line 21
input ("syncName", "enum", title: "Update Device Names and Labels", // library marker davegut.tpLinkCommon, line 22
options: ["hubMaster", "tapoAppMaster", "notSet"], defaultValue: "notSet") // library marker davegut.tpLinkCommon, line 23
input ("logEnable", "bool", title: "Enable debug logging for 30 minutes", defaultValue: false) // library marker davegut.tpLinkCommon, line 24
input ("infoLog", "bool", title: "Enable information logging",defaultValue: true) // library marker davegut.tpLinkCommon, line 25
} // library marker davegut.tpLinkCommon, line 26
def commonInstalled() { // library marker davegut.tpLinkCommon, line 28
Map logData = [method: "commonInstalled"] // library marker davegut.tpLinkCommon, line 29
updateAttr("commsError", "false") // library marker davegut.tpLinkCommon, line 30
state.errorCount = 0 // library marker davegut.tpLinkCommon, line 31
logData << [configure: configure()] // library marker davegut.tpLinkCommon, line 32
return logData // library marker davegut.tpLinkCommon, line 33
} // library marker davegut.tpLinkCommon, line 34
def commonUpdated() { // library marker davegut.tpLinkCommon, line 36
unschedule() // library marker davegut.tpLinkCommon, line 37
sendEvent(name: "commsError", value: "false") // library marker davegut.tpLinkCommon, line 38
state.errorCount = 0 // library marker davegut.tpLinkCommon, line 39
Map logData = [commsError: "cleared"] // library marker davegut.tpLinkCommon, line 40
if (rebootDev == true) { // library marker davegut.tpLinkCommon, line 41
List requests = [[method: "device_reboot"]] // library marker davegut.tpLinkCommon, line 42
sendDevCmd(requests, "rebootDevice", "parseUpdates") // library marker davegut.tpLinkCommon, line 43
logData << [rebootDevice: "device reboot being attempted"] // library marker davegut.tpLinkCommon, line 44
} else { // library marker davegut.tpLinkCommon, line 45
runEvery3Hours(deviceHandshake) // library marker davegut.tpLinkCommon, line 46
logData << [handshakeInterval: "3 Hours"] // library marker davegut.tpLinkCommon, line 47
logData << [pollInterval: setPollInterval()] // library marker davegut.tpLinkCommon, line 48
logData << [logging: setLogsOff()] // library marker davegut.tpLinkCommon, line 49
logData << [updateDevSettings: updDevSettings()] // library marker davegut.tpLinkCommon, line 50
runIn(2, refresh) // library marker davegut.tpLinkCommon, line 51
if (getDataValue("isEm") == "true") { // library marker davegut.tpLinkCommon, line 52
runIn(7, emUpdated) // library marker davegut.tpLinkCommon, line 53
} // library marker davegut.tpLinkCommon, line 54
} // library marker davegut.tpLinkCommon, line 55
return logData // library marker davegut.tpLinkCommon, line 56
} // library marker davegut.tpLinkCommon, line 57
def finishReboot(respData) { // library marker davegut.tpLinkCommon, line 59
Map logData = [method: "finishReboot", respData: respData] // library marker davegut.tpLinkCommon, line 60
logData << [wait: "20s for device to reconnect to LAN", action: "executing deviceHandshake"] // library marker davegut.tpLinkCommon, line 61
device.updateSetting("rebootDev",[type:"bool", value: false]) // library marker davegut.tpLinkCommon, line 62
runIn(20, configure) // library marker davegut.tpLinkCommon, line 63
logDebug(logData) // library marker davegut.tpLinkCommon, line 64
} // library marker davegut.tpLinkCommon, line 65
def updDevSettings() { // library marker davegut.tpLinkCommon, line 67
List requests = [] // library marker davegut.tpLinkCommon, line 68
if (syncName == "hubMaster") { // library marker davegut.tpLinkCommon, line 69
String nickname = device.getLabel().bytes.encodeBase64().toString() // library marker davegut.tpLinkCommon, line 70
requests << [method: "set_device_info", params: [nickname: nickname]] // library marker davegut.tpLinkCommon, line 71
} // library marker davegut.tpLinkCommon, line 72
if (ledRule) { // library marker davegut.tpLinkCommon, line 73
requests << [method: "get_led_info"] // library marker davegut.tpLinkCommon, line 74
} // library marker davegut.tpLinkCommon, line 75
if (getDataValue("isEm") == "true") { // library marker davegut.tpLinkCommon, line 76
requests << [method: "get_energy_usage"] // library marker davegut.tpLinkCommon, line 77
} // library marker davegut.tpLinkCommon, line 78
requests << [method: "get_device_info"] // library marker davegut.tpLinkCommon, line 79
sendDevCmd(requests, "updateDevSettings", "parseUpdates") // library marker davegut.tpLinkCommon, line 80
return "Updated" // library marker davegut.tpLinkCommon, line 81
} // library marker davegut.tpLinkCommon, line 82
def setPollInterval(pInterval = pollInterval) { // library marker davegut.tpLinkCommon, line 84
if (pInterval.contains("sec")) { // library marker davegut.tpLinkCommon, line 85
logWarn("Poll intervals of less than 1 minute may overload the Hub") // library marker davegut.tpLinkCommon, line 86
def interval = pInterval.replace(" sec", "").toInteger() // library marker davegut.tpLinkCommon, line 87
def start = Math.round((interval-1) * Math.random()).toInteger() // library marker davegut.tpLinkCommon, line 88
schedule("${start}/${interval} * * * * ?", "refresh") // library marker davegut.tpLinkCommon, line 89
} else { // library marker davegut.tpLinkCommon, line 90
def interval= pInterval.replace(" min", "").toInteger() // library marker davegut.tpLinkCommon, line 91
def start = Math.round(59 * Math.random()).toInteger() // library marker davegut.tpLinkCommon, line 92
schedule("${start} */${interval} * * * ?", "refresh") // library marker davegut.tpLinkCommon, line 93
} // library marker davegut.tpLinkCommon, line 94
return pInterval // library marker davegut.tpLinkCommon, line 95
} // library marker davegut.tpLinkCommon, line 96
// ===== Data Distribution (and parse) ===== // library marker davegut.tpLinkCommon, line 98
def parseUpdates(resp, data = null) { // library marker davegut.tpLinkCommon, line 99
Map logData = [method: "parseUpdates", data: data] // library marker davegut.tpLinkCommon, line 100
def respData = parseData(resp, getDataValue("protocol"), data) // library marker davegut.tpLinkCommon, line 101
if (resp.status == 200 && respData.cryptoStatus == "OK") { // library marker davegut.tpLinkCommon, line 102
def cmdResp = respData.cmdResp.result.responses // library marker davegut.tpLinkCommon, line 103
if (respData.cmdResp.result.responses != null) { // library marker davegut.tpLinkCommon, line 104
respData.cmdResp.result.responses.each { // library marker davegut.tpLinkCommon, line 105
if (it.error_code == 0) { // library marker davegut.tpLinkCommon, line 106
distGetData(it, data) // library marker davegut.tpLinkCommon, line 107
} else { // library marker davegut.tpLinkCommon, line 108
logData << ["${it.method}": [status: "cmdFailed", data: it]] // library marker davegut.tpLinkCommon, line 109
logDebug(logData) // library marker davegut.tpLinkCommon, line 110
} // library marker davegut.tpLinkCommon, line 111
} // library marker davegut.tpLinkCommon, line 112
} // library marker davegut.tpLinkCommon, line 113
if (respData.cmdResp.result.responseData != null) { // library marker davegut.tpLinkCommon, line 114
respData.cmdResp.result.responseData.result.responses.each { // library marker davegut.tpLinkCommon, line 115
if (it.error_code == 0) { // library marker davegut.tpLinkCommon, line 116
distChildGetData(it, data) // library marker davegut.tpLinkCommon, line 117
} else { // library marker davegut.tpLinkCommon, line 118
logData << ["${it.method}": [status: "cmdFailed", data: it]] // library marker davegut.tpLinkCommon, line 119
logDebug(logData) // library marker davegut.tpLinkCommon, line 120
} // library marker davegut.tpLinkCommon, line 121
} // library marker davegut.tpLinkCommon, line 122
} // library marker davegut.tpLinkCommon, line 123
} else { // library marker davegut.tpLinkCommon, line 124
logData << [errorMsg: "Misc Error"] // library marker davegut.tpLinkCommon, line 125
logDebug(logData) // library marker davegut.tpLinkCommon, line 126
} // library marker davegut.tpLinkCommon, line 127
} // library marker davegut.tpLinkCommon, line 128
def distGetData(devResp, data) { // library marker davegut.tpLinkCommon, line 130
switch(devResp.method) { // library marker davegut.tpLinkCommon, line 131
case "get_device_info": // library marker davegut.tpLinkCommon, line 132
parse_get_device_info(devResp.result, data) // library marker davegut.tpLinkCommon, line 133
parseNameUpdate(devResp.result) // library marker davegut.tpLinkCommon, line 134
break // library marker davegut.tpLinkCommon, line 135
case "get_current_power": // library marker davegut.tpLinkCommon, line 136
parse_get_current_power(devResp.result, data) // library marker davegut.tpLinkCommon, line 137
break // library marker davegut.tpLinkCommon, line 138
case "get_device_usage": // library marker davegut.tpLinkCommon, line 139
parse_get_device_usage(devResp.result, data) // library marker davegut.tpLinkCommon, line 140
break // library marker davegut.tpLinkCommon, line 141
case "get_child_device_list": // library marker davegut.tpLinkCommon, line 142
parse_get_child_device_list(devResp.result, data) // library marker davegut.tpLinkCommon, line 143
break // library marker davegut.tpLinkCommon, line 144
case "get_alarm_configure": // library marker davegut.tpLinkCommon, line 145
parse_get_alarm_configure(devResp.result, data) // library marker davegut.tpLinkCommon, line 146
break // library marker davegut.tpLinkCommon, line 147
case "get_led_info": // library marker davegut.tpLinkCommon, line 148
parse_get_led_info(devResp.result, data) // library marker davegut.tpLinkCommon, line 149
break // library marker davegut.tpLinkCommon, line 150
case "device_reboot": // library marker davegut.tpLinkCommon, line 151
finishReboot(devResp) // library marker davegut.tpLinkCommon, line 152
break // library marker davegut.tpLinkCommon, line 153
// RoboVac // library marker davegut.tpLinkCommon, line 154
case "getBatteryInfo": // library marker davegut.tpLinkCommon, line 155
parse_getBatteryInfo(devResp.result, data) // library marker davegut.tpLinkCommon, line 156
break // library marker davegut.tpLinkCommon, line 157
case "getCleanNumber": // library marker davegut.tpLinkCommon, line 158
parse_getCleanNumber(devResp.result, data) // library marker davegut.tpLinkCommon, line 159
break // library marker davegut.tpLinkCommon, line 160
case "getSwitchClean": // library marker davegut.tpLinkCommon, line 161
parse_getSwitchClean(devResp, data) // library marker davegut.tpLinkCommon, line 162
break // library marker davegut.tpLinkCommon, line 163
case "getMopState": // library marker davegut.tpLinkCommon, line 164
parse_getMopState(devResp, data) // library marker davegut.tpLinkCommon, line 165
break // library marker davegut.tpLinkCommon, line 166
case "getSwitchCharge": // library marker davegut.tpLinkCommon, line 167
updateAttr("docking", devResp.switch_charge, data) // library marker davegut.tpLinkCommon, line 168
break // library marker davegut.tpLinkCommon, line 169
case "getVacStatus": // library marker davegut.tpLinkCommon, line 170
parse_getVacStatus(devResp.result, data) // library marker davegut.tpLinkCommon, line 171
break // library marker davegut.tpLinkCommon, line 172
case "getMapInfo": // library marker davegut.tpLinkCommon, line 173
parse_getMapInfo(devResp.result, data) // library marker davegut.tpLinkCommon, line 174
break // library marker davegut.tpLinkCommon, line 175
case "getMapData": // library marker davegut.tpLinkCommon, line 176
parse_getMapData(devResp.result, data) // library marker davegut.tpLinkCommon, line 177
break // library marker davegut.tpLinkCommon, line 178
case "getLastAlarmInfo": // library marker davegut.tpLinkCommon, line 179
parse_getLastAlarmInfo(devResp.result, data) // library marker davegut.tpLinkCommon, line 180
break // library marker davegut.tpLinkCommon, line 181
default: // library marker davegut.tpLinkCommon, line 182
if (!devResp.method.contains("set_")) { // library marker davegut.tpLinkCommon, line 183
Map logData = [method: "distGetData", data: data, // library marker davegut.tpLinkCommon, line 184
devMethod: devResp.method, status: "unprocessed"] // library marker davegut.tpLinkCommon, line 185
logDebug(logData) // library marker davegut.tpLinkCommon, line 186
} // library marker davegut.tpLinkCommon, line 187
} // library marker davegut.tpLinkCommon, line 188
} // library marker davegut.tpLinkCommon, line 189
def parse_get_led_info(result, data) { // library marker davegut.tpLinkCommon, line 191
Map logData = [method: "parse_get_led_info", data: data] // library marker davegut.tpLinkCommon, line 192
if (ledRule != result.led_rule) { // library marker davegut.tpLinkCommon, line 193
Map request = [ // library marker davegut.tpLinkCommon, line 194
method: "set_led_info", // library marker davegut.tpLinkCommon, line 195
params: [ // library marker davegut.tpLinkCommon, line 196
led_rule: ledRule, // library marker davegut.tpLinkCommon, line 197
night_mode: [ // library marker davegut.tpLinkCommon, line 198
night_mode_type: result.night_mode.night_mode_type, // library marker davegut.tpLinkCommon, line 199
sunrise_offset: result.night_mode.sunrise_offset, // library marker davegut.tpLinkCommon, line 200
sunset_offset:result.night_mode.sunset_offset, // library marker davegut.tpLinkCommon, line 201
start_time: result.night_mode.start_time, // library marker davegut.tpLinkCommon, line 202
end_time: result.night_mode.end_time // library marker davegut.tpLinkCommon, line 203
]]] // library marker davegut.tpLinkCommon, line 204
asyncSend(request, "delayedUpdates", "parseUpdates") // library marker davegut.tpLinkCommon, line 205
device.updateSetting("ledRule", [type:"enum", value: ledRule]) // library marker davegut.tpLinkCommon, line 206
logData << [status: "updatingLedRule"] // library marker davegut.tpLinkCommon, line 207
} // library marker davegut.tpLinkCommon, line 208
logData << [ledRule: ledRule] // library marker davegut.tpLinkCommon, line 209
logDebug(logData) // library marker davegut.tpLinkCommon, line 210
} // library marker davegut.tpLinkCommon, line 211
def parseNameUpdate(result) { // library marker davegut.tpLinkCommon, line 213
if (syncName != "notSet") { // library marker davegut.tpLinkCommon, line 214
Map logData = [method: "parseNameUpdate"] // library marker davegut.tpLinkCommon, line 215
byte[] plainBytes = result.nickname.decodeBase64() // library marker davegut.tpLinkCommon, line 216
def newLabel = new String(plainBytes) // library marker davegut.tpLinkCommon, line 217
device.setLabel(newLabel) // library marker davegut.tpLinkCommon, line 218
device.updateSetting("syncName",[type:"enum", value: "notSet"]) // library marker davegut.tpLinkCommon, line 219
logData << [label: newLabel] // library marker davegut.tpLinkCommon, line 220
logDebug(logData) // library marker davegut.tpLinkCommon, line 221
} // library marker davegut.tpLinkCommon, line 222
} // library marker davegut.tpLinkCommon, line 223
// ===== Capability Refresh ===== // library marker davegut.tpLinkCommon, line 225
def refresh() { // library marker davegut.tpLinkCommon, line 226
def type = getDataValue("type") // library marker davegut.tpLinkCommon, line 227
List requests = [[method: "get_device_info"]] // library marker davegut.tpLinkCommon, line 228
if (type == "Hub" || type == "Parent") { // library marker davegut.tpLinkCommon, line 229
requests << [method:"get_child_device_list"] // library marker davegut.tpLinkCommon, line 230
} // library marker davegut.tpLinkCommon, line 231
if (getDataValue("isEm") == "true") { // library marker davegut.tpLinkCommon, line 232
requests << [method: "get_current_power"] // library marker davegut.tpLinkCommon, line 233
} // library marker davegut.tpLinkCommon, line 234
if (type == "Robovac") { // library marker davegut.tpLinkCommon, line 235
requests = [[method: "getBatteryInfo"], // library marker davegut.tpLinkCommon, line 236
[method: "getCleanNumber"], // library marker davegut.tpLinkCommon, line 237
[method: "getSwitchClean"], // library marker davegut.tpLinkCommon, line 238
[method: "getVacStatus"], // library marker davegut.tpLinkCommon, line 239
[method: "getMopState"], // library marker davegut.tpLinkCommon, line 240
[method: "getSwitchCharge"]] // library marker davegut.tpLinkCommon, line 241
} // library marker davegut.tpLinkCommon, line 242
sendDevCmd(requests, "refresh", "parseUpdates") // library marker davegut.tpLinkCommon, line 243
} // library marker davegut.tpLinkCommon, line 244
def plugEmRefresh() { refresh() } // library marker davegut.tpLinkCommon, line 246
def parentRefresh() { refresh() } // library marker davegut.tpLinkCommon, line 247
def minRefresh() { refresh() } // library marker davegut.tpLinkCommon, line 248
def sendDevCmd(requests, data, action) { // library marker davegut.tpLinkCommon, line 250
Map cmdBody = [ // library marker davegut.tpLinkCommon, line 251
method: "multipleRequest", // library marker davegut.tpLinkCommon, line 252
params: [requests: requests]] // library marker davegut.tpLinkCommon, line 253
asyncSend(cmdBody, data, action) // library marker davegut.tpLinkCommon, line 254
} // library marker davegut.tpLinkCommon, line 255
def nullParse(resp, data) { } // library marker davegut.tpLinkCommon, line 257
// ===== Check/Update device data ===== // library marker davegut.tpLinkCommon, line 259
def updateDeviceData(fromConfig = false) { // library marker davegut.tpLinkCommon, line 260
def devData = parent.getDeviceData(device.getDeviceNetworkId()) // library marker davegut.tpLinkCommon, line 261
updateChild(devData, fromConfig) // library marker davegut.tpLinkCommon, line 262
return [updateDeviceData: "updating with app data"] // library marker davegut.tpLinkCommon, line 263
} // library marker davegut.tpLinkCommon, line 264
def updateChild(devData, fromConfig = false) { // library marker davegut.tpLinkCommon, line 266
def currVersion = getDataValue("version") // library marker davegut.tpLinkCommon, line 267
Map logData = [method: "updateChild", devData: devData] // library marker davegut.tpLinkCommon, line 268
if (devData != null) { // library marker davegut.tpLinkCommon, line 269
devData.each { // library marker davegut.tpLinkCommon, line 270
if (it.key != "deviceType" && it.key != "model" && it.key != "alias") { // library marker davegut.tpLinkCommon, line 271
updateDataValue(it.key, it.value.toString()) // library marker davegut.tpLinkCommon, line 272
} // library marker davegut.tpLinkCommon, line 273
} // library marker davegut.tpLinkCommon, line 274
if (currVersion != version()) { // library marker davegut.tpLinkCommon, line 275
updateDataValue("version", version()) // library marker davegut.tpLinkCommon, line 276
logData << [updateVersion: version()] // library marker davegut.tpLinkCommon, line 277
runIn(20, updated) // library marker davegut.tpLinkCommon, line 278
} // library marker davegut.tpLinkCommon, line 279
} else { // library marker davegut.tpLinkCommon, line 280
logData << [Note: "DEVICE DATA IS NULL"] // library marker davegut.tpLinkCommon, line 281
} // library marker davegut.tpLinkCommon, line 282
if (!fromConfig) { deviceHandshake() } // library marker davegut.tpLinkCommon, line 283
logInfo(logData) // library marker davegut.tpLinkCommon, line 284
} // library marker davegut.tpLinkCommon, line 285
// ~~~~~ end include (384) davegut.tpLinkCommon ~~~~~
// ~~~~~ start include (385) davegut.tpLinkComms ~~~~~
library ( // library marker davegut.tpLinkComms, line 1
name: "tpLinkComms", // library marker davegut.tpLinkComms, line 2
namespace: "davegut", // library marker davegut.tpLinkComms, line 3
author: "Compiled by Dave Gutheinz", // library marker davegut.tpLinkComms, line 4
description: "Communication methods for TP-Link Integration", // library marker davegut.tpLinkComms, line 5
category: "utilities", // library marker davegut.tpLinkComms, line 6
documentationLink: "" // library marker davegut.tpLinkComms, line 7
) // library marker davegut.tpLinkComms, line 8
import org.json.JSONObject // library marker davegut.tpLinkComms, line 9
import groovy.json.JsonOutput // library marker davegut.tpLinkComms, line 10
import groovy.json.JsonBuilder // library marker davegut.tpLinkComms, line 11
import groovy.json.JsonSlurper // library marker davegut.tpLinkComms, line 12
// ===== Communications Methods ===== // library marker davegut.tpLinkComms, line 14
def asyncSend(cmdBody, reqData, action) { // library marker davegut.tpLinkComms, line 15
Map cmdData = [cmdBody: cmdBody, reqData: reqData, action: action] // library marker davegut.tpLinkComms, line 16
def protocol = getDataValue("protocol") // library marker davegut.tpLinkComms, line 17
Map reqParams = [:] // library marker davegut.tpLinkComms, line 18
if (protocol == "KLAP") { // library marker davegut.tpLinkComms, line 19
reqParams = getKlapParams(cmdBody) // library marker davegut.tpLinkComms, line 20
} else if (protocol == "camera") { // library marker davegut.tpLinkComms, line 21
reqParams = getCameraParams(cmdBody, reqData) // library marker davegut.tpLinkComms, line 22
} else if (protocol == "AES") { // library marker davegut.tpLinkComms, line 23
reqParams = getAesParams(cmdBody) // library marker davegut.tpLinkComms, line 24
} else if (protocol == "vacAes") { // library marker davegut.tpLinkComms, line 25
reqParams = getVacAesParams(cmdBody, "${getDataValue("baseUrl")}/?token=${token}") // library marker davegut.tpLinkComms, line 26
} // library marker davegut.tpLinkComms, line 27
if (reqParams != [:]) { // library marker davegut.tpLinkComms, line 28
if (state.errorCount == 0) { state.lastCommand = cmdData } // library marker davegut.tpLinkComms, line 29
asynchttpPost(action, reqParams, [data: reqData]) // library marker davegut.tpLinkComms, line 30
logDebug([method: "asyncSend", reqData: reqData]) // library marker davegut.tpLinkComms, line 31
} else { // library marker davegut.tpLinkComms, line 32
unknownProt(reqData) // library marker davegut.tpLinkComms, line 33
} // library marker davegut.tpLinkComms, line 34
} // library marker davegut.tpLinkComms, line 35
def unknownProt(reqData) { // library marker davegut.tpLinkComms, line 37
Map warnData = ["UnknownProtocol": [data: reqData, // library marker davegut.tpLinkComms, line 38
msg: "Device will not install or if installed will not work"]] // library marker davegut.tpLinkComms, line 39
logWarn(warnData) // library marker davegut.tpLinkComms, line 40
} // library marker davegut.tpLinkComms, line 41
def parseData(resp, protocol = getDataValue("protocol"), data = null) { // library marker davegut.tpLinkComms, line 43
Map logData = [method: "parseData", status: resp.status, protocol: protocol, // library marker davegut.tpLinkComms, line 44
sourceMethod: data.data] // library marker davegut.tpLinkComms, line 45
def message = "OK" // library marker davegut.tpLinkComms, line 46
if (resp.status == 200) { // library marker davegut.tpLinkComms, line 47
if (protocol == "KLAP") { // library marker davegut.tpLinkComms, line 48
logData << parseKlapData(resp, data) // library marker davegut.tpLinkComms, line 49
} else if (protocol == "AES") { // library marker davegut.tpLinkComms, line 50
logData << parseAesData(resp, data) // library marker davegut.tpLinkComms, line 51
} else if (protocol == "vacAes") { // library marker davegut.tpLinkComms, line 52
logData << parseVacAesData(resp, data) // library marker davegut.tpLinkComms, line 53
} else if (protocol == "camera") { // library marker davegut.tpLinkComms, line 54
logData << parseCameraData(resp, data) // library marker davegut.tpLinkComms, line 55
} // library marker davegut.tpLinkComms, line 56
} else { // library marker davegut.tpLinkComms, line 57
message = resp.errorMessage // library marker davegut.tpLinkComms, line 58
String userMessage = "unspecified" // library marker davegut.tpLinkComms, line 59
if (resp.status == 403) { // library marker davegut.tpLinkComms, line 60
userMessage = "Try again. If error persists, check your credentials" // library marker davegut.tpLinkComms, line 61
} else if (resp.status == 408) { // library marker davegut.tpLinkComms, line 62
userMessage = "Your router connection to ${getDataValue("baseUrl")} failed. Run Configure." // library marker davegut.tpLinkComms, line 63
} else { // library marker davegut.tpLinkComms, line 64
userMessage = "Unhandled error Lan return" // library marker davegut.tpLinkComms, line 65
} // library marker davegut.tpLinkComms, line 66
logData << [respMessage: message, userMessage: userMessage] // library marker davegut.tpLinkComms, line 67
logDebug(logData) // library marker davegut.tpLinkComms, line 68
} // library marker davegut.tpLinkComms, line 69
handleCommsError(resp.status, message) // library marker davegut.tpLinkComms, line 70
return logData // library marker davegut.tpLinkComms, line 71
} // library marker davegut.tpLinkComms, line 72
private sendFindCmd(ip, port, cmdData, action, commsTo = 5, ignore = false) { // library marker davegut.tpLinkComms, line 74
def myHubAction = new hubitat.device.HubAction( // library marker davegut.tpLinkComms, line 75
cmdData, // library marker davegut.tpLinkComms, line 76
hubitat.device.Protocol.LAN, // library marker davegut.tpLinkComms, line 77
[type: hubitat.device.HubAction.Type.LAN_TYPE_UDPCLIENT, // library marker davegut.tpLinkComms, line 78
destinationAddress: "${ip}:${port}", // library marker davegut.tpLinkComms, line 79
encoding: hubitat.device.HubAction.Encoding.HEX_STRING, // library marker davegut.tpLinkComms, line 80
ignoreResponse: ignore, // library marker davegut.tpLinkComms, line 81
parseWarning: true, // library marker davegut.tpLinkComms, line 82
timeout: commsTo, // library marker davegut.tpLinkComms, line 83
callback: action]) // library marker davegut.tpLinkComms, line 84
try { // library marker davegut.tpLinkComms, line 85
sendHubCommand(myHubAction) // library marker davegut.tpLinkComms, line 86
} catch (error) { // library marker davegut.tpLinkComms, line 87
logWarn("sendLanCmd: command to ${ip}:${port} failed. Error = ${error}") // library marker davegut.tpLinkComms, line 88
} // library marker davegut.tpLinkComms, line 89
return // library marker davegut.tpLinkComms, line 90
} // library marker davegut.tpLinkComms, line 91
// Unknown Protocol method // library marker davegut.tpLinkComms, line 93
// ===== Communications Error Handling ===== // library marker davegut.tpLinkComms, line 94
def handleCommsError(status, msg = "") { // library marker davegut.tpLinkComms, line 95
// Retransmit all comms error except Switch and Level related (Hub retries for these). // library marker davegut.tpLinkComms, line 96
// This is determined by state.digital // library marker davegut.tpLinkComms, line 97
if (status == 200) { // library marker davegut.tpLinkComms, line 98
setCommsError(status, "OK") // library marker davegut.tpLinkComms, line 99
} else { // library marker davegut.tpLinkComms, line 100
Map logData = [method: "handleCommsError", status: code, msg: msg] // library marker davegut.tpLinkComms, line 101
def count = state.errorCount + 1 // library marker davegut.tpLinkComms, line 102
logData << [count: count, status: status, msg: msg] // library marker davegut.tpLinkComms, line 103
switch(count) { // library marker davegut.tpLinkComms, line 104
case 1: // library marker davegut.tpLinkComms, line 105
case 2: // library marker davegut.tpLinkComms, line 106
// errors 1 and 2, retry immediately // library marker davegut.tpLinkComms, line 107
runIn(1, delayedPassThrough) // library marker davegut.tpLinkComms, line 108
break // library marker davegut.tpLinkComms, line 109
case 3: // library marker davegut.tpLinkComms, line 110
// error 3, login or scan find device on the lan // library marker davegut.tpLinkComms, line 111
// then retry // library marker davegut.tpLinkComms, line 112
if (status == 403) { // library marker davegut.tpLinkComms, line 113
logData << [action: "attemptLogin"] // library marker davegut.tpLinkComms, line 114
// await device handshake result???? // library marker davegut.tpLinkComms, line 115
deviceHandshake() // library marker davegut.tpLinkComms, line 116
runIn(4, delayedPassThrough) // library marker davegut.tpLinkComms, line 117
} else { // library marker davegut.tpLinkComms, line 118
logData << [action: "Find on LAN then login"] // library marker davegut.tpLinkComms, line 119
configure() // library marker davegut.tpLinkComms, line 120
// await configure result????? // library marker davegut.tpLinkComms, line 121
runIn(10, delayedPassThrough) // library marker davegut.tpLinkComms, line 122
} // library marker davegut.tpLinkComms, line 123
break // library marker davegut.tpLinkComms, line 124
case 4: // library marker davegut.tpLinkComms, line 125
runIn(1, delayedPassThrough) // library marker davegut.tpLinkComms, line 126
break // library marker davegut.tpLinkComms, line 127
default: // library marker davegut.tpLinkComms, line 128
// Set comms error first time errros are 5 or more. // library marker davegut.tpLinkComms, line 129
logData << [action: "SetCommsErrorTrue"] // library marker davegut.tpLinkComms, line 130
setCommsError(status, msg, 5) // library marker davegut.tpLinkComms, line 131
} // library marker davegut.tpLinkComms, line 132
state.errorCount = count // library marker davegut.tpLinkComms, line 133
logInfo(logData) // library marker davegut.tpLinkComms, line 134
} // library marker davegut.tpLinkComms, line 135
} // library marker davegut.tpLinkComms, line 136
def delayedPassThrough() { // library marker davegut.tpLinkComms, line 138
def cmdData = new JSONObject(state.lastCommand) // library marker davegut.tpLinkComms, line 139
def cmdBody = parseJson(cmdData.cmdBody.toString()) // library marker davegut.tpLinkComms, line 140
asyncSend(cmdBody, cmdData.reqData, cmdData.action) // library marker davegut.tpLinkComms, line 141
} // library marker davegut.tpLinkComms, line 142
def setCommsError(status, msg = "OK", count = state.commsError) { // library marker davegut.tpLinkComms, line 144
Map logData = [method: "setCommsError", status: status, errorMsg: msg, count: count] // library marker davegut.tpLinkComms, line 145
if (device && status == 200) { // library marker davegut.tpLinkComms, line 146
state.errorCount = 0 // library marker davegut.tpLinkComms, line 147
if (device.currentValue("commsError") == "true") { // library marker davegut.tpLinkComms, line 148
sendEvent(name: "commsError", value: "false") // library marker davegut.tpLinkComms, line 149
setPollInterval() // library marker davegut.tpLinkComms, line 150
unschedule("errorConfigure") // library marker davegut.tpLinkComms, line 151
logInfo(logData) // library marker davegut.tpLinkComms, line 152
} // library marker davegut.tpLinkComms, line 153
} else if (device) { // library marker davegut.tpLinkComms, line 154
if (device.currentValue("commsError") == "false" && count > 4) { // library marker davegut.tpLinkComms, line 155
updateAttr("commsError", "true") // library marker davegut.tpLinkComms, line 156
setPollInterval("30 min") // library marker davegut.tpLinkComms, line 157
runEvery10Minutes(errorConfigure) // library marker davegut.tpLinkComms, line 158
logData << [pollInterval: "30 Min", errorConfigure: "ever 10 min"] // library marker davegut.tpLinkComms, line 159
logWarn(logData) // library marker davegut.tpLinkComms, line 160
if (status == 403) { // library marker davegut.tpLinkComms, line 161
logWarn(logInErrorAction()) // library marker davegut.tpLinkComms, line 162
} else { // library marker davegut.tpLinkComms, line 163
logWarn(lanErrorAction()) // library marker davegut.tpLinkComms, line 164
} // library marker davegut.tpLinkComms, line 165
} else { // library marker davegut.tpLinkComms, line 166
logData << [error: "Unspecified Error"] // library marker davegut.tpLinkComms, line 167
logWarn(logData) // library marker davegut.tpLinkComms, line 168
} // library marker davegut.tpLinkComms, line 169
} // library marker davegut.tpLinkComms, line 170
} // library marker davegut.tpLinkComms, line 171
def errorConfigure() { // library marker davegut.tpLinkComms, line 173
logDebug([method: "errorConfigure"]) // library marker davegut.tpLinkComms, line 174
if (device.currentValue("commsError") == "true") { // library marker davegut.tpLinkComms, line 175
configure() // library marker davegut.tpLinkComms, line 176
} else { // library marker davegut.tpLinkComms, line 177
unschedule("errorConfigure") // library marker davegut.tpLinkComms, line 178
} // library marker davegut.tpLinkComms, line 179
} // library marker davegut.tpLinkComms, line 180
def lanErrorAction() { // library marker davegut.tpLinkComms, line 182
def action = "Likely cause of this error is YOUR LAN device configuration: " // library marker davegut.tpLinkComms, line 183
action += "a. VERIFY your device is on the DHCP list in your router, " // library marker davegut.tpLinkComms, line 184
action += "b. VERIFY your device is in the active device list in your router, and " // library marker davegut.tpLinkComms, line 185
action += "c. TRY controlling your device from the TAPO phone app." // library marker davegut.tpLinkComms, line 186
return action // library marker davegut.tpLinkComms, line 187
} // library marker davegut.tpLinkComms, line 188
def logInErrorAction() { // library marker davegut.tpLinkComms, line 190
def action = "Likely cause is your login credentials are incorrect or the login has expired. " // library marker davegut.tpLinkComms, line 191
action += "a. RUN command Configure. b. If error persists, check your credentials in the App" // library marker davegut.tpLinkComms, line 192
return action // library marker davegut.tpLinkComms, line 193
} // library marker davegut.tpLinkComms, line 194
// ~~~~~ end include (385) davegut.tpLinkComms ~~~~~
// ~~~~~ start include (386) davegut.tpLinkCrypto ~~~~~
library ( // library marker davegut.tpLinkCrypto, line 1
name: "tpLinkCrypto", // library marker davegut.tpLinkCrypto, line 2
namespace: "davegut", // library marker davegut.tpLinkCrypto, line 3
author: "Compiled by Dave Gutheinz", // library marker davegut.tpLinkCrypto, line 4
description: "Handshake methods for TP-Link Integration", // library marker davegut.tpLinkCrypto, line 5
category: "utilities", // library marker davegut.tpLinkCrypto, line 6
documentationLink: "" // library marker davegut.tpLinkCrypto, line 7
) // library marker davegut.tpLinkCrypto, line 8
import java.security.spec.PKCS8EncodedKeySpec // library marker davegut.tpLinkCrypto, line 9
import javax.crypto.Cipher // library marker davegut.tpLinkCrypto, line 10
import java.security.KeyFactory // library marker davegut.tpLinkCrypto, line 11
import java.util.Random // library marker davegut.tpLinkCrypto, line 12
import javax.crypto.spec.SecretKeySpec // library marker davegut.tpLinkCrypto, line 13
import javax.crypto.spec.IvParameterSpec // library marker davegut.tpLinkCrypto, line 14
import java.security.MessageDigest // library marker davegut.tpLinkCrypto, line 15
// ===== Crypto Methods ===== // library marker davegut.tpLinkCrypto, line 17
def klapEncrypt(byte[] request, encKey, encIv, encSig, seqNo) { // library marker davegut.tpLinkCrypto, line 18
byte[] encSeqNo = integerToByteArray(seqNo) // library marker davegut.tpLinkCrypto, line 19
byte[] ivEnc = [encIv, encSeqNo].flatten() // library marker davegut.tpLinkCrypto, line 20
def cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") // library marker davegut.tpLinkCrypto, line 21
SecretKeySpec key = new SecretKeySpec(encKey, "AES") // library marker davegut.tpLinkCrypto, line 22
IvParameterSpec iv = new IvParameterSpec(ivEnc) // library marker davegut.tpLinkCrypto, line 23
cipher.init(Cipher.ENCRYPT_MODE, key, iv) // library marker davegut.tpLinkCrypto, line 24
byte[] cipherRequest = cipher.doFinal(request) // library marker davegut.tpLinkCrypto, line 25
byte[] payload = [encSig, encSeqNo, cipherRequest].flatten() // library marker davegut.tpLinkCrypto, line 27
byte[] signature = mdEncode("SHA-256", payload) // library marker davegut.tpLinkCrypto, line 28
cipherRequest = [signature, cipherRequest].flatten() // library marker davegut.tpLinkCrypto, line 29
return [cipherData: cipherRequest, seqNumber: seqNo] // library marker davegut.tpLinkCrypto, line 30
} // library marker davegut.tpLinkCrypto, line 31
def klapDecrypt(cipherResponse, encKey, encIv, seqNo) { // library marker davegut.tpLinkCrypto, line 33
byte[] encSeqNo = integerToByteArray(seqNo) // library marker davegut.tpLinkCrypto, line 34
byte[] ivEnc = [encIv, encSeqNo].flatten() // library marker davegut.tpLinkCrypto, line 35
def cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") // library marker davegut.tpLinkCrypto, line 36
SecretKeySpec key = new SecretKeySpec(encKey, "AES") // library marker davegut.tpLinkCrypto, line 37
IvParameterSpec iv = new IvParameterSpec(ivEnc) // library marker davegut.tpLinkCrypto, line 38
cipher.init(Cipher.DECRYPT_MODE, key, iv) // library marker davegut.tpLinkCrypto, line 39
byte[] byteResponse = cipher.doFinal(cipherResponse) // library marker davegut.tpLinkCrypto, line 40
return new String(byteResponse, "UTF-8") // library marker davegut.tpLinkCrypto, line 41
} // library marker davegut.tpLinkCrypto, line 42
def aesEncrypt(request, encKey, encIv) { // library marker davegut.tpLinkCrypto, line 44
def cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") // library marker davegut.tpLinkCrypto, line 45
SecretKeySpec key = new SecretKeySpec(encKey, "AES") // library marker davegut.tpLinkCrypto, line 46
IvParameterSpec iv = new IvParameterSpec(encIv) // library marker davegut.tpLinkCrypto, line 47
cipher.init(Cipher.ENCRYPT_MODE, key, iv) // library marker davegut.tpLinkCrypto, line 48
String result = cipher.doFinal(request.getBytes("UTF-8")).encodeBase64().toString() // library marker davegut.tpLinkCrypto, line 49
return result.replace("\r\n","") // library marker davegut.tpLinkCrypto, line 50
} // library marker davegut.tpLinkCrypto, line 51
def aesDecrypt(cipherResponse, encKey, encIv) { // library marker davegut.tpLinkCrypto, line 53
byte[] decodedBytes = cipherResponse.decodeBase64() // library marker davegut.tpLinkCrypto, line 54
def cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") // library marker davegut.tpLinkCrypto, line 55
SecretKeySpec key = new SecretKeySpec(encKey, "AES") // library marker davegut.tpLinkCrypto, line 56
IvParameterSpec iv = new IvParameterSpec(encIv) // library marker davegut.tpLinkCrypto, line 57
cipher.init(Cipher.DECRYPT_MODE, key, iv) // library marker davegut.tpLinkCrypto, line 58
return new String(cipher.doFinal(decodedBytes), "UTF-8") // library marker davegut.tpLinkCrypto, line 59
} // library marker davegut.tpLinkCrypto, line 60
// ===== Encoding Methods ===== // library marker davegut.tpLinkCrypto, line 62
def mdEncode(hashMethod, byte[] data) { // library marker davegut.tpLinkCrypto, line 63
MessageDigest md = MessageDigest.getInstance(hashMethod) // library marker davegut.tpLinkCrypto, line 64
md.update(data) // library marker davegut.tpLinkCrypto, line 65
return md.digest() // library marker davegut.tpLinkCrypto, line 66
} // library marker davegut.tpLinkCrypto, line 67
String encodeUtf8(String message) { // library marker davegut.tpLinkCrypto, line 69
byte[] arr = message.getBytes("UTF8") // library marker davegut.tpLinkCrypto, line 70
return new String(arr) // library marker davegut.tpLinkCrypto, line 71
} // library marker davegut.tpLinkCrypto, line 72
int byteArrayToInteger(byte[] byteArr) { // library marker davegut.tpLinkCrypto, line 74
int arrayASInteger // library marker davegut.tpLinkCrypto, line 75
try { // library marker davegut.tpLinkCrypto, line 76
arrayAsInteger = ((byteArr[0] & 0xFF) << 24) + ((byteArr[1] & 0xFF) << 16) + // library marker davegut.tpLinkCrypto, line 77
((byteArr[2] & 0xFF) << 8) + (byteArr[3] & 0xFF) // library marker davegut.tpLinkCrypto, line 78
} catch (error) { // library marker davegut.tpLinkCrypto, line 79
Map errLog = [byteArr: byteArr, ERROR: error] // library marker davegut.tpLinkCrypto, line 80
logWarn("byteArrayToInteger: ${errLog}") // library marker davegut.tpLinkCrypto, line 81
} // library marker davegut.tpLinkCrypto, line 82
return arrayAsInteger // library marker davegut.tpLinkCrypto, line 83
} // library marker davegut.tpLinkCrypto, line 84
byte[] integerToByteArray(value) { // library marker davegut.tpLinkCrypto, line 86
String hexValue = hubitat.helper.HexUtils.integerToHexString(value, 4) // library marker davegut.tpLinkCrypto, line 87
byte[] byteValue = hubitat.helper.HexUtils.hexStringToByteArray(hexValue) // library marker davegut.tpLinkCrypto, line 88
return byteValue // library marker davegut.tpLinkCrypto, line 89
} // library marker davegut.tpLinkCrypto, line 90
def getSeed(size) { // library marker davegut.tpLinkCrypto, line 92
byte[] temp = new byte[size] // library marker davegut.tpLinkCrypto, line 93
new Random().nextBytes(temp) // library marker davegut.tpLinkCrypto, line 94
return temp // library marker davegut.tpLinkCrypto, line 95
} // library marker davegut.tpLinkCrypto, line 96
// ~~~~~ end include (386) davegut.tpLinkCrypto ~~~~~
// ~~~~~ start include (388) davegut.tpLinkTransAes ~~~~~
library ( // library marker davegut.tpLinkTransAes, line 1
name: "tpLinkTransAes", // library marker davegut.tpLinkTransAes, line 2
namespace: "davegut", // library marker davegut.tpLinkTransAes, line 3
author: "Compiled by Dave Gutheinz", // library marker davegut.tpLinkTransAes, line 4
description: "Handshake methods for TP-Link Integration", // library marker davegut.tpLinkTransAes, line 5
category: "utilities", // library marker davegut.tpLinkTransAes, line 6
documentationLink: "" // library marker davegut.tpLinkTransAes, line 7
) // library marker davegut.tpLinkTransAes, line 8
def aesHandshake(baseUrl = getDataValue("baseUrl"), devData = null) { // library marker davegut.tpLinkTransAes, line 10
Map reqData = [baseUrl: baseUrl, devData: devData] // library marker davegut.tpLinkTransAes, line 11
Map rsaKey = getRsaKey() // library marker davegut.tpLinkTransAes, line 12
def pubPem = "-----BEGIN PUBLIC KEY-----\n${rsaKey.public}-----END PUBLIC KEY-----\n" // library marker davegut.tpLinkTransAes, line 13
Map cmdBody = [ method: "handshake", params: [ key: pubPem]] // library marker davegut.tpLinkTransAes, line 14
Map reqParams = [uri: baseUrl, // library marker davegut.tpLinkTransAes, line 15
body: new groovy.json.JsonBuilder(cmdBody).toString(), // library marker davegut.tpLinkTransAes, line 16
requestContentType: "application/json", // library marker davegut.tpLinkTransAes, line 17
timeout: 10] // library marker davegut.tpLinkTransAes, line 18
asynchttpPost("parseAesHandshake", reqParams, [data: reqData]) // library marker davegut.tpLinkTransAes, line 19
} // library marker davegut.tpLinkTransAes, line 20
def parseAesHandshake(resp, data){ // library marker davegut.tpLinkTransAes, line 22
Map logData = [method: "parseAesHandshake"] // library marker davegut.tpLinkTransAes, line 23
if (resp.status == 200 && resp.data != null) { // library marker davegut.tpLinkTransAes, line 24
try { // library marker davegut.tpLinkTransAes, line 25
Map reqData = [devData: data.data.devData, baseUrl: data.data.baseUrl] // library marker davegut.tpLinkTransAes, line 26
Map cmdResp = new JsonSlurper().parseText(resp.data) // library marker davegut.tpLinkTransAes, line 27
// cookie // library marker davegut.tpLinkTransAes, line 28
def cookieHeader = resp.headers["Set-Cookie"].toString() // library marker davegut.tpLinkTransAes, line 29
def cookie = cookieHeader.substring(cookieHeader.indexOf(":") +1, cookieHeader.indexOf(";")) // library marker davegut.tpLinkTransAes, line 30
// keys // library marker davegut.tpLinkTransAes, line 31
byte[] privateKeyBytes = getRsaKey().private.decodeBase64() // library marker davegut.tpLinkTransAes, line 32
byte[] deviceKeyBytes = cmdResp.result.key.getBytes("UTF-8").decodeBase64() // library marker davegut.tpLinkTransAes, line 33
Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding") // library marker davegut.tpLinkTransAes, line 34
instance.init(2, KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes))) // library marker davegut.tpLinkTransAes, line 35
byte[] cryptoArray = instance.doFinal(deviceKeyBytes) // library marker davegut.tpLinkTransAes, line 36
byte[] encKey = cryptoArray[0..15] // library marker davegut.tpLinkTransAes, line 37
byte[] encIv = cryptoArray[16..31] // library marker davegut.tpLinkTransAes, line 38
logData << [respStatus: "Cookies/Keys Updated", cookie: cookie, // library marker davegut.tpLinkTransAes, line 39
encKey: encKey, encIv: encIv] // library marker davegut.tpLinkTransAes, line 40
String password = encPassword // library marker davegut.tpLinkTransAes, line 41
String username = encUsername // library marker davegut.tpLinkTransAes, line 42
if (device) { // library marker davegut.tpLinkTransAes, line 43
password = parent.encPassword // library marker davegut.tpLinkTransAes, line 44
username = parent.encUsername // library marker davegut.tpLinkTransAes, line 45
device.updateSetting("cookie",[type:"password", value: cookie]) // library marker davegut.tpLinkTransAes, line 46
device.updateSetting("encKey",[type:"password", value: encKey]) // library marker davegut.tpLinkTransAes, line 47
device.updateSetting("encIv",[type:"password", value: encIv]) // library marker davegut.tpLinkTransAes, line 48
} else { // library marker davegut.tpLinkTransAes, line 49
reqData << [cookie: cookie, encIv: encIv, encKey: encKey] // library marker davegut.tpLinkTransAes, line 50
} // library marker davegut.tpLinkTransAes, line 51
Map cmdBody = [method: "login_device", // library marker davegut.tpLinkTransAes, line 52
params: [password: password, // library marker davegut.tpLinkTransAes, line 53
username: username], // library marker davegut.tpLinkTransAes, line 54
requestTimeMils: 0] // library marker davegut.tpLinkTransAes, line 55
def cmdStr = JsonOutput.toJson(cmdBody).toString() // library marker davegut.tpLinkTransAes, line 56
Map reqBody = [method: "securePassthrough", // library marker davegut.tpLinkTransAes, line 57
params: [request: aesEncrypt(cmdStr, encKey, encIv)]] // library marker davegut.tpLinkTransAes, line 58
Map reqParams = [uri: reqData.baseUrl, // library marker davegut.tpLinkTransAes, line 59
body: reqBody, // library marker davegut.tpLinkTransAes, line 60
timeout:10, // library marker davegut.tpLinkTransAes, line 61
headers: ["Cookie": cookie], // library marker davegut.tpLinkTransAes, line 62
contentType: "application/json", // library marker davegut.tpLinkTransAes, line 63
requestContentType: "application/json"] // library marker davegut.tpLinkTransAes, line 64
asynchttpPost("parseAesLogin", reqParams, [data: reqData]) // library marker davegut.tpLinkTransAes, line 65
logDebug(logData) // library marker davegut.tpLinkTransAes, line 66
} catch (err) { // library marker davegut.tpLinkTransAes, line 67
logData << [respStatus: "ERROR parsing HTTP resp.data", // library marker davegut.tpLinkTransAes, line 68
respData: resp.data, error: err] // library marker davegut.tpLinkTransAes, line 69
logWarn(logData) // library marker davegut.tpLinkTransAes, line 70
} // library marker davegut.tpLinkTransAes, line 71
} else { // library marker davegut.tpLinkTransAes, line 72
logData << [respStatus: "ERROR in HTTP response", resp: resp.properties] // library marker davegut.tpLinkTransAes, line 73
logWarn(logData) // library marker davegut.tpLinkTransAes, line 74
} // library marker davegut.tpLinkTransAes, line 75
} // library marker davegut.tpLinkTransAes, line 76
def parseAesLogin(resp, data) { // library marker davegut.tpLinkTransAes, line 78
if (device) { // library marker davegut.tpLinkTransAes, line 79
Map logData = [method: "parseAesLogin"] // library marker davegut.tpLinkTransAes, line 80
if (resp.status == 200) { // library marker davegut.tpLinkTransAes, line 81
if (resp.json.error_code == 0) { // library marker davegut.tpLinkTransAes, line 82
try { // library marker davegut.tpLinkTransAes, line 83
byte[] encKey = new JsonSlurper().parseText(encKey) // library marker davegut.tpLinkTransAes, line 84
byte[] encIv = new JsonSlurper().parseText(encIv) // library marker davegut.tpLinkTransAes, line 85
def clearResp = aesDecrypt(resp.json.result.response, encKey, encIv) // library marker davegut.tpLinkTransAes, line 86
Map cmdResp = new JsonSlurper().parseText(clearResp) // library marker davegut.tpLinkTransAes, line 87
if (cmdResp.error_code == 0) { // library marker davegut.tpLinkTransAes, line 88
def token = cmdResp.result.token // library marker davegut.tpLinkTransAes, line 89
logData << [respStatus: "OK", token: token] // library marker davegut.tpLinkTransAes, line 90
device.updateSetting("token",[type:"password", value: token]) // library marker davegut.tpLinkTransAes, line 91
setCommsError(200) // library marker davegut.tpLinkTransAes, line 92
logDebug(logData) // library marker davegut.tpLinkTransAes, line 93
} else { // library marker davegut.tpLinkTransAes, line 94
logData << [respStatus: "ERROR code in cmdResp", // library marker davegut.tpLinkTransAes, line 95
error_code: cmdResp.error_code, // library marker davegut.tpLinkTransAes, line 96
check: "cryptoArray, credentials", data: cmdResp] // library marker davegut.tpLinkTransAes, line 97
logInfo(logData) // library marker davegut.tpLinkTransAes, line 98
} // library marker davegut.tpLinkTransAes, line 99
} catch (err) { // library marker davegut.tpLinkTransAes, line 100
logData << [respStatus: "ERROR parsing respJson", respJson: resp.json, // library marker davegut.tpLinkTransAes, line 101
error: err] // library marker davegut.tpLinkTransAes, line 102
logInfo(logData) // library marker davegut.tpLinkTransAes, line 103
} // library marker davegut.tpLinkTransAes, line 104
} else { // library marker davegut.tpLinkTransAes, line 105
logData << [respStatus: "ERROR code in resp.json", errorCode: resp.json.error_code, // library marker davegut.tpLinkTransAes, line 106
respJson: resp.json] // library marker davegut.tpLinkTransAes, line 107
logInfo(logData) // library marker davegut.tpLinkTransAes, line 108
} // library marker davegut.tpLinkTransAes, line 109
} else { // library marker davegut.tpLinkTransAes, line 110
logData << [respStatus: "ERROR in HTTP response", respStatus: resp.status, data: resp.properties] // library marker davegut.tpLinkTransAes, line 111
logInfo(logData) // library marker davegut.tpLinkTransAes, line 112
} // library marker davegut.tpLinkTransAes, line 113
} else { // library marker davegut.tpLinkTransAes, line 114
// Code used in application only. // library marker davegut.tpLinkTransAes, line 115
getAesToken(resp, data.data) // library marker davegut.tpLinkTransAes, line 116
} // library marker davegut.tpLinkTransAes, line 117
} // library marker davegut.tpLinkTransAes, line 118
def getAesParams(cmdBody) { // library marker davegut.tpLinkTransAes, line 120
byte[] encKey = new JsonSlurper().parseText(encKey) // library marker davegut.tpLinkTransAes, line 121
byte[] encIv = new JsonSlurper().parseText(encIv) // library marker davegut.tpLinkTransAes, line 122
def cmdStr = JsonOutput.toJson(cmdBody).toString() // library marker davegut.tpLinkTransAes, line 123
Map reqBody = [method: "securePassthrough", // library marker davegut.tpLinkTransAes, line 124
params: [request: aesEncrypt(cmdStr, encKey, encIv)]] // library marker davegut.tpLinkTransAes, line 125
Map reqParams = [uri: "${getDataValue("baseUrl")}?token=${token}", // library marker davegut.tpLinkTransAes, line 126
body: new groovy.json.JsonBuilder(reqBody).toString(), // library marker davegut.tpLinkTransAes, line 127
contentType: "application/json", // library marker davegut.tpLinkTransAes, line 128
requestContentType: "application/json", // library marker davegut.tpLinkTransAes, line 129
timeout: 10, // library marker davegut.tpLinkTransAes, line 130
ignoreSSLIssues: true, // library marker davegut.tpLinkTransAes, line 131
headers: ["Cookie": cookie]] // library marker davegut.tpLinkTransAes, line 132
return reqParams // library marker davegut.tpLinkTransAes, line 133
} // library marker davegut.tpLinkTransAes, line 134
def parseAesData(resp, data) { // library marker davegut.tpLinkTransAes, line 136
Map parseData = [parseMethod: "parseAesData", sourceMethod: data.data] // library marker davegut.tpLinkTransAes, line 137
try { // library marker davegut.tpLinkTransAes, line 138
byte[] encKey = new JsonSlurper().parseText(encKey) // library marker davegut.tpLinkTransAes, line 139
byte[] encIv = new JsonSlurper().parseText(encIv) // library marker davegut.tpLinkTransAes, line 140
Map cmdResp = new JsonSlurper().parseText(aesDecrypt(resp.json.result.response, // library marker davegut.tpLinkTransAes, line 141
encKey, encIv)) // library marker davegut.tpLinkTransAes, line 142
parseData << [cryptoStatus: "OK", cmdResp: cmdResp] // library marker davegut.tpLinkTransAes, line 143
} catch (err) { // library marker davegut.tpLinkTransAes, line 144
parseData << [cryptoStatus: "decryptDataError", error: err, dataLength: resp.data.length()] // library marker davegut.tpLinkTransAes, line 145
} // library marker davegut.tpLinkTransAes, line 146
return parseData // library marker davegut.tpLinkTransAes, line 147
} // library marker davegut.tpLinkTransAes, line 148
def getRsaKey() { // library marker davegut.tpLinkTransAes, line 150
return [public: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGr/mHBK8aqx7UAS+g+TuAvE3J2DdwsqRn9MmAkjPGNon1ZlwM6nLQHfJHebdohyVqkNWaCECGXnftnlC8CM2c/RujvCrStRA0lVD+jixO9QJ9PcYTa07Z1FuEze7Q5OIa6pEoPxomrjxzVlUWLDXt901qCdn3/zRZpBdpXzVZtQIDAQAB", // library marker davegut.tpLinkTransAes, line 151
private: "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMav+YcErxqrHtQBL6D5O4C8TcnYN3CypGf0yYCSM8Y2ifVmXAzqctAd8kd5t2iHJWqQ1ZoIQIZed+2eULwIzZz9G6O8KtK1EDSVUP6OLE71An09xhNrTtnUW4TN7tDk4hrqkSg/GiauPHNWVRYsNe33TWoJ2ff/NFmkF2lfNVm1AgMBAAECgYEAocxCHmKBGe2KAEkq+SKdAxvVGO77TsobOhDMWug0Q1C8jduaUGZHsxT/7JbA9d1AagSh/XqE2Sdq8FUBF+7vSFzozBHyGkrX1iKURpQFEQM2j9JgUCucEavnxvCqDYpscyNRAgqz9jdh+BjEMcKAG7o68bOw41ZC+JyYR41xSe0CQQD1os71NcZiMVqYcBud6fTYFHZz3HBNcbzOk+RpIHyi8aF3zIqPKIAh2pO4s7vJgrMZTc2wkIe0ZnUrm0oaC//jAkEAzxIPW1mWd3+KE3gpgyX0cFkZsDmlIbWojUIbyz8NgeUglr+BczARG4ITrTV4fxkGwNI4EZxBT8vXDSIXJ8NDhwJBAIiKndx0rfg7Uw7VkqRvPqk2hrnU2aBTDw8N6rP9WQsCoi0DyCnX65Hl/KN5VXOocYIpW6NAVA8VvSAmTES6Ut0CQQCX20jD13mPfUsHaDIZafZPhiheoofFpvFLVtYHQeBoCF7T7vHCRdfl8oj3l6UcoH/hXMmdsJf9KyI1EXElyf91AkAvLfmAS2UvUnhX4qyFioitjxwWawSnf+CewN8LDbH7m5JVXJEh3hqp+aLHg1EaW4wJtkoKLCF+DeVIgbSvOLJw"] // library marker davegut.tpLinkTransAes, line 152
} // library marker davegut.tpLinkTransAes, line 153
// ~~~~~ end include (388) davegut.tpLinkTransAes ~~~~~
// ~~~~~ start include (390) davegut.tpLinkTransKlap ~~~~~
library ( // library marker davegut.tpLinkTransKlap, line 1
name: "tpLinkTransKlap", // library marker davegut.tpLinkTransKlap, line 2
namespace: "davegut", // library marker davegut.tpLinkTransKlap, line 3
author: "Compiled by Dave Gutheinz", // library marker davegut.tpLinkTransKlap, line 4
description: "Handshake methods for TP-Link Integration", // library marker davegut.tpLinkTransKlap, line 5
category: "utilities", // library marker davegut.tpLinkTransKlap, line 6
documentationLink: "" // library marker davegut.tpLinkTransKlap, line 7
) // library marker davegut.tpLinkTransKlap, line 8
def klapHandshake(baseUrl, localHash, devData = null) { // library marker davegut.tpLinkTransKlap, line 10
byte[] localSeed = getSeed(16) // library marker davegut.tpLinkTransKlap, line 11
Map reqData = [localSeed: localSeed, baseUrl: baseUrl, localHash: localHash, devData:devData] // library marker davegut.tpLinkTransKlap, line 12
Map reqParams = [uri: "${baseUrl}/handshake1", // library marker davegut.tpLinkTransKlap, line 13
body: localSeed, // library marker davegut.tpLinkTransKlap, line 14
contentType: "application/octet-stream", // library marker davegut.tpLinkTransKlap, line 15
requestContentType: "application/octet-stream", // library marker davegut.tpLinkTransKlap, line 16
timeout:10, // library marker davegut.tpLinkTransKlap, line 17
ignoreSSLIssues: true] // library marker davegut.tpLinkTransKlap, line 18
asynchttpPost("parseKlapHandshake", reqParams, [data: reqData]) // library marker davegut.tpLinkTransKlap, line 19
} // library marker davegut.tpLinkTransKlap, line 20
def parseKlapHandshake(resp, data) { // library marker davegut.tpLinkTransKlap, line 22
Map logData = [method: "parseKlapHandshake"] // library marker davegut.tpLinkTransKlap, line 23
if (resp.status == 200 && resp.data != null) { // library marker davegut.tpLinkTransKlap, line 24
try { // library marker davegut.tpLinkTransKlap, line 25
Map reqData = [devData: data.data.devData, baseUrl: data.data.baseUrl] // library marker davegut.tpLinkTransKlap, line 26
byte[] localSeed = data.data.localSeed // library marker davegut.tpLinkTransKlap, line 27
byte[] seedData = resp.data.decodeBase64() // library marker davegut.tpLinkTransKlap, line 28
byte[] remoteSeed = seedData[0 .. 15] // library marker davegut.tpLinkTransKlap, line 29
byte[] serverHash = seedData[16 .. 47] // library marker davegut.tpLinkTransKlap, line 30
byte[] localHash = data.data.localHash.decodeBase64() // library marker davegut.tpLinkTransKlap, line 31
byte[] authHash = [localSeed, remoteSeed, localHash].flatten() // library marker davegut.tpLinkTransKlap, line 32
byte[] localAuthHash = mdEncode("SHA-256", authHash) // library marker davegut.tpLinkTransKlap, line 33
if (localAuthHash == serverHash) { // library marker davegut.tpLinkTransKlap, line 34
// cookie // library marker davegut.tpLinkTransKlap, line 35
def cookieHeader = resp.headers["Set-Cookie"].toString() // library marker davegut.tpLinkTransKlap, line 36
def cookie = cookieHeader.substring(cookieHeader.indexOf(":") +1, cookieHeader.indexOf(";")) // library marker davegut.tpLinkTransKlap, line 37
// seqNo and encIv // library marker davegut.tpLinkTransKlap, line 38
byte[] payload = ["iv".getBytes(), localSeed, remoteSeed, localHash].flatten() // library marker davegut.tpLinkTransKlap, line 39
byte[] fullIv = mdEncode("SHA-256", payload) // library marker davegut.tpLinkTransKlap, line 40
byte[] byteSeqNo = fullIv[-4..-1] // library marker davegut.tpLinkTransKlap, line 41
int seqNo = byteArrayToInteger(byteSeqNo) // library marker davegut.tpLinkTransKlap, line 43
if (device) { // library marker davegut.tpLinkTransKlap, line 44
state.seqNo = seqNo // library marker davegut.tpLinkTransKlap, line 45
} else { // library marker davegut.tpLinkTransKlap, line 46
atomicState.seqNo = seqNo // library marker davegut.tpLinkTransKlap, line 47
} // library marker davegut.tpLinkTransKlap, line 48
// encKey // library marker davegut.tpLinkTransKlap, line 50
payload = ["lsk".getBytes(), localSeed, remoteSeed, localHash].flatten() // library marker davegut.tpLinkTransKlap, line 51
byte[] encKey = mdEncode("SHA-256", payload)[0..15] // library marker davegut.tpLinkTransKlap, line 52
// encSig // library marker davegut.tpLinkTransKlap, line 53
payload = ["ldk".getBytes(), localSeed, remoteSeed, localHash].flatten() // library marker davegut.tpLinkTransKlap, line 54
byte[] encSig = mdEncode("SHA-256", payload)[0..27] // library marker davegut.tpLinkTransKlap, line 55
if (device) { // library marker davegut.tpLinkTransKlap, line 56
device.updateSetting("cookie",[type:"password", value: cookie]) // library marker davegut.tpLinkTransKlap, line 57
device.updateSetting("encKey",[type:"password", value: encKey]) // library marker davegut.tpLinkTransKlap, line 58
device.updateSetting("encIv",[type:"password", value: fullIv[0..11]]) // library marker davegut.tpLinkTransKlap, line 59
device.updateSetting("encSig",[type:"password", value: encSig]) // library marker davegut.tpLinkTransKlap, line 60
} else { // library marker davegut.tpLinkTransKlap, line 61
reqData << [cookie: cookie, seqNo: seqNo, encIv: fullIv[0..11], // library marker davegut.tpLinkTransKlap, line 62
encSig: encSig, encKey: encKey] // library marker davegut.tpLinkTransKlap, line 63
} // library marker davegut.tpLinkTransKlap, line 64
byte[] loginHash = [remoteSeed, localSeed, localHash].flatten() // library marker davegut.tpLinkTransKlap, line 65
byte[] body = mdEncode("SHA-256", loginHash) // library marker davegut.tpLinkTransKlap, line 66
Map reqParams = [uri: "${data.data.baseUrl}/handshake2", // library marker davegut.tpLinkTransKlap, line 67
body: body, // library marker davegut.tpLinkTransKlap, line 68
timeout:10, // library marker davegut.tpLinkTransKlap, line 69
ignoreSSLIssues: true, // library marker davegut.tpLinkTransKlap, line 70
headers: ["Cookie": cookie], // library marker davegut.tpLinkTransKlap, line 71
contentType: "application/octet-stream", // library marker davegut.tpLinkTransKlap, line 72
requestContentType: "application/octet-stream"] // library marker davegut.tpLinkTransKlap, line 73
asynchttpPost("parseKlapHandshake2", reqParams, [data: reqData]) // library marker davegut.tpLinkTransKlap, line 74
} else { // library marker davegut.tpLinkTransKlap, line 75
logData << [respStatus: "ERROR: localAuthHash != serverHash", data: data, // library marker davegut.tpLinkTransKlap, line 76
action: "Check credentials and try again"] // library marker davegut.tpLinkTransKlap, line 77
logWarn(logData) // library marker davegut.tpLinkTransKlap, line 78
} // library marker davegut.tpLinkTransKlap, line 79
} catch (err) { // library marker davegut.tpLinkTransKlap, line 80
logData << [respStatus: "ERROR parsing 200 response", resp: resp.properties, error: err] // library marker davegut.tpLinkTransKlap, line 81
logData << [action: "Try Configure command"] // library marker davegut.tpLinkTransKlap, line 82
logWarn(logData) // library marker davegut.tpLinkTransKlap, line 83
} // library marker davegut.tpLinkTransKlap, line 84
} else { // library marker davegut.tpLinkTransKlap, line 85
logData << [respStatus: resp.status, message: resp.errorMessage] // library marker davegut.tpLinkTransKlap, line 86
logData << [action: "Try Configure command"] // library marker davegut.tpLinkTransKlap, line 87
logWarn(logData) // library marker davegut.tpLinkTransKlap, line 88
} // library marker davegut.tpLinkTransKlap, line 89
} // library marker davegut.tpLinkTransKlap, line 90
def parseKlapHandshake2(resp, data) { // library marker davegut.tpLinkTransKlap, line 92
Map logData = [method: "parseKlapHandshake2"] // library marker davegut.tpLinkTransKlap, line 93
if (resp.status == 200 && resp.data == null) { // library marker davegut.tpLinkTransKlap, line 94
logData << [respStatus: "Login OK"] // library marker davegut.tpLinkTransKlap, line 95
setCommsError(200) // library marker davegut.tpLinkTransKlap, line 96
logDebug(logData) // library marker davegut.tpLinkTransKlap, line 97
} else { // library marker davegut.tpLinkTransKlap, line 98
logData << [respStatus: "LOGIN FAILED", reason: "ERROR in HTTP response", // library marker davegut.tpLinkTransKlap, line 99
resp: resp.properties] // library marker davegut.tpLinkTransKlap, line 100
logWarn(logData) // library marker davegut.tpLinkTransKlap, line 101
} // library marker davegut.tpLinkTransKlap, line 102
if (!device) { sendKlapDataCmd(logData, data) } // library marker davegut.tpLinkTransKlap, line 103
} // library marker davegut.tpLinkTransKlap, line 104
def getKlapParams(cmdBody) { // library marker davegut.tpLinkTransKlap, line 106
int seqNo = state.seqNo + 1 // library marker davegut.tpLinkTransKlap, line 107
state.seqNo = seqNo // library marker davegut.tpLinkTransKlap, line 108
byte[] encKey = new JsonSlurper().parseText(encKey) // library marker davegut.tpLinkTransKlap, line 109
byte[] encIv = new JsonSlurper().parseText(encIv) // library marker davegut.tpLinkTransKlap, line 110
byte[] encSig = new JsonSlurper().parseText(encSig) // library marker davegut.tpLinkTransKlap, line 111
String cmdBodyJson = new groovy.json.JsonBuilder(cmdBody).toString() // library marker davegut.tpLinkTransKlap, line 112
Map encryptedData = klapEncrypt(cmdBodyJson.getBytes(), encKey, encIv, // library marker davegut.tpLinkTransKlap, line 114
encSig, seqNo) // library marker davegut.tpLinkTransKlap, line 115
Map reqParams = [ // library marker davegut.tpLinkTransKlap, line 116
uri: "${getDataValue("baseUrl")}/request?seq=${seqNo}", // library marker davegut.tpLinkTransKlap, line 117
body: encryptedData.cipherData, // library marker davegut.tpLinkTransKlap, line 118
headers: ["Cookie": cookie], // library marker davegut.tpLinkTransKlap, line 119
contentType: "application/octet-stream", // library marker davegut.tpLinkTransKlap, line 120
requestContentType: "application/octet-stream", // library marker davegut.tpLinkTransKlap, line 121
timeout: 10, // library marker davegut.tpLinkTransKlap, line 122
ignoreSSLIssues: true] // library marker davegut.tpLinkTransKlap, line 123
return reqParams // library marker davegut.tpLinkTransKlap, line 124
} // library marker davegut.tpLinkTransKlap, line 125
def parseKlapData(resp, data) { // library marker davegut.tpLinkTransKlap, line 127
Map parseData = [Method: "parseKlapData", sourceMethod: data.data] // library marker davegut.tpLinkTransKlap, line 128
try { // library marker davegut.tpLinkTransKlap, line 129
byte[] encKey = new JsonSlurper().parseText(encKey) // library marker davegut.tpLinkTransKlap, line 130
byte[] encIv = new JsonSlurper().parseText(encIv) // library marker davegut.tpLinkTransKlap, line 131
int seqNo = state.seqNo // library marker davegut.tpLinkTransKlap, line 132
byte[] cipherResponse = resp.data.decodeBase64()[32..-1] // library marker davegut.tpLinkTransKlap, line 133
Map cmdResp = new JsonSlurper().parseText(klapDecrypt(cipherResponse, encKey, // library marker davegut.tpLinkTransKlap, line 134
encIv, seqNo)) // library marker davegut.tpLinkTransKlap, line 135
parseData << [cryptoStatus: "OK", cmdResp: cmdResp] // library marker davegut.tpLinkTransKlap, line 136
} catch (err) { // library marker davegut.tpLinkTransKlap, line 137
parseData << [cryptoStatus: "decryptDataError", error: err] // library marker davegut.tpLinkTransKlap, line 138
} // library marker davegut.tpLinkTransKlap, line 139
return parseData // library marker davegut.tpLinkTransKlap, line 140
} // library marker davegut.tpLinkTransKlap, line 141
// ~~~~~ end include (390) davegut.tpLinkTransKlap ~~~~~
// ~~~~~ start include (376) davegut.Logging ~~~~~
library ( // library marker davegut.Logging, line 1
name: "Logging", // library marker davegut.Logging, line 2
namespace: "davegut", // library marker davegut.Logging, line 3
author: "Dave Gutheinz", // library marker davegut.Logging, line 4
description: "Common Logging and info gathering Methods", // library marker davegut.Logging, line 5
category: "utilities", // library marker davegut.Logging, line 6
documentationLink: "" // library marker davegut.Logging, line 7
) // library marker davegut.Logging, line 8
def nameSpace() { return "davegut" } // library marker davegut.Logging, line 10
def version() { return "2.4.2a" } // library marker davegut.Logging, line 12
def label() { // library marker davegut.Logging, line 14
if (device) { // library marker davegut.Logging, line 15
return device.displayName + "-${version()}" // library marker davegut.Logging, line 16
} else { // library marker davegut.Logging, line 17
return app.getLabel() + "-${version()}" // library marker davegut.Logging, line 18
} // library marker davegut.Logging, line 19
} // library marker davegut.Logging, line 20
def updateAttr(attr, value) { // library marker davegut.Logging, line 22
if (device.currentValue(attr) != value) { // library marker davegut.Logging, line 23
sendEvent(name: attr, value: value) // library marker davegut.Logging, line 24
} // library marker davegut.Logging, line 25
} // library marker davegut.Logging, line 26
def listAttributes() { // library marker davegut.Logging, line 28
def attrData = device.getCurrentStates() // library marker davegut.Logging, line 29
Map attrs = [:] // library marker davegut.Logging, line 30
attrData.each { // library marker davegut.Logging, line 31
attrs << ["${it.name}": it.value] // library marker davegut.Logging, line 32
} // library marker davegut.Logging, line 33
return attrs // library marker davegut.Logging, line 34
} // library marker davegut.Logging, line 35
def setLogsOff() { // library marker davegut.Logging, line 37
def logData = [infoLog: infoLog, logEnable: logEnable] // library marker davegut.Logging, line 38
if (logEnable) { // library marker davegut.Logging, line 39
runIn(1800, debugLogOff) // library marker davegut.Logging, line 40
logData << [debugLogOff: "scheduled"] // library marker davegut.Logging, line 41
} // library marker davegut.Logging, line 42
return logData // library marker davegut.Logging, line 43
} // library marker davegut.Logging, line 44
def logTrace(msg){ log.trace "${label()}: ${msg}" } // library marker davegut.Logging, line 46
def logInfo(msg) { // library marker davegut.Logging, line 48
if (infoLog) { log.info "${label()}: ${msg}" } // library marker davegut.Logging, line 49
} // library marker davegut.Logging, line 50
def debugLogOff() { // library marker davegut.Logging, line 52
if (device) { // library marker davegut.Logging, line 53
device.updateSetting("logEnable", [type:"bool", value: false]) // library marker davegut.Logging, line 54
} else { // library marker davegut.Logging, line 55
app.updateSetting("logEnable", false) // library marker davegut.Logging, line 56
} // library marker davegut.Logging, line 57
logInfo("debugLogOff") // library marker davegut.Logging, line 58
} // library marker davegut.Logging, line 59
def logDebug(msg) { // library marker davegut.Logging, line 61
if (logEnable) { log.debug "${label()}: ${msg}" } // library marker davegut.Logging, line 62
} // library marker davegut.Logging, line 63
def logWarn(msg) { log.warn "${label()}: ${msg}" } // library marker davegut.Logging, line 65
def logError(msg) { log.error "${label()}: ${msg}" } // library marker davegut.Logging, line 67
// ~~~~~ end include (376) davegut.Logging ~~~~~