Getting CurrentCost data into HA

I thought I’d share here how I get data from my CurrentCost EnviR.
First of all, credits were credits are due, the original code comes from Robin Wilson

Now that this is out of the way, here is what you’ll need:

  • CurrentCost EnviR or a CurrentCost CC128 (though the latter only allows 1 energy monitor device connected)
  • RJ45 to USB data Cable
  • MQTT Setup and running. Check HA’s docs

You will most likely need to install a few libraries for this to work:
sudo pip install requests untangle simplejson pyserial

Now copy both scripts below.

The first file calls the main script, ensuring only 1 script is running at a time (since we’re reading data from a serial port)

#!/usr/bin/python
# -*- coding: utf-8 -*-
# CurrentCost.py

import time
import subprocess

# Kill reader program in case it's running since we're reading over a serial port
cmd1 = "echo INSERT_SU_PASSWORD_HERE | sudo -S pkill -9 -f CurrentCostReader"
try:
	ps = subprocess.Popen(cmd1, shell=True)
except:
	pass
time.sleep(1)
cmd2 = "echo INSERT_SU_PASSWORD_HERE | sudo -S FULL_PATH_TO_FILE/CurrentCostReader.py"
print "Starting CurrentCostReader..."
ps = subprocess.Popen(cmd2, shell=True)

# Ensures main reader program gets killed if main program stops
def Goodbye():
	print "Killing CurrentCostReader..."
	try:
		ps = subprocess.Popen(cmd1, shell=True)
	except:
		pass

import atexit
atexit.register(Goodbye)


while(True):
	time.sleep(1)

The 2nd file does all the work:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# CurrentCostReader.py

# there may be a few libraries that need installing. 
import untangle
import serial
import json, requests, simplejson
import paho.mqtt.client as mqtt

MQTT_Host = "INSERT_MQTT_HOST_HERE"
MQTT_Port = "INSERT_PORT_HERE"
MQTT_User = "INSERT_USERNAME_HERE"
MQTT_Password = "INSERT_PASSWORD_HERE"
degree_sign= u'\u2103' #don't touch, this is the code for 'º'

# Appliances Definitions, 1 definition by if block below
# Set to 0.1 to force the program to send an initial status at first run
Temperature = 0.1
Total = 0.1
Dehumidifier = 0.1


def get_data(port='/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0', verbose=False):
	# port: the port that the CurrentCost meter is attached to. Something like /dev/ttyUSB0 or COM1 or /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0
	# Returns:
	# (sensor, temperature, usage), with sensor as the number of the interface a device is assigned to, temperature in degrees C, and usage in Watts

	ser = serial.Serial(port, 57600)
	xmldata = ser.readline()
	if verbose:
		print(xmldata)
	ser.close()
	p = untangle.parse(xmldata)
	temperature = float(p.msg.tmpr.cdata)
	watts = int(p.msg.ch1.watts.cdata)
	sensor = int(p.msg.sensor.cdata)
	return [sensor, watts, temperature]





client = mqtt.Client("P1") # must be unique on MQTT network
client.username_pw_set(str(MQTT_User),str(MQTT_Password))
client.connect(MQTT_Host, port=str(MQTT_Port))


while(True):
	try:
		CCData = get_data()
		CCData[2] = CCData[2] - 2.5 # CCData[2]  is the temperature. I'm adjusting as my CurrentCost is 2.5ºC higher than real temp

		# If block for Temperature
		if (Temperature != CCData[2]):
			print ("Temperature: %s" % CCData[2])
			Temperature = CCData[2]
			client.publish('CurrentCost/Temperature','{"state":"'+str(Temperature)+'","unit_of_measurement":"'+degree_sign+'"}', qos=0, retain=True)

		# If block for Appliance connected to channel #0
		if (CCData[0] == 0):
			if (Total != CCData[1]):
				print ("Total: %s" % CCData[1])
				Total = CCData[1]
				client.publish('CurrentCost/TTL_Power','{"state":"'+str(Total)+'","unit_of_measurement":"Wh"}', qos=0, retain=True)

		# If block for Appliance connected to channel #2
		if (CCData[0] == 2):
			if (Dehumidifier != CCData[1]):
				print ("Dehumidifier: %s" % CCData[1])
				Dehumidifier = CCData[1]
				client.publish('CurrentCost/Dehumidifier_Power','{"state":"'+str(Dehumidifier)+'","unit_of_measurement":"Wh"}', qos=0, retain=True)

		# Add if blocks here for other appliances, as shown on the CurrentCost display on the bottom right.
		# If adding a block add name under definitions and assign 0.1 to initialise
		# To see the acutal output, do not start this program and instead run below command in terminal, changin the ttyUSB with the one of the CurrentCost
		# minicom -D /dev/ttyUSB0 -b 57600
			
	except:
		pass

