Compare commits

..

10 Commits

Author SHA1 Message Date
Vinay Rao
fc70b5ce55 Merge pull request #2167 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-18 11:43:24 -07:00
Vinay Rao
36e63133fc Merge pull request #2146 from marstorp/icp1148HoneywellZwave
ICP-1148 Support Thermostat Dynamic data
2017-07-17 15:45:19 -07:00
Vinay Rao
838c466312 Merge pull request #2158 from varzac/smartsense-battery-updates
[DVCSMP-2811] Update ranges for centralite battery values
2017-07-14 14:37:45 -07:00
Zach Varberg
97bfe61baa Update ranges for centralite battery values
This just updates the range to be more conservative as well as match the
battery curve better.
2017-07-14 11:39:48 -05:00
Vinay Rao
34df40d5b4 Merge pull request #2155 from jackchi/health-aeon6
[DHF-24] Fix Aeon MultiSensor6 OFFLINE issue
2017-07-12 14:29:03 -07:00
jackchi
545be046f0 [DHF-24] Update Aeon Multi6 to 2hr2min 2017-07-11 17:05:42 -07:00
Vinay Rao
a5041e0fcb Merge pull request #2154 from SmartThingsCommunity/master
Rolling up staging to master
2017-07-11 14:03:19 -07:00
Vinay Rao
6996a07969 Merge pull request #2153 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-11 13:59:29 -07:00
marstorp
771926c337 ICP-1148 Support Thermostat Dynamic data
Adding support for dynamic thermostat and fan modes to TCC DTH.
Also replaced capability "Polling" with "Refresh" and runEvery5Minutes("refresh") as polling capability is unreliable.
Also removed capability "Relative Humidity Measurement" as Honeywell Z-Wave Thermostat (YTH8320ZW1007/U) doesn't support humidity.
2017-07-05 16:50:30 -07:00
Vinay Rao
728b169a08 Merge pull request #2143 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-05 14:16:16 -07:00
7 changed files with 55 additions and 501 deletions

View File

