From 2536c6908305cba66625253431144324c20a6dcd Mon Sep 17 00:00:00 2001 From: Brian Steere Date: Wed, 5 Jul 2017 16:01:16 -0500 Subject: [PATCH 1/7] Update Every Element with more accurate enum settings Added a couple of extra enum inputs to demonstrate additional states --- .../examples/every-element.src/every-element.groovy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/smartapps/smartthings/examples/every-element.src/every-element.groovy b/smartapps/smartthings/examples/every-element.src/every-element.groovy index 417ae1c..caad6aa 100644 --- a/smartapps/smartthings/examples/every-element.src/every-element.groovy +++ b/smartapps/smartthings/examples/every-element.src/every-element.groovy @@ -202,7 +202,8 @@ def inputSelectionPage() { section("options variations") { paragraph "tap these elements and look at the differences when selecting an option" - input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions)) + input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", options: ["Thing 1", "Thing 2", "(Complicated) Thing 3"]) + input(type: "enum", name: "selectionSimpleGrouped", title: "Simple (Grouped) options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions)) input(type: "enum", name: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions) } @@ -214,15 +215,15 @@ def inputSelectionPage() { section("segmented") { paragraph "segmented should only work if there are either 2 or 3 options to choose from" - input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", groupedOptions: addGroup(["One"])) - input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", groupedOptions: addGroup(["One", "Two", "Three", "Four"])) + input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", options: ["One"]) + input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", options: ["One", "Two", "Three", "Four"]) paragraph "multiple and required will have no effect on segmented selection elements. There will always be exactly 1 option selected" input(type: "enum", name: "selectionSegmented2", style: "segmented", title: "2 options", options: ["One", "Two"]) input(type: "enum", name: "selectionSegmented3", style: "segmented", title: "3 options", options: ["One", "Two", "Three"]) paragraph "specifying defaultValue still works with segmented selection elements" - input(type: "enum", name: "selectionSegmentedWithDefault", title: "defaulted to 'two'", groupedOptions: addGroup(["One", "Two", "Three"]), defaultValue: "Two") + input(type: "enum", name: "selectionSegmentedWithDefault", style: "segmented", title: "defaulted to 'two'", options: ["One", "Two", "Three"], defaultValue: "Two") } section("required: true") { @@ -231,6 +232,8 @@ def inputSelectionPage() { section("multiple: true") { input(type: "enum", name: "selectionMultiple", title: "This allows multiple selections", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true) + input(type: "enum", name: "selectionMultipleDefault1", title: "This allows multiple selections with a single default", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: "an option") + input(type: "enum", name: "selectionMultipleDefault2", title: "This allows multiple selections with multiple defaults", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: ["an option", "another option"]) } section("with image") { From 771926c33730b7490ce9b06f184910ca37bfc986 Mon Sep 17 00:00:00 2001 From: marstorp Date: Wed, 5 Jul 2017 16:50:30 -0700 Subject: [PATCH 2/7] 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. --- .../zwave-thermostat.groovy | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy index 743ab0f..5caf602 100644 --- a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy +++ b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy @@ -15,10 +15,9 @@ metadata { definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") { capability "Actuator" capability "Temperature Measurement" - capability "Relative Humidity Measurement" capability "Thermostat" capability "Configuration" - capability "Polling" + capability "Refresh" capability "Sensor" capability "Health Check" @@ -117,7 +116,7 @@ metadata { state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff" } 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" details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"]) @@ -125,13 +124,20 @@ metadata { } def installed(){ - // 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]) + sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())) + initialize() } def updated(){ + initialize() +} + +def initialize() { // 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]) + unschedule() + runEvery5Minutes("refresh") + refresh() } def parse(String description) @@ -149,6 +155,7 @@ def parse(String description) ] if (map.name == "thermostatMode") { state.lastTriedMode = map.value + map.data = [supportedThermostatModes:state.supportedThermostatModes] if (map.value == "cool") { map2.value = device.latestValue("coolingSetpoint") log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}" @@ -172,6 +179,7 @@ def parse(String description) } } else if (map.name == "thermostatFanMode" && map.isStateChange) { state.lastTriedFanMode = map.value + map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes] } log.debug "Parse returned $result" result @@ -305,26 +313,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod } def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { - def supportedModes = "" - if(cmd.off) { supportedModes += "off " } - if(cmd.heat) { supportedModes += "heat " } - if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " } - if(cmd.cool) { supportedModes += "cool " } - if(cmd.auto) { supportedModes += "auto " } + def supportedModes = [] + if(cmd.off) { supportedModes << "off" } + if(cmd.heat) { supportedModes << "heat" } + if(cmd.cool) { supportedModes << "cool" } + if(cmd.auto) { supportedModes << "auto" } + if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" } - state.supportedModes = supportedModes - // No events to be generated, return empty map + state.supportedThermostatModes = supportedModes + sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false) return [:] } def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) { - def supportedFanModes = "" - if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto " - if(cmd.low) { supportedFanModes += "on " } // "fanOn" - if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate" + def supportedFanModes = [] + if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto " + if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate" + if(cmd.low) { supportedFanModes << "on" } // "fanOn" - state.supportedFanModes = supportedFanModes - // No events to be generated, return empty map + state.supportedThermostatFanModes = supportedFanModes + sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false) return [:] } @@ -337,15 +345,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { } // Command Implementations -def poll() { - delayBetween([ - 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 refresh() { + def cmds = [] + cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()) + cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format()) + cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format()) + cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format()) + cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature + cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()) + 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) { @@ -416,28 +426,14 @@ def ping() { 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() { - ["off", "heat", "cool", "auto", "emergency heat"] + return state.supportedThermostatModes } def switchMode() { def currentMode = device.currentState("thermostatMode")?.value - def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off" - def supportedModes = getDataByName("supportedModes") + def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"] + def supportedModes = getDataByName("supportedThermostatModes") def modeOrder = modes() def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def nextMode = next(lastTriedMode) @@ -454,7 +450,7 @@ def switchMode() { } 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 (nextMode in modes()) { state.lastTriedMode = nextMode @@ -466,9 +462,9 @@ def switchToMode(nextMode) { def switchFanMode() { def currentMode = device.currentState("thermostatFanMode")?.value - def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off" - def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn" - def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn" + def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"] + def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"] + def modeOrder = state.supportedThermostatFanModes def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def nextMode = next(lastTriedMode) while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto" @@ -478,7 +474,7 @@ def switchFanMode() { } def switchToFanMode(nextMode) { - def supportedFanModes = getDataByName("supportedFanModes") + def supportedFanModes = getDataByName("supportedThermostatFanModes") if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" def returnCommand From 1b0437c633a2a7371aa7a862c53d1ba205da3a13 Mon Sep 17 00:00:00 2001 From: juano2310 Date: Wed, 5 Jul 2017 14:00:24 -0400 Subject: [PATCH 3/7] ICP-507 - Replace location count null with 0 Updated with def count = options.size().toString() --- smartapps/smartthings/lifx-connect.src/lifx-connect.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy index caca7b2..fbda03b 100644 --- a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy +++ b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy @@ -72,7 +72,7 @@ def authPage() { log.debug "have LIFX access token" def options = locationOptions() ?: [] - def count = options.size() + def count = options.size().toString() return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) { section("Select your location") { From 4da49283bff51c9a45a6a4d52035d29e7e81a40f Mon Sep 17 00:00:00 2001 From: Nathan Cauffman Date: Thu, 6 Jul 2017 13:22:12 -0700 Subject: [PATCH 4/7] MSA-2084: Updated with new model number and health check --- .../spruce-sensor.src/spruce-sensor.groovy | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index 77a5698..fc1fe39 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -1,5 +1,5 @@ /** - * Spruce Sensor -Pre-release V2 10/8/2015 + * Spruce Sensor -updated with SLP model number 5/2017 * * Copyright 2014 Plaid Systems * @@ -14,25 +14,34 @@ * -------10/20/2015 Updates-------- -Fix/add battery reporting interval to update - -remove polling and/or refresh(?) + -remove polling and/or refresh + + -------5/2017 Updates-------- + -Add fingerprints for SLP + -add device health, check every 60mins + 2mins */ + metadata { - definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") { + definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") { capability "Configuration" capability "Battery" capability "Relative Humidity Measurement" capability "Temperature Measurement" capability "Sensor" + capability "Health Check" //capability "Polling" + attribute "checkInterval", "string" attribute "maxHum", "string" attribute "minHum", "string" + command "resetHumidity" command "refresh" - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", deviceJoinName: "Spruce Sensor" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", deviceJoinName: "Spruce Sensor" } preferences { @@ -293,6 +302,11 @@ def setConfig(){ sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized") } +def installed(){ + //check every 1 hour + 2mins + sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +} + //when device preferences are changed def updated(){ log.debug "device updated" @@ -303,6 +317,8 @@ def updated(){ sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins") } } + //check every 1 hour + 2mins + sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } //poll From f519a2d828ec3e9e5e0673d4af7d35f4d8a9d817 Mon Sep 17 00:00:00 2001 From: natec007 Date: Thu, 6 Jul 2017 14:27:33 -0700 Subject: [PATCH 5/7] Update spruce-sensor.groovy Removed checkInterval attribute sendEvent(name: "checkInterval" already in updated and installed --- .../plaidsystems/spruce-sensor.src/spruce-sensor.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index fc1fe39..f2e1154 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -32,7 +32,6 @@ metadata { capability "Health Check" //capability "Polling" - attribute "checkInterval", "string" attribute "maxHum", "string" attribute "minHum", "string" @@ -411,4 +410,4 @@ private byte[] reverseArray(byte[] array) { i++; } return array -} \ No newline at end of file +} From 545be046f0a1f6603214b3966fd8e9cb7b025923 Mon Sep 17 00:00:00 2001 From: jackchi Date: Tue, 11 Jul 2017 17:05:42 -0700 Subject: [PATCH 6/7] [DHF-24] Update Aeon Multi6 to 2hr2min --- devicetypes/smartthings/aeon-multisensor-6.src/README.md | 8 ++------ .../aeon-multisensor-6.src/aeon-multisensor-6.groovy | 8 ++++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/README.md b/devicetypes/smartthings/aeon-multisensor-6.src/README.md index 22015f9..85cd8e6 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/README.md +++ b/devicetypes/smartthings/aeon-multisensor-6.src/README.md @@ -27,13 +27,9 @@ Works with: ## Device Health 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. -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. +Aeon MultiSensor reports in once every hour. -* __32min__ checkInterval +* __122min__ checkInterval ## Troubleshooting diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy index c789cd9..a678596 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy +++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy @@ -130,13 +130,13 @@ metadata { } def installed(){ -// 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]) +// Device-Watch simply pings if no device events received for 122min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } def updated() { -// 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]) +// Device-Watch simply pings if no device events received for 122min(checkInterval) + 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 "${device.displayName} is now ${device.latestValue("powerSupply")}" From 97bfe61baa464cdac8894a566f19bcd6cc3c68c7 Mon Sep 17 00:00:00 2001 From: Zach Varberg Date: Fri, 14 Jul 2017 10:08:44 -0500 Subject: [PATCH 7/7] Update ranges for centralite battery values This just updates the range to be more conservative as well as match the battery curve better. --- .../smartsense-motion-sensor.groovy | 4 ++-- .../smartsense-multi-sensor.groovy | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index 336c31e..c00e65e 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -171,8 +171,8 @@ private Map getBatteryResult(rawValue) { def pct = batteryMap[volts] result.value = pct } else { - def minVolts = 2.1 - def maxVolts = 3.0 + def minVolts = 2.4 + def maxVolts = 2.7 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) if (roundedPct <= 0) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index e6978b8..a8d436e 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -274,7 +274,7 @@ private Map getBatteryResult(rawValue) { result.value = pct } else { def minVolts = 2.1 - def maxVolts = 3.0 + def maxVolts = 2.7 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) if (roundedPct <= 0)