I have hopefully added enough comments in the scripts to help with the local settings, so be sure to read them :slight_smile:

Now add a shell_command to your configuration,yaml:

shell_command:
  getcurrentcost: /FULL_PATH_TO_FILE/CurrentCost.py

and add sensors to your configuration.yaml:

sensor:
  - platform: mqtt
    state_topic: "CurrentCost/Temperature"
    name: "CurrentCost_Temperature"
    unit_of_measurement: '°C'
    value_template: '{{ value_json.state }}'

  - platform: mqtt
    state_topic: "CurrentCost/TTL_Power"
    name: "CurrentCost_Power"
    unit_of_measurement: "Wh"
    value_template: '{{ value_json.state }}'

Add an automation so the script starts when HA starts:

automation:
  - alias: HASS Start CurrentCost
    trigger:
      - platform: homeassistant
        event: start
    action:
    - service: shell_command.getcurrentcost

That should be it
image

[EDIT] Amended code to make it python3 compatible.
If nothing gets displayed you may have to edit /usr/local/lib/python3.x/dist-packages/untangle.py (replace x with your python3 version):

  1. Replace parser.parse(StringIO(filename)) with parser.parse(StringIO(filename.decode('utf-8')))
  2. Replace return string.startswith('http://') or string.startswith('https://') with return string.startswith(b'http://') or string.startswith(b'https://')
5 Likes

this is excellent! thanks again heaps for sharing!

Do you need any special USB drivers for the Serial-To-USB cable?

I haven’t got my Pi get but trying to read in the CurrentCost data to my mac using this cable I’ve been getting gibberish. I’ve googled and noted there are drivers for this cable for windows/mac so I’ll try downloading these/running these, but I guess wondering if there’s a step for this with a Raspberry Pi?

Not that I can remember. At least not under Linux. If you do, you can get them from currentcost

which linux build did you use lolouk44 can I ask? Avoid hass.io for this perhaps?

I installed hass.io and then got stuck trying to use sudo and apt-get to install the Python virtual environment. I’m getting the impression hass.io is an applicance and I probably should go back and start again with Raspbian perhaps?

I use a regular Ubuntu 16.04 as I use my server for other tasks too.
Hass.io is good if you don’t want to tinker too much and just need HA.

@lolouk44 - forgot to come back and say thanks - got it works with a couple of days of your post/info :slight_smile: very cool to be able to leverage the currentcost monitors I’d already had set up!

1 Like

Would this script also work for the current cost IAM’s @lolouk44 ?

Yes just make sure you choose the channel the IAM is connected to (see my code I have an extra device)

Hi,

Is this process using 100% of CPU for anyone else?

Regards,
Andrea

If it is add import time at the beginning and below the while statement add time.sleep(0.1)

OK, working now! :slight_smile:
Let me explain what I did and my environment, so that hopefully will help someone else.

I’m running this on a raspberry pi 1 (model B), on Raspbian 9 (stretch).
Python is version 2.7.13.

To get the script going I had to install these python libraries and, first of all, pip itself:
apt-get install python-pip
pip install --user untangle serial requests simplejson paho-mqtt pyserial

“pyserial” was not obvious, as there was no apparent error thrown by the script, and it might have been the reason why it was using 100% CPU time.

At this stage I had the temperature sensor working fine, but not the total house power consumption (called TTL_Power in the script).
This is because that is provided (in my device) on channel #0 (not #1).
So I had to change “if (CDData[0] == 1):” to “if (CDData[0] == 0):” for the TTL_Power IF block.

Lastly, I’m running this as a cronjob:
*/5 * * * * /usr/bin/flock -w 1 /tmp/currentCost.lock /usr/bin/python /root/currentCost/CurrentCost.py

Hope it helps!
Andrea

2 Likes

Thanks, I’ve modified my original post to reflect this.

Hi - Super excited to see this - I copied all the files and created the sensors etc as per the instructions above.
If I run the script manually I get errors about the file not being found. I added python in front of the strings that called the .py scripts and got it to run from the command line, updating the sensor.
When I run it form HA (ie I restarted HA) nothing happens, digging into the logs I ended up with an error -15 when it tried to run, figured out that the VENV environment runs python3, made the changes, updated the code - the print command in CurrentCost.py need () around the print statement - fixed everything and got the code running with python3
I can now see the processes running but nothing back in HA, clearly I must be missing something very obvious - any pointers would be much appreciated.
Also not sure what code/scripts/logs would be helpful to post.

Thanks
Wayne

Does the script when ran manually indeed display the correct energy consumption?
If yes, above assume you a’ready have MQTT setup. Is this your case?

Yes - running manually I can see updates on the command line (Temp, Wh) and using MQTT.fx can see it hitting the mqtt server and consequently appears in HA

If I then restart HA, I don’t get anything on the mqtt server or in HA

I modified your scripts above:

# Kill reader program in case it's running since we're reading over a serial port
cmd1 = 'echo secretpassword | sudo -S pkill -9 -f CurrentCostReader'
try:
	ps = subprocess.Popen(cmd1, shell=True)
except:
	pass
time.sleep(1)
cmd2 = 'echo secretpassword | sudo -S python3 /home/homeassistant/.homeassistant/shell_scripts/CurrentCostReader.py'
print("Starting CurrentCostReader...")
ps = subprocess.Popen(cmd2, shell=True)
# Ensures main reader program gets killed if main program stops
def Goodbye():
	print ("Killing CurrentCostReader...")
	try:
		ps = subprocess.Popen(cmd1, shell=True)
	except:
		pass 

I made changes adding ‘python3’ in front of the cmd2 line, and brackets around the print statement to get it to work with python3

Have also added python3 to shell_command

shell_command:
  getcurrentcost: python3 /home/homeassistant/.homeassistant/shell_scripts/CurrentCost.py

FYI I’m running HASS 0.69.1

Wayne

what is your MQTT Client? Is it HA’s own or a separate one like mosquitto?
If HA, I’m guessing the script attempts to connect to the MQTT Server but it may not yet be up and so it fails altogether? (I have to admit I’ve not covered this eventuality). If so maybe delay the script by several seconds after HA start to see if that fixes it?
You could also try and replace client.loop_start() with client.loop_forever() to see if it works better?

It’s a seperate mosquitto server that’s always up - not on the HA server

Not that I really know, but it seems like when it’s running from within HA environment it doesn’t have the correct permissions

Is there any logging/debugging I can turn on in the script to see what it’s doing or not as the case may be

(I’ll try adding the code change as well)

// I can’t seem to find “client.loop_start()” this anywhere in the code.

Add it below client.connect:

client = mqtt.Client("CurrentCost") # must be unique on MQTT network
client.username_pw_set(str(MQTT_User),str(MQTT_Password))
client.connect(MQTT_Host, port=str(MQTT_Port), keepalive=60)
client.loop_start()

client.loop_start() handles reconnection and might be what you need.
I’ve not added debugging to my script apart from the print functions already in place.

I get the following error, running from the command line:

Starting CurrentCostReader...
Traceback (most recent call last):
  File "/home/homeassistant/.homeassistant/shell_scripts/CurrentCostReader.py", line 46, in <module>
    client.connect(MQTT_Host, port=str(MQTT_Port), keepalive=60)
  File "/usr/local/lib/python3.6/site-packages/paho/mqtt/client.py", line 767, in connect
    self.connect_async(host, port, keepalive, bind_address)
  File "/usr/local/lib/python3.6/site-packages/paho/mqtt/client.py", line 823, in connect_async
    if port <= 0:
TypeError: '<=' not supported between instances of 'str' and 'int'
^CTraceback (most recent call last):
  File "/home/homeassistant/.homeassistant/shell_scripts/CurrentCost.py", line 32, in <module>
    time.sleep(1)
KeyboardInterrupt
Killing CurrentCostReader...

if I change client.connect(MQTT_Host, port=str(MQTT_Port), keepalive=60) to client.connect(MQTT_Host, port=int(MQTT_Port), keepalive=60)
It works on the command line, still not in HA

ok looks like python 3.6 handles differently.
You can try and replace MQTT_Port = "INSERT_PORT_HERE" with MQTT_Port = INSERT_PORT_HERE
I don’t understand why it would not work in HA, especially if you have already confirmed that you see the messages on your broker via mqtt/fx
Do remember however that the script needs to run as root/su.
I personally don’t start it from HA anymore since I’ve moved to docker. I start the script manually, but in the process or moving it to a crontab…

1 Like