Musica.pm

Musica

SYNOPSIS

Important Note:

   Because the new FM-Tuner keypads (and possibly other newer keypads?) take
   *forever (minutes) to respond to the StatVer command, their version is
   stored in the persistant Misterhouse %Save hash.  This means that if you
   change the physical keypad, you'll need to stop Misterhouse, edit
   data_dir/mh_temp.saved_states.persistent, and search for 'Musica' and
   remove or modify the entry for the zone you chaged.  Then start Misterhouse
   back up.  If you simply remove a keypad, just take the entry for that zone
   out of your .mht file -- you don't need to worry about %Save.

   Also, some keypads never respond to the StatVer command Misterhouse sends
   to try to find the version of each keypad.  Watch your print log and if
   You see StatVer being re-send over and over again for a specific keypad,
   you may want to specify its version in your .mht file as so:

      MUSICA_ZONE, music_kitchen,    Musica, 1, 40822

   My keypads (version 40822) do respond to StatVer after 2-3 minutes and
   then this version is stored by the Musica module in the persistent %Save
   hash.

   The only keypad versions I know are:
      30419: MU4601 keypad
      40822: MU4602 FM-Tuner keypad

Initialization:

   You can define any number of Musica systems, but each one requires its own
   serial port.  To begin with, come up with a name for your object such as
   $Musica.  Then add an entry to your mh.private.ini file:

      Musica_serial_port=/dev/ttyS9

   If you wanted to define two Musica systems, you would just choose two unique
   names:

      Musica1_serial_port=/dev/ttyS9
      Musica2_serial_port=/dev/ttyS10

   Once you have specified which serial port to use, simply define the main
   object and your zone objects in your .mht file:

      MUSICA, Musica
      MUSICA_ZONE, music_kitchen,    Musica, 1
      MUSICA_ZONE, music_master_bed, Musica, 2

   This would define the object $Musica, with zone 1 named $music_kitchen, and
   zone 2 named $music_master_bed.  If you have more than one system to
   control, just use the same names as you used in your .ini file:

      MUSICA, Musica1
      MUSICA, Musica2
      MUSICA_ZONE, music_kitchen,     Musica1, 1
      MUSICA_ZONE, music_master_bed,  Musica1, 2
      MUSICA_ZONE, music_patio,       Musica2, 1
      MUSICA_ZONE, music_dining_room, Musica2, 2

   Finally, you can define an object representing each of the four Musica
   sources if you so desire.  This is only necessary if you need to do
   something like start or stop an MP3 player or turn something on via X10 when
   a source is accessed, if you want to watch for users changing the source
   labels from keypads, or if you want to change the source labels from
   Misterhouse.  Finally, if you want to watch for button presses or cause
   buttons to be pressed through Misterhouse, these are useful.  These are not
   required for casual use.

      MUSICA_SOURCE, mrhouse_speech, Musica, 1
      MUSICA_SOURCE, am_fm_tuner,    Musica, 2
      MUSICA_SOURCE, music_source3,  Musica, 3
      MUSICA_SOURCE, music_source4,  Musica, 4

Interface Overview:

   All objects (the main Musica object, the zone objects, and the source objects)
   will return various states (as documented below) when somebody performs any
   kind of action from a keypad.

   If somebody changes the volume in a specific zone, for example, the object
   for that zone will have a state of 'volume_changed'.  You can watch for
   those states as follows:

      if ($state = state_now $music_kitchen) {
         print_log "Kitchen keypad new state: $state";
      }

   Note that when changes are made FROM Misterhouse (using the various
   controlling functions such as set_volume()) any resulting object state
   changes will have the set_by set to 'misterhouse'.  By contrast, new states
   initiated by the keypad or Musica remote control have a set_by of 'keypad'.
   You can also see that your command took effect by calling get_volume() and
   seeing that it returns the volume you set.  Note that this function will
   return your new value only after your command has been sent to the Musica
   system and it has confirmed your command.  If there is a large queue of
   pending commands this can take several seconds.

   Also, many functions can be called on either the main Musica object or one
   only one zone object.  You can, for example, call $Musica->set_volume('100%')
   to set all zones to 100% volume or call $music_kitchen->set_volume('100%') to
   only change the volume of one zone.  But even if you change the volume of
   all zones using the main object, you must still call get_volume() on the zone
   objects since the system as a whole doesn't actually have a volume.

   Finally, if any object returns a state of 'error', it means that an error
   was encountered with the initialization of the system (i.e. a zone keypad
   was not found) or there was a problem executing one of your commands.
   The error will be displayed in the print log but you can also call
   get_last_error() on any object to see this error message.

