Hyperion LED’s & Nagios (Part 1)

Part 1 is more Background story on my use of WS2801 WS2812b and Hyperion with the Raspberry Pi. Skip to Part 2 for the techy bit.

I’ve been using Hyperion for a while. I setup light behind the TV first off (using sticky tape) WS2801 and RaspBMC. This work brilliantly and I loved it. Spent hours tweaking the config so the LED’s were picking up the correct colour to the screen.

With all that working and a length of LED’s left over I decided to run some up the stairs. They sit just under the banister lip so you can’t see them, just the light on the stairs. I set these to Rainbow swirl and let it. They’re been running for months, occasionally changing the effect to show off what they can do.

Then disaster struck, the power adapter stopped working. Have to be honest I didn’t really notice until I was going to bed at 2am and almost fell over. They’ve been there giving off light (possibly a bit bright if anything) and I just got used to being able to see in the middle of the night without any other lights.

Anyway I digress, ordering a new power adapter I went searching for more LED’s (yes you can’t have enough of them once you’ve been playing). I decided that I’d really like to run some in my bedroom, the effects are cool and there would be plenty of light I wont need to use the main light with them on.

So I looking at where I originally bought my WS2801’s and nothing 🙁 so off to google, the obvious thing was I was going to have a hard time sourcing them in the UK. but why they’re great. So off I went to the hyperion git site for info and found there’s newer versions WS2811 and WS2812b. ah that may help, another search and I found someone selling a load on ebay. So I bought all he had 3xreels of WS1812b’s.

They turned up and I connected them up to try them as directed by hyperion. It was at this point I read the important bits RPi2 isn’t working yet and there maybe a problem with the PI communicating with them due to the voltage. I really thought I was going to have to make another little circuit to (buffer?) get them working. As a last ditched attempt it was mentioned try removing the resistor and try running them direct from python. I did both at the same time (not the best decision for troubleshooting. But to my surprise they worked.

So I killed the python program and restart hyperion, yep they’re working.

So off I went to stick them to the ceiling (they have sticky tape on the back). Done. If only I’d thought about connecting them before I stuck them up. I now had to work up in the air joining the cables. Not to worry I’ve done worse.

So I go to get what I need, by the time I got back up they’ve come down 🙁 bloody gravity! Now you’d think at this point I’d connect them up and sort out attaching them later Nope! (didn’t even enter my head) I was now on track to get them to stay up. Enter ‘SuperGlue’, applied little dots along the strip and stuck them up (yes I glued my fingers to the ceiling too). Finally they’re up and staying there. Oh I should have connected them when they were down!

‘Bugger it, where’s my screw driver’ I connected them up, put a power connector on the end and powered them and the PI.

Then installed hyperion on yet another pi. and it all worked like magic.

Have a look at the video, there’s no light other than the TV and it’s dark outside, but the room is really bright.

[youtube=https://www.youtube.com/watch?v=khfJW3vXcCE]

Click here for Part 2

Weewx+Raspberry PI+HDMI+PyGame

I’ve been using wview with my WH1080 weather station for some time (actually 2 of them). My main setup has been using my server, and every now and again the WH1080 would seem to lock up and nothing could get data out of it. The solution was to drop it’s power, on reboot it would all start working again.

However wview also seemed to introduce lockups of it’s own and the only solution there was to reboot the server (not ideal). So when it came to setting up a second weather station (in a remote location) I needed something a bit more stable and started looking at alternatives. I was doing this on a Raspberry PI and found wview. After installing it sometime last year it seemed pretty stable (although the WH1080 still manages to lockup).

Back to my house and I’ve finally had enough of missing weather data. One thing I really liked with wview was the ability to pull the archive data if the weather station had been running when the machine hadn’t (providing the USB hadn’t locked up), I really recommend looking at wview if your starting out.

I’ve already covered setting up weewx on a Raspberry PI and I’m not going to post about my exact configuration here. Instead I’m going to share my Python code for displaying the various graphs and gauges straight to the TV. At the moment I have my weather PI connected to the TV via HDMI. This may change later and then I’ll have to adjust the code to pull the images to another pi before displaying them (I have a similar project for displaying webcams already).

So a few things before the code.

  1. It’s my first real attempt at using classes, so my code will be more than a little scattered.
  2. You need to have python, pygame, wview, (and for this exact code Bootstrap for wview but you could just change the file paths to the Standard guages and graphs), mysql and wview configured for mysql.
  3. I have this started using an init script (added below).
  4. This runs the python program as root, I need to find a way to run this as a normal user (but that will affect point 5&6).
  5. This program checks mysql is able to be connected to and restarts mysql of not.
  6. It also checks the freshness of the index.html file (not the best way but a quick way) to make sure wview is running keeping the files upto date. If not it reboot the PI, this causes the weather station to reboot so if the USB locks up the whole system resets fixing it.

So now onto the python code

#!/usr/bin/python
import os
import time
import pygame
import MySQLdb as mdb
import signal
import sys

imglocation = "/var/www/weewx/Bootstrap"

class pyscreen :
   screen = None;

   def __init__(self):
       "Initializes a new pygame screen using the framebuffer"
       disp_no = os.getenv("DISPLAY")
       if disp_no:
           print "I'm running under X display = {0}".format(disp_no)

       # Check which framebuffer drivers are available.
       drivers = ['fbcon', 'directfb', 'svgalib']
       found = False
       for driver in drivers:
           # Make sure that SDL_VIDEODRIVER is set
           if not os.getenv('SDL_VIDEODRIVER'):
               os.putenv('SDL_VIDEODRIVER', driver)
           try:
               pygame.display.init()
           except pygame.error:
               print 'Driver: {0} failed.'.format(driver)
               continue
           found = True
           break

       if not found:
           raise Exception('No suitable video driver found!')

       size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
       print "Framebuffer size: %d x %d" % (size[0], size[1])
       self.screen = pygame.display.set_mode(size, pygame.FULLSCREEN)
       pygame.mouse.set_visible(False)
       # Clear the screen to start
       self.screen.fill((0, 0, 0))
       # Initialise font support
       pygame.font.init()
       # Render the screen
       pygame.display.update()

   def __del__(self):
       "Destructor to make sure pygame shuts down, etc."

   def size(self):
       size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
       return size

   def fill(self, colour):
       if self.screen.get_at((0,0)) != colour:
           self.screen.fill((0, 0, 0))
           self.screen.fill(colour)
           pygame.display.update()

   def image(self, img, locX, locY, sizX, sizY):
       try:
           if (( "week" not in img) and (os.stat(img).st_mtime > time.time() - 600)):
               # 600 = 10 mins.
               image = pygame.image.load(img)
               image = pygame.transform.scale(image, (sizX, sizY))
               self.screen.blit(image, (locX, locY))
           elif(( "week" in img) and (os.stat(img).st_mtime > time.time() - 7200)):
               # 7200 = 2 hours.
               image = pygame.image.load(img)
               image = pygame.transform.scale(image, (sizX, sizY))
               self.screen.blit(image, (locX, locY))
           else:
               pygame.draw.rect(self.screen, (255, 0, 0), (locX, locY, sizX, sizY), 0)
       except pygame.error, message:
           pygame.draw.rect(self.screen, (255, 0, 0), (locX, locY, sizX, sizY), 0)
       pygame.display.update()

   def text_object(self, msg, font):
       black = (0, 0, 0)
       textSurface = font.render(msg, True, black)
       return textSurface, textSurface.get_rect()

   def error(self, msg):
       largeText = pygame.font.Font('freesansbold.ttf', 115)
       TextSurf, TextRect = screeny.text_object(msg, largeText)
       size = screeny.size
       TextRect.center = ((pygame.display.Info().current_w/2),(pygame.display.Info().current_h/2))
       self.screen.blit(TextSurf, TextRect)

       pygame.display.update()

class fileman:
   global imglocation

   def __init__(self):
       "Init for fileman. Nothing to do atm."

   def __del__(self):
       "Destructor for fileman. Nothing to do again."

   def total_files(self):
       count=0
       for file in os.listdir(imglocation):
           if file.endswith(".png"):
               count=count+1
       return count

class sqly:
   def __init__(self):
       "do nothing"

   def test(self):
       try:
           con = mdb.connect('localhost', 'weewx', 'weewx', 'weather')

           cur = con.cursor()
           cur.execute("SELECT VERSION()")
           ver = cur.fetchone()
           return 0
       except mdb.Error, e:
           return 1

       finally:
            "do nothing"
   def restart(self):
       #wait 30 secs and try the connection again.
       time.sleep(30)
       if not mysql_con.test():
           try:
               os.system("service mysql start")
           except:
               "do nothing"

def sigterm_handler(_signo, _stack_frame):
    "When sysvinit send the TERM signal, cleanup before exiting"
    print("[" + get_now() + "] received signal {}, exiting...".format(_signo))
    sys.exit(0)

signal.signal(signal.SIGTERM, sigterm_handler)

def reboot():
    "check if we've been reboot in the last 30 mins"
    uptimef = open("/proc/uptime", "r")
    uptimestr = uptimef.read()
    uptimelst = uptimestr.split()
    uptimef.close()

    if float(uptimelst[0]) < 1800:
        "We've reboot in the last 30 mins, ignoring"
    else:
        try:
            os.system("reboot")
        except:
            "do nothing"


if __name__ == "__main__":
    try:
        screeny = pyscreen()
        filey = fileman()
        screeny.fill((0, 0, 255))
        size = screeny.size()
        border = 7
        sizewquarter = ((size[0]-(border*5))/4)
        sizehthird = ((size[1]-(border*4))/3)

        print("Total files : %d" % (filey.total_files()))
        while 1:
            mysql_con = sqly()
            if mysql_con.test():
                screeny.fill((250, 0, 0))
                screeny.error("Database Offline. Restarting!")
                mysql_con.restart()
            elif (os.stat("/var/www/weewx/Bootstrap/index.html").st_mtime < time.time() - 600):
                screeny.fill((254, 0, 0))
                screeny.error("NOT Updating. Reboot Imminent!")
                reboot()
            else:
                screeny.fill((0, 0, 255))
                screeny.image("/var/www/weewx/Bootstrap/barometerGauge.png", (border*1), (border*1), sizewquarter, sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/outTempGauge.png", ((border*2)+(sizewquarter*1)), (border*1), sizewquarter, sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/windDirGauge.png", ((border*3)+(sizewquarter*2)), (border*1), sizewquarter, sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/windSpeedGauge.png", ((border*4)+(sizewquarter*3)), (border*1), sizewquarter, sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/big_images/weekbarometer-Bootstrap.png", (border*1), ((border*2)+(sizehthird*1)), ((sizewquarter*2)+(border*1)), sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/big_images/weektempchill-Bootstrap.png", (border*1), ((border*3)+(sizehthird*2)), ((sizewquarter*2)+(border*1)), sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/big_images/weekwinddir-Bootstrap.png", ((border*3)+(sizewquarter*2)), ((border*2)+(sizehthird*1)), ((sizewquarter*2)+(border*1)), sizehthird)
                screeny.image("/var/www/weewx/Bootstrap/big_images/weekwind-Bootstrap.png", ((border*3)+(sizewquarter*2)), ((border*3)+(sizehthird*2)), ((sizewquarter*2)+(border*1)), sizehthird)
            time.sleep(1)
    except KeyboardInterrupt:
        "We've got an interupt"

and now the init.d code

#!/bin/sh
#
# init script for displayweewx
#

### BEGIN INIT INFO
# Provides:          displayweewx
# Required-Start:    $syslog $network
# Required-Stop:     $syslog $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: init script to display weewx charts via HDMI output
# Description:       The python script queries mysql and file ages, so does not rely on mysql as a backup way to kick it.
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NAME=displayweewx
DAEMON=/root/displayweewx/main.py
DAEMONARGS=""
PIDFILE=/var/run/$NAME.pid
LOGFILE=/var/log/$NAME.log

. /lib/lsb/init-functions

test -f $DAEMON || exit 0

case "$1" in
    start)
        start-stop-daemon --start --background \
            --pidfile $PIDFILE --make-pidfile --startas /bin/bash \
            -- -c "exec stdbuf -oL -eL $DAEMON $DAEMONARGS > $LOGFILE 2>&1"
        log_end_msg $?
        ;;
    stop)
        start-stop-daemon --stop --pidfile $PIDFILE
        log_end_msg $?
        rm -f $PIDFILE
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    status)
        start-stop-daemon --status --pidfile $PIDFILE
        log_end_msg $?
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 2
        ;;
