2 "Interact with Squeezebox media servers"
6 (defcustom squeeze-server-address "localhost"
7 "Address for the Squeezebox server"
9 (defcustom squeeze-server-port 9090
10 "Port number for the Squeezebox server"
13 (defvar squeeze-mode-map
14 (let ((map (make-sparse-keymap)))
15 (define-key map (kbd "TAB") 'completion-at-point)
18 (define-derived-mode squeeze-mode comint-mode "Squeeze"
19 "Major mode for interacting with the Squeezebox Server CLI.\\<squeeze-mode-map>"
20 (add-hook 'comint-preoutput-filter-functions 'url-unhex-string nil t)
21 (add-hook 'comint-preoutput-filter-functions 'squeeze-update-state nil t))
23 (defvar squeeze-control-mode-map
24 (let ((map (make-sparse-keymap)))
25 (define-key map (kbd "SPC") 'squeeze-control-toggle-power)
26 (define-key map (kbd "g") 'squeeze-control-refresh)
29 (define-derived-mode squeeze-control-mode special-mode "SqueezeControl"
30 "Major mode for controlling Squeezebox Servers.\\<squeeze-control-mode-map>")
32 (defun squeeze-update-state (string)
34 (dolist (line (split-string string "\n"))
35 (when (squeeze-update-state-from-line line)
36 (setq done-something t))))
37 (squeeze-control-display-players)
40 (defun squeeze-update-state-from-line (string)
42 ((string-match "^players 0" string)
43 (setq squeeze-players (squeeze-parse-players-line string))
45 ((string-match "^\\([0-9a-f][0-9a-f]%3A[0-9a-f][0-9a-f]%3A[0-9a-f][0-9a-f]%3A[0-9a-f][0-9a-f]%3A[0-9a-f][0-9a-f]%3A[0-9a-f][0-9a-f]\\) power\\( \\([01]\\)\\)?" string)
46 (let ((state (match-string 3 string))
47 (id (url-unhex-string (match-string 1 string)))
49 (dolist (p squeeze-players)
50 (when (string= id (squeeze-player-playerid p))
54 (setf (squeeze-player-power player) state)
55 (let ((current (squeeze-player-power player)))
56 (setf (squeeze-player-power player)
57 (cond ((string= current "0") "1")
58 ((string= current "1") "0"))))))
61 (defface squeeze-player-face
63 "Face for displaying players"
65 (defface squeeze-player-on-face
67 "Face for displaying players which are on"
68 :inherit 'squeeze-player-face
70 (defface squeeze-player-off-face
72 "Face for displaying players which are off"
73 :inherit 'squeeze-player-face
76 (defvar squeeze-players ())
78 (defun squeeze-control-query-players ()
80 (comint-send-string (get-buffer-process "*squeeze*") (format "players ?\n")))
82 (defun squeeze-control-toggle-power (&optional id)
85 (setq id (get-text-property (point) 'squeeze-playerid)))
86 (comint-send-string (get-buffer-process "*squeeze*") (format "%s power\n" id)))
88 (defun squeeze-control-query-power (&optional id)
91 (setq id (get-text-property (point) 'squeeze-playerid)))
92 (comint-send-string (get-buffer-process "*squeeze*") (format "%s power ?\n" id)))
94 (defun squeeze-control-player-face (player)
95 (let ((power (squeeze-player-power player)))
96 (cond ((string= power "1") 'squeeze-player-on-face)
97 ((string= power "0") 'squeeze-player-off-face)
98 (t 'squeeze-player-face))))
100 (defun squeeze-control-listen ()
101 (comint-send-string (get-buffer-process "*squeeze*") (format "listen 1\n")))
103 (defun squeeze-control-refresh ()
105 (squeeze-control-query-players)
106 (dolist (player squeeze-players)
107 (squeeze-control-query-power (squeeze-player-playerid player))))
109 (defun squeeze-control-display-players ()
111 (let ((players squeeze-players))
112 (with-current-buffer (get-buffer-create "*squeeze-control*")
113 (squeeze-control-mode)
116 (dolist (player players)
117 (insert (propertize (squeeze-player-name player)
118 'face (squeeze-control-player-face player)
119 'squeeze-playerid (squeeze-player-playerid player))
121 (read-only-mode 1))))
123 (cl-defstruct (squeeze-player (:constructor squeeze-make-player))
124 playerindex playerid uuid ip name model isplayer displaytype canpoweroff connected power)
126 (defun squeeze-string-plistify (string start end)
130 (message "start: %d" start)
131 (if (string-match "\\([a-z]+\\)%3A\\([^ \n]+\\)" string start)
132 (let ((mend (match-end 0)))
135 (push (intern (format ":%s" (substring string (match-beginning 1) (match-end 1)))) result)
136 (push (url-unhex-string (substring string (match-beginning 2) (match-end 2))) result)
141 (defun squeeze-parse-players-line (string)
142 (let ((countpos (string-match " count%3A\\([0-9]\\) " string))
143 (startpos (match-end 0)))
145 (message "no count found in players line"))
146 (let ((count (parse-integer string (match-beginning 1) (match-end 1)))
148 (dotimes (i (1- count))
149 (setq endpos (progn (string-match " connected%3A[0-1] " string startpos)
151 (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos endpos)) result)
152 (setq startpos endpos))
153 (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos (length string))) result)
156 (defun squeeze-complete-command-at-point ()
158 (list (progn (backward-word) (point))
159 (progn (forward-word) (point))
160 '(;; General commands and queries
161 "login" "can" "version" "listen" "subscribe" "pref"
162 "logging" "getstring" "setsncredentials" "debug"
165 ;; Player commands and queries
166 "player" "count" "id" "uuid" "name" "ip" "model" "isplayer"
167 "displaytype" "canpoweroff" "?" "signalstrength" "connected"
168 "sleep" "sync" "syncgroups" "power" "mixer" "volume" "muting"
169 "bass" "treble" "pitch" "show" "display" "linesperscreen"
170 "displaynow" "playerpref" "button" "ir" "irenable"
171 "connect" "client" "forget" "disconnect" "players"
173 ;; Database commands and queries
174 "rescan" "rescanprogress" "abortscan" "wipecache" "info"
175 "total" "genres" "artists" "albums" "songs" "years"
176 "musicfolder" "playlists" "tracks" "new" "rename" "delete"
177 "edit" "songinfo" "titles" "search" "pragma"
179 ;; Playlist commands and queries
180 "play" "stop" "pause" "mode" "time" "genre" "artist" "album"
181 "title" "duration" "remote" "current_title" "path" "playlist"
182 "add" "insert" "deleteitem" "move" "delete" "preview" "resume"
183 "save" "loadalbum" "addalbum" "loadtracks" "addtracks"
184 "insertalbum" "deletealbum" "clear" "zap" "name" "url"
185 "modified" "playlistsinfo" "index" "shuffle" "repeat"
189 "serverstatus" "status" "displaystatus" "readdirectory"
193 ;; Alarm commands and queries
196 ;; Plugins commands and queries
202 (let ((buffer (make-comint-in-buffer "squeeze" nil
203 (cons squeeze-server-address
204 squeeze-server-port))))
205 (switch-to-buffer buffer)
208 (defun squeeze-control ()
211 (let ((buffer (get-buffer-create "*squeeze-control*")))
212 (switch-to-buffer buffer)
213 (squeeze-control-listen)
214 (squeeze-control-refresh)
215 (squeeze-control-display-players)))