mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-14 21:03:19 +00:00
Compare commits
1 Commits
MSA-999-5
...
MSA-996-21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8ac40d532 |
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Copyright 2015 Astralink
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
|
||||
definition (name: "Philio Smart Energy Plug in Switch", namespace: "astralink", author: "AstraLink"){
|
||||
capability "Relay Switch"
|
||||
capability "Power Meter"
|
||||
capability "Energy Meter"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
attribute "ManufacturerCode", "string"
|
||||
attribute "ProductCode", "string"
|
||||
attribute "ProduceTypeCode", "string"
|
||||
attribute "WirelessConfig", "string"
|
||||
attribute "WakeUp", "string"
|
||||
|
||||
fingerprint deviceId: "0x1001", inClusters: "0x5E, 0x86, 0x72, 0x98, 0x5A, 0x85, 0x59, 0x73, 0x25, 0x20, 0x27, 0x32, 0x70, 0x71, 0x75, 0x7A"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
status "on": "command: 2003, payload: FF"
|
||||
status "off": "command: 2003, payload: 00"
|
||||
|
||||
// reply messages
|
||||
reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
|
||||
reply "200100,delay 100,2502": "command: 2503, payload: 00"
|
||||
}
|
||||
|
||||
// tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
valueTile("power", "device.power", decoration: "flat") {
|
||||
state "default", label:'${currentValue} W'
|
||||
}
|
||||
valueTile("energy", "device.energy", decoration: "flat") {
|
||||
state "default", label:'${currentValue} kWh'
|
||||
}
|
||||
standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details(["switch","power","energy","refresh","configure"])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
|
||||
|
||||
log.debug cmd
|
||||
|
||||
if (cmd) {
|
||||
result = createEvent(zwaveEvent(cmd))
|
||||
}
|
||||
if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
|
||||
result = [result, response(zwave.basicV1.basicGet())]
|
||||
log.debug "Was hailed: requesting state update"
|
||||
} else {
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
[name: "switch", value: cmd.value ? "on" : "off", type: "physical"]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
|
||||
[name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) {
|
||||
log.debug "MeterReport ${cmd}"
|
||||
|
||||
if (cmd.scale == 0) {
|
||||
[name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
|
||||
} else if (cmd.scale == 1) {
|
||||
[name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
|
||||
}
|
||||
else {
|
||||
[name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"]
|
||||
}
|
||||
}
|
||||
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
|
||||
zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 1, size: 2, scaledConfigurationValue: 12).format(), // Watt Meter report 5s per unit
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 2, size: 2, scaledConfigurationValue: 2).format() // KWH report 10mins per unit
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
|
||||
def value = "when off"
|
||||
if (cmd.configurationValue[0] == 1) {value = "when on"}
|
||||
if (cmd.configurationValue[0] == 2) {value = "never"}
|
||||
[name: "indicatorStatus", value: value, display: false]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
|
||||
[name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
def result = []
|
||||
|
||||
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
|
||||
def ManufacturerCode = String.format("%04X", cmd.manufacturerId)
|
||||
def ProductCode = String.format("%04X", cmd.productId)
|
||||
def ProduceTypeCode = String.format("%04X", cmd.productTypeId)
|
||||
def WirelessConfig = "ZWP"
|
||||
|
||||
sendEvent(name: "ManufacturerCode", value: ManufacturerCode)
|
||||
sendEvent(name: "ProductCode", value: ProductCode)
|
||||
sendEvent(name: "ProduceTypeCode", value: ProduceTypeCode)
|
||||
sendEvent(name: "WirelessConfig", value: WirelessConfig)
|
||||
|
||||
result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
return createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
|
||||
|
||||
}
|
||||
|
||||
def on() {
|
||||
delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def off() {
|
||||
delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0x00).format(),
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def poll() {
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinaryGet().format(),
|
||||
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
|
||||
])
|
||||
}
|
||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
|
||||
log.debug "encapsulated: $encapsulatedCommand"
|
||||
if (encapsulatedCommand) {
|
||||
state.sec = 1
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
private secureSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ secure(it) }, delay)
|
||||
}
|
||||
@@ -87,10 +87,13 @@ def authPage(){
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
initialize()
|
||||
}
|
||||
|
||||
@@ -103,24 +106,72 @@ def uninstalled() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
unsubscribe()
|
||||
atomicState.attached_sensors = [:]
|
||||
if (plantlinksensors){
|
||||
subscribe(plantlinksensors, "moisture_status", moistureHandler)
|
||||
subscribe(plantlinksensors, "battery_status", batteryHandler)
|
||||
plantlinksensors.each{ sensor_device ->
|
||||
subscribe(sensor_device, "moisture_status", moistureHandler)
|
||||
subscribe(sensor_device, "battery_status", batteryHandler)
|
||||
sensor_device.setStatusIcon("Waiting on First Measurement")
|
||||
sensor_device.setInstallSmartApp("connectedToSmartApp")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def updatePlantNameIfNeeded(plant, expected_plant_name){
|
||||
def dock_sensor(device_serial, expected_plant_name) {
|
||||
def docking_body_json_builder = new JsonBuilder([version: '1c', smartthings_device_id: device_serial])
|
||||
def docking_params = [
|
||||
uri : appSettings.https_plantLinkServer,
|
||||
path : "/api/v1/smartthings/links",
|
||||
headers : ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType: "application/json",
|
||||
body: docking_body_json_builder.toString()
|
||||
]
|
||||
def plant_post_body_map = [
|
||||
plant_type_key: 999999,
|
||||
soil_type_key : 1000004
|
||||
]
|
||||
def plant_post_params = [
|
||||
uri : appSettings.https_plantLinkServer,
|
||||
path : "/api/v1/plants",
|
||||
headers : ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType: "application/json",
|
||||
]
|
||||
log.debug "Creating new plant on myplantlink.com - ${expected_plant_name}"
|
||||
httpPost(docking_params) { docking_response ->
|
||||
if (parse_api_response(docking_response, "Docking a link")) {
|
||||
if (docking_response.data.plants.size() == 0) {
|
||||
log.debug "creating plant for - ${expected_plant_name}"
|
||||
plant_post_body_map["name"] = expected_plant_name
|
||||
plant_post_body_map['links_key'] = [docking_response.data.key]
|
||||
def plant_post_body_json_builder = new JsonBuilder(plant_post_body_map)
|
||||
plant_post_params["body"] = plant_post_body_json_builder.toString()
|
||||
httpPost(plant_post_params) { plant_post_response ->
|
||||
if(parse_api_response(plant_post_response, 'creating plant')){
|
||||
def attached_map = atomicState.attached_sensors
|
||||
attached_map[device_serial] = plant_post_response.data
|
||||
atomicState.attached_sensors = attached_map
|
||||
}
|
||||
}
|
||||
} else {
|
||||
def plant = docking_response.data.plants[0]
|
||||
def attached_map = atomicState.attached_sensors
|
||||
attached_map[device_serial] = plant
|
||||
atomicState.attached_sensors = attached_map
|
||||
checkAndUpdatePlantIfNeeded(plant, expected_plant_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
def checkAndUpdatePlantIfNeeded(plant, expected_plant_name){
|
||||
def plant_put_params = [
|
||||
uri : appSettings.https_plantLinkServer,
|
||||
headers : ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType : "application/json"
|
||||
]
|
||||
if (plant.name != expected_plant_name) {
|
||||
log.debug "renaming plant ${plant.key} - ${expected_plant_name}"
|
||||
log.debug "updating plant for - ${expected_plant_name}"
|
||||
plant_put_params["path"] = "/api/v1/plants/${plant.key}"
|
||||
def plant_put_body_map = [
|
||||
name: expected_plant_name
|
||||
@@ -134,39 +185,32 @@ def updatePlantNameIfNeeded(plant, expected_plant_name){
|
||||
}
|
||||
|
||||
def moistureHandler(event){
|
||||
log.debug "moistureHandler - ${event.value}"
|
||||
|
||||
def expected_plant_name = "${event.displayName} (ST)"
|
||||
def expected_plant_name = "SmartThings - ${event.displayName}"
|
||||
def device_serial = getDeviceSerialFromEvent(event)
|
||||
def device_battery = atomicState["battery${device_serial}"]
|
||||
if ( device_battery == null){
|
||||
log.error "Missing Battery Voltage - next cycle should have it"
|
||||
|
||||
if (!atomicState.attached_sensors.containsKey(device_serial)){
|
||||
dock_sensor(device_serial, expected_plant_name)
|
||||
}else{
|
||||
// {"type":"link","signal":"0x00","zigbeedeviceid":"0022A3000003D75A","created":1458843686,"moisture":"0x1987"}
|
||||
def appendedEventWithBatteryInfo = event.value.replace('}',",\"battery\":\"${device_battery}\"}")
|
||||
log.debug "payload - ${appendedEventWithBatteryInfo}"
|
||||
def measurement_post_params = [
|
||||
uri: appSettings.https_plantLinkServer,
|
||||
path: "/api/v1/smartthings/links/${device_serial}/measurements",
|
||||
headers: ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType: "application/json",
|
||||
body: appendedEventWithBatteryInfo
|
||||
uri: appSettings.https_plantLinkServer,
|
||||
path: "/api/v1/smartthings/links/${device_serial}/measurements",
|
||||
headers: ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType: "application/json",
|
||||
body: event.value
|
||||
]
|
||||
|
||||
httpPost(measurement_post_params) { measurement_post_response ->
|
||||
if (parse_api_response(measurement_post_response, 'creating moisture measurement') && measurement_post_response.data.size() >0){
|
||||
if (parse_api_response(measurement_post_response, 'creating moisture measurement') &&
|
||||
measurement_post_response.data.size() >0){
|
||||
def measurement = measurement_post_response.data[0]
|
||||
def plant = measurement.plant
|
||||
|
||||
updatePlantNameIfNeeded(plant, expected_plant_name)
|
||||
|
||||
log.debug plant
|
||||
checkAndUpdatePlantIfNeeded(plant, expected_plant_name)
|
||||
plantlinksensors.each{ sensor_device ->
|
||||
if (sensor_device.id == event.deviceId){
|
||||
sensor_device.setStatusIcon(plant.status)
|
||||
if (plant.last_measurements && plant.last_measurements[0].moisture){
|
||||
sensor_device.setPlantFuelLevel(plant.last_measurements[0].moisture * 100 as int)
|
||||
}
|
||||
|
||||
if (plant.last_measurements && plant.last_measurements[0].battery){
|
||||
sensor_device.setBatteryLevel(plant.last_measurements[0].battery * 100 as int)
|
||||
}
|
||||
@@ -180,7 +224,21 @@ def moistureHandler(event){
|
||||
def batteryHandler(event){
|
||||
def expected_plant_name = "SmartThings - ${event.displayName}"
|
||||
def device_serial = getDeviceSerialFromEvent(event)
|
||||
atomicState["battery${device_serial}"] = getDeviceBatteryFromEvent(event)
|
||||
|
||||
if (!atomicState.attached_sensors.containsKey(device_serial)){
|
||||
dock_sensor(device_serial, expected_plant_name)
|
||||
}else{
|
||||
def measurement_post_params = [
|
||||
uri: appSettings.https_plantLinkServer,
|
||||
path: "/api/v1/smartthings/links/${device_serial}/measurements",
|
||||
headers: ["Content-Type": "application/json", "Authorization": "Bearer ${atomicState.authToken}"],
|
||||
contentType: "application/json",
|
||||
body: event.value
|
||||
]
|
||||
httpPost(measurement_post_params) { measurement_post_response ->
|
||||
parse_api_response(measurement_post_response, 'creating battery measurement')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getDeviceSerialFromEvent(event){
|
||||
@@ -189,12 +247,6 @@ def getDeviceSerialFromEvent(event){
|
||||
return match_result[0][1]
|
||||
}
|
||||
|
||||
def getDeviceBatteryFromEvent(event){
|
||||
def pattern = /.*"battery"\s*:\s*"(\w+)".*/
|
||||
def match_result = (event.value =~ pattern)
|
||||
return match_result[0][1]
|
||||
}
|
||||
|
||||
def oauthInitUrl(){
|
||||
atomicState.oauthInitState = UUID.randomUUID().toString()
|
||||
def oauthParams = [
|
||||
@@ -211,13 +263,12 @@ def buildRedirectUrl(){
|
||||
}
|
||||
|
||||
def swapToken(){
|
||||
log.debug "PlantLink Connector - OAuth Token"
|
||||
def code = params.code
|
||||
def oauthState = params.state
|
||||
def stcid = appSettings.client_id
|
||||
def postParams = [
|
||||
method: 'POST',
|
||||
uri: appSettings.https_plantLinkServer,
|
||||
uri: "https://oso-tech.appspot.com",
|
||||
path: "/api/v1/oauth-token",
|
||||
query: [grant_type:'authorization_code', code:params.code, client_id:stcid,
|
||||
client_secret:appSettings.client_secret, redirect_uri: buildRedirectUrl()],
|
||||
@@ -270,11 +321,10 @@ def swapToken(){
|
||||
}
|
||||
|
||||
private refreshAuthToken() {
|
||||
log.debug "PlantLink Connector - Refresh OAuth"
|
||||
def stcid = appSettings.client_id
|
||||
def refreshParams = [
|
||||
method: 'POST',
|
||||
uri: appSettings.https_plantLinkServer,
|
||||
uri: "https://hardware-dot-oso-tech.appspot.com",
|
||||
path: "/api/v1/oauth-token",
|
||||
query: [grant_type:'refresh_token', code:"${atomicState.refreshToken}", client_id:stcid,
|
||||
client_secret:appSettings.client_secret],
|
||||
@@ -283,6 +333,7 @@ private refreshAuthToken() {
|
||||
def jsonMap
|
||||
httpPost(refreshParams) { resp ->
|
||||
if(resp.status == 200){
|
||||
log.debug "OAuth Token refreshed"
|
||||
jsonMap = resp.data
|
||||
if (resp.data) {
|
||||
atomicState.refreshToken = resp?.data?.refresh_token
|
||||
@@ -295,12 +346,12 @@ private refreshAuthToken() {
|
||||
}
|
||||
data.action = ""
|
||||
}else{
|
||||
log.debug "PlantLink Server - ${resp.status} : ${resp.status.code}"
|
||||
log.debug "refresh failed ${resp.status} : ${resp.status.code}"
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
log.debug "PlantLink Connector - OAuth Refresh Failed: " + e
|
||||
log.debug "caught exception refreshing auth token: " + e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +364,7 @@ def parse_api_response(resp, message) {
|
||||
refreshAuthToken()
|
||||
return false
|
||||
} else {
|
||||
debugEvent("Plantlink Error: ${resp.status} - ${resp.status.code}", true)
|
||||
debugEvent("Authentication error, invalid authentication method, lack of credentials, etc.", true)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ private sendDeveloperReq() {
|
||||
headers: [
|
||||
HOST: host
|
||||
],
|
||||
body: [devicetype: "$token-0"]], "${selectedHue}"))
|
||||
body: [devicetype: "$token-0", username: "$token-0"]], "${selectedHue}"))
|
||||
}
|
||||
|
||||
private discoverHueBulbs() {
|
||||
|
||||
Reference in New Issue
Block a user