esac

exit 0

After all of this we get the following on the TV

From far far away.
From far far away.

Todays WordPress Adventures

Well it had to happen at some point, today I had a nice email from DigitalOcean saying they’ve disabled networking on one of my servers. This was because it’s ip address had been reported to RBL’s by several other servers.

Looking at the logs they included I was beating the s**t out of others wp-admin login pages. Now I know I wasn’t doing it personally, it was the first time in a long time I was in bed early and this seemed to start at 2am.

Luckily I could access my Droplet using the Console page, so after login I sat thinking ‘um…..’ where exactly do you start. The server normally has quite a bit of traffic so the logs are always cluttered. Needle in a haystack springs to mind.

I decided to run htop and see if the server was doing much without any traffic coming in. Oh yes /usr/bin/host is eating resources. So do I kill it or not. I decided not to at this point. Without networking I’m not doing anymore harm, and leaving it running may help find out what’s calling it.

It was a good call. I can’t give details of everything I did, I spent a hour hours checking through stuff. I do remember checking lsof and finding a link between a process id for host and a file within wpallimport’s uploads directory. So I had a look in there, followed by some further searching of google. 1 file in particular .sd0 seems to bring back results and this seems to be what’s caused it.

To get my server running again, I disabled the entire site within apache that was affected (luckily not a major site) and reboot the server. Once I was happy there were no cronjobs or anything calling on this script I mailed DigitalOcean and asked them to re-enable networking. They’re pretty speedy and within 15 mins had done it. A further reboot and my servers back online minus the one site I’ve disabled.

I expect the cleanup for this is going to take weeks of checking files, against backups while keeping as much as possible online.

I’m pretty confident I know what’s caused it, an out of date wordpress install with an out of date wpallimport install. It really goes to show that you have to check old stuff and keep it upto date.

The most annoying thing for me is that WordPress has a multisite option (which I use on 2 installs) and this allows me to keep plugins and everything upto date easily of sub-sites that are barely used. but it doesn’t extend to multiple domains which would really allow wordpress to be used across all my domains from one central console and then everything would be kept upto date in one go.

I know there’s a plugin for multisite domains, but I feel this is more of a hack of the wordpress system rather than wordpress properly designed to function with this in mind. I don’t want to install it and encounter many more problems.

It’s very bad admin on my part not having kept this site upto date, I’ll be the first to admit that but it’s easy to forget about installs you don’t use regularly. There must be some kind of nagios plugin to alert me to out of date plugins/versions for wordpress so I’ll be looking for that later in the week 🙂

WooCommerce New Bulk Action Part 2

Ok so in Part 1 I said I wasn’t going to use the email function so removed it from the code I used from http://www.niepes.com/web/how-to-create-a-custom-order-status-in-woocommerce/

