Logitech Media Server

From Domoticz
Revision as of 07:07, 11 July 2019 by Philchillbill (talk | contribs) (→‎Setup process)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Documentation for Logitech Media Server

Introduction

Logitech Media Server (formerly SlimServer, SqueezeCenter and Squeezebox Server) is a streaming audio server supported by Logitech (formerly Slim Devices), developed in particular to support their Squeezebox range of digital audio receivers. Logitech EOL'd the product line in 2012 but the open-source LMS software is still actively developed in a so-called community effort. The latest version can be downloaded here - http://downloads.slimdevices.com/nightly. Synology provides updates to a fairly recent community build from time to time on their beta channel.

Logitech Media Server in Domoticz

Logitech Media Server Settings

Your Logitech Media Server must be configured to allow Domoticz to control the attached players. Default port for Web Server connections is 9000 or 9002.

Adding Hardware

Add the Logitech Media Server via the Hardware page under Settings. Fill in the IP of your Logitech Media Server and the port you specified.

Domoticz Settings

Click 'Setup' to see the detected/connected players.

  • Poll interval controls how often Domoticz will attempt to reconnect to the Logitech Media Server to poll the player's status.
  • Ping timeout controls how long Domoticz will try to contact the Logitech Media Server.

Detected Players

If you entered the correct IP & port of your Logitech Media Server, your players will be detected and displayed under Devices. It displays names and MAC-addresses.

Supported Models

The supported models are:

  • Squeezebox 1
  • Squeezebox 2
  • Squeezebox 3
  • Squeezebox Receiver
  • Squeezebox Boom
  • Squeezebox Controller
  • SqueezePlay
  • Squeezebox Radio
  • Squeezebox Touch
  • iPeng iOS App (iPhone & iPad)
  • Max2Play SqueezePlug
  • PiCoreplayer based on Squeezelite software

Troubleshooting

If your player model is not supported, it will be reported in the logfile. The logfile also reports detected players when starting the Logitech Media Server hardware.

Events and Notifications

Domoticz device events work for Logitech Media Players using both Blockly and LUA.

Events and notifications are triggered for:

On
Off/Disconnected
Play
Pause
Stop

The following actions can be performed by using 'Set' on a Logitech Media Player.

On or Group On Will turn the device on.
Off or Group Off Will turn the device off.
Play Will send a Play command.
Pause Will toggle between Play and Pause.
Stop Will send a Stop command.
Set Volume <0-100> Will set the volume of the player-output.
Play Playlist <name> Will play the playlist with name <name>.
Play Favorites Will play the favorites.

Example:
commandArray['SqueezeBox']='On'
commandArray['SqueezeBox']='Play'
commandArray['SqueezeBox']='Set Volume 30'
commandArray['SqueezeBox']='Play Playlist WakeUpList1'
commandArray['SqueezeBox']='Play Favorites

On Screen Notifications

Domoticz can display notification messages on the screens of certain Logitech Media Players. This is configured via the Notifications tab on the Settings page as shown here:

Fill in the MAC-address (separated by a semicolon) and the duration that the message will be displayed on the screen (in seconds) The MAC-address of your player(s) can be found in the Hardware-settings of your Logitech Media Server hardware.

Known Issues

  • (Song-)Titles with special characters will show wrong symbols in the logfile.

Work in Progress

  • Add support for Timers (schedule your player to play playlists)

Audio Alerts via Squeezebox Players

The Squeezebox radio and boom players can be used to play audio alerts from Domoticz via the following perl scripts. The concept will work with any type of player (including software players) but the radio and boom are well-suited as they have their own amplifier and can therefore act autonomously.

Two different approaches are shown. The first one uses a script that's multi-threaded and will play alerts on multiple players around your home at more or less the same time (but not truly synced). This has the advantage of not interfering with any syncgroups you may habitually use. The second approach assembles/disassembles a temporary syncgroup from the players in question, meaning no echo around the house if you deploy alerts to a lot of players at the same time. You can try both approaches and see which one works best for you. One thing worth knowing about syncgroups is that they often start out with a small time-difference between players which is then quickly corrected by LMS (it speeds up a lagging player to catch up). If this happens when playing an audio alert then part of the speech will be skipped over as the sync progresses. YMMV but if it happens a lot in your situation then it's best to use the multi-threaded version.

How it works

The basic idea (with both approaches) is to first retrieve and save several current parameters from each player: power state (on/off), play-mode (play/pause/stop), repeat-mode (none/song/playlist), current-playlist (if any), current time-position within currently-playing song (if any), plus the current volume setting. Next we play the desired alert audio clips - first a 'pre-chime' to get our attention, then the actual alert message MP3 audio file (you can pre-generate a series of these using e.g. text-to-speech software). In doing this we power-on each player if it was off, pause the current song if the player was active, set repeat to 'none' so we don't play an infinite loop of alerts, and set the desired volume-level. When we see the alert has come to an end, we return everything back the way it was prior to the alert. In the case of the second approach which builds temporary syncgroups, the group is also disassembled.

The heavy-lifting is done by a perl subroutine called &squeezeAlert which is contained in a perl script squeezealert.pl. In use, we will call this from a tiny bash script sqalert.sh which takes care of running the perl job as a background process so we return immediately to Domoticz and there's no waiting. This bash pre-script also allows us to use ssh to execute the perl on a remote machine if we need to (see below).

In the on-action / off-action fields of a Domoticz switch, you can now use a construct along the lines of

script://sqalert.sh kitchen Ring06.wav downloadReady.mp3 60
script://sqalert.sh livingroom none garageDoorClosed.mp3 50
script://sqalert.sh "kitchen, livingroom" Ring06.wav garageDoorOpen.mp3 55
script://sqalert.sh "livingroom, office, studio" Ring06.wav washingmachineFinished.mp3 70

See the comments in the code for an exact description of the 4 parameters (players, prechime, ttsfile, volume). If it doesn't work for you, try specifying the full path to the bash file with a triple-slash

script:///home/pi/domoticz/scripts/sqalert.sh livingroom none garageDoorClosed.mp3 50

Tip: You can copy some of the audio clip files in a Windows /media folder over to your alerts directory as pre-alert chimes (the RingXX.wav ones are nice for this purpose). The spoken messages alerting for things like 'Motion detected in Hallway' or 'Garage door has been left open longer than 10 minutes' can be saved to mp3 files using an online service like Amazon Polly.

Setup Considerations

With all the perl scripts in this LMS wiki, some modules from CPAN (Comprehensive Perl Archive Network) need to be installed. This is no problem on Linux and on Windows, but is a major problem on Synology NAS devices which have a rather stripped-down Linux under the hood with no build environment. There is a simple workaround for this using remote scripting, but let's first get something clear about where/what needs to be installed. When any one of the perl scripts here are active, 3 different pieces of software are ultimately involved:

1. LMS server software
2. Domoticz
3. Our perl script(s)

