Bitcoins have always been something I have been interested in, and even after last years bubble, Bitcoin prices are still well above pre-bubble prices. The middle of the bubble also coincided with the announcement of several companies building ASICs hardware purposed for mining, which would be both faster and more energy efficient than a computer using 4 high powered GPUs which was the current standard.

Bitcoin Prices over Time Google Finance Chart

I purchased 2 KNC Neptune ASIC Bitcoin miners, and recently the 2nd one arrived and when setting it up I noticed there was new firmware available. For whatever reason after loading the newest firmware on my original miner, sometimes it will stop hashing and sit idle for a day or so until I happen to check and see it isn't working.

My miners are both part of the pool, who provide a handy API for their site. I wrote a quick Python script to using the API to find out when either of my miners stopped working and send an email alert.

Here is the link to the Github repo

import cexapi
import smtplib
from email.mime.multipart import MIMEMultipart
from email.MIMEText import MIMEText
from config import config
import json2html
import time

username = config['username']
api_key = config['key']
api_secrect = config['secret']

alert_addresses = config['alert_addresses']
sender = config['sender_address']
smtp_server = config['smtp_server']
workers = config['workers']


The first part of the script loads the modules and the configuration from the file. I used Gerland Anderson's fork of since his pull request, which adds support for pulling the workers statistics, has not been accepted into the original repository.

def get_stats():
	ghash = cexapi.api(username, api_key, api_secrect)
	stats = ghash.workers()
    return stats

def check_stats(stats, workers):
    bad_workers = []
    for worker in workers:
		if stats[worker]['last15m'] < 1500000:

	if len(bad_workers) > 0:
		return bad_workers

The first two functions pull the worker stats from's API and then check if the the hashing has fallen below the set threshold, which then returns a list with the names of workers that are below the threshold.

def send_alert(recipient, sender, alert_for, stats, table):
	message = MIMEMultipart('related')
	message['Subject'] = 'Miner Alert!'
	message['From'] = sender
	message['To'] = COMMASPACE.join(recipient)
	message.preamble = 'This is a multi-part message in MIME format.'

	message_alternative = MIMEMultipart('alternative')

	message_text = MIMEText('{0} may be having issues. GHash is reporting low hash rates\n Ghash stats are below\n {1}'.format(alert_for, stats))

	message_text = MIMEText('<h2>{0} may be having issues.</h2><br>GHash is reporting low hash rates<br>Ghash stats are below<br>{1}'.format(alert_for, table))

	s = smtplib.SMTP(smtp_server)
	s.sendmail(sender, recipient, message.as_string())

def make_table(json):
	table = json2html.json2html.convert(json = json)

	return table    

The send_alert function generates a MIME format message with an HTML body and plain text alternative with the worker statistics and sends the email to the defined recpients.

make_table uses json2html to convert all the JSON formatted stats into a HTML table for the email.

while True:


	stats = get_stats()

	bad_workers = check_stats(stats, workers)
	if bad_workers:
		table = make_table(stats)
		send_alert(alert_addresses, sender, bad_workers, stats, table)


The main loop waits 15 minutes and then gets the worker stats and checks them against the hashing threshold, if any workers are returned in the bad_workers list then an alert email is generated and sent and the script waits an hour before checking again. Otherwise, the script goes back to the beginning of the loop.

Below is an example of the HTML email body with the worker names removed.

Example Alert Email

The miners themselves have a web interface and SSH server so I might add on to this later have it restart the miner automatically since that is what I do manually.