mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-11 21:03:07 +00:00
Compare commits
3 Commits
MSA-1956-3
...
MSA-1963-7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
549dd0df0f | ||
|
|
68f5cda945 | ||
|
|
da42ee63fb |
@@ -103,7 +103,7 @@ metadata {
|
||||
}
|
||||
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "illuminance", label:'${currentValue} ${unit}', unit:"lux"
|
||||
state "illuminance", label:'${currentValue} lux', unit:""
|
||||
}
|
||||
|
||||
valueTile("ultravioletIndex", "device.ultravioletIndex", inactiveLabel: false, width: 2, height: 2) {
|
||||
@@ -410,4 +410,4 @@ private command(physicalgraph.zwave.Command cmd) {
|
||||
private commands(commands, delay=200) {
|
||||
log.info "sending commands: ${commands}"
|
||||
delayBetween(commands.collect{ command(it) }, delay)
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ metadata {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
|
||||
state "luminosity", label:'${currentValue} lux', unit:""
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -282,5 +282,4 @@ private secure(physicalgraph.zwave.Command cmd) {
|
||||
|
||||
private secureSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ secure(it) }, delay)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,7 +79,7 @@ metadata {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
|
||||
state "luminosity", label:'${currentValue} lux', unit:""
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -193,4 +193,4 @@ def configure() {
|
||||
// set data reporting period to 5 minutes
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300).format()
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* Copyright 2017 Stelpro
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Stelpro Ki Thermostat
|
||||
*
|
||||
* Author: Stelpro
|
||||
*
|
||||
* Date: 2017-05-08
|
||||
*/
|
||||
|
||||
preferences {
|
||||
input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "[Do not use space](Blank = No Forecast)")
|
||||
input("heatdetails", "enum", title: "Do you want a detailed operating state notification?", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true)
|
||||
}
|
||||
|
||||
metadata {
|
||||
definition (name: "Stelpro Ki Thermostat", namespace: "stelpro", author: "Stelpro") {
|
||||
capability "Thermostat"
|
||||
capability "Temperature Measurement"
|
||||
capability "Actuator"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Configuration"
|
||||
|
||||
attribute "outsideTemp", "number"
|
||||
|
||||
command "switchMode"
|
||||
command "quickSetHeat"
|
||||
command "quickSetOutTemp"
|
||||
command "increaseHeatSetpoint"
|
||||
command "decreaseHeatSetpoint"
|
||||
command "setCustomThermostatMode"
|
||||
command "eco"
|
||||
command "applyNow"
|
||||
|
||||
fingerprint deviceId: "0x0806", inClusters: "0x5E,0x86,0x72,0x40,0x43,0x31,0x85,0x59,0x5A,0x73,0x20,0x42"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
//Add test code here
|
||||
}
|
||||
|
||||
tiles {
|
||||
multiAttributeTile(name:"thermostatMulti", type:"thermostat", width:6, height:4) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("temp", label:'${currentValue}')
|
||||
attributeState("high", label:'HIGH')
|
||||
attributeState("low", label:'LOW')
|
||||
attributeState("--", label:'--')
|
||||
}
|
||||
tileAttribute("device.heatingSetpoint", key: "VALUE_CONTROL") {
|
||||
attributeState("VALUE_UP", action: "increaseHeatSetpoint")
|
||||
attributeState("VALUE_DOWN", action: "decreaseHeatSetpoint")
|
||||
}
|
||||
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
|
||||
attributeState("idle", backgroundColor:"#44b621")
|
||||
attributeState("heating", backgroundColor:"#ffa81e")
|
||||
}
|
||||
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
|
||||
attributeState("off", label:'Off')
|
||||
attributeState("comfort", label:'Comfort')
|
||||
attributeState("eco", label:'Eco')
|
||||
}
|
||||
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT")
|
||||
{
|
||||
attributeState("heatingSetpoint", label:'${currentValue}')
|
||||
}
|
||||
}
|
||||
standardTile("mode", "device.thermostatMode", width: 2, height: 2) {
|
||||
state "off", label:'${name}', action:"switchMode", nextState:"to_comfort", icon:"st.thermostat.off"
|
||||
state "comfort", label:'${name}', action:"switchMode", nextState:"to_eco", icon:"http://cdn.device-icons.smartthings.com/Home/home29-icn@2x.png"
|
||||
state "eco", label:'${name}', action:"switchMode", nextState:"...", icon:"http://cdn.device-icons.smartthings.com/Outdoor/outdoor3-icn@2x.png"
|
||||
state "to_comfort", label: "comfort", action:"switchMode", nextState:"to_eco"
|
||||
state "to_eco", label: "eco", action:"switchMode", nextState:"..."
|
||||
state "...", label: "...", action:"heat", nextState:"comfort"
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2) {
|
||||
state "temperature", label:'Setpoint\n${currentValue}°', backgroundColors:[
|
||||
[value: 31, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 95, color: "#d04e00"],
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
state "--", label:'--', backgroundColor:"#bdbdbd"
|
||||
}
|
||||
standardTile("refresh", "device.refresh", decoration: "flat", width: 2, height: 2) {
|
||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
main ("thermostatMulti")
|
||||
details(["thermostatMulti", "mode", "heatingSetpoint", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
{
|
||||
if (description == "updated")
|
||||
return []
|
||||
|
||||
def unitScale = getTemperatureScale()
|
||||
//Class, version
|
||||
def map = createEvent(zwaveEvent(zwave.parse(description, [0x40:2, 0x43:2, 0x31:3, 0x42:1])))
|
||||
if (!map) {
|
||||
return null
|
||||
}
|
||||
|
||||
def result = [map]
|
||||
if (map.name in ["heatingSetpoint","thermostatMode"]) {
|
||||
def map2 = [
|
||||
name: "thermostatSetpoint",
|
||||
unit: getTemperatureScale()
|
||||
]
|
||||
if (map.name == "thermostatMode") {
|
||||
state.lastTriedMode = map.value
|
||||
}
|
||||
else {
|
||||
def mode = device.latestValue("thermostatMode")
|
||||
log.info "THERMOSTAT, latest mode = ${mode}"
|
||||
if (map.name == "heatingSetpoint") {
|
||||
map2.value = map.value
|
||||
map2.unit = map.unit
|
||||
}
|
||||
}
|
||||
if (map2.value != null) {
|
||||
log.debug "THERMOSTAT, adding setpoint event: $map"
|
||||
result << createEvent(map2)
|
||||
}
|
||||
}
|
||||
log.debug "Parse returned $result"
|
||||
result
|
||||
}
|
||||
|
||||
// Event Generation
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
|
||||
{
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
def temp;
|
||||
float tempfloat;
|
||||
def map = [:]
|
||||
if (cmd.scaledValue >= 327)
|
||||
{
|
||||
map.value = "--"
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
|
||||
tempfloat = (Math.round(temp.toFloat() * 2)) / 2
|
||||
map.value = tempfloat
|
||||
}
|
||||
map.unit = getTemperatureScale()
|
||||
map.displayed = false
|
||||
switch (cmd.setpointType) {
|
||||
case 1:
|
||||
map.name = "heatingSetpoint"
|
||||
break;
|
||||
default:
|
||||
return [:]
|
||||
}
|
||||
// So we can respond with same format
|
||||
state.size = cmd.size
|
||||
state.scale = cmd.scale
|
||||
state.precision = cmd.precision
|
||||
sendEvent(name:"heatingSetpoint", value:map.value)
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd)
|
||||
{
|
||||
def temp;
|
||||
float tempfloat;
|
||||
def format;
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
|
||||
temp = map.value
|
||||
if (temp == "32765") //0x7FFD
|
||||
{
|
||||
map.value = "low"
|
||||
}
|
||||
else if (temp == "32767") //0x7FFF
|
||||
{
|
||||
map.value = "high"
|
||||
}
|
||||
else if (temp == "-32768") //0x8000
|
||||
{
|
||||
map.value = "--"
|
||||
}
|
||||
else
|
||||
{
|
||||
tempfloat = (Math.round(temp.toFloat() * 2)) / 2
|
||||
map.value = tempfloat
|
||||
}
|
||||
|
||||
} else if (cmd.sensorType == 5) {
|
||||
map.value = cmd.scaledSensorValue
|
||||
map.unit = "%"
|
||||
map.name = "humidity"
|
||||
}
|
||||
sendEvent(name:"temperature", value:map.value)
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
switch (cmd.operatingState) {
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
|
||||
map.value = "idle"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_HEATING:
|
||||
map.value = "heating"
|
||||
break
|
||||
}
|
||||
map.name = "thermostatOperatingState"
|
||||
|
||||
if (settings.heatdetails == "No") {
|
||||
map.displayed = false
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
||||
def map = [:]
|
||||
switch (cmd.mode) {
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
|
||||
map.value = "comfort"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
|
||||
map.value = "off"
|
||||
break
|
||||
default:
|
||||
map.value = "eco"
|
||||
break
|
||||
}
|
||||
map.name = "thermostatMode"
|
||||
sendEvent(name:"thermostatMode", value:map.value)
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
|
||||
delayBetween([
|
||||
zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:0).format(),
|
||||
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
|
||||
poll()
|
||||
], 2300)
|
||||
}
|
||||
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||
log.debug "Zwave event received: $cmd"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
log.debug "Zwave event received: $cmd"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.warn "Unexpected zwave command $cmd"
|
||||
}
|
||||
|
||||
// Command Implementations
|
||||
def poll() {
|
||||
def weather
|
||||
|
||||
// If there is a zipcode defined, weather forecast will be sent. Otherwise, no weather forecast.
|
||||
if (settings.zipcode) {
|
||||
log.debug "ZipCode: ${settings.zipcode}"
|
||||
weather = getWeatherFeature( "conditions", settings.zipcode )
|
||||
|
||||
// Check if the variable is populated, otherwise return.
|
||||
if (!weather) {
|
||||
log.debug( "Something went wrong, no data found." )
|
||||
return false
|
||||
}
|
||||
|
||||
// Set the tiles
|
||||
def locationScale = getTemperatureScale()
|
||||
def tempToSend
|
||||
if (locationScale == "C")
|
||||
{
|
||||
log.debug( "Outdoor Temperature: ${weather.current_observation.temp_c}ºC" )
|
||||
sendEvent( name: 'outsideTemp', value: weather.current_observation.temp_c )
|
||||
tempToSend = weather.current_observation.temp_c
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug( "Outdoor Temperature: ${weather.current_observation.temp_f}ºF" )
|
||||
sendEvent( name: 'outsideTemp', value: weather.current_observation.temp_f )
|
||||
tempToSend = weather.current_observation.temp_f
|
||||
}
|
||||
|
||||
|
||||
delayBetween([
|
||||
quickSetOutTemp(tempToSend),
|
||||
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format(),
|
||||
zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1).format(),
|
||||
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
|
||||
sendEvent( name: 'change', value: 0 )
|
||||
], 100)
|
||||
} else {
|
||||
delayBetween([
|
||||
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format(),
|
||||
zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1).format(),
|
||||
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
|
||||
sendEvent( name: 'change', value: 0 )
|
||||
], 100)
|
||||
}
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
poll()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
poll()
|
||||
}
|
||||
|
||||
def applyNow() {
|
||||
float currentHeatSetpoint = device.currentValue("heatingSetpoint")
|
||||
def deviceScale
|
||||
def locationScale = getTemperatureScale()
|
||||
|
||||
sendEvent( name: 'change', value: 0 )
|
||||
log.debug("currentHeatSetpoint $currentHeatSetpoint")
|
||||
if (locationScale == "C")
|
||||
{
|
||||
deviceScale = 0
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceScale = 1
|
||||
}
|
||||
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV2.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: state.precision, scaledValue: currentHeatSetpoint).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def quickSetHeat(degrees) {
|
||||
setHeatingSetpoint(degrees, 0)
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(degrees, delay = 0) {
|
||||
sendEvent(name:"heatingSetpoint", value:degrees)
|
||||
applyNow()
|
||||
}
|
||||
|
||||
def quickSetOutTemp(degrees) {
|
||||
setOutdoorTemperature(degrees, 0)
|
||||
}
|
||||
|
||||
def setOutdoorTemperature(degrees, delay = 0) {
|
||||
setOutdoorTemperature(degrees.toDouble(), delay)
|
||||
}
|
||||
|
||||
def setOutdoorTemperature(Double degrees, Integer delay = 0) {
|
||||
def deviceScale
|
||||
def locationScale = getTemperatureScale()
|
||||
def p = (state.precision == null) ? 1 : state.precision
|
||||
|
||||
if (locationScale == "C")
|
||||
{
|
||||
deviceScale = 0
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceScale = 1
|
||||
}
|
||||
log.info "setOutdoorTemperature: ${degrees}"
|
||||
zwave.sensorMultilevelV3.sensorMultilevelReport(sensorType: 1, scale: deviceScale, precision: p, scaledSensorValue: degrees).format()
|
||||
}
|
||||
|
||||
def increaseHeatSetpoint()
|
||||
{
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
if (currentMode != "off")
|
||||
{
|
||||
float currentSetpoint = device.currentValue("heatingSetpoint")
|
||||
def locationScale = getTemperatureScale()
|
||||
float maxSetpoint
|
||||
float step
|
||||
|
||||
if (locationScale == "C")
|
||||
{
|
||||
maxSetpoint = 30;
|
||||
step = 0.5
|
||||
}
|
||||
else
|
||||
{
|
||||
maxSetpoint = 86
|
||||
step = 1
|
||||
}
|
||||
|
||||
if (currentSetpoint < maxSetpoint)
|
||||
{
|
||||
currentSetpoint = currentSetpoint + step
|
||||
quickSetHeat(currentSetpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def decreaseHeatSetpoint()
|
||||
{
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
if (currentMode != "off")
|
||||
{
|
||||
float currentSetpoint = device.currentValue("heatingSetpoint")
|
||||
def locationScale = getTemperatureScale()
|
||||
float minSetpoint
|
||||
float step
|
||||
|
||||
if (locationScale == "C")
|
||||
{
|
||||
minSetpoint = 5;
|
||||
step = 0.5
|
||||
}
|
||||
else
|
||||
{
|
||||
minSetpoint = 41
|
||||
step = 1
|
||||
}
|
||||
|
||||
if (currentSetpoint > minSetpoint)
|
||||
{
|
||||
currentSetpoint = currentSetpoint - step
|
||||
quickSetHeat(currentSetpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def switchMode() {
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "comfort"
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
def modeOrder = modes()
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
if (supportedModes?.contains(currentMode)) {
|
||||
while (!supportedModes.contains(nextMode) && nextMode != "comfort") {
|
||||
nextMode = next(nextMode)
|
||||
}
|
||||
}
|
||||
state.lastTriedMode = nextMode
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[nextMode]).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def modes() {
|
||||
["comfort", "eco", "off"]
|
||||
}
|
||||
|
||||
def getModeMap() { [
|
||||
"off": 0,
|
||||
"comfort": 1,
|
||||
"eco": 11,
|
||||
]}
|
||||
|
||||
def getDataByName(String name) {
|
||||
state[name] ?: device.getDataValue(name)
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(coolingSetpoint) {
|
||||
log.trace "${device.displayName} does not support cool setpoint"
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.trace "off mode applied"
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def heat() {
|
||||
log.trace "heat mode applied"
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def eco() {
|
||||
log.trace "eco mode applied"
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 11).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def auto() {
|
||||
log.trace "${device.displayName} does not support auto mode"
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
log.trace "${device.displayName} does not support emergency heat mode"
|
||||
}
|
||||
|
||||
def cool() {
|
||||
log.trace "${device.displayName} does not support cool mode"
|
||||
}
|
||||
|
||||
def setCustomThermostatMode(mode) {
|
||||
setThermostatMode(mode)
|
||||
}
|
||||
|
||||
def setThermostatMode(String value) {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
|
||||
poll()
|
||||
], 1000)
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
log.trace "${device.displayName} does not support fan on"
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
log.trace "${device.displayName} does not support fan auto"
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
log.trace "${device.displayName} does not support fan circulate"
|
||||
}
|
||||
|
||||
def setThermostatFanMode() {
|
||||
log.trace "${device.displayName} does not support fan mode"
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/**
|
||||
* Lloyds Banking Group Connect & Protect
|
||||
*
|
||||
* Copyright 2016 Domotz
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
definition(
|
||||
name: "Lloyds Banking Group Connect & Protect",
|
||||
namespace: "domotz.dev",
|
||||
author: "Domotz",
|
||||
description: "The Lloyds Connect & Protect SmartApp is a bridge between SmartThings Cloud and Lloyds Banking Group to enable advanced connected device monitoring and alerting features to be included in the offering to their customers for the connected home service",
|
||||
category: "Convenience",
|
||||
singleInstance: true,
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
oauth: [displayName: "Lloyds Banking Group Connect & Protect", displayLink: ""]) {
|
||||
appSetting "endpointRetrievalUrl"
|
||||
appSetting "xApiKey"
|
||||
}
|
||||
|
||||
def getSupportedTypes() {
|
||||
return [
|
||||
[obj: switches, name: "switches", attribute: "switch", capability: "switch", title: "Switches"],
|
||||
[obj: motions, name: "motions", attribute: "motion", capability: "motionSensor", title: "Motion Sensors"],
|
||||
[obj: temperature, name: "temperature", attribute: "temperature", capability: "temperatureMeasurement", title: "Temperature Sensors"],
|
||||
[obj: contact, name: "contact", attribute: "contact", capability: "contactSensor", title: "Contact Sensors"],
|
||||
[obj: presence, name: "presence", attribute: "presence", capability: "presenceSensor", title: "Presence Sensors"],
|
||||
[obj: water, name: "water", attribute: "water", capability: "waterSensor", title: "Water Sensors"],
|
||||
[obj: smoke, name: "smoke", attribute: "smoke", capability: "smokeDetector", title: "Smoke Sensors"],
|
||||
[obj: battery, name: "battery", attribute: "battery", capability: "battery", title: "Batteries"]
|
||||
]
|
||||
}
|
||||
|
||||
preferences {
|
||||
section("Allow Lloyds Banking Group Connect & Protect service to monitor these devices") {
|
||||
for (type in getSupportedTypes()) {
|
||||
input type.get("name"), "capability.${type.get('capability')}", title: type.get('title'), multiple: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
unsubscribe()
|
||||
initialize()
|
||||
hubUpdateHandler()
|
||||
|
||||
}
|
||||
|
||||
def getRequestHeaders() {
|
||||
return ['Accept': '*/*', 'X-API-KEY': appSettings.xApiKey]
|
||||
}
|
||||
|
||||
def subscribeToDeviceEvents() {
|
||||
for (type in getSupportedTypes()) {
|
||||
subscribe(type.get("obj"), "${type.get("attribute")}", genericDeviceEventHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
if (atomicState.endpoint != null) {
|
||||
log.debug "Detected endpoint: ${atomicState.endpoint}"
|
||||
subscribeToDeviceEvents()
|
||||
subscribe(location, "routineExecuted", modeChangeHandler)
|
||||
subscribe(location, "mode", modeChangeHandler)
|
||||
} else {
|
||||
log.debug "There is no endpoint, requesting domotz for a new one"
|
||||
requestNewEndpoint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def getHubLocation() {
|
||||
def location_info = [:]
|
||||
location_info['uid'] = location.id
|
||||
//location_info['hubs'] = location.hubs
|
||||
location_info['latitude'] = location.latitude
|
||||
location_info['longitude'] = location.longitude
|
||||
location_info['current_mode'] = location.mode
|
||||
//location_info['modes'] = location.modes
|
||||
location_info['name'] = location.name
|
||||
location_info['temperature_scale'] = location.temperatureScale
|
||||
location_info['version'] = location.version
|
||||
location_info['channel_name'] = location.channelName
|
||||
location_info['zip_code'] = location.zipCode
|
||||
log.debug "Triggered getHubLocation with properties: ${location_info}"
|
||||
return location_info
|
||||
}
|
||||
|
||||
def modeChangeHandler(evt) {
|
||||
log.debug "mode changed to ${evt.value}"
|
||||
def url = null
|
||||
if (atomicState.endpoint != null) {
|
||||
url = atomicState.endpoint + '/hub-change'
|
||||
|
||||
httpPutJson(
|
||||
uri: url,
|
||||
body: getHubLocation(),
|
||||
headers: getRequestHeaders()
|
||||
)
|
||||
} else {
|
||||
log.debug "There is no endpoint, requesting domotz for a new one"
|
||||
requestNewEndpoint()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def genericDeviceEventHandler(event) {
|
||||
log.debug "Device Event Handler, event properties: ${event.getProperties().toString()}"
|
||||
log.debug "Device Event Handler, value: ${event.value}"
|
||||
def resp = [:]
|
||||
def url = null
|
||||
def device = null
|
||||
|
||||
device = getDevice(event.device, resp)
|
||||
|
||||
url = atomicState.endpoint + "/device/" + device.provider_uid + "/${event.name}"
|
||||
|
||||
log.debug "Device Event Handler, put url: ${url}"
|
||||
log.debug "Device Event Handler, put body: value: ${event.value}\ndate: ${event.isoDate}"
|
||||
httpPutJson(
|
||||
uri: url,
|
||||
body: [
|
||||
"value": event.value,
|
||||
"time" : event.isoDate
|
||||
],
|
||||
headers: getRequestHeaders()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def hubUpdateHandler() {
|
||||
log.debug "Hub Update Handler, with settings: ${settings}"
|
||||
def url = null
|
||||
def deviceList = [:]
|
||||
|
||||
if (atomicState.endpoint != null) {
|
||||
url = atomicState.endpoint + '/device-list'
|
||||
log.debug "Hub Update Event Handler, put url: ${url}"
|
||||
deviceList = getDeviceList()
|
||||
httpPutJson(
|
||||
uri: url,
|
||||
body: deviceList,
|
||||
headers: getRequestHeaders()
|
||||
)
|
||||
} else {
|
||||
log.debug "There is no endpoint, requesting domotz for a new one"
|
||||
requestNewEndpoint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
def getDeviceList() {
|
||||
try {
|
||||
|
||||
def resp = [:]
|
||||
def attribute = null
|
||||
|
||||
for (type in getSupportedTypes()) {
|
||||
type.get("obj").each {
|
||||
device = getDevice(it, resp)
|
||||
attribute = type.get("attribute")
|
||||
if (it.currentState(attribute)) {
|
||||
device['attributes'][attribute] = [
|
||||
"value": it.currentState(attribute).value,
|
||||
"time" : it.currentState(attribute).getIsoDate(),
|
||||
"unit" : it.currentState(attribute).unit
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return resp
|
||||
} catch (e) {
|
||||
log.debug("caught exception", e)
|
||||
return [:]
|
||||
}
|
||||
}
|
||||
|
||||
def getDevice(it, resp) {
|
||||
if (resp[it.id]) {
|
||||
return resp[it.id]
|
||||
}
|
||||
resp[it.id] = [name: it.name, display_name: it.displayName, provider_uid: it.id, type: it.typeName, label: it.label, manufacturer_name: it.manufacturerName, model: it.modelName, attributes: [:]]
|
||||
|
||||
}
|
||||
|
||||
def activateMonitoring(resp) {
|
||||
unsubscribe()
|
||||
log.debug "Event monitoring activated for endpoint: ${request.JSON.endpoint}"
|
||||
atomicState.endpoint = request.JSON.endpoint
|
||||
log.debug "Event monitoring activated for endpoint: ${atomicState.endpoint}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def deactivateMonitoring() {
|
||||
log.debug "Event monitoring deactivated."
|
||||
atomicState.endpoint = null
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
def requestNewEndpoint() {
|
||||
log.debug "Requesting a new endpoint."
|
||||
def hubId = location.id
|
||||
def params = [
|
||||
uri : "${appSettings.endpointRetrievalUrl}/${hubId}/endpoint",
|
||||
headers: getRequestHeaders()
|
||||
]
|
||||
|
||||
try {
|
||||
httpGet(params) { response ->
|
||||
log.debug "Request was successful, received endpoint: ${response.data.endpoint}"
|
||||
atomicState.endpoint = response.data.endpoint
|
||||
subscribeToDeviceEvents()
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
log.debug "Unable to retrieve the endpoint"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def handleClientUninstall() {
|
||||
log.info("Deactivated from client")
|
||||
try {
|
||||
app.delete()
|
||||
} catch (e) {
|
||||
unschedule()
|
||||
unsubscribe()
|
||||
httpError(500, "An error occurred during deleting SmartApp: ${e}")
|
||||
}
|
||||
}
|
||||
|
||||
mappings {
|
||||
path("/device") {
|
||||
action:
|
||||
[
|
||||
GET: getDeviceList
|
||||
]
|
||||
}
|
||||
path("/location") {
|
||||
action:
|
||||
[
|
||||
GET: getHubLocation
|
||||
]
|
||||
}
|
||||
path("/monitoring") {
|
||||
action:
|
||||
[
|
||||
POST : activateMonitoring,
|
||||
DELETE: deactivateMonitoring
|
||||
]
|
||||
}
|
||||
path("/uninstall") {
|
||||
action
|
||||
[GET: handleClientUninstall]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user