These 3 can all run on the same computer but they can also be run on 3 different computers with no problems whatsoever. Synology users can run both LMS and Domoticz e.g. on their Synology, but run the perl script(s) on a separate perl-friendly machine such as a spare RPi. This is possible because we can get a Domoticz on/off action to run a script on a remote machine by using ssh (explained below) with Public-Key Authentication. You also only need one instance of Domoticz running, so if you are using a separate machine for the perl-scripts then it does not need to have a slave Domoticz instance installed on it. Please keep this in mind when deciding on what machine(s) to run these packages. Also note that the ssh remote-scripting option is only needed when the perl-script is remote to the Domoticz machine - it doesn't matter if LMS is remote or not.

Setup process

Note: The temporary now-playing playlist saved for restoring after the alert is played will be stored in your regular LMS playlists folder as defined in your LMS configuration. You must define a playlists folder (even if you use Spotify) or else resuming the current song after the alert plays will go wrong and Spotty will just stop after the alert. In the LMS web interface, go to settings --> basic settings --> playlists folder. Also, please ensure that the folder has read/write permissions set correctly (on Linux, chmod 666).

For the local audio files, it's best to create a subfolder in your LMS music folder called alerts and to put all your pre-chimes and alert-TTS files in there.

First make sure perl is installed (it very likely is on a linux-based OS) and then install the modules from CPAN that we need (note: Parallel::Loops is not needed for the syncgroup version):

sudo apt-get update
sudo cpan install Parallel::Loops
sudo cpan install JSON::RPC::Client
sudo cpan install JSON::XS

Create a bash script called sqalert.sh with the following content. Substitute the .../domoticz/scripts for the path to the directory that Domoticz accesses by default when you use the on/off actions for a switch (on a pi that would nominally be /home/pi/domoticz/scripts, on Synology /volume1/homes/USERNAME/domoticz/scripts)

#! /bin/sh
/usr/bin/perl .../domoticz/scripts/squeezealert.pl $1 $2 $3 $4 > /dev/null 2>&1 &

Put it in that .../domoticz/scripts directory on whichever machine in your setup runs Domoticz and of course do a chmod +x afterwards.

If you are using the remote script option (i.e. Domoticz and perl-script on separate machines), use this version of sqalert.sh instead (note: it is still installed in the .../domoticz/scripts directory on the local machine running Domoticz):

#! /bin/sh
ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy /usr/bin/perl /path/to/remote/scriptdir/squeezealert.pl $1 $2 $3 $4 > /dev/null 2>&1 &

where 192.168.xxx.yyy is the IP address of the remote machine that the perl script will actually run on, the -p specifies the port to use for ssh on the remote machine (usually 22), privatekey is the keyfile in the local ~/.ssh directory (on the Domoticz machine) allowing ssh login to the remote machine without specifying a password, and /path/to/remote/scriptdir is the arbitrary path to the directory your perl scripts are stored in on that remote machine.

If you plan on using the remote-scripting option and you haven't already set up ssh for non-interactive login on the target/remote machine, use the command ssh-keygen -t rsa to generate the two files with public/private keys and put the public key in the ~/.ssh directory on the target/remote machine and the private key in ~/.ssh on the local machine where Domoticz is running. You will also have to do a chmod 700 on the ~/.ssh directory itself and chmod 600 on the key files themselves or it won't work.

Next, create a perl script called squeezealert.pl in the directory/machine pointed to by the path you just used in the bash script, with the following content (and do a chmod +x afterwards). Note: You will need to edit the parameters for player-mac's, the URL of your LMS instance, plus the full linux pathname to your alerts directory after creating this script (all parameters are case-sensitive too).

First variant of squeezealert.pl, not using syncgroups but using threading

This script implements semi-synchronised alerts on multiple players through multi-threading using the Parallel::Loops module. You can use either this one or the next script below (second variant):

#!/usr/bin/perl

# Plays a text-to-speech (TTS) alert on a Squeezebox, temporarily interrupting any music that's playing and then restoring
# An optional pre-chime doodle is played to first get our attention

# Author:  philchillbill, 2017-2018

use JSON::RPC::Client;
use JSON::XS;
use Parallel::Loops;
no warnings 'uninitialized';

# -----------------------------------------------------------------------------
# We call this perl script from the command line, passing 4 parameters to it:

# 1. 'players' is a string consisting of a comma-separated list of player names (no spaces!) in quotes. 
#     e.g. "kitchen" or "kitchen, livingroom, bedroom" but NOT "kitchen, living room, bedroom"
# 2. 'prechime' is the filename of the jingle to be played just prior to the spoken alert to grab our attention. e.g. 'Ring06.wav'. 
#    If no pre-chime is desired, use 'none'. The folder 'C:\Windows\media' has nice examples (files RingXX.wav).
# 3. 'ttsfile' is the filename of the spoken alert message. e.g. 'garageDoorOpen.mp3'
# 4. 'avol' is the volume that the alert is to be played at and should be 0-100. In practice, values of 40-80 are realistic.

# Edit the following parameters to suit your setup:

# The mac addresses for all the squeezebox players that will use alerting
%uuids = (
 kitchen => '00:04:20:aa:bb:cc',
 livingroom => '00:04:20:aa:bb:cc',
 office => '00:04:20:aa:bb:cc'
);

# The full path to the directory where all the TTS mp3/wav files are stored. Please use the name 'alerts' somewhere in it.
%dir = (
 alerts	=> 'file:///volume1/music/alerts'
);

# The URL for the LMS server instance, usually on port 9000 or 9002
%url = (
 lms => 'http://192.168.1.3:9002/jsonrpc.js'
);

# -----------------------------------------------------------------------------


sub squeezeAlert {
 ($player, $prechime, $afile, $avol)=@_;
 $client=new JSON::RPC::Client; $playerid=$uuids{$player};
 # Get the player's present state so it can be restored later, but not if last-playing was an alert (path is checked)
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'path', '?'] ] });
 if (!$res) { return }; # player must be disconnected/offline so return
 if ($res->is_success) {
  $jres=$res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $path=$$jdata{result}{_path} || 'empty';
  $restore=($path!~m/alerts|^empty$/) || 0;
  if ($restore) {
   $res = ( $client->call( $url{lms}, { method  => 'slim.request', params  => [ $playerid, ['status', '-', '1', 'tags' ] ] }) ) || 0;
   $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); 
   $repeat=$$jdata{result}{'playlist repeat'};
   $power=$$jdata{result}{power};
   $mode=$$jdata{result}{mode}; $noplay=($mode!~m/play/) || 0;
   $mvol=$$jdata{result}{'mixer volume'};
   $time=$$jdata{result}{time};
   if ($mode eq 'play') { $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['pause'] ] }); }
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'save', "nowplaying$player", 'silent:1'] ] });
 }}
 # now we have the present state of the player, get ready to play the alert:
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', '1'] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $avol ] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', '0'] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'clear'] ] });
 # build the temporary alert playlist
 if ($prechime eq 'none') { @play=($afile) } else { @play=($prechime, $afile) };
 foreach $file (@play) {
  $filetoplay="$dir{alerts}/$file";
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'add', "$filetoplay"] ] });
 }
 # now play the alert-playlist
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['play'] ] });
 # and wait for it to stop
 do {
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mode', '?'] ] });
  $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $mode=$$jdata{result}{_mode};
  sleep 1;
 } until ($mode eq 'stop');
 # ok, we are done playing so either restore the music playlist we interrupted, or power off if nothing was playing
 if ($restore) {
  # Restore player to previous state
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $mvol ] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', $repeat ] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'resume', "nowplaying$player", "noplay:$noplay" ] ] }); ;
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['time', $time ] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', $power] ] });
 }
 else { # no restore, just clear and power-off
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'clear'] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', '0'] ] });
 }
 return;
}