Controlling either all zones or one zone:

   These functions can all be called on the main Musica object to influence
   all zones or can be called on one specific zone object:

   set_treble(level): Sets the treble level for the zone.  Valid values are -14
   through 14 with 0 being the default (note that the keypads only have a
   resolution of two, such as -4, -2, 0, 2, 4, and a value such as 1 will be
   the same as 0 or 2).

   set_bass(level): Sets the bass level for the zone.  Valid values are -14
   through 14 with 0 being the default (note that the keypads only have a
   resolution of two, such as -4, -2, 0, 2, 4, and a value such as 1 will be
   the same as 0 or 2).

   set_source(identifier): Changes zone or zones to specified source.  The
   parameter can be a number 1-4, the letter 'E' for the local expansion port,
   and the letter 'F' for the integrated FM tuner.
   the label assigned to a specific source (such as 'MP3'), or a source object.
   Examples:
      $zone1_obj->set_source(1);
      $Musica->set_source('E');
      $zone1_obj->set_source('MP3');
      $Musica->set_source($source1_obj);

   set_volume(level): Sets the volume of one or all zones, where the level
   specified must range from 0 to 35.  Can also be specified as a percentage
   where '0%' is off and '100%' is full volume.

   set_balance(level): Sets the balance where -7 is full-left, +7 is full-right,
   and 0 is centered.

   mute(): Mutes one or all zones.

   unmute(): Unmutes one or all zones.

   loudness_on(): Turns on loudness in one or all zones.

   loudness_off(): Turns off loudness in one or all zones.

   internal_amp(): Use only the internal amplifier.

   both_amps(): Use both internal and external amplifiers.

   external_amp(): Use only the external amplifier.

   green_backlight(): Set backlight to the color green.

   amber_backlight(): Set backlight to the color amber.

   white_backlight(): Set backlight to the color white (instead of green when applicable).

   blue_backlight(): Set backlight to the color blue (instead of amber when applicable).

   set_backlight_brightness(level): Sets backlight level to a value between
      0 and 8 where 0 is off and 8 is full brightness.  Can also be specified
      with a percentage where '0%' is off and '100%' is full brightness.

   nudge_volume_down(): Reduces the volume one notch.

   nudge_volume_up(): Increases the volume one notch.

   nudge_bass_down(): Reduces the bass one notch.

   nudge_bass_up(): Increases the bass one notch.

   nudge_treble_down(): Reduces the treble one notch.

   nudge_treble_up(): Increases the treble one notch.

   nudge_balance_left(): Balance shifted one notch to the left.

   nudge_balance_right(): Balance shifted one notch to the right.

   lock_menu(): Locks the menu on the keypad to prevent the user from
      entering the setup menu.  Source selection and volume can still
      be changed.

   unlock_menu(): Unlocks the menu.

   set_preset_label(number, label): Sets FM preset 1-8 to specified numeric
   or text label.  Label must be one listed in %source_name_to_number_30419.

   set_preset_frequency(number, freq): Sets FM preset 1-8 to specified frequency,
   where 8950 is 89.5, for example.

Controlling the Musica system object:

   The following functions allow you to make changes to the Musica system
   as a whole from Misterhouse.

   all_off(): Turn off all zones.

Retrieving data from the Musica system object:

   get_object_version(): Returns the version of the Musica object.
   get_port_name(): Returns the name of the serial port used for this object.
   get_zones(): Returns list of zone objects associated with this system.
   get_sources(): Returns list of source objects associated with this system.
   get_adc_version(): Returns the version string as reported by the Audio
      Distribution Center.

Monitoring the Musica system object:

   Following is a list of states that may be returned by the Musica system:

   error: An error has occurred, call get_last_error() for details.

Controlling the Musica zone objects using functions:

   The following functions allow you to make changes to a specific Musica
   zone from Misterhouse.

   set_source(source): Sets the zone to the specified source, where 'source'
   is a number between 1 and 4 or E for the local expansion source.  Turns
   the zone ON if it is not already on.

   turn_off(): Turns off the zone.
   nudge_source_up(): Switches to the next source (must already be on).
   nudge_source_down(): Switches to the previous source (must already be on).
   delay_off(seconds): Automatically turn the zone off in the specified number
      of seconds.  Timer is cancelled if the user selects a new source or if
      this function is called with '0' for the argument.  Also can be reset
      by calling this function again.  Returns current delay if no argument
      is provided.

Controlling the Musica zone objects using set()

   The following input states are recognized by the Musica zone objects:
      off: Turn zone off
      1: Turn to source 1
      2: Turn to source 2
      3: Turn to source 3
      4: Turn to source 4
      E: Turn to external input
      volumeXX: Set volume where XX ranges from 0 to 35
      mute: Mutes the zone
      unmute: Unmutes the zone

