Apple 7th Gen iPad + Smart Cover Fix

Perhaps the most frustrating part of using the Apple Smart Cover with the 7th Gen iPad is that the keyboard just randomly stops working from time to time with no real rhyme or reason as to why. After lots of searching around and not getting anywhere I finally found a solution that works like a charm

  1. Physically disconnect cover from the iPad
  2. Reconnect the cover
  3. use Cmd + Tab to switch through a few apps
  4. resume typing because Apple hasn’t fixed this in the software yet

Considering I have seen articles from like 2 years ago where this was an issue it really feels like Apple should have fixed this by now but nope, an up to date 7th generation iPad in 2020 is still having this issue. Makes me wonder what that premium price tag for most Apple goods go to if not fixing/refining the products.

Winter Improvements for Hockey-Info

Finally got around to a rather large update for this project, fixed some small bugs such as the L10 data being way wrong (was showing win-loss-ot for the entire seaseon) as well as added in missing stats for Goalies and made the display of previous game results more sensible. Also redid about 95% of the interface to use Bootstrap4 which has made the look more uniform throughout. If you are interested in seeing the code itself you can see that here, or if you just want to check out the live site which I host as well then you can head over to

On the Road: Chicago

Not too shabby of a view

So I find myself on the road in Chicago as of Sunday, doing a round of technical interviews in my effort to take the next step in my career progression. Not a very long stay but the view from my hotel room at night was great, really captured the feel of the city at night I think. As much as I miss being at home and sleeping in my own bed I kind of wish I could have at least one more day if only to take a trip to a few landmarks and hit up a nearby fountain pen shop.

Still Moving

Slowly but surely I am getting my stuff set back up, the computer was the first part as I am currently searching for a job and need access to my resume files and my email in a manner other than my phone or tablet. Speaking of tablets I am really enjoying my 6th generation iPad that I got recently with the LTE option on it. After some talk with a buddy of mine (mikep) and getting blink-shell on the app store I gotta say its a fantastic ssh experience on iOS. I have been using it to decent success to work on a few side projects like my youtube-dl front-end.

It is pretty wild how much stuff accumulates over the course of 5 years and nothing quite puts it in focus like moving and having to drastically downsize. My office had 3 different desks, 3 computers and 6 monitors ranging in size from 23″ to 27″. To say the heat could get excessive in there was an understatement, I ran a box fan in the hallway to help circulate the air. In the move I have downsized to just a matched pair of 24″ screens and a single desk, hopefully this more focused environment will let me be more productive when it comes to projects and work.

On the topic of work it seems like the more moving I do the more I just want to lock myself away and code, I have ideas for tweaks to existing projects like the hockey-info website and new stuff like YeRP which needs a big push to get to a point where I am ready to release it to the internet for general use. I wonder what it is about absence of coding that makes me want to do it more?

New Digs

So I am moving and took the opportunity to change my desk setup to something a lilttle more focused from my previously insane configuration. Behold an actual matched pair of 24″ screens on a mount where I will hopefully be churning out all sorts of new projects (like YeRP – Yet another Ripping Portal that uses flask and youtube-dl to present a mobile friendly interface.

Patching CentOS 7 (and overcoming problems)

So I was working on patching some of my Icinga infrastructure at work, and it seems that sometimes libyajl breaks things, as illustrated below

root@icingasatellite ~]# yum update
Loaded plugins: fastestmirror, rhnplugin
This system is receiving updates from RHN Classic or Red Hat Satellite.
Loading mirror speeds from cached hostfile

  • epel:
    Resolving Dependencies
    --> Running transaction check
    ---> Package icinga2.x86_64 0:2.10.4-1.el7.icinga will be updated
    ---> Package icinga2.x86_64 0:2.10.5-1.el7.icinga will be an update
    ---> Package icinga2-bin.x86_64 0:2.10.4-1.el7.icinga will be updated
    ---> Package icinga2-bin.x86_64 0:2.10.5-1.el7.icinga will be an update
    --> Processing Dependency: for package: icinga2-bin-2.10.5-1.el7.icinga.x86_64
    Traceback (most recent call last):
    File "/bin/yum", line 29, in
    yummain.user_main(sys.argv[1:], exit_code=True)
    File "/usr/share/yum-cli/", line 375, in user_main
    errcode = main(args)
    File "/usr/share/yum-cli/", line 239, in main
    (result, resultmsgs) = base.buildTransaction()
    File "/usr/lib/python2.7/site-packages/yum/", line 1198, in buildTransaction
    (rescode, restring) = self.resolveDeps()
    File "/usr/lib/python2.7/site-packages/yum/", line 893, in resolveDeps
    CheckDeps, checkinstalls, checkremoves, missing = self._resolveRequires(errors)
    File "/usr/lib/python2.7/site-packages/yum/", line 1025, in _resolveRequires
    (checkdep, missing, errormsgs) = self._processReq(po, dep)
    File "/usr/lib/python2.7/site-packages/yum/", line 350, in _processReq
    CheckDeps, missingdep = self._requiringFromTransaction(po, requirement, errormsgs)
    File "/usr/lib/python2.7/site-packages/yum/", line 680, in _requiringFromTransaction
    File "/usr/lib/python2.7/site-packages/yum/", line 5280, in update
    availpkgs = self._compare_providers(availpkgs, requiringPo)
    File "/usr/lib/python2.7/site-packages/yum/", line 1648, in _compare_providers
    bestnum = max(pkgresults.values())
    ValueError: max() arg is an empty sequence

Turns out the secret is simply to install yaljl and yajal-devel and then I can patch successfully, really surprised nobody else out there has run into this yet but its the second time in a month I have had it happen when patching.

Project: hockey-info

So I don’t always have great cell phone service, sometimes its weak 4G or even not 4G at all so modern designed apps suffer when bandwidth is a trickle at best. I would be away from home trying to find out whats going on with a Caps game and the NHL app would just be painfully slow or not work at all sometimes. Eventually I decided the only reasonable thing a hockey nerd such as myself could do was write something to fill this void, ideally something simple and effective to get me the information I wanted without a lot of overhead and frilly extra stuff I didn’t really care about.

The repos are still in high flux right now as I don’t even have a readme file yet for the main one, however this page can serve to sort of explain the bits and pieces.

nhlapi – This is what started it all for me really, I wanted more information about games (for an IRC bot) and threw myself into pulling various bits of information together about the NHL API in an easy to read and access way so others didn’t have to spend the hours I did looking for how to do things.

hockey-info – A super simple website written in Python utilizing the Flask framework. The focus is to be fast, simple and mobile friendly. It directly queries the NHL API for all its information and is formatted in a way that works well on mobile

hockey-info-docker – A bare bones Dockerfile to deploy the latest release of hockey-info. The container is based on Alpine and is as trimmed down as possible, makes deployment super simple and easy for anybody to run their own instance with only a few commands (provided you already have a Docker host to run it on).

Naturally I make no warranty about this app or any of the code I have written, its purely something neat I built in my spare time and am tossing out there for others to enjoy, modify and extend to their hearts content. If you do happen to have input, ideas, or feedback hit me up on twitter or just open an issue on Gitlab if its a purely technical issue to address with the code.

Hockey Records

The NHL was kind enough to release to the public to browse more interesting stats than just game-by-game data, things like players that have hit the 1000 point milestone and other more trivia-friendly factoids.  Naturally the spidey senses went to tingling as soon as I saw the news on Reddit so I ran off to start poking at it and lo-and-behold it actually hits what appears to be the same data source as but with all sorts of extra endpoints to try out.  This time around I attempted to be slightly clever and looked at to save myself the trial-and-error process I used on a lot of the Stats API.  Turns out this was actually a smart move an probably would have let me document a lot of this stuff sooner if I had thought to spend some time poking around the code of the stats website.  No matter,  what counts is that now there is a rough outline of the Records API and it has been rolled into the NHLAPI repo on Gitlab.  Just like before if you see something I missed feel free to open a PR and if I don’t happen to see it right away @ me on twitter, I try to respond fairly quickly.

Footnote: is fantastic, it let me unmangle the client.bundle.js file so it was readable

Simple CI with Chef

So I needed to work out a way to make a script I wrote recently be deployed across a whole host of systems, turns out the only option is Chef so I had to dive into it and read a bunch of stuff.  Also had to try a bunch of things and ended up with my own Chef server in the lab to test against.  Several hours of clicking and clacking later and I have my task worked out, so here it is.

First we need to create a new cookbook and drop a pretty simple default recipe in, all it does is make sure git is installed then clone a repo to /opt/nhlapi.

# Cookbook:: repo
# Recipe:: default
# Copyright:: 2018, The Authors, All Rights Reserved.
package 'git' do
  action :install

git '/opt/nhlapi' do
  repository 'git://'
  revision 'master'
  action :sync
default.rb (END)

Once we have the recipe we need a role to tell it what to do.

   "name": "repo-update",
   "description": "update chef from time to time",
   "json_class": "Chef::Role",
   "default_attributes": {
     "chef_client": {
       "interval": 1800,
       "splay": 60
   "override_attributes": {
   "chef_type": "role",
   "run_list": ["recipe[chef-client::default]",
   "env_run_lists": {

Create the role with # knife role from file repo-update.json  (or whatever you named the file to create the role from).

Now all that is left is to assign the role to the node so use #knife node edit itsj-cheftest.itscum.local  and assign the role and repo to the node we want

  "name": "itsj-cheftest.itscum.local",
  "chef_environment": "_default",
  "normal": {
    "tags": [

  "policy_name": null,
  "policy_group": null,
  "run_list": [


That is enough to get it working, you can kick back and watch it with # while :; do knife status ‘role:repo-update’ –run-list; sleep 120; done and wait to see it run in about 30 minutes based on the interval and splay values.  Speaking of which Interval is pretty self explanatory, but Splay not-so-much; Splay is used keep a bunch of nodes from all running at once basically so it doesn’t overwhelm a system that they might be checking into or otherwise digitally assaulting.

Simple Icinga2 Plugin

I’ve seen bits and pieces of the process of creating an Icinga2 (or Nagios) plugin, so here are my notes dumped straight from my brain.

First and foremost we need a script to call from Icinga, in this case I created a very simple Python script to simply get the version of LibreNMS running on my monitoring system.

import argparse
import requests
import json
import sys

parser = argparse.ArgumentParser(description='Process some integers.')

parser.add_argument('-H', action="store",dest="host", help='name of host to check')

#parser.add_argument('token', metavar='token', help='API token')
token = 'yourAPItokenGOEShere'
args = parser.parse_args()

host_check = 'http://''/api/v0/system'
headers = {'X-Auth-Token': token }
r = requests.get(host_check, headers=headers,verify=False)


json_string = r.text
parsed_json = json.loads(json_string)

system_status = parsed_json['status']
system_ver = parsed_json['system'][0]['local_ver']

if system_status == 'ok':
	ret = "status: "+system_status+" version:"+system_ver
elif system_status != 'ok':
	ret = "status: "+system_status+" version:"+system_ver

This is a pretty simple script, you could call it with ./ -H to see how it works.  With the script working the next portion is done in the command line, first create the directory that will later be referenced as CustomPluginDir

# mkdir -p /opt/monitoring/plugins

Now we need to tell Icinga2 about the directory, this is done in a few different places

in /etc/icinga2/constants.conf add the following

const CustomPluginDir = “/opt/monitoring/plugins”

and in /etc/icinga2/conf.d/commands.conf we add the following block

object CheckCommand "check-lnms" {
    command = [ CustomPluginDir + "/" ]

    arguments = {
        "-H" = "$address$"

The block above defines the custom command, specifies the script we created first and also passes the correct flags.  Now its time to add the check into the hosts.conf file, so place the following block into /etc/icinga2/conf.d/hosts.conf

object Host "itsj-lnms" {
        address = ""
        check_command = "check-lnms"

And with that we wait for the next polling cycle and should see something like the screenshot below

This is a highly simplistic example, but figuring it out was necessary for me because I had to port some existing code from Ruby to Python so I wanted to know exactly how a plugin was created to understand what values were returned and how it all fits together.

Close Bitnami banner