Serial Binding and Current Cost Energy Meter

I’m new to all this but, managed to get my CurrentCost Energy Meter working with the Serial Binding in OH3. I hope it may be of use to others, also any advice on how to improve it would be greatly appreciated!

Things

Bridge serial:serialBridge:currentCost [serialPort="/dev/ttyUSB0", baudRate=57600] {
    // Not using the serial device, I was hoping to sort the xml data here with 
    // regex instead of xpath in a rule, but could not get this to work, 
    // does anyone has any ideas? 
    // 
    // Thing serialDevice displayUnit [patternMatch=".*"] {
    //    Channels:
    //        Type string : test [transform="REGEX:(.*?)"]
}

currentcost.items

String  CurrentCostRaw  "CurrentCost XML [%s]"      { channel="serial:serialBridge:currentCost:string" }

// Realtime energy usage
Number CurrentCostElectricTotalWatts "Usage now [%.0f W]"       <energy> 
String CurrentCostSoftwareVersion    "Software version [%s]"    <text> 
Number CurrentCostDaysAlive          "Age [%.0f days]"          <time>
String CurrentCostLastUpdate         "Last updated [%s]"        <time>  
Number CurrentCostTemperature        "Temperature [%.1f °C]"    <temperature>
Number CurrentCostDailyCost          "Daily cost [£ %.2f]"      <piggybank> 
Number CurrentCostMonthlyCost        "Monthly cost [£ %.2f]"    <piggybank> 

// Enable if 3 phase electric
// Number CurrentCostElectric1Watts     "Electric 1 [%.0f W]"  
// Number CurrentCostElectric1Watts     "Electric 2 [%.0f W]"
// Number CurrentCostElectric1Watts     "Electric 3 [%.0f W]"

// IAMs
Number CurrentCostIam1 "IAM 1 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam2 "IAM 2 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam3 "IAM 3 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam4 "IAM 4 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam5 "IAM 5 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam6 "IAM 6 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam7 "IAM 7 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam8 "IAM 8 [%.0f W]" <poweroutlet_uk> 
Number CurrentCostIam9 "IAM 9 [%.0f W]" <poweroutlet_uk> 

// Historic values
Number CurrentCostHistoricHour4  "2 to 4 hours ago [%.3f kW]"  <line>
Number CurrentCostHistoricHour6  "4 to 6 hours ago [%.3f kW]"  <line>
Number CurrentCostHistoricHour8  "6 to 8 hours ago [%.3f kW]"  <line>
Number CurrentCostHistoricHour10 "8 to 10 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour12 "10 to 12 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour14 "12 to 14 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour16 "14 to 16 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour18 "16 to 18 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour20 "18 to 20 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour22 "20 to 22 hours ago [%.3f kW]" <line>
Number CurrentCostHistoricHour24 "22 to 24 hours ago [%.3f kW]" <line>

Number CurrentCostHistoricDay1  "1 day ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay2  "2 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay3  "3 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay4  "4 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay5  "5 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay6  "6 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay7  "7 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay8  "8 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay9  "9 days ago [%.3f kW]"  <line>
Number CurrentCostHistoricDay10 "10 days ago [%.3f kW]" <line>
Number CurrentCostHistoricDay11 "11 days ago [%.3f kW]" <line>
Number CurrentCostHistoricDay12 "12 days ago [%.3f kW]" <line>
Number CurrentCostHistoricDay13 "13 days ago [%.3f kW]" <line>
Number CurrentCostHistoricDay14 "14 days ago [%.3f kW]" <line>

Number CurrentCostHistoricMonth1  "1 month ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth2  "2 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth3  "3 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth4  "4 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth5  "5 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth6  "6 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth7  "7 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth8  "8 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth9  "9 months ago [%.3f kW]"  <line>
Number CurrentCostHistoricMonth10 "10 months ago [%.3f kW]" <line>
Number CurrentCostHistoricMonth11 "11 months ago [%.3f kW]" <line>
Number CurrentCostHistoricMonth12 "12 months ago [%.3f kW]" <line>

currentcost.rule

// This rule updates currentcoast raw xml serial data

// Requres XPATH transformations

rule UpdateCurrentCost
when
Item CurrentCostRaw received update
then

// Code has been writted in accordance to the description 
// issued 18th February 2009 at http://www.currentcost.com/cc128/xml.htm

var Number unitCost = 18.60  // pence cost per kw