Clearly I need to think ahead a bit more. Over the last few weeks I’ve had a ton of sales, and I’m preparing everything to be ready to send them out as soon as I receive them from the supplier. Unfortunately our supplier contact me and advised there’s a few days delay on one product and about 10 days delay on another.

So I now need to mass mail people telling them.

I did look at using elasticemail to mass mail, but there’s a whole thing in their templates gearing towards marketing mails and opt out information. This isn’t a marketing mail it’s part of the transaction, I could live with giving it some opt out links. but what I really need is to be able to mail merge the order information into the mail.

So I quickly gave up on using elastcmail’s campaign function. I still use them as my mail relay and they’ve been very good, and support has been top notch for the few questions I’ve had.

Anyway, I thought back to the code for adding bulk actions and remembered there was an email function. So I checked out the code again, and yep it should be able to do what I need.

I decided to do things a little differently though.

First I create 2 new order status using JetPack (you can code this but it’s easier to just type and submit in jetpack) :
stock-emailed
stock-email-fail

I’m going to be bulk assigning every order I want emailed to stock-email, anything that fails (wp-mail does fail) will go to stock-email-fail. This is the bit that wasn’t in the orginal code.

I also want to pass the orderid and name into the mail function. I could just have the mail function go get them, but it already queries the email address in the change_order_status function so I may as well keep it there.

Here’s my new Code.

//added for woocommerce bulk actions.
add_action('admin_footer-edit.php', 'custom_bulk_admin_footer');
function custom_bulk_admin_footer() {
 global $post_type;
 if($post_type == 'shop_order') {
 ?>
 <script type="text/javascript">
 jQuery(document).ready(function() {
 //printing status
 jQuery('<option>').val('printing').text('<?php _e('Mark as Printing')?>').appendTo("select[name='action']");
 jQuery('<option>').val('printing').text('<?php _e('Mark as Printing')?>').appendTo("select[name='action2']");

 //stock status
 jQuery('<option>').val('stock').text('<?php _e('Mark as Awaiting Stock')?>').appendTo("select[name='action']");
 jQuery('<option>').val('stock').text('<?php _e('Mark as Awaiting Stock')?>').appendTo("select[name='action2']");

 //stock status
 jQuery('<option>').val('stock-emailed').text('<?php _e('Mark as Awaiting Stock Emailed')?>').appendTo("select[name='action']");
 jQuery('<option>').val('stock-emailed').text('<?php _e('Mark as Awaiting Stock Emailed')?>').appendTo("select[name='action2']");
 });
 </script>
 <?php
 }
}
add_action('load-edit.php', 'custom_bulk_action');
function custom_bulk_action() {
 global $typenow;
 $post_type = $typenow;

 if($post_type == 'shop_order') {
 $wp_list_table = _get_list_table('WP_Posts_List_Table');
 $action = $wp_list_table->current_action();
 $allowed_actions = array("stock","printing","stock-emailed");
 if(!in_array($action, $allowed_actions)) return;

 if(isset($_REQUEST['post'])) {
 $orderids = array_map('intval', $_REQUEST['post']);
 }

 switch($action) {
 case "printing":
 foreach( $orderids as $orderis ) {
 change_order_status($orderid, $action);
 }
 case "stock":
 foreach( $orderids as $orderid ) {
 change_order_status($orderid, $action);
 }
 case "stock-emailed":
 foreach( $orderids as $orderid ) {
 set_time_limit(0);
 change_order_status($orderid, $action);
 }
 break;
 default: return;
 }

 $sendback = admin_url( "edit.php?post_type=$post_type&success=1" );
 wp_redirect($sendback);
 exit();
 }
}
function change_order_status($orderid, $action) {
 $order = new WC_Order($orderid);
 if(($action=='printing') && ($order->status!='printing')) {
 $order->update_status('printing', '');
 }
 if(($action=='stock') && ($order->status!='stock')) {
 $order->update_status('stock', '');
 }
 if(($action=='stock-emailed') && ($order->status!='stock-emailed')) {
 $email = get_post_meta( $orderid, '_billing_email' )[0];
 $name = get_post_meta( $orderid, '_billing_first_name')[0];
 $name = ucwords($name);
 if (send_this($email,$orderid,$name)) {
 $order->update_status('stock-emailed', '');
 } else {
 $order->update_status('stock-email-fail', '');
 }
 }
}
add_action('admin_notices', 'custom_bulk_admin_notices');
function custom_bulk_admin_notices() {
 global $post_type, $pagenow;
 if( $post_type == 'shop_order' && isset($_GET['success']) ) {
 echo '<div class="updated"><p>The orders have been successfully update!</p></div>';
 }
}
function send_this($email,$orderid,$name) {

 $headers = 'From: Shop <[email protected]>' . "\r\n";
 $headers .= "MIME-Version: 1.0\r\n";
 $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";

 $subject = 'Order Update';

 $message = '

<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>
 Order Update: #' . $orderid . '
 </title>
 </head>
 <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0">
 <div style=" background-color: #f5f5f5; width:100%; -webkit-text-size-adjust:none !important; margin:0; padding: 70px 0 70px 0;">
 <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%">
 <tbody>
 <tr>
 <td align="center" valign="top">
 <div id="template_header_image">
 </div>
 <table border="0" cellpadding="0" cellspacing="0" max-width="600" id="template_container" style=" box-shadow:0 0 0 3px rgba(0,0,0,0.025) !impo$
 <tbody>
 <tr>
 <td align="center" valign="top">
 <!-- Header -->
 <table border="0" cellpadding="0" cellspacing="0" width="100%" id="template_header" style=" background-color: #557da1; color: #ffffff;$
 <tbody>
 <tr>
 <td>
 <h1 style=" color: #ffffff; margin:0; padding: 28px 24px; text-shadow: 0 1px 0 #7797b4; display:block; font-family:Arial; font$
 Order: #' . $orderid . ' Update
 </h1>
 </td>
 </tr>
 </tbody>
 </table>
 <!-- End Header -->
 </td>
 </tr>
 <tr>
 <td align="center" valign="top">
 <!-- Body -->
 <table border="0" cellpadding="0" cellspacing="0" width="600" id="template_body">
 <tbody>
 <tr>
 <td valign="top" style=" background-color: #fdfdfd; border-radius:6px !important;">
 <!-- Content -->
 <table border="0" cellpadding="20" cellspacing="0" width="100%">
 <tbody>
 <tr>
 <td valign="top">
 <div style=" color: #737373; font-family:Arial; font-size:14px; line-height:150%; text-align:left;">
 <p>
 Hi ' . $name . ',
 <br>
 <br>
 Thank you 

';

 return wp_mail( $email, $subject, $message, $headers );
}

I haven’t include the full email above, just a snip so you can see where I put the orderid and name. The actual template I’ve used is one off woocommerce itself to keep the look of the emails the same as an order progress.

To get the mails flowing just select a bunch of orders and bulk change them to ‘Mark as Awaiting Stock Emailed’. This happily looped through 250 at a time on my system. After doing over 3,000 I only had 14 moved automatically to the failed status, and rerunning them didn’t cause any problems.

The one issue I did run into was the name of the failed status, originally I had it set as stock-emailed-fail. But there’s a limit on the number of characters you can use and it came into the system as stock-emailed-fai. I managed to loose 3 orders in the first test batch, as they were now assigned a status that didn’t exist. So I had to find them in the database and set them to the proper status. If you keep your slugs short, you shouldn’t see this issue. but make sure you also type the exactly the same in the code as they appear in jetpack.

I take no responsibility for any lost orders as a result of using this code.

WooCommerce New Bulk Action

A few months back I added some new order statuses in WooCommerce, then changed the code to be able to use these from the bulk actions menu. I then added them into the dashboard so I could get a quick overview of what I needed to do each day.

This was just a quick (maybe not so quick) edit of the admin files. A while later I update WooCommerce and bang goes my edits (yes I can hear everyone shouting ‘should have made a child theme’). I did actually have a child theme running, but it was just a quick dirty way of getting it done at the time. I needed to concentrate on orders.

Anyway back to now, we’ve had a few thousand orders in a short space of time and once again I need my custom statuses from the bulk menu. This time I decided to do it right.

After searching and searching, I couldn’t find anyone saying how to add to the bulk menu, plenty of stuff about adding a status but you have to then edit each order to use it. Knowing I’ve done it before and didn’t take half the day doing so I kept searching. Eventually I found http://www.niepes.com/web/how-to-create-a-custom-order-status-in-woocommerce/ and this is exactly what I needed.

Well sort of. I didn’t want the email side of it and actually wanted to add a few customs (2 atm) . So I changed the code a little.

Find below the code I have now added to functions.php (note I already created the custom statuses of ‘printing’ and ‘stock’ using WooCommerce Jetpack.

//added for woocommerce bulk actions.
add_action('admin_footer-edit.php', 'custom_bulk_admin_footer');
function custom_bulk_admin_footer() {
 global $post_type;
 if($post_type == 'shop_order') {
 ?>
 <script type="text/javascript">
 jQuery(document).ready(function() {
 //printing status
 jQuery('<option>').val('printing').text('<?php _e('Mark as Printing')?>').appendTo("select[name='action']");
 jQuery('<option>').val('printing').text('<?php _e('Mark as Printing')?>').appendTo("select[name='action2']");
//stock status
 jQuery('<option>').val('stock').text('<?php _e('Mark as Awaiting Stock')?>').appendTo("select[name='action']");
 jQuery('<option>').val('stock').text('<?php _e('Mark as Awaiting Stock')?>').appendTo("select[name='action2']");
 });
 </script>
 <?php
 }
}
add_action('load-edit.php', 'custom_bulk_action');
function custom_bulk_action() {
 global $typenow;
 $post_type = $typenow;
if($post_type == 'shop_order') {
 $wp_list_table = _get_list_table('WP_Posts_List_Table');
 $action = $wp_list_table->current_action();
 $allowed_actions = array("stock","printing");
 if(!in_array($action, $allowed_actions)) return;
if(isset($_REQUEST['post'])) {
 $orderids = array_map('intval', $_REQUEST['post']);
 }
switch($action) {
 case "printing":
 foreach( $orderids as $orderis ) {
 change_order_status($orderid, $action);
 }
 case "stock":
 foreach( $orderids as $orderid ) {
 change_order_status($orderid, $action);
 }
 break;
 default: return;
 }
$sendback = admin_url( "edit.php?post_type=$post_type&success=1" );
 wp_redirect($sendback);
 exit();
 }
}
function change_order_status($orderid, $action) {
 $order = new WC_Order($orderid);
 if(($action=='printing') && ($order->status!='printing')) {
 $order->update_status('printing', '');
 }
 if(($action=='stock') && ($order->status!='stock')) {
 $order->update_status('stock', '');
 }
}
add_action('admin_notices', 'custom_bulk_admin_notices');
function custom_bulk_admin_notices() {
 global $post_type, $pagenow;
 if( $post_type == 'shop_order' && isset($_GET['success']) ) {
 echo '<div class="updated"><p>The orders have been successfully update!</p></div>';
 }
}

Checkout Part 2

Just can’t find it.

Another rambling entry.

I started this bog with the idea of keeping notes of things I may need to refer back to. Tonight I’ve spent 30 mins searching the entries for LDAP authentication on the PI. I do LDAP Auth on all my servers against 2 LDAP servers, and I’m sure I posted somewhere what I did to configure the PI’s to auth against LDAP but I just can’t find it.

What it has highlight though is 1) I don’t post nearly enough of what I do, and when I do it’s normally after I’ve done it and then write it up from memory. 2) The blog design just ins’t very user friendly.

