-
Notifications
You must be signed in to change notification settings - Fork 88.5k
[WWST-5914] POPP Radiator Thermostat integration #11575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ metadata { | |
| fingerprint mfr: "0060", prod: "0015", model: "0001", deviceJoinName: "Everspring Thermostatic Radiator Valve", mnmn: "SmartThings", vid: "generic-radiator-thermostat" | ||
| //this DTH is sending temperature setpoint commands using Celsius scale and assumes that they'll be handled correctly by device | ||
| //if new device added to this DTH won't be able to do that, make sure to you'll handle conversion in a right way | ||
| fingerprint mfr: "0002", prod: "0115", model: "A010", deviceJoinName: "POPP Radiator Thermostat Valve", mnmn: "SmartThings", vid: "generic-radiator-thermostat-2" | ||
| } | ||
|
|
||
| tiles(scale: 2) { | ||
|
|
@@ -85,7 +86,7 @@ metadata { | |
| } | ||
|
|
||
| def initialize() { | ||
| sendEvent(name: "checkInterval", value: 4 * 60 * 60 + 24 * 60 , displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) | ||
| sendEvent(name: "checkInterval", value: checkInterval , displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) | ||
| sendEvent(name: "supportedThermostatModes", value: thermostatSupportedModes.encodeAsJson(), displayed: false) | ||
| sendEvent(name: "heatingSetpointRange", value: [minHeatingSetpointTemperature, maxHeatingSetpointTemperature], displayed: false) | ||
| response(refresh()) | ||
|
|
@@ -103,6 +104,8 @@ def configure() { | |
| def cmds = [] | ||
| if (isEverspringRadiatorThermostat()) { | ||
| cmds += secure(zwave.configurationV1.configurationSet(parameterNumber: 1, size: 2, scaledConfigurationValue: 15)) //automatic temperature reports every 15 minutes | ||
| } else if (isPoppRadiatorThermostat()) { | ||
| cmds += secure(zwave.wakeUpV2.wakeUpIntervalSet(seconds: 600, nodeid: zwaveHubNodeId)) | ||
| } | ||
| return cmds | ||
| } | ||
|
|
@@ -130,6 +133,25 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat | |
| } | ||
| } | ||
|
|
||
| def zwaveEvent(physicalgraph.zwave.commands.multicmdv1.MultiCmdEncap cmd) { | ||
| cmd.encapsulatedCommands().collect { encapsulatedCommand -> | ||
| zwaveEvent(encapsulatedCommand) | ||
| }.flatten() | ||
| } | ||
|
|
||
| def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { | ||
| def cmds = [] | ||
| if (!isPoppRadiatorThermostat()) { | ||
| cmds += zwave.batteryV1.batteryGet() // POPP sends battery report automatically every wake up by itself, there's no need to duplicate it | ||
| } | ||
| cmds += [ | ||
| zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: state.cachedSetpoint, setpointType: 1, size: 2]), | ||
| zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1), | ||
| zwave.wakeUpV2.wakeUpNoMoreInformation() | ||
| ] | ||
| [response(multiEncap(cmds))] | ||
| } | ||
|
|
||
| def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { | ||
| def value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel | ||
| def map = [name: "battery", value: value, unit: "%"] | ||
|
|
@@ -205,8 +227,12 @@ def off() { | |
| } | ||
|
|
||
| def setHeatingSetpoint(setpoint) { | ||
| if (isPoppRadiatorThermostat()) { | ||
| sendEvent(name: "heatingSetpoint", value: setpoint, unit: temperatureScale) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand why you did this, since I bet the app will just spin forever unless you do, but I'm just going to note that it seems wrong to do this when we know that it won't actually be set for up to 5 minutes.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I know, but since we don't have any 'pending' indicator in OneApp or something else that would inform user that it still hasn't been changed I think it's lesser evil. |
||
| } | ||
| setpoint = temperatureScale == 'C' ? setpoint : fahrenheitToCelsius(setpoint) | ||
| setpoint = Math.max(Math.min(setpoint, maxHeatingSetpointTemperature), minHeatingSetpointTemperature) | ||
| state.cachedSetpoint = setpoint | ||
| [ | ||
| secure(zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: setpoint, setpointType: 1, size: 2])), | ||
| "delay 2000", | ||
|
|
@@ -237,9 +263,23 @@ private secure(cmd) { | |
| } | ||
| } | ||
|
|
||
| def multiEncap(cmds) { | ||
| if (zwaveInfo.cc.contains("8F")) { | ||
| secure(zwave.multiCmdV1.multiCmdEncap().encapsulate(cmds.collect { | ||
| cmd -> cmd.format() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have we used multiencap anywhere else before this? Any problems?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we haven't, at least I haven't found any example. In this particular case it works like a charm. I haven't really noticed that such CC existed until I got this device. Manufacturer stated in manual that using this would extend battery life, so I figured out it's worth trying. I don't know whether it is commonly used in other devices. |
||
| })) | ||
| } else { | ||
| delayBetween(cmds.collect { | ||
| cmd -> secure(cmd) | ||
| }, 2500) | ||
| } | ||
| } | ||
|
|
||
| private getMaxHeatingSetpointTemperature() { | ||
| if (isEverspringRadiatorThermostat()) { | ||
| temperatureScale == 'C' ? 35 : 95 | ||
| } else if (isPoppRadiatorThermostat()) { | ||
| temperatureScale == 'C' ? 28 : 82 | ||
| } else { | ||
| temperatureScale == 'C' ? 30 : 86 | ||
| } | ||
|
|
@@ -248,6 +288,8 @@ private getMaxHeatingSetpointTemperature() { | |
| private getMinHeatingSetpointTemperature() { | ||
| if (isEverspringRadiatorThermostat()) { | ||
| temperatureScale == 'C' ? 15 : 59 | ||
| } else if (isPoppRadiatorThermostat()) { | ||
| temperatureScale == 'C' ? 4 : 39 | ||
| } else { | ||
| temperatureScale == 'C' ? 10 : 50 | ||
| } | ||
|
|
@@ -256,11 +298,25 @@ private getMinHeatingSetpointTemperature() { | |
| private getThermostatSupportedModes() { | ||
| if (isEverspringRadiatorThermostat()) { | ||
| ["off", "heat", "emergency heat"] | ||
| } else if (isPoppRadiatorThermostat()) { //that's just for looking fine in Classic | ||
| ["heat"] | ||
| } else { | ||
| ["off","heat"] | ||
| } | ||
| } | ||
|
|
||
| def getCheckInterval() { | ||
| if (isPoppRadiatorThermostat()) { | ||
| 2 * 60 * 10 + 2 * 60 | ||
| } else { | ||
| 4 * 60 * 60 + 24 * 60 | ||
| } | ||
| } | ||
|
|
||
| private isEverspringRadiatorThermostat() { | ||
| zwaveInfo.mfr == "0060" && zwaveInfo.prod == "0015" | ||
| } | ||
|
|
||
| private isPoppRadiatorThermostat() { | ||
| zwaveInfo.mfr == "0002" && zwaveInfo.prod == "0115" | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kianooshST here's some command caching for wakeup like we had talked about. Maybe a more generic implementation in the future?