Serverless Hot Water

In another post in the series “that’s niche even for you” this is how to get an alert about the performance of solar hot water production with the MyEnergi Eddi diverter.

We have an unvented hot water cylinder which like many British homes is connected to a gas boiler, which heats the water with a big coil inside the cylinder. Our cylinder also has an electric immersion heater which would not ordinarily be used but is there as a backup I guess.

Except now it is. We also have solar PV which is where the Eddi diverter comes in. It tracks house usage and if the PV is generating more than the house is consuming it switches on the electric hot water immersion heater. It’s slightly smarter than that, but anyway.

For us we need to put in about 6kWh of energy (gas or electric) a day, which in the summer is no problemo, but as the days shorten we need to start thinking of supplementing it with gas. The MyEnergi app is fine but checking it becomes a chore, so we need a way to send an alert if we didn’t make enough hot water today.

Onto the idiocy: this is all driven from AWS Lambda, and invoked on a schedule by an Eventbridge rule. SES is used to send the mail so make sure to validate both the sender and recipient first (and update any SPF rules).

Create a Python 3.7 function – 3.7 still has the requests library which handles the Digest authentication method that MyEnergi inexplicably uses.

from botocore.vendored import requests
import json
import urllib
import boto3
import time
from botocore.exceptions import ClientError



def send_notification(kwh):
	print("Not enough hot water")
	
	# Replace sender@example.com with your "From" address.
	# This address must be verified with Amazon SES.
	SENDER = "AWS Automation <email@address>"

	# Replace recipient@example.com with a "To" address. If your account 
	# is still in the sandbox, this address must be verified.
	RECIPIENT = "email@address"

	# If necessary, replace with the AWS Region you're using for Amazon SES.
	AWS_REGION = "eu-west-1"

	# The subject line for the email.
	SUBJECT = "Check the hot water"
	
	plaintext = 'Only {} kWh hot water made today\r\n'.format(kwh)
	htmltext = '<html><head></head><body><h1>Only {} kWh hot water made today</h1></body></html>'.format(kwh)
	
	# The email body for recipients with non-HTML email clients.
	BODY_TEXT = plaintext
			
	# The HTML body of the email.
	BODY_HTML = htmltext          

	# The character encoding for the email.
	CHARSET = "UTF-8"

	# Create a new SES resource and specify a region.
	client = boto3.client('ses',region_name=AWS_REGION)

	# Try to send the email.
	try:
		#Provide the contents of the email.
		response = client.send_email(
			Destination={
				'ToAddresses': [
					RECIPIENT,
				],
			},
			Message={
				'Body': {
					'Html': {
						'Charset': CHARSET,
						'Data': BODY_HTML,
					},
					'Text': {
						'Charset': CHARSET,
						'Data': BODY_TEXT,
					},
				},
				'Subject': {
					'Charset': CHARSET,
					'Data': SUBJECT,
				},
			},
			Source=SENDER,

		)
	# Display an error if something goes wrong.	
	except ClientError as e:
		print(e.response['Error']['Message'])
	else:
		print("Email sent! Message ID:"),
		print(response['MessageId'])



def lambda_handler(event, context):

    from requests.auth import HTTPDigestAuth
    #The username is your Hub ID, the password is your API key
    auth = HTTPDigestAuth('12345', 'APIkey')
    
    #Replace the Exxxxxx value with the serial of your Eddi. I think you are supposed to dynamically fetch the server name from director but YOLO

    results = requests.get('https://s18.myenergi.net/cgi-jstatus-Exxxxxx', auth=auth)
    
    parsed = json.loads(results.text)
    
    hot_water_kwh = parsed["eddi"][0]["che"]
    
    if hot_water_kwh < 6:
    	send_notification(hot_water_kwh)