MyRaspberryAndMe

Tinkering with Raspberry (and other things)

Soundbridge Information Display

5 Comments

I own two Roku/Pinnacle Soundbridges. And I am frightened for the day they cease existence. As a matter of fact I am trying to buy some used ones, just as spares. These little tubes are the best internet radios around, even nowadays. Plug them in, configure the network and voilá, you have a running radio that is accessible via a webinterface. Back in these days no marketing chef or product designer even thought about forcing the buyers to register with their mailadress, birthdate, creditcard numbers, etc.

The Soundbridge has an open telnet port that can be used to configure and control it. So it’s the perfect device for tinkering around and I am going to use the display to show information sent from the Raspberry.

The idea

For a start it would be cool to have the actual weather forecast visible. The Soundbridge is located in the kitchen and usually the second thing that is switched on in the morning. Sounds easy. Just get the weather somehow, telnet to the Soundbridge and display the weather. Which, technically would just be something like this:

macmini:~ th$ telnet xxx.xxx.xxx.xxx 4444
Trying xxx.xxx.xxx.xxx...
Connected to soundbridge
Escape character is '^]'.

Welcome to the SoundBridge Shell version 3.0.44 Release
Type '?' for help or 'help ' for help on .

SoundBridge> sketch
welcome to sketch (? for help)
sketch> text 0 0 "This is the message!"
sketch> quit

SoundBridge> exit

Connection closed by foreign host.
macmini:~ th$

Pretty straightforward and working. The Soundbridge exposes a shell from which we go to the “sketch” subshell. There we can write to the display. Hooray. Pretty fast prototyping. And it looks like this:

sb_test1

Way too easy!

Unfortunately, as with every other software development project, things get a little more complicated once it has started. And one begins to think seriously about what has to be done.

  1. When the telnet connection is closed, the display resets to its idle state, so the message is gone
  2. As long as the sketch-subshell is active the Soundbridge does not respond to its remote

So in fact I can have the weather displayed but then no longer listen to the radio. Not good. Furthermore I want to keep the load on the Raspberry as low as possible, so I need a non-blocking solution and cron is out of the question.

The Soundbridge shell has some more features that come in handy. It is possible to catch the IR-commands it receives via the shell. So problem number 2 is easily solved: Get in “catch”-mode and wait for a command from the remote, then free the display and let the Soundbridge respond to commands. But this would mean having the program cycle forever until a command is received.

I wanted to use this project to learn programming in Python so I decided against using threads. They would have been the solution to the above problems. Have one thread listen for IR-commands and signal to the display-thread when to free the Soundbridge.

Daemons!

Unix system have another great way of doing things in the background: daemons. So having to decide between threads and daemons I went for the daemons. A little bit of research led me to a great tutorial and sample code. The author states that it is in the public domain and may be used, so I did.

I came up with a solution using two scripts. One very small script is responsible for only waiting for an IR-command and then free the display, I am going to call it the Listener. The second, larger, script will grab the weather, display it and then wait an hour and start over again, the Writer. This is only possible because the Soundbridge does not care from which telnet connection it receives the “quit from sketch subshell” command.

As there is no signalling between the two scripts there is one desirable side effect. The scripts are never terminated. When the Listener receives an IR-command it frees the display and one may use the Soundbridge as a radio. The Writer continues running, so after an hour, even when listening to the radio, an updated weather information is displayed. Cool.

So here’s the Listener:

#!/usr/bin/env python

import sys, time, telnetlib
from daemon import Daemon

class SBListener(Daemon):
   def run(self):
      """
      open telnet port to soundbridge
      start echoing of IR commands
      wait for user pressing SELECT-button on remote
      go into sketch subshell
      quit out of sketch subshell to release display
      """
      port = 4444
      tn = telnetlib.Telnet("soundbridge_ip_here", port)
      while True:                            # run forever
         tn.read_until("SoundBridge> ")      # wait for prompt from soundbridge
         tn.write("irman echo\n")            # issue command for IR intercept
         tn.read_until("irman: CK_SELECT")   # wait for prompt on received IR
         tn.write("sketch\n")                # use this session to enter sketch subshell
         tn.read_until("sketch> ")           # wait for prompt
         tn.write("quit\n")                  # exit sketch subshell, clears display
         tn.write("\n")

if __name__ == "__main__":
   daemon = SBListener('/tmp/sblistener.pid')
   if len(sys.argv) == 2:
      if 'start' == sys.argv[1]:
         daemon.start()
      elif 'stop' == sys.argv[1]:
         daemon.stop()
      elif 'restart' == sys.argv[1]:
         daemon.restart()
      else:
         print "Unknown command"
         sys.exit(2)
      sys.exit(0)
   else:
      print "usage: %s start|stop|restart" % sys.argv[0]
      sys.exit(2)

This is the Writer:

#!/usr/bin/python
'''
Writing weather data to a Roku Soundbridge.
Version 0.1a
'''

import sys, time, telnetlib
from daemon import Daemon
from YahooWeather import YahooWeather    # one of my packages

class SBWriter(Daemon):
   def run(self):
      try:
         myWeather = YahooWeather("yahoo_WOEID_here", "c")       # initiate weather object
         port = 4444
         tn = telnetlib.Telnet("soundbridge_ip_here", port)
         sleeptime = 3600                              # update interval is 3600 seconds
         while True:                                   # forever
            tn.write("\n")
            tn.read_until("SoundBridge> ")             # wait for prompt
            tn.write("sketch\n")                       # enter sketch subshell
            tn.read_until("sketch> ")                  # wait for prompt
            tn.write("encoding utf8\n")                # set display encoding
            tn.read_until("sketch> ")                  # wait for prompt
            tn.write("font 1\n")                       # select font
            tn.read_until("sketch> ")
            '''
            the next two lines write the weather forecast to the display
            the methods they invoke on the myWeather-object return preformatted strings
            parameters after text command: c is centered x-axis, 0/9 is y-axis
            '''
            tn.write("text c 0 " + myWeather.getSoundbridgeOneLinerNow().encode('utf-8'))
            tn.write("text c 9 " + myWeather.getSoundbridgeOneLinerFCast().encode('utf-8'))
            time.sleep(sleeptime)                       # wait some time
            myWeather.populate()                        # repopulate weather object
            sleeptime = 3600                            # everything was OK, next read in 60 minutes
            tn.write("quit\n") # exit sketch subshell
         except:
         '''
         if anything goes wrong the sleeptime is reduced to 10 minutes
         so maybe we get updated weather next time
         '''
         sleeptime = 600

if __name__ == "__main__":
   daemon = SBKitchenWriter('/tmp/sbkitchenwriter.pid')
   if len(sys.argv) == 2:
      if 'start' == sys.argv[1]:
         daemon.start()
      elif 'stop' == sys.argv[1]:
         daemon.stop()
      elif 'restart' == sys.argv[1]:
         daemon.restart()
      else:
         print "Unknown command"
         sys.exit(2)
      sys.exit(0)
   else:
      print "usage: %s start|stop|restart" % sys.argv[0]
      sys.exit(2)

So this works great. I have refined my YahooWeather (which I wrote for learning purposes, too)  class a little bit. Now between 0:00am and 5:00pm the first line displays the current weather and the forecast for today. After 5pm the actual weather and a forecast for the next day is displayed.

The image on top of the page, the “featured image” is an actual photograph of what is displayed. In German, OK. The format is :

actual temperature (time of update), Today/Tomorrow: high/low – textual weather condition
next three days with high/low

Thank you for reading the whole long thing. These are my first steps with Python. So feel free to comment on any quirks or things that could be done more elegant in Python. Or, if something is just quite plain wrong.

Advertisements

5 thoughts on “Soundbridge Information Display

  1. Nice post!

    I have also stacked up a few sb as spares, and people think i’m crazy.
    But it really still is a great product. Beautiful, open and compatible.

    My current project is to make them work as a kookoo clock but with better sound.
    [link removed by admin]

    These quirky things are possible with open products.

  2. I’m done with my script towards the SoundBridge, which makes them work as modern cuckoo clock.

    http://www.lounge.se/wiki2/show/BirdsClock

    A nice extended use of the SoundBridge !

    • Nice one.
      I’m currently thinking about how to adopt the easy Soundbridge user interface to a Raspberry Pi based internet radio/DLNA player. It seems the cost of used Soundbridges is becoming higher and higher…

  3. FANTASTIC!!!
    I’m going to just the same thing with my M1000! Only instead of forecast, I’m going to grab the current conditions from my weather station in the back yard, and run wview on Beaglebone
    http://www.wviewweather.com/

    • That sounds cool. I’ve been thinking about a weather station for a long time and I do not have a back yard. I’m considering going with an Arduino and try to power the thing from solar modules, but that seems not to work (not enough sun, not enough space…).
      Please post a follow-up on your progress!