[StumpWM] Add bluetooth utility code

This commit is contained in:
Lucien Cartier-Tilet 2021-11-22 11:17:15 +01:00
parent 0b9e9d2655
commit 667ebc8db3
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
1 changed files with 162 additions and 1 deletions

View File

@ -138,6 +138,7 @@ Now, well load a couple of my custom files that will be described below:
#+name: first-loaded-files
| File to be loaded |
|-------------------|
| bluetooth.lisp |
| commands.lisp |
| placement.lisp |
| keybindings.lisp |
@ -155,8 +156,9 @@ Now, well load a couple of my custom files that will be described below:
#+end_src
This is equivalent to the Common Lisp code:
#+RESULTS[5ec83707b957847594f436dfabc8458904c4ab8b]: gen-load-files
#+RESULTS[29848aaa616d9b2a828a5602ea6b42dd344efaf2]: gen-load-files
#+begin_src lisp
(load "~/.stumpwm.d/bluetooth.lisp")
(load "~/.stumpwm.d/commands.lisp")
(load "~/.stumpwm.d/placement.lisp")
(load "~/.stumpwm.d/keybindings.lisp")
@ -896,6 +898,165 @@ Binwarp mode is now available from the keybind ~s-m~ at top level.
((kbd "q") "exit-binwarp"))
#+end_src
** Bluetooth
:PROPERTIES:
:CUSTOM_ID: Utilities-Bluetooth-rns0nr902aj0
:header-args:lisp: :mkdirp yes :tangle ~/.stumpwm.d/bluetooth.lisp :noweb yes
:END:
Although there is a bluetooth module for the modeline, this is about
the extent to which StumpWM can interact with the systems bluetooth.
However, I wish for some more interecactivity, like powering on and
off bluetooth, connecting to devices and so on.
First, out code relies on ~cl-ppcre~, so lets quickload it.
#+begin_src lisp
(ql:quickload :cl-ppcre)
#+end_src
Lets indicate which command well be using.
#+begin_src lisp
(defvar *bluetooth-command* "bluetoothctl"
"Base command for interacting with bluetooth.")
#+end_src
*** Utilities
:PROPERTIES:
:CUSTOM_ID: Utilities-Bluetooth-Utilities-3zicf7k03aj0
:END:
Well need a couple of functions that will take care of stuff for us
so we dont have to repeat ourselves. The first one is a way for us to
share a message. The function ~bluetooth-message~ will first display
~Bluetooth:~ in green, then it will display the message we want it to
display.
#+begin_src lisp
(defun bluetooth-message (&rest message)
(message (format nil
"^2Bluetooth:^7 ~{~A~^ ~}"
message)))
#+end_src
This function is a builder function which will create our commands.
For instance, src_lisp[:exports code]{(bluetooth-make-command "power"
"on")} will return ~"bluetoothctl power on"~ with ~*bluetooth-ctl*~ set as
~"bluetoothctl"~ --- simply put, it joins ~*bluetooth-command*~ with ~args~
with a space as their separator.
#+begin_src lisp
(defun bluetooth-make-command (&rest args)
(format nil
"~a ~{~A~^ ~}"
,*bluetooth-command*
args))
#+end_src
Now we can put ~bluetooth-make-command~ to use with ~bluetooth-command~
which will actually run the result of the former. As you can see, it
also collects the output so we can display it later in another
function.
#+begin_src lisp
(defmacro bluetooth-command (&rest args)
`(run-shell-command (bluetooth-make-command ,@args) t))
#+end_src
Finally, ~bluetooth-message-command~ is the function that both executes
and also displays the result of the bluetooth command we wanted to see
executed. Each argument of the command is a separate string. For
instance, if we want to power on the bluetooth on our device, we can
call src_lisp[:exports code]{(bluetooth-message-command "power"
"on")}.
#+begin_src lisp
(defmacro bluetooth-message-command (&rest args)
`(bluetooth-message (bluetooth-command ,@args)))
#+end_src
*** Toggle Bluetooth On and Off
:PROPERTIES:
:CUSTOM_ID: Utilities-Bluetooth-Toggle-Bluetooth-On-and-Off-9pyfbtd02aj0
:END:
This part is easy. Now that we can call our bluetooth commands easily,
we can easily define how to turn on bluetooth.
#+begin_src lisp
(defcommand bluetooth-turn-on () ()
"Turn on bluetooth."
(bluetooth-message-command "power" "on"))
#+end_src
And how to power it off.
#+begin_src lisp
(defcommand bluetooth-turn-off () ()
"Turn off bluetooth."
(bluetooth-message-command "power" "off"))
#+end_src
*** Bluetooth Devices
:PROPERTIES:
:CUSTOM_ID: Utilities-Bluetooth-Bluetooth-Devices-196gbtd02aj0
:END:
In order to manipulate bluetooth device, which we can represent as a
MAC address and a name, we can create a structure that will make use
of a constructor for simpler use. The constructor
~make-bluetooth-device-from-command~ expects an entry such as ~Device
00:00:00:00:00:00 Home Speaker~. The constructor discards the term
~Device~ and stores the MAC address separately from the rest of the
string which is assumed to be the full name of the device.
#+begin_src lisp
(defstruct (bluetooth-device
(:constructor
make-bluetooth-device (&key (address "")
(name nil)))
(:constructor
make-bluetooth-device-from-command
(&key (raw-name "")
&aux (address (cadr (cl-ppcre:split " " raw-name)))
(full-name (format nil "~{~A~^ ~}" (cddr (cl-ppcre:split " " raw-name)))))))
address
(full-name (progn
(format nil "~{~A~^ ~}" name))))
#+end_src
We can now collect our devices easily.
#+begin_src lisp
(defun bluetooth-get-devices ()
(let ((literal-devices (bluetooth-command "devices")))
(mapcar (lambda (device)
(make-bluetooth-device-from-command :raw-name device))
(cl-ppcre:split "\\n" literal-devices))))
#+end_src
*** Connect to a device
:PROPERTIES:
:CUSTOM_ID: Utilities-Bluetooth-Connect-to-a-device-tjqcf7k03aj0
:END:
When we want to connect to a bluetooth device, we always need
bluetooth turned on, so ~bluetooth-turn-on~ will always be called. Then
the function will attempt to connect to the device specified by the
~device~ argument, whether the argument is a bluetooth structure as
defined above or a plain MAC address.
#+begin_src lisp
(defun bluetooth-connect-device (device)
(progn
(bluetooth-turn-on)
(cond ((bluetooth-device-p device) ;; it is a bluetooth-device structure
(bluetooth-message-command "connect"
(bluetooth-device-address device)))
((stringp device) ;; assume it is a MAC address
(bluetooth-message-command "connect" device))
(t (message (format nil "Cannot work with device ~a" device))))))
#+end_src
The command to connect to a device displays a choice between the
collected bluetooth device and the user only has to select it. It will
then attempt to connect to it.
#+begin_src lisp
(defcommand bluetooth-connect () ()
(let* ((devices (bluetooth-get-devices))
(choice (cdr (stumpwm:select-from-menu
(stumpwm:current-screen)
(mapcar (lambda (device)
`(,(bluetooth-device-full-name device) . ,device))
devices)))))
(bluetooth-connect-device choice)))
#+end_src
** NetworkManager integration
:PROPERTIES:
:CUSTOM_ID: Utilities-NetworkManager-integration-nm7jxbt0z9j0