Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pulseaudio] Bug/Feature Request: Avoid random assignment of pulseaudio sinks to openhab things #12555

Closed
herrep opened this issue Apr 1, 2022 · 21 comments · Fixed by #12598
Closed
Labels
enhancement An enhancement or new feature for an existing add-on

Comments

@herrep
Copy link

herrep commented Apr 1, 2022

Dear Maintainer of the PulseAudio binding,

Thank you very much for providing this binding. When working with this binding, I encountered a random assignment of PulsAudio sinks to a particular OpenHAB thing under OpenHAB 3.2.0. Probably this effect is at the border between bug report and feature request.

The present version of the PulseAudio binding supports an assignment of PulseAudio sinks to a particular Thing in OpenHAB based on the name of the PulseAudio sink:

Thing sink multiroom "Snapcast" @ "Room" [name="alsa_card.pci-0000_00_1f.3", activateSimpleProtocolSink="true", simpleProtocolSinkPort=4711]

While this kind of assignment works fine for PulseAudio sinks realized in hardware, it is not possible to uniquely assign a PulseAudio sink that was discovered via the PulseAudio module module-raop-discovery.

The reason is that PulseAudio fails to assign always the same name on such PulseAudio sinks. This random assignment occurs in two cases:

  1. In case you have an ipv4 and ipv6 network enabled, the same PulseAudio sink is discovered twice. As the propagated name of the respective audio sink is identical, PulseAudio appends an enumerator value to the name to make the name unique. But it is only the name that is unique, not the assignment. For example, audio sink raop_output.room.local is detected via the ipv4 network. Then the ipv6 network causes creation of raop_output.room.local.2. This would not be so bad if it is ensured that ipv6 always receives the .2 postfix. Unfortunately, it cannot be ensured that the assignment order is always the same. Therefore, when creating a thing definition in OpenHAB, it is a random assignment whether the ipv4 route will be taken or the ipv6 route. One might argue that this is irrelevant as long as it works. However, in terms of defined network conditions it appears not to be recommandable that the administrator has no way to enforce OpenHAB to use exactly the PulseAudio sink as desired. It is in fact a random choice whether ipv4 or ipv6 is used when addressing the respective OpenHAB thing.

  2. In case you have a RAOP playback device (e.g. an AirPlay Receiver) capable of playing back multiple streams to support speakers in multiple rooms the situation gets even worse. The RAOP playback device shares the same name for all of the provided AirPlay sinks. As reported above for ipv4 / ipv6, each separate AirPlay sink will receive an enumerater value appended to the same name. As the sequence in the discovery of these AirPlay sinks differs upon their detection, we have a random assignment in the OpenHAB things as we can only use the name of the audio sink for the assignment to an OpenHAB thing and the name may differ in the next discovery. Therefore, it is impossible to specify the room where audio is played. It is random.

In view of these issues, I recommend to expand the possibilities to assign a PulseAudio sink to an OpenHAB Thing such that a unique assignment is supported without causing a random selection of a PulseAudio sink will be assigned to an OpenHAB thing in the next discovery.

Let me suggest a possible solution:

a) The assignment between PulseAudio sink and Thing shall consider an optional parameter sinkNetworkType that needs to match the network type (ipv4 or ipv6) of the PulseAudio sink.

b) The assignment between PulseAudio sink and Thing shall consider an optional parameter sinkDescription that needs to match the description of the PulseAudio sink.

c) The assignment between PulseAudio sink and Thing shall consider that the name may only specify the base name of the respective PulseAudio sink without any enumerator. So when specifying the name "raop_output.room.local" it also matches "raop_output.room.local.". The parameters specificed in a) and b) will make the choice unique.

When considering these parameters in the OpenHAB thing definition of a PulseAudio sink, an assignment is possible that remains unique also in the next discovery.

Thank you very much for considering this Feature Request.

Best regards,
Peter

@herrep herrep added the enhancement An enhancement or new feature for an existing add-on label Apr 1, 2022
@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/pulseaudio-random-assignment-of-raop-airplay-sinks-to-corresponding-openhab-pulseaudio-things/134718/1

@dalgwen
Copy link
Contributor

dalgwen commented Apr 2, 2022

Hello,

1- for the network differentiation suggestion : Could you provide the result of the "pactl list sinks" command with the two nearly identical sink created ? I cannot identify a parameter allowing to make a distinction between the network used in the underlying pulseaudio sink implementation.

2- I have, instead of an additional parameter helping to make a distinction, another suggestion : the name parameter could become a "name or description" parameter. Using a unique parameter for both simplifies the code and match another use case I already identified but never address : the pulseaudio naming itself could be somewhat inconsistent. Making it a "weak" requirement help addressing this use case.
So, is using the same parameter (i.e "name"), for matching the technical name or the description, sufficient in your case ?
I have already prepared a binding snapshot. For testing purpuse, you can find it here.

@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/no-audio-output-in-openhab-3-2-0-how-to-setup-a-pulseaudio-sink-as-java-sink/134665/4

@herrep
Copy link
Author

herrep commented Apr 3, 2022

Hi Gwendal,

Ad 1, here is the output of pactl -s localhost list sinks reduced to those two sinks which are the same but only access via ipv4 and ipv6:

`Ziel #3
Status: SUSPENDED
Name: raop_output.arm-7.local.3
Beschreibung: Wohnzimmer
Treiber: module-raop-sink.c
Abtastwert-Angabe: s16le 2ch 44100Hz
Kanalzuordnung: front-left,front-right
Besitzer-Modul: 31
Stumm: nein
Lautstärke: front-left: 19661 / 30%, front-right: 19661 / 30%
Verteilung 0,00
Basis-Lautstärke: 65536 / 100%
Quellen-Monitor: raop_output.arm-7.local.3.monitor
Latenz: 0 usec, eingestellt 0 usec
Flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
Eigenschaften:
device.string = "[192.168.4.160]:5010"
device.intended_roles = "music"
device.description = "Wohnzimmer"
device.model = "Unknown device model"
device.icon_name = "audio-card"
Ports:
network-output: [192.168.4.160]:5010 (priority: 0)
Aktiver Port: network-output
Formate:
pcm

Ziel #12
Status: SUSPENDED
Name: raop_output.arm-7.local.7
Beschreibung: Wohnzimmer
Treiber: module-raop-sink.c
Abtastwert-Angabe: s16le 2ch 44100Hz
Kanalzuordnung: front-left,front-right
Besitzer-Modul: 40
Stumm: nein
Lautstärke: front-left: 19661 / 30%, front-right: 19661 / 30%
Verteilung 0,00
Basis-Lautstärke: 65536 / 100%
Quellen-Monitor: raop_output.arm-7.local.7.monitor
Latenz: 0 usec, eingestellt 0 usec
Flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
Eigenschaften:
device.string = "[2a02:8106:245:7300:235:ffff:fe95:2194]:5010"
device.intended_roles = "music"
device.description = "Wohnzimmer"
device.model = "Unknown device model"
device.icon_name = "audio-card"
Ports:
network-output: [2a02:8106:245:7300:235:ffff:fe95:2194]:5010 (priority: 0)
Aktiver Port: network-output
Formate:
pcm`

Ad 2, thank you very much for considering a toggle for defining either the device name or device description as identifier to select the pulseaudio sink as an OpenHAB thing. This solves the problem of having multiple RAOP sinks on one sound device as we can assume that the user will make the description different within the same sound device. Ambiguities could occur if multiple sound devices are in the same network where the description for different audio sinks across different sound devices is the same. Just consider a user naming his audio sink in the living room as "living room" (=description) and an AppleTV in the living room also as "living room". The iOS devices will show the same description for these AirPlay devices, but the icon shown beside "living romm" is different, athere will be an audio symbol or the AppleTV symbol. Nevertheless, such users may be asked to make the description unique across sound devices. Of course, the problem of wrong identification still remains because of doubled sinks due to ipv4/ipv6, as both have the same description.

Best regards,
Peter

@herrep
Copy link
Author

herrep commented Apr 3, 2022

The new binding snapshots discovers the audio sinks based on the identified description.

There are only warnings during the OpenHAB startup that the audio sink configured with the description cannot be found, but then afterwards they get ONLINE:

2022-04-03 15:03:42.131 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device appears to be offline) 2022-04-03 15:03:42.131 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from UNKNOWN to OFFLINE 2022-04-03 15:03:43.081 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'localtime' changed from 2022-04-03T15:03:33.070+0200 to 2022-04-03T15:03:43.077+0200 2022-04-03 15:03:43.097 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:bad' changed from OFFLINE to ONLINE 2022-04-03 15:03:43.110 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from OFFLINE to ONLINE 2022-04-03 15:03:43.113 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:wohnzimmer' changed from OFFLINE to ONLINE 2022-04-03 15:03:43.114 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:kinderzimmer' changed from OFFLINE to ONLINE

Unfortunately, the audio sinks identified via the descrition cannot playback audio. Invoking a play command causes an exception:
2022-04-03 15:07:01.405 [ERROR] [b.core.io.console.ConsoleInterpreter] - An error occurred while executing the console command. java.lang.IllegalArgumentException: Cannot select one audio device at org.openhab.binding.pulseaudio.internal.PulseaudioClient.getGenericAudioItem(PulseaudioClient.java:348) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioBridgeHandler.getDevice(PulseaudioBridgeHandler.java:126) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler.getIdleTimeout(PulseaudioHandler.java:523) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioSimpleProtocolStream.scheduleDisconnect(PulseaudioSimpleProtocolStream.java:92) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseAudioAudioSink.process(PulseAudioAudioSink.java:109) ~[?:?] at org.openhab.core.audio.internal.AudioManagerImpl.play(AudioManagerImpl.java:140) ~[?:?] at org.openhab.core.audio.internal.AudioManagerImpl.playFile(AudioManagerImpl.java:180) ~[?:?] at org.openhab.core.audio.internal.AudioConsoleCommandExtension.playOnSink(AudioConsoleCommandExtension.java:171) ~[?:?] at org.openhab.core.audio.internal.AudioConsoleCommandExtension.playOnSinks(AudioConsoleCommandExtension.java:165) ~[?:?] at org.openhab.core.audio.internal.AudioConsoleCommandExtension.play(AudioConsoleCommandExtension.java:146) ~[?:?] at org.openhab.core.audio.internal.AudioConsoleCommandExtension.execute(AudioConsoleCommandExtension.java:85) ~[?:?] at org.openhab.core.io.console.ConsoleInterpreter.execute(ConsoleInterpreter.java:55) [bundleFile:?] at org.openhab.core.io.console.karaf.internal.CommandWrapper.execute(CommandWrapper.java:78) [bundleFile:?] at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:68) [bundleFile:4.3.4] at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:86) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:599) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:526) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:415) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Pipe.doCall(Pipe.java:416) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Pipe.call(Pipe.java:229) [bundleFile:4.3.4] at org.apache.felix.gogo.runtime.Pipe.call(Pipe.java:59) [bundleFile:4.3.4] at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?] at java.lang.Thread.run(Thread.java:829) [?:?]

@herrep
Copy link
Author

herrep commented Apr 3, 2022

I do not know how whether the pulseaudio binding is able to access an audio sink always under its description or whether the description is only used in the initial assignment of identifying the name of a particular audio sink to link it to an OpenHAB thing. In the latter case, the following remark is relevant:

When using the description to identify a particular audio sink, it must be considered that the corresponding name of the audio sink may change over time. Therefore, each time when accessing an audio sink under its description, it must be verified whether the present audio sink that has been originally identified via its description still has the same description. If not, the audio sinks have been re-arranged in and different names have been assigned so that the respective audio sink "moved" to a different audio sink.

@dalgwen
Copy link
Contributor

dalgwen commented Apr 4, 2022

Hello,

Thanks for the information about your sink. I see that the parameter allowing to make a distinction between the two sink is rather non obvious. I think making a special parameter for the "network-output" parameter is too specific.
But I think I managed to do something for your use case :
1- Matching by name or description
2- Adding an advanced parameter "additionalFilter". This parameter is a regex. If not empty, then the pulseaudio will make a requirement : the pulseaudio device MUST have a matching property value (whatever the key, only the value matter).
In your case, you should put your IPV4 (192.168.4.160) and it will match only the sink you want.

I updated the snapshot release for you to test.

There are only warnings during the OpenHAB startup that the audio sink configured with the description cannot be found, but then afterwards they get ONLINE:

I fixed this bug in the release. As you said, it was just at startup and had no consequences further.

Unfortunately, the audio sinks identified via the descrition cannot playback audio. Invoking a play command causes an exception

It was linked to your use case : you have two sinks with the same description (one for IPV4, another for IPV6), and so the binding couldn't choose which one to use. With the new release and the additionalFilter parameter, you should be able to fix this.

I do not know how whether the pulseaudio binding is able to access an audio sink always under its description or whether the description is only used in the initial assignment

It always check the description to access it, so it should be good.

@herrep
Copy link
Author

herrep commented Apr 4, 2022

Hi Gwendal,

Thank you very much for the new snapshot file. I modified my pulseaudio.things as follows:

Thing sink bad "Bad" [name="Bad", additionalFilter="192.168.4.160", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4721]
Thing sink kinderzimmer "Kinderzimmer" [name="Kinderzimmer", additionalFilter="192.168.4.160", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4722]
Thing sink wohnzimmer "Wohnzimmer" [name="Wohnzimmer", additionalFilter="192.168.4.160", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4723]
Thing sink schlafzimmer "Schlafzimmer" [name="Schlafzimmer", additionalFilter="192.168.4.160", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4724]

The regexp pattern "192.168.4.160" in additionalFilter could be probably narrowed to something like "192\.168\.4\.160", but according to my understanding the given value should match the ip address anyway.

Unfortunately, the audio sinks are not detected any longer when specifying the description in the field name and the given ip address in the fieldadditionalFilter:

2022-04-04 12:34:54.373 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'pulseaudio.things'
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.422 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:almando' changed from ONLINE to UNKNOWN
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.425 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device almando-489c9c appears to be offline)
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.425 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:almando' changed from UNKNOWN to OFFLINE
2022-04-04 12:34:54.428 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:appletv' changed from ONLINE to UNKNOWN
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.429 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device Wohn­zimmer appears to be offline)
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.429 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:appletv' changed from UNKNOWN to OFFLINE
2022-04-04 12:34:54.435 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:bad' changed from ONLINE to UNKNOWN
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.436 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device Bad appears to be offline)
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.437 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:bad' changed from UNKNOWN to OFFLINE
2022-04-04 12:34:54.449 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:kinderzimmer' changed from ONLINE to UNKNOWN
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.450 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device Kinderzimmer appears to be offline)
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.450 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:kinderzimmer' changed from UNKNOWN to OFFLINE
2022-04-04 12:34:54.459 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:wohnzimmer' changed from ONLINE to UNKNOWN
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.460 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device Wohnzimmer appears to be offline)
==> /var/log/openhab/events.log <==
2022-04-04 12:34:54.461 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:wohnzimmer' changed from UNKNOWN to OFFLINE
2022-04-04 12:34:54.470 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from ONLINE to UNKNOWN
2022-04-04 12:34:54.471 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from UNKNOWN to OFFLINE
==> /var/log/openhab/openhab.log <==
2022-04-04 12:34:54.471 [WARN ] [o.internal.handler.PulseaudioHandler] - pulseaudio binding cannot connect to the module-simple-protocol-tcp on 127.0.0.1 (missing device info, device Schlafzimmer appears to be offline)

Best regards,
Peter

@herrep
Copy link
Author

herrep commented Apr 4, 2022

When I change the additionalFilter by replacing the points in the ip address by backslash point, like additionalFilter="192\.168\.4\.160", I receive the following errors:

2022-04-04 12:49:25.711 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'pulseaudio.things'
2022-04-04 12:49:25.726 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'pulseaudio.things' has errors, therefore ignoring it: [6,90]: mismatched character '.' expecting set null

@dalgwen
Copy link
Contributor

dalgwen commented Apr 4, 2022

Sorry, my mistake !
I wasn't clear, it's a regular expression and it must be build with some rules.
To match your IP, you should put this in the parameter :

.*192.168.4.160.*

The sequence .* means any character, any number of times.
So this regular expression means basically : "anything that contains 192.168.4.160"
(actually it could match more, because the "." character means "anything", but its not a problem and it should be sufficient here)

@herrep
Copy link
Author

herrep commented Apr 4, 2022

Hi Gwendal,

Now the discovery of the audio sinks works! I thought that a partial match would fulfill the regex criterion but the parsing appears to start from ^ until $ so that we need to add the .* in the leading a trailing part.

I am sorry to bother you with a further detail: Sometimes, only one of the ipv4 or ipv6 audio sink is detected - for whatever reason. And of course, when trying the updated additionalFilter for the ipv4 address, only the ipv6 sink was present so that it couldnot match th ipv4 filter in the thing definition.

Would it be possible to use additionalFilter only in case the description is not unique? Then we should cover all cases...

Best regards,
Peter

@herrep
Copy link
Author

herrep commented Apr 4, 2022

Although the audio sinks are now discovered based on their description, play back of a previously working audio file throws an exception:

2022-04-04 20:33:27.923 [WARN ] [seaudio.internal.PulseAudioAudioSink] - Error while trying to send audio to pulseaudio audio sink. Cannot connect to 127.0.0.1:unknown, error: missing device info, device Wohnzimmer appears to be offline 2022-04-04 20:33:55.790 [WARN ] [seaudio.internal.PulseAudioAudioSink] - Error while trying to send audio to pulseaudio audio sink. Cannot connect to 127.0.0.1:unknown, error: missing device info, device Wohnzimmer appears to be offline 2022-04-04 20:35:51.734 [WARN ] [seaudio.internal.PulseAudioAudioSink] - Error while trying to send audio to pulseaudio audio sink. Cannot connect to 127.0.0.1:unknown, error: missing device info, device Wohnzimmer appears to be offline 2022-04-04 20:38:41.578 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from ONLINE to OFFLINE 2022-04-04 20:38:41.578 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:kinderzimmer' changed from ONLINE to OFFLINE 2022-04-04 20:38:41.578 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:bad' changed from ONLINE to OFFLINE 2022-04-04 20:39:06.447 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:bad' changed from OFFLINE to ONLINE 2022-04-04 20:39:06.464 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:kinderzimmer' changed from OFFLINE to ONLINE 2022-04-04 20:39:06.548 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:schlafzimmer' changed from OFFLINE to ONLINE 2022-04-04 20:39:06.562 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'pulseaudio:sink:local:wohnzimmer' changed from OFFLINE to ONLINE at org.openhab.binding.pulseaudio.internal.PulseaudioClient.extractArgumentFromLine(PulseaudioClient.java:513) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.lambda$2(PulseaudioClient.java:473) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.findSimpleProtocolTcpModule(PulseaudioClient.java:505) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.loadModuleSimpleProtocolTcpIfNeeded(PulseaudioClient.java:432) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler.getSimpleTcpPort(PulseaudioHandler.java:480) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioSimpleProtocolStream.connectIfNeeded(PulseaudioSimpleProtocolStream.java:66) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseAudioAudioSink.process(PulseAudioAudioSink.java:72) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.extractArgumentFromLine(PulseaudioClient.java:513) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.lambda$2(PulseaudioClient.java:473) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.findSimpleProtocolTcpModule(PulseaudioClient.java:505) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.loadModuleSimpleProtocolTcpIfNeeded(PulseaudioClient.java:432) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler.getSimpleTcpPort(PulseaudioHandler.java:480) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioSimpleProtocolStream.connectIfNeeded(PulseaudioSimpleProtocolStream.java:66) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseAudioAudioSink.process(PulseAudioAudioSink.java:72) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.extractArgumentFromLine(PulseaudioClient.java:513) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.lambda$2(PulseaudioClient.java:473) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.findSimpleProtocolTcpModule(PulseaudioClient.java:505) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.loadModuleSimpleProtocolTcpIfNeeded(PulseaudioClient.java:432) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler.getSimpleTcpPort(PulseaudioHandler.java:480) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioSimpleProtocolStream.connectIfNeeded(PulseaudioSimpleProtocolStream.java:66) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseAudioAudioSink.process(PulseAudioAudioSink.java:72) ~[?:?] 2022-04-04 20:40:43.862 [DEBUG] [dia.internal.MediaActionTypeProvider] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaActionTypeProvider(100)] : dm $000 tracking 2 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (enter) 2022-04-04 20:40:43.862 [DEBUG] [dia.internal.MediaActionTypeProvider] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaActionTypeProvider(100)] : dm $000 tracking 2 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (exit) 2022-04-04 20:40:43.863 [DEBUG] [a.internal.MediaModuleHandlerFactory] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaModuleHandlerFactory(101)] : dm $000 tracking 3 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (enter) 2022-04-04 20:40:43.863 [DEBUG] [a.internal.MediaModuleHandlerFactory] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaModuleHandlerFactory(101)] : dm $000 tracking 3 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (exit) 2022-04-04 20:40:43.863 [DEBUG] [ia.internal.MediaScriptScopeProvider] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaScriptScopeProvider(102)] : dm AudioManager tracking 3 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (enter) 2022-04-04 20:40:43.863 [DEBUG] [ia.internal.MediaScriptScopeProvider] - bundle org.openhab.core.automation.module.media:3.2.0 (158)[org.openhab.core.automation.module.media.internal.MediaScriptScopeProvider(102)] : dm AudioManager tracking 3 SingleStatic modified {org.openhab.core.audio.AudioManager, org.openhab.core.config.core.ConfigOptionProvider}={service.id=213, defaultSource=javasound, service.bundleid=154, service.scope=bundle, defaultSink=pulseaudio:sink:local:bad, component.name=org.openhab.core.audio.internal.AudioManagerImpl, service.config.label=Audio, component.id=69, service.config.factory=false, service.config.category=system, felix.fileinstall.filename=file:/var/lib/openhab/etc/org.openhab.audio.cfg, service.config.description.uri=system:audio, service.pid=[org.openhab.audio, org.openhab.audio]} (exit) at org.openhab.binding.pulseaudio.internal.PulseaudioClient.extractArgumentFromLine(PulseaudioClient.java:513) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.lambda$2(PulseaudioClient.java:473) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.findSimpleProtocolTcpModule(PulseaudioClient.java:505) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioClient.loadModuleSimpleProtocolTcpIfNeeded(PulseaudioClient.java:432) ~[?:?] at org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler.getSimpleTcpPort(PulseaudioHandler.java:480) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseaudioSimpleProtocolStream.connectIfNeeded(PulseaudioSimpleProtocolStream.java:66) ~[?:?] at org.openhab.binding.pulseaudio.internal.PulseAudioAudioSink.process(PulseAudioAudioSink.java:72) ~[?:?]

@dalgwen
Copy link
Contributor

dalgwen commented Apr 4, 2022

I'm totally depressed (and at a loss).
It works flawlessly on my environnment. I cannot exactly reproduce your system, of course, but I have several sinks, on the same pulseaudio server, and I can choose between them, output sound, with no issue at all.
I can select them by name or description, use the filter, input wrong value to see the device goes OFFLINE, then change to right value and see it goes back ONLINE.
I tested all variations I could come up with, connection, disconnection, and I have no signs of your issue.

Could you try deleting the following line on your configuration file, as I mentionned here ?
load-module module-simple-protocol-tcp
(and reboot your machine/docker with the pulseaudio server)
It will ensure that the binding will handle the module loading on its own.
I do not think higly of this hypothesis, but I'd like to check.

In your log, I can see at 20:35:51 that you are trying to play sound, and it's rejected because it cannot find the pulseaudio sink on your pulseaudio server.
It is confirmed that is it indeed OFFLINE, because I can see it becoming ONLINE at 20:39:06
But why the device was OFFLINE during this time is the real question.

Could you please make this test : monitor when openHAB says a device if offline/online, and check in the same time if the 'pactl list sinks' still lists it.
If the command line pactl says the devices are here, and openHAB cannot see them, then it is an openHAB issue. If the command line says, same as openHAB, that the device is not there, then it is a pulseaudio issue with the RAOP module.

@dalgwen
Copy link
Contributor

dalgwen commented Apr 4, 2022

Sometimes, only one of the ipv4 or ipv6 audio sink is detected - for whatever reason. And of course, when trying the updated additionalFilter for the ipv4 address, only the ipv6 sink was present so that it couldnot match th ipv4 filter in the thing definition.
Best regards, Peter

I'm feeling like patching a sinking boat 😅
If the RAOP module is kinda unstable, may I suggest you disable the ipv6 stack in your pulseaudio server?
I know it's not really a solution, but if the network is unreliable (as seen with the disconnection / reconnection), maybe it will help, somehow ?
I can add what you ask. But for code/architecture reason it won't be as simple as it seems and it will require a rewrite. It will complexify the code base for a use case that is really specific, and here because of an underlying bug in another component.
So If you can disable ipv6, I think it will do both of us a favor 😁

@herrep
Copy link
Author

herrep commented Apr 4, 2022

Thank you very much for all your efforts. Although we enounter new surprises, thanks to your patches there is phantastic progress. More in detail below:

  1. When I reported the successful discovery of audio sink in connection with the exceptions when trying to play back, I had already removed "load-module module-simple-protocol-tcp" from /etc/pulse/default.pa and rebooted. However, when I switch back and include "load-module module-simple-protocol-tcp" in /etc/pulse/default.pa, I can properly play back!

  2. The issue with the high pitch MaryTTS voice output is resolved. Voice output works fine!

  3. The issue with the high quality mp3 files not playing at all is resolved. Mp3 files play back fine!

  4. The issue with various audio sinks available or not available mainly concerns missing ipv4 audio sinks and not ipv6 audio sinks. Probably you may consider another option to allow ipv6 audio sinks if ipv4 is not available: If I know that an audio sink is of a "vanishing type" and either accessible via ipv4 or ipv6, I can omit the additionalFilter. In this case, when identifying two audio sinks because ipv4 and ipv6 are active, you may simply grab the first one and output a warning because of detected two audio sinks instead of giving up and outputting an error.

  5. There is an issue with parameters like volume and mute that are assigned to an audio sink based on description if an audio sink changes its name. In this case, the parameter values remain at the audio sink that has the same name, but is now attached to another description. With the result that the expected volume is wrong. Probably this is the thoughest issue.

  6. I have another effect with my AirPlay sinks. There is one sound device providing two AirPlay channels under the same description and in the same network. As we also have ipv6 with the same name (but a different enumerator), I cannot simply use the name field to have a unique choice. I learned that additionalFilter filters about various fields. Could you recommend which content I could use to select only one audio sink out of the following two:

