[Release] IoTaWatt Power Monitor Driver v0.1.20220723

Anybody know how to get the Iotawatt cheaper in Canada? With everything it turns out to be around $500 CAD. Pretty expensive :frowning:

You could drive over the border to New Hampshire where they are made... :wink:

2 Likes

First of all thank you very much for your work on this project. After reading this thread I did purchase IoTaWatt and have since found that Bob over there is extremely knowledgeable and helpful.

I have a few questions about the child devices.

The inputs display a decimal formatted to 1 while the outputs display a decimal formatted to 3. Is there a way to change the outputs to a decimal of 1?

The parent device shows as a presence device. Just curious why and is there a use for that?

My guess is the number of decimal places is coming from the IoTaWatt. We should be able to truncate the extra decimal places inside the child power driver. I'll take a look and will get back to you regarding this.

The 'presence' capability is useful to know whether of not Hubitat can connect to, and receive data from, the IoTaWatt device. If the connection is lost for long enough, then the Parent device's presence changes to 'not present'. Once the connection is restored, it will change back to 'present'. I use the Hubitat Notifications App to send be a push notification each time the Parent's presence attribute changes state. This way I know if the IoTaWatt is having an issue. The only time I have seen this is when I am messing around with my UniFi WiFi network, either upgrading firmware or trying a new configuration. The IoTaWatt is pretty robust at reconnecting. Also, I have had the GFCI outlet in the Orbit sprinkler box trip from time to time. It is nice to have Hubitat notify me that the IoTaWatt is offline.

@scunny - I have modified the Child Power Meter driver to round the value to one decimal place. You can simply open up the code for this driver on your hub, then click the IMPORT button at the top of the editor window, and then click Import button on the pop-up window. Finally, be sure to click the SAVE button at the top of the editor window. Once fresh data is received from the IoTaWatt, you should see all child devices will now have one one decimal place.

3 Likes

Hey there, I'm just about to setup my IoTaWatt here at home after having it sitting in a box for quite some time. I saw your most recent post today but I'm only showing your parent driver on your initial post and it hasn't been updated in 5 months. Is this still the same place to get the driver(s) for this?

Yes, same GitHub repo. There has not been a need for changes to the parent in some time. :sunglasses:

Thank you. That worked.

1 Like

Maybe I’m missing something, is there no child driver? Where is this latest update you just did?

Per the ReadMe... See STEP #4 below

Create Hubitat Driver

Open up the "iotawatt-parent.groovy" driver from this repository. Make sure you hit the "RAW" button, then select/highlight all of the source code, and COPY everything (Ctrl-C on Windows, or right click->Copy).

  1. In your Hubitat Elevation Hub's admin web page, select the "Drivers Code" section and then click the "+ New Driver" button in the top right corner. This will open a editor window for manipulating source code.
  2. Click in the editor window. Then PASTE all of the code you copied in the first step.
  3. Click the SAVE button in the editor window.
  4. Repeat the above steps to install the required HubDuino "Child Power Meter" and "Child Voltage Sensor" Drivers. These are avilable at ST_Anything/HubDuino/Drivers at master · DanielOgorchock/ST_Anything · GitHub
1 Like

Thanks. Sorry! Lol

1 Like

Had my hub lock up tonight for the first time. I’m polling every 30 seconds but bumped it back to one minute. I have an old c4 hub here I may have to offload this too if this keep happening. Just curious @ogiewon what your polling rate is you’re using? Thanks.

I also have 13 inputs and double the outputs.

I am using a C4 hub as well, and have two IoTaWatt devices configured. One polls at 30 seconds, while the second one polls at 60 seconds. No issues noted here, but this particular hub is not heavily loaded. I have a C3 hub that has all of my Zigbee and Lutron devices, and it runs most of the automations for the house.

I use 14 inputs + 4 outputs on one IoTaWatt. The second one has 12 inputs and about 6 outputs.

Thanks, I’ll have to look into moving them over. Appreciate it.

Silly question. I found this thread last month and have put purchasing an IoTaWatt on my list of things todo.

I did some reading on the iotawatt page as well and figured out what I need to buy. I’m just planning to monitor the main + 5 x 240V loads.

When you mention outputs what are you referring to? I thought there were only current sense inputs.

When you setup your IoTaWatt and configure your inputs, they read watts by default. I have outputs configured to also get readings for amps and outputs for a combined total between two circuit breakers so I can have calculated how much power is being used by certain devices in my home that are spread across multiple outlets. For example I wanted to know how much power is being used for my aquarium each month, but it’s spread across 3 20amp outlets. So I run a calculation that gives me the combined total which I then calculate against how much I pay for electricity and have a close estimate of how much everything running is costing me.

2 Likes

To further inetjnky an output is like a virtual item. You can create an output in Iotawatt that represents the mathematical result of any of the inputs. I do something similar where I have an output combining my air handler and outside compressor giving me a total energy usage for my ac system. Another example might be if you wanted to know how much energy a specific circuit is using relative to the whole house you would to take the total of the two mains (total usage) minus any other measured circuit(s). These virtual outputs also show as a device in HE and can be used in rules.

1 Like

@steve.maddigan - just an FYI that I have created a slightly revised version of my Parent IoTaWatt Driver for Hubitat. This new version uses the Hubitat built-in 'Generic Component Power Meter" and "Generic Component Voltage Sensor" drivers, instead of my custom ones. These Hubitat built-in child drivers were added to the platform after my initial release of this integration.

Thus, for new users, this new version is probably a better, cleaner solution. However, for exiting users, this will be a 'Breaking Change'. Thus, I am trying to figure out the best way to release this to prevent users from inadvertently upgrading and messing up their systems.

For right now, I will just leave a copy of the new version of the Parent Driver here for you to test out once your new IoTaWatt arrives.

BREAKING CHANGE!!! Existing Users Should Not Blindly Upgrade to this version!!!

/**
 *
 *  File: IoTaWatt-Parent.groovy
 *  Platform: Hubitat
 *
 *  Requirements:
 *     1) IoTaWatt Home Energy Meter from https://iotawatt.com/ using a reserved/static TCP/IP address
 *
 *  Copyright 2018 Dan G Ogorchock 
 *
 *  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 History:
 *
 *    Date        Who            What
 *    ----        ---            ----
 *    2018-12-20  Dan Ogorchock  Original Creation
 *    2019-05-24  Dan Ogorchock  Added ImportURL metadata
 *    2019-09-15  Dan Ogorchock  Added Presence Capability to know if the IoTaWatt device is online or offline
 *    2020-05-06  Dan Ogorchock  Added cleanup functionality to the uninstalled() routine 
 *    2020-05-08  Dan ogorchock  Ensure scheduling works properly after a hub reboot
 *    2020-05-16  Dan Ogorchock  Improved error handling
 *    2020-11-02  Dan Ogorchock  Added timeout to http calls
 *    2021-08-07  Dan Ogorchock  Convert child devices to use Hubitat's built-in 'Generic Component' drivers.
 *                               Note:  THIS IS A BREAKING CHANGE!
 *
 *
 */

 def version() {"v1.0.20210807"}

metadata {
    definition (name: "IoTaWatt Parent", namespace: "ogiewon", author: "Dan Ogorchock", importUrl: "https://raw.githubusercontent.com/ogiewon/Hubitat/master/Drivers/iotawatt-parent.src/iotawatt-parent.groovy") {
        capability "Initialize"
        capability "Refresh"
        capability "Presence Sensor"  //used to determine is the IoTaWatt microcontroller is still reporting data or not
        
        //command "deleteAllChildDevices"
    }
}

preferences {
    input "deviceIP", "text", title: "IoTaWatt IP Address", description: "in form of 192.168.1.138", required: true, displayDuringSetup: true
    input "pollingInterval", "number", title: "Polling Interval", description: "in seconds", range: "10..300", defaultValue: 30, displayDuringSetup: true
    input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
}

def logsOff(){
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable",[value:"false",type:"bool"])
}

def refresh() {
   handleUpdates() 
}

def installed() {
    initialize()
}

def updated() {
    log.info "updated() called"
    unschedule()
    if (logEnable) runIn(1800,logsOff)
    initialize()
    //schedule("0/${pollingInterval} * * * * ? *", handleUpdates)
}

def initialize() {
    state.version = version()
    log.info "initialize() called"
    if (deviceIP) {
        handleUpdates()
    }
    else
    {
        log.warn "Please enter the IoTaWatt IP Address and then click SAVE"
    }

}

def handleUpdates() {
    runIn(pollingInterval, handleUpdates)
    
    String type
    String name
    String units
    def child
    float tmpValue
    
    def IoTaWattData = getData()
    IoTaWattData?.each{ it -> //iterate over all the updates in array
        //make sure we were given name and type
        if(!it.containsKey('name'))
        {
            log.error("'name' was not supplied")
            return
        }
        if(!it.containsKey('value'))
        {
            log.error("'value' was not supplied")
            return
        }
        if(!it.containsKey('units'))
        {
            log.error("'units' was not supplied")
            return
        }


        
        if (it.units == "Watts") {
            type = "Power Meter"
            name = "power"
            units = "W"
        }
        else if (it.units == "Vrms") {
            type = "Voltage Sensor"
            name = "voltage"
            units = "V"
        }
        else if (it.units == "Hz") {
            type = "Voltage Sensor"
            name = "frequency"
            units = "Hz"
        }
        
        child = fetchChild(type, it.name)
        tmpValue = Float.parseFloat(it.value)
        tmpValue = tmpValue.round(1)
        ///def event = [[name: name, value: tmpValue, unit: units, descriptionText:"${name} is now ${tmpValue} ${units}"]]
        ///log.debug child
        ///log.debug event
        child.parse([[name: name, value: tmpValue, unit: units, descriptionText:"${name} is now ${tmpValue} ${units}"]])
    }
    
    //cleanup
    IoTaWattData = null
    child = null
    tmpValue = null    
    type = null
    name = null
    units = null    
}


def getData(){
   
    def params = [
        uri: "http://${deviceIP}/status?inputs=yes&outputs=yes",
        contentType: "application/json",
        requestContentType: "application/json",
        timeout: 10
    ]
    
    if (deviceIP) {
        try{
            httpGet(params){response ->
                if(response.status != 200) {
                    if (device.currentValue("presence") != "not present") {
                        sendEvent(name: "presence", value: "not present", isStateChange: true, descriptionText: "Error trying to communicate with IoTaWatt device")
                    }
                    log.error "Received HTTP error ${response.status}. Check your IP Address and IoTaWatt!"
                } else {
                    if (device.currentValue("presence") != "present") {
                        sendEvent(name: "presence", value: "present", isStateChange: true, descriptionText: "New update received from IoTaWatt device")
                    }
                    //log.debug "Response from IoTaWatt = ${response.data}"
                    def inputs = response.data.inputs
                    def outputs = response.data.outputs             
                    def IoTaWattData = []
                    
                    //Format the IoTaWatt Input and Output data consistently to simplify the child device creation process
                    response?.data?.inputs?.each { input ->
                        String tmpName = "Input_${input.channel.toString().padLeft(2,'0')}"
                        if (input.Watts) {
                            //log.debug "name: ${input.channel}, units: Watts, value: ${input.Watts}, units: Pf, value: ${input.Pf}"
                            IoTaWattData << [name:tmpName, units:'Watts', value:input.Watts.toString().trim()]
                        } else if (input.Vrms) {
                            //log.debug "name: ${input.channel}, units: Vrms, value: ${input.Vrms}, units: Hz, value: ${input.Hz}"
                            IoTaWattData << [name:tmpName, units:'Vrms', value:input.Vrms.toString().trim()]
                            IoTaWattData << [name:tmpName, units:'Hz', value:input.Hz.toString().trim()]
                        } else {
                            log.error "Unhandled case for ${input}"
                        }
                    }

                    response?.data?.outputs?.each { output ->
                        String tmpName = "Output_${output.name}"
                        IoTaWattData << [name:tmpName, units:output.units, value:output.value.toString().trim()]
                    }
                    
                    return IoTaWattData
                }
            }
        } catch (Exception e) {
            if (device.currentValue("presence") != "not present") {
                sendEvent(name: "presence", value: "not present", isStateChange: true, descriptionText: "Error trying to communicate with IoTaWatt device")
            }
            log.warn "IoTaWatt Server Returned: ${e}"
            if (e == "java.net.NoRouteToHostException: No route to host (Host unreachable)") {
                //Give the IoTaWatt extra time to recover
                runIn((pollingInterval * 2), handleUpdates)
            }
        } 
    } else {
        log.error "IP Address '${deviceIP}' is not properly formatted!"
    }
}

def fetchChild(String type, String name){
    String thisId = device.id
    def cd = getChildDevice("${thisId}-${type}_${name}")
    if (!cd) {
        cd = addChildDevice("hubitat", "Generic Component ${type}", "${thisId}-${type}_${name}", [name: "${name}", isComponent: true])
    }
    return cd 
}

def uninstalled() {
    log.info "Executing 'uninstalled()'"
    unschedule()
    deleteAllChildDevices()
}

def deleteAllChildDevices() {
    log.info "Uninstalling all Child Devices"
    getChildDevices().each {
          deleteChildDevice(it.deviceNetworkId)
       }
}

//child device methods
void componentRefresh(cd) {
    log.info "received refresh request from ${cd.displayName}"
    runIn(2, refresh)
}
1 Like

If I’ll be moving my IoTaWatt integration over to my C4, I’m guessing now would be the time to do it since I’ll be removing snd readding there? Lol. I’m just loathing having to redo my influx power dashboard lol