Compare commits

...

1 Commits

Author SHA1 Message Date
Nicholas Atherton
9b9665eb59 MSA-2605: Kasa Switch Control 2018-02-03 23:12:26 -08:00

View File

@@ -0,0 +1,223 @@
/*
TP-Link Plug and Switch Device Handler, 2018, Version 2
Copyright 2018 Dave Gutheinz
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.
Discalimer: This Service Manager and the associated Device
Handlers are in no way sanctioned or supported by TP-Link.
All development is based upon open-source data on the
TP-Link devices; primarily various users on GitHub.com.
===== History =============================================
2018-01-31 Update to Version 2
a. Common file content for all bulb implementations,
using separate files by model only.
b. User file-internal selection of Energy Monitor
function enabling.
// ===== Hub or Cloud Installation =========================*/
def installType = "Cloud"
//def installType = "Hub"
// ===========================================================
metadata {
definition (name: "(${installType}) TP-Link Plug-Switch",
namespace: "davegut",
author: "Dave Gutheinz",
deviceType: "Plug-Switch",
energyMonitor: "Standard",
installType: "${installType}") {
capability "Switch"
capability "refresh"
capability "polling"
capability "Sensor"
capability "Actuator"
}
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc",
nextState:"waiting"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff",
nextState:"waiting"
attributeState "waiting", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#15EE10",
nextState:"waiting"
attributeState "commsError", label:'Comms Error', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#e86d13",
nextState:"waiting"
}
tileAttribute ("deviceError", key: "SECONDARY_CONTROL") {
attributeState "deviceError", label: '${currentValue}'
}
}
standardTile("refresh", "capability.refresh", width: 2, height: 1, decoration: "flat") {
state "default", label:"Refresh", action:"refresh.refresh"
}
main("switch")
details("switch", "refresh")
}
def rates = [:]
rates << ["5" : "Refresh every 5 minutes"]
rates << ["10" : "Refresh every 10 minutes"]
rates << ["15" : "Refresh every 15 minutes"]
rates << ["30" : "Refresh every 30 minutes"]
preferences {
if (installType == "Hub") {
input("deviceIP", "text", title: "Device IP", required: true, displayDuringSetup: true)
input("gatewayIP", "text", title: "Gateway IP", required: true, displayDuringSetup: true)
}
input name: "refreshRate", type: "enum", title: "Refresh Rate", options: rates, description: "Select Refresh Rate", required: false
}
}
// ===== Update when installed or setting changed =====
def installed() {
update()
}
def updated() {
runIn(2, update)
}
def update() {
state.deviceType = metadata.definition.deviceType
state.installType = metadata.definition.installType
unschedule()
switch(refreshRate) {
case "5":
runEvery5Minutes(refresh)
log.info "Refresh Scheduled for every 5 minutes"
break
case "10":
runEvery10Minutes(refresh)
log.info "Refresh Scheduled for every 10 minutes"
break
case "15":
runEvery15Minutes(refresh)
log.info "Refresh Scheduled for every 15 minutes"
break
default:
runEvery30Minutes(refresh)
log.info "Refresh Scheduled for every 30 minutes"
}
runIn(5, refresh)
}
void uninstalled() {
if (state.installType == "Cloud") {
def alias = device.label
log.debug "Removing device ${alias} with DNI = ${device.deviceNetworkId}"
parent.removeChildDevice(alias, device.deviceNetworkId)
}
}
// ===== Basic Plug Control/Status =====
def on() {
sendCmdtoServer('{"system":{"set_relay_state":{"state": 1}}}', "deviceCommand", "commandResponse")
}
def off() {
sendCmdtoServer('{"system":{"set_relay_state":{"state": 0}}}', "deviceCommand", "commandResponse")
}
def poll() {
sendCmdtoServer('{"system":{"get_sysinfo":{}}}', "deviceCommand", "commandResponse")
}
def refresh(){
sendCmdtoServer('{"system":{"get_sysinfo":{}}}', "deviceCommand", "commandResponse")
}
def commandResponse(cmdResponse){
if (cmdResponse.system.set_relay_state == null) {
def status = cmdResponse.system.get_sysinfo.relay_state
if (status == 1) {
status = "on"
} else {
status = "off"
}
log.info "${device.name} ${device.label}: Power: ${status}"
sendEvent(name: "switch", value: status)
} else {
refresh()
}
}
// ----- SEND COMMAND TO CLOUD VIA SM -----
private sendCmdtoServer(command, hubCommand, action) {
if (state.installType == "Hub") {
sendCmdtoHub(command, hubCommand, action)
} else {
sendCmdtoCloud(command, hubCommand, action)
}
}
private sendCmdtoCloud(command, hubCommand, action){
def appServerUrl = getDataValue("appServerUrl")
def deviceId = getDataValue("deviceId")
def cmdResponse = parent.sendDeviceCmd(appServerUrl, deviceId, command)
String cmdResp = cmdResponse.toString()
if (cmdResp.substring(0,5) == "ERROR"){
def errMsg = cmdResp.substring(7,cmdResp.length())
log.error "${device.name} ${device.label}: ${errMsg}"
sendEvent(name: "switch", value: "commsError", descriptionText: errMsg)
sendEvent(name: "deviceError", value: errMsg)
action = ""
} else {
sendEvent(name: "deviceError", value: "OK")
}
actionDirector(action, cmdResponse)
}
private sendCmdtoHub(command, hubCommand, action){
def headers = [:]
headers.put("HOST", "$gatewayIP:8082") // Same as on Hub.
headers.put("tplink-iot-ip", deviceIP)
headers.put("tplink-command", command)
headers.put("action", action)
headers.put("command", hubCommand)
sendHubCommand(new physicalgraph.device.HubAction([
headers: headers],
device.deviceNetworkId,
[callback: hubResponseParse]
))
}
def hubResponseParse(response) {
def action = response.headers["action"]
def cmdResponse = parseJson(response.headers["cmd-response"])
if (cmdResponse == "TcpTimeout") {
log.error "$device.name $device.label: Communications Error"
sendEvent(name: "switch", value: "offline", descriptionText: "ERROR at hubResponseParse TCP Timeout")
sendEvent(name: "deviceError", value: "TCP Timeout in Hub")
} else {
actionDirector(action, cmdResponse)
sendEvent(name: "deviceError", value: "OK")
}
}
def actionDirector(action, cmdResponse) {
switch(action) {
case "commandResponse":
commandResponse(cmdResponse)
break
default:
log.debug "at default"
}
}
// ----- CHILD / PARENT INTERCHANGE TASKS -----
def syncAppServerUrl(newAppServerUrl) {
updateDataValue("appServerUrl", newAppServerUrl)
log.info "Updated appServerUrl for ${device.name} ${device.label}"
}