[Depreciated] Aeotec HEM (Gen 5) ZW095-A Driver

I got this working with no issues for reading the clamps and reporting the values, however I can not figure out how to get it to refresh the values in the dashboard or viewing them within the device. If I hit the refresh button the values are updated. Anyone else have this issue and resolve it? Also should I stay on FW 1.31 or upgrade to 1.35?

No! 1.35 is a mess be glad you have 1.31 (I wish I did, wanna trade?)

What values are you looking for and how to you have your preferences setup? Please post screen shots and details on prefs selection and log files, so we can help.

Here is a screen shot

Enable "descriptiveText logging for meter reports"

That should at least create a log for the events. If it does not fix the refresh, post the log please.

1 Like

I set Reporting Group 1 Frequency
Interval (secs) for reporting group 1 reports
to 20.
Works great.

1 Like

I fixed it by creating a device refresh at X min. Also applied the same to my garage lock which sometimes would not report back that it was open after double turning to unlock until locked again by someone.

Might be a hack at it but it's working.

As long as your X is reasonable, and you don't have to do it with lots of devices, it should be ok.

I went with 15 minutes. The Information is not that important for me to have a constant refresh.

Thanks!

1 Like

Hey @codahq: Where is your driver on github? I can't find it there.

2 Likes

Since the github link is gone nowI am posting my copy of @codahq's code here. @markebby

I have also reached out to @codahq to make sure he is doing well, and see what is going on with the github. We hope it's all good with him enjoying vacation. Will update when possible.

See below.

It is with much regret that I learned @codahq is no longer supporting Hubitat. To my knowledge that includes this driver.

The code posted here is from the last time he, and I worked on it. This is sad since we were also working with Aeotec to fix a known bug with firmware v1.35, and I was enjoying being able to contribute. This code was/is all Ben's fine work with not much help but testing, and complaining from me.

Unfortunately I'm not a coder and cannot maintain this driver or help with any problems that may come from using it. If anyone who is capable would like to carry on Ben's fine work (with proper credit due), I would be happy to work with them in any testing capacity needed.


/*
 *  Aeon HEM Gen5 Aeotec Model ZW095-A
 *  https://products.z-wavealliance.org/products/1289
 *
 *  Copyright 2020 Ben Rimmasch
 *
 *
 *  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.
 *  
 *  Change Log:
 *  2020-02-29: Initial
 *  2020-04-12: Added energy duration to match the OOB drivers
 *  2020-04-22: Added preference and handled CRC16 encapsulation
 *              Changed precision from 2 decimal places to read precision from the command for meter reports
 *              Added preference and added descriptive logging to meter reports
 *
 */


import groovy.transform.Field

