System Events

Automating System Events

“System Events” is a bit unfortunately named since it is not really about events (or at least not on the surface). Also, it is not an application. If you inspect its function library, you’ll see a lot of preferences related things, but also some methods providing information about the current environment.

Getting the current user’s name(s)#

(() => {
  const app = Application("System Events");
  const user = app.currentUser();
  const longName =user.fullName();
  const shortName = user.name();
  const userHome = user.homeDirectory();
})

Handling property lists#

System Events seem to support reading and parsing of property lists (plist files). While that may be the case, it is a tedious exercise. It’s a lot easier to handle property lists with the Objective-C and the Foundation framework, as described elsewhere.

Connecting to a VPN#

The following example gets all IPSec-VPNs configured on the current computer, displays their names in a list and asks the user for one of them to connect to. If the selected VPN is currently not connected, the script connects it.

(() => {
  const app = Application("System Events");
  const curApp = Application.currentApplication();
  curApp.includeStandardAdditions = true;
  /* 
   * Get all configured IPSec-VPNs 
   * for the current network location
   */
  const vpns = 
      app.networkPreferences.currentLocation.services.whose(
        {_match: 
          [ObjectSpecifier().interface.name, "IPSec"]
        }).name();
  /*
   * Display dialog with VPN names for user to choose from
   */
  const selectedVPN = curApp.chooseFromList(vpns, {
       withPrompt: "Select VPN to connect to:",
       defaultItems: [`${vpns[0]}`] })
  /* 
   * If the user aborted, do nothing.
   */
  if (!selectedVPN) return;
  /*
   * Get the current configuration of the selected VPN to
   * check if it is already connected
   */
  const service = app.networkPreferences.services(selectedVPN)[0];
  const curConfig = service.currentConfiguration;
  /* Connect to the VPN */
  
    /* connect() does not work with IKE2, Apple broke it */
//    app.connect(service); 
  curApp.doShellScript(`scutil --nc start "${selectedVPN[0]}"`)
})()

As you see, this is not a pure JavaScript solution. In fact, Apple broke the connect() method for IPSec connections that use IKE2. In doing so, they were really thorough: it works neither for AppleScript nor for JavaScript. The single line call to doShellScript() works flawlessly (at least up until Big Sur).

Note the usage of whose() at the beginning of the script to find all IPSec services. It is a lot simpler than looping over all services and uses the ObjectSpecifier() trick to get at a property of a service element, namely the name of the interface.