-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMIONITOR.scpt
308 lines (241 loc) · 10.5 KB
/
MIONITOR.scpt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
property StatusItem : missing value
property selectedMenu : "" -- each menu action will set this to a number, this will determin which IP is shown
property theDisplay : ""
property defaults : class "NSUserDefaults"
property internalMenuItem : class "NSMenuItem"
property externalMenuItem : class "NSMenuItem"
property newMenu : class "NSMenu"
-- MIONITOR (MIO MONITOR)
-- Copyright © David Tilley
-- Quick and somewhat dirty tool to gracefully disconnect RTP-Midi clients (specifically iConnectivity MIO's)
-- so that (what I assume is) a bug in Ventura and Sonoma doesn't cause the session to restart
-- and then require what ever midi host applications don't require a restart
-- Note: Devices can be referred to by their Bonjour name by using "DeviceName._udp.local" for the IP address.
-- Credits:
-- uses Late Night Software's PrefsStorageLib
-- Menu bar code based on https://apple.stackexchange.com/a/293392
-- Greatly inspired by and a a very distant decendant of https://forums.iconnectivity.com/index.php?p=/discussion/2495/auto-connect-network-midi-session-on-a-mac
-- (Feat Spiritviews Activate and Patch Midi Network Session)
--This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
-- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-- See <https://www.gnu.org/licenses/> for license details.
set bonjourName to missing value -- Replace with the actual Bonjour name
-- Number of ping attempts before considering it a failure
set maxAttempts to 5
-- Delay between successful ping attempts (in seconds)
set successInterval to 3
-- Delay between ping attempts after a failure (in seconds)
set failureInterval to 1
set sleepInterval to 10
-- state of the system
set status to -1
-- check we are running in foreground - YOU MUST RUN AS APPLICATION. to be thread safe and not crash
if not (current application's NSThread's isMainThread()) as boolean then
display alert "This script must be run from the main thread." buttons {"Cancel"} as critical
error number -128
end if
on menuNeedsUpdate:(menu)
(* NSMenu's delegates method, when the menu is clicked this is called.
We use it here to call the method makeMenus(). Which removes the old menuItems and builds new ones.
This means the menu items can be changed dynamically.
*)
my makeMenus()
end menuNeedsUpdate:
on makeMenus()
newMenu's removeAllItems() -- remove existing menu items
-- display stored ip (or not) menu item
set storedIP to readPlist("MIOIPAddressProperty") -- read plist
if storedIP is missing value then set storedIP to "localhost" -- if plist not found
set currentIP to (storedIP)
if (currentIP as string) does not contain "localhost" then
set displayIP to (current application's NSMenuItem's alloc()'s initWithTitle:("MIO Address: " & (currentIP as string)) action:"xIPClick:" keyEquivalent:"")
-- display dialog (currentIP as text)
(newMenu's addItem:displayIP)
(displayIP's setTarget:me)
else
set displayIP to (current application's NSMenuItem's alloc()'s initWithTitle:("MIO Address not set ") action:"xIPClick:" keyEquivalent:"")
-- display dialog (currentIP as text)
(newMenu's addItem:displayIP)
(displayIP's setTarget:me)
end if
-- set ip menu item
set SetIP to (current application's NSMenuItem's alloc()'s initWithTitle:"Set IP" action:"xSetIPAction:" keyEquivalent:"")
(newMenu's addItem:SetIP)
(SetIP's setTarget:me)
-- quit menu item
set xQuit to (current application's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"xquitAction:" keyEquivalent:"")
(newMenu's addItem:xQuit)
(xQuit's setTarget:me)
end makeMenus
-- set ip action \ responder to menu item
on xSetIPAction:sender
--MenuItem --do some thing
display dialog "Enter IP or Bonjour name of MIO" default answer "" with icon note buttons {"Cancel", "Set Address"} default button "Set Address"
set newIP to text returned of result
-- display dialog "Hellox, " & (newIP as string) & "."
writePlist("MIOIPAddressProperty", (newIP as string))
menuNeedsUpdate
end xSetIPAction:
-- quit action \ responder to menu item
on xquitAction:sender
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:StatusItem
tell current application to quit
end xquitAction:
-- no action menu item
on xIPClick:sender
--
end xIPClick:
-- create an NSStatusBar
on makeStatusBar()
set bar to current application's NSStatusBar's systemStatusBar
set StatusItem to bar's statusItemWithLength:-1.0
-- set up the initial NSStatusBars title
StatusItem's setTitle:"🎹"
-- set up the initial NSMenu of the statusbar
set newMenu to current application's NSMenu's alloc()'s initWithTitle:"Custom"
newMenu's setDelegate:me (*
Requied delegation for when the Status bar Menu is clicked the menu will use the delegates method (menuNeedsUpdate:(menu)) to run dynamically update.
*)
StatusItem's setMenu:newMenu
end makeStatusBar
-- code for reading and writing defaults
on readPlist(theKey)
set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.gatewaybaptistchurch.MIOIP"
return theDefaults's objectForKey:theKey
end readPlist
on writePlist(theKey, theValue)
set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.gatewaybaptistchurch.MIOIP"
theDefaults's setObject:theValue forKey:theKey
end writePlist
my makeStatusBar()
on showMIDINetworkSetup()
try
activate application "Audio MIDI Setup"
tell application "System Events"
tell process "Audio MIDI Setup"
keystroke return
key code 53 (* escape *)
try
click menu item "Show MIDI Studio" of menu 1 of menu bar item "Window" of menu bar 1
end try
delay 2
try
click menu item "Open MIDI Network Setup…" of menu 1 of menu bar item "MIDI Studio" of menu bar 1
end try
end tell
end tell
on error
return false
end try
end showMIDINetworkSetup
on selectMidiSession(sessionNum)
--activate application "Audio MIDI Setup"
tell application "System Events"
tell process "Audio MIDI Setup"
try
set numSessions to count UI elements of table 1 of scroll area 1 of group 1 of window "MIDI Network Setup"
--log "Start: Rows to try " & numSessions - 1
repeat with sn from 1 to numSessions - 1
try
-- this is the checkbox that says a session is enabled
set theCheckbox to checkbox 1 of UI element 1 of row sn of table 1 of scroll area 1 of group 1 of window "MIDI Network Setup"
tell theCheckbox
set checkboxStatus to value of theCheckbox as boolean
end tell
set participants to false
-- this is the little participants icon that appears when someone connects to a session
set hasUsers to value of attribute "AXDescription" of image 1 of UI element 1 of row sn of table 1 of scroll area 1 of group 1 of window "MIDI Network Setup"
if hasUsers is not equal to "" then set participants to true
--log "Row " & sn & " is " & checkboxStatus & " participants " & participants
-- if the session is enabled and there are participants, we have stuff to do!
if checkboxStatus is equal to true and participants is true then
--log " proceed with disconnecting session"
-- select the x session
select row sn of table 1 of scroll area 1 of group 1 of window "MIDI Network Setup"
-- participants removal block
delay 0.5
try
try
set numParticipants to count UI elements of table 1 of scroll area 1 of group 2 of window "MIDI Network Setup"
--log " Participants to disconnect " & numParticipants - 3
repeat with px from 1 to numParticipants - 3
try
select row px of table 1 of scroll area 1 of group 2 of window "Midi Network Setup"
click button "Disconnect" of group 2 of window "MIDI Network Setup"
end try
end repeat
end try
end try
end if
end try
delay 0.5
end repeat
tell application "Audio MIDI Setup" to if it is running then quit
end try
end tell
end tell
end selectMidiSession
-- Loop to perform ping attempts
repeat
set bonjourName to readPlist("MIOIPAddressProperty") -- read plist
if bonjourName is not missing value then
-- display dialog "Pinging for " & bonjourName
-- Perform the ping using the 'ping' command in the shell
set ping to do shell script ("ping -c 1 " & bonjourName & "| head -2 | tail -1 |cut -d = -f 4")
-- Check the result of the ping
if ping contains "ms" then
-- Ping successful, log the success
--log "Ping successful for " & bonjourName
if status = 2 then
display notification "MIO connection restablished"
end if
if status = -1 then
tell application "Finder" to activate
display notification "MIO connection established"
end if
set status to 0
--log status
-- Wait for the specified interval before the next attempt
delay successInterval
else
-- Ping failed, perform additional attempts with shorter intervals
if status is not equal to 2 then
tell application "Finder" to activate
display notification "MIO connection is down"
repeat with i from 1 to maxAttempts - 1
delay failureInterval
set status to 1
--log status
-- set ping to do shell script ("ping -c 1 " & bonjourName & "| head -2 | tail -1 |cut -d = -f 4")
--log "fail " & i
if ping contains "ms" then
tell application "Finder" to activate
display notification "MIO connection restored"
exit repeat
end if
end repeat
end if
-- If all ping attempts fail, log the failure and disconnect the participant in the Network MIDI window
if i = maxAttempts - 1 and status = 1 then
--log "All ping attempts failed for " & bonjourName & " performing pre-emptive disconnection"
tell application "Finder" to activate
display notification "Attempting to gracefully disconnect"
showMIDINetworkSetup()
delay 2
selectMidiSession(2)
delay 2
set status to 2
tell application "Finder" to activate
display notification "MIO connections gracefully disconnected"
end if
if status = 2 then
--log "sleep mode"
delay sleepInterval
end if
end if
end if
end repeat