($players, $prechime, $ttsfile, $avol)=@ARGV;
@players=split(/,\s?/, $players);

$maxProcs = 6;
$pl = Parallel::Loops->new($maxProcs);
$pl->foreach (\@players, sub { &squeezeAlert( $_, $prechime, $ttsfile, $avol ) });

Second variant of squeezealert.pl, using temporary syncgroups

This script assembles/disassembles temporary syncgroups when alerts are played on multiple players (note: you should use either this script or the first variant above, but not both!):

#!/usr/bin/perl

# Plays a text-to-speech (TTS) alert on a Squeezebox, temporarily interrupting any music that's playing and then restoring
# An optional pre-chime doodle is played to first get our attention

# THIS VERSION ASSEMBLES/DISASSEMBLES TEMPORARY SYNCGROUPS FOR THE DURATION OF THE ALERT

# Author:  philchillbill, 2017-2018

use JSON::RPC::Client;
use JSON::XS;
no warnings 'uninitialized';

# -----------------------------------------------------------------------------
# We call this perl script from the command line, passing 4 parameters to it:

# 1. 'players' is a string consisting of a comma-separated list of player names (no spaces!) in quotes. 
#     e.g. "kitchen" or "kitchen, livingroom, bedroom" but NOT "kitchen, living room, bedroom"
# 2. 'prechime' is the filename of the jingle to be played just prior to the spoken alert to grab our attention. e.g. 'Ring06.wav'. 
#    If no pre-chime is desired, use 'none'. The folder 'C:\Windows\media' has nice examples RingXX.wav.
# 3. 'ttsfile' is the filename of the spoken alert message. e.g. 'garageDoorOpen.mp3'
# 4. 'avol' is the volume the alert is to be played at and should be 0-100. In practice, values of 40-80 are realistic.

# Edit the following parameters to suit your setup:

# The mac addresses for all the squeezebox players that will use alerting
%uuids = (
 kitchen => '00:04:20:aa:bb:cc',
 livingroom => '00:04:20:aa:bb:cc',
 office => '00:04:20:aa:bb:cc'
);

# The full path to the directory where all the TTS mp3/wav files are stored. Please use the name 'alerts' somewhere in it.
%dir = (
 alerts	=> 'file:///volume1/music/alerts'
);

# The URL for the LMS server instance, usually on port 9000 or 9002
%url = (
 lms => 'http://192.168.1.3:9002/jsonrpc.js'
);

# -----------------------------------------------------------------------------


sub squeezeAlert {
 ($sbgroup, $prechime, $afile, $avol)=@_; @players=@{$sbgroup};
 $client=new JSON::RPC::Client; 
 # Get each player's present state so it can be restored later, but not if last-playing was an alert (path is checked)
 foreach $sb (@players) {
  $playerid=$uuids{$sb};
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'path', '?'] ] });
  if (!$res) { if (scalar(@players)==1) { return } else { next } }; # player must be disconnected/offline so skip
  if ($res->is_success) {
   push(@alive, $sb); # add sb to the array of connected players
   $jres=$res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $path=$$jdata{result}{_path} || 'empty';
   $restore{$sb}=($path!~m/alerts|^empty$/) || 0;
   if ($restore{$sb}) {
    $res = ( $client->call( $url{lms}, { method  => 'slim.request', params  => [ $playerid, ['status', '-', '1', 'tags' ] ] }) ) || 0;
    $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); 
    $repeat{$sb}=$$jdata{result}{'playlist repeat'};
    $power{$sb}=$$jdata{result}{power};
    $mode{$sb}=$$jdata{result}{mode}; $noplay{$sb}=($mode{$sb}!~m/play/) || 0;
    $mvol{$sb}=$$jdata{result}{'mixer volume'};
    $time{$sb}=$$jdata{result}{time};
    if ($mode{$sb} eq 'play') { $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['pause'] ] }); }
    $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'save', "nowplaying$sb", 'silent:1'] ] });
  }}
  # now we have the present state of the player, get ready to play the alert:
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', '1'] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $avol ] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', '0'] ] });
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'clear'] ] });
 }
 # replace the players-list by the list of verified connected players
 @players=@alive;
 # build the sync group, if more than one player specified
 $master=shift @players;
 foreach $sb (@players) {
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['sync', $uuids{$sb} ] ] });   
 }
 # build the temporary alert playlist
 if ($prechime eq 'none') { @play=($afile) } else { @play=($prechime, $afile) };
 foreach $file (@play) {
  $filetoplay="$dir{alerts}/$file";
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['playlist', 'add', "$filetoplay"] ] });
 }
 # now play the alert-playlist
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['play'] ] });
 # and wait for it to stop
 do {
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['mode', '?'] ] });
  $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $mode=$$jdata{result}{_mode};
  sleep 1;
 } until ($mode eq 'stop');
 # ok, we are done playing so either restore the music playlist we interrupted, or power off if nothing was playing
 foreach $sb ($master, @players) {
  $playerid=$uuids{$sb}; $res = $client->call($url{lms}, { method => 'slim.request', params => [ $playerid, ['sync', '-' ] ] });   
  if ($restore{$sb}) {
   # Restore player to previous state
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $mvol{$sb} ] ] });
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', $repeat{$sb} ] ] });
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'resume', "nowplaying$sb", "noplay:$noplay{$sb}" ] ] }); ;
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['time', $time{$sb} ] ] });
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', $power{$sb}] ] });
  }
  else { # no restore, just clear and power-off
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'clear'] ] });
   $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', '0'] ] });
  }
 }
 return;
}

($players, $prechime, $ttsfile, $avol)=@ARGV;
@players=split(/,\s?/, $players);
&squeezeAlert( \@players, $prechime, $ttsfile, $avol );

Testing

To test either script first (outside Domoticz), you should cd to the directory where you placed the bash script and issue the following command (assuming you have a player called livingroom, plus files called Ring06.wav and garageDoorOpen.mp3 in your alerts folder - change as appropriate to your setup):

./sqalert.sh livingroom Ring06.wav garageDoorOpen.mp3 60

If that works OK and you hear the pre-chime and message, you should then be good to go to use the script within Domoticz. If you have issues getting the remote (ssh) scripting working, you can first verify that the remote-login keys are working by typing

ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy

from the command line on the local machine. If it works then you will just silently receive a terminal-prompt from the remote machine (you might have to accept the remote machine as trusted the first time this is done - just go ahead and do it). If it doesn't work then check the permissions (not higher than 600 on the keyfiles!) and check that the keyfiles are in the home directory for the correct user (are you going to run Domoticz as root, pi, another user... ?). At a minimum, you should already be able to login via ssh to the remote machine using a username and password - if that's not working then this certainly won't work (e.g. check port 22 open, check that the ssh service is started).

Using Ambient Light Sensor in SB-Radio and SB-Touch

Both the SB Radio and SB Touch have an internal sensor to detect the ambient light level and dim the display when the room is darker or to brighten it when it's light. It's possible to install a background-running script on the player that will regularly update Domoticz with this measured light level (don't worry - it still operates when the player is in standby). This only works meaningfully for a player located in a normally-dark room such as a bathroom with no windows. It can then be used to check if the bathroom is occupied because the light will usually be on when somebody is in there. Use this info e.g. to change the colour of a Hue lamp downstairs so you know when the toilet is occupied upstairs, to start a ventilator if the light in the bathroom is on for a while, or even to autoplay music on the player when somebody turns on the light in the room.

Setup

First of all, you should enable ssh login on the SB in question. On the player's own UI, go to:

Settings --> Advanced --> Remote Login and make sure Enable SSH is ticked/selected.

Now use a program such as putty or smarTTY to ssh into the player, using username root and default password 1234. This will log you into the /root home directory on the SB. IF you have problems with ssh, try ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -c 3des-cbc [email protected] instead. Use smarTTY or a program like Cyberduck to upload the following shell script to that /root directory. Call this file domosb.sh and make sure you have Unix file-endings in it (LF) if you copy/paste from here. When it's copied to the SB, don't forget to chmod +x to make it executable.

 #!/bin/sh
 # script to read the ambient light sensor on a Squeezebox Radio or Touch and send the data to Domoticz
 # it returns a value of 100000 in near darkness and a value of typically ~100-500 when illuminated
 
 # Author: philchillbill, 2017. Inspired by https://github.com/luebbers/bathroomd
 # Improved: Robert Lagus 2018 with logic checker for near darkness values
 
 DOMO="192.168.XXX.XXX:8080" 	# IP and port of your Domoticz instance
 PLAYER="Smart%20Radio" 	# Name of the player to appear in the Domoticz log (URI encoded)
 LEVEL=XX			# idx of the Domoticz 'lux' device to update with the ambient light level
 SWITCH=XX			# idx of the on/off switch used to indicate presence
 
 # for the SB Radio use this as the path to the SENSOR data
 SENSOR="/sys/class/i2c-adapter/i2c-1/1-0010/ambient"
 # Note: for the SB Touch use the following instead 
 # SENSOR="/sys/class/i2c-adapter/i2c-0/0-0039/lux"
 
 # say Hello World to the Domoticz Log after waiting a bit for network to be up
 sleep 20
 wget -O /dev/null -q "http://"$DOMO"/json.htm?type=command&param=addlogmessage&message=SB%20"$PLAYER"%20commenced%20ambient%20light%20monitoring."
 
 # read out the sensor every 2 seconds in an infinite loop
 # we only update Domoticz when a meaningful change in Lux occurs (but at least once every 30 minutes to keep the sensor active)
 PREV=0; LOOPS=0; SET=0
 while /bin/true; do
  LOOPS=$(( $LOOPS + 1 )); if [ "$LOOPS" -gt 1000000 ]; then LOOPS=0; fi
  VAL=`cat $SENSOR | tr -d "\n"`
  if [ "$VAL" -gt 90000 ]; then
    VAL=0
  fi
  DELTA=$(($VAL - $PREV))
  if [ "$DELTA" -ge 0 ]; then
   STEP=$DELTA
  else				
   STEP=$((0 - $DELTA))	# step is the absolute-value of the delta in lux
  fi 
  # this updates the sensor-level value shown in the Domoticz 'Lux' meter
  if [ "$STEP" -gt 20 ] || [ $(($LOOPS % 900)) -eq 0 ]; then
   wget -O /dev/null -q "http://"$DOMO"/json.htm?type=command&param=udevice&idx="$LEVEL"&svalue="$VAL""
  fi
  # this sets the on/off switch in Domoticz, with a little hysteresis to prevent the switch 'chattering'
  if [ "$VAL" -lt 400 ] && [ "$SET" -eq 0 ]; then
   wget -O /dev/null -q "http://"$DOMO"/json.htm?type=command&param=switchlight&idx="$SWITCH"&switchcmd=On"
   SET=1
  else
   if [ "$VAL" -gt 500 ] && [ "$SET" -eq 1 ]; then
    wget -O /dev/null -q "http://"$DOMO"/json.htm?type=command&param=switchlight&idx="$SWITCH"&switchcmd=Off"
    SET=0
   fi
  fi
  PREV=$VAL
  sleep 2
#  echo $VAL "=VAL" 
#  echo $STEP "=STEP"
#  echo $DELTA "=DELTA"
#  echo $PREV "=PREV"
#  echo $LOOPS "=LOOPS"
#  echo " "
 done

The i2c sensor in the SB-Radio produces a value of 100,000 in (almost) darkness and a value of ~100 - ~500 when the lights in the room are on. This script makes sure that Domoticz only gets updated if there is a meaningful change in light level (>20) to mask tiny fluctuations and keep network traffic down. It sends something at least every 30 minutes just to make sure Domoticz doesn't mark it as an offline sensor if it hears nothing for too long.

Testing

After transferring the script into the /root folder on the SB you can play around with it first interactively (command: ./domosb.sh) to make sure you got it all set up right and also that the thresholds work for you. I created a Lux sensor-type in Domoticz (via Setup-->Hardware-->Dummy-->Create Virtual Sensors) to display the raw sensor data (in the example above it had an idx of LEVEL) and also an on/off switch (with idx of SWITCH) to act as a toggled occupancy-sensor. When you are satisfied that the script works OK you can get it to autostart upon booting of the SB by adding a line to the very end of the /etc/init.d/rcS file (vi was the only editor on my SB - there was no nano editor).

 # Add the following to the very end of the file '/etc/init.d/rcS':

 # Start Domoticz Script
 echo "Starting Domoticz script"
 /root/domosb.sh &

Don't forget that & at the end or it won't run in the background. The load of running this script makes the SB user interface a tiny bit laggy but does not affect audio playback. You can type reboot to restart the player and get the script auto-running. Of course the data produced by this script is not really a lux value (due to the inversion and high non-linearity) but for our purposes here that's irrelevant.

Transfer Playlist Among Players

As you move around a house with multiple Squeezebox players, it can be useful to transfer the parameters of the currently-playing player (in a room you are about to leave, for example) to a player in a room you are about to go to. For example, you were listening to music on the SB-Touch in the livingroom and now you're going to take a shower so you want to transfer to the SB-Radio in the bathroom. The following script achieves that by saving the state of the source player and restoring those parameters to the target player before powering off the source. If the source player was not playing anything at that time then nothing happens because there's nothing to transfer. The volume-level and repeat-mode of the source are also transferred to the target. This script can be used e.g. with Alexa/HA-Bridge or it can be linked to a Domoticz switch-event such as an occupancy-sensor.

In the on-action / off-action fields of a Domoticz switch, you can use a construct along the lines of

script://sqxfer.sh livingroom bathroom
script://sqxfer.sh kitchen livingroom

If it doesn't work for you, try specifying the full path to the bash file with a triple-slash

script:///home/pi/domoticz/scripts/sqxfer.sh livingroom bathroom

Setup Considerations

With all the perl scripts in this LMS wiki, some modules from CPAN (Comprehensive Perl Archive Network) need to be installed. This is no problem on Linux and on Windows, but is a major problem on Synology NAS devices which have a rather stripped-down Linux under the hood with no build environment. There is a simple workaround for this using remote scripting, but let's first get something clear about where/what needs to be installed. When any one of the perl scripts here are active, 3 different pieces of software are ultimately involved:

1. LMS server software
2. Domoticz
3. Our perl script(s)

These 3 can all run on the same computer but they can also be run on 3 different computers with no problems whatsoever. Synology users can run both LMS and Domoticz e.g. on their Synology, but run the perl script(s) on a separate perl-friendly machine such as a spare RPi. This is possible because we can get a Domoticz on/off action to run a script on a remote machine by using ssh (explained below) with Public-Key Authentication. You also only need one instance of Domoticz running, so if you are using a separate machine for the perl-scripts then it does not need to have a slave Domoticz instance installed on it. Please keep this in mind when deciding on what machine(s) to run these packages. Also note that the ssh remote-scripting option is only needed when the perl-script is remote to the Domoticz machine - it doesn't matter if LMS is remote or not.

Setup

First make sure perl is installed (it very likely is on a linux-based OS) and then install the 2 modules from CPAN that we need:

sudo apt-get update
sudo cpan install JSON::RPC::Client
sudo cpan install JSON::XS

Create a bash script called sqxfer.sh with the following content. Substitute the .../domoticz/scripts for the path to the directory that Domoticz accesses by default when you use the on/off actions for a switch (on a pi that would nominally be /home/pi/domoticz/scripts, on Synology /volume1/homes/USERNAME/domoticz/scripts)

#! /bin/sh
/usr/bin/perl .../domoticz/scripts/sqxfer.pl $1 $2 > /dev/null 2>&1 &

Put it in that .../domoticz/scripts directory on whichever machine in your setup runs Domoticz and of course do a chmod +x afterwards.

If you are using the remote script option (i.e. Domoticz and perl-script on separate machines), use this version of sqxfer.sh instead (note: it is still installed in the .../domoticz/scripts directory on the local machine running Domoticz):

#! /bin/sh
ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy /usr/bin/perl /path/to/remote/scriptdir/sqxfer.pl $1 $2 > /dev/null 2>&1 &

where 192.168.xxx.yyy is the IP address of the remote machine that the perl script will actually run on, the -p specifies the port to use for ssh on the remote machine (usually 22), privatekey is the keyfile in the local ~/.ssh directory (on the Domoticz machine) allowing ssh login to the remote machine without specifying a password, and /path/to/remote/scriptdir is the arbitrary path to the directory your perl scripts are stored in on that remote machine.

If you plan on using the remote-scripting option and you haven't already set up ssh for non-interactive login on the target/remote machine, use the command ssh-keygen -t rsa to generate the two files with public/private keys and put the public key in the ~/.ssh directory on the target/remote machine and the private key in ~/.ssh on the local machine where Domoticz is running. You will also have to do a chmod 700 on the ~/.ssh directory itself and chmod 600 on the key files themselves or it won't work.

Next, create a perl script called sqxfer.pl in the directory/machine pointed to by the path you just used in the bash script, with the following content (and do a chmod +x afterwards). If you are using the remote-script option, the file is placed in the /path/to/remote/scriptdir directory on the remote machine. Note: You will need to edit the parameters for player-mac's and the URL of your LMS instance after creating this script (all parameters are case-sensitive too):

#!/usr/bin/perl

use JSON::RPC::Client;
use JSON::XS;
 
no warnings 'uninitialized';

# Transfers a playlist from one squeezebox player ('source') to another ('target') while maintaining current song and time-position within that song.
# The volume level and repeat-mode from the source player are also transferred to the target.

# Author: philchillbill, 2018

# -----------------------------------------------------------------------------
# Edit the following parameters to suit your setup:

# The mac addresses for all the squeezebox players in your setup
%uuids = (
 kitchen => '00:04:20:aa:bb:cc',
 livingroom => '00:04:20:aa:bb:cc',
 bathroom => '00:04:20:aa:bb:cc'
);

# The URL for the LMS server instance, usually on port 9000 or 9002
%url = (
 lms => 'http://192.168.1.3:9002/jsonrpc.js'
);

# -----------------------------------------------------------------------------

($source, $target)=@ARGV;

$client=new JSON::RPC::Client;
$playerid=$uuids{$source};

# First make sure that an alert isn't by chance playing at the moment of transfer (pity to transfer that)
do {
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'path', '?'] ] });
 if (!$res) { print "Source player inactive, exiting.\n" ; exit };
 $jres=$res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $path=$$jdata{result}{_path} || 'empty';
 $cnt++; $forced=($cnt==30) || 0;
} until ( ($path!~m/alerts/) || ($forced) );