Retrieving data from the Musica zone object:

   get_musica_obj(): Returns the main musica object.
   get_zone_num(): Returns the zone number (from 1 to 6) for this object.
   get_keypad_version(): Returns the version number returned by the keypad.
   get_last_on_time(): Returns the last time the keypad was turned on
      (in Epoch format, as $::Time expresses it)
   get_source(): Returns the currently-selected source number, or 0 if off.
   get_source_obj(): Returns the currently-selected source object.
   get_volume(): Returns the volume, from 0 to 35.
   get_bass_level(): Returns the bass level from -14 to 14.
   get_treble_level(): Returns the bass level from -14 to 14.
   get_balance(): Returns the balance where -7 is full-left, 7 is full-right,
      and 0 is centered.
   get_loudness(): Returns 1 if on, 0 if off.
   get_mute(): Returns 1 if on, 0 if off.
   get_blcolor(): Returns 'green' or 'amber' to indicate backlight color.
      ('green' == 'white' and 'amber' == 'blue' on applicable keypads)
   get_brightness(): Returns backlight brightness from 0 to 8 where 0 means
      that the backlight is currently off.
   get_audioport(): Returns 1 if the audioport is connected.
   get_amp(): Returns 'room_amp', 'both', or 'external_amp'.
   get_locked(): Returns 1 if the keypad is locked.
   get_overheat(): Returns 1 if the keypad is overheated.
   get_button_labels(): Returns a list of the names of the 12 programmable
      buttons on the Musica keypad and remote.
   press_button(button): Sends the IR code associated with pressing the
      specified button for this source.  Button can be specified as either
      a number from 1 to 12 or as a label as returned by get_button_labels().
   hold_button(button): Sends the IR code associated with holding the
      specified button for this source.  Button can be specified as either
      a number from 1 to 12 or as a label as returned by get_button_labels().

Monitoring the Musica zone objects:

   You can watch for state changes of the Musica zone object to see when a
   user changes the system in a way that affects a particular zone.

   error: An error has occurred, call get_last_error() for details.
   zone_on: The zone was turned on from an off state.
   zone_off: The zone was turned off from an on state.
   source_changed: The zone is already on and a user changed the source.
   changed_label_source_X: This zone keypad was used to change the label
      of source X (where X is a number from 1 to 4).
   volume_changed: Volume of the zone was changed.
   bass_changed: Bass level of the zone was changed.
   treble_changed: Treble level of the zone was changed.
   balance_changed: Balance level of the zone was changed.
   loudness_on: Loudness was enabled in the zone
   loudness_off: Loudness was disabled in the zone
   mute_on: This zone was muted.
   mute_off: This zone was unmuted.
   color_amber: The backlight color in this zone was changed to amber
      (or blue on applicable keypads).
   color_green: The backlight color in this zone was changed to green
      (or white on applicable keypads).
   backlight_on: The backlight was turned on (not given when the backlight
      comes on when the zone is turned on).
   backlight_off: The backlight was turned off.
   brightness_changed: The backlight brightness was changed.
   internal_amp: The user has chosen to use the internal amp only.
   internal_external_amp: The user has chosen to use the internal amp as well
      as an amp connected through the Expansion Interface Module.
   external_amp: Only the external amp is being used.
   locked: The keypad has been locked
   unlocked: The keypad has been unlocked
   overheated: The zone keypad has become overheated
   heat_normal: The zone keypad is no longer overheated
   button_pressed_*: A button was pressed (button names can be found after
      this comment section or by calling get_button_labels() on a zone
      object).
   button_held_*: A button was pressed and held (button names can be found
      after this comment section or by calling get_button_labels() on a
      zone object).

Controlling the Musica source object:

   The following functions allow you to make changes to a particular source
   accessible from the Musica system.

   set_label(label): Sets the global label for this particular source.  The
      label can either be given as a number between 1 and 30 or as a string.
      Only certain strings are allowed, and these strings can be determined
      by calling get_source_labels() on a source object.

Retrieving data from a Musica source object:

   get_musica_obj(): Returns the main musica object.
   get_source_num(): Returns the numerical source number associated with
      this source object.
   get_label(): Returns the label for the source as a string.
   get_source_labels(): Returns a list of valid source labels.
   get_zones(): Returns array of zones currently listening to this source.
   get_usage_count(): Returns the number of zones currently listening to
      this source.