So I’m going to try to write things up a bit more often as I’m doing them and tonight I’m going to look at some better designs. I’ve been installing wordpress quite a bit recently and it just seems to have themes and a better layout, so I’d like to get a similar design running on this blog. I would just run the blog on my own server, but I think there’s a good chance moving my blog will screw up any references to blogspot. Maybe something to look at though when I’m bored.

Raspbmc Hyperion On/Off

I’ve just finished making the surround for my TV after testing it all with sticky tape 🙂 it’s looks awesome.

But the first question I was asked “How do I turn it off?”, Apparently the answer of “You don’t” wasn’t the right one. So a little searching brought me to a fellow blog post http://blog.nadnerb.co.uk/?p=11 which takes you through setting up a remote button in xbmc to disable the service.
While I’d be happy with the remote button option, turning the entire service on and off doesn’t sit well with me, what if I want to set mood lighting from my phone 🙂

So I had a little play with hyperion-remote and thought yeah I can script that. So below is my quick and dirty alternative to disabling the service.

First connect to the PI and create a new file (I called it hyperion_toggle_black.sh)
nano -w hyperion_toggle_black.sh

#!/bin/bash
priority=222

hyper_check=`hyperion-remote -l | grep -i ""priority" : $priority"`
echo $hyper_check
if [ -z "$hyper_check" ]
then
   hyperion-remote -p $priority -c black
else
   hyperion-remote -p $priority -x
fi
exit 0

Next make the script executable
chmod +x hyperion_toggle_black.sh
Now you can test it using ./hyperion_toggle_black.sh
Run it a few times and see that the LED’s go on and off.
The important part of this is the priority, XBMC seems to use a priority of 1000, so any number lower should be great. The android app and hyperion-remote seems to default to 50 or 100. Since I want these to still work I need a value higher than these. 222 should be good, but your free to change it if you like. If for example you want a complete off, setting it to 0 or 1 should be above everything else.
This script basically gives hyperion another job at priority 222 to set all the LED’s black, if there is no current priority 222, and clears priority 222 if it already exists. hence the toggle. On the plus side this wont require root privileges to start/stop the service.
Now you can edit the remote keymap file
nano -w /home/pi/.xbmc/userdata/keymaps/remote.xml
Because I’m using an LG TV and the symlink seems fairly basic, I’m limited to what buttons I can assign. I’ve already previously added a ‘Home’ button to my pause, So I’ve decided to change this slightly by using the pause button to trigger the script while it’s already on the Home screen.
Adding the line  lines
<pause>XBMC.System.Exec(“/home/pi/hyperion_toggle_black.sh”)</pause>
Within the Home>Remote section, but keeping the Global>Remote as
<pause>XBMC.ActivateWindow(Home)</pause>
This took a little playing around to work out which buttons I can use, the blog post at http://forum.osmc.tv/showthread.php?tid=6978 gave me the debug and tail commands to use.

Reboot the PI and viola. There’s about a 1 second delay between pressing the button and the LED’s going off/on, but I can live with that for now.

I didn’t need the XBMC notifications like the other blog post, but if this is something you want, you can mash my script to his and get your own thing 🙂

Notes:
I’m not entirely sure if turning the LED’s Black actually turns them off (i.e. no power) so I may in the future expand it with maybe a relay to control the actual power to the LED’s, but this would break the priority thing unless I put in some checks and run the script in the background.
I could also then add a push button to the PI’s GPIO triggering the on/off script at more of a physical level, hey I could even add a few buttons to be able to select an effect. but for now I’m happy to do all of that via my phone, and just have a priority function for XBMC.

xlib_shm Zoneminder Got unexpected memory map file size X, expected Y

After upgrading my Ubuntu system first from 12.04 to 13.10 then onto 14.04.1 I started getting problems with xlib_shm
It wouldn’t start up, all the cameras were working fine from the web interface but nothing to the TV.

So I started troubleshooting and found the following error coming back at me.

shared_data_size=12166644
mem_size=884
Got unexpected memory map file size 4056640, expected 884

It doesn’t really make any sense though, it take you down the path of increasing the shmmax an shmall. But this was all working fine before the update and these values haven’t been changed. Still I increased them just incase.
No joy.
I also tried to recompile xlib_shm incase (being upgraded a few times) too much had changed. This unfortunately fell flat on its face when it can’t compile there’s stuff missing (we’ll come back to that).
After going round in circles for quite a while, I found while checking the camera configs that each camera had been set to 8bit greyscale. I set this back to 24bit colour. And restart. hey presto! my tv is once again filled with cameras.
I do still have a problem at the moment though, although the cams are now displaying they are frozen. I’m not sure though if this is more to do with how I start xlib_shm, and think it may be crashing out but not completely enough to take the display away.
So if your having problems with xlib_shm after upgrading either your OS or Zoneminder, check that your cams are setup properly. I almost missed the colour as it’s nighttime and they are grey anyway 🙂
As for the compile problems. It looks as though upgrading through 2 versions of Ubuntu has removed some packages, I’ll have to try and work out what incase I need to compile again.

Raspberry PI + Maplin WH1080 Weatherstation

Download and write to SD Card the debian image for Raspberry PI.
Boot and connect via SSH (Putty from Windows)
Change to root using sudo su – alternative use sudo in front of commands. Using sudo su – is bad, but I always do it.

Run

apt-get update
apt-get upgrade

