Steam Frame storage setup from a phone or tablet
Companion to https://immernews.com/steam-frame-microsd-usb-storage

Status: a proposed workflow based on Valve's documented SSH access and
mobile SSH clients. The complete phone/tablet workflow has not been tested
on Steam Frame yet. The same Linux mount/helper commands were tested from
a computer. SFTP login also worked from a computer using the existing SSH key. A previous USB connection should not be necessary for this route.

1. On Steam Frame itself, enable Steam Settings > System > Enable Developer
   Mode. You need the Linux steamos account password. Under Developer,
   Set User Password lets you choose one if none exists. Change User Password
   requires the current password.
   This is not your Steam account password. The phone-only route assumes
   you already have or can set a working password on the headset.

2. Open the connected Wi-Fi network in Quick Settings, or under Steam
   Settings > Internet, and read the headset's IPv4 address. The phone/tablet
   and headset must be able to reach one another on the local network.
   Guest Wi-Fi with client isolation can prevent this.

3. Use a mobile client with an SSH terminal and SFTP/SCP file upload.
   Termux on Android offers an OpenSSH package to install; Blink Shell is an
   iOS/iPadOS example.
   These are examples, not a claim that their Steam Frame workflow was tested.
   App installation and permission prompts depend on the chosen client.

4. Connect to HOST = the headset's IP address, PORT = 22, USER = steamos.
   Choose password login and enter the password from step 1. If using a
   command-line client, the command is:

   ssh steamos@HEADSET_IP

   Replace HEADSET_IP with the actual address. The username is included in
   that command. A first-connection host-key prompt is separate from the
   password prompt. Password entry may display no characters. An already
   authorised key is an alternative; it is not a prerequisite.

5. Download and extract the helper ZIP on the mobile device:
   https://immervr.de/files/Steam_Frame_microSD_USB_helper.zip
   Using the
   client's SFTP upload/file picker, put map_lepton_storage.py here:

   /home/steamos/Documents/map_lepton_storage.py

   If the client provides SCP and the local terminal is in the extracted
   helper folder, this is the equivalent upload command:

   scp map_lepton_storage.py steamos@HEADSET_IP:/home/steamos/Documents/map_lepton_storage.py

6. Launch the standalone version of immerGallery on Steam Frame and keep it
   running. It needs its own active Lepton container when the helper runs.

7. Inside the SSH terminal, identify and mount the card read-only:

   lsblk -p -o NAME,SIZE,FSTYPE,MOUNTPOINTS /dev/mmcblk0
   findmnt -rn -S /dev/mmcblk0p1 -o TARGET,OPTIONS

   If already mounted, the first column is CARD_PATH. ro means read-only;
   rw means read/write. Keep an ro mount and use the default helper below.
   To keep an rw mount and allow writing/deleting, add --read-write to the
   helper instead. Skip the next mount command if keeping an existing mount.
   Only if the card is not mounted:

   udisksctl mount --block-device /dev/mmcblk0p1 --options ro --no-user-interaction

   After mounting, run findmnt again to confirm the folder and mode:
   findmnt -rn -S /dev/mmcblk0p1 -o TARGET,OPTIONS

   This assumes the tested single-partition card layout. Check the size and
   filesystem first. Replace CARD_PATH below with the full mount path printed
   by udisksctl, without trailing status-message punctuation:

   python3 /home/steamos/Documents/map_lepton_storage.py --container lepton-steamlaunch-3073280 --source 'CARD_PATH' --name microSD

   For a USB stick, identify its partition first and use its actual values:

   lsblk -p -o NAME,SIZE,TRAN,FSTYPE,MOUNTPOINTS
   udisksctl mount --block-device USB_PARTITION --options ro --no-user-interaction
   findmnt -rn -S USB_PARTITION -o TARGET,OPTIONS
   python3 /home/steamos/Documents/map_lepton_storage.py --container lepton-steamlaunch-3073280 --source 'USB_PATH' --name USBStick

   With -p, lsblk prints full device paths. The tree shows relationships,
   not folders: /dev/sdf1 is the first partition on /dev/sdf, not sdf/sdf1.
   Use your partition's actual path for USB_PARTITION. After mounting,
   findmnt's first column is USB_PATH, normally /run/media/steamos/USB_ID.
   Neither example is universal. The helper uses that folder, not /dev/...
   No adb prefix is used inside the Linux SSH session.

   If the USB drive was already mounted, inspect its mount path and options:
   findmnt -rn -S PARTITION_PATH -o TARGET,OPTIONS
   Keep ro for the default helper, or keep rw and add --read-write if you
   want write access. AlreadyMounted does not change the current access mode.
   To switch either drive from rw to ro, close files, remove any existing app
   mapping with its original access option plus --undo, then unmount using
   udisksctl unmount --block-device PARTITION_PATH --no-user-interaction
   and mount read-only. Stop if busy. Do not format a drive for this procedure.

8. Stop any playback in immerGallery and press the top-bar refresh icon.
   Then open microSD or USBStick. Repeat the mount/helper steps after a headset reboot or Lepton
   container restart. The uploaded helper remains available.

Optional write access:
Use --options rw in the Linux mount command and add --read-write to the
Python helper command. Both must agree. This permits changes to the actual
drive, including deletion, subject to filesystem permissions.
If already mapped read-only, close media, run the original helper with
--undo, then use udisksctl unmount --block-device PARTITION_PATH
--no-user-interaction. Recreate both mounts with the writable options.
If Linux has already mounted the drive rw and there is no app mapping yet,
check the path with findmnt and run only the helper with --read-write.
To remove a writable mapping, keep --read-write and add --undo, then unmount
the Linux source. See Commands_Quick_Reference.txt for complete examples.
File operations passed in the app container on exFAT microSD and FAT32 USB.
The app Delete button also passed on both drives in the computer-based test.

The drive is attached to Steam Frame. The phone/tablet only sends setup
commands and uploads the small script. Each separately Steam-launched
Android app has its own container, so the mount targets immerGallery.

ADB on a phone is a separate option. Android ADB clients exist, but the
tested Wi-Fi ADB endpoint on Steam Frame reaches Lepton, not native Linux.
The mounting helper needs Linux. A phone-to-headset USB cable would depend
on the phone's USB-host support, permissions and ADB client, and is untested.

References:
https://partner.steamgames.com/doc/steamhardware/steamframe/setup
https://partner.steamgames.com/doc/steamhardware/steamframe/debugging
https://partner.steamgames.com/doc/steamhardware/steamframe/adb_lepton
https://termux.dev/en
https://docs.blink.sh/basics/commands