// metadata
metadata {
  definition(name: "Aeon HEM Gen5", namespace: "hubitat-codahq", author: "Ben Rimmasch",
    importUrl: "https://raw.githubusercontent.com/codahq/hubitat_codahq/master/devicestypes/aeotec-hem-gen5.groovy") {
    capability "Energy Meter"
    capability "Power Meter"
    capability "Configuration"
    capability "Sensor"
    capability "Refresh"

    command "reset"

    attribute "current", "number"
    attribute "voltage", "number"
    attribute "cost", "number"
    attribute "energyDuration", "text"

    //comment the ones out that you don't want to persist
    attribute "power1", "number"
    attribute "power2", "number"
    attribute "current1", "number"
    attribute "current2", "number"
    attribute "voltage1", "number"
    attribute "voltage2", "number"
    attribute "cost1", "number"
    attribute "cost2", "number"

    fingerprint deviceId: "0x005F", inClusters: "0x5E,0x86,0x72,0x32,0x56,0x60,0x70,0x59,0x85,0x7A,0x73,0x98"
  }

  preferences {
    section("Aeon HEM") {
      input "kWhCost", "string", title: "Cost in \$ / kWh", defaultValue: "0.144508" as String,
        required: false, displayDuringSetup: true
      input name: "enableCrc16Encapsulation", type: "bool", title: "${paramDescriptions[13]}", defaultValue: false
      input name: "enableSelectiveReports", type: "bool", title: "${paramDescriptions[3]}", defaultValue: false
      input name: "wholeHemWattTrigger", type: "number", range: 0..60000, title: "${paramDescriptions[4]}", defaultValue: 10
      input name: "clamp1WattTrigger", type: "number", range: 0..60000, title: "${paramDescriptions[5]}", defaultValue: 50
      input name: "clamp2WattTrigger", type: "number", range: 0..60000, title: "${paramDescriptions[6]}", defaultValue: 50
      input name: "wholeHemPercTrigger", type: "number", range: 0..100, title: "${paramDescriptions[8]}", defaultValue: 5
      input name: "clamp1PercTrigger", type: "number", range: 0..100, title: "${paramDescriptions[9]}", defaultValue: 10
      input name: "clamp2PercTrigger", type: "number", range: 0..100, title: "${paramDescriptions[10]}", defaultValue: 10
      input name: "group1Reports", type: "enum", title: "${paramDescriptions[101]}", multiple: true, required: true, options: reportDescriptions, defaultValue: [1, 2, 4, 8]
      input name: "group2Reports", type: "enum", title: "${paramDescriptions[102]}", multiple: true, required: true, options: reportDescriptions, defaultValue: [256, 512, 2048, 4096]
      input name: "group3Reports", type: "enum", title: "${paramDescriptions[103]}", multiple: true, required: true, options: reportDescriptions, defaultValue: [65536, 131072, 524288, 1048576]
      input "group1Interval", "number", title: "${paramDescriptions[111]}", description: "Interval (secs) for reporting group 1 reports", defaultValue: 60,
        range: "1..4294967295", required: false, displayDuringSetup: true
      input "group2Interval", "number", title: "${paramDescriptions[112]}", description: "Interval (secs) for reporting group 2 reports", defaultValue: 60,
        range: "1..4294967295", required: false, displayDuringSetup: true
      input "group3Interval", "number", title: "${paramDescriptions[113]}", description: "Interval (secs) for reporting group 3 reports", defaultValue: 60,
        range: "1..4294967295", required: false, displayDuringSetup: true

      input name: "refreshReturnsConfig", type: "bool", title: "Refresh command requests configuration parameters", defaultValue: false
      input name: "descriptiveLoggingForMeterReports", type: "bool", title: "Enable descriptionText logging for meter reports", defaultValue: false
      input name: "descriptionTextEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: false
      input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
      input name: "traceLogEnable", type: "bool", title: "Enable trace logging", defaultValue: false
    }
  }
}

def updated() {
  initialize()
  configure()
}

def initialize() {
  if (!state.energyTime) {
    state.energyTime = new Date().getTime()
  }
  if (!state."Explanation About Selective Reporting") {
    state."Explanation About Selective Reporting" = "Selective reporting is a way to cut down on Z-Wave traffic by " +
      "excluding reports from sending that don't meet a certain threshold.  Selective reporting is only available for " +
      "periodic wattage reports for the HEM or the individual clamps.  What this means is that if you decide to " +
      "set report group 1 to send you HEM watts, HEM kWh, HEM volts and HEM amps every 5 seconds and if you set the " +
      "threshold of the HEM watts to 50 watts and 10% the HEM watts report will only send on the 5 second tick where " +
      "it actually exceeded the threshold.  However, the other three reports (HEM kWh, HEM volts and HEM amps) will " +
      "still send every 5 seconds."
  }
  if (!state."Explanation About Reporting Groups") {
    state."Explanation About Reporting Groups" = "There are three reporting groups.  You can choose as many data points " +
      "for each group as you want. All of the data points chosen will be reported at the interval specified for each " +
      "group. Certain combinations do not work for some reason. There is a chart in the PDF documentation that (I think) " +
      "explains the combinations that do not work but I don't understand it.\n"
  }
  if (!state."Explanation About Preferences") {
    state."Explanation About Preferences" = "Unfortunately, since Hubitat doesn't support multi-select preferences in " +
      "drivers you will have to select your reports EVERY time you save preferences.  Please bring it up with the HE " +
      "platform developers to prioritize this feature.\n" +
      "When you first install the driver each of the reporting group multi-select preferences will be single select.  " +
      "Choose any option and save and they will become multi-select on reload."
  }
}

def refresh() {
  //Get HEM totals
  def request = [
    zwave.meterV3.meterGet(scale: 0),  //kWh
    zwave.meterV3.meterGet(scale: 2),  //Wattage
    zwave.meterV3.meterGet(scale: 4),  //Volts
    zwave.meterV3.meterGet(scale: 5),  //Amps
  ]

  //add parameter query
  if (refreshReturnsConfig) {
    request += configurationGets
  }

  commands(request)
}