Download the latest Debian Weewx version (http://sourceforge.net/projects/weewx/files/) using wget. Then run

dpkg -i weewx_2.6.4-1_all.deb

This will most likely throw error errors about missing dependencies. Install them using apt-get install

dpkg -i weewx_2.6.4.1_all.deb
(Reading database ... 75409 files and directories currently installed.)
Preparing to replace weewx 2.6.4-1 (using weewx_2.6.4.1_all.deb) ...
Unpacking replacement weewx ...
dpkg: dependency problems prevent configuration of weewx:
 weewx depends on python-configobj (>= 4.5); however:
  Package python-configobj is not installed.
 weewx depends on python-cheetah (>= 2.0); however:
  Package python-cheetah is not installed.
 weewx depends on python-imaging (>= 1.1.6); however:
  Package python-imaging is not installed.
 weewx depends on python-usb (>= 0.4); however:
  Package python-usb is not installed.
dpkg: error processing weewx (--install):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 weewx

So in my case I run
apt-get install python-configobj python-cheetah python-imaging python-usb
During the install you’ll be asked several configuration questions, fill them in (You can always edit the config file later if you make a mistake).

For the maplin WH1080, Select the FineOffsetUSB.

Once installed weewx should attempt to start (if not you can start it with /etc/init.d/weewx start). Check the syslog for any errors
tail /var/log/syslog -n 50
If all goes well you should see something like

wxengine: Initializing weewx version 2.6.4


wxengine: Using Python 2.7.3 (default, Mar 18 2014, 05:13:23) #012[GCC 4.6.3]


wxengine: pid file is /var/run/weewx.pid


wxengine: Using configuration file /etc/weewx/weewx.conf


wxengine: Loading station type FineOffsetUSB (weewx.drivers.fousb)


fousb: driver version is 1.6


fousb: polling mode is PERIODIC


fousb: polling interval is 60


fousb: altitude is 4.2672 meters


fousb: pressure offset is 0.0


fousb: found station on USB bus=001 device=005


wxengine: StdConvert target unit is 0x1


wxengine: Record generation will be attempted in 'hardware'


wxengine: The archive interval in the configuration file (300) does not match the station hardware interval (60).


wxengine: Using archive interval of 60 seconds


archive: Created and initialized table 'archive' in database 'weewx.sdb'


wxengine: Using archive database: archive_sqlite


stats: Created schema for statistical database


stats: stats database up to date.


wxengine: Using stats database: stats_sqlite


wxengine: Starting up weewx version 2.6.4

If you encounter errors you can edit your weewx configuration using nano -w /etc/weex/weewx.conf
Once you’ve finished editing press ctrl+x (to exit), then y(to save), then enter(same filename). Then restart weewx using /etc/init.d/weewx restart
If all has gone well you may also see entries in your syslog like
weewx[12559]: archive: added record 2014-09-20 18:32:44 UTC (1411237964) to database 'weewx.sdb'; table 'archive'
You should also have /var/www/weewx loaded with files.
As we haven’t yet installed a webserver though you can’t view them.
We’ll install Apache2 Server to handle our webpages.
apt-get install apache2
Once apache is installed open the weewx pages by visiting http://{raspberry pi ip address}/weewx from your browser. e.g. http://192.168.1.52/weewx
This setup does leave weewx running as root, not really something you would do for a system running on the internet. but outside the scope of securing your server for here.
Checkout http://www.weewx.com/docs/usersguide.htm#installing for more info on installing, problems and further guides for configuring your weewx installation.
Also checkout http://davies-barnard.co.uk/2013/12/weewx-rasp/ for a better looking skin template (the link is in one of the comments).

Mining DarkCoin on Linux using Digital Ocean

A friend recent;y asked me about DarkCoin, and I had to admit I hadn’t heard anything about it. Sure I have some bitcoins but only really to say I have them. So when he asked I just thought `ah there’s another one`. He then asked how someone would mine these things, So I showed him a few youtube videos of pretty impressive setups.

Anyway to the point, I ran through a quick demonstration of how the miner would work and setup one up on a Virtual Server (Droplet). So for anyone that’s interested here goes (p.s. I can’t say you’ll make a fortune from this method, it’s more proof of concept).

We’ll be using DigitalOcean for our server, so you’ll need an account. Signup here There’s always some promo codes for some credit at present SSDMAY10 give you an extra $10 credit. I personaly use DigitalOcean to host a few of my projects and they’re great for being able to test ideas.

Once you have your account setup, Create a new Droplet I normally use the Latest Ubuntu.
So the specs I’m testing this on are:-

Hostname : Testy
Size : 512MB, 1CPU, 20GB SSD, 1TB XFER $5pm
Region : Amsterdam 2
Image : Ubuntu 14.04 x64
SSH Key : testy (optional, you may not have added any ssh keys so you can skip this selection)
Settings : Enable VirtIO - Checked, Private Networking - Unchecked, Enable Backups - Unchecked.

Once created you will be emailed your root password (unless you selected SSH Keys).
More CPU’s would be better, but this one’s just for testing it out.

Using Putty login to your new Droplet

Now we’ll install some dependencies, and download the mining git.

apt-get update
apt-get install build-essential m4 libssl-dev libdb++-dev libboost-all-dev libminiupnpc-dev git automake libcurl4-openssl-dev screen

git clone https://github.com/ig0tik3d/darkcoin-cpuminer-1.2c.git

cd darkcoin-cpuminer-1.2c
chmod a+x autogen.sh
./autogen.sh
./configure
make
make install
cd ~

If all has gone well you can run the miner using

minerd -a X11 -o stratum+tcp://drkpool.com:3333 -u bighippo999.testy -p password

This should output something similar to

One thing to note, the above command will test under MY pool test login. You will not receive any coins doing this and should only be used as a test. You can stop this using CTRL+C

As long as that all seems to be ok, we should now join a pool. I’m currently using DarkCoin Official Pool I have no idea how ‘Official’ it is, but I was drawn to the graphs 🙂 I did briefly run a miner on windows using this guide and received coins directly into my wallet. Whereas using the pool I’ll have to cash anything out to my wallet and that will incur a charge. I would have also shown using the servers from the other guide, but at present they seem to be down I suppose that’s why this Pool does charge small admin % on each transaction. I can’t say I mind for good service.

Once you’ve signed up and have your details just run the miner again

e.g. minerd -a X11 -o server:port -u user.worker -p password

Replacing server:port user.worker and password with your own details.

If your concerned of your pool failing a handy script a come across

#!/bin/bash
## Miner Failover Script

## Will continously try each pool until one responds, ordered by priority.

#GLOBAL
## Set options
RETRIES="3"
SECONDS="5"
MINERD="minerd"

#POOL1
## Set userpass information for first pool
USERPASS1="name.worker:password"
URL1="stratum+tcp://lotterymining.com"

#POOL2
## Set userpass information for second pool
USERPASS2="name.worker:password"
URL2="stratum+tcp://www.drkpool.com:3333"

while :
do
$MINERD -a X11 --retries=$RETRIES --retry-pause=$SECONDS --userpass=$USERPASS1 --url=$URL1
$MINERD -a X11 --retries=$RETRIES --retry-pause=$SECONDS --userpass=$USERPASS2 --url=$URL2
done

You can copy and past this into a new document

e.g. nano -w startmining.sh

Paste the code.
CTRL+X, then Y and enter. Will exit saving the file.
Then issue

chmod +x startmining.sh

To make the file executable. Lastly it’s always better to run processes you want to keep running (but also be able to check on) in a screen.
The command

screen -d -m -S MINER minerd -a X11 -o stratum+tcp://drkpool.com:3333 -u bighippo999.testy -p password

Will start the miner in a screen, and you can view this with

screen -r

Then either disconnect with CTRL+A, then D or stop the miner using CTRL+C.
If you used the above script you can start that in a screen with

screen -d -m -S MINERPOOL ./startmining.sh

Again to view it

screen -r

Then either disconnect with CTRL+A, then D or stop the miner using CTRL+C.

EDIT:
The other pool I’m using came back online check it out here. This would be started with the following

minerd -a X11 -o http://q30.qhor.net:7903 -u Xn7JauXvQmorx82yEN2EMvbt3dX45uoiCh -p password

I hope you’ve found some of this useful, if you feel like sending me a few DarkCoins my address Xn7JauXvQmorx82yEN2EMvbt3dX45uoiCh and my BitCoin address 1DpfEhiVjNM4WZT49X18m3vXyUvGpuvz9i