Advertisement
bigbluebanana

Better Trane Z-Wave Thermostat

Jan 6th, 2017
193
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* Better-Thermostat.device.groovy
  2. *
  3. * Variation of TWACK's "Zwave-Thermostat"
  4. * My Trane XL624 had conflicting humidity readings, so I modified TWACK's code to fix it.
  5. * I also integrated code by jwaymire
  6. *
  7. * Device type removes the sliders and replaces them with incremental
  8. * up and down buttons on each side of the heating and cooling setpoints.
  9. *
  10. * To use you must have IDE access on your account. Add a new device
  11. * type and add the custom commands:
  12. * heatLevelUp
  13. * heatLevelDown
  14. * coolLevelUp
  15. * coolLevelDown
  16. * switchMode
  17. * switchFanMode
  18. * Replace the starter code with this code and save the file. Go into
  19. * "My devices" and select the thermostat you want to change. Select "Edit"
  20. * and then change the "Type" to use this device type.
  21. *
  22. * Happy Hacking!
  23. *
  24. * twack@wackware.net
  25. * 20140209
  26. *
  27. */
  28. metadata {
  29. // Automatically generated. Make future change here.
  30. definition (name: "Better Thermostat - Trane XL624", author: "nick@nickdaria.com") {
  31. capability "Temperature Measurement"
  32. capability "Refresh"
  33. capability "Thermostat"
  34. capability "Configuration"
  35. capability "Polling"
  36.  
  37. command "heatLevelUp"
  38. command "heatLevelDown"
  39. command "coolLevelUp"
  40. command "coolLevelDown"
  41. command "switchMode"
  42. command "switchFanMode"
  43. }
  44.  
  45. // simulator metadata
  46. simulator {
  47. status "off" : "command: 4003, payload: 00"
  48. status "heat" : "command: 4003, payload: 01"
  49. status "cool" : "command: 4003, payload: 02"
  50. status "auto" : "command: 4003, payload: 03"
  51. status "emergencyHeat" : "command: 4003, payload: 04"
  52.  
  53. status "fanAuto" : "command: 4403, payload: 00"
  54. status "fanOn" : "command: 4403, payload: 01"
  55. status "fanCirculate" : "command: 4403, payload: 06"
  56.  
  57. status "heat 60" : "command: 4303, payload: 01 01 3C"
  58. status "heat 68" : "command: 4303, payload: 01 01 44"
  59. status "heat 72" : "command: 4303, payload: 01 01 48"
  60.  
  61. status "cool 72" : "command: 4303, payload: 02 01 48"
  62. status "cool 76" : "command: 4303, payload: 02 01 4C"
  63. status "cool 80" : "command: 4303, payload: 02 01 50"
  64.  
  65. status "temp 58" : "command: 3105, payload: 01 22 02 44"
  66. status "temp 62" : "command: 3105, payload: 01 22 02 6C"
  67. status "temp 70" : "command: 3105, payload: 01 22 02 BC"
  68. status "temp 74" : "command: 3105, payload: 01 22 02 E4"
  69. status "temp 78" : "command: 3105, payload: 01 22 03 0C"
  70. status "temp 82" : "command: 3105, payload: 01 22 03 34"
  71.  
  72. status "idle" : "command: 4203, payload: 00"
  73. status "heating" : "command: 4203, payload: 01"
  74. status "cooling" : "command: 4203, payload: 02"
  75. status "fan only" : "command: 4203, payload: 03"
  76. status "pending heat" : "command: 4203, payload: 04"
  77. status "pending cool" : "command: 4203, payload: 05"
  78. status "vent economizer": "command: 4203, payload: 06"
  79.  
  80. // reply messages
  81. reply "2502": "command: 2503, payload: FF"
  82. }
  83.  
  84. tiles {
  85. valueTile("temperature", "device.temperature", width: 2, height: 2) {
  86. state("temperature", label:'${currentValue}°', unit:'F',
  87. backgroundColors:[
  88. [value: 31, color: "#153591"],
  89. [value: 44, color: "#1e9cbb"],
  90. [value: 59, color: "#90d2a7"],
  91. [value: 74, color: "#44b621"],
  92. [value: 84, color: "#f1d801"],
  93. [value: 95, color: "#d04e00"],
  94. [value: 96, color: "#bc2323"]
  95. ]
  96. )
  97. }
  98. standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
  99. state "off", label:'', action:"switchMode", icon:"st.thermostat.heating-cooling-off"
  100. state "heat", label:'', action:"switchMode", icon:"st.thermostat.heat"
  101. state "emergencyHeat", label:'', action:"switchMode", icon:"st.thermostat.emergency-heat"
  102. state "cool", label:'', action:"switchMode", icon:"st.thermostat.cool"
  103. state "auto", label:'', action:"switchMode", icon:"st.thermostat.auto"
  104. }
  105. standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
  106. state "fanAuto", label:'', action:"switchFanMode", icon:"st.thermostat.fan-auto"
  107. state "fanOn", label:'', action:"switchFanMode", icon:"st.thermostat.fan-on"
  108. state "fanCirculate", label:' ', action:"switchFanMode", icon:"st.thermostat.fan-circulate"
  109. }
  110. valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
  111. state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff"
  112. }
  113. valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
  114. state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff"
  115. }
  116. standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
  117. state "default", action:"polling.poll", icon:"st.secondary.refresh"
  118. }
  119. standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
  120. state "configure", label:' ', action:"configuration.configure", icon:"st.secondary.configure"
  121. }
  122. standardTile("heatLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
  123. state "heatLevelUp", label:' ', action:"heatLevelUp", icon:"st.thermostat.thermostat-up"
  124. }
  125. standardTile("heatLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
  126. state "heatLevelDown", label:' ', action:"heatLevelDown", icon:"st.thermostat.thermostat-down"
  127. }
  128. standardTile("coolLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
  129. state "coolLevelUp", label:' ', action:"coolLevelUp", icon:"st.thermostat.thermostat-up"
  130. }
  131. standardTile("coolLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
  132. state "coolLevelDown", label:' ', action:"coolLevelDown", icon:"st.thermostat.thermostat-down"
  133. }
  134.  
  135. main "temperature"
  136. details(["temperature", "mode", "fanMode", "heatLevelDown", "heatingSetpoint", "heatLevelUp", "coolLevelDown", "coolingSetpoint", "coolLevelUp", "refresh", "configure"])
  137. }
  138. }
  139.  
  140. def coolLevelUp(){
  141. int nextLevel = device.currentValue("coolingSetpoint") + 1
  142.  
  143. if( nextLevel > 99){
  144. nextLevel = 99
  145. }
  146. log.debug "Setting cool set point up to: ${nextLevel}"
  147. setCoolingSetpoint(nextLevel)
  148. }
  149.  
  150. def coolLevelDown(){
  151. int nextLevel = device.currentValue("coolingSetpoint") - 1
  152.  
  153. if( nextLevel < 50){
  154. nextLevel = 50
  155. }
  156. log.debug "Setting cool set point down to: ${nextLevel}"
  157. setCoolingSetpoint(nextLevel)
  158. }
  159.  
  160. def heatLevelUp(){
  161. int nextLevel = device.currentValue("heatingSetpoint") + 1
  162.  
  163. if( nextLevel > 90){
  164. nextLevel = 90
  165. }
  166. log.debug "Setting heat set point up to: ${nextLevel}"
  167. setHeatingSetpoint(nextLevel)
  168. }
  169.  
  170. def heatLevelDown(){
  171. int nextLevel = device.currentValue("heatingSetpoint") - 1
  172.  
  173. if( nextLevel < 40){
  174. nextLevel = 40
  175. }
  176. log.debug "Setting heat set point down to: ${nextLevel}"
  177. setHeatingSetpoint(nextLevel)
  178. }
  179.  
  180. def parse(String description)
  181. {
  182. def map = createEvent(zwaveEvent(zwave.parse(description, [0x42:1, 0x43:2, 0x31: 3])))
  183. if (!map) {
  184. return null
  185. }
  186.  
  187. def result = [map]
  188. if (map.isStateChange && map.name in ["heatingSetpoint","coolingSetpoint","thermostatMode"]) {
  189. def map2 = [
  190. name: "thermostatSetpoint",
  191. unit: "F"
  192. ]
  193. if (map.name == "thermostatMode") {
  194. updateState("lastTriedMode", map.value)
  195. if (map.value == "cool") {
  196. map2.value = device.latestValue("coolingSetpoint")
  197. log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
  198. }
  199. else {
  200. map2.value = device.latestValue("heatingSetpoint")
  201. log.info "THERMOSTAT, latest heating setpoint = ${map2.value}"
  202. }
  203. }
  204. else {
  205. def mode = device.latestValue("thermostatMode")
  206. log.info "THERMOSTAT, latest mode = ${mode}"
  207. if ((map.name == "heatingSetpoint" && mode == "heat") || (map.name == "coolingSetpoint" && mode == "cool")) {
  208. map2.value = map.value
  209. map2.unit = map.unit
  210. }
  211. }
  212. if (map2.value != null) {
  213. log.debug "THERMOSTAT, adding setpoint event: $map"
  214. result << createEvent(map2)
  215. }
  216. } else if (map.name == "thermostatFanMode" && map.isStateChange) {
  217. updateState("lastTriedFanMode", map.value)
  218. }
  219. log.debug "Parse returned $result"
  220. result
  221. }
  222.  
  223. // Event Generation
  224. def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
  225. {
  226. def map = [:]
  227. map.value = cmd.scaledValue.toString()
  228. map.unit = cmd.scale == 1 ? "F" : "C"
  229. map.displayed = false
  230. switch (cmd.setpointType) {
  231. case 1:
  232. map.name = "heatingSetpoint"
  233. break;
  234. case 2:
  235. map.name = "coolingSetpoint"
  236. break;
  237. default:
  238. return [:]
  239. }
  240. // So we can respond with same format
  241. state.size = cmd.size
  242. state.scale = cmd.scale
  243. state.precision = cmd.precision
  244. map
  245. }
  246.  
  247. def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd)
  248. {
  249. log.debug "SensorMultilevelReport ${cmd}"
  250. def map = [:]
  251. switch (cmd.sensorType) {
  252. case 1:
  253. // temperature
  254. map.value = cmd.scaledSensorValue.toString()
  255. map.unit = cmd.scale == 1 ? "F" : "C"
  256. map.name = "temperature"
  257.  
  258. break;
  259. case 5:
  260. // humidity
  261. map.value = cmd.scaledSensorValue.toInteger().toString()
  262. map.unit = "%"
  263. map.name = "humidity"
  264. break;
  265. }
  266. map
  267. }
  268.  
  269. def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport cmd)
  270. {
  271. def map = [:]
  272. switch (cmd.operatingState) {
  273. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
  274. map.value = "idle"
  275. break
  276. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_HEATING:
  277. map.value = "heating"
  278. break
  279. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_COOLING:
  280. map.value = "cooling"
  281. break
  282. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_FAN_ONLY:
  283. map.value = "fan only"
  284. break
  285. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_PENDING_HEAT:
  286. map.value = "pending heat"
  287. break
  288. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_PENDING_COOL:
  289. map.value = "pending cool"
  290. break
  291. case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_VENT_ECONOMIZER:
  292. map.value = "vent economizer"
  293. break
  294. }
  295. map.name = "thermostatOperatingState"
  296. map
  297. }
  298.  
  299. def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
  300. def map = [:]
  301. switch (cmd.mode) {
  302. case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
  303. map.value = "off"
  304. break
  305. case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
  306. map.value = "heat"
  307. break
  308. case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
  309. map.value = "emergencyHeat"
  310. break
  311. case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
  312. map.value = "cool"
  313. break
  314. case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
  315. map.value = "auto"
  316. break
  317. }
  318. map.name = "thermostatMode"
  319. map
  320. }
  321.  
  322. def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
  323. def map = [:]
  324. switch (cmd.fanMode) {
  325. case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
  326. map.value = "fanAuto"
  327. break
  328. case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
  329. map.value = "fanOn"
  330. break
  331. case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
  332. map.value = "fanCirculate"
  333. break
  334. }
  335. map.name = "thermostatFanMode"
  336. map.displayed = false
  337. map
  338. }
  339.  
  340. def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
  341. def supportedModes = ""
  342. if(cmd.off) { supportedModes += "off " }
  343. if(cmd.heat) { supportedModes += "heat " }
  344. if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergencyHeat " }
  345. if(cmd.cool) { supportedModes += "cool " }
  346. if(cmd.auto) { supportedModes += "auto " }
  347.  
  348. updateState("supportedModes", supportedModes)
  349. }
  350.  
  351. def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
  352. def supportedFanModes = ""
  353. if(cmd.auto) { supportedFanModes += "fanAuto " }
  354. if(cmd.low) { supportedFanModes += "fanOn " }
  355. if(cmd.circulation) { supportedFanModes += "fanCirculate " }
  356.  
  357. updateState("supportedFanModes", supportedFanModes)
  358. }
  359.  
  360. def updateState(String name, String value) {
  361. state[name] = value
  362. device.updateDataValue(name, value)
  363. }
  364.  
  365. def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
  366. log.debug "Zwave event received: $cmd"
  367. }
  368.  
  369. def zwaveEvent(physicalgraph.zwave.Command cmd) {
  370. log.warn "Unexpected zwave command $cmd"
  371. }
  372.  
  373. // Command Implementations
  374. def poll() {
  375. delayBetween([
  376. zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
  377. zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
  378. zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
  379. zwave.thermostatModeV2.thermostatModeGet().format(),
  380. zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
  381. zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
  382. ], 2300)
  383. }
  384.  
  385. def setHeatingSetpoint(degreesF) {
  386. setHeatingSetpoint(degreesF.toDouble())
  387. }
  388.  
  389. def setHeatingSetpoint(Double degreesF) {
  390. def p = (state.precision == null) ? 1 : state.precision
  391. delayBetween([
  392. zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: 1, precision: p, scaledValue: degreesF).format(),
  393. zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
  394. ])
  395. }
  396.  
  397. def setCoolingSetpoint(degreesF) {
  398. setCoolingSetpoint(degreesF.toDouble())
  399. }
  400.  
  401. def setCoolingSetpoint(Double degreesF) {
  402. def p = (state.precision == null) ? 1 : state.precision
  403. delayBetween([
  404. zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 2, scale: 1, precision: p, scaledValue: degreesF).format(),
  405. zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()
  406. ])
  407. }
  408.  
  409. def configure() {
  410. delayBetween([
  411. zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
  412. zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
  413. zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()
  414. ], 2300)
  415. }
  416.  
  417. def modes() {
  418. ["off", "auto", "emergencyHeat", "heat", "cool"]
  419. }
  420.  
  421. def switchMode() {
  422. def currentMode = device.currentState("thermostatMode")?.value
  423. def lastTriedMode = getDataByName("lastTriedMode") ?: currentMode ?: "off"
  424. def supportedModes = getDataByName("supportedModes")
  425. def modeOrder = modes()
  426. def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
  427. def nextMode = next(lastTriedMode)
  428. if (supportedModes?.contains(currentMode)) {
  429. while (!supportedModes.contains(nextMode) && nextMode != "off") {
  430. nextMode = next(nextMode)
  431. }
  432. }
  433. log.debug "Switching to mode: ${nextMode}"
  434. switchToMode(nextMode)
  435. }
  436.  
  437. def switchToMode(nextMode) {
  438. def supportedModes = getDataByName("supportedModes")
  439. if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
  440. if (nextMode in modes()) {
  441. updateState("lastTriedMode", nextMode)
  442. return "$nextMode"()
  443. } else {
  444. log.debug("no mode method '$nextMode'")
  445. }
  446. }
  447.  
  448. def switchFanMode() {
  449. def currentMode = device.currentState("thermostatFanMode")?.value
  450. def lastTriedMode = getDataByName("lastTriedFanMode") ?: currentMode ?: "off"
  451. def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
  452. def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
  453. def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
  454. def nextMode = next(lastTriedMode)
  455. while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
  456. nextMode = next(nextMode)
  457. }
  458. switchToFanMode(nextMode)
  459. }
  460.  
  461. def switchToFanMode(nextMode) {
  462. def supportedFanModes = getDataByName("supportedFanModes")
  463. if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
  464.  
  465. def returnCommand
  466. if (nextMode == "fanAuto") {
  467. returnCommand = fanAuto()
  468. } else if (nextMode == "fanOn") {
  469. returnCommand = fanOn()
  470. } else if (nextMode == "fanCirculate") {
  471. returnCommand = fanCirculate()
  472. } else {
  473. log.debug("no fan mode '$nextMode'")
  474. }
  475. if(returnCommand) updateState("lastTriedFanMode", nextMode)
  476. returnCommand
  477. }
  478.  
  479. def getDataByName(String name) {
  480. state[name] ?: device.getDataValue(name)
  481. }
  482.  
  483. def getModeMap() { [
  484. "off": 0,
  485. "heat": 1,
  486. "cool": 2,
  487. "emergency heat": 4
  488. ]}
  489.  
  490. def setThermostatMode(String value) {
  491. delayBetween([
  492. zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
  493. zwave.thermostatModeV2.thermostatModeGet().format()
  494. ])
  495. }
  496.  
  497. def getFanModeMap() { [
  498. "auto": 0,
  499. "on": 1,
  500. "circulate": 6
  501. ]}
  502.  
  503. def setThermostatFanMode(String value) {
  504. delayBetween([
  505. zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
  506. zwave.thermostatFanModeV3.thermostatFanModeGet().format()
  507. ])
  508. }
  509.  
  510. def off() {
  511. delayBetween([
  512. zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
  513. zwave.thermostatModeV2.thermostatModeGet().format()
  514. ])
  515. }
  516.  
  517. def heat() {
  518. delayBetween([
  519. zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
  520. zwave.thermostatModeV2.thermostatModeGet().format()
  521. ])
  522. }
  523.  
  524. def emergencyHeat() {
  525. delayBetween([
  526. zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
  527. zwave.thermostatModeV2.thermostatModeGet().format()
  528. ])
  529. }
  530.  
  531. def cool() {
  532. delayBetween([
  533. zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
  534. zwave.thermostatModeV2.thermostatModeGet().format()
  535. ])
  536. }
  537.  
  538. def auto() {
  539. delayBetween([
  540. zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
  541. zwave.thermostatModeV2.thermostatModeGet().format()
  542. ])
  543. }
  544.  
  545. def fanOn() {
  546. delayBetween([
  547. zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
  548. zwave.thermostatFanModeV3.thermostatFanModeGet().format()
  549. ])
  550. }
  551.  
  552. def fanAuto() {
  553. delayBetween([
  554. zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
  555. zwave.thermostatFanModeV3.thermostatFanModeGet().format()
  556. ])
  557. }
  558.  
  559. def fanCirculate() {
  560. delayBetween([
  561. zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
  562. zwave.thermostatFanModeV3.thermostatFanModeGet().format()
  563. ])
  564. }
Advertisement
RAW Paste Data Copied
Advertisement