for (String raw: CurrentCostRaw.state.toString.replace("</msg> <msg>","</msg>\r\n<msg>").split("\r\n"))
{

	postUpdate(CurrentCostSoftwareVersion, transform("XPATH", "/msg/src/text()", raw))
	postUpdate(CurrentCostDaysAlive, transform("XPATH", "/msg/dsb/text()", raw))
	postUpdate(CurrentCostLastUpdate, transform("XPATH", "/msg/time/text()", raw))
	
	// If Xml contains real-time data
	if ( transform("XPATH", "boolean(/msg/type[text() = '1'])", raw) == "true" ) {
		
		postUpdate(CurrentCostTemperature, transform("XPATH", "/msg/tmpr/text()", raw))
	
		// if sensor = 0 then whole house electricty
		if ( transform("XPATH", "/msg/sensor", raw) == "0" ) {
					
			// Uncomment lines below if you use 3 phase electric UNTESTED
			// postUpdate(CurrentCostElectric1Watts, transform("XPATH", "/msg/ch1/watts",raw))
			// postUpdate(CurrentCostElectric2Watts, transform("XPATH", "/msg/ch2/watts",raw))
			// postUpdate(CurrentCostElectric3Watts, transform("XPATH", "/msg/ch3/watts",raw))
			// var Number totalWatts = Integer::parseInt(transform("XPATH", "<?sum(./msg/ch1/watts | ./msg/ch2/watts | ./msg/ch3/watts)?>"))

			var Number totalWatts = Integer::parseInt(transform("XPATH", "/msg/ch1/watts/text()",raw))
			
			postUpdate( CurrentCostElectricTotalWatts, totalWatts )
			postUpdate( CurrentCostDailyCost, totalWatts * 24 / 1000 * unitCost / 100 )
			postUpdate( CurrentCostMonthlyCost, totalWatts * 24 / 1000 * unitCost / 100 * 30.436875)
		
		}

		// else log IAMs response
		else if ( transform("XPATH", "/msg/sensor", raw) == "1" ) { postUpdate(CurrentCostIam1, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "2" ) { postUpdate(CurrentCostIam2, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "3" ) { postUpdate(CurrentCostIam3, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "4" ) { postUpdate(CurrentCostIam4, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "5" ) { postUpdate(CurrentCostIam5, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "6" ) { postUpdate(CurrentCostIam6, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "7" ) { postUpdate(CurrentCostIam7, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "8" ) { postUpdate(CurrentCostIam8, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else if ( transform("XPATH", "/msg/sensor", raw) == "9" ) { postUpdate(CurrentCostIam9, transform("XPATH", "/msg/ch1/watts/text()",raw)) }
		else { logInfo("CurrentCost","[Realtime XML not processed - " + raw + "]") }
		
	}  

	// Historical
	else if ( transform("XPATH", "boolean(/msg/hist)",raw) == "true" ) {
	
		// BI-HOURLYS
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h002)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour2, transform("XPATH", "/msg/hist/data[sensor='0']/h002/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h004)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour4, transform("XPATH", "/msg/hist/data[sensor='0']/h004/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h006)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour6, transform("XPATH", "/msg/hist/data[sensor='0']/h006/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h008)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour8, transform("XPATH", "/msg/hist/data[sensor='0']/h008/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h010)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour10, transform("XPATH", "/msg/hist/data[sensor='0']/h010/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h012)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour12, transform("XPATH", "/msg/hist/data[sensor='0']/h012/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h014)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour14, transform("XPATH", "/msg/hist/data[sensor='0']/h014/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h016)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour16, transform("XPATH", "/msg/hist/data[sensor='0']/h016/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h018)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour18, transform("XPATH", "/msg/hist/data[sensor='0']/h018/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h020)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour20, transform("XPATH", "/msg/hist/data[sensor='0']/h020/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h022)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour22, transform("XPATH", "/msg/hist/data[sensor='0']/h022/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/h024)", raw) == "true" ) {	postUpdate(CurrentCostHistoricHour24, transform("XPATH", "/msg/hist/data[sensor='0']/h024/text()",raw))	}		
		
		// DAYS
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d001)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay1, transform("XPATH", "/msg/hist/data[sensor='0']/d001/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d002)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay2, transform("XPATH", "/msg/hist/data[sensor='0']/d002/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d003)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay3, transform("XPATH", "/msg/hist/data[sensor='0']/d003/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d004)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay4, transform("XPATH", "/msg/hist/data[sensor='0']/d004/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d005)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay5, transform("XPATH", "/msg/hist/data[sensor='0']/d005/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d006)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay6, transform("XPATH", "/msg/hist/data[sensor='0']/d006/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d007)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay7, transform("XPATH", "/msg/hist/data[sensor='0']/d007/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d008)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay8, transform("XPATH", "/msg/hist/data[sensor='0']/d008/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d009)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay9, transform("XPATH", "/msg/hist/data[sensor='0']/d009/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d010)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay10, transform("XPATH", "/msg/hist/data[sensor='0']/d010/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d011)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay11, transform("XPATH", "/msg/hist/data[sensor='0']/d011/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d012)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay12, transform("XPATH", "/msg/hist/data[sensor='0']/d012/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d013)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay13, transform("XPATH", "/msg/hist/data[sensor='0']/d013/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/d014)", raw) == "true" ) { postUpdate(CurrentCostHistoricDay14, transform("XPATH", "/msg/hist/data[sensor='0']/d014/text()",raw)) }
		
		// MONTHS
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m001)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth1, transform("XPATH", "/msg/hist/data[sensor='0']/m001/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m002)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth2, transform("XPATH", "/msg/hist/data[sensor='0']/m002/text()",raw)) }
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m003)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth3, transform("XPATH", "/msg/hist/data[sensor='0']/m003/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m004)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth4, transform("XPATH", "/msg/hist/data[sensor='0']/m004/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m005)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth5, transform("XPATH", "/msg/hist/data[sensor='0']/m005/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m006)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth6, transform("XPATH", "/msg/hist/data[sensor='0']/m006/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m007)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth7, transform("XPATH", "/msg/hist/data[sensor='0']/m007/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m008)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth8, transform("XPATH", "/msg/hist/data[sensor='0']/m008/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m009)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth9, transform("XPATH", "/msg/hist/data[sensor='0']/m009/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m010)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth10, transform("XPATH", "/msg/hist/data[sensor='0']/m010/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m011)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth11, transform("XPATH", "/msg/hist/data[sensor='0']/m011/text()",raw))	}
		if ( transform("XPATH", "boolean(/msg/hist/data[sensor='0']/m012)", raw) == "true" ) {	postUpdate(CurrentCostHistoricMonth12, transform("XPATH", "/msg/hist/data[sensor='0']/m012/text()",raw))	}
	}
	
	else { logInfo("CurrentCost","[XML UNKNOWN - " + raw + "]") }
}

end

Sitemap

// Current Cost Energy Monitor
	Text item=CurrentCostElectricTotalWatts label="Energy Usage" icon="energy" {	
		Frame{	
			Text item=CurrentCostElectricTotalWatts 
			Text item=CurrentCostDailyCost 
			Text item=CurrentCostMonthlyCost 
			Text item=CurrentCostLastUpdate
			Text item=CurrentCostTemperature
			Text item=CurrentCostDaysAlive
			Text item=CurrentCostSoftwareVersion
		}		
		Frame label="Individual Appliance Monitors" {
			Text item=CurrentCostIam1
			Text item=CurrentCostIam2
			Text item=CurrentCostIam3
			//Text item=CurrentCostIam4
			//Text item=CurrentCostIam5
			//Text item=CurrentCostIam6
			//Text item=CurrentCostIam7
			//Text item=CurrentCostIam8
			//Text item=CurrentCostIam9
		}			
		Frame label="Historic Bi-hourly Usage" {	
			Text item=CurrentCostHistoricHour4
			Text item=CurrentCostHistoricHour6
			Text item=CurrentCostHistoricHour8
			Text item=CurrentCostHistoricHour10
			Text item=CurrentCostHistoricHour12
			Text item=CurrentCostHistoricHour14
			Text item=CurrentCostHistoricHour16
			Text item=CurrentCostHistoricHour18
			Text item=CurrentCostHistoricHour20
			Text item=CurrentCostHistoricHour22
			Text item=CurrentCostHistoricHour24
		}			
		Frame label="Historic Day Usage" {			
			Text item=CurrentCostHistoricDay1
			Text item=CurrentCostHistoricDay2
			Text item=CurrentCostHistoricDay3
			Text item=CurrentCostHistoricDay4
			Text item=CurrentCostHistoricDay5
			Text item=CurrentCostHistoricDay6
			Text item=CurrentCostHistoricDay7
			Text item=CurrentCostHistoricDay8
			Text item=CurrentCostHistoricDay9
			Text item=CurrentCostHistoricDay10
			Text item=CurrentCostHistoricDay11
			Text item=CurrentCostHistoricDay12
			Text item=CurrentCostHistoricDay13
			Text item=CurrentCostHistoricDay14
		}			
		Frame label="Historic Month Usage" {			
			Text item=CurrentCostHistoricMonth1
			Text item=CurrentCostHistoricMonth2
			Text item=CurrentCostHistoricMonth3
			Text item=CurrentCostHistoricMonth4
			Text item=CurrentCostHistoricMonth5
			Text item=CurrentCostHistoricMonth6
			Text item=CurrentCostHistoricMonth7
			Text item=CurrentCostHistoricMonth8
			Text item=CurrentCostHistoricMonth9
			Text item=CurrentCostHistoricMonth10
			Text item=CurrentCostHistoricMonth11
			Text item=CurrentCostHistoricMonth12
		}
	}

It all seems to be working ok, the only issue is that sometimes the rule drops into XML not recognised catches and when I inspect the XML I just can’t work out why the XPATH is not parsing it. It only happens now and then, so no biggy, it just bugs me! If you have any ideas then please let me know.

Thanks.

This has been fixed now. It was caused by sometimes getting 2 complete XML documents, so would crash xpath, This has been fixed by splitting the raw data at the \r\n and looping one by one. I have updated the code in the first post.

Just found the analysing sections on openhab that plots graphs with different items. Wow, it’s amazing. Here’s the Current Cost data!

Hi
´Could you help me with your rule? I am completely new to OpenHab scripting and would like to integrate my Current Cost Energy meter.

When I try to save the Rule designed by you, it gives throws an error:

openHAB
YAMLSemanticError: Implicit map keys need to be on a single line

I cannot figure out the error by myself…

Thanks!

The rules @digitalgeek.me shows are formatted for use in a xxx.rules file, not for GUI entry