Skip to content

Commit 42880db

Browse files
Migrate all services to /data to persist them and their settings better
Move all services from `--user pi` to root to allow for reasonable ownership management between multiple OS instances Add postflash service that validates that apt packages are properly installed Fix small bug with LMS album art
1 parent 4e15c31 commit 42880db

9 files changed

Lines changed: 203 additions & 107 deletions

File tree

amplipi/display/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def get_status(url: str, no_serial_ok: bool = False, emoji: bool = True, max_len
248248
result_status = st
249249

250250
# Check if API is running
251-
api_on = subprocess.run("systemctl --user is-active amplipi.service".split(), stdout=subprocess.DEVNULL)
251+
api_on = subprocess.run("systemctl is-active amplipi.service".split(), stdout=subprocess.DEVNULL)
252252
if api_on.returncode != 0:
253253
return DisplayError.NO_AMPLIPI_SERVICE, None, 0
254254

amplipi/rt.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ def _recover_preamps(self) -> bool:
283283
try:
284284
self.reset_preamps()
285285
self.set_i2c_addr()
286+
try:
287+
self.bus.close()
288+
except Exception:
289+
pass
286290
self.bus = SMBus(1)
287291
for addr, regs in list(self.preamps.items()):
288292
for reg, val in enumerate(regs):
@@ -319,6 +323,10 @@ def write_byte_data(self, preamp_addr, reg, data):
319323
# Fallback 1: reopen the bus handle and retry (transient bus glitch).
320324
try:
321325
time.sleep(0.001)
326+
try:
327+
self.bus.close()
328+
except Exception:
329+
pass
322330
self.bus = SMBus(1)
323331
self.bus.write_byte_data(preamp_addr, reg, data)
324332
except Exception:

amplipi/updater/asgi.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,14 @@ async def start_upload(file: UploadFile = File(...)):
294294

295295
def download(url, file_name):
296296
""" Download a binary file from @url to @file_name """
297+
# (connect timeout, read timeout) - read timeout is the max gap between
298+
# received chunks, not a cap on total download time
299+
response = requests.get(url, stream=True, timeout=(10, 30))
300+
response.raise_for_status()
297301
with open(file_name, "wb") as file:
298-
# get request
299-
response = requests.get(url)
300-
# write to file
301-
file.write(response.content)
302+
for chunk in response.iter_content(chunk_size=4 * 1024 * 1024):
303+
if chunk:
304+
file.write(chunk)
302305
# TODO: verify file has amplipi version
303306

304307

@@ -668,6 +671,7 @@ async def stream():
668671
# The section below used to be more pythonic by using with open(...) as f:, reading, and writing to the file
669672
# All of those operations require sudo privs due to touching a boot partition that doesn't belong to the root that's doing it
670673

674+
if manifest.boot is not None:
671675
yield {'data': json.dumps({'type': 'info', 'message': 'Patching cmdline.txt'})}
672676
content = subprocess.run(['sudo', 'cat', '/data/tmpmnt/cmdline.txt'], capture_output=True, text=True, check=True).stdout
673677
content = re.sub(rf'(root=PARTUUID=[0-9a-f]+-0){active_slot.value.root}\b', rf'\g<1>{target_slot.value.root}', content)

scripts/amplipi-postflash.service

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[Unit]
2+
Description=AmpliPi post-flash setup (package install and custom scripts)
3+
After=network-online.target
4+
Before=amplipi.service amplipi-tasks.service
5+
Wants=network-online.target
6+
7+
[Service]
8+
Type=oneshot
9+
ExecStart=/usr/local/bin/amplipi-postflash.sh
10+
RemainAfterExit=yes
11+
12+
[Install]
13+
WantedBy=multi-user.target

scripts/amplipi-postflash.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
PENDING_FILE="/boot/firmware/update-pending"
5+
PACKAGES_FILE="/data/packages.apt"
6+
SCRIPTS_DIR="/data/update_scripts"
7+
UPDATE_LOG="/data/update-log.txt"
8+
9+
log() {
10+
local msg="[$(date -Iseconds)] amplipi-postflash: $*"
11+
echo "${msg}"
12+
echo "${msg}" >> "${UPDATE_LOG}" 2>/dev/null || true
13+
}
14+
15+
if [ ! -f "${PENDING_FILE}" ]; then
16+
exit 0
17+
fi
18+
19+
log "Trial boot detected — running post-flash setup"
20+
21+
# Refresh apt package index (cleared during image prep to reduce image size)
22+
log "Refreshing apt package index..."
23+
apt-get update -qq || log "Warning: apt-get update failed — package installs may not work"
24+
25+
# Install user-customized packages that need to survive OTA updates.
26+
# Add package names (one per line) to /data/packages.apt on p7.
27+
if [ -f "${PACKAGES_FILE}" ]; then
28+
log "Installing packages from ${PACKAGES_FILE}..."
29+
DEBIAN_FRONTEND=noninteractive xargs apt-get install -y < "${PACKAGES_FILE}" \
30+
|| log "Warning: some packages from ${PACKAGES_FILE} failed to install"
31+
fi
32+
33+
# Run device-specific customization scripts from p7.
34+
# These survive OTA updates and are re-applied on each new slot's first boot.
35+
if [ -d "${SCRIPTS_DIR}" ]; then
36+
for script in "${SCRIPTS_DIR}"/*.sh; do
37+
[ -f "${script}" ] || continue
38+
log "Running ${script}..."
39+
bash "${script}" || log "Warning: ${script} exited non-zero"
40+
done
41+
fi
42+
43+
log "Post-flash setup complete"

scripts/amplipi-tryboot-verify.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ log "Running health checks"
7070

7171
failed_checks=()
7272

73-
# TODO: switch to system services once services are moved off p7
74-
retry 12 5 systemctl --user -M pi@ is-active amplipi || failed_checks+=("amplipi service")
75-
retry 12 5 systemctl --user -M pi@ is-active amplipi-tasks || failed_checks+=("amplipi-tasks service")
73+
retry 12 5 systemctl is-active amplipi || failed_checks+=("amplipi service")
74+
retry 12 5 systemctl is-active amplipi-tasks || failed_checks+=("amplipi-tasks service")
7675
retry 6 5 systemctl is-active redis-server || failed_checks+=("redis-server service")
7776
retry 12 5 curl -sf --max-time 5 http://localhost/api || failed_checks+=("API health check")
7877

scripts/cleanup

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ if ! ssh -o PasswordAuthentication=no $user_host 'echo "AmpliPi has your SSH Key
6363
fi
6464

6565
echo -e "reverting amplipi back to stock configuration"
66-
ssh $user_host "systemctl --user stop amplipi"
66+
ssh $user_host "sudo systemctl stop amplipi"
6767
ssh $user_host "find /data/.config/amplipi -maxdepth 1 -type f -delete" # intentionally avoids deleting folders used as mountpoints
68-
ssh $user_host "systemctl --user start amplipi"
68+
ssh $user_host "sudo systemctl start amplipi"
6969

7070
echo -e "removing deployments"
7171
ssh $user_host "rm -rf amplipi-*.tar.gz amplipi-dev/amplipi-*"

0 commit comments

Comments
 (0)