Display-o-tron Plugin Gallery

Received the Dot3k yesterday, and although completely new to the Pi and Python I’ve got it displaying several of the elements Mr A suggested.

I’m using it as a status monitor at the moment in a PiBow rainbow case on a Pi 2, with the script launched from /etc/rc.local.

At the moment it displays the script PID, CPU temperature and percentage utilisation and the current time. The CPU utilisation is also being shown on the bar graph, and the background lighting colours also being set based on the CPU levels.

To anyone thinking of getting a Dot3k, I’d say go ahead and order one, you won’t regret it.

It’so so much more capable as a monitor than the PiGlow (although that’s also excellent). Waiting for the BlackHat and Unicorn Hat to arrive this weekend, already bought a second Pi to leave the Explorer hat connected to as I gradually experiment with the start of a weatherstation. I can see an order for another Pi (or two) being put together before long!

Paul

1 Like

Ok, I have now managed to switch my Piglow as a system monitor out and installed the DOT3K to display more detailed information. I am running a slightly modified version of the menu.py advanced example to display the total transmission of data over ethernet (GraphNetTrans) to give me a rough idea of how much data has been downloaded, and have added menu options to reboot and shutdown the pi. So far so good.

The one thing I am missing is the ability to display how much storage space is left on my NAS drive (automounted to my Pi) and I think I might be on the right lines, but wanted some advice.

I presume being able to utilise the df -h command for a specific directory or disk would be the area I need, and adapting the parts in the graph.py plugin for GraphNetTrans, I have started to come up with the following:

class GraphStorageSpace(MenuOption):
  """
  How much storage space is left on the external HDD.
  """
  def __init__(self):
    self.last = self.millis()
    MenuOption.__init__(self)

  def get_storage(self):
    show_dl_raw = ""
    show_dl_hr = "df -h /LOCATION/OF/NAS | grep Avail"
    hr_dl = run_cmd(show_dl_hr) 
    return hr_dl

  def redraw(self, menu):
    now = self.millis()
    if now - self.last < 1000:
      return false
    
    menu.write_row(0,'HDD Storage')
    menu.write_row(1,str('Free:' + self.get_storage())[:-1]) 

I would then add a menu option to my adapted menu.py to look like:

import dot3k.joystick as joystick
import dot3k.lcd as lcd
import dot3k.backlight as backlight
from dot3k.menu import Menu, MenuOption
from plugins.utils import Backlight, Contrast
from plugins.graph import IPAddress, GraphTemp, GraphCPU, GraphNetSpeed, GraphNetTrans, GraphStorageSpace
from plugins.clock import Clock
from plugins.wlan import Wlan
import time

"""
Using a set of nested lists you can describe
the menu you want to display on dot3k.

Instances of classes derived from MenuOption can
be used as menu items to show information or change settings.

See GraphTemp, GraphCPU, Contrast and Backlight for examples.
"""

menu = Menu({
    'Data':GraphNetTrans(),
    'Speed':GraphNetSpeed(),
    'Storage':GraphStorageSpace(),
    'IP':IPAddress(),
    'CPU':GraphCPU(),
    'Temp':GraphTemp()
    },
  lcd,
  30)

Am I on the right lines here? I am at work at the moment (and even though I could access my Pi remotely, I would not be able to see the DOT3K anyway!) so would need to test this at home…

Any advice would be appreciated!

I have solved it myself, so thought I should share for anybody else interested:

This is my updated graph.py file

class GraphStorageSpace(MenuOption):
  """
  How much storage space is left on the external HDD.
  """
  def __init__(self):
    self.last = self.millis()
    MenuOption.__init__(self)

  def get_space(self):
    show_dl_raw = ""
    show_st_sp = "df -h /PATH/TO/NAS/HERE | grep G | cut -d' ' -f5"
    hr_dl = run_cmd(show_st_sp) 
    return hr_dl

  def get_storage(self):
    show_dl_raw = ""
    show_dl_hr = "df -h /PATH/TO/NAS/HERE | grep G | cut -d' ' -f7"
    hr_dl = run_cmd(show_dl_hr) 
    return hr_dl

  def redraw(self, menu):
    now = self.millis()
    if now - self.last < 1000:
      return false

    menu.write_row(0,'NAS Storage')
    menu.write_row(2,str('Free:' + self.get_storage())[:-1])
    menu.write_row(1,str('Used:' + self.get_space())[:-1])

I have update the menu.py and renamed it sys-monitor.py and it looks like this:

#!/usr/bin/env python

import dot3k.joystick as joystick
import dot3k.lcd as lcd
import dot3k.backlight as backlight
from dot3k.menu import Menu, MenuOption
from plugins.utils import Backlight, Contrast
from plugins.graph import IPAddress, GraphTemp, GraphCPU, GraphNetSpeed, GraphNetTrans, GraphStorageSpace, GraphSysReboot, GraphSysShutdown
from plugins.clock import Clock
from plugins.wlan import Wlan
import time

"""
Using a set of nested lists you can describe
the menu you want to display on dot3k.

Instances of classes derived from MenuOption can
be used as menu items to show information or change settings.

See GraphTemp, GraphCPU, Contrast and Backlight for examples.
"""

menu = Menu({
    'Data':GraphNetTrans(),
    'Speed':GraphNetSpeed(),
    'IP':IPAddress(),
    'CPU':GraphCPU(),
    'Temp':GraphTemp(),
    'Storage':GraphStorageSpace(),
    'Settings': {
      'Maintenance': {
	'Reboot':GraphSysReboot(),
        'Shutdown':GraphSysShutdown(),
	'Contrast':Contrast(lcd),
        'Backlight':Backlight(backlight)
      }
    }
  },
  lcd,
  30)

"""
You can use anything to control dot3k.menu,
but you'll probably want to use dot3k.joystick
"""
REPEAT_DELAY = 0.5
@joystick.on(joystick.UP)
def handle_up(pin):
  menu.up()
  joystick.repeat(joystick.UP,menu.up,REPEAT_DELAY,0.9)

@joystick.on(joystick.DOWN)
def handle_down(pin):
  menu.down()
  joystick.repeat(joystick.DOWN,menu.down,REPEAT_DELAY,0.9)

@joystick.on(joystick.LEFT)
def handle_left(pin):
  menu.left()
  joystick.repeat(joystick.LEFT,menu.left,REPEAT_DELAY,0.9)

@joystick.on(joystick.RIGHT)
def handle_right(pin):
  menu.right()
  joystick.repeat(joystick.RIGHT,menu.right,REPEAT_DELAY,0.9)

@joystick.on(joystick.BUTTON)
def handle_button(pin):
  menu.select()

while 1:
  menu.redraw()
  time.sleep(0.05)

I am now able to see various stats on my headless Pi including:

  • Storage space
  • Data transmission
  • Download / Upload Speed
  • CPU utilisation
  • Temperature
  • IP Address

I am also able to reboot and shutdown the Pi using the DOT3K too.

Now off to feel smug about doing something myself for once!! :)

3 Likes

Great work! This could make a great plugin if tidied up a little. I’d probably make it scan all connected drives, determine the storage space left on all of them and display it as a list, but it’s definitely useful as it is.

Thanks! Yeah, that would be useful. I have to admit I wasn’t 100% sure what I was doing, but figured enough from the examples provided so kinda bodged them to work in a way I needed. I have learnt a few thing though, so maybe I will be able to get this up and running too! I did have a few issues with the menus though, which I couldn’t figure out - do you know how to display them in a particular order?

The menu ordering might be because the menus are stores in a Python Dictionary, rather than a Python List. Because a Dictionary is a key/value store it doesn’t really have a specific order… this is a mistake in my design of the framework which I need to look into.

Apart from their lack of order, I like nested Dictionaries because they’re nice and tidy! I might be able to fix it with an OrderedDict.

Ok, thanks for that. I had tried to order the menu without success, but I’m no expert!

I found a reasonably elegant solution for it, which will work using a “menu builder” module that lets you do this:

import menubuilder as mb

mb.add_item('Space Invader', my_invader)
mb.add_item('Clock', Clock())
mb.add_item('Status/IP', IPAddress())
mb.add_item('Status/Test','')
mb.add_item('Status/CPU', GraphCPU())
mb.add_item('Status/Arrr','Blah blah')
mb.add_item('Status/Temp',GraphTemp())
mb.add_item('Settings/Display/Contrast',Contrast(lcd)),
mb.add_item('Settings/Display/Backlight',Backlight(backlight))

menu = Menu(
  mb.menu,
  lcd,
  my_invader,
  30)

Behind the scenes, this creates and populates an OrderedDict sequentially, and automatically creates nested OrderdDicts to handle submenus which you can either express as a path like 'Status/Some Item' which is automatically split on slashes, or more explicitly as a list like ['Status','Some Item'] where you can use forward slashes in your menu text.

This code requires a small modification to the Dot3k library to run, so I’m going to have to push it all up to GitHub and create an example. But it solves the problem of menu ordering!

The contents of menubuilder.py look like this:

from collections import OrderedDict

menu = OrderedDict()

def add_item(path, handler):
    global menu
    if not type(path) is list:
        path = path.split('/')
    loc = menu
    last = path.pop()

    while len(path) > 0:
      key = path.pop()
      if not key in loc:
          loc[key] = OrderedDict()
      loc = loc[key]

    loc[last] = handler
1 Like

Wow, that was quick. Ok, will look forward to seeing that when it’s updated. Thanks.

And here it is, a finished feature for your testing pleasure: https://github.com/pimoroni/dot3k/commit/0c68ebe5ab1b1576296e7c6cae0d32974c982ab5

If you’re not familiar with the process of installing a library from GitHub, in a nutshell it’s:

git clone https://github.com/pimoroni/dot3k
cd dot3k/python/library
sudo ./setup.py install

I’ll be pushing it up to pip when I’m happy it’s not going to explode/break all the things.

I’ve also found/fixed a bug in the screensaver ( idle display ) feature.

Great stuff, thanks. Will check it out sometime!

I’ve also made some Python 3 compatibility tweaks in preparation for a working Python 3 SMBus ( i2c ) library, and fixed a really irritating bug in Debris where you’d just crash randomly because it was colliding you with asteroids it couldn’t draw on the screen.

Debris also know has a seed for the “random” number generator that builds the level. This means that everyone who plays it will see the exact same level, and can get a bit competitive :D

https://github.com/pimoroni/dot3k/commit/e005d63e0efb802657d8f8a007672610260e8a47

Hi Phil,

Quick question I was hoping you could answer for me. In the graph.py plugin, there is a command that looks like this:

menu.write_row(1,str('Dn:' + self.get_down())[:-1])

I have figured out that the [:-1] part has something to do with the formatting of text displayed, but I can’t figure out what I need to adjust to get my storage space displaying properly. It was working until I went into TB’s and now I see a funny upside down L symbol. I’ve tried changing the number (i.e. to 2, 3 etc) but I can’t get the text to display properly.

Could you point me in the right direction? I have tried Googling, but I’m not 100% sure what I need to search for!

The [:-1] chops the last character off the string, for example:

>>> 'abcdefg'[:-1]
'abcdef'

Also, try using awk:

df -h /home | grep G | awk '{print $5}'

Where $5 is the column number from 1 to 5.

Smashing. Sorted! awk was much easier than cut and all the guess work. I have even managed to combine the “used” space on my NAS drive with the percentage used. Thanks!

1 Like

Phil,

I suspect I have an earlier model of the DOT3K where the green and blue channels are swapped. Also, how do I swap them? I’ve seen the bit in the backlight.py about this, but I’m not sure what I need to do.

I’ve tried setting the backlight colours manually using the basic examples of:

from dot3k import lcd
from dot3k import backlight

lcd.write("Hello World!")
backlight.rgb(255,0,0)

This gives me (as expected) a fully red backlight. I then try:

backlight.rgb(0,255,0)

And instead of an expected green backlight, I get blue one. And then:

backlight.rgb(0,0,255)

Gives me a green backlight.

Would a hacky fix be just swapping the colour codes (i.e. for green use blue and vice versa) or is there a global setting I can change?

Thanks in advance… AGAIN! :)

Right after you import dot3k.backlight, do:

import dot3k.backlight
dot3k.backlight.use_rbg()

This switches the dot3k library into RBG ( note the G and B are flipped ) mode, and it’ll take care of making rgb(0,255,0) and rgb(0,0,255) work as expected.

also, make sure you get the latest dot3k lib from the github, you’ll want that if you start toying around with set_bar!

1 Like

Thanks both. Appreciate it.

Hi Mr_A
I have used your script “GraphStorageSpace” but unfortunately I cant get the GraphStorageSpace to work.

My drive is located at /media/usbhdd or /dev/sda1 I have tried to use both paths but the screen where free and used parts are blank. any help or pointers much appreciated.

Tony