`Ziel #7
Status: SUSPENDED
Name: raop_output.almandomsd-501.local
Beschreibung: almando-489
Treiber: module-raop-sink.c
Abtastwert-Angabe: s16le 2ch 44100Hz
Kanalzuordnung: front-left,front-right
Besitzer-Modul: 35
Stumm: nein
Lautstärke: front-left: 19661 / 30%, front-right: 19661 / 30%
Verteilung 0,00
Basis-Lautstärke: 65536 / 100%
Quellen-Monitor: raop_output.almandomsd-501.local.monitor
Latenz: 0 usec, eingestellt 0 usec
Flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
Eigenschaften:
device.string = "[192.168.4.157]:7000"
device.intended_roles = "music"
device.description = "almando-489"
device.model = "Multiplay Surround"
device.icon_name = "audio-card"
Ports:
network-output: [192.168.4.157]:7000 (priority: 0)
Aktiver Port: network-output
Formate:
pcm

Ziel #8
Status: SUSPENDED
Name: raop_output.almandomsd-501.local.2
Beschreibung: almando-489
Treiber: module-raop-sink.c
Abtastwert-Angabe: s16le 2ch 44100Hz
Kanalzuordnung: front-left,front-right
Besitzer-Modul: 36
Stumm: nein
Lautstärke: front-left: 19661 / 30%, front-right: 19661 / 30%
Verteilung 0,00
Basis-Lautstärke: 65536 / 100%
Quellen-Monitor: raop_output.almandomsd-501.local.2.monitor
Latenz: 0 usec, eingestellt 0 usec
Flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
Eigenschaften:
device.string = "[192.168.4.157]:7000"
device.intended_roles = "music"
device.description = "almando-489"
device.model = "Multiplay Surround"
device.icon_name = "audio-card"
Ports:
network-output: [192.168.4.157]:7000 (priority: 0)
Aktiver Port: network-output
Formate:
pcm
`

@herrep
Copy link
Author

herrep commented Apr 4, 2022

Probably also the name field in the pulseaudio binding should support regex so that names could be addressed where the specific value of the enumerator added at the end can be ignored.

Example:
raop_output.description.local => ipv4 audio sink 1
raop_output.description.local.2 => ipv4 audio sink 2
raop_output.description.local.3 => ipv6 audio sink 1
raop_output.description.local.4 => ipv6 audio sink 2

Unfortunately, only a part of these audio sinks is accessible via pulseaudio and I need to access exactly audio sink 1, meaningless whether ipv4 or ipv6. How can I address them that this conditon is met, although the enumerators in the name always show a different value, but with the guarantee that audio sink 2 always has an enumerator value in its name greater than the enumerator value of audio sink 1.

@dalgwen
Copy link
Contributor

dalgwen commented Apr 4, 2022

1. [...] I can properly play back!

OK... I really don't understand what's going on... But if it works, then I'm fine with it !

4.  In this case, when identifying two audio sinks because ipv4 and ipv6 are active, you may simply grab the first one and output a warning because of detected two audio sinks instead of giving up and outputting an error.

Seems OK, I will do that : log a warning, grab the first one matching.

5. There is an issue with parameters like volume and mute that are assigned to an audio sink based on description if an audio sink changes its name. In this case, the parameter values remain at the audio sink that has the same name, but is now attached to another description. With the result that the expected volume is wrong. Probably this is the thoughest issue.

Sorry, by looking at the code, I don't understand how it can be possible.
There is a periodic job pulling the information from the server and updating the channels. So if volume channel is wrong (and I don't understand how it could), it shouldn't be wrong for a long time.
Without reproducing, i think I'm stuck on this one.

6. I have another effect with my AirPlay sinks. There is one sound device providing two AirPlay channels under the same description and in the same network. As we also have ipv6 with the same name (but a different enumerator), I cannot simply use the name field to have a unique choice. I learned that additionalFilter filters about various fields. Could you recommend which content I could use to select only one audio sink out of the following two:

The device name is a property, like the others, and can be matched with the additionalFilters parameter
I added the possibility to use several additionalFilter, by using a separator in the additionalFilters parameter : "###"
So you can match several values (all filters must be matched at least once by a property of a device for it to be selected)

Beside that, I don't know how I can help you more to match your use case. It needs a complex logic outside the scope of a regular binding...

New snapshot release (modifications : when several matches, grab the first one + several additionalFilters possible)
Be careful : I change the name additionalFilter to additionalFilters

@dalgwen
Copy link
Contributor

dalgwen commented Apr 6, 2022

How can I address them that this conditon is met, although the enumerators in the name always show a different value, but with the guarantee that audio sink 2 always has an enumerator value in its name greater than the enumerator value of audio sink 1.

raop_output.description.local => ipv4 audio sink 1
raop_output.description.local.2 => ipv4 audio sink 2
raop_output.description.local.3 => ipv6 audio sink 1
raop_output.description.local.4 => ipv6 audio sink 2

If I understand correctly, is it OK if you take the first name alphabetically ?...
If so, then with this new snapshot here (sort alphabetically and take the first device matching), and "additionalFilters" set to "raop_output.description.local.*", then I think it will match your use case,

@herrep
Copy link
Author

herrep commented Apr 6, 2022

Dear Gwendal,

Thank you very much for another snapshot that I can test. :-)

