mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-25 03:44:52 +00:00
96_Snapcast: possible fix for mute problem
git-svn-id: https://svn.fhem.de/fhem/trunk@26597 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
f57dc050ac
commit
44c2b03ef0
@ -31,7 +31,7 @@ use Time::HiRes qw( gettimeofday );
|
||||
use DevIo;
|
||||
use JSON ();
|
||||
use GPUtils qw( GP_Import );
|
||||
use List::Util qw( max min );
|
||||
use List::Util 1.45 qw( max min uniq );
|
||||
|
||||
|
||||
#-- Run before package compilation
|
||||
@ -85,7 +85,8 @@ my %_clientmethods = (
|
||||
volume => 'Client.SetVolume',
|
||||
mute => 'Client.SetVolume',
|
||||
stream => 'Group.SetStream',
|
||||
latency => 'Client.SetLatency'
|
||||
latency => 'Client.SetLatency',
|
||||
group => 'Group.SetClients'
|
||||
);
|
||||
|
||||
=pod
|
||||
@ -262,38 +263,30 @@ sub Set {
|
||||
}
|
||||
return;
|
||||
}
|
||||
my @ids = grep {m{\A(?:clients_.+_group)\z}xms}
|
||||
keys %{ $hash->{READINGS} };
|
||||
my @group;
|
||||
for my $sid ( @ids ) {
|
||||
my $gr = ReadingsVal($name, $sid, '');
|
||||
if ( $client eq $gr ) {
|
||||
$sid =~ m{\Aclients_(.+)_group\z}xms;
|
||||
push @group, $1;
|
||||
};
|
||||
#clients_84a93e695051_2_group a269028b-7078-210f-0e75-54acd507faaa
|
||||
}
|
||||
Log3( $hash, 5, "Snap: group members for arg. $client are @group within @ids");
|
||||
if ( @group ) {
|
||||
|
||||
my $grp = _getGroupMembers($hash, $client);
|
||||
if ( @{$grp} ) {
|
||||
my $sparm;
|
||||
my @paramset;
|
||||
if ( $opt eq 'volume' && looks_like_number($value) && $value !~ m{[+-]}x ) {
|
||||
#Log3($hash,3,"SNAP: Group absolute volume command, volume: $value");
|
||||
my @paramset;
|
||||
my $grvol;
|
||||
for my $sclient ( @group ) {
|
||||
for my $sclient ( @{$grp} ) {
|
||||
$grvol += ReadingsNum( $name, "clients_${sclient}_volume", 0);
|
||||
}
|
||||
$grvol = int $grvol/@group;
|
||||
$grvol = int $grvol/@{$grp};
|
||||
my $change = $value - $grvol;
|
||||
for my $sclient ( @group ) {
|
||||
my $sparm->{id} = ReadingsVal( $name, "clients_${sclient}_origid", undef) // next;#_getId( $hash, $sclient) // next;
|
||||
for my $sclient ( @{$grp} ) {
|
||||
$sparm->{id} = ReadingsVal( $name, "clients_${sclient}_origid", undef) // next;#_getId( $hash, $sclient) // next;
|
||||
my $vol = ReadingsNum( $name, "clients_${sclient}_volume", 0) + $change;
|
||||
$vol = max( 0, min( 100, $vol ) );
|
||||
my $muteState = ReadingsVal( $name, "clients_${sclient}_muted", 'false' );
|
||||
$muteState = 'false' if $vol && ( $muteState eq 'true' || $muteState eq '1' );
|
||||
$sparm->{volume}->{muted} = $muteState;
|
||||
$sparm->{volume}->{percent} = $vol;
|
||||
my $payload = push @paramset, Snapcast_Encode( $hash, $_clientmethods{volume}, $sparm);
|
||||
push @paramset, Snapcast_Encode( $hash, $_clientmethods{volume}, $sparm, 1);
|
||||
}
|
||||
|
||||
return if !@paramset;
|
||||
my $payload = q{[};
|
||||
$payload .= join q{,},@paramset;
|
||||
@ -301,7 +294,30 @@ sub Set {
|
||||
#Log3($hash,3,"SNAP: send batch $payload");
|
||||
return DevIo_SimpleWrite( $hash, $payload, 2 );
|
||||
}
|
||||
for my $sclient ( @group ) {
|
||||
if ( $opt eq 'group' ) {
|
||||
Log3( $hash, 3, "Snap: $opt command received for @{$grp}" );
|
||||
my $opt2 = shift @param;
|
||||
my $clnt = shift @param // return 'group commands require two additional arguments!';
|
||||
$clnt = _getId( $hash, $clnt) // $clnt;
|
||||
$sparm->{id} = $client; #needs group2id function as well
|
||||
|
||||
if ( $opt2 eq 'name' ) {
|
||||
$sparm->{name} = $clnt;
|
||||
|
||||
return DevIo_SimpleWrite( $hash, Snapcast_Encode( $hash, 'Group.SetName', $sparm), 2 );
|
||||
}
|
||||
push @{$grp}, $clnt if $opt2 eq 'add';
|
||||
my @grIds;
|
||||
for ( @{$grp} ) {
|
||||
push @grIds, _getId( $hash, $_) // $_;
|
||||
}
|
||||
@grIds = grep { $_ !~ m{\A$clnt\z}x } @grIds if $opt2 eq 'remove';
|
||||
@grIds = uniq(@grIds);
|
||||
$sparm->{clients} = \@grIds;
|
||||
return DevIo_SimpleWrite( $hash, Snapcast_Encode( $hash, 'Group.SetClients', $sparm), 2 );
|
||||
}
|
||||
|
||||
for my $sclient ( @{$grp} ) {
|
||||
$sclient =~ s{:}{}gx;
|
||||
$sclient =~ s{[#]}{_}gx;
|
||||
my $res = _setClient( $hash, $sclient, $opt, $value );
|
||||
@ -316,6 +332,20 @@ sub Set {
|
||||
return "$opt not implemented yet!";
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
For <i>client</i> type devices, you may use a single group id as argument to add the client to the given group or the keyword <code>remove</code> to singularize that client.
|
||||
Options for <i>server</i> type devices:
|
||||
<ul>
|
||||
<li><code>name <group id> <new name></code></li>
|
||||
<li><code> add <client> <group id></code> add that client to the given group</li>
|
||||
<li><code>remove <client> <group id></code></li>
|
||||
</ul>
|
||||
|
||||
Group.SetClients
|
||||
{"id":3,"jsonrpc":"2.0","method":"Group.SetClients","params":{"clients":["00:21:6a:7d:74:fc#2","00:21:6a:7d:74:fc"],"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1"}}
|
||||
=cut
|
||||
|
||||
sub Read {
|
||||
my $hash = shift // return;
|
||||
my $name = $hash->{NAME};
|
||||
@ -428,10 +458,12 @@ sub Read {
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdateIfChanged( $hash, "clients_${client}_$key", $update->{result} );
|
||||
readingsEndUpdate( $hash, 1 );
|
||||
my $clienthash = $defs{$hash->{$client}} // return;
|
||||
readingsBeginUpdate($clienthash);
|
||||
readingsBulkUpdateIfChanged( $clienthash, $key, $update->{result} );
|
||||
readingsEndUpdate( $clienthash, 1 );
|
||||
if ( defined $hash->{$client} ) {
|
||||
my $clienthash = $defs{$hash->{$client}} // return;
|
||||
readingsBeginUpdate($clienthash);
|
||||
readingsBulkUpdateIfChanged( $clienthash, $key, $update->{result} );
|
||||
readingsEndUpdate( $clienthash, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -741,7 +773,7 @@ sub _setClient {
|
||||
if ( $param eq 'stream' ) {
|
||||
$paramset->{id} = ReadingsVal( $name, "clients_${id}_group", "" ); # for setting stream we now use group id instead of client id in snapcast 0.11 JSON format
|
||||
$param = 'stream_id';
|
||||
if ( $value eq "next" ) { # just switch to the next stream, if last stream, jump to first one. This way streams can be cycled with a button press
|
||||
if ( $value eq 'next' ) { # just switch to the next stream, if last stream, jump to first one. This way streams can be cycled with a button press
|
||||
my $totalstreams = ReadingsVal( $name, 'streams', 0 );
|
||||
my $currentstream = _getStreamNumber( $hash, ReadingsVal( $name, "clients_${id}_stream_id", '' ) );
|
||||
my $newstream = $currentstream + 1;
|
||||
@ -774,7 +806,8 @@ sub _setClient {
|
||||
$value = $currentVol - $step;
|
||||
}
|
||||
$value = max( 0, min( 100, $value ) );
|
||||
$muteState = 'false' if ( $value > 0 && ( $muteState eq 'true' || $muteState == 1 ));
|
||||
$muteState = 'false' if ( $value > 0 && ( $muteState eq 'true' || $muteState ne '1' ));
|
||||
$volumeobject->{muted} = $muteState;
|
||||
}
|
||||
return if !looks_like_number($value);
|
||||
$volumeobject->{percent} = $value + 0;
|
||||
@ -783,14 +816,16 @@ sub _setClient {
|
||||
|
||||
if ( $param eq 'mute' ) {
|
||||
$volumeobject->{percent} = $currentVol + 0;
|
||||
$value = $volumeobject;
|
||||
|
||||
if ( !defined $value->{muted} || $value->{muted} eq '' ) {
|
||||
$value = $muteState eq 'true' || $muteState == 1 ? 'false' : 'true';
|
||||
if ( $value eq 'true' || $value eq 'false' ) {
|
||||
$volumeobject->{muted} = $value;
|
||||
$volumeobject->{percent} = $currentVol + 0;
|
||||
$value = $volumeobject;
|
||||
} else {
|
||||
$value = $volumeobject;
|
||||
if ( !defined $value->{muted} || $value->{muted} eq '' ) {
|
||||
$value = $muteState eq 'true' || $muteState == 1 ? 'false' : 'true';
|
||||
$volumeobject->{muted} = $value;
|
||||
}
|
||||
}
|
||||
$value = $volumeobject;
|
||||
$param = 'volume'; # change param to "volume" to match new format
|
||||
}
|
||||
|
||||
@ -810,8 +845,7 @@ sub Snapcast_Do {
|
||||
my $method = shift // return;
|
||||
my $param = shift // '';
|
||||
my $payload = Snapcast_Encode( $hash, $method, $param );
|
||||
$payload .= "\r\n";
|
||||
|
||||
|
||||
#Log3($hash,3,"SNAP: Do $payload");
|
||||
return DevIo_SimpleWrite( $hash, $payload, 2 );
|
||||
}
|
||||
@ -820,7 +854,8 @@ sub Snapcast_Encode {
|
||||
my $hash = shift // return;
|
||||
my $method = shift // return;
|
||||
my $param = shift // '';
|
||||
|
||||
my $nonl = shift;
|
||||
|
||||
if ( defined( $hash->{helper}{REQID} ) ) { $hash->{helper}{REQID}++; }
|
||||
else { $hash->{helper}{REQID} = 1; }
|
||||
$hash->{helper}{REQID} = 1 if $hash->{helper}{REQID} > 16383; # not sure if this is needed but we better dont let this grow forever
|
||||
@ -834,6 +869,7 @@ sub Snapcast_Encode {
|
||||
$request->{id} = $request->{id} + 0;
|
||||
$json = JSON->new->encode($request);
|
||||
$json =~ s{("(true|false|null)")}{$2}gxms;
|
||||
$json .= "\r\n" if !$nonl;
|
||||
return $json;
|
||||
}
|
||||
|
||||
@ -847,6 +883,25 @@ sub _getStreamNumber {
|
||||
return;
|
||||
}
|
||||
|
||||
sub _getGroupMembers {
|
||||
my $hash = shift // return;
|
||||
my $grid = shift // return;
|
||||
my $name = $hash->{NAME} // return;
|
||||
|
||||
my @ids = grep {m{\A(?:clients_.+_group)\z}xms}
|
||||
keys %{ $hash->{READINGS} };
|
||||
my @group;
|
||||
for my $sid ( @ids ) {
|
||||
my $gr = ReadingsVal($name, $sid, '');
|
||||
if ( $grid eq $gr ) {
|
||||
$sid =~ m{\Aclients_(.+)_group\z}xms;
|
||||
push @group, $1;
|
||||
};
|
||||
}
|
||||
Log3( $hash, 5, "Snap: group members for arg. $grid are @group within @ids");
|
||||
return \@group;
|
||||
}
|
||||
|
||||
sub _getId {
|
||||
my $hash = shift // return;
|
||||
my $client = shift // return;
|
||||
@ -854,7 +909,7 @@ sub _getId {
|
||||
my $name = $hash->{NAME} // return;
|
||||
|
||||
# client is ID
|
||||
if ( $client =~ m/^([0-9a-f]{12}([#_]*\d*|$))$/i ) {
|
||||
if ( $client =~ m{\A(?:[[:xdigit:]]{8}-(?:[[:xdigit:]]{4}-){3}[[:xdigit:]]{12}(?:[#_]*\d*)|[[:xdigit:]]{12}(?:[#_]*\d*))\z}i ) {
|
||||
for my $i ( 1 .. ReadingsVal( $name, 'streams', 1 ) ) {
|
||||
return $hash->{STATUS}->{clients}->{$i}->{origid}
|
||||
if $client eq $hash->{STATUS}->{clients}->{$i}->{id};
|
||||
@ -954,9 +1009,14 @@ __END__
|
||||
<a id="Snapcast-set-update"></a><li><i>update</i><br>
|
||||
Perform a full update of the Snapcast Status including streams and servers. Only needed if something is not working. Server module only</li>
|
||||
<a id="Snapcast-set-volume"></a><li><i>volume</i><br>
|
||||
Set the volume of a client. For this and all the following 4 options, give client as second parameter (only for the server module), either as name, IP , or MAC and the desired value as third parameter.
|
||||
Client can be given as "all", in that case all clients are changed at once (only for server module)<br>
|
||||
Volume can be given in 3 ways: Range between 0 and 100 to set volume directly. Increment or Decrement given between -100 and +100. Keywords <em>up</em> and <em>down</em> to increase or decrease with a predifined step size.
|
||||
Set the volume of a client. For this and all the following 4 options, give <i>target</i> as second parameter (only for the server module) and the desired value as third parameter.
|
||||
<i>target</i> can be given as
|
||||
<ul>
|
||||
<li><i>client</i> either as name, IP or MAC</li>
|
||||
<li><i>all</i> to change volume of all clients at once (only for server module)</li>
|
||||
<li><i>group id</i> to change volume of all clients belonging to that group at once</li>
|
||||
</ul>
|
||||
<i>volume</i> can be given in 3 ways: Range between 0 and 100 to set volume directly. Increment or Decrement given between -100 and +100. Keywords <em>up</em> and <em>down</em> to increase or decrease with a predifined step size.
|
||||
The step size can be defined in the attribute <em>volumeStepSize</em><br>
|
||||
The step size can be defined smaller for the lower volume range, so that finetuning is possible in this area.
|
||||
See the description of the attributes <em>volumeStepSizeSmall</em> and <em>volumeStepThreshold</em>
|
||||
@ -970,6 +1030,15 @@ __END__
|
||||
<a id="Snapcast-set-stream"></a><li><i>stream</i><br>
|
||||
Change the stream that the client is listening to. Snapcast uses one or more streams which can be unterstood as virtual audio channels. Each client/room can subscribe to one of them.
|
||||
By using next as value, you can cycle through the avaialble streams</li>
|
||||
<a id="Snapcast-set-group"></a><li><i>group</i><br>
|
||||
For <i>client</i> type devices, you may use a single group id as argument to add the client to the given group or the keyword <code>remove</code> to singularize that client.
|
||||
Options for <i>server</i> type devices:
|
||||
<ul>
|
||||
<li><code><group id> name <new name></code></li>
|
||||
<li><code><group id> add <client></code> add that client to the given group</li>
|
||||
<li><code><group id> remove <client> </code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<br><br>
|
||||
|
Loading…
x
Reference in New Issue
Block a user