3 from pulsectl import Pulse,PulseLoopStop
9 from ConfigParser import ConfigParser
10 import paho.mqtt.client as paho
12 # ====================== PulseAudio-related part ========================
20 paLock1=threading.RLock()
21 paLock2=threading.RLock()
28 pulse = Pulse("mqtt-pa")
35 return pulse.server_info().default_sink_name
38 sinkname=GetDefaultOut()
39 for sink in pulse.sink_list():
40 if sink.name==sinkname:
43 def GetDefaultVolume():
44 return pulse.volume_get_all_chans(GetDefaultSink())
46 def SetDefaultVolume(volume):
47 pulse.volume_set_all_chans(GetDefaultSink(), volume)
50 return GetDefaultSink().mute<>0
52 def MuteDefault(mute = True):
53 return pulse.mute(GetDefaultSink(),mute)
56 tname=threading.current_thread().name
58 print tname," aquiring action..."
61 print tname," aquired action..."
62 pulse.event_listen_stop()
64 print tname," event_listener stop command sent..."
65 print tname," aquiring loop..."
68 print tname," aquired loop..."
71 tname=threading.current_thread().name
73 print tname," releasing loop..."
76 print tname," released loop..."
77 print tname," releasing action..."
80 print tname," released action..."
83 tname=threading.current_thread().name
85 print tname," aquiring loop..."
88 print tname," aquired loop..."
90 def ReleaseLoopLock():
91 tname=threading.current_thread().name
93 print tname," releasing loop..."
96 print tname," released loop..."
98 def EventListener(callback):
99 pulse.event_mask_set('all')
100 pulse.event_callback_set(callback)
107 def EventProcess(ev):
111 global sink_name,muted,volume
114 tname=threading.current_thread().name
115 current_sink=GetDefaultOut()
116 current_vol=round(GetDefaultVolume(),2)
117 current_muted="ON" if IsDefaultMuted() else "OFF"
118 if current_sink<>sink_name:
119 sink_name=current_sink
121 print 'sink='+sink_name
125 callback_changed('sink',sink_name)
126 if current_vol<>volume:
129 print 'volume='+str(volume)
133 callback_changed('volume',str(volume))
134 if current_muted<>muted:
141 callback_changed('muted',muted)
147 EventListener(EventProcess)
150 def RunBackground(process):
152 thread = threading.Thread(target=process,name="Background")
155 def StopBackground():
158 pulse.event_listen_stop()
160 def CommandGetDefaultOut():
163 result=GetDefaultOut()
168 def CommandGetDefaultVolume():
171 result=GetDefaultVolume()
176 def CommandIsDefaultMuted():
179 result=IsDefaultMuted()
184 def CommandSetDefaultVolume(volume):
187 SetDefaultVolume(volume)
191 def CommandMuteDefault(mute=True):
198 # ====================== MQTT-related part ========================
200 lockMQTT = threading.RLock()
202 def on_message(mosq, obj, msg):
204 print("Received: " + msg.topic + " " + str(msg.payload))
206 subtopic=msg.topic[len(mqtt_topic_in)+1:]
208 if subtopic=="volume":
209 CommandSetDefaultVolume(float(payload))
210 elif subtopic=="muted":
217 CommandMuteDefault(payload)
220 print "Unknown command"
223 print "Command failed"
227 global client,mqtt_topic_in,mqtt_topic_out
229 conffile = sys.argv[1]
231 config = ConfigParser()
232 config.add_section('mqtt')
233 # set defaults for anonymous auth
234 config.set('mqtt', 'username', '')
235 config.set('mqtt', 'password', '')
236 config.set('mqtt', 'port', '1883')
237 config.set('mqtt', 'in', 'pulse/in')
238 config.set('mqtt', 'out', 'pulse/out')
239 config.read(conffile)
241 mqtt_server = config.get('mqtt', 'server')
242 mqtt_port = config.getint('mqtt', 'port')
243 mqtt_username = config.get('mqtt', 'username')
244 mqtt_password = config.get('mqtt', 'password')
245 mqtt_topic_in = config.get('mqtt', 'in')
246 mqtt_topic_out = config.get('mqtt', 'out')
248 client = paho.Client('pulse')
249 client.username_pw_set(mqtt_username, mqtt_password)
250 client.on_message=on_message
251 client.connect(mqtt_server, port=mqtt_port)
252 client.subscribe(mqtt_topic_in+'/#',1)
254 def MQTTCallback(param,value):
258 client.publish(mqtt_topic_out+'/'+param, payload=value)
260 print "Sent "+param+"="+value
264 def StartPulseListener():
265 global callback_changed
268 callback_changed=MQTTCallback
270 RunBackground(PAListener)
278 except KeyboardInterrupt: