<reponame>leochencipher/PiRadio<filename>resources/piradio.py<gh_stars>1-10
import datetime
from threading import Thread
from time import sleep

from .menubase import RadioMenu
from .radioselector import RadioSelector
from .display import RadioDisplay
from .volume_control import VolumeControl


# Initial volume on boot (0-100)
INITIAL_VOLUME = 50

# Define pin layouts

# Display pin mapping
lcd_rs = 17
lcd_en = 18
lcd_d4 = 27
lcd_d5 = 22
lcd_d6 = 23
lcd_d7 = 24
lcd_backlight = 25

# Volume control pin mapping
vol_a = 19
vol_b = 20
vol_button = 21

# Selection dial pin mapping
sel_a = 5
sel_b = 6
sel_button = 13


class PiRadio(object):
    """PiRadio class definition.

       This class handles the initialisation of the display as well as the
       volume and selection rotary encoders.

       The class switches between defined modes as requested by the user.

       The class also provides two functions for updating information on the
       display:

         Volume information
         Clock

       Finally, the class also provides a graceful shutdown of the radio on an
       exit event.
    """

    def __init__(self, pi, modes):
        """Initialisation of the radio. Requires two (mandatory) parameters:

             pi:    A pigpio instance
             modes: A list of initialised radio modes

          Note: Initialising the class does not start the application.
        """
        self.pi = pi

        # Define the volume control and set its callback function
        self.volume_control = VolumeControl(self.pi, vol_a, vol_b, vol_button,
                                            cb=self.vol_change)

        # Define the selection control (we don't use a callback here as the
        # control will be bound to the menu object later)
        self.selector = RadioSelector(self.pi, sel_a, sel_b, sel_button)

        # Define the LCD disply
        self.lcd = RadioDisplay(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7,
                                lcd_backlight)

        # Define the main menu object and set up some callbacks
        self.main_menu = RadioMenu("", modeselect=self.change_mode,
                                       cb_display=self.menu_change)

        # Build our menu based on the modes
        for mode in modes:

            # Modes need access to the LCD queue
            mode.display_q = self.lcd.queue

            # Add the mode-specific menu to our own menu
            self.main_menu.add_item(mode.modemenu)

        # Bind the selection control actions to the main menu
        self.selector.bind_rotate(self.main_menu.rotate)
        self.selector.bind_select(self.main_menu.select)

        # No mode set at the moment
        self.mode = None

        # Set the initial volume level
        self.vol_change(INITIAL_VOLUME)

        # Blank time for the display
        self.now = "00:00"

        # Set up a thread to change the time in the LCD and daemonise it
        self._time = Thread(target=self._time_worker)
        self._time.daemon = True

    def start(self):
        """Method to start all the controls required to run the radio."""

        # Set a flag to show we're running
        self.running = True

        # Start radio controls and display
        self.volume_control.start()
        self.selector.start()
        self.lcd.start()

        # Start the time thread
        self._time.start()

    def exit(self):
        """Method to stop the radio and shutdown gracefully."""

        # Set the flag (stops threads that are watching this)
        self.running = False

        # Run the active mode's exit method so processes can be stopped
        try:
            self.mode.exit()
        except (AttributeError, TypeError):
            pass

        # Remove text from the display
        self.lcd.clear()

        # Turn the backlight off
        self.lcd.set_backlight(0)

    def change_mode(self, newmode):
        """Method to change the active mode of the radio."""

        # Run the current mode's exit method to stop processes
        if self.mode:
            self.mode.exit()

        # Set the new mode
        self.mode = newmode

        # Run the enter method of the mode to start relevant processes
        self.mode.enter()

        # Remove the metadata from the display
        self.lcd.clear_metadata()

        # Upate the mode name on the display
        self.lcd.queue.put(("mode", newmode.name))

    def menu_change(self, txt):
        """Simple method to change the menu info on the display."""
        if self.lcd.queue:
            self.lcd.queue.put(("menuinfo", txt))

    def vol_change(self, level):
        """Method to provide graphical representation of volume on the
           display.
        """

        # We've got 10 boxes for volume so rebase the percentage number
        l = int(level/10)

        # Create the volume string (square blocks for volume padded with '-')
        vol = "{v:-<10}".format(v=chr(255)*l)

        # Send the string to the display
        if self.lcd.queue:
            self.lcd.queue.put(("vol", "{vol}".format(vol=vol)))

    def _time_worker(self):
        """Thread to update the time on the display."""

        # Loop to keep thread running while radio is running
        while self.running:

            # Get the time
            now = datetime.datetime.now().time()

            # Pretty formatting
            timestring = "{h:0>2}:{m:0>2}".format(h=now.hour, m=now.minute)

            # We only update if the time has changed
            if timestring != self.now:
                self.lcd.queue.put(("time", timestring))
                self.now = timestring

            # 1 second refresh should be accurate enough
            sleep(1)

import datetime
from threading import Thread
from time import sleep

from .menubase import RadioMenu
from .radioselector import RadioSelector
from .display import RadioDisplay
from .volume_control import VolumeControl


# Initial volume on boot (0-100)
INITIAL_VOLUME = 50

# Define pin layouts

# Display pin mapping
lcd_rs = 17
lcd_en = 18
lcd_d4 = 27
lcd_d5 = 22
lcd_d6 = 23
lcd_d7 = 24
lcd_backlight = 25

# Volume control pin mapping
vol_a = 19
vol_b = 20
vol_button = 21

# Selection dial pin mapping
sel_a = 5
sel_b = 6
sel_button = 13


class PiRadio(object):
    """PiRadio class definition.

       This class handles the initialisation of the display as well as the
       volume and selection rotary encoders.

       The class switches between defined modes as requested by the user.

       The class also provides two functions for updating information on the
       display:

         Volume information
         Clock

       Finally, the class also provides a graceful shutdown of the radio on an
       exit event.
    """

    def __init__(self, pi, modes):
        """Initialisation of the radio. Requires two (mandatory) parameters:

             pi:    A pigpio instance
             modes: A list of initialised radio modes

          Note: Initialising the class does not start the application.
        """
        self.pi = pi

        # Define the volume control and set its callback function
        self.volume_control = VolumeControl(self.pi, vol_a, vol_b, vol_button,
                                            cb=self.vol_change)

        # Define the selection control (we don't use a callback here as the
        # control will be bound to the menu object later)
        self.selector = RadioSelector(self.pi, sel_a, sel_b, sel_button)

        # Define the LCD disply
        self.lcd = RadioDisplay(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7,
                                lcd_backlight)

        # Define the main menu object and set up some callbacks
        self.main_menu = RadioMenu("", modeselect=self.change_mode,
                                       cb_display=self.menu_change)

        # Build our menu based on the modes
        for mode in modes:

            # Modes need access to the LCD queue
            mode.display_q = self.lcd.queue

            # Add the mode-specific menu to our own menu
            self.main_menu.add_item(mode.modemenu)

        # Bind the selection control actions to the main menu
        self.selector.bind_rotate(self.main_menu.rotate)
        self.selector.bind_select(self.main_menu.select)

        # No mode set at the moment
        self.mode = None

        # Set the initial volume level
        self.vol_change(INITIAL_VOLUME)

        # Blank time for the display
        self.now = "00:00"

        # Set up a thread to change the time in the LCD and daemonise it
        self._time = Thread(target=self._time_worker)
        self._time.daemon = True

    def start(self):
        """Method to start all the controls required to run the radio."""

        # Set a flag to show we're running
        self.running = True

        # Start radio controls and display
        self.volume_control.start()
        self.selector.start()
        self.lcd.start()

        # Start the time thread
        self._time.start()

    def exit(self):
        """Method to stop the radio and shutdown gracefully."""

        # Set the flag (stops threads that are watching this)
        self.running = False

        # Run the active mode's exit method so processes can be stopped
        try:
            self.mode.exit()
        except (AttributeError, TypeError):
            pass

        # Remove text from the display
        self.lcd.clear()

        # Turn the backlight off
        self.lcd.set_backlight(0)

    def change_mode(self, newmode):
        """Method to change the active mode of the radio."""

        # Run the current mode's exit method to stop processes
        if self.mode:
            self.mode.exit()

        # Set the new mode
        self.mode = newmode

        # Run the enter method of the mode to start relevant processes
        self.mode.enter()

        # Remove the metadata from the display
        self.lcd.clear_metadata()

        # Upate the mode name on the display
        self.lcd.queue.put(("mode", newmode.name))

    def menu_change(self, txt):
        """Simple method to change the menu info on the display."""
        if self.lcd.queue:
            self.lcd.queue.put(("menuinfo", txt))

    def vol_change(self, level):
        """Method to provide graphical representation of volume on the
           display.
        """

        # We've got 10 boxes for volume so rebase the percentage number
        l = int(level/10)

        # Create the volume string (square blocks for volume padded with '-')
        vol = "{v:-<10}".format(v=chr(255)*l)

        # Send the string to the display
        if self.lcd.queue:
            self.lcd.queue.put(("vol", "{vol}".format(vol=vol)))

    def _time_worker(self):
        """Thread to update the time on the display."""

        # Loop to keep thread running while radio is running
        while self.running:

            # Get the time
            now = datetime.datetime.now().time()

            # Pretty formatting
            timestring = "{h:0>2}:{m:0>2}".format(h=now.hour, m=now.minute)

            # We only update if the time has changed
            if timestring != self.now:
                self.lcd.queue.put(("time", timestring))
                self.now = timestring

            # 1 second refresh should be accurate enough
            sleep(1)
# Initial volume on boot (0-100) # Define pin layouts # Display pin mapping # Volume control pin mapping # Selection dial pin mapping PiRadio class definition.         This class handles the initialisation of the display as well as the        volume and selection rotary encoders.         The class switches between defined modes as requested by the user.         The class also provides two functions for updating information on the        display:           Volume information          Clock         Finally, the class also provides a graceful shutdown of the radio on an        exit event. Initialisation of the radio. Requires two (mandatory) parameters:               pi:    A pigpio instance              modes: A list of initialised radio modes            Note: Initialising the class does not start the application. # Define the volume control and set its callback function # Define the selection control (we don't use a callback here as the # control will be bound to the menu object later) # Define the LCD disply # Define the main menu object and set up some callbacks # Build our menu based on the modes # Modes need access to the LCD queue # Add the mode-specific menu to our own menu # Bind the selection control actions to the main menu # No mode set at the moment # Set the initial volume level # Blank time for the display # Set up a thread to change the time in the LCD and daemonise it Method to start all the controls required to run the radio. # Set a flag to show we're running # Start radio controls and display # Start the time thread Method to stop the radio and shutdown gracefully. # Set the flag (stops threads that are watching this) # Run the active mode's exit method so processes can be stopped # Remove text from the display # Turn the backlight off Method to change the active mode of the radio. # Run the current mode's exit method to stop processes # Set the new mode # Run the enter method of the mode to start relevant processes # Remove the metadata from the display # Upate the mode name on the display Simple method to change the menu info on the display. Method to provide graphical representation of volume on the            display. # We've got 10 boxes for volume so rebase the percentage number # Create the volume string (square blocks for volume padded with '-') # Send the string to the display Thread to update the time on the display. # Loop to keep thread running while radio is running # Get the time # Pretty formatting # We only update if the time has changed # 1 second refresh should be accurate enough