@@ -1,284 +0,0 @@
/**
* SmartThings Device Handler: Yamaha Zone
*
* Author: redloro@gmail.com
*
* 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: "Yamaha Zone", namespace: "redloro-smartthings", author: "redloro@gmail.com") {
/**
* List our capabilties. Doing so adds predefined command(s) which
* belong to the capability.
*/
capability "Music Player"
capability "Switch"
capability "Switch Level"
capability "Refresh"
capability "Polling"
capability "Sensor"
capability "Actuator"
/**
* Define all commands, ie, if you have a custom action not
* covered by a capability, you NEED to define it here or
* the call will not be made.
*
* To call a capability function, just prefix it with the name
* of the capability, for example, refresh would be "refresh.refresh"
*/
command "source0"
command "source1"
command "source2"
command "source3"
command "source4"
command "source5"
command "mutedOn"
command "mutedOff"
command "partyModeOn"
command "partyModeOff"
command "zone"
}
/**
* Define the various tiles and the states that they can be in.
* The 2nd parameter defines an event which the tile listens to,
* if received, it tries to map it to a state.
*
* You can also use ${currentValue} for the value of the event
* or ${name} for the name of the event. Just make SURE to use
* single quotes, otherwise it will only be interpreted at time of
* launch, instead of every time the event triggers.
*/
tiles(scale: 2) {
multiAttributeTile(name:"state", type:"lighting", width:6, height:4) {
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'On', action:"switch.off", icon:"st.Electronics.electronics16", backgroundColor:"#79b821", nextState:"off"
attributeState "off", label:'Off', action:"switch.on", icon:"st.Electronics.electronics16", backgroundColor:"#ffffff", nextState:"on"
}
tileAttribute ("source", key: "SECONDARY_CONTROL") {
attributeState "source", label:'${currentValue}'
}
}
// row
//controlTile("volume", "device.volume", "slider", height: 1, width: 6, range:"(0..100)") {
controlTile("volume", "device.volume", "slider", height: 1, width: 6, range:"(-80..16)") {
state "volume", label: "Volume", action:"music Player.setLevel", backgroundColor:"#00a0dc"
}
// row
standardTile("0", "device.source0", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV1", action:"source0", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV1", action:"source0", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
standardTile("1", "device.source1", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV2", action:"source1", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV2", action:"source1", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
standardTile("2", "device.source2", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV3", action:"source2", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV3", action:"source2", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
standardTile("3", "device.source3", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV4", action:"source3", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV4", action:"source3", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
standardTile("4", "device.source4", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV5", action:"source4", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV5", action:"source4", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
standardTile("5", "device.source5", decoration: "flat", width: 2, height: 2) {
state("off", label:"AV6", action:"source5", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff")
state("on", label:"AV6", action:"source5", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-green.png", backgroundColor:"#ffffff")
}
// row
standardTile("muted", "device.muted", decoration: "flat", width: 2, height: 2) {
state("off", label:'Muted', action:"mutedOn", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff", nextState:"on")
state("on", label:'Muted', action:"mutedOff", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-mute.png", backgroundColor:"#ffffff", nextState:"off")
}
standardTile("partyMode", "device.partyMode", decoration: "flat", width: 2, height: 2, inactiveLabel: false) {
state("off", label:'Party Mode', action:"partyModeOn", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-gray.png", backgroundColor:"#ffffff", nextState:"on")
state("on", label:'Party Mode', action:"partyModeOff", icon:"https://raw.githubusercontent.com/redloro/smartthings/master/images/indicator-dot-party.png", backgroundColor:"#ffffff", nextState:"off")
}
standardTile("refresh", "device.status", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Refresh", action:"refresh.refresh", icon:"st.secondary.refresh-icon", backgroundColor:"#ffffff"
}
// Defines which tile to show in the overview
main "state"
// Defines which tile(s) to show when user opens the detailed view
details([
"state",
"volume",
"0","1","2","3","4","5",
"muted", "partyMode","refresh"
])
}
preferences {
input name: "source0", type: "text", title: "Source 1", defaultValue: "AV1"
input name: "source1", type: "text", title: "Source 2", defaultValue: "AV2"
input name: "source2", type: "text", title: "Source 3", defaultValue: "AV3"
input name: "source3", type: "text", title: "Source 4", defaultValue: "AV4"
input name: "source4", type: "text", title: "Source 5", defaultValue: "AV5"
input name: "source5", type: "text", title: "Source 6", defaultValue: "AV6"
}
}
/**************************************************************************
* The following section simply maps the actions as defined in
* the metadata into onAction() calls.
*
* This is preferred since some actions can be dealt with more
* efficiently this way. Also keeps all user interaction code in
* one place.
*
*/
def on() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Power_Control><Power>On</Power></Power_Control></${getZone()}></YAMAHA_AV>")
sendEvent(name: "switch", value: "on")
}
def off() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Power_Control><Power>Standby</Power></Power_Control></${getZone()}></YAMAHA_AV>")
sendEvent(name: "switch", value: "off")
}
def setLevel(value) {
//sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Volume><Lvl><Val>${(Math.round(value * 9 / 5) * 5 - 800).intValue()}</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></${getZone()}></YAMAHA_AV>")
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Volume><Lvl><Val>${(value * 10).intValue()}</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></${getZone()}></YAMAHA_AV>")
sendEvent(name: "volume", value: value)
}
def source0() {
setSource(0)
}
def source1() {
setSource(1)
}
def source2() {
setSource(2)
}
def source3() {
setSource(3)
}
def source4() {
setSource(4)
}
def source5() {
setSource(5)
}
def mutedOn() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Volume><Mute>On</Mute></Volume></${getZone()}></YAMAHA_AV>")
sendEvent(name: "muted", value: "on")
}
def mutedOff() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Volume><Mute>Off</Mute></Volume></${getZone()}></YAMAHA_AV>")
sendEvent(name: "muted", value: "off")
}
def partyModeOn() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><System><Party_Mode><Mode>On</Mode></Party_Mode></System></YAMAHA_AV>")
sendEvent(name: "partyMode", value: "on")
}
def partyModeOff() {
sendCommand("<YAMAHA_AV cmd=\"PUT\"><System><Party_Mode><Mode>Off</Mode></Party_Mode></System></YAMAHA_AV>")
sendEvent(name: "partyMode", value: "off")
}
def refresh() {
sendCommand("<YAMAHA_AV cmd=\"GET\"><${getZone()}><Basic_Status>GetParam</Basic_Status></${getZone()}></YAMAHA_AV>")
sendCommand("<YAMAHA_AV cmd=\"GET\"><System><Party_Mode><Mode>GetParam</Mode></Party_Mode></System></YAMAHA_AV>")
}
/**************************************************************************/
/**
* Called every so often (every 5 minutes actually) to refresh the
* tiles so the user gets the correct information.
*/
def poll() {
refresh()
}
def parse(String description) {
return
}
def setSource(id) {
//log.debug "source: "+getSourceName(id)
sendCommand("<YAMAHA_AV cmd=\"PUT\"><${getZone()}><Input><Input_Sel>"+getSourceName(id)+"</Input_Sel></Input></${getZone()}></YAMAHA_AV>")
setSourceTile(getSourceName(id))
}
def getSourceName(id) {
if (settings) {
return settings."source${id}"
} else {
return ['AV1', 'AV2', 'AV3', 'AV4', 'AV5', 'AV6'].get(id)
}
}
def setSourceTile(name) {
sendEvent(name: "source", value: "Source: ${name}")
for (def i = 0; i < 6; i++) {
if (name == getSourceName(i)) {
sendEvent(name: "source${i}", value: "on")
}
else {
sendEvent(name: "source${i}", value: "off")
}
}
}
def zone(evt) {
/*
* Zone On/Off
*/
if (evt.Basic_Status.Power_Control.Power.text()) {
sendEvent(name: "switch", value: (evt.Basic_Status.Power_Control.Power.text() == "On") ? "on" : "off")
}
/*
* Zone Volume
*/
if (evt.Basic_Status.Volume.Lvl.Val.text()) {
def int volLevel = evt.Basic_Status.Volume.Lvl.Val.toInteger() ?: -250
//sendEvent(name: "volume", value: ((volLevel + 800) / 9).intValue())
sendEvent(name: "volume", value: (volLevel / 10).intValue())
}
/*
* Zone Muted
*/
if (evt.Basic_Status.Volume.Mute.text()) {
sendEvent(name: "muted", value: (evt.Basic_Status.Volume.Mute.text() == "On") ? "on" : "off")
}
/*
* Zone Source
*/
if (evt.Basic_Status.Input.Input_Sel.text()) {
setSourceTile(evt.Basic_Status.Input.Input_Sel.text())
}
/*
* Party Mode
*/
if (evt.Party_Mode.Mode.text()) {
sendEvent(name: "partyMode", value: (evt.Party_Mode.Mode.text() == "On") ? "on" : "off")
}
}
private sendCommand(body) {
parent.sendCommand(body)
}
private getZone() {
return new String(device.deviceNetworkId).tokenize('|')[2]
}

View File

@@ -27,13 +27,9 @@ Works with:
## Device Health ## Device Health
Aeon Labs MultiSensor 6 is polled by the hub. Aeon Labs MultiSensor 6 is polled by the hub.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed. Aeon MultiSensor reports in once every hour.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __32min__ checkInterval * __122min__ checkInterval
## Troubleshooting ## Troubleshooting

View File

@@ -130,13 +130,13 @@ metadata {
} }
def installed(){ def installed(){
// Device-Watch simply pings if no device events received for 32min(checkInterval) // Device-Watch simply pings if no device events received for 122min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
} }
def updated() { def updated() {
// Device-Watch simply pings if no device events received for 32min(checkInterval) // Device-Watch simply pings if no device events received for 122min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
log.debug "Updated with settings: ${settings}" log.debug "Updated with settings: ${settings}"
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}" log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"

View File

@@ -171,8 +171,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.1 def minVolts = 2.4
def maxVolts = 3.0 def maxVolts = 2.7
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)

View File

@@ -274,7 +274,7 @@ private Map getBatteryResult(rawValue) {
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 2.7
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)

View File

@@ -15,10 +15,9 @@ metadata {
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") { definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Thermostat" capability "Thermostat"
capability "Configuration" capability "Configuration"
capability "Polling" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check" capability "Health Check"
@@ -117,7 +116,7 @@ metadata {
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff" state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
} }
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "default", action:"polling.poll", icon:"st.secondary.refresh" state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main "temperature" main "temperature"
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"]) details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
@@ -125,13 +124,20 @@ metadata {
} }
def installed(){ def installed(){
// Device-Watch simply pings if no device events received for 32min(checkInterval) sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()))
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) initialize()
} }
def updated(){ def updated(){
initialize()
}
def initialize() {
// Device-Watch simply pings if no device events received for 32min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
unschedule()
runEvery5Minutes("refresh")
refresh()
} }
def parse(String description) def parse(String description)
@@ -149,6 +155,7 @@ def parse(String description)
] ]
if (map.name == "thermostatMode") { if (map.name == "thermostatMode") {
state.lastTriedMode = map.value state.lastTriedMode = map.value
map.data = [supportedThermostatModes:state.supportedThermostatModes]
if (map.value == "cool") { if (map.value == "cool") {
map2.value = device.latestValue("coolingSetpoint") map2.value = device.latestValue("coolingSetpoint")
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}" log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
@@ -172,6 +179,7 @@ def parse(String description)
} }
} else if (map.name == "thermostatFanMode" && map.isStateChange) { } else if (map.name == "thermostatFanMode" && map.isStateChange) {
state.lastTriedFanMode = map.value state.lastTriedFanMode = map.value
map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes]
} }
log.debug "Parse returned $result" log.debug "Parse returned $result"
result result
@@ -305,26 +313,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
def supportedModes = "" def supportedModes = []
if(cmd.off) { supportedModes += "off " } if(cmd.off) { supportedModes << "off" }
if(cmd.heat) { supportedModes += "heat " } if(cmd.heat) { supportedModes << "heat" }
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " } if(cmd.cool) { supportedModes << "cool" }
if(cmd.cool) { supportedModes += "cool " } if(cmd.auto) { supportedModes << "auto" }
if(cmd.auto) { supportedModes += "auto " } if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
state.supportedModes = supportedModes state.supportedThermostatModes = supportedModes
// No events to be generated, return empty map sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
return [:] return [:]
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
def supportedFanModes = "" def supportedFanModes = []
if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto " if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto "
if(cmd.low) { supportedFanModes += "on " } // "fanOn" if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate"
if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate" if(cmd.low) { supportedFanModes << "on" } // "fanOn"
state.supportedFanModes = supportedFanModes state.supportedThermostatFanModes = supportedFanModes
// No events to be generated, return empty map sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
return [:] return [:]
} }
@@ -337,15 +345,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
} }
// Command Implementations // Command Implementations
def poll() { def refresh() {
delayBetween([ def cmds = []
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(), cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(), cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
zwave.thermostatModeV2.thermostatModeGet().format(), cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
zwave.thermostatFanModeV3.thermostatFanModeGet().format(), cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format() cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
], 2300) cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
sendHubCommand(cmds)
} }
def quickSetHeat(degrees) { def quickSetHeat(degrees) {
@@ -416,28 +426,14 @@ def ping() {
poll() poll()
} }
def configure() {
delayBetween([
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
zwave.thermostatModeV2.thermostatModeGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 2300)
}
def modes() { def modes() {
["off", "heat", "cool", "auto", "emergency heat"] return state.supportedThermostatModes
} }
def switchMode() { def switchMode() {
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off" def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"]
def supportedModes = getDataByName("supportedModes") def supportedModes = getDataByName("supportedThermostatModes")
def modeOrder = modes() def modeOrder = modes()
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
@@ -454,7 +450,7 @@ def switchMode() {
} }
def switchToMode(nextMode) { def switchToMode(nextMode) {
def supportedModes = getDataByName("supportedModes") def supportedModes = getDataByName("supportedThermostatModes")
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if (nextMode in modes()) { if (nextMode in modes()) {
state.lastTriedMode = nextMode state.lastTriedMode = nextMode
@@ -466,9 +462,9 @@ def switchToMode(nextMode) {
def switchFanMode() { def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off" def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"]
def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn" def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"]
def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn" def modeOrder = state.supportedThermostatFanModes
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto" while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
@@ -478,7 +474,7 @@ def switchFanMode() {
} }
def switchToFanMode(nextMode) { def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedFanModes") def supportedFanModes = getDataByName("supportedThermostatFanModes")
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
def returnCommand def returnCommand

View File

@@ -1,154 +0,0 @@
/**
* SmartThings SmartApp: Yamaha Network Receiver
*
* Author: redloro@gmail.com
*
* 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.
*
* https://github.com/PSeitz/yamaha-nodejs
* http://<RECEIVER_IP_ADDRESS>/YamahaRemoteControl/desc.xml
*/
import groovy.util.XmlSlurper
definition(
name: "Yamaha Receiver",
namespace: "redloro-smartthings",
author: "redloro@gmail.com",
description: "Yamaha SmartApp",
category: "My Apps",
iconUrl: "https://raw.githubusercontent.com/redloro/smartthings/master/images/yamaha-receiver.png",
iconX2Url: "https://raw.githubusercontent.com/redloro/smartthings/master/images/yamaha-receiver.png",
iconX3Url: "https://raw.githubusercontent.com/redloro/smartthings/master/images/yamaha-receiver.png"
)
preferences {
section("SmartThings Hub") {
input "hostHub", "hub", title: "Select Hub", multiple: false, required: true
}
section("Yamaha Receiver") {
input name: "receiverName", type: "text", title: "Name", required: true, defaultValue: "Yamaha"
input name: "receiverIp", type: "text", title: "IP", required: true
input name: "receiverZones", type: "enum", title: "Zones", required: true, multiple: true, options: ["Main_Zone","Zone_B","Zone_2","Zone_3","Zone_4"]
}
}
def installed() {
subscribeToEvents()
}
def subscribeToEvents() {
subscribe(location, null, lanResponseHandler, [filterEvents:false])
}
def updated() {
addChildDevices()
}
def uninstalled() {
removeChildDevices()
}
def lanResponseHandler(evt) {
def map = stringToMap(evt.stringValue)
//verify that this message is from Yamaha Receiver IP
if (!map.ip || map.ip != convertIPtoHex(settings.receiverIp)) {
return
}
def headers = getHttpHeaders(map.headers);
def body = getHttpBody(map.body);
//log.trace "Headers: ${headers}"
//log.trace "Body: ${body}"
updateZoneDevices(body.children()[0])
}
private updateZoneDevices(evt) {
//log.debug "updateZoneDevices: ${evt.toString()}"
if (evt.name() == "System") {
//log.debug "Update all zones"
childDevices*.zone(evt)
return
}
def zonedevice = getChildDevice(getDeviceId(evt.name()))
if (zonedevice) {
zonedevice.zone(evt)
}
//check for Zone_B
zonedevice = getChildDevice(getDeviceId("Zone_B"))
if (zonedevice && evt.name() == "Main_Zone") {
zonedevice.zone(evt)
}
}
private addChildDevices() {
// add yamaha device
settings.receiverZones.each {
def deviceId = getDeviceId(it)
if (!getChildDevice(deviceId)) {
addChildDevice("redloro-smartthings", "Yamaha Zone", deviceId, hostHub.id, ["name": it, label: "${settings.receiverName}: ${it}", completedSetup: true])
log.debug "Added Yamaha zone: ${deviceId}"
}
}
childDevices*.refresh()
}
private removeChildDevices() {
getAllChildDevices().each { deleteChildDevice(it.deviceNetworkId) }
}
private sendCommand(body) {
//log.debug "Yamaha Network Receiver send command: ${body}"
def hubAction = new physicalgraph.device.HubAction(
headers: [HOST: getReceiverAddress()],
method: "POST",
path: "/YamahaRemoteControl/ctrl",
body: body
)
sendHubCommand(hubAction)
}
private getHttpHeaders(headers) {
def obj = [:]
new String(headers.decodeBase64()).split("\r\n").each {param ->
def nameAndValue = param.split(":")
obj[nameAndValue[0]] = (nameAndValue.length == 1) ? "" : nameAndValue[1].trim()
}
return obj
}
private getHttpBody(body) {
def obj = null;
if (body) {
obj = new XmlSlurper().parseText(new String(body.decodeBase64()))
}
return obj
}
private getDeviceId(zone) {
return "yamaha|${settings.receiverIp}|${zone}"
}
private getReceiverAddress() {
return settings.receiverIp + ":80"
}
private String convertIPtoHex(ipAddress) {
return ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join().toUpperCase()
}
private String convertPortToHex(port) {
return port.toString().format( '%04x', port.toInteger() ).toUpperCase()
}