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/100.0)
49 print "volume set %s" % volume
52 return GetDefaultSink().mute<>0
54 def MuteDefault(mute = True):
55 pulse.mute(GetDefaultSink(),mute)
57 print "mute set %s" % mute
60 tname=threading.current_thread().name
62 print tname," aquiring action..."
65 print tname," aquired action..."
66 pulse.event_listen_stop()
68 print tname," event_listener stop command sent..."
69 print tname," aquiring loop..."
72 print tname," aquired loop..."
75 tname=threading.current_thread().name
77 print tname," releasing loop..."
80 print tname," released loop..."
81 print tname," releasing action..."
84 print tname," released action..."
87 tname=threading.current_thread().name
89 print tname," aquiring loop..."
92 print tname," aquired loop..."
94 def ReleaseLoopLock():
95 tname=threading.current_thread().name
97 print tname," releasing loop..."
100 print tname," released loop..."
102 def EventListener(callback):
103 pulse.event_mask_set('all')
104 pulse.event_callback_set(callback)
111 def EventProcess(ev):
115 global sink_name,muted,volume
118 tname=threading.current_thread().name
119 current_sink=GetDefaultOut()
120 current_vol=round(GetDefaultVolume()*100,2)
121 current_muted="ON" if IsDefaultMuted() else "OFF"
122 if current_sink<>sink_name:
123 sink_name=current_sink
125 print 'sink='+sink_name
129 callback_changed('sink',sink_name)
130 if current_vol<>volume:
133 print 'volume='+str(volume)
137 callback_changed('volume',str(volume))
138 if current_muted<>muted:
145 callback_changed('muted',muted)
151 EventListener(EventProcess)
154 def RunBackground(process):
156 thread = threading.Thread(target=process,name="Background")
159 def StopBackground():
162 pulse.event_listen_stop()
164 def CommandGetDefaultOut():
167 result=GetDefaultOut()
172 def CommandGetDefaultVolume():
175 result=GetDefaultVolume()
180 def CommandIsDefaultMuted():
183 result=IsDefaultMuted()
188 def CommandSetDefaultVolume(volume):
191 SetDefaultVolume(volume)
195 def CommandMuteDefault(mute=True):
202 # ====================== MQTT-related part ========================
204 lockMQTT = threading.RLock()
206 def on_message(mosq, obj, msg):
208 print("Received: " + msg.topic + " " + str(msg.payload))
210 subtopic=msg.topic[len(mqtt_topic_in)+1:]
212 if subtopic=="volume":
213 CommandSetDefaultVolume(float(payload))
214 elif subtopic=="muted":
221 CommandMuteDefault(payload)
224 print "Unknown command"
227 print "Command failed"
231 global client,mqtt_topic_in,mqtt_topic_out
233 conffile = sys.argv[1]
235 config = ConfigParser()
236 config.add_section('mqtt')
237 # set defaults for anonymous auth
238 config.set('mqtt', 'username', '')
239 config.set('mqtt', 'password', '')
240 config.set('mqtt', 'port', '1883')
241 config.set('mqtt', 'in', 'pulse/in')
242 config.set('mqtt', 'out', 'pulse/out')
243 config.read(conffile)
245 mqtt_server = config.get('mqtt', 'server')
246 mqtt_port = config.getint('mqtt', 'port')
247 mqtt_username = config.get('mqtt', 'username')
248 mqtt_password = config.get('mqtt', 'password')
249 mqtt_topic_in = config.get('mqtt', 'in')
250 mqtt_topic_out = config.get('mqtt', 'out')
252 client = paho.Client('pulse')
253 client.username_pw_set(mqtt_username, mqtt_password)
254 client.on_message=on_message
255 client.connect(mqtt_server, port=mqtt_port)
256 client.subscribe(mqtt_topic_in+'/#',1)
258 def MQTTCallback(param,value):
262 client.publish(mqtt_topic_out+'/'+param, payload=value)
264 print "Sent "+param+"="+value
268 def StartPulseListener():
269 global callback_changed
272 callback_changed=MQTTCallback
274 RunBackground(PAListener)
282 except KeyboardInterrupt: