Saturday, November 30, 2013

Big Sound for small RaspberryPI with IR: Adding Favorites



As soon as I had created my music center with RaspberryPI controlled by an Onkyo remote, I've realized that I needed a few dedicated "favorites" buttons and a more complex configuration file for the remote to make sure that I could persist them and make available after a reboot.

Fortunately, Onkyo remote has a lot of buttons that I don't normally use, so I've dedicated the whole Deck B panel to favorites and used Group Preset Up/Down buttons to navigate between different favorites.



Now I have 7 pre-programmed buttons, which are sufficient to remember all my favorite Internet radio-stations and local MPD's playlists.  Since I might want to re-program some favorites on fly, I've decided to use a combination of Stop+<FavoriteN> buttons to assign a new playlist to a button <FavoriteN>.

You can find all those new features in the new IR configuration file below. I think, it can be re-used by anyone who has the same model of Onkyo's remote - RC287S .

/etc/lirc/lircd.conf:


# Please make this file available to others
# by sending it to <lirc@bartelmus.de>
#
# this config file was automatically generated
# using lirc-0.9.0-pre1(default) on Sat Nov 16 03:37:07 2013
#
# contributed by
#
# brand:                       lircd.conf.conf
# model no. of remote control:
# devices being controlled by this remote:
#

begin remote

  name  ONKYO-RC-287S
  flags RAW_CODES|CONST_LENGTH
  eps            30
  aeps          100

  gap          108090

      begin raw_codes

          name KEY_POWER
             9017    4480     624     509     579    1663
              581     555     576     529     600    1670
              577     530     600    1667     579    1662
              583    1661     598     537     571    1670
              599    1668     579     531     598    1678
              569    1666     572     562     576     530
              600     536     572    1669     598     536
              581     555     574     531     600     534
              582     554     576    1664     625    1618
              598     536     584    1658     599    1675
              576    1667     571    1672     592    1677
              574

          name KEY_LEFT
             9054    4447     601     533     575    1667
              601     533     625     509     579    1664
              625     509     578    1672     616    1617
              609    1659     579     538     604    1664
              570    1676     622     501     574    1668
              581    1661     607     527     581    1660
              606    1673     565    1667     631    1610
              606     530     577     556     574    1677
              570     556     573     540     605     526
              577     556     573     539     603    1666
              569    1672     626     508     583    1652
              631

          name KEY_RIGHT
             9040    4461     600     534     582    1658
              601     535     572     562     578    1663
              573     562     577    1663     588    1659
              606    1660     576     531     609    1659
              578    1665     591     543     576    1666
              581    1661     608     526     581     553
              576    1666     583    1660     606    1660
              589     525     604     527     580    1667
              600     540     569    1659     608     529
              579     554     575     533     606    1662
              575    1675     572     555     574    1667
              579

          name KEY_PLAY
             9016    4485     628     505     581    1660
              575     559     581     528     609    1657
              583     527     601    1670     579    1666
              570    1665     603     532     575    1665
              604    1667     581     526     601    1666
              582    1661     576     558     580     528
              602     533     574     559     581     527
              602    1667     580     527     603    1667
              580     526     603    1672     581    1673
              563    1666     603    1668     577     526
              601    1673     572     527     602    1665
              581

          name KEY_PAUSE
             9072    4439     569     555     574    1666
              594     541     576     532     606    1664
              574     532     606    1662     576    1677
              570    1663     604     530     579    1661
              607    1663     575     533     605    1672
              565    1671     577     553     575    1668
              579     555     574    1676     571    1663
              606     529     579     554     575    1668
              580     554     608     498     608    1669
              567     534     605     530     579    1661
              606    1662     576     532     616    1653
              574

          name KEY_LEFTALT
             9015    4488     573     561     579    1660
              575     560     579     528     601    1677
              571     527     601    1669     578    1663
              574    1665     603     533     583    1660
              599    1668     579     528     601    1669
              578    1665     574     562     575     530
              599    1668     580     529     599     536
              572    1670     598     536     571    1669
              612     528     598    1649     600     526
              581    1660     607    1672     565     532
              608    1661     576     531     608    1659
              578

          name KEY_RIGHTALT
             9044    4466     594     529     579    1664
              603     529     579     556     573    1669
              579     556     572    1669     587    1656
              609    1667     567     538     600    1670
              566    1676     583     546     572    1668
              577    1665     601     533     573    1669
              600     534     574     561     579     528
              601    1668     577     531     600    1668
              580     528     600     544     564    1668
              598    1670     579    1664     573     561
              578    1672     563     561     579    1664
              572

          name KEY_VOLUMEUP
             9036    4464     597     537     579    1663
              602     532     575     560     578    1662
              573     567     573    1674     569    1663
              599    1668     578     531     605    1665
              571    1671     572     561     576    1667
              579    1662     603     541     566     560
              578    1661     573     563     575     534
              603     530     576     558     580    1673
              561     562     575    1668     577     556
              572    1670     573    1668     597    1673
              573    1669     574     560     577    1663
              582

          name KEY_VOLUMEDOWN
             9032    4470     603     528     579    1666
              605     529     574     559     580    1673
              562     563     576    1615     627    1668
              595    1677     567     533     603    1663
              571    1672     573     575     563    1666
              578    1662     603     533     574    1667
              598    1679     565     534     603     538
              569     559     579     528     598    1670
              574     534     605     529     604     541
              558    1674     572    1669     606    1662
              572    1678     569     562     578    1661
              579

          name BTN_1
             9009    4501     568     556     573    1668
              579     557     572     536     603    1672
              565     537     602    1662     574    1671
              576    1666     601     533     584    1656
              604    1666     580     528     602    1666
              581    1679     567     549     580     529
              599    1668     580    1665     571     571
              567    1664     584     551     577     530
              599     536     573    1669     597     538
              571     566     576    1682     552     562
              577    1665     570    1673     607    1661
              576

          name BTN_2
             9018    4480     569     536     604    1666
              571     537     602     542     565    1667
              601     533     576    1666     599    1669
              579    1671     566     560     579    1663
              573    1668     602     537     572    1674
              592    1668     577     531     600     535
              572     562     578     529     600    1669
              578    1671     566     560     577     530
              598     537     572    1670     598    1670
              577    1665     572     563     576     533
              596    1671     576    1668     579    1663
              614

          name BTN_3
             9058    4442     600     534     572    1670
              599     536     572     562     576    1665
              573     562     576    1666     572    1680
              587    1672     573     535     606    1663
              574    1666     581     562     566    1666
              581    1663     605     529     578     556
              573    1669     577     566     564    1669
              578    1665     603     531     577     557
              586     523     602    1674     562     537
              602    1667     580     527     608     530
              571    1667     601    1675     572    1662
              574

          name BTN_4
             9015    4479     582     551     576    1666
              581     553     577     532     607    1659
              578     533     606    1662     574    1667
              580    1661     607     528     579    1663
              605    1668     575     529     602    1667
              572    1669     578     556     582    1660
              577     556     584    1660     576     568
              571    1662     574     558     580     527
              602     533     575     559     581    1662
              583     552     579    1661     575     560
              579    1663     574    1669     599    1668
              579

          name BTN_5
             9019    4480     583     526     601    1668
              589     518     603     533     573    1667
              607     528     574    1668     600    1668
              579    1662     574     561     579    1662
              573    1669     599     535     572    1673
              597    1670     577     531     604    1669
              578    1659     572     576     563     531
              597    1672     575     532     608     527
              579     556     575     532     606     528
              580    1669     598    1663     573     536
              604    1663     573    1669     579    1664
              602

          name BTN_6
             9021    4481     572     533     606    1664
              581     527     603     532     576    1665
              603     530     579    1665     602    1666
              579    1662     576     557     572    1672
              575    1666     603     532     575    1685
              581    1669     580     528     601    1668
              579     529     600     534     572    1678
              593    1667     578     529     601     534
              573     560     577     531     600    1668
              580    1663     573     561     578     530
              598    1671     577    1664     572    1679
              590

          name BTN_7
             9013    4481     573     560     576    1666
              573     561     577     530     599    1670
              577     532     596    1671     576    1665
              583    1663     606     527     580    1661
              607    1661     575     534     606    1672
              564    1667     580     554     573     535
              603    1666     572     536     603    1665
              585     524     603     531     576    1666
              602     532     576    1666     602     532
              586    1655     602     534     575    1667
              601    1667     580     527     600    1677
              572

          name BTN_8
             9024    4484     570     529     599    1680
              566     530     598     538     570    1670
              598     535     573    1672     596    1669
              578    1666     571     563     575    1667
              571    1670     608     526     591    1652
              605    1664     573     534     606     533
              574     582     547     560     578     556
              556     554     573     560     578     556
              553     562     565    1670     580    1661
              605    1675     562    1670     578    1663
              604    1665     572    1679     567    1665
              603

          name BTN_9
             9043    4469     587     536     572    1670
              598     536     570     565     576    1666
              622     513     574    1666     570    1671
              607    1668     590     521     598    1666
              572    1666     579     555     573    1670
              576    1665     603     532     579    1662
              603     532     576     558     571     538
              600     533     576     559     579     527
              602     534     581     553     579    1663
              626    1616     600    1667     580    1662
              634    1608     599    1668     579    1665
              624

      end raw_codes

end remote

I've also changed a config file format for storing favorites and a current playlist in a JSON file and  added support for playlists created at soundcloud.com. Internet radiostations are supported through traditional m3u files that MPD understands. 

{
   "current": "PlayList1", 
   "soundcloud": [
      "soundcloud://playlist/xxxxxxxx"
   ], 
   "favorites": [
      "soundcloud://playlist/xxxxxxxx", 
      "InternetRadio1", 
      "InternetRadio2", 
      "InternetRadio3", 
      "InternetRadio4", 
      "LocalPLayList1", 
      "LocalPLayList2"
   ]
}

where

"current" - the currently playing playlist
"soundcloud" - a list of soundclud URL's
"favorites" - a list of all favorites assigned to IR buttons 


Finally, I had to change /etc/lirc/lircrc configuration file and Python script that is called when an IR button or combination of buttons are pressed.

/etc/lirc/lircrc:

begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_POWER
        config = sudo shutdown -h now
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_RIGHT
        config = /usr/bin/mpc next
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_LEFT
        config = /usr/bin/mpc prev
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PLAY
        config = /usr/bin/mpc toggle
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_1
        config = /home/pi/pls_man.py -s 1
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_2
        config = /home/pi/pls_man.py -s 2
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_3
        config = /home/pi/pls_man.py -s 3
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_4
        config = /home/pi/pls_man.py -s 4
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_5
        config = /home/pi/pls_man.py -s 5
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_6
        config = /home/pi/pls_man.py -s 6
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_PAUSE
    button = BTN_7
        config = /home/pi/pls_man.py -s 7
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_1
        config = /home/pi/pls_man.py -f 1
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_2
        config = /home/pi/pls_man.py -f 2
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_3
        config = /home/pi/pls_man.py -f 3
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_4
        config = /home/pi/pls_man.py -f 4
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_5
        config = /home/pi/pls_man.py -f 5
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_6
        config = /home/pi/pls_man.py -f 6
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_7
        config = /home/pi/pls_man.py -f 7
        flags = quit
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_LEFTALT
        config = /home/pi/pls_man.py -p
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_RIGHTALT
        config = /home/pi/pls_man.py -n
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_8
        config = /home/pi/pls_man.py -P
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = BTN_9
        config = /home/pi/pls_man.py -N
end
begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_VOLUMEUP
        repeat = 1
        config = mpc volume +1
end

begin
    remote = ONKYO-RC-287S
        prog   = irexec
    button = KEY_VOLUMEDOWN
        repeat = 1
        config = mpc volume -1
end
pls_man.py script is provided below

#!/usr/bin/env python
# encoding: utf-8
'''
pls_man -- Manage Play List

pls_man is a module for controlling play lists through an IR remote


@author:     ogryb

@copyright:  2013 Oleg Gryb. All rights reserved.

@license:    GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with luna_mech.  If not, see <http://www.gnu.org/licenses/>.


@contact:    oleg@gryb.info
@deffield    updated: 2013.11.18
'''

import sys
import os
import re
import json

from optparse import OptionParser

__all__ = []
__version__ = 0.1
__date__ = '2013-11-18'
__updated__ = '2013-11-18'

DEBUG = 0
TESTRUN = 0
PROFILE = 0

SOUND_CLOUD = ['soundcloud://playlist/12692664']

def main(argv=None):
    '''Command line options.'''

    program_name = os.path.basename(sys.argv[0])
    program_version = "v0.1"
    program_build_date = "%s" % __updated__

    program_version_string = '%%prog %s (%s)' % (program_version, program_build_date)
    #program_usage = '''usage: spam two eggs''' # optional - will be autogenerated by optparse
    program_longdesc = '''''' # optional - give further explanation about what the program does
    program_license = "Copyright 2013 Oleg Gryb                                            \
                Licensed under the GPL\nhttp://www.gnu.org/licenses/"

    if argv is None:
        argv = sys.argv[1:]
    try:
        # setup option parser
        parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license)
        parser.add_option("-c", "--config", dest="config", help="config json file [default: %default]", metavar="FILE")
        parser.add_option("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %default]")

        parser.add_option("-n", "--next", dest="nextpl",
                  action="store_true", default=False,
                  help="Next playlist")

        parser.add_option("-p", "--prev", dest="prevpl",
                  action="store_true", default=False,
                  help="Previous playlist")

        parser.add_option("-N", "--next-fav", dest="nextfav",
                  action="store_true", default=False,
                  help="Next favorites")

        parser.add_option("-P", "--prev-fav", dest="prevfav",
                  action="store_true", default=False,
                  help="Previous favorites")
       
        parser.add_option("-s", "--store-fav", dest="storenum",
                  help="Store favorite", default=0, type="int", metavar="NUMBER")

        parser.add_option("-f", "--play-fav", dest="playnum",
                  help="Store favorite", default=0, type="int", metavar="NUMBER")

        # set defaults
        parser.set_defaults(config="~/.pls_man/config", verbosity=0)

        # process options
        (opts, args) = parser.parse_args(argv)
       
        cf = os.path.expanduser(opts.config)
        if not os.path.isfile(cf):
            if not os.path.isdir(os.path.dirname(cf)):
                os.system("mkdir -p %s" % os.path.dirname(cf))
            cfj = dict()
            cfj['current'] = ''
            cfj['favorites'] = ["","","","","","",""]
            cfj['soundcloud'] = SOUND_CLOUD
            with open(cf, 'w') as out:
                json.dump(cfj, out)

        if not os.path.isfile(cf):
            sys.exit("Can's create file %s" % cf)  

        io = open(cf)
        cfj = json.load(io)
        io.close()
       
        if not cfj:
            cfj = dict()
       
        ind = 0

        if (opts.nextpl or opts.prevpl):
            if 'current' in cfj:
                s = cfj['current']
                lsa = []
                if 'soundcloud' in cfj:
                    lsa = cfj['soundcloud']
                ls = os.popen('mpc lsplaylists').read()
                lsa = lsa + ls.split("\n")
                del lsa [-1]
                for i in range(0,len(lsa)-1):
                    if (s == lsa[i]):
                        if opts.nextpl:
                            ind = i+1
                        else:
                            ind = i-1
                        break
                if ind < 0:
                    ind = len(lsa) - 1
                if ind >= len(lsa):
                    ind = 0
                if ind < 0:
                    ind = 0
                if ind < len(lsa):
                    os.system("mpc clear")
                    loaded = os.system("mpc load %s" % lsa[ind])
                    if loaded == 0:
                        os.system("mpc play")
                        cfj['current'] = lsa[ind]
                        with open(cf, 'w') as out:
                            json.dump(cfj, out)
  
        if (opts.nextfav or opts.prevfav):
            if 'current' in cfj:
                s = cfj['current']
                lsa = []
                if 'favorites' in cfj:
                    lsa = cfj['favorites']
                    for i in range(0,len(lsa)-1):
                        if (s == lsa[i]):
                            if opts.nextfav:
                                ind = i+1
                                while ind < len(lsa) and ((not lsa[ind]) or len(lsa[ind]) == 0):
                                    ind += 1
                            else:
                                ind = i-1
                                while ind >= 0 and ((not lsa[ind]) or len(lsa[ind]) == 0):
                                    ind -= 1
                            break
                    if ind < 0:
                        ind = len(lsa) - 1
                        while ind >= 0 and ((not lsa[ind]) or len(lsa[ind]) == 0):
                            ind -= 1
                    if ind >= len(lsa):
                        ind = 0
                        while ind < len(lsa) and ((not lsa[ind]) or len(lsa[ind]) == 0):
                            ind += 1

                    if 0 <= ind and ind < len(lsa):
                        os.system("mpc clear")
                        loaded = os.system("mpc load %s" % lsa[ind])
                        if loaded == 0:
                            os.system("mpc play")
                            cfj['current'] = lsa[ind]
                            with open(cf, 'w') as out:
                                json.dump(cfj, out)

        if ( 0 < opts.storenum and opts.storenum <= 7 ):
            if not 'favorites' in cfj:
                cfj['favorites'] = ["","","","","","",""]
            if 'current' in cfj:
                s = cfj['current']
                cfj['favorites'][opts.storenum-1] = s
                with open(cf, 'w') as out:
                    json.dump(cfj, out)
  
        if ( 0 < opts.playnum and opts.playnum <= 7 ):
            ind = opts.playnum - 1
            if 'favorites' in cfj and ind < len(cfj['favorites']):
                fav = cfj['favorites'][ind]
                if (len(fav) > 0):
                    os.system("mpc clear")
                    loaded = os.system("mpc load %s" % fav)
                    if loaded == 0:
                        os.system("mpc play")
                        cfj['current'] = fav
                        with open(cf, 'w') as out:
                            json.dump(cfj, out)

    except Exception, e:
        indent = len(program_name) * " "
        sys.stderr.write(program_name + ": " + repr(e) + "\n")
        sys.stderr.write(indent + "  for help use --help")
        return 2


if __name__ == "__main__":
    if DEBUG:
        sys.argv.append("-h")
    if TESTRUN:
        import doctest
        doctest.testmod()
    if PROFILE:
        import cProfile
        import pstats
        profile_filename = 'pls_man_profile.txt'
        cProfile.run('main()', profile_filename)
        statsfile = open("profile_stats.txt", "wb")
        p = pstats.Stats(profile_filename, stream=statsfile)
        stats = p.strip_dirs().sort_stats('cumulative')
        stats.print_stats()
        statsfile.close()
        sys.exit(0)
    sys.exit(main())


Everything works as expected so far, switching between Internet radio-stations and local MPD's playlists stored on Raspberry's is very easy now.
         


No comments:

Post a Comment