# Get the source player's present state so it can be transferred, then power it off
$res = ( $client->call( $url{lms}, { method  => 'slim.request', params  => [ $playerid, ['status', '-', '1', 'tags' ] ] }) ) || 0;
if (!$res) { print "Source player inactive, exiting.\n" ; exit };
$jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres);
$power=$$jdata{result}{power}; if ($power==0) { print "Source player inactive, exiting.\n" ; exit };
$curvol=$$jdata{result}{'mixer volume'};
$repeat=$$jdata{result}{'playlist repeat'};
$time=$$jdata{result}{time};
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'save', "transfer", 'silent:1'] ] });
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', 0 ] ] });

# now setup the target player and get it playing
$playerid=$uuids{$target};
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['power', '1'] ] });
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $curvol ] ] });
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', $repeat] ] });
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'resume', "transfer", "wipePlaylist:1" ] ] });
$res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['time', $time ] ] });

Note that the temporary playlist generated from the source for restoring on the target is automatically deleted upon transfer due to the wipePlaylist:1 in the code.

Testing

To test the script first outside Domoticz, cd to the directory where you placed the bash script and issue the following command (assuming you have players called kitchen and livingroom - change as appropriate to your setup). Make sure something is also actually playing on the source player or the script will just quietly exit.

./sqxfer.sh kitchen livingroom

