# print(dbus_filter) # /org/example/ca/server def cb_server_signal_emission(*args): """ Callback on emitting signal from server Emitted signal is the value of the counter Data is in args[4]. The first item in a tuple.args[4][0] """ data = args[4][0] print("Client:", data) if __name__=="__main__": print("Client Starting...") # Subscribe to bus to monitor for server signal emissions # dbus_filter. E.g. /org/example/ca/server bus.subscribe(object = dbus_filter, signal_fired = cb_server_signal_emission) loop.run() ''' Example of client_test.py... $ python3 client_test.py Client Starting... Client: The random integer for this second is 12 Client: The random integer for this second is 98 Client: The random integer for this second is 6 '''
def main(api_endpoint, credentials, project_id, device_model_id, device_id, device_config, lang, verbose, input_audio_file, output_audio_file, audio_sample_rate, audio_sample_width, audio_iter_size, audio_block_size, audio_flush_size, grpc_deadline, once, *args, **kwargs): """Samples for the Google Assistant API. Examples: Run the sample with microphone input and speaker output: $ python -m googlesamples.assistant Run the sample with file input and speaker output: $ python -m googlesamples.assistant -i <input file> Run the sample with file input and output: $ python -m googlesamples.assistant -i <input file> -o <output file> """ # Setup logging. logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO) # Load OAuth 2.0 credentials. try: with open(credentials, 'r') as f: credentials = google.oauth2.credentials.Credentials(token=None, **json.load(f)) http_request = google.auth.transport.requests.Request() credentials.refresh(http_request) except Exception as e: logging.error('Error loading credentials: %s', e) logging.error('Run google-oauthlib-tool to initialize ' 'new OAuth 2.0 credentials.') sys.exit(-1) # D-Bus preparation cv_trigger = Condition(Lock()) eventq = Queue() dbus_obj = DBusSignals() loop = GLib.MainLoop() system_bus = SystemBus() def dbus_handler(sender, object, iface, signal, params): # logging.info(sender) # logging.info(object) # logging.info(signal) # logging.info(params) logging.debug('Received D-Bus signal: {}'.format(signal)) if signal == 'trigger': cv_trigger.acquire() cv_trigger.notify() cv_trigger.release() pub = system_bus.publish("io.respeaker.respeakerd", dbus_obj) sub = system_bus.subscribe(iface='respeakerd.signal', signal_fired=dbus_handler) def exit_dbus(): sub.unsubscribe() pub.unpublish() # Create an authorized gRPC channel. grpc_channel = google.auth.transport.grpc.secure_authorized_channel( credentials, http_request, api_endpoint) logging.info('Connecting to %s', api_endpoint) # Configure audio source and sink. audio_device = None if input_audio_file: audio_source = audio_helpers.WaveSource( open(input_audio_file, 'rb'), sample_rate=audio_sample_rate, sample_width=audio_sample_width) else: audio_source = audio_device = (audio_device or audio_helpers.SoundDeviceStream( sample_rate=audio_sample_rate, sample_width=audio_sample_width, block_size=audio_block_size, flush_size=audio_flush_size)) if output_audio_file: audio_sink = audio_helpers.WaveSink(open(output_audio_file, 'wb'), sample_rate=audio_sample_rate, sample_width=audio_sample_width) else: audio_sink = audio_device = (audio_device or audio_helpers.SoundDeviceStream( sample_rate=audio_sample_rate, sample_width=audio_sample_width, block_size=audio_block_size, flush_size=audio_flush_size)) # Create conversation stream with the given audio source and sink. conversation_stream = audio_helpers.ConversationStream( source=audio_source, sink=audio_sink, iter_size=audio_iter_size, sample_width=audio_sample_width, ) if not device_id or not device_model_id: try: with open(device_config) as f: device = json.load(f) device_id = device['id'] device_model_id = device['model_id'] logging.info("Using device model %s and device id %s", device_model_id, device_id) except Exception as e: logging.warning('Device config not found: %s' % e) logging.info('Registering device') if not device_model_id: logging.error('Option --device-model-id required ' 'when registering a device instance.') exit_dbus() sys.exit(-1) if not project_id: logging.error('Option --project-id required ' 'when registering a device instance.') exit_dbus() sys.exit(-1) device_base_url = ('https://%s/v1alpha2/projects/%s/devices' % (api_endpoint, project_id)) device_id = str(uuid.uuid1()) payload = { 'id': device_id, 'model_id': device_model_id, 'client_type': 'SDK_SERVICE' } session = google.auth.transport.requests.AuthorizedSession( credentials) r = session.post(device_base_url, data=json.dumps(payload)) if r.status_code != 200: logging.error('Failed to register device: %s', r.text) exit_dbus() sys.exit(-1) logging.info('Device registered: %s', device_id) pathlib.Path(os.path.dirname(device_config)).mkdir(exist_ok=True) with open(device_config, 'w') as f: json.dump(payload, f) device_handler = device_helpers.DeviceRequestHandler(device_id) @device_handler.command('action.devices.commands.OnOff') def onoff(on): if on: logging.info('Turning device on') else: logging.info('Turning device off') # If file arguments are supplied: # exit after the first turn of the conversation. if input_audio_file or output_audio_file: with SampleAssistant(lang, device_model_id, device_id, conversation_stream, grpc_channel, grpc_deadline, device_handler, eventq) as assistant: dbus_obj.ready() assistant.assist() exit_dbus() return # else it's a long term run def assistant_thread(conversation_stream_): with SampleAssistant(lang, device_model_id, device_id, conversation_stream_, grpc_channel, grpc_deadline, device_handler, eventq) as assistant: dbus_obj.ready() # If no file arguments supplied: # keep recording voice requests using the microphone # and playing back assistant response using the speaker. # When the once flag is set, don't wait for a trigger. Otherwise, wait. wait_for_user_trigger = not once # capture device should always be opened when not playing conversation_stream_.start_recording() while True: if wait_for_user_trigger: logging.info("speak hotword to wake up") cv_trigger.acquire() cv_trigger.wait() cv_trigger.release() logging.info("wake up!") continue_conversation = False try: continue_conversation = assistant.assist() except PortAudioError as e: logging.warn('PortAudio Error: {}'.format(str(e))) eventq.put('on_idle') # wait for user trigger if there is no follow-up turn in # the conversation. wait_for_user_trigger = not continue_conversation # If we only want one conversation, break. if once and (not continue_conversation): break exit_dbus() logging.debug('Exit from the assistant thread...') loop.quit() def event_process_thread(): while True: event = eventq.get() if event == 'on_listen': dbus_obj.on_listen() elif event == 'on_think': dbus_obj.on_think() elif event == 'on_speak': dbus_obj.on_speak() elif event == 'on_idle': dbus_obj.on_idle() time.sleep(0.5) def on_exit(sig): exit_dbus() loop.quit() logging.info("Quit...") setup_signals( signals=[sys_signal.SIGINT, sys_signal.SIGTERM, sys_signal.SIGHUP], handler=on_exit) # make conversation_stream writable inside thread thrd1 = Thread(target=assistant_thread, args=(conversation_stream, )) thrd2 = Thread(target=event_process_thread) thrd1.daemon = True thrd2.daemon = True thrd1.start() thrd2.start() logging.info("Glib mainloop start running...") loop.run() dbus_obj.on_idle()
class BusClient: """ Base class to use to implement a DbusClient using the sink services It automatically manage sink connection/disconnection and offers some abstraction of dbus """ def __init__(self, logger=None, c_extension=True, ignored_ep_filter=None): # logger self.logger = logger or logging.getLogger(__name__) # Main loop for events self.loop = GLib.MainLoop() # Connect to session bus self.bus = SystemBus() # Manage sink list self.sink_manager = SinkManager( bus=self.bus, on_new_sink_cb=self.on_sink_connected, on_sink_removal_cb=self.on_sink_disconnected, on_stack_started=self.on_stack_started, logger=self.logger, ) self.ignore_ep_filter = ignored_ep_filter # Register for packet on Dbus if c_extension: self.logger.info("Starting dbus client with c extension") self.c_extension_thread = DbusEventHandler( self._on_data_received_c, self.logger) else: self.logger.info("Starting dbus client without c extension") # Subscribe to all massages received from any sink (no need for # connected sink for that) self.bus.subscribe( signal="MessageReceived", object="/com/wirepas/sink", signal_fired=self._on_data_received, ) self.c_extension_thread = None def _on_data_received_c( self, sender, timestamp, src, dst, src_ep, dst_ep, travel_time, qos, hop_count, data, ): # Could be done in C extension if needed by providing list to extension if self.ignore_ep_filter is not None and dst_ep in self.ignore_ep_filter: self.logger.debug("Message received on ep % filtered out", dst_ep) return # Get sink name from sender unique name name = self.sink_manager.get_sink_name(sender) self.on_data_received( sink_id=name, timestamp=timestamp, src=src, dst=dst, src_ep=src_ep, dst_ep=dst_ep, travel_time=travel_time, qos=qos, hop_count=hop_count, data=data, ) def _on_data_received(self, sender, object, iface, signal, params): # pylint: disable=unused-argument # pylint: disable=redefined-builtin # filter out endpoint if self.ignore_ep_filter is not None and params[ 4] in self.ignore_ep_filter: self.logger.debug("Message received on ep % filtered out", params[4]) return # Get sink name from sender unique name name = self.sink_manager.get_sink_name(sender) self.on_data_received( sink_id=name, timestamp=params[0], src=params[1], dst=params[2], src_ep=params[3], dst_ep=params[4], travel_time=params[5], qos=params[6], hop_count=params[7], data=bytearray(params[8]), ) def run(self): self.on_start_client() # If needed start C extension thread if self.c_extension_thread is not None: self.c_extension_thread.start() # For now, start the GLib loop for even if C extension is # in use as some signals are still handled on it. But should not be # the case in future. Even without handling signals, this thread takes # 30% of one CPU on a rpi3. try: self.loop.run() except KeyboardInterrupt: self.loop.quit() self.on_stop_client() def stop_dbus_client(self): """ Explicitly stop the dbus client """ self.loop.quit() # Method should be overwritten by child class def on_data_received( self, sink_id, timestamp, src, dst, src_ep, dst_ep, travel_time, qos, hop_count, data, ): pass def on_sink_connected(self, name): pass def on_sink_disconnected(self, name): pass def on_stack_started(self, name): pass def on_start_client(self): pass def on_stop_client(self): pass
bus_name = "org.example.ca.server" dbus_filter = "/" + "/".join(bus_name.split(".")) # print(dbus_filter) # /org/example/ca/server def cb_server_signal_emission(*args): """ Callback on emitting signal from server Emitted signal is the value of the counter Data is in args[4]. The first item in a tuple.args[4][0] """ data = args[4][0] print("Client:", data) if __name__ == "__main__": print("Client Starting...") # Subscribe to bus to monitor for server signal emissions # dbus_filter. E.g. /org/example/ca/server bus.subscribe(object=dbus_filter, signal_fired=cb_server_signal_emission) loop.run() ''' Example of client_test.py... $ python3 client_test.py Client Starting... Client: The random integer for this second is 12 Client: The random integer for this second is 98 Client: The random integer for this second is 6 '''
if signal != 'ActiveChanged': logging.debug("Not what we are looking for, ignored") return logging.debug("START OF SIGNAL PROCESSING") if signal_msg is True: logging.debug("Screensaver now active") else: logging.info("Running 'on 0\\nas\\nquit' to turn on TV") proc = subprocess.Popen(['/usr/bin/cec-client'], stdin=subprocess.PIPE) results = proc.communicate(input='on 0\nas\nquit\n'.encode()) time.sleep(3) logging.debug(results[0]) logging.debug(results[1]) proc.terminate() proc.kill() logging.debug("END OF SIGNAL PROCESSING") BUS.subscribe(iface=DBUS_INTERFACE, signal_fired=signal_recieved) # loop forever, until CTRL+C, or something goes wrong try: MainLoop().run() except KeyboardInterrupt: logging.debug('Got CTRL+C, exiting cleanly') raise SystemExit
def main(): # enable led power en = mraa.Gpio(12) if os.geteuid() != 0: time.sleep(1) en.dir(mraa.DIR_OUT) en.write(0) pixel_ring.set_brightness(100) pixel_ring.think() sm = StateMachine() def bus_handler(sender, object, iface, signal, params): # print(sender) # print(object) # print(signal) # print(params) if signal == 'trigger': try: dir = params[0] except: dir = 0 sm.on_detected(dir) elif signal == 'on_listen': sm.on_listening() elif signal == 'on_think': sm.on_thinking() elif signal == 'on_speak': sm.on_speaking() elif signal == 'on_idle': sm.on_idle() elif signal == 'connecting': sm.on_thinking() elif signal == 'ready': sm.on_avs_ready() elif signal == 'respeakerd_ready': sm.on_respeakerd_ready() system_bus = SystemBus() loop = GLib.MainLoop() sub = system_bus.subscribe(iface='respeakerd.signal', signal_fired=bus_handler) def on_exit(sig): sub.unsubscribe() loop.quit() setup_signals(signals=[signal.SIGINT, signal.SIGTERM, signal.SIGHUP], handler=on_exit) print("Running...") try: loop.run() except KeyboardInterrupt: pass finally: pixel_ring.off() print("Quit...") en.write(1)
if __name__ == '__main__': logging.debug('starting') bus = SystemBus() if_list = [ ('org.ofono.Modem', 'PropertyChanged', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), prop(signal, params))), ('org.ofono.VoiceCall', 'PropertyChanged', lambda _a, obj, iface, signal, params: mqtt(vcdev(obj, iface), prop(signal, params))), ('org.ofono.CallVolume', 'PropertyChanged', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), prop(signal, params))), ('org.ofono.VoiceCallManager', 'PropertyChanged', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), prop(signal, params))), ('org.ofono.VoiceCallManager', 'CallAdded', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), cadd(signal, params))), ('org.ofono.VoiceCallManager', 'CallRemoved', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), crem(signal, params))), ('org.ofono.NetworkRegistration', 'PropertyChanged', lambda _a, obj, iface, signal, params: mqtt(dev(obj, iface), prop(signal, params))), ] [ bus.subscribe(iface=iface, signal=signal, signal_fired=cb) for iface, signal, cb in if_list ] mqtt("monitor", payload="started", retain=True) loop = GLib.MainLoop() loop.run()
class Central: """ Create a device in a central role and communicate with a peripheral device """ def __init__(self, address): self.bus = SystemBus() self.mngr = self.bus.get(BLUEZ_SERVICE, '/') self._device_path = self._from_device_address(address) if self._device_path is None: raise KeyError(f'Device address [{address}] is not found.' f'Use bluetoothctl to find what devices are nearby') self._adapter_path = '/'.join(self._device_path.split('/')[:-1]) self.adapter = self.bus.get(BLUEZ_SERVICE, self._adapter_path)['org.bluez.Adapter1'] self.device = self.bus.get(BLUEZ_SERVICE, self._device_path) self.chrcs = {} self._handlers = {} self.mainloop = GLib.MainLoop() def _from_device_address(self, addr): """Look up Device D-Bus object path from device address""" mng_objs = self.mngr.GetManagedObjects() for path in mng_objs: dev_addr = mng_objs[path].get(BLUEZ_DEV_IFACE, {}).get('Address', '') if addr.casefold() == dev_addr.casefold(): return path return None def _get_device_chars(self): """Find all GATT characteristics for device. Store for look-up later""" mng_objs = self.mngr.GetManagedObjects() for path in mng_objs: chr_uuid = mng_objs[path].get(BLUEZ_CHR_IFACE, {}).get('UUID') if path.startswith(self._device_path) and chr_uuid: self.chrcs[chr_uuid] = self.bus.get(BLUEZ_SERVICE, path) def _get_uuid(self, path): """Get the Characteristics UUID from its D-Bus object path""" mng_objs = self.mngr.GetManagedObjects() return mng_objs[path].get(BLUEZ_CHR_IFACE, {}).get('UUID') def connect(self): """ Connect to device. Wait for GATT services to be resolved before returning """ self.device.Connect() while not self.device.ServicesResolved: sleep(0.5) self._get_device_chars() def disconnect(self): """Disconnect from device""" self.device.Disconnect() def char_write(self, uuid, value): """Write value to given GATT characteristic UUID""" if uuid.casefold() in self.chrcs: self.chrcs[uuid.casefold()].WriteValue(value, {}) else: raise KeyError(f'UUID {uuid} not found') def char_read(self, uuid): """Read value of given GATT characteristic UUID""" if uuid.casefold() in self.chrcs: return self.chrcs[uuid.casefold()].ReadValue({}) raise KeyError(f'UUID {uuid} not found') def _chrc_value_update(self, bus_name, path, signal_iface, signal_name, notification): """ Only send notifications to a users event handler when it is a GATT characteristics value that has changed """ uuid = self._get_uuid(path) iface, props_changed, props_removed = notification if iface == BLUEZ_CHR_IFACE and 'Value' in props_changed: self._handlers[uuid.casefold()].user_handler( props_changed['Value']) def on_value_change(self, uuid, handler): """ A function reference which will be called when a property value is changed for given GATT characteristic UUID if notifications are available """ uuid = uuid.casefold() if uuid in self.chrcs: if all(('notify' not in self.chrcs[uuid].Flags, 'indicate' not in self.chrcs[uuid].Flags)): raise NotImplementedError( f'Notifications are not implemented on {uuid}') subs = self.bus.subscribe(iface='org.freedesktop.DBus.Properties', signal='PropertiesChanged', object=self.chrcs[uuid]._path, signal_fired=self._chrc_value_update) self._handlers[uuid] = PropChangedHandler(handler, subs) self.chrcs[uuid].StartNotify() else: raise KeyError(f'UUID {uuid} not found') def remove_notify(self, uuid): """Stop notifications for given GATT characteristic UUID""" uuid = uuid.casefold() if uuid in self.chrcs and uuid in self._handlers: self.chrcs[uuid.casefold()].StopNotify() self._handlers[uuid.casefold()].subscription.disconnect() del self._handlers[uuid] def wait_for_notifications(self): """ Has the effect of block the code from exiting. In the background it starts an event loop to listen for updates from the device """ try: self.mainloop.run() except KeyboardInterrupt: self.cleanup() def cleanup(self): """ If you have the used `wait_for_notifications`, then this command will remove that blocking. This will stop the event loop, remove all notification subscriptions, disconnect from the peripheral device. """ self.mainloop.quit() all_uuids = list(self._handlers.keys()) for uuid in all_uuids: self.remove_notify(uuid) self.disconnect()
class Bluetooth: def __init__(self, name): self.__bus = SystemBus() self.__adapter = self.__bus.get(BLUEZ_SERVICE, 'hci0')[INTERFACE['ADAPTER']] self.__set_alias(name) self.__player = None self.__change_callback = None self.__bus.subscribe(sender=BLUEZ_SERVICE, signal='PropertiesChanged', arg0=INTERFACE['ADAPTER'], signal_fired=self.__adapter_handler) self.__bus.subscribe(sender=BLUEZ_SERVICE, signal='PropertiesChanged', arg0=INTERFACE['MEDIA_CONTROL'], signal_fired=self.__mediacontrol_handler) self.__bus.subscribe(sender=BLUEZ_SERVICE, signal='PropertiesChanged', arg0=INTERFACE['MEDIA_PLAYER'], signal_fired=self.__mediaplayer_handler) def start_discovery(self): self.__powered() self.__pairable() self.__discoverable() return self.__adapter.StartDiscovery() def set_on_change(self, callback): self.__change_callback = callback @property def device_name(self): device_object = self.__bus.get( BLUEZ_SERVICE, self.__player[INTERFACE['MEDIA_PLAYER']].Device) device = device_object[INTERFACE['DEVICE']] return device.Alias @property def current_track(self): player = self.__player[INTERFACE['MEDIA_PLAYER']] return { 'title': player.Track.get('Title', ''), 'album': player.Track.get('Album', ''), 'artist': player.Track.get('Artist', ''), 'genre': player.Track.get('Genre', ''), 'track_number': player.Track.get('TrackNumber', 0), 'number_of_tracks': player.Track.get('NumberOfTracks', 0), 'duration_ms': player.Track.get('Duration', 0), 'position_ms': player.Position, 'status': player.Status.lower() } def play(self): try: self.__player[INTERFACE['MEDIA_PLAYER']].Play() except GLib.Error: log.warning('Can\'t play track, player not ready.') def pause(self): try: self.__player[INTERFACE['MEDIA_PLAYER']].Pause() except GLib.Error: log.warning('Can\'t pause track, player not ready.') def next(self): try: self.__player[INTERFACE['MEDIA_PLAYER']].Next() except GLib.Error: log.warning('Can\'t go to next track, player not ready.') def previous(self): try: self.__player[INTERFACE['MEDIA_PLAYER']].Previous() except GLib.Error: log.warning('Can\'t go to previous track, player not ready.') def shuffle(self, setting): shuffle_settings = ['off', 'alltracks', 'group'] if setting not in shuffle_settings: return try: self.__player[INTERFACE['MEDIA_PLAYER']].Shuffle = setting except GLib.Error: log.warning('Can\'t set shuffle mode to %s, player not ready.', setting) def repeat(self, setting): repeat_settings = ['off', 'singletrack', 'alltracks', 'group'] if setting not in repeat_settings: return try: self.__player[INTERFACE['MEDIA_PLAYER']].Repeat = setting except GLib.Error: log.warning('Can\'t set repeat mode to %s, player not ready.', setting) def __get_managed_objects(self): object_manager = self.__bus.get( BLUEZ_SERVICE, '/')['org.freedesktop.DBus.ObjectManager'] return object_manager.GetManagedObjects() def __set_alias(self, alias): self.__adapter.Alias = alias def __powered(self): self.__adapter.Powered = True def __discoverable(self, timeout=90): self.__adapter.Discoverable = True self.__adapter.DiscoverableTimeout = timeout def __pairable(self, timeout=90): self.__adapter.Pairable = True self.__adapter.PairableTimeout = timeout def __get_devices(self): objects = self.__get_managed_objects() devices_filter = lambda item: INTERFACE['DEVICE'] in item[1] return dict(filter(devices_filter, objects.items())) def __get_paired_media_devices(self): devices = self.__get_devices() paired_media_filter = lambda item: item[1][INTERFACE['DEVICE']][ 'Paired'] and INTERFACE['MEDIA_CONTROL'] in item[1] return dict(filter(paired_media_filter, devices.items())) def __connect_to_paired_media_device(self): devices = self.__get_paired_media_devices() device_path = list(devices.keys())[0] device_object = self.__bus.get(BLUEZ_SERVICE, device_path) device = device_object[INTERFACE['DEVICE']] if device.Connected: log.info('Connected to device %s.', device.Name) player_path = device_object[INTERFACE['MEDIA_CONTROL']].Player self.__init_player(player_path) else: try: device.Connect() except GLib.GError as err: log.warning('Can\'t connect to device %s.', device.Alias) def __init_player(self, player_path): self.__player = self.__bus.get(BLUEZ_SERVICE, player_path) def __adapter_handler(self, sender, path, interface, signal, parameters): interface, properties, optional = parameters if 'Discovering' in properties: log.info('Discovering active. Looking for devices.') try: self.__connect_to_paired_media_device() except: log.info('No paired devices found.') def __mediacontrol_handler(self, sender, path, interface, signal, parameters): interface, properties, optional = parameters if properties['Connected']: if 'Player' in properties: self.__init_player(properties['Player']) def __mediaplayer_handler(self, sender, path, interface, signal, parameters): self.__change_callback()