Retrospective power report (or, "How I learned to stop worrying and love the bomb.")

So after a slugfest getting OpenHAB to do my latest bidding, I find myself wondering (even though I have a working solution) if maybe I encountered so many problems because I was doing something (or somethings) the hard way. I present my solution for retrospective power reports, and am asking aloud if anyone has any better/cleaner ways to solve the problem. (Jump to PUTTING IT ALL TOGETHER to skip the backstory and jump straight to the code.)

THE MAIN PROBLEM: I need OpenHAB to calculate the amount of power I’ve used for an electric billing cycle, which will have ended anywhere from 1-15 days ago, and started between 29 to 39 days before that. Start date and run range must be configurable through the classic UI; manually editing a text file is not considered a solution.

Recently (this past year) my electric company switched my house to a smart meter. Using the “trust but verify” principle, I want to verify that this new meter (and their billing system) isn’t wildly mis-billing me. If they say I used 1040 KWH in a month and my meter says I used 1030 KWH in a month, whatever. But if they say I used 2590 KWH and my meter says I used 1030 KWH, then we got problems dude.

THE ROOT CAUSE: My electric company (which rhymes exactly with Commonwealth Edison) has a billing department run by sociopaths that still live in the mid-1970s. For some unfathomable reason (even though I have a smart meter that’s read remotely by computer!), my billing cycle varies between 29 and 34 days per month. This billing cycle varies month-to-month dictated by company holidays and neighborhood meter reading schedules which are not available to the customer. Oh, the amount of shade I threw at them on facebook for that!

“I can’t find any month on my calendar that has 34 days in it. You mean to tell me someone has to manually go and tell your computer to read my meter each month!?? No? Then why is the meter reading schedule dependent at all on company holidays? You expect me to believe that you give your computers time off to observe Christmas!??”

The criticism was not received enthusiastically.

Having exhausted my possibilities with their 1970s-era billing department, I realized I could already solve the problem. I have z-wave electric meters on my main feeds logging both power and KWH used every few minutes into mysql; it shouldn’t be too hard to whip up something to do what I need, right? I also have a bunch of other z-wave devices that report cumulative KWH usage, so I should be able to figure out how much those are using as well. Finally, since I know how much I roughly pay per KWH, after I figure out how much power each device used it shouldn’t be hard to figure out how much each cost to run per month, right? Finally, it’d be really great if the whole shebang was emailed so I could read it at my leisure.

…I don’t for ask much. :smiley:


PROBLEM #1: There appears to be no way to input dates to OpenHAB.

I didn’t need to input a date so much as I needed some way to indicate a date range. (“How much power did I use between Jan 1 and Feb 3rd?”)

Solution: Dimmers! Stop laughing; outside of switches dimmers are pretty much the only way I can see to get user input into OpenHAB. So I made two dimmers; one representing the number of days ago the billing period began, and one representing the number of days ago the billing period ended. I also made a textual representation of the date so the user has confirmation after moving the dimmer what date they’re picking. (Anytime the dimmer gets updated I recalculate the associated date string.)


Dimmer VIRTUAL_START_POWER_REPORT_DAYS_AGO "Number of days ago power report should start [%d]" (Group_PowerKWH)
String VIRTUAL_START_POWER_REPORT_DATE "Power report start date [%s]" (Group_PowerKWH)
Dimmer VIRTUAL_STOP_POWER_REPORT_DAYS_AGO "Number of days ago power report should stop [%d]" (Group_PowerKWH)
String VIRTUAL_STOP_POWER_REPORT_DATE "Power report stop date [%s]" (Group_PowerKWH)



PROBLEM #2: There appears to be no way to store information while iterating over members of a group.

For simplicity sake, I made any device that reads KWH that I wanted a report on a member of group Group_KWHReport . So now I needed to iterate over the members of that group, figure out the power usage at the end, figure out the power usage at the beginning, subtract, and I should have the number I need. Add the info for that device to the email and then go onto the next item.

…which doesn’t sound crazy, except OpenHAB says you can’t store things in variables between iterations of group members. If you make a string variable outside of your group iterator and try to change it within the group iterator, OpenHAB will throw a fit saying you “cannot refer to a non-final variable from within a closure”. (That’s designer’s phrase; runtime error should be something similar.) The closure in this case being the group iterator itself. Ugh. So we need a place to stick a string that doesn’t look like a string, that will persist even though we’re iterating over elements in the group.

Solution: Make the email body an openhab element rather than a variable. (If all you have is a hammer, everything looks like a nail.) So instead of writing something like this:

var String Email_Body = "First line of email.\r\n"

Group_PowerKWHReport.members.forEach[item|
Email_Body = Email_Body + "Another line of the email body.  Item: " + item.name + "\r\n"
]

You have to do something like this… item definition:

String VIRTUAL_POWER_REPORT "Dummy string that holds the actual report"

usage:

sendCommand(VIRTUAL_POWER_REPORT,"First line of email.\r\n")
Group_PowerKWHReport.members.forEach[item|
sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + "Another line of the email body.  item: " + item.name + "\r\n")
]

This was the point where I really began to question if I was really doing things the right way. Nevertheless I had a working solution within my grasp, so I persevered on.


PROBLEM #3: Not every meter has complete data.

This was really a problem of my own making; if I was simply after my overall power usage then I could keep tweaking the dimmers until I found my usable date range, errors be damned. But since I was now iterating over a group of items, I needed a better solution.

Solution: If you get a null back from historicstate, go back one less day.

while (item.historicState(now.minusDays(DaysStart.intValue))) == null)
	{
		// if we don't have any historic data for the start date, go forward day by day until we have data
		logInfo("openhab", "power report:  no valid info " + DaysStart + " days ago.  Going to next day.")
		DaysStart = DaysStart - 1  // go back one fewer day
		DaysStartOffset = DaysStartOffset + 1 // counter for how many days we skipped
	}

PROBLEM #4: Negative overall KWH

Similar to problem #3 but different cause. In a nutshell, if you’ve reset one of your KWH meters at a different point than all the others, and your report’s date range spans that time frame, you can wind up with a negative KWH for amount of power used. (e.g. meter was previously at 800 KWH, you reset it on the tenth day of the billing cycle, and then you only use 70 KWH after that. 70-800 = -730KWH. Whoops!)

Solution is nearly identical to problem #3; if you subtract the start from the end and get a negative number, go back one fewer day for the start.


PUTTING IT ALL TOGETHER

To make it all happen, first you need to throw any item that’s a KWH meter into its own group. (Mine is called Group_PowerKWHReport .) Every item also must store persistence data in a queryable database, so mysql is fine, but storing in logfiles is a no-go. I think rrd4j should work as long as you’re not going back in time farther than you’re storing precise data, but I haven’t tried it as I’m a mysql kind of guy.

Finally, you need the following item definitions put somewhere on a sitemap so you can manipulate and see them through the UI:

Dimmer VIRTUAL_START_POWER_REPORT_DAYS_AGO "Number of days ago power report should start [%d]" (Group_PowerKWH)
String VIRTUAL_START_POWER_REPORT_DATE "Power report start date [%s]" (Group_PowerKWH)
Dimmer VIRTUAL_STOP_POWER_REPORT_DAYS_AGO "Number of days ago power report should stop [%d]" (Group_PowerKWH)
String VIRTUAL_STOP_POWER_REPORT_DATE "Power report stop date [%s]" (Group_PowerKWH)
Switch VIRTUAL_POWER_REPORT_GO "GO!  Generate power usage report NOW!  (...and mail it)" (Group_PowerKWH)
String VIRTUAL_POWER_REPORT "Dummy string that holds the actual report"

And now what you’ve all been waiting for, the meat & potatoes, the rules:

val String Mail_Destination = "test@test.com" // where to send mail
val Number Cost_Per_KWH = 0.135 // dollars per KWH


rule "recalculate power report start date"
when
	Item VIRTUAL_START_POWER_REPORT_DAYS_AGO changed
then
	{
		var Number MyDays = 0
		MyDays = VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType
		logInfo("openhab","number of days ago for report to start:" + MyDays.intValue)
		sendCommand(VIRTUAL_START_POWER_REPORT_DATE,now.minusDays(MyDays.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60).toLocalDateTime.toString)
	}
end

rule "recalculate power report end date"
when
	Item VIRTUAL_STOP_POWER_REPORT_DAYS_AGO changed
then
	{
		var Number MyDays = 0
		MyDays = VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType
		logInfo("openhab","number of days ago for report to stop:" + MyDays.intValue)
		sendCommand(VIRTUAL_STOP_POWER_REPORT_DATE,now.minusDays(MyDays.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60).toLocalDateTime.toString)
	}
end

rule "generate and mail power report"
when
	Item VIRTUAL_POWER_REPORT_GO changed to ON
then
	{
		sendCommand(VIRTUAL_POWER_REPORT_GO, OFF)  
		
		logInfo("openhab","power report button pushed")	

		sendCommand(VIRTUAL_POWER_REPORT, "\r\nOpenHAB power usage report for " + ((VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType) - (VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType) ) + " day")
		if (((VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType) - (VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType) ) > 1) sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + "s")
		sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + ", ending with " + VIRTUAL_STOP_POWER_REPORT_DATE.state + "\r\n")

		Group_PowerKWHReport.members.forEach[item|

			var Number KWHStart = 0
			var Number KWHStop = 0
			var Number KWHDifference = 0
			var Number DaysStart = 0
			var Number DaysStartOffset = 0
			var Number DaysStop = 0
			var String FriendlyName = item.name
					
			DaysStart = VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType
			DaysStop = VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType
	
			switch FriendlyName as String
				{
					case "BASEMENT_NETWORK_RACK_KWH": {
							FriendlyName = "Cable modem" }
					case "BEDROOM_FAN_KWH": {
							FriendlyName = "Bedroom fan" }
                                        // couldn't find a way to grab the item description text string while iterating, so change the raw name to something friendly here.  lots of cases omitted for brevity.
				}
			
			if (DaysStop > DaysStart)
				{
//	 				logInfo("openhab","power report:  end date is before start date.  setting start date to end date - 1.")
					DaysStart = DaysStop + 1  // but counterintuitively that's actually "one more day ago" than the stop date
				}
	
			// it seems as though if mysql reports an error (such as table doesn't exist), that the entire routine is aborted for that item on the first mysql historic data access.
			// this fits our use case well, and it does not appear to abort the sequence for the rest of the members, so...  GREAT!
			// but if we get wonky behavior, that's where I'd start looking first.  for now though it just works, so letting sleeping dogs lie.
	
			if (item.historicState(now.minusDays(DaysStop.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60)) == null)
				{
					logInfo("openhab", "power report: report end date for " + FriendlyName + " doesn't have valid data; days before that can't have valid data.  nothing to do.")
				}
			else
				{
					logInfo("openhab","Power report for " + FriendlyName)
	
					KWHStop = item.historicState(now.minusDays(DaysStop.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60)).state as DecimalType
					logInfo("openhab","power report:  got end value")
					sendCommand(VIRTUAL_POWER_REPORT,VIRTUAL_POWER_REPORT.state + "\r\n" + FriendlyName + ": ")
	
					while (item.historicState(now.minusDays(DaysStart.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60)) == null && DaysStart > DaysStop)
						{
							// if we don't have any historic data for the start date, go forward day by day until either we have data or we overrun the stop date
							// logInfo("openhab", "power report:  no valid info " + DaysStart + " days ago.  Going to next day.")
							DaysStart = DaysStart - 1  // go back one fewer day
							DaysStartOffset = DaysStartOffset + 1 // increment counter for how many days we skipped
						}
					
					while (item.historicState(now.minusDays(DaysStart.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60)).state as DecimalType > KWHStop && DaysStart > DaysStop)
						{
							// the meter was reset between the start and end points, resulting in a negative KWH.
							// go forward until either that's not true, or we overrun the end date
							// logInfo("openhab","power report:  negative KWH (meter reset) for " + FriendlyName + ", going forward a day")
							DaysStart = DaysStart - 1 // go back one fewer day
							DaysStartOffset = DaysStartOffset + 1 // inc counter for how many days we skipped ahead						
						}
						
					if (DaysStartOffset > 0)
						{
							logInfo("openhab", "power report:  skipped forward " + DaysStartOffset + " to get to valid data for " + FriendlyName)
						}
						
					KWHStart = item.historicState(now.minusDays(DaysStart.intValue).minusHours(now.getHourOfDay).minusMinutes(now.getMinuteOfDay%60)).state as DecimalType
					logInfo("openhab","power report:  got start value for " + FriendlyName)
					KWHDifference = KWHStop - KWHStart
					
			//		logInfo("openhab","power report, " + FriendlyName + " start: " + KWHStart + " stop:" + KWHStop + " difference:" + KWHDifference) 
	
					var String FormattedNumber = String::format("%1$.3f",KWHDifference)
					sendCommand(VIRTUAL_POWER_REPORT,VIRTUAL_POWER_REPORT.state + FormattedNumber + " KWH")
					
					if (DaysStartOffset > 0)
						{
							sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + " (" + (DaysStart.intValue - DaysStop.intValue) + " day")
							if (DaysStart.intValue - DaysStop.intValue > 1) sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + "s")
							sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + ")")
						}				
					
					FormattedNumber = String::format ("%1$.2f",(KWHDifference * Cost_Per_KWH))
					sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + " [$" + FormattedNumber + "]")
				}
			]  // close forEach

		sendCommand(VIRTUAL_POWER_REPORT, VIRTUAL_POWER_REPORT.state + "\r\n\r\nEnd of OpenHAB power report.\r\n")
		sendMail(Mail_Destination,"OpenHAB Power Report",VIRTUAL_POWER_REPORT.state.toString)  // mail it!
		logInfo("openhab","End of power report")
	}
end

To use it, adjust the dimmers (ignoring the % signs; can’t find a way to get rid of them) to adjust the start and end days for your report. The date picked will show up in the corresponding string so you know what you’ll be getting. (Might need to manually refresh if you’re on OH 1.8.3 to see it update.) Finally, kick the generate power usage report button to on, and it’ll email out a report like so:

OpenHAB power usage report for 30 days, ending with 2017-01-18T00:00:13.497

Cable modem: 1.571 KWH (11 days) [$0.21]
Bedroom fan: 0.000 KWH (11 days) [$0.00]
Bedroom TV: 3.836 KWH (12 days) [$0.52]
Front bush christmas lights: 2.436 KWH (11 days) [$0.33]
Amplifiers: 1.648 KWH (11 days) [$0.22]
Christmas tree: 1.247 KWH (11 days) [$0.17]
Utility room lights: 0.264 KWH (11 days) [$0.04]
Basement freezer: 16.610 KWH (11 days) [$2.24]
Basement fridge: 4.274 KWH (11 days) [$0.58]
Auxillary HVAC: 3.074 KWH (11 days) [$0.41]
Kitchen oven & dishwasher: 10.998 KWH (11 days) [$1.48]
Kitchen microwave & fridge: 42.116 KWH (11 days) [$5.69]
Bedrooms and attic: 45.569 KWH (11 days) [$6.15]
Washer, dryer & server: 50.818 KWH (11 days) [$6.86]
Primary air conditioning: 2.927 KWH (11 days) [$0.40]
Living & dining rooms: 34.067 KWH [$4.60]
Furnace: 71.181 KWH [$9.61]
House TOTAL: 319.890 KWH (11 days) [$43.19]

End of OpenHAB power report.

Simple, eh? :smiley:


You made it all the way through – kudos to you!

I’m wondering, despite working perfectly, if there were any better ways to do some of this code. When the report is generating the CPU spikes pretty hard, and that’s bad in principle. There are other things I don’t like, for example, stuffing the email body into a string object while I’m iterating over group members. That just feels wrong, so looking to see if anyone has any better suggestions to avoid that. Or suggestions on how to improve anything else, for that matter!

8 Likes

Which ones? While not as bad as it could be, I’m part of a power coop (gotta love unincorporated community living) which is also a little backwards in their ways. I’d also like to track my usage a bit. I didn’t know there were zwave meters I could use on the main feeds.

Did you see the Alarm Clock examples and were any of them of help to you?

Dimmers is IMHO a good way to handle this.

This also points out that we really do need a Calendar and Clock widget in the UIs.

SetPoints would also work.

But you can use a StringBuilder.

val StringBuilder Email_Body = "First line of email.\r\n"
Group_PowerKWHReport.members.forEach[item |
    Email_Body.append("Another line of the email body. Item: " + item.name + "\r\n"
]

or, you can use the map/reduce functions

val String Email_Body = "First line of email.\r\n" +  
    Group_PowerKWHReport.members.map[name].reduce[body, name | body = body + "Another line of the email " + name + "\r\n"]

I’ve recently written up a tutorial on using Groups here.

That seems a reasonable approach.

I probably wouldn’t choose rrd4j for this as it starts to “compress” the data in days which will skew your end of month result.

The rules look pretty good as is, with the changes I talk about above. Here is what I would probably do. Not sure they are better. But when I see rules that do almost exactly the same thing I tend to want to combine them. Any reason you are not using DateTime Items instead of Strings for the REPORT_DATEs?:

import org.xtext.lib.base.Functions

val String Mail_Destination = "test@test.com"
val Number Cost_Per_KWH = 0.125 // dollars per KWH

val Functions$Function2 <DimmerItem, StringItem, Boolean> procDaysChange = [ ago, rpt |
    var int MyDays = (ago.state as DecimalType).intValue

    var startStop = if(ago.name.contains("START")) "start" else "stop"
    logInfo("openhab", "number of days ago for report to " + startStop + ": " + MyDays)
    rpt.sendCommand(now.minusDays(MyDays).withTimeAtStartOfDay.toLocalDateTime.toString) // I assume you are shooting for midnight MyDays ago

    true
]

// There is no problem recalculating both dates when either dimmer changes
rule "recalculate power report dates"
when
    Item VIRTUAL_START_POWER_REPORT_DAYS_AGO changed or
    Item VIRTUAL_STOP_POWER_REPORT_DAYS_AGO changed
then
    procDaysChange.apply(VIRTUAL_START_POWER_REPORTS_DAYS_AGO, VIRTUAL_START_POWER_REPORT_DATE)
    procDaysChange.apply(VIRTUAL_STOP_POWER_REPORTS_DAYS_AGO, VIRTUAL_STOP_POWER_REPORT_DATE)
end

I already addressed how you can use StringBuilder instead of a String Item to build your report.

DaysStart = VIRTU ...

Why not just initialize this var with this value in the first place?

You can prefilter the list of values that don’t have a historicState in the table to avoid the MySQL error.

You can replace your switch FriendlyName as String with a .map file and FriendlyName = transform("MAP", "kwh.map", "item.name") It really cleans up tedious code like this.

I’m pretty sure you are trying to get to midnight DaysStart days ago in which case you can use `now.minusDays(DaysStart.intValue).withTimeAtStartOfDay

Putting it all together:

rule "generate and mail power report"
when
	Item VIRTUAL_POWER_REPORT_GO changed to ON
then
	{
                VIRTUAL_POWER_REPORT_GO.sendCommand(OFF)
		
		logInfo("openhab","power report button pushed")	

                StringBuilder report = new StringBuilder

		report.append("\r\nOpenHAB power usage report for ")

        val reportLength = ((VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType) - (VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType) )

        report.append(reportLength)
        report.append(" day")
		if (reportLength > 1) report.append("s")
        report.append(", ending with ")
        report.append(VIRTUAL_STOP_POWER_REPORT_DATE.state)
        report.append("\r\n")

	    var int DaysStart = (VIRTUAL_START_POWER_REPORT_DAYS_AGO.state as DecimalType).intValue
	    var int DaysStop = (VIRTUAL_STOP_POWER_REPORT_DAYS_AGO.state as DecimalType).intValue
         var stopTime = now.minusDays(DaysStop).withTimeAtStartOfDay

		Group_PowerKWHReport.members.filter[item|item.historicState(stopTime) != NULL].forEach[item|

			var Number KWHStart = 0
			var Number KWHStop = 0
			var Number KWHDifference = 0
			var Number DaysStartOffset = 0
			var String FriendlyName = transform("MAP", "kwh.map", item.name)
			
			if (DaysStop > DaysStart)
				{
//	 				logInfo("openhab","power report:  end date is before start date.  setting start date to end date - 1.")
					DaysStart = DaysStop + 1  // but counterintuitively that's actually "one more day ago" than the stop date
				}
	
                                var startTime = now.minusDays(DaysStart).withTimeAtStartOfDay
				logInfo("openhab","Power report for " + FriendlyName)
	
				KWHStop = item.historicState(stopTime).state as DecimalType
				logInfo("openhab","power report:  got end value")
				report.append("\r\n" + FriendlyName + ": ")
	
				while (item.historicState(startTime) == null && DaysStart > DaysStop)
						{
							// if we don't have any historic data for the start date, go forward day by day until either we have data or we overrun the stop date
							// logInfo("openhab", "power report:  no valid info " + DaysStart + " days ago.  Going to next day.")
                            startTime = startTime.plusDays(1)
                                                        DaysStart = DaysStart - 1
							DaysStartOffset = DaysStartOffset + 1 // increment counter for how many days we skipped
						}
					
					while (item.historicState(startTime).state as DecimalType > KWHStop && startTime.isBefore(stopTime))
						{
							// the meter was reset between the start and end points, resulting in a negative KWH.
							// go forward until either that's not true, or we overrun the end date
							// logInfo("openhab","power report:  negative KWH (meter reset) for " + FriendlyName + ", going forward a day")
							startTime.plusDays(1) // go back one fewer day
                                                        DaysStart = DaysStart - 1
							DaysStartOffset = DaysStartOffset + 1 // inc counter for how many days we skipped ahead						
						}
						
					if (DaysStartOffset > 0)
						{
							logInfo("openhab", "power report:  skipped forward " + DaysStartOffset + " to get to valid data for " + FriendlyName)
						}
						
					KWHStart = item.historicState(startTime).state as DecimalType
					logInfo("openhab","power report:  got start value for " + FriendlyName)
					KWHDifference = KWHStop - KWHStart
					
			//		logInfo("openhab","power report, " + FriendlyName + " start: " + KWHStart + " stop:" + KWHStop + " difference:" + KWHDifference) 
	
					var String FormattedNumber = String::format("%1$.3f",KWHDifference)
                    report.append(FormattedNumber + " KWH")
					
					if (DaysStartOffset > 0)
						{
							report.append(" (" + (DaysStart - DaysStop) + " day")
							if (DaysStart - DaysStop > 1) report.append("s")
                                                        report.append(")")
						}				
					
					FormattedNumber = String::format ("%1$.2f",(KWHDifference * Cost_Per_KWH))
                                        report.append(" [$" + FormattedNumber + "]")
				}
			]  // close forEach

                 report.append("\r\n\r\nEnd of OpenHAB power report.\r\n")
		sendMail(Mail_Destination,"OpenHAB Power Report",report.toString)  // mail it!
		logInfo("openhab","End of power report")
	}
end

Note: I just typed this in. I probably introduced a myriad of errors.

Thanks for sharing!

1 Like

I can’t help too much with suggestions for improvements but I laughed so hard that I just had to reply to this excellent thread :slight_smile: (had similar experiences with my local electric company)

I’m using version 1 dual phase Aeon Home Energy Monitors. They’ve been going on sale recently for as low as ten dollars. I originally bought them to use them as $$CHEAP$$ repeaters, but wound up using them as originally intended instead. I have six of them. :smiley:

Here’s a picture of my panel; each white box is a clamp that leads to one of my HEMs. As you can tell, I have one around each phase for my main power, and then a bunch off of branch circuits. They aren’t amazingly accurate (they get decidedly non-linear below about 10W), but I’m not sweating the small stuff; in-the-ballpark numbers are good enough.

A power co-op I can cut lots of slack. My power company is part of the Fortune 100; they deserve no such slack!

No, I hadn’t! I did kick around the idea of using setpoints instead of dimmers, and that’ll likely be a “version 2.0” type improvement. By the time I thought of setpoints I already had the core working with the dimmers. I figured I’d complete version 1 then double back and make it better vs. doubling back and never completing anything. (…never getting anything done annoys me.)

Oh, wow! That’s exactly the type of thing I was looking for, that would clean up a lot of this mess. Thanks!

No actual good reason; originally I had them as DateTime items but ran into my achillies heel of conversion of types (between openhab types and java types) and basically this was what I was able to throw enough rocks at until it worked. :smiley:

Oh, ha… that’s an old programming style that I use to make sure I never use any undefined variables. Basically when I start to write a routine, I put all my variables I think I’m going to need at the top, and always immediately initialize them to something. That way no matter what variable I use I know it at least exists with something in it. If I know from the get-go what I want to initialize it to I’ll do that, but often times it’s a vague idea jelling as I’m writing code.

Yup! Thanks, that’s a lot cleaner. (I was OK living with “give or take a minute” resolution, but this is better as it’s the true intent. :smiley:)

I’m going to have to start shopping where you do. I can’t find them right now for less than $25.

As soon as I saw all the cars up top I pegged you for an old C programmer. :grinning:

Learned using assembly, but did more than enough C as well. :wink:

+1 - couldn’t but LMAO.
Btw, @TheKorn are you in the U.S. ? If so, I wouldn’t post pictures like that one under that thread title :slight_smile:

Don’t worry, I don’t plan on bringing my house’s electrical panel through airport security anytime soon. :stuck_out_tongue_winking_eye:

2 Likes

I guess they’ve gone up in price? I don’t see any for less than $80 on eBay right now.

Am I searching for the wrong item?

-Nathan

It’s been well over a year since this was posted. And when it was posted the Gen 1 of this meter was only cheap because the Gen 2 or Gen 5 (I forget which) just came out so companies were dumping their stock. I’d be surprised if you could find a Gen 1 for sale at all any more, let alone a cheap one.

Every once in a long while I’ll still run across them around $20, but it’s definitely very slim (and patient!) pickings these days.

Kinda sucks, since I’ve had two of them straight out die on me in the interim. Luckily I had one spare, though!