The song playing on kitchen should transfer to livingroom at exactly the same moment in the same song and then power off kitchen for you. Note that the script will wait for an alert (see section 6 above) to finish playing if one accidentally commences just as you requested the transfer. If you have issues getting the remote (ssh) scripting working, you can first verify that the remote-login keys are working by typing

ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy

from the command line on the local machine. If it works then you will just silently receive a terminal-prompt from the remote machine (you might have to accept the remote machine as trusted the first time this is done - just go ahead and do it). If it doesn't work then check the permissions (not higher than 600 on the keyfiles!) and check that the keyfiles are in the home directory for the correct user (are you going to run Domoticz as root, pi, another user... ?). At a minimum, you should already be able to login via ssh to the remote machine using a username and password - if that's not working then this certainly won't work (e.g. check port 22 open, check that the ssh service is started).

Sync/Unsync a Group of Players from Domoticz

A cool feature of Squeezeboxes is their ability to play multi-room synchronised music, whereby a group of players all plays the same song at the exact same timestamp. The web UI for LMS allows assembling and disassembling groups, as does e.g. the iPeng app. However, the concept really flies when you can assemble and disassemble sync-groups at the flick of a switch. This script can be used e.g. with Alexa/HA-Bridge or it can be linked to a bunch of Domoticz switches that setup different groups for you for different purposes. We need to first provide a sync or unsync parameter, next define a player as master, and finally list the other players in the group that will become slaves. If only one slave is involved, no quotes are needed around the slave-name. If multiple slaves are desired then place names in quotes and separate with commas (see example). When done, anything that happens on the master is mirrored on the slaves.

In the on-action / off-action fields of a Domoticz switch, you can use a construct along the lines of

script://sqsync.sh sync livingroom "kitchen, studio, office"
script://sqsync.sh sync livingroom bedroom
script://sqsync.sh unsync kitchen "livingroom, bedroom"

If it doesn't work for you, try specifying the full path to the bash file with a triple-slash

script:///home/pi/domoticz/scripts/sqsync.sh sync livingroom bedroom

Setup Considerations

With all the perl scripts in this LMS wiki, some modules from CPAN (Comprehensive Perl Archive Network) need to be installed. This is no problem on Linux and on Windows, but is a major problem on Synology NAS devices which have a rather stripped-down Linux under the hood with no build environment. There is a simple workaround for this using remote scripting, but let's first get something clear about where/what needs to be installed. When any one of the perl scripts here are active, 3 different pieces of software are ultimately involved:

1. LMS server software
2. Domoticz
3. Our perl script(s)

These 3 can all run on the same computer but they can also be run on 3 different computers with no problems whatsoever. Synology users can run both LMS and Domoticz e.g. on their Synology, but run the perl script(s) on a separate perl-friendly machine such as a spare RPi. This is possible because we can get a Domoticz on/off action to run a script on a remote machine by using ssh (explained below) with Public-Key Authentication. You also only need one instance of Domoticz running, so if you are using a separate machine for the perl-scripts then it does not need to have a slave Domoticz instance installed on it. Please keep this in mind when deciding on what machine(s) to run these packages. Also note that the ssh remote-scripting option is only needed when the perl-script is remote to the Domoticz machine - it doesn't matter if LMS is remote or not.

Setup

First make sure perl is installed (it very likely is on a linux-based OS) and then install the 2 modules from CPAN that we need:

sudo apt-get update
sudo cpan install JSON::RPC::Client
sudo cpan install JSON::XS

Create a bash script called sqsync.sh with the following content. Substitute the .../domoticz/scripts for the path to the directory that Domoticz accesses by default when you use the on/off actions for a switch (on a pi that would nominally be /home/pi/domoticz/scripts, on Synology /volume1/homes/USERNAME/domoticz/scripts)

#! /bin/sh
/usr/bin/perl .../domoticz/scripts/sqsync.pl $1 $2 $3 > /dev/null 2>&1 &

Put it in that .../domoticz/scripts directory on whichever machine in your setup runs Domoticz and of course do a chmod +x afterwards.

If you are using the remote script option (i.e. Domoticz and perl-script on separate machines), use this version of sqsync.sh instead (note: it is still installed in the .../domoticz/scripts directory on the local machine running Domoticz):

#! /bin/sh
ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy /usr/bin/perl /path/to/remote/scriptdir/sqsync.pl $1 $2 $3 > /dev/null 2>&1 &

where 192.168.xxx.yyy is the IP address of the remote machine that the perl script will actually run on, the -p specifies the port to use for ssh on the remote machine (usually 22), privatekey is the keyfile in the local ~/.ssh directory (on the Domoticz machine) allowing ssh login to the remote machine without specifying a password, and /path/to/remote/scriptdir is the arbitrary path to the directory your perl scripts are stored in on that remote machine.

If you plan on using the remote-scripting option and you haven't already set up ssh for non-interactive login on the target/remote machine, use the command ssh-keygen -t rsa to generate the two files with public/private keys and put the public key in the ~/.ssh directory on the target/remote machine and the private key in ~/.ssh on the local machine where Domoticz is running. You will also have to do a chmod 700 on the ~/.ssh directory itself and chmod 600 on the key files themselves or it won't work.

Next, create a perl script called sqsync.pl in the directory/machine pointed to by the path you just used in the bash script, with the following content (and do a chmod +x afterwards). If you are using the remote-script option, the file is placed in the /path/to/remote/scriptdir directory on the remote machine. Note: You will need to edit the parameters for player-mac's and the URL of your LMS instance after creating this script (all parameters are case-sensitive too):

#!/usr/bin/perl

use JSON::RPC::Client;
use JSON::XS;
no warnings 'uninitialized';

# -----------------------------------------------------------------------------
# Syncs or unsyncs a group of Squeezebox players in one operation
# The first parameter should be either 'sync' or 'unsync'.
# Next parameter is the master player name
# Finally, a comma-separated list of slave players to join/leave the sync-group, in quotes

# Author: philchillbill, 2018

# Edit the following parameters to suit your setup:

# The mac addresses of all the squeezebox players in your setup
%uuids = (
 kitchen => '00:04:20:aa:bb:cc',
 livingroom => '00:04:20:aa:bb:cc',
 bathroom => '00:04:20:aa:bb:cc'
);

# The URL for the LMS server instance, usually on port 9000 or 9002
%url = (
 lms => 'http://192.168.1.3:9002/jsonrpc.js'
);

# -----------------------------------------------------------------------------

($action, $master, $slaves)=@ARGV;
@slaves=split(/,\s?/, $slaves);

$client=new JSON::RPC::Client;

if (lc $action eq 'sync') { 
 $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['power', '1' ] ] });
 foreach $player (@slaves) {
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$player}, ['power', '1' ] ] });
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['sync', $uuids{$player} ] ] });   
 }
 $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$master}, ['play'] ] });
}

if (lc $action eq 'unsync') { 
 
 foreach $player ($master, @slaves) {
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$player}, ['pause'] ] });
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$player}, ['sync', '-' ] ] });
  $res = $client->call($url{lms}, { method => 'slim.request', params => [ $uuids{$player}, ['power', '0' ] ] });
 } 
 
}

Testing

To test the script first outside Domoticz, cd to the directory where you placed the bash script and issue the following command (assuming you have players called livingroom and kitchen - change as appropriate to your setup).

./sqsync.sh sync livingroom kitchen

Both the livingroom and kitchen players should now start playing in sync, with whatever was playing before on the master (here, livingroom) also playing on the slave: kitchen. It does not matter if livingroom was already playing or not as it will just start playing if it wasn't. Next, try

./sqsync.sh unsync livingroom kitchen

and both players will pause, unsync and finally power-off. If you have issues getting the remote (ssh) scripting working, you can first verify that the remote-login keys are working by typing

ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy

from the command line on the local machine. If it works then you will just silently receive a terminal-prompt from the remote machine (you might have to accept the remote machine as trusted the first time this is done - just go ahead and do it). If it doesn't work then check the permissions (not higher than 600 on the keyfiles!) and check that the keyfiles are in the home directory for the correct user (are you going to run Domoticz as root, pi, another user... ?). At a minimum, you should already be able to login via ssh to the remote machine using a username and password - if that's not working then this certainly won't work (e.g. check port 22 open, check that the ssh service is started).

Spoken Now-Playing using Amazon Polly TTS

The following perl script allows your Squeezebox players to literally tell you what song is currently playing using a female English-language voice. It queries all active players in your system for their currently-playing artist and title information and sends this off to the Amazon Polly TTS (Text-to-Speech) cloud service to generate mp3 files containing each player's artist/title information in spoken form. Just as with the audio-alerts script above, the current song is temporarily paused, the TTS information mp3 is played audibly on each active player, and the player(s) then resume playing as they were before the query, with the temporary mp3 being deleted when finished. Kicking it off can be linked e.g. to a Domoticz switch event or to HA-Bridge so that you can ask Alexa to tell you what's currently playing (although Alexa won't answer - her sister Polly will answer through your Squeezebox rather than through the Echo). The following example video demonstrates the principle via a command 'Tell Me' through Alexa to HA-Bridge to a Domoticz switch:

Youtube Video showing sqnowplaying.pl in action

Amazon reserves the utterance Now Playing for music played via Alexa herself so that's why I chose 'Tell Me' as the utterance, but you can use whatever you like that's not reserved. After you have re-created all this in your own setup, in the on-action / off-action fields of a Domoticz switch, you can use

script://sqnowplaying.sh

If it doesn't work for you, try specifying the full path to the bash file with a triple-slash, e.g.

script:///home/pi/domoticz/scripts/sqnowplaying.sh

Setup Considerations

With all the perl scripts in this LMS wiki, some modules from CPAN (Comprehensive Perl Archive Network) need to be installed. This is no problem on Linux and on Windows, but is a major problem on Synology NAS devices which have a rather stripped-down Linux under the hood with no build environment. There is a simple workaround for this using remote scripting, but let's first get something clear about where/what needs to be installed. When any one of the perl scripts here are active, 3 different pieces of software are ultimately involved:

1. LMS server software
2. Domoticz
3. Our perl script(s)

These 3 can all run on the same computer but they can also be run on 3 different computers with no problems whatsoever. Synology users can run both LMS and Domoticz e.g. on their Synology, but run the perl script(s) on a separate perl-friendly machine such as a spare RPi. This is possible because we can get a Domoticz on/off action to run a script on a remote machine by using ssh (explained below) with Public-Key Authentication. You also only need one instance of Domoticz running, so if you are using a separate machine for the perl-scripts then it does not need to have a slave Domoticz instance installed on it. Please keep this in mind when deciding on what machine(s) to run these packages. Also note that the ssh remote-scripting option is only needed when the perl-script is remote to the Domoticz machine - it doesn't matter if LMS is remote or not.

Setup

First of all, you will need a (free) account with the AWS Amazon Polly service because you need to provide login details for the perl script (AWS_ACCESS_KEY and AWS_SECRET_KEY). The following Getting Started article will guide you through that process:

Getting started with AWS Polly

Next, make sure perl is installed (it very likely is on a linux-based OS) and then install the 4 modules from CPAN that we need:

sudo apt-get update
sudo cpan install Parallel::Loops
sudo cpan install JSON::RPC::Client
sudo cpan install JSON::XS

Because our heavy-lifter Paws::Polly has a lot of dependencies, it's best to install it via cpanminus which you should install first if you don't have it already:

curl -L http://cpanmin.us | perl - --sudo App::cpanminus
sudo cpanm install Paws::Polly

Warning: Installing Paws:Polly will take a long time (139 dependencies) so be patient and let it finish its course.

Now create a bash script called sqnowplaying.sh with the following content. Substitute the .../domoticz/scripts for the path to the directory that Domoticz accesses by default when you use the on/off actions for a switch (on a pi that would nominally be /home/pi/domoticz/scripts, on Synology /volume1/homes/USERNAME/domoticz/scripts)

#! /bin/sh
/usr/bin/perl .../domoticz/scripts/sqnowplaying.pl > /dev/null 2>&1 &

Put it in that .../domoticz/scripts directory on whichever machine in your setup runs Domoticz and of course do a chmod +x afterwards.

If you are using the remote script option (i.e. Domoticz and perl-script on separate machines), use this version of sqnowplaying.sh instead (note: it is still installed in the .../domoticz/scripts directory on the local machine running Domoticz):

#! /bin/sh
ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy /usr/bin/perl /path/to/remote/scriptdir/sqnowplaying.pl > /dev/null 2>&1 &

where 192.168.xxx.yyy is the IP address of the remote machine that the perl script will actually run on, the -p specifies the port to use for ssh on the remote machine (usually 22), privatekey is the keyfile in the local ~/.ssh directory (on the Domoticz machine) allowing ssh login to the remote machine without specifying a password, and /path/to/remote/scriptdir is the arbitrary path to the directory your perl scripts are stored in on that remote machine.

If you plan on using the remote-scripting option and you haven't already set up ssh for non-interactive login on the target/remote machine, use the command ssh-keygen -t rsa to generate the two files with public/private keys and put the public key in the ~/.ssh directory on the target/remote machine and the private key in ~/.ssh on the local machine where Domoticz is running. You will also have to do a chmod 700 on the ~/.ssh directory itself and chmod 600 on the key files themselves or it won't work.

Next, create a perl script called sqnowplaying.pl in the directory/machine pointed to by the path you just used in the bash script, with the following content (and do a chmod +x afterwards). If you are using the remote-script option, the file is placed in the /path/to/remote/scriptdir directory on the remote machine. Note: You will need to provide your AWS login credentials, edit the parameters for your player-mac's, provide the URL of your LMS instance, plus define the path to your LMS playlists directory after creating this script (all parameters are case-sensitive too):

#!/usr/bin/perl
use Parallel::Loops;
use JSON::RPC::Client;
use JSON::XS;
use Paws;
no warnings 'uninitialized';

# Uses Amazon Polly TTS to read out the nowplaying song Title and Artist on an active Squeezebox
# Author: philchillbill, 2018

# ----------------------------------------------------------------------------------------------
# Edit the following parameters to suit your setup:

$ENV{AWS_ACCESS_KEY} = 'AJKTYRE764JOKL0LSH74';
$ENV{AWS_SECRET_KEY} = 'v8tRFJzPkkj876TgYPnua90eWrEaioqnmMd88AZw';

# The mac addresses of all the squeezebox players in your setup
%uuids = (
 livingroom => '00:04:20:aa:bb:cc',
 kitchen=> '00:04:20:aa:bb:cc',
 office	=> '00:04:20:aa:bb:cc'
);

# The URL for the LMS server instance, usually on port 9000 or 9002
%url = (
 lms => 'http://192.168.1.3:9002/jsonrpc.js'
);

# The full path to your LMS playlists directory
%dir = (
 playlists => 'file:///volume1/music/playlists'
);

# ----------------------------------------------------------------------------------------------


sub tts {
 ($voice, $filename, $text)=@_;
 my $polly = Paws->service('Polly', region => 'eu-west-1'); 
 my $res = $polly->SynthesizeSpeech( VoiceId =>  $voice, Text => $text, OutputFormat => 'mp3' );
 open(MP3, '>', "$filename.mp3"); binmode(MP3); print MP3 $res->AudioStream; close(MP3);
}

sub nowplayingTTS {
 $player=shift; $playerid=$uuids{$player};
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['artist', '?'] ] });
 $response=$res->jsontext; $jdata=decode_json $response; $artist=$$jdata{result}{_artist};
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['title', '?'] ] });
 $response=$res->jsontext; $jdata=decode_json $response; $title=$$jdata{result}{_title};
 if ($title=~m/\(/) { $title=~s/\(/, (/ };
 &tts('Amy', "$dir{playlists}/tts_$player", "This song is, '$title', by '$artist'");
 
 $res = ( $client->call( $url{lms}, { method  => 'slim.request', params  => [ $playerid, ['status', '-', '1', 'tags' ] ] }) ) || 0;
 $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); 
 $repeat=$$jdata{result}{'playlist repeat'};
 $mvol=$$jdata{result}{'mixer volume'};
 $time=$$jdata{result}{time};
 if ($mode eq 'play') { $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['pause'] ] }); }
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'save', "nowplaying$player", 'silent:1'] ] });
 
 $avol=$mvol+25; if ($avol>80) { $avol=80 };
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $avol ] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', '0'] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'play', "$dir{playlists}/tts_$player.mp3"] ] });
 
 do {
  $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mode', '?'] ] });
  $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres); $mode=$$jdata{result}{_mode};
  sleep 1;
 } until ($mode eq 'stop');
 
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['mixer', 'volume', $mvol ] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'repeat', '2' ] ] });
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['playlist', 'resume', "nowplaying$player" ] ] }); ;
 $res = $client->call( $url{lms}, { method => 'slim.request', params => [ $playerid, ['time', $time ] ] });
 
 unlink("$dir{playlists}/tts_$player.mp3");
}