Your understanding of selecting the audio sink that matches first in alphabetical order is correct.

In my usecase, I have an AirPlay device with description "Wohnzimmer", but which causes creation of up to four pulseaudio sinks.

Example 1:

raop_output.wohnzimmer.local => ipv4 audio sink 1
raop_output.wohnzimmer.local.2 => ipv4 audio sink 2
raop_output.wohnzimmer.local.3 => ipv6 audio sink 1
raop_output.wohnzimmer.local.4 => ipv6 audio sink 2

I will use name="Wohnzimmer", additionalFilters="raop_output.wohnzimmer.local.*" and then I receive raop_output.wohnzimmer.local as selected audio sink.

Example 2:

raop_output.wohnzimmer.local => ipv6 audio sink 1
raop_output.wohnzimmer.local.2 => ipv6 audio sink 2
raop_output.wohnzimmer.local.3 => ipv4 audio sink 1
raop_output.wohnzimmer.local.4 => ipv4 audio sink 2

If I want to ensure that only ipv4 with ip 192.168.4.160 is selected, I will use name="Wohnzimmer", additionalFilters="raop_output.wohnzimmer.local.*###.192.168.4.160.". This will result in raop_output.wohnzimmer.local.3 to be selected.

With the present implementation of additionalFilters and grabbing the first sink found, you have provided a very flexible solution suited for multiroom audio devices under pulseaudio! Thanks a lot for your patience.

Best regards,
Peter

@dalgwen
Copy link
Contributor

dalgwen commented Apr 6, 2022

I'm glad if it works. I will make a pull request soon.
If maintainers are OK, it could be integrated in the official 3.3.0 release.
Meantime, feel free to test as much as possible.

dalgwen added a commit to dalgwen/openhab-addons that referenced this issue Apr 11, 2022
To identify the device on the pulseaudio server, you can now use the description instead of the technical id (a.k.a. "name").
To filter furthermore, you can also use the parameter additionalFilters (optional regular expressions that need to match a property value of a device on the pulseaudio server)

Closes openhab#12555

Signed-off-by: Gwendal Roulleau <[email protected]>
@herrep
Copy link
Author

herrep commented Apr 20, 2022

Dear Gwendal,

Thank you very much for all your efforts. THe pulseaudio sinks can be properly addressed despite the unambiguous naming issues. However, the issue with getting wrong settings after the naming of a particular pulseaudio sink changes is still relevant. I will open a new ticket for this.

Best regards,
Peter

lolodomo pushed a commit that referenced this issue Apr 28, 2022
…ice (#12598)

* [pulseaudio] Allow flexible parameters to find a given pulseaudio device

To identify the device on the pulseaudio server, you can now use the description instead of the technical id (a.k.a. "name").
To filter furthermore, you can also use the parameter additionalFilters (optional regular expressions that need to match a property value of a device on the pulseaudio server)

Closes #12555

Signed-off-by: Gwendal Roulleau <[email protected]>
andan67 pushed a commit to andan67/openhab-addons that referenced this issue Nov 6, 2022
…ice (openhab#12598)

* [pulseaudio] Allow flexible parameters to find a given pulseaudio device

To identify the device on the pulseaudio server, you can now use the description instead of the technical id (a.k.a. "name").
To filter furthermore, you can also use the parameter additionalFilters (optional regular expressions that need to match a property value of a device on the pulseaudio server)

Closes openhab#12555

Signed-off-by: Gwendal Roulleau <[email protected]>
andrasU pushed a commit to andrasU/openhab-addons that referenced this issue Nov 12, 2022
…ice (openhab#12598)

* [pulseaudio] Allow flexible parameters to find a given pulseaudio device

To identify the device on the pulseaudio server, you can now use the description instead of the technical id (a.k.a. "name").
To filter furthermore, you can also use the parameter additionalFilters (optional regular expressions that need to match a property value of a device on the pulseaudio server)

Closes openhab#12555

Signed-off-by: Gwendal Roulleau <[email protected]>
Signed-off-by: Andras Uhrin <[email protected]>
psmedley pushed a commit to psmedley/openhab-addons that referenced this issue Feb 23, 2023
…ice (openhab#12598)

* [pulseaudio] Allow flexible parameters to find a given pulseaudio device

To identify the device on the pulseaudio server, you can now use the description instead of the technical id (a.k.a. "name").
To filter furthermore, you can also use the parameter additionalFilters (optional regular expressions that need to match a property value of a device on the pulseaudio server)

Closes openhab#12555

Signed-off-by: Gwendal Roulleau <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature for an existing add-on
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants