Since I've installed my RPi driving four USB Erupters, I thought I needed a web interface for it. Looking at available projects, I didn't found anything I liked. So I decided to configure my own portal with Wordpress. This is the only first step in integrating CGMiner stats with it but I think is already usable (even if it's really simple).
This is the result (look at the bottom left):
https://i.imgur.com/zTJv3ZJ.png
This is the configuration panel visible from Wordpress Dashboard where you can configure IP and Port of your miner:
https://i.imgur.com/9dMKTWj.png
And this is the widget source code:
* Plugin Name: Miner Widget
* Description: A widget that displays CGMiner stats
* Version: 0.1
* Author: dna2
*/
ini_set('display_errors', 'On');
add_action( 'widgets_init', 'register_miner_widget' );
function register_miner_widget() {
register_widget( 'MinerWidget' );
}
class MinerWidget extends WP_Widget {
function MinerWidget() {
$widget_ops = array( 'classname' => 'example', 'description' => __('A widget that displays statistics from CGMiner ', 'example') );
$control_ops = array( 'id_base' => 'miner-widget' );
$this->WP_Widget( 'miner-widget', __('Miner Widget', 'example'), $widget_ops, $control_ops );
}
function widget( $args, $instance ) {
extract( $args );
$title = apply_filters('widget_title', $instance['title'] );
$ip = $instance['ip'];
$port = $instance['port'];
$show_info = isset( $instance['show_info'] ) ? $instance['show_info'] : false;
echo $before_widget;
if ( $title )
echo $before_title . $title . $after_title;
$data = $this->requestMiner($ip, $port, 'summary');
if ( $data )
{
//printf( 'Version: ' . $data['STATUS']['Description'] . '
');
printf( 'Uptime: ' . $this->formatTime($data['SUMMARY']['Elapsed']) . '
');
printf( 'MHS av: ' . $this->formatNumber($data['SUMMARY']['MHS av']) . '
');
printf( 'Found Blocks: ' . $data['SUMMARY']['Found Blocks'] . '
');
//printf( 'Getworks: ' . $this->formatNumber($data['SUMMARY']['Getworks']) . '
');
printf( 'Accepted: ' . $this->formatNumber($data['SUMMARY']['Accepted']) . '
' );
printf( 'Rejected: ' . $this->formatNumber($data['SUMMARY']['Rejected']) . '
' );
printf( 'Hardware Errors: ' . $this->formatNumber($data['SUMMARY']['Hardware Errors']) . '
' );
//printf( 'Utility: ' . $data['SUMMARY']['Utility'] . '
' );
//printf( 'Discarded: ' . $this->formatNumber($data['SUMMARY']['Discarded']) . '
' );
//printf( 'Stale: ' . $this->formatNumber($data['SUMMARY']['Stale']) . '
' );
//printf( 'Get Failures: ' . $this->formatNumber($data['SUMMARY']['Get Failures']) . '
' );
//printf( 'Local Work: ' . $this->formatNumber($data['SUMMARY']['Local Work']) . '
' );
//printf( 'Remote Failures: ' . $this->formatNumber($data['SUMMARY']['Remote Failures']) . '
' );
//printf( 'Network Blocks: ' . $this->formatNumber($data['SUMMARY']['Network Blocks']) . '
' );
//printf( 'Total MH: ' . $this->formatNumber($data['SUMMARY']['Total MH']) . '
' );
//printf( 'Work Utility: ' . $data['SUMMARY']['Work Utility'] . '
' );
//printf( 'Diff. Accepted: ' . $data['SUMMARY']['Difficulty Accepted'] . '
' );
//printf( 'Diff. Rejected: ' . $data['SUMMARY']['Difficulty Rejected'] . '
' );
//printf( 'Diff. Stale: ' . $data['SUMMARY']['Difficulty Stale'] . '
' );
printf( 'Best Share: ' . $this->formatNumber($data['SUMMARY']['Best Share']) . '
' );
}
//if ( $show_info )
// printf( $ip );
echo $after_widget;
}
private function requestMiner($ip, $port, $cmd)
{
try
{
$socket = $this->getsock($ip, $port);
if ($socket != null)
{
socket_write($socket, $cmd, strlen($cmd));
$line = $this->readsockline($socket);
socket_close($socket);
if (strlen($line) == 0)
{
//echo "WARN: '$cmd' returned nothing\n";
return $line;
}
//print "$cmd returned '$line'\n";
if (substr($line,0,1) == '{')
return json_decode($line, true);
$data = array();
$objs = explode('|', $line);
foreach ($objs as $obj)
{
if (strlen($obj) > 0)
{
$items = explode(',', $obj);
$item = $items[0];
$id = explode('=', $items[0], 2);
if (count($id) == 1 or !ctype_digit($id[1]))
$name = $id[0];
else
$name = $id[0].$id[1];
if (strlen($name) == 0)
$name = 'null';
if (isset($data[$name]))
{
$num = 1;
while (isset($data[$name.$num]))
$num++;
$name .= $num;
}
$counter = 0;
foreach ($items as $item)
{
$id = explode('=', $item, 2);
if (count($id) == 2)
$data[$name][$id[0]] = $id[1];
else
{
$data[$name][$counter] = $id[0];
}
$counter++;
}
}
}
return $data;
}
return null;
}
catch (Exception $e)
{
return $e->getMessage();
}
}
private function getsock($addr, $port)
{
$socket = null;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false || $socket === null)
{
$error = socket_strerror(socket_last_error());
$msg = "socket create(TCP) failed";
echo "ERR: $msg '$error'\n";
return null;
}
$res = socket_connect($socket, $addr, $port);
if ($res === false)
{
$error = socket_strerror(socket_last_error());
$msg = "socket connect($addr,$port) failed";
echo "ERR: $msg '$error'\n";
socket_close($socket);
return null;
}
return $socket;
}
private function readsockline($socket)
{
$line = '';
while (true)
{
$byte = socket_read($socket, 1);
if ($byte === false || $byte === '')
break;
if ($byte == "\0")
break;
$line .= $byte;
}
return $line;
}
private function formatTime($value)
{
$s = $value % 60;
$value -= $s;
$value /= 60;
if ($value == 0)
$ret = $s.'s';
else
{
$m = $value % 60;
$value -= $m;
$value /= 60;
if ($value == 0)
$ret = sprintf("%dm $b%02ds", $m, $s);
else
{
$h = $value % 24;
$value -= $h;
$value /= 24;
if ($value == 0)
$ret = sprintf("%dh $b%02dm $b%02ds", $h, $m, $s);
else
{
if ($value == 1)
$days = '';
else
$days = 's';
$ret = sprintf("%dday$days, $b%02dh $b%02dm $b%02ds", $value, $h, $m, $s);
}
}
}
return $ret;
}
private function formatNumber($value)
{
$parts = explode('.', $value, 2);
if (count($parts) == 1)
$dec = '';
else
$dec = '.'.$this->endzero($parts[1]);
$ret = number_format((float)$parts[0]).$dec;
return $ret;
}
private function endzero($num)
{
$rep = preg_replace('/0*$/', '', $num);
if ($rep === '')
$rep = '0';
return $rep;
}
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = strip_tags( $new_instance['title'] );
$instance['ip'] = strip_tags( $new_instance['ip'] );
$instance['port'] = strip_tags( $new_instance['port'] );
$instance['show_info'] = $new_instance['show_info'];
return $instance;
}
function form( $instance ) {
$defaults = array( 'title' => __('My Miner', 'example'), 'ip' => __('127.0.0.1', 'example'), 'port' => 4028, 'show_info' => true );
$instance = wp_parse_args( (array) $instance, $defaults );
?>
echo $this->get_field_id( 'title' ); ?>" name="echo $this->get_field_name( 'title' ); ?>" value="echo $instance['title']; ?>" style="width:100%;" />
echo $this->get_field_id( 'ip' ); ?>" name="echo $this->get_field_name( 'ip' ); ?>" value="echo $instance['ip']; ?>" style="width:100%;" />
echo $this->get_field_id( 'port' ); ?>" name="echo $this->get_field_name( 'port' ); ?>" value="echo $instance['port']; ?>" style="width:100%;" />
( $instance['show_info'], true ); ?> id="echo $this->get_field_id( 'show_info' ); ?>" name="echo $this->get_field_name( 'show_info' ); ?>" />
}
}
?>
Installation:
- Copy & Paste source code into a new file MinerWidget.php
- Place the file inside your WordPress installation folder under WORDPRESS_ROOT/wp-content/plugins/MinerWidget/
- Enable the widget from wordpress Plugins page, available in Dashboard
- Place and configure the widget from the widget page wherever you want
Enjoy it