$client = new JSON::RPC::Client;

# find out which of our players are currently active, if any
foreach $sb (keys %uuids) {
 $playerid=$uuids{$sb};
 $res = ( $client->call( $url{lms}, { method  => 'slim.request', params  => [ $playerid, ['status', '-', '1', 'tags' ] ] }) ) || 0;
 if (!$res) { next }; # offline
 $jres = $res->jsontext; $jdata=JSON::XS->new->allow_nonref->decode($jres);
 $power=$$jdata{result}{power}; $mode=$$jdata{result}{mode}; 
 if ( ($power==0) || ($mode ne 'play') ) { next } else { $active{$sb}=1 };
}

# play the created TTS file(s) on any active player(s), or else silently exit
$maxProcs = 6;
$pl = Parallel::Loops->new($maxProcs);
@tgt=(keys %active);
 
$pl->foreach (\@tgt, sub { 
 &nowplayingTTS($_);
});

Testing

To test the script first outside Domoticz, cd to the directory where you placed the bash script and issue the following command. Make sure something is also actually playing on one or more of your players or the script will just quietly exit.

./sqnowplaying.sh

After a slight delay (due to going up and back to the cloud for Polly), all active players (active meaning 'powered on' and in 'play' mode) should have their current song temporarily paused, the name of the song and the artist should be called out through the relevant player at a slightly-higher volume than the music was at, and then the song(s) that were playing should finally continue where they were at before the pause.

If you have issues getting the remote (ssh) scripting working, you can first verify that the remote-login keys are working by typing

ssh -p 22 -i ~/.ssh/privatekey 192.168.xxx.yyy

from the command line on the local machine. If it works then you will just silently receive a terminal-prompt from the remote machine (you might have to accept the remote machine as trusted the first time this is done - just go ahead and do it). If it doesn't work then check the permissions (not higher than 600 on the keyfiles!) and check that the keyfiles are in the home directory for the correct user (are you going to run Domoticz as root, pi, another user... ?). At a minimum, you should already be able to login via ssh to the remote machine using a username and password - if that's not working then this certainly won't work (e.g. check port 22 open, check that the ssh service is started).

Resources

The HTML documentation for all possible JSON RPC interactions with LMS is included in every LMS install so you already have it. Browse in the UI of your LMS instance to Help down in the bottom left corner, then click on Technical Information, then on The Logitech Media Server Command Line Interface. The perl scripts above were built entirely on the basis of the information there. There is also plenty of information on how to interact with LMS using JSON from e.g. a web-browser or a UserAgent online. See tutoriels.domotique, for example.

If you want to read more on the subject of Public-Key Authentication and the use of ssh-keygen, consult ssh-keygen or for details on editing the sshd_config file, see digitalocean

As mentioned in the section Setup Considerations above, a big issue with trying to use the above perl scripts natively on Synology is the lack of CPAN support for installing/building the JSON::RPC::Client and JSON::XS modules (Synology uses a rather stripped Linux variant). A simple workaround (detailed above) is for LMS (and your music) to be on your Synology (with Domoticz too) and to have only the perl scripts running on a different Linux machine in the network. However, if you really want to run things on your Synology natively because you don't want the overhead/cost of an additional machine, the JSON RPC calls can be made from bash using curl without having to install anything special, although the scripts will of course need total modification. Here's an example of what the calls look like in bash:

#!/bin/bash

JSONRPC="http://192.168.1.3:9002/jsonrpc.js"
UUID="00:04:20:1a:4d:5e"

curl -i -X POST -H "Content-Type: application/json" -d '{"id":1,"method":"slim.request","params":[ "'$UUID'", ["power", "1"] ]}' $JSONRPC
curl -i -X POST -H "Content-Type: application/json" -d '{"id":1,"method":"slim.request","params":[ "'$UUID'", ["playlist", "resume", "Trance_Daylist"] ]}' $JSONRPC