# Focus (/docs/haus/rooms/focus)



**Focus** is the quiet switch. The moon pill, **Toggle Focus** in the palette
and `focus` in a terminal all turn macOS **Do Not Disturb** on or off, set your
Slack status if you ask, and run your scripts on both edges.

```nix
haus.focus.enable = true;
```

It flips the real Do Not Disturb, never a named Focus mode, so your iPhone goes
quiet too with **Share Across Devices** on, and your own allow list still applies.

## The grants [#the-grants]

haus presses macOS's own Do Not Disturb hotkey (slot 175, bound to ⌃⌥⇧⌘ F13
on every rebuild), which needs **Accessibility**; reading the
state back needs **Full Disk Access**. Grant both to Pounce.app, the launcher's
palette, once; every surface rides them. `focus doctor` checks each and
prints the fix.

Without a launcher the invoking app needs them itself (sketchybar's is
asked again after a rebuild moves it), and without Full Disk Access `focus
status` is its own memory and drifts when you toggle from Control Center or
your phone.

## Quiet for a while [#quiet-for-a-while]

`focus 25` turns Do Not Disturb on and turns it back off in twenty-five minutes.
The moon pill counts down in minutes while it runs, so you can see how long is
left without asking.

```sh
focus 25        # a bare number is minutes
focus 90min     # so are 90m and 90min
focus 1h        # an hour, up to a day
focus timer     # what is left
focus timer off # drop the countdown, stay quiet
```

The palette carries a row per duration. Two by default, and you pick them:

```nix
haus.focus.timers = [ 25 60 ];   # Focus 25m, Focus 60m in ⌘Space
```

A timer ends only the quiet it started. Turn quiet off and on again by hand
while one runs and it forgets itself rather than ending the quiet you just
chose. Entering a scene does the same, because a scene owns the whole state
while it is on. It checks about once a minute, Control Center and your phone
included, so quiet switched off and straight back on inside one of those gaps is
the sequence it can miss.

What gets written down is the time it ends, not the process waiting for it, so a
reboot or a rebuild in the middle loses nothing. A timer that ran out while the
Mac was shut ends the quiet on the way back up.

## The Slack leg [#the-slack-leg]

This leg tells your **teammates** and silences your **phone**, which Do Not
Disturb can't, then puts your status back. It needs a personal token, so it's
off by default:

1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps),
   "From scratch".
2. Under **OAuth & Permissions → User Token Scopes** add `users.profile:write`
   and `dnd:write`, install it, and copy the **User OAuth Token** (`xoxp-…`).
3. Turn the leg on, rebuild, then `haus-secret --check` asks for the token
   once and keeps it wherever [`haus.secrets.provider`](/docs/haus/rooms/security)
   points, never in your config or the store.

```nix
haus.focus.slack = {
  enable = true;
  statusText = "heads down";
  statusEmoji = ":no_bell:";
  snooze = true;          # Slack's own DND, every device
};
```

`focus doctor` verifies it; `tokenCommand`, host-only, fetches it another way.

## Hooks [#hooks]

Every switch runs your scripts with `on` or `off`; a failing one is logged,
never fatal.

```nix
haus.focus.hooks = [
  ./onair-light.sh              # copied into the store
  "/Users/ada/bin/pause-music"  # strings run as-is
];
```

## Scenes [#scenes]

A scene is the handful of settings you'd otherwise flip by hand, in the same
order, every time:

```nix
haus.focus.scenes.recording = {
  description = "camera on, nothing interrupts";
  dnd = true;
  preventSleep = true;              # a caffeinate hold until you leave
  audio.input = "Studio Mic";       # as SwitchAudioSource -a -t input prints it
  apps.open = [ "OBS" ];
  apps.closeOnExit = true;          # only what the scene started
  hooks = [ ./key-light-on.sh ];
};
```

```sh
focus scene recording
focus scene toggle recording  # enter it, or leave it if it's on
focus scene list
focus scene status        # the scene, "quiet", or "off"
focus scene off
```

One at a time: entering a scene leaves the last one, and leaving reverses only
what the scene changed, so one entered while already quiet leaves you quiet
(`restorePreviousState = false` if you'd rather not). The pill and `focus off`
release a scene too, hold and microphone included.

`quiet` is the built-in scene, shaped by `slack` and `hooks`; it, `off`, `list`,
`status` and `toggle` are refused as names. With [the
launcher](/docs/haus/rooms/launcher) on, every scene is a **Scene: recording**
row in ⌘Space with **Leave Scene** beside it.

### Give a scene a key [#give-a-scene-a-key]

```nix
haus.focus.scenes.recording.key = "r";   # the leader, then r
```

The binding, and with [the launcher](/docs/haus/rooms/launcher) on its line on
the cheatsheet, are both made from that one field. The key toggles: press it
again to leave.

The key shares [launch mode](/docs/haus/rooms/keys) with your app roster, the
numbered workspaces, the fixed launch-mode actions (`z`, `,`, `.`, `` ` ``,
`-`, `=`, the arrows), the launcher's `/`, `v` and `f`, and
`haus.keys.leaderExtras`. A key one of those already owns is refused when you
rebuild, rather than quietly shadowing it. It needs the [Windows](/docs/haus/rooms/windows) room; without it the scene
loses the key and nothing else.

### Enter a scene automatically [#enter-a-scene-automatically]

Give a scene a `when` and it enters itself:

```nix
haus.focus.scenes.evening = {
  when = {
    time = "19:00-23:00";   # an end before the start wraps midnight
    days = [ "mon" "tue" "wed" "thu" "fri" ];
  };
};
```

Also `wifi = [ "Home" ]` (the exact SSID), `power = "battery"` or `"ac"`, and
`displays = 2` (at least two), which is how you say *docked*. All must hold at
once, checked every `haus.focus.triggers.interval` seconds (30).

**It never overrides a state you chose.** It enters on the edge where a
condition turns true, only from a Mac with no scene on and not quiet, and
leaves only what it entered. Leave the evening scene at 19:10 and you're out
for the night; already quiet at 19:00 and that evening is missed.

<Callout type="warn" title="Probe it first">
  `focus auto --probe` prints what each probe reads and what every condition
  makes of it. macOS can refuse to report your SSID (it sits behind Location
  Services), and a network it won't name matches nothing.
</Callout>

`hooks` and `when.wifi` are host-only: a script is a person's, an SSID names
one building. The rest a desktop can ship.

## Options [#options]

[Every setting, with types and defaults](/docs/haus/reference/options#focus).
