2
0
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:
Beta-User 2022-10-27 04:08:31 +00:00
parent f57dc050ac
commit 44c2b03ef0

View File

@ -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 &lt;group id&gt; &lt;new name&gt;</code></li>
<li><code> add &lt;client&gt; &lt;group id&gt;</code> add that client to the given group</li>
<li><code>remove &lt;client&gt; &lt;group id&gt;</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>&lt;group id&gt; name &lt;new name&gt;</code></li>
<li><code>&lt;group id&gt; add &lt;client&gt;</code> add that client to the given group</li>
<li><code>&lt;group id&gt; remove &lt;client&gt; </code></li>
</ul>
</li>
</ul>
</ul>
<br><br>