def reset() {
  def curr = new Date()
  def stamp = curr.format("YYYY-MM-dd", location.timeZone) + " @ " + curr.format("h:mm a", location.timeZone)
  state."Last Reset Time" = stamp
  state.energyTime = curr.getTime()
  def request = [
    zwave.meterV3.meterReset(),
    zwave.meterV3.meterGet(scale: 0),  //kWh
    zwave.meterV3.meterGet(scale: 2),  //Wattage
    zwave.meterV3.meterGet(scale: 4),  //Volts
    zwave.meterV3.meterGet(scale: 5),  //Amps
  ]
  commands(request)
}

def configure() {
  logInfo "Configuring device ${device.label}"

  def request = [
    //Reset meter parameters to defaults
    //zwave.configurationV1.configurationSet(parameterNumber: 255, size: 1, scaledConfigurationValue: 1),
    //Reset parameters 101-103 to defaults
    //zwave.configurationV1.configurationSet(parameterNumber: 100, size: 1, scaledConfigurationValue: 1),
    //Reset parameters 111-113 to defaults
    //zwave.configurationV1.configurationSet(parameterNumber: 110, size: 1, scaledConfigurationValue: 1),

    // Associate so that reports work
    zwave.associationV2.associationRemove(groupingIdentifier: 1, nodeId: []),
    zwave.associationV2.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId),

    //Selective reports. Set to 0 to reduce network traffic. If disabled reports by from parameters 4-11 are disabled
    zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: enableSelectiveReports ? 1 : 0),

    //Trigger HEM watts with change by this value (default 10)
    zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: wholeHemWattTrigger),
    //Trigger clamp 1 watts with change by this value (default 50)
    zwave.configurationV1.configurationSet(parameterNumber: 5, size: 2, scaledConfigurationValue: clamp1WattTrigger),
    //Trigger clamp 2 watts with change by this value (default 50)
    zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: clamp2WattTrigger),
    //Trigger HEM watts with change by this percent (default 5%)
    zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: wholeHemPercTrigger),
    //Trigger clamp 1 watts with change by this percent (default 10%)
    zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, scaledConfigurationValue: clamp1PercTrigger),
    //Trigger clamp 2 watts with change by this percent (default 10%)
    zwave.configurationV1.configurationSet(parameterNumber: 10, size: 1, scaledConfigurationValue: clamp2PercTrigger),

    //Set Crc16 Encapsulation
    zwave.configurationV1.configurationSet(parameterNumber: 13, size: 1, scaledConfigurationValue: enableCrc16Encapsulation ? 1 : 0),

    // Which reports need to send in Report group 1.
    zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: group1Reports.collect {
      it.toInteger()
    }.sum()),
    // Which reports need to send in Report group 2.
    zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: group2Reports.collect {
      it.toInteger()
    }.sum()),
    // Which reports need to send in Report group 3.
    zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: group3Reports.collect {
      it.toInteger()
    }.sum()),
    // Interval to send Report group 1.
    zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: group1Interval.toInteger()),
    // Interval to send Report group 2.
    zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: group2Interval.toInteger()),
    // Interval to send Report group 3.
    zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: group3Interval.toInteger())

  ]

  // Report which configuration commands were sent to and received by the HEM Gen5 successfully.
  request += configurationGets
  commands(request)
}

def parse(String description) {
  logDebug "parse(description)"
  logTrace "description: $description"

  def result
  def cmd = zwave.parse(description, commandClasses)
  if (cmd) {
    result = zwaveEvent(cmd)
  }
  else {
    log.warn "Command not parsed! ${description}"
  }
  return result
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd)"
  logTrace "cmd: $cmd"

  state.secure = 1

  def encapCmd = cmd.encapsulatedCommand(commandClasses)
  logTrace "encap cmd: ${encapCmd}"
  if (encapCmd) {
    zwaveEvent(encapCmd)
  }
  else {
    log.warn "Unable to extract security message encapsulated cmd from $cmd"
    createEvent(descriptionText: cmd.toString())
  }
}

//def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
//  logDebug "zwaveEvent(hubitat.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd)"
//  logTrace "cmd: $cmd"
//}

def zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd)"
  logTrace "cmd: $cmd"
  def value = cmd.scaledConfigurationValue?.toInteger()
  logTrace "value: $value"

  switch (cmd.parameterNumber) {
    case 2:
      sendConfigEvent(cmd.parameterNumber, detectionModeDescriptions[value])
      break
    case 3:
    case 13:
    case 252:
      sendConfigEvent(cmd.parameterNumber, value ? "enabled" : "disabled")
      break
    case 4:
    case 5:
    case 6:
      sendConfigEvent(cmd.parameterNumber, "Trigger change by ${value} watts")
      break
    case 8:
    case 9:
    case 10:
      sendConfigEvent(cmd.parameterNumber, "Trigger change by ${value}%")
      break
    case 101:
    case 102:
    case 103:
      sendConfigEvent(cmd.parameterNumber, getReportsFromInt(value))
      break
    case 111:
    case 112:
    case 113:
      sendConfigEvent(cmd.parameterNumber, "Trigger every ${value} seconds")
      break
    case 200:
    case 254:
      sendConfigEvent(cmd.parameterNumber, "${value}")
      break
    default:
      log.warn "Unhandled parameter ${cmd.parameterNumber} with value ${value}"
      break
  }
}

private sendConfigEvent(short param, String desc) {
  logInfo "Parameter ${param} \"${paramDescriptions[param as int]}\" has value:: ${desc}"
  sendEvent([name: paramDescriptions[param as int], value: desc])
}

def zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd)"
  logTrace "cmd: $cmd"

  logInfo "Assocation Group ${cmd.groupingIdentifier} has nodes :: ${cmd.nodeId}"
  sendEvent([name: "Group 1 Assocations", value: cmd.nodeId])
}

def zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd)"
  logTrace "cmd: $cmd"

  def firmware = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
  updateDataValue("firmware", firmware)
  logDebug "${device.displayName} is running firmware version: $firmware, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd)"
  logTrace "cmd: $cmd"

  logDebug "manufacturerId:   ${cmd.manufacturerId}"
  logDebug "manufacturerName: ${cmd.manufacturerName}"
  logDebug "productId:        ${cmd.productId}"
  logDebug "productTypeId:    ${cmd.productTypeId}"
  def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
  updateDataValue("MSR", msr)
  updateDataValue("manufacturer", cmd.manufacturerId.toString())
  updateDataValue("manufacturerName", cmd.manufacturerName)
  sendEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
}

def zwaveEvent(hubitat.zwave.commands.meterv3.MeterReport cmd, int clamp = 0) {
  logDebug "zwaveEvent(hubitat.zwave.commands.meterv3.MeterReport cmd)"
  logTrace "cmd: $cmd"

  def sourceName = clamp == 0 ? "HEM" : "Clamp ${clamp}"
  def source = clamp == 0 ? "" : "${clamp}"

  def value = String.format("%5.${cmd.precision}f", cmd.scaledMeterValue)
  switch (cmd.scale) {
    case 1: //kVAh
    case 3: //pulses
    case 4: //volts
    case 6: //power factor
    case 7: //scale2 values
      break
    case 0: //kWh
      def cost = String.format("%5.2f", cmd.scaledMeterValue * (kWhCost as BigDecimal))
      def msg = "${sourceName} cost is \$${cost}"
      logInfo msg
      sendEvent(name: "cost${source}", value: cost, unit: "", descriptionText: msg)
      def dur_ms = new Date().getTime() - state.energyTime
      def days = (dur_ms / (1000.0 * 60.0 * 60.0 * 24.0))
      sendEvent(name: "energyDuration", value: "${formatDays(days)} Days")
      break
    case 2: //WATTS
      if (cmd.scaledMeterValue > MAX_WATTS) {
        log.warn "Watts ${cmd.scaledMeterValue} too high for ${sourceName}"
        return
      }
      break
    case 5: //Amps
      if (cmd.scaledMeterValue > MAX_AMPS) {
        log.warn "Amps ${cmd.scaledMeterValue} too high for ${sourceName}"
        return
      }
      break
    default:
      log.warn "scale ${cmd.scale} not handled!"
      break
  }

  def msg = "${sourceName} ${unitNames[cmd.scale]} is ${value} ${unitAbbrs[cmd.scale]}"
  if (descriptiveLoggingForMeterReports) {
    logInfo msg
  }
  sendEvent(name: "${unitNames[cmd.scale]}${source}", value: cmd.scaledMeterValue, unit: "", descriptionText: msg)
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd)"
  logTrace "cmd: $cmd"

  if (cmd.commandClass == 50) {
    //extract encapsulated command
    def encapCmd = cmd.encapsulatedCommand([0x30: 1, 0x31: 1])
    logTrace "Command from clamp ${cmd.sourceEndPoint}: ${encapCmd}"
    if (encapCmd) {
      zwaveEvent(encapCmd, cmd.sourceEndPoint)
    }
    else {
      log.warn "Couldn't extract encapsulated command!"
    }
  }
  else {
    log.warn "cmd.commandClass: ${cmd.commandClass}"
  }
}

def zwaveEvent(hubitat.zwave.commands.crc16encapv1.Crc16Encap cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.crc16encapv1.Crc16Encap cmd)"
  logTrace "cmd: $cmd"

  def version = commandClasses[cmd.commandClass as int] ?: 1

  def encapsulatedCommand = zwave.getCommand(cmd.commandClass, cmd.command, cmd.data, version)
  if (!encapsulatedCommand) {
    log.warn "zwaveEvent(): Could not extract command from ${cmd}"
  }
  else {
    logDebug("zwaveEvent(): Extracted command ${encapsulatedCommand}")
    return zwaveEvent(encapsulatedCommand)
  }
}

def zwaveEvent(hubitat.zwave.Command cmd) {
  log.warn "Unhandled: $cmd"
  createEvent(descriptionText: cmd.toString(), isStateChange: false)
}

def getConfigurationGets() {
  return [
    zwave.configurationV1.configurationGet(parameterNumber: 2),   //Energy detection mode configuration for parameters 101~103
    zwave.configurationV1.configurationGet(parameterNumber: 3),   //enable/disable parameter selective reporting parameters 4~10
    zwave.configurationV1.configurationGet(parameterNumber: 4),   //Induce an automatic report of HEM by watts
    zwave.configurationV1.configurationGet(parameterNumber: 5),   //Induce an automatic report of Channel 1 by watts
    zwave.configurationV1.configurationGet(parameterNumber: 6),   //Induce an automatic report of Channel 2 by watts
    zwave.configurationV1.configurationGet(parameterNumber: 8),   //Induce an automatic report of HEM by percent
    zwave.configurationV1.configurationGet(parameterNumber: 9),   //Induce an automatic report of Channel 1 by percent
    zwave.configurationV1.configurationGet(parameterNumber: 10),  //Induce an automatic report of Channel 2 by percent
    zwave.configurationV1.configurationGet(parameterNumber: 13),  //Enable/disable CRC-16 Encapsulation
    zwave.configurationV1.configurationGet(parameterNumber: 101), //Report group 1 reports
    zwave.configurationV1.configurationGet(parameterNumber: 102), //Report group 2 reports
    zwave.configurationV1.configurationGet(parameterNumber: 103), //Report group 3 reports
    zwave.configurationV1.configurationGet(parameterNumber: 111), //Report group 1 frequency
    zwave.configurationV1.configurationGet(parameterNumber: 112), //Report group 2 frequency
    zwave.configurationV1.configurationGet(parameterNumber: 113), //Report group 3 frequency
    zwave.configurationV1.configurationGet(parameterNumber: 200), //Is Aeon or Third-party
    zwave.configurationV1.configurationGet(parameterNumber: 252), //Configuration locked?
    zwave.configurationV1.configurationGet(parameterNumber: 254), //Device Tag
    zwave.associationV2.associationGet(groupingIdentifier: 1),
    zwave.versionV1.versionGet(),
    zwave.manufacturerSpecificV1.manufacturerSpecificGet()
  ]
}

private getCommandClasses() {
  [
    0x32: 3,
    0x56: 1,
    0x59: 1,
    0x5A: 1,
    //0x5E: 2,
    0x60: 3,
    0x70: 4, //even though firmware v1.31 on hubitat reports 1
    0x72: 2,
    0x73: 1,
    //0x7A: 2,
    0x82: 1,
    0x85: 2,
    0x86: 1, //even though firmware v1.31 on hubitat reports v2
    0x8E: 2,
    //0x98: 1,
    0xEF: 1
  ]
}

@Field def detectionModeDescriptions = [
  0: "(Default) Report wattage and the absolute KWH value",
  1: "Report positive/negative wattage and the algebraic sum KWH value",
  2: "Report positive/negative wattage and the positive KWH value (consuming electricity)",
  3: "Report positive/negative wattage and the negative KWH value (generating electricity)"
]

@Field def paramDescriptions = [
  2: "Group Reporting Energy Detection Mode ", //parameters 101~103
  3: "Selective Reporting", //parameters 4~11
  4: "Whole HEM Watts Change",
  5: "Clamp 1 Watts Change",
  6: "Clamp 2 Watts Change",
  7: "Clamp 3 Watts Change",
  8: "Whole HEM Watts Percentage Change",
  9: "Clamp 1 Watts Percentage Change",
  10: "Clamp 2 Watts Percentage Change",
  11: "Clamp 3 Watts Percentage Change",
  13: "CRC-16 Encapsulation",
  101: "Reporting Group 1",
  102: "Reporting Group 2",
  103: "Reporting Group 3",
  111: "Reporting Group 1 Frequency",
  112: "Reporting Group 2 Frequency",
  113: "Reporting Group 3 Frequency",
  200: "Partner ID",
  252: "Configuration Locked?",
  254: "Device Tag"
]

@Field def reportDescriptions = [
  1: "KWH HEM",
  2: "Watts HEM",
  4: "Voltage HEM",
  8: "Current HEM",
  16: "kVarh HEM",
  32: "kVar HEM",
  256: "Watts Clamp 1",
  512: "Watts Clamp 2",
  //1024: "Watts Clamp 3",
  2048: "KWH Clamp 1",
  4096: "KWH Clamp 2",
  //8192: "KWH Clamp 3",
  65536: "Voltage Clamp 1",
  131072: "Voltage Clamp 2",
  //262144: "Voltage Clamp 3",
  524288: "Current (Amperes) Clamp 1",
  1048576: "Current (Amperes) Clamp 2",
  //2097152: "Current (Amperes) Clamp 3"
]

@Field def unitNames = ["energy", "energy", "power", "count", "voltage", "current", "powerFactor", "unknown"]
@Field def unitAbbrs = ["kWh", "kVAh", "W", "pulses", "V", "A", "Power Factor", ""]
@Field def MAX_AMPS = 220
@Field def MAX_WATTS = 24000

private getReportsFromInt(int value) {
  def result = []
  while (value > 0) {
    reportDescriptions.reverseEach {
      if (value >= it.key) {
        value = value - it.key
        result << it.value
      }
    }
  }
  return "${result.reverse()}"
}

private command(hubitat.zwave.Command cmd) {
  if (state.secure) {
    zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
  }
  else {
    cmd.format()
  }
}

private commands(commands, delay = 500) {
  delayBetween(commands.collect { command(it) }, delay)
}

private String formatDays(BigDecimal duration) {
  java.text.DecimalFormat df = new java.text.DecimalFormat();
  df.setMaximumFractionDigits(2);
  df.setMinimumFractionDigits(0);
  df.setGroupingUsed(false);
  return df.format(duration);
}

private logInfo(msg) {
  if (descriptionTextEnable) log.info msg
}

def logDebug(msg) {
  if (logEnable) log.debug msg
}

def logTrace(msg) {
  if (traceLogEnable) log.trace msg
}

2 Likes

Thanks. This worked for me!

1 Like

Working for me.

1 Like

How do you guys do reporting for one specific month?

I have a webcore routine that resets my device each month. Just before reset I write the current month's energy data to a thingsspeak graph. I want to switch this to Hubigraphs, but the current version will only display 2 months of data. I use a virtual sensor to hold the data for HE, but you could also write it to a file with HE or write to a google spreadsheet.

Sounds complicated and tedious. How do you do it with the virtual sensor?

Just read the month's end usage from your HEM and then write it to a Virtual Sensor. The virtual sensor then holds your monthly energy usage. I mainly did this so I could then use Hubigraphs to plot the data. I do the same for my daily and hourly energy usage as well. Those I can plot with hubigraphs and see in my dashboard. The monthly is not very useful right now because as I mentioned hubigraphs will only let you keep about 2 months of data, and can only plot about 1. The developer has said he may extend that at some point.

So I still send my monthly data to a free thingspeak account and plot that data there.

My HEM is actually still connected to ST, but I control everything through HE via Hubconnect. I have to reset the HEM with ST as the Hubconnect driver won't let me, but I read all my energy data using RM. My plan is to move the HEM to HE when I get my C7 hub up and running. I just haven't got around to it yet.

The RM rules to capture and store the data in the virtual sensor are not that complicated.

This is the rule for capturing the daily data and writing to a sensor. EnergyMidnight is a global that I set at midnight with the energy reading from the HEM. Daily Energy is the virtual sensor.

3 Likes

thank you so much for the explanation. Will try to apply the same to mine.

1 Like

@TechMedX I'm working on getting this working with my 2 clamp v5 HEM to monitor my dryer.

Any idea on what my preferences should be set to get power and current readings? I have my reporting groups setup as per your first post here. Thanks.

@inetjnky If you have it set like my first post it should look like this (except not negative numbers)...


Once you have selected all 3 items per group using the CTRL key, hit "Preferences." and then "Configure".

Funny enough I just move from C4 to C7 yesterday. I actually just reset them using my 1st posy after you mentioned it, thanks!

Download the Hubitat app