Monitoring the Musica source objects:

   You can watch for state changes of the Musica source object to see when a
   user changes the system in a way that affects a particular source.

   error: An error has occurred, call get_last_error() for details.
   label_changed: somebody changed the label for this source from a keypad
      (call get_set_by() to find the object name of the keypad used to make
      the change).
   first_listener: the source now has a listener and it did not before.
   no_listeners: the only listener stopped listening and now nobody is using
      this particular source.
   listener_zone_X: zone X just selected this source.
   button_pressed_*: A button was pressed (button names can be found after
      this comment section or by calling get_button_labels()).
   button_held_*: A button was pressed and held (button names can be found
      after this comment section or by calling get_button_labels()).

Usage Examples:

   To better understand the following examples, you should know that I have
   an array of MP3 players defined as:

      my @players;
      if ($Startup) {
         push @players, undef;
         foreach ('channel12', 'channel34', 'channel56', 'channel78') {
            push @players, new AlsaPlayer($_);
         }
      }

   Then I create an array of all of the Musica::Source objects (which
   are already defined in my .mht file):

      my @musica_sources = ( $music_source1, $music_source2, $music_source3,
                             $music_source4 );

   The 'first_listener' and 'no_listeners' states are handy to start/stop
   or pause/unpause something like an MP3 player, or even to power on/off
   sources or send out IR commands.

      foreach (@musica_sources) {
         if (state_now $_ eq 'first_listener') {
            $players[$_->get_source_num()]->unpause();
         }
         if (state_now $_ eq 'no_listeners') {
            $players[$_->get_source_num()]->pause();
         }
      }

   To watch for button presses on all keypads and all sources and take
   the appropriate action:

      foreach (@musica_sources) {
         if (state_now $_ eq 'button_pressed_next') {
            $players[$_->get_source_num()]->next_song();
         }
         if (state_now $_ eq 'button_pressed_previous') {
            $players[$_->get_source_num()]->previous_song();
         }
         if (state_now $_ eq 'button_pressed_pause') {
            $players[$_->get_source_num()]->pause_toggle();
         }
         if (state_now $_ eq 'button_pressed_play') {
            $players[$_->get_source_num()]->unpause();
         }
      }

MUSICA SYSTEM BUGS:

   [ADC Version M30419/Keypad Version R40822 (FM-Tuner)]
   - Keypad takes FOREVER (1-4 minutes) to respond to StatVer.
   - When you hold down a button on the keypad often times a button pressed
   event is sent right after the button held event.  This code will ignore
   a press right after a hold.
   - Never sends back a copy of the ChangeAmp command nor does it send
   an EventData message back.
   - Does not respond to various commands like ChangeVol, ChangeBass, etc.
   Instead, sends an EventData message showing the changes (bug or protocol
   change?)

   [ADC Version M30419/Keypad Version R30419]
   If I send the ChangeSrc command at least three seconds after the
   ChangeStore command then it works fine.  But if the gap is two seconds or
   less the zone never turns on like it should.

   [ADC Version M30419/Keypad Version R30419]
   ADC never responds to a NudgeSrc to a zone that is off.
      Workaround: only sends command if the zone is already on.

   [ADC Version M30419/Keypad Version R30419]
   ChangeSwOu command is always responded to with ChangeSwOu/1 whether you
   are trying to turn it on or off, and it seems to have no effect?

   [ADC Version M30419/Keypad Version R30419]
   The backlight brightness sent in EventData comes in as 0 until you
   change the backlight from its original value (or at least that is
   what happened to me).

TODO:

   - Detect door/phone muting by watching for volume changes?
   - Implement IR_Dn/IR_Up?
   - ExeMenu* is not implemented as the ADC does not respond which makes it
     not like every other command plus I don't know why you would use it as it
     just allows you to navigate the menu system on a keypad but you can
     change everything directly through other messages anyways.  But, for the
     record:
        ExeMenu/Zone/Command where Command is:
           1: Menu button
           2: Up Arrow
           3: Down Arrow
           4: Left Arrow
           5: Right Arrow

DESCRIPTION

Allows control of the Musica whole-house audio system by Netstreams over the RS232 port. This system has excellent controllability through Misterhouse and provides 6 zones and 4 sources with very nice keypads.

http://www.netstreams.com

If you are interested in installing a Netstreams Musica system yourself, contact me and I can put you in contact with somebody who can give you a good price.

Note that you must use a null-modem cable between the Musica system and your Misterhouse computer.

This module uses Rev 2.0 of the Musica RS-232 protocol.

INHERITS

Serial_Item

METHODS

UnDoc

INI PARAMETERS

NONE

AUTHOR

Kirk Bauer kirk@kaybee.org

SEE ALSO

NONE

LICENSE

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 Musica.pm