Created
May 6, 2015 00:24
-
-
Save bdwilson/0843da30fb6bcda97752 to your computer and use it in GitHub Desktop.
HEMv2+ Sandood
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Aeon HEMv2+ | |
* | |
* Copyright 2014 Barry A. Burke | |
* | |
* 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. | |
* | |
* | |
* Aeon Home Energy Meter v2 (US) | |
* | |
* Author: Barry A. Burke | |
* Contributors: Brock Haymond: UI updates | |
* | |
* Genesys: Based off of Aeon Smart Meter Code sample provided by SmartThings (2013-05-30). Built on US model | |
* may also work on international versions (currently reports total values only) | |
* | |
* History: | |
* | |
* 2014-06-13: Massive OverHaul | |
* - Fixed Configuration (original had byte order of bitstrings backwards | |
* - Increased reporting frequency to 10s - note that values won't report unless they change | |
* (they will also report if they exceed limits defined in the settings - currently just using | |
* the defaults). | |
* - Added support for Volts & Amps monitoring (was only Power and Energy) | |
* - Added flexible tile display. Currently only used to show High and Low values since last | |
* reset (with time stamps). | |
* - All tiles are attributes, so that their values are preserved when you're not 'watching' the | |
* meter display | |
* - Values are formatted to Strings in zwaveEvent parser so that we don't lose decimal values | |
* in the tile label display conversion | |
* - Updated fingerprint to match Aeon Home Energy Monitor v2 deviceId & clusters | |
* - Added colors for Watts and Amps display | |
* - Changed time format to 24 hour | |
* 2014-06-17: Tile Tweaks | |
* - Reworked "decorations:" - current values are no longer "flat" | |
* - Added colors to current Watts (0-18000) & Amps (0-150) | |
* - Changed all colors to use same blue-green-orange-red as standard ST temperature guages | |
* 2014-06-18: Cost calculations | |
* - Added $/kWh preference | |
* 2014-09-07: Bug fix & Cleanup | |
* - Fixed "Unexpected Error" on Refresh tile - (added Refresh Capability) | |
* - Cleaned up low values - reset to ridiculously high value instead of null | |
* - Added poll() command/capability (just does a refresh) | |
* 2014-09-19: GUI Tweaks, HEM v1 alterations (from Brock Haymond) | |
* - Reworked all tiles for look, color, text formatting, & readability | |
* 2014-09-20: Added HEMv1 Battery reporting (from Brock Haymond) | |
* 2014-11-06: Added alternate display of L2 and L2 values instead of Low/High, based on version by Jayant Jhamb | |
* 2014-11-11: Massive overhaul completed (see GitHub comments for specifics) | |
* - | |
*/ | |
metadata { | |
// Automatically generated. Make future change here. | |
definition ( | |
name: "Aeon HEMv2+ SANdood", | |
namespace: "Green Living", | |
category: "Green Living", | |
author: "Brian Wilson" | |
) | |
{ | |
capability "Energy Meter" | |
capability "Power Meter" | |
capability "Configuration" | |
capability "Sensor" | |
capability "Refresh" | |
capability "Polling" | |
capability "Battery" | |
attribute "energy", "string" | |
attribute "power", "string" | |
attribute "volts", "string" | |
attribute "voltage", "string" // We'll deliver both, since the correct one is not defined anywhere | |
attribute "amps", "string" | |
attribute "energyDisp", "string" | |
attribute "energyOne", "string" | |
attribute "energyTwo", "string" | |
attribute "powerDisp", "string" | |
attribute "powerOne", "string" | |
attribute "powerTwo", "string" | |
attribute "voltsDisp", "string" | |
attribute "voltsOne", "string" | |
attribute "voltsTwo", "string" | |
attribute "ampsDisp", "string" | |
attribute "ampsOne", "string" | |
attribute "ampsTwo", "string" | |
command "reset" | |
command "configure" | |
command "refresh" | |
command "poll" | |
command "toggleDisplay" | |
fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60" | |
// v2 fingerprint deviceId: "0x3101", inClusters: "0x70,0x32,0x60,0x85,0x56,0x72,0x86" | |
} | |
// simulator metadata | |
simulator { | |
for (int i = 0; i <= 10000; i += 1000) { | |
status "power ${i} W": new physicalgraph.zwave.Zwave().meterV1.meterReport( | |
scaledMeterValue: i, precision: 3, meterType: 33, scale: 2, size: 4).incomingMessage() | |
} | |
for (int i = 0; i <= 100; i += 10) { | |
status "energy ${i} kWh": new physicalgraph.zwave.Zwave().meterV1.meterReport( | |
scaledMeterValue: i, precision: 3, meterType: 33, scale: 0, size: 4).incomingMessage() | |
} | |
// TODO: Add data feeds for Volts and Amps | |
} | |
// tile definitions | |
tiles { | |
// Watts row | |
valueTile("powerDisp", "device.powerDisp") { | |
state ( | |
"default", | |
label:'${currentValue}', | |
foregroundColors:[ | |
[value: 1, color: "#000000"], | |
[value: 10000, color: "#ffffff"] | |
], | |
foregroundColor: "#000000", | |
backgroundColors:[ | |
[value: "0 Watts", color: "#153591"], | |
[value: "3000 Watts", color: "#1e9cbb"], | |
[value: "6000 Watts", color: "#90d2a7"], | |
[value: "9000 Watts", color: "#44b621"], | |
[value: "12000 Watts", color: "#f1d801"], | |
[value: "15000 Watts", color: "#d04e00"], | |
[value: "18000 Watts", color: "#bc2323"] | |
/* For low-wattage homes, use these values | |
[value: "0 Watts", color: "#153591"], | |
[value: "500 Watts", color: "#1e9cbb"], | |
[value: "1000 Watts", color: "#90d2a7"], | |
[value: "1500 Watts", color: "#44b621"], | |
[value: "2000 Watts", color: "#f1d801"], | |
[value: "2500 Watts", color: "#d04e00"], | |
[value: "3000 Watts", color: "#bc2323"] | |
*/ | |
] | |
) | |
} | |
valueTile("powerOne", "device.powerOne") { | |
state( | |
"default", | |
label:'${currentValue}', | |
foregroundColors:[ | |
[value: 1, color: "#000000"], | |
[value: 10000, color: "#ffffff"] | |
], | |
foregroundColor: "#000000", | |
backgroundColors:[ | |
[value: "0 Watts", color: "#153591"], | |
[value: "3000 Watts", color: "#1e9cbb"], | |
[value: "6000 Watts", color: "#90d2a7"], | |
[value: "9000 Watts", color: "#44b621"], | |
[value: "12000 Watts", color: "#f1d801"], | |
[value: "15000 Watts", color: "#d04e00"], | |
[value: "18000 Watts", color: "#bc2323"] | |
/* For low-wattage homes, use these values | |
[value: "0 Watts", color: "#153591"], | |
[value: "500 Watts", color: "#1e9cbb"], | |
[value: "1000 Watts", color: "#90d2a7"], | |
[value: "1500 Watts", color: "#44b621"], | |
[value: "2000 Watts", color: "#f1d801"], | |
[value: "2500 Watts", color: "#d04e00"], | |
[value: "3000 Watts", color: "#bc2323"] | |
*/ | |
] | |
) | |
} | |
valueTile("powerTwo", "device.powerTwo") { | |
state( | |
"default", | |
label:'${currentValue}', | |
foregroundColors:[ | |
[value: 1, color: "#000000"], | |
[value: 10000, color: "#ffffff"] | |
], | |
foregroundColor: "#000000", | |
backgroundColors:[ | |
[value: "0 Watts", color: "#153591"], | |
[value: "3000 Watts", color: "#1e9cbb"], | |
[value: "6000 Watts", color: "#90d2a7"], | |
[value: "9000 Watts", color: "#44b621"], | |
[value: "12000 Watts", color: "#f1d801"], | |
[value: "15000 Watts", color: "#d04e00"], | |
[value: "18000 Watts", color: "#bc2323"] | |
/* For low-wattage homes, use these values | |
[value: "0 Watts", color: "#153591"], | |
[value: "500 Watts", color: "#1e9cbb"], | |
[value: "1000 Watts", color: "#90d2a7"], | |
[value: "1500 Watts", color: "#44b621"], | |
[value: "2000 Watts", color: "#f1d801"], | |
[value: "2500 Watts", color: "#d04e00"], | |
[value: "3000 Watts", color: "#bc2323"] | |
*/ | |
] | |
) | |
} | |
// Power row | |
valueTile("energyDisp", "device.energyDisp") { | |
state( | |
"default", | |
label: '${currentValue}', | |
foregroundColor: "#000000", | |
backgroundColor: "#ffffff") | |
} | |
valueTile("energyOne", "device.energyOne") { | |
state( | |
"default", | |
label: '${currentValue}', | |
foregroundColor: "#000000", | |
backgroundColor: "#ffffff") | |
} | |
valueTile("energyTwo", "device.energyTwo") { | |
state( | |
"default", | |
label: '${currentValue}', | |
foregroundColor: "#000000", | |
backgroundColor: "#ffffff") | |
} | |
// Volts row | |
valueTile("voltsDisp", "device.voltsDisp") { | |
state( | |
"default", | |
label: '${currentValue}', | |
backgroundColors:[ | |
[value: "115.6 Volts", color: "#bc2323"], | |
[value: "117.8 Volts", color: "#D04E00"], | |
[value: "120.0 Volts", color: "#44B621"], | |
[value: "122.2 Volts", color: "#D04E00"], | |
[value: "124.4 Volts", color: "#bc2323"] | |
] | |
) | |
} | |
valueTile("voltsOne", "device.voltsOne") { | |
state( | |
"default", | |
label:'${currentValue}', | |
backgroundColors:[ | |
[value: "L1", color: "#ffffff"], | |
[value: "115.6 Volts", color: "#bc2323"], | |
[value: "117.8 Volts", color: "#D04E00"], | |
[value: "120.0 Volts", color: "#44B621"], | |
[value: "122.2 Volts", color: "#D04E00"], | |
[value: "124.4 Volts", color: "#bc2323"] | |
] | |
) | |
} | |
valueTile("voltsTwo", "device.voltsTwo") { | |
state( | |
"default", | |
label:'${currentValue}', | |
backgroundColors:[ | |
[value: "L2", color: "#ffffff"], | |
[value: "115.6 Volts", color: "#bc2323"], | |
[value: "117.8 Volts", color: "#D04E00"], | |
[value: "120.0 Volts", color: "#44B621"], | |
[value: "122.2 Volts", color: "#D04E00"], | |
[value: "124.4 Volts", color: "#bc2323"] | |
] | |
) | |
} | |
// Amps row | |
valueTile("ampsDisp", "device.ampsDisp") { | |
state ( | |
"default", | |
label: '${currentValue}' , | |
foregroundColor: "#000000", | |
color: "#000000", | |
backgroundColors:[ | |
[value: "0 Amps", color: "#153591"], | |
[value: "25 Amps", color: "#1e9cbb"], | |
[value: "50 Amps", color: "#90d2a7"], | |
[value: "75 Amps", color: "#44b621"], | |
[value: "100 Amps", color: "#f1d801"], | |
[value: "125 Amps", color: "#d04e00"], | |
[value: "150 Amps", color: "#bc2323"] | |
] | |
) | |
} | |
valueTile("ampsOne", "device.ampsOne") { | |
state( | |
"default", | |
label:'${currentValue}', | |
foregroundColor: "#000000", | |
color: "#000000", | |
backgroundColors:[ | |
[value: "0 Amps", color: "#153591"], | |
[value: "25 Amps", color: "#1e9cbb"], | |
[value: "50 Amps", color: "#90d2a7"], | |
[value: "75 Amps", color: "#44b621"], | |
[value: "100 Amps", color: "#f1d801"], | |
[value: "125 Amps", color: "#d04e00"], | |
[value: "150 Amps", color: "#bc2323"] | |
] | |
) | |
} | |
valueTile("ampsTwo", "device.ampsTwo") { | |
state( | |
"default", | |
label:'${currentValue}', | |
foregroundColor: "#000000", | |
color: "#000000", | |
backgroundColors:[ | |
[value: "0 Amps", color: "#153591"], | |
[value: "25 Amps", color: "#1e9cbb"], | |
[value: "50 Amps", color: "#90d2a7"], | |
[value: "75 Amps", color: "#44b621"], | |
[value: "100 Amps", color: "#f1d801"], | |
[value: "125 Amps", color: "#d04e00"], | |
[value: "150 Amps", color: "#bc2323"] | |
] | |
) | |
} | |
// Controls row | |
standardTile("reset", "command.reset", inactiveLabel: false) { | |
state "default", label:'reset', action:"reset", icon: "st.Health & Wellness.health7" | |
} | |
standardTile("refresh", "command.refresh", inactiveLabel: false) { | |
state "default", label:'refresh', action:"refresh.refresh", icon:"st.secondary.refresh-icon" | |
} | |
standardTile("configure", "command.configure", inactiveLabel: false) { | |
state "configure", label:'', action: "configure", icon:"st.secondary.configure" | |
} | |
standardTile("toggle", "command.toggleDisplay", inactiveLabel: false) { | |
state "default", label: "toggle", action: "toggleDisplay", icon: "st.motion.motion.inactive" | |
} | |
/* HEMv1 has a battery; v2 is line-powered */ | |
valueTile("battery", "device.battery", decoration: "flat") { | |
state "battery", label:'${currentValue}% battery', unit:"" | |
} | |
// HEM Version Configuration only needs to be done here - comments to choose what gets displayed | |
main (["energyDisp","energyTwo", | |
// "ampsDisp","voltsDisp", // Comment out this one for HEMv1 | |
"powerDisp" | |
]) | |
details([ | |
"energyOne","energyDisp","energyTwo", | |
"powerOne","powerDisp","powerTwo", | |
// "ampsOne","ampsDisp","ampsTwo", // Comment out these two lines for HEMv1 | |
// "voltsOne","voltsDisp","voltsTwo", // Comment out these two lines for HEMv1 | |
"reset","refresh","toggle", | |
// "battery", // Include this for HEMv1 | |
"configure" | |
]) | |
} | |
preferences { | |
input "kWhCost", "string", title: "\$/kWh (0.16)", description: "0.16", defaultValue: "0.16" as String | |
input "kWhDelay", "number", title: "kWh report seconds (60)", description: "120", defaultValue: "120" | |
input "detailDelay", "number", title: "Detail report seconds (30)", description: "30", defaultValue: "30" | |
} | |
} | |
def installed() { | |
state.display = 1 | |
reset() // The order here is important | |
configure() // Since reports can start coming in even before we finish configure() | |
refresh() | |
} | |
def updated() { | |
configure() | |
resetDisplay() | |
refresh() | |
} | |
def parse(String description) { | |
// log.debug "Parse received ${description}" | |
def result = null | |
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3]) | |
if (cmd) { | |
result = createEvent(zwaveEvent(cmd)) | |
} | |
if (result) { | |
log.debug "Parse returned ${result?.descriptionText}" | |
return result | |
} else { | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) { | |
def dispValue | |
def newValue | |
def formattedValue | |
def dateString = new Date().format("M/d/YY", location.timeZone) | |
def timeString = new Date().format("h:mm a", location.timeZone) | |
timeString = dateString+"\n"+timeString | |
if (cmd.meterType == 33) { | |
if (cmd.scale == 0) { | |
newValue = Math.round(cmd.scaledMeterValue * 100) / 100 | |
if (newValue != state.energyValue) { | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkWh" | |
sendEvent(name: "energyDisp", value: dispValue as String, unit: "", descriptionText: "Display Energy: ${newValue} kVh") | |
state.energyValue = newValue | |
BigDecimal costDecimal = newValue * ( kWhCost as BigDecimal ) | |
def costDisplay = String.format("%5.2f",costDecimal) | |
state.costDisp = "Cost\n\$"+costDisplay | |
if (state.display == 1) { sendEvent(name: "energyTwo", value: state.costDisp, unit: "", descriptionText: "Display Cost: \$${costDisp}") } | |
[name: "energy", value: newValue, unit: "kWh", descriptionText: "Total Energy: ${formattedValue} kWh"] | |
} | |
} | |
else if (cmd.scale == 1) { | |
newValue = Math.round( cmd.scaledMeterValue * 100) / 100 | |
if (newValue != state.energyValue) { | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkVAh" | |
sendEvent(name: "energyDisp", value: dispValue as String, unit: "", descriptionText: "Display Energy: ${formattedValue} kVAh") | |
state.energyValue = newValue | |
[name: "energy", value: newValue, unit: "kVAh", descriptionText: "Total Energy: ${formattedValue} kVAh"] | |
} | |
} | |
else if (cmd.scale==2) { | |
newValue = Math.round(cmd.scaledMeterValue) // really not worth the hassle to show decimals for Watts | |
if (newValue != state.powerValue) { | |
dispValue = newValue+"\nWatts" | |
sendEvent(name: "powerDisp", value: dispValue as String, unit: "", descriptionText: "Display Power: ${newValue} Watts") | |
if (newValue < state.powerLow) { | |
dispValue = newValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "powerOne", value: dispValue as String, unit: "", descriptionText: "Lowest Power: ${newValue} Watts") } | |
state.powerLow = newValue | |
state.powerLowDisp = dispValue | |
} | |
if (newValue > state.powerHigh) { | |
dispValue = newValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "powerTwo", value: dispValue as String, unit: "", descriptionText: "Highest Power: ${newValue} Watts") } | |
state.powerHigh = newValue | |
state.powerHighDisp = dispValue | |
} | |
state.powerValue = newValue | |
[name: "power", value: newValue, unit: "W", descriptionText: "Total Power: ${newValue} Watts"] | |
} | |
} | |
} | |
else if (cmd.meterType == 161) { | |
if (cmd.scale == 0) { | |
newValue = Math.round( cmd.scaledMeterValue * 100) / 100 | |
if (newValue != state.voltsValue) { | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nVolts" | |
sendEvent(name: "voltsDisp", value: dispValue as String, unit: "", descriptionText: "Display Voltage: ${formattedValue} Volts") | |
if (newValue < state.voltsLow) { | |
dispValue = formattedValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "voltsOne", value: dispValue as String, unit: "", descriptionText: "Lowest Voltage: ${formattedValue} Volts") } | |
state.voltsLow = newValue | |
state.voltsLowDisp = dispValue | |
} | |
if (newValue > state.voltsHigh) { | |
dispValue = formattedValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "voltsTwo", value: dispValue as String, unit: "", descriptionText: "Highest Voltage: ${formattedValue} Volts") } | |
state.voltsHigh = newValue | |
state.voltsHighDisp = dispValue | |
} | |
state.voltsValue = newValue | |
sendEvent( name: "voltage", value: newValue, unit: "V", descriptionText: "Total Voltage: ${formattedValue} Volts") | |
[name: "volts", value: newValue, unit: "V", descriptionText: "Total Volts: ${formattedValue} Volts"] | |
} | |
} | |
else if (cmd.scale==1) { | |
newValue = Math.round( cmd.scaledMeterValue * 100) / 100 | |
if (newValue != state.ampsValue) { | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nAmps" | |
sendEvent(name: "ampsDisp", value: dispValue as String, unit: "", descriptionText: "Display Current: ${formattedValue} Amps") | |
if (newValue < state.ampsLow) { | |
dispValue = formattedValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "ampsOne", value: dispValue as String, unit: "", descriptionText: "Lowest Current: ${formattedValue} Amps") } | |
state.ampsLow = newValue | |
state.ampsLowDisp = dispValue | |
} | |
if (newValue > state.ampsHigh) { | |
dispValue = formattedValue+"\n"+timeString | |
if (state.display == 1) { sendEvent(name: "ampsTwo", value: dispValue as String, unit: "", descriptionText: "Highest Current: ${formattedValue} Amps") } | |
state.ampsHigh = newValue | |
state.ampsHighDisp = dispValue | |
} | |
state.ampsValue = newValue | |
[name: "amps", value: newValue, unit: "A", descriptionText: "Total Current: ${formattedValue} Amps"] | |
} | |
} | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { | |
def dispValue | |
def newValue | |
def formattedValue | |
if (cmd.commandClass == 50) { | |
def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1]) // can specify command class versions here like in zwave.parse | |
if (encapsulatedCommand) { | |
if (cmd.sourceEndPoint == 1) { | |
if (encapsulatedCommand.scale == 2 ) { | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue) | |
formattedValue = newValue as String | |
dispValue = "${formattedValue}\nWatts" | |
if (dispValue != state.powerL1Disp) { | |
state.powerL1Disp = dispValue | |
if (state.display == 2) { | |
[name: "powerOne", value: dispValue, unit: "", descriptionText: "L1 Power: ${formattedValue} Watts"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 0 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkWh" | |
if (dispValue != state.energyL1Disp) { | |
state.energyL1Disp = dispValue | |
if (state.display == 2) { | |
[name: "energyOne", value: dispValue, unit: "", descriptionText: "L1 Energy: ${formattedValue} kWh"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 1 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkVAh" | |
if (dispValue != state.energyL1Disp) { | |
state.energyL1Disp = dispValue | |
if (state.display == 2) { | |
[name: "energyOne", value: dispValue, unit: "", descriptionText: "L1 Energy: ${formattedValue} kVAh"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 5 ) { | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nAmps" | |
if (dispValue != state.ampsL1Disp) { | |
state.ampsL1Disp = dispValue | |
if (state.display == 2) { | |
[name: "ampsOne", value: dispValue, unit: "", descriptionText: "L1 Current: ${formattedValue} Amps"] | |
} | |
else { | |
} | |
} | |
} | |
/* Ignore voltage updates, because they always match the current Total Voltage | |
else if (encapsulatedCommand.scale == 4 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nVolts" | |
if (dispValue != statevoltsL1Disp) { | |
state.voltsL1Disp = dispValue | |
if (state.display == 2) { | |
[name: "voltsOne", value:dispValue, unit: "", descriptionText: "L1 Voltage: ${formattedValue} Volts"] | |
} | |
else { | |
} | |
} | |
} | |
*/ | |
} | |
else if (cmd.sourceEndPoint == 2) { | |
if (encapsulatedCommand.scale == 2 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue) | |
formattedValue = newValue as String | |
dispValue = "${formattedValue}\nWatts" | |
if (dispValue != state.powerL2Disp) { | |
state.powerL2Disp = dispValue | |
if (state.display == 2) { | |
[name: "powerTwo", value: dispValue, unit: "", descriptionText: "L2 Power: ${formattedValue} Watts"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 0 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkWh" | |
if (dispValue != state.energyL2Disp) { | |
state.energyL2Disp = dispValue | |
if (state.display == 2) { | |
[name: "energyTwo", value: dispValue, unit: "", descriptionText: "L2 Energy: ${formattedValue} kWh"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 1 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nkVAh" | |
if (dispValue != state.energyL2Disp) { | |
state.energyL2Disp = dispValue | |
if (state.display == 2) { | |
[name: "energyTwo", value: dispValue, unit: "", descriptionText: "L2 Energy: ${formattedValue} kVAh"] | |
} | |
else { | |
} | |
} | |
} | |
else if (encapsulatedCommand.scale == 5 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nAmps" | |
if (dispValue != state.ampsL2Disp) { | |
state.ampsL2Disp = dispValue | |
if (state.display == 2) { | |
[name: "ampsTwo", value: dispValue, unit: "", descriptionText: "L2 Current: ${formattedValue} Amps"] | |
} | |
else { | |
} | |
} | |
} | |
/* Ignore voltage updates, because they always match the current Total Voltage | |
else if (encapsulatedCommand.scale == 4 ){ | |
newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100 | |
formattedValue = String.format("%5.2f", newValue) | |
dispValue = "${formattedValue}\nVolts" | |
if (dispValue != statevoltsL2Disp) { | |
state.voltsL2Disp = dispValue | |
if (state.display == 2) { | |
[name: "voltsTwo", value:dispValue, unit: "", descriptionText: "L2 Voltage: ${formattedValue} Volts"] | |
} | |
else { | |
} | |
} | |
} | |
*/ | |
} | |
} | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { | |
def map = [:] | |
map.name = "battery" | |
map.unit = "%" | |
if (cmd.batteryLevel == 0xFF) { | |
map.value = 1 | |
map.descriptionText = "${device.displayName} battery is low" | |
map.isStateChange = true | |
} | |
else { | |
map.value = cmd.batteryLevel | |
} | |
log.debug map | |
return map | |
} | |
def zwaveEvent(physicalgraph.zwave.Command cmd) { | |
// Handles all Z-Wave commands we aren't interested in | |
log.debug "Unhandled event ${cmd}" | |
[:] | |
} | |
def refresh() { // Request HEMv2 to send us the latest values for the 4 we are tracking | |
log.debug "refresh()" | |
delayBetween([ | |
zwave.meterV2.meterGet(scale: 0).format(), // Change 0 to 1 if international version | |
zwave.meterV2.meterGet(scale: 2).format(), | |
zwave.meterV2.meterGet(scale: 4).format(), | |
zwave.meterV2.meterGet(scale: 5).format() | |
]) | |
resetDisplay() | |
} | |
def poll() { | |
log.debug "poll()" | |
refresh() | |
} | |
def toggleDisplay() { | |
log.debug "toggleDisplay()" | |
if (state.display == 1) { | |
state.display = 2 | |
} | |
else { | |
state.display = 1 | |
} | |
resetDisplay() | |
} | |
def resetDisplay() { | |
log.debug "resetDisplay() - energyL1Disp: ${state.energyL1Disp}" | |
if ( state.display == 1 ) { | |
sendEvent(name: "voltsOne", value: state.voltsLowDisp, unit: "") | |
sendEvent(name: "ampsOne", value: state.ampsLowDisp, unit: "") | |
sendEvent(name: "powerOne", value: state.powerLowDisp, unit: "") | |
sendEvent(name: "energyOne", value: state.lastResetTime, unit: "") | |
sendEvent(name: "voltsTwo", value: state.voltsHighDisp, unit: "") | |
sendEvent(name: "ampsTwo", value: state.ampsHighDisp, unit: "") | |
sendEvent(name: "powerTwo", value: state.powerHighDisp, unit: "") | |
sendEvent(name: "energyTwo", value: state.costDisp, unit: "") | |
} | |
else { | |
sendEvent(name: "voltsOne", value: "L1", unit: "") | |
sendEvent(name: "ampsOne", value: state.ampsL1Disp, unit: "") | |
sendEvent(name: "powerOne", value: state.powerL1Disp, unit: "") | |
sendEvent(name: "energyOne", value: state.energyL1Disp, unit: "") | |
sendEvent(name: "voltsTwo", value: "L2", unit: "") | |
sendEvent(name: "ampsTwo", value: state.ampsL2Disp, unit: "") | |
sendEvent(name: "powerTwo", value: state.powerL2Disp, unit: "") | |
sendEvent(name: "energyTwo", value: state.energyL2Disp, unit: "") | |
} | |
} | |
def reset() { | |
log.debug "reset()" | |
state.energyValue = -1 | |
state.powerValue = -1 | |
state.ampsValue = -1 | |
state.voltsValue = -1 | |
state.powerHigh = 0 | |
state.powerHighDisp = "" | |
state.powerLow = 99999 | |
state.powerLowDisp = "" | |
state.ampsHigh = 0 | |
state.ampsHighDisp = "" | |
state.ampsLow = 999 | |
state.ampsLowDisp = "" | |
state.voltsHigh = 0 | |
state.voltsHighDisp = "" | |
state.voltsLow = 999 | |
state.voltsLowDisp = "" | |
state.energyL1Disp = "" | |
state.energyL2Disp = "" | |
state.powerL1Disp = "" | |
state.powerL2Disp = "" | |
state.ampsL1Disp = "" | |
state.ampsL2Disp = "" | |
state.voltsL1Disp = "" | |
state.voltsL2Disp = "" | |
if (!state.display) { state.display = 1 } // Sometimes it appears that installed() isn't called | |
def dateString = new Date().format("M/d/YY", location.timeZone) | |
def timeString = new Date().format("h:mm a", location.timeZone) | |
state.lastResetTime = "Since\n"+dateString+"\n"+timeString | |
state.costDisp = "Cost\n--" | |
resetDisplay() | |
sendEvent(name: "energyDisp", value: "", unit: "") | |
sendEvent(name: "powerDisp", value: "", unit: "") | |
sendEvent(name: "ampsDisp", value: "", unit: "") | |
sendEvent(name: "voltsDisp", value: "", unit: "") | |
// No V1 available | |
def cmd = delayBetween( [ | |
zwave.meterV2.meterReset().format(), // Reset all values | |
zwave.meterV2.meterGet(scale: 0).format(), // Request the values we are interested in (0-->1 for kVAh) | |
zwave.meterV2.meterGet(scale: 2).format(), | |
zwave.meterV2.meterGet(scale: 4).format(), | |
zwave.meterV2.meterGet(scale: 5).format() | |
]) | |
cmd | |
} | |
def configure() { | |
log.debug "configure()" | |
def kDelay = 120 | |
def dDelay = 30 | |
def cmd = delayBetween([ | |
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: 0).format(), // Disable (=0) selective reporting | |
zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: 50).format(), // Don't send whole HEM unless watts have changed by 30 | |
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 2, scaledConfigurationValue: 50).format(), // Don't send L1 Data unless watts have changed by 15 | |
zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 50).format(), // Don't send L2 Data unless watts have changed by 15 | |
zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 5).format(), // Or by 5% (whole HEM) | |
zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, scaledConfigurationValue: 5).format(), // Or by 5% (L1) | |
zwave.configurationV1.configurationSet(parameterNumber: 10, size: 1, scaledConfigurationValue: 5).format(), // Or by 5% (L2) | |
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 6145).format(), // Whole HEM and L1/L2 power in kWh | |
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: kDelay).format(), // Default every 120 Seconds | |
zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1573646).format(), // L1/L2 for Amps & Watts, Whole HEM for Amps, Watts, & Volts | |
zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: dDelay).format(), // Defaul every 30 seconds | |
zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0).format(), // Disable Report 3 | |
zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: 0).format() // eDesable Report 3 | |
]) | |
log.debug cmd | |
cmd | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment