plc-checkweigher 1.21.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1126,6 +1126,330 @@ SMBC
1126
1126
  echo ""
1127
1127
  ;;
1128
1128
 
1129
+ # ─────────────────────────────────────────────────────────────────────────────
1130
+ # UPDATE — pull latest code from GitHub, re-lock files, restart services
1131
+ # ─────────────────────────────────────────────────────────────────────────────
1132
+ update)
1133
+ banner "System Update"
1134
+ echo ""
1135
+ need_sudo
1136
+
1137
+ # ── Update report log (pushed to SMB at the end) ──────────────────────────
1138
+ ULOG_DIR="/home/pi/reports/logs"
1139
+ mkdir -p "$ULOG_DIR" 2>/dev/null || true
1140
+ ULOG="${ULOG_DIR}/update_$(date '+%Y%m%d_%H%M%S').log"
1141
+ ulog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$ULOG"; }
1142
+ ulog "=== Update started ==="
1143
+
1144
+ # ── Warn if a batch is actively running ───────────────────────────────────
1145
+ _IS_RUNNING=0
1146
+ if [[ -f "/tmp/plc_live.json" ]]; then
1147
+ _RUN_VAL=$("${PYTHON}" -c \
1148
+ "import json; d=json.load(open('/tmp/plc_live.json')); print(d.get('running',False))" \
1149
+ 2>/dev/null || echo "False")
1150
+ [[ "$_RUN_VAL" == "True" ]] && _IS_RUNNING=1
1151
+ fi
1152
+
1153
+ if [[ $_IS_RUNNING -eq 1 ]]; then
1154
+ warn "A batch is currently RUNNING on the PLC."
1155
+ warn "The update will stop the current batch — any unsaved data will be lost."
1156
+ echo ""
1157
+ read -r -p " Continue anyway? [y/N]: " _UPD_CONT </dev/tty
1158
+ _UPD_CONT="${_UPD_CONT:-N}"
1159
+ [[ "${_UPD_CONT^^}" == "Y" ]] || { echo " Update cancelled."; exit 0; }
1160
+ echo ""
1161
+ fi
1162
+
1163
+ # ── Network reachability ──────────────────────────────────────────────────
1164
+ spin_start "Checking connectivity"
1165
+ if ! ping -c 1 -W 3 8.8.8.8 &>/dev/null && ! ping -c 1 -W 3 github.com &>/dev/null; then
1166
+ spin_err "No internet connection — cannot pull updates"
1167
+ exit 1
1168
+ fi
1169
+ spin_ok "Network reachable"
1170
+
1171
+ # ── Snapshot current commit ───────────────────────────────────────────────
1172
+ _GIT="sudo git -c safe.directory=${INSTALL_DIR} -C ${INSTALL_DIR}"
1173
+ _PREV_HASH=$(${_GIT} rev-parse HEAD 2>/dev/null || echo "unknown")
1174
+ _PREV_SHORT="${_PREV_HASH:0:7}"
1175
+
1176
+ # ── Pull ──────────────────────────────────────────────────────────────────
1177
+ spin_start "Pulling latest code from GitHub"
1178
+ _PULL_OUT=$(${_GIT} pull origin main 2>&1 || echo "GIT_FAILED")
1179
+
1180
+ if echo "$_PULL_OUT" | grep -qE "^GIT_FAILED|^error:|^fatal:"; then
1181
+ spin_err "git pull failed"
1182
+ echo "$_PULL_OUT" | head -6 | sed 's/^/ /'
1183
+ exit 1
1184
+ fi
1185
+
1186
+ _NEW_HASH=$(${_GIT} rev-parse HEAD 2>/dev/null || echo "unknown")
1187
+ _NEW_SHORT="${_NEW_HASH:0:7}"
1188
+
1189
+ if [[ "$_PREV_HASH" == "$_NEW_HASH" ]]; then
1190
+ spin_ok "Already up to date (${_PREV_SHORT})"
1191
+ echo ""
1192
+ info "No changes pulled — services not restarted."
1193
+ echo ""
1194
+ exit 0
1195
+ fi
1196
+ spin_ok "Pulled ${_PREV_SHORT} → ${_NEW_SHORT}"
1197
+ ulog "GIT: pulled ${_PREV_SHORT} -> ${_NEW_SHORT}"
1198
+
1199
+ # ── Show what changed ─────────────────────────────────────────────────────
1200
+ echo ""
1201
+ info "Changes:"
1202
+ ${_GIT} log --oneline "${_PREV_HASH}..HEAD" 2>/dev/null \
1203
+ | sed 's/^/ /' | head -15
1204
+ echo ""
1205
+
1206
+ # ── Re-lock source files (root:root 644) ──────────────────────────────────
1207
+ spin_start "Re-locking source file permissions"
1208
+ sudo find "${INSTALL_DIR}" -maxdepth 1 -name "*.py" \
1209
+ -exec chown root:root {} \; -exec chmod 644 {} \; 2>/dev/null || true
1210
+ sudo find "${INSTALL_DIR}/web" -name "*.py" \
1211
+ -exec chown root:root {} \; -exec chmod 644 {} \; 2>/dev/null || true
1212
+ sudo chown root:root "${INSTALL_DIR}/bin/plc_checkweigher" 2>/dev/null && \
1213
+ sudo chmod 755 "${INSTALL_DIR}/bin/plc_checkweigher" 2>/dev/null || true
1214
+ # Keep data/ writable by pi
1215
+ sudo chown -R pi:pi "${DATA_DIR}" 2>/dev/null || true
1216
+ sudo chmod 755 "${DATA_DIR}" 2>/dev/null || true
1217
+ spin_ok "Source files locked (root:root 644)"
1218
+
1219
+ # ── Install updated CLI to /usr/local/bin/ ────────────────────────────────
1220
+ spin_start "Installing updated CLI (/usr/local/bin/plc_checkweigher)"
1221
+ if sudo cp "${INSTALL_DIR}/bin/plc_checkweigher" /usr/local/bin/plc_checkweigher \
1222
+ && sudo chmod 755 /usr/local/bin/plc_checkweigher; then
1223
+ spin_ok "CLI updated"
1224
+ else
1225
+ spin_warn "CLI install failed — still running previous version until next install"
1226
+ fi
1227
+ # Also sync ~/.local/bin/ if a copy exists there (it takes PATH priority)
1228
+ _LOCAL_CLI="/home/pi/.local/bin/plc_checkweigher"
1229
+ if [[ -f "$_LOCAL_CLI" ]]; then
1230
+ cp "${INSTALL_DIR}/bin/plc_checkweigher" "$_LOCAL_CLI" 2>/dev/null || true
1231
+ fi
1232
+
1233
+ # ── Update systemd unit files if they changed ─────────────────────────────
1234
+ _UNITS_UPDATED=0
1235
+ for _svc_src in "${INSTALL_DIR}/plc_watcher.service" \
1236
+ "${INSTALL_DIR}/web/plc_web.service"; do
1237
+ [[ ! -f "$_svc_src" ]] && continue
1238
+ _svc_dst="/etc/systemd/system/$(basename "$_svc_src")"
1239
+ if [[ ! -f "$_svc_dst" ]] || ! diff -q "$_svc_src" "$_svc_dst" &>/dev/null; then
1240
+ sudo cp "$_svc_src" "$_svc_dst" 2>/dev/null && _UNITS_UPDATED=1
1241
+ fi
1242
+ done
1243
+ if [[ $_UNITS_UPDATED -eq 1 ]]; then
1244
+ spin_start "Reloading systemd (unit files changed)"
1245
+ sudo systemctl daemon-reload 2>/dev/null || true
1246
+ spin_ok "systemd reloaded"
1247
+ fi
1248
+
1249
+ # ── Update Plymouth boot splash ───────────────────────────────────────────
1250
+ _THEME_DIR="/usr/share/plymouth/themes/saismruth"
1251
+ _LOGO_SRC="${INSTALL_DIR}/assets/logo.png"
1252
+ _PLY_CHANGED=0
1253
+
1254
+ if [[ -f "$_LOGO_SRC" ]] && [[ -d "$_THEME_DIR" ]]; then
1255
+ spin_start "Updating boot splash"
1256
+ # Regenerate logo + pre-rendered text (idempotent)
1257
+ "${PYTHON}" - << 'PYEOF' 2>/dev/null && _PLY_CHANGED=1 || true
1258
+ from PIL import Image, ImageDraw, ImageFont
1259
+ import os, hashlib
1260
+
1261
+ THEME = "/usr/share/plymouth/themes/saismruth"
1262
+ LOGO = os.path.join(os.path.dirname(os.path.abspath(
1263
+ "/home/pi/plc_checkweigher/assets/logo.png")), "..", "assets", "logo.png")
1264
+ LOGO = "/home/pi/plc_checkweigher/assets/logo.png"
1265
+ TEXT = "Sai Samarth Engineering"
1266
+
1267
+ changed = False
1268
+
1269
+ # Logo 400x400
1270
+ img = Image.open(LOGO).convert("RGBA").resize((400, 400), Image.LANCZOS)
1271
+ logo_dst = os.path.join(THEME, "logo.png")
1272
+ import io; buf = io.BytesIO(); img.save(buf, "PNG"); new_bytes = buf.getvalue()
1273
+ old_bytes = open(logo_dst, "rb").read() if os.path.exists(logo_dst) else b""
1274
+ if new_bytes != old_bytes:
1275
+ open(logo_dst, "wb").write(new_bytes); changed = True
1276
+
1277
+ # Pre-rendered text
1278
+ FONTS = ["/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
1279
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"]
1280
+ font = next((ImageFont.truetype(f, 30) for f in FONTS if os.path.exists(f)), ImageFont.load_default())
1281
+ bbox = font.getbbox(TEXT); pad = 10
1282
+ tw, th = bbox[2]-bbox[0], bbox[3]-bbox[1]
1283
+ canvas = Image.new("RGBA", (tw+pad*2, th+pad*2), (0,0,0,0))
1284
+ ImageDraw.Draw(canvas).text((pad-bbox[0], pad-bbox[1]), TEXT, fill=(255,255,255,255), font=font)
1285
+ text_dst = os.path.join(THEME, "text.png")
1286
+ buf2 = io.BytesIO(); canvas.save(buf2, "PNG"); new2 = buf2.getvalue()
1287
+ old2 = open(text_dst, "rb").read() if os.path.exists(text_dst) else b""
1288
+ if new2 != old2:
1289
+ open(text_dst, "wb").write(new2); changed = True
1290
+
1291
+ import sys; sys.exit(0 if changed else 99)
1292
+ PYEOF
1293
+ _PLY_PY_EXIT=$?
1294
+ [[ $_PLY_PY_EXIT -eq 99 ]] && _PLY_CHANGED=0 # exit 99 = no change
1295
+
1296
+ # Patch boot cmdline once (idempotent)
1297
+ _CMDLINE="/boot/firmware/cmdline.txt"
1298
+ if [[ -f "$_CMDLINE" ]]; then
1299
+ if ! grep -q "logo.nologo" "$_CMDLINE"; then
1300
+ sudo sed -i 's/$/ logo.nologo/' "$_CMDLINE" 2>/dev/null && _PLY_CHANGED=1
1301
+ fi
1302
+ if grep -q "loglevel=3" "$_CMDLINE"; then
1303
+ sudo sed -i 's/loglevel=3/loglevel=1/' "$_CMDLINE" 2>/dev/null && _PLY_CHANGED=1
1304
+ fi
1305
+ fi
1306
+ if ! grep -q "^disable_splash=1" /boot/firmware/config.txt 2>/dev/null; then
1307
+ echo "disable_splash=1" | sudo tee -a /boot/firmware/config.txt >/dev/null && _PLY_CHANGED=1
1308
+ fi
1309
+
1310
+ if [[ $_PLY_CHANGED -eq 1 ]]; then
1311
+ spin_ok "Boot splash updated — rebuilding initramfs (~30 s)"
1312
+ # Rebuild for the RUNNING kernel (plain -u may pick a different one)
1313
+ _KVER="$(uname -r)"
1314
+ sudo update-initramfs -u -k "${_KVER}" > /tmp/update_initramfs.log 2>&1 \
1315
+ && ok "initramfs rebuilt for ${_KVER}" \
1316
+ || warn "initramfs warnings — see /tmp/update_initramfs.log"
1317
+ # Sync to the firmware partition — this is what actually boots
1318
+ if [[ -f "/boot/initrd.img-${_KVER}" ]] \
1319
+ && grep -q "initramfs initramfs8-rt" /boot/firmware/config.txt 2>/dev/null; then
1320
+ sudo cp "/boot/initrd.img-${_KVER}" /boot/firmware/initramfs8-rt \
1321
+ && ok "initramfs8-rt synced — reboot to apply splash" \
1322
+ || warn "Could not sync initramfs8-rt"
1323
+ fi
1324
+ ulog "SPLASH: regenerated, initramfs rebuilt + synced (${_KVER})"
1325
+ else
1326
+ spin_ok "Boot splash already up to date"
1327
+ ulog "SPLASH: up to date"
1328
+ fi
1329
+ fi
1330
+
1331
+ # ── Verify crucial system configuration ───────────────────────────────────
1332
+ echo ""
1333
+ echo -e " ${B}▸ System Configuration Check${NC}"; hr; echo ""
1334
+
1335
+ # 1. RT kernel active in config.txt
1336
+ spin_start "RT kernel boot config"
1337
+ if grep -q "^kernel=kernel8-rt.img" /boot/firmware/config.txt 2>/dev/null \
1338
+ && grep -q "^initramfs initramfs8-rt" /boot/firmware/config.txt 2>/dev/null; then
1339
+ spin_ok "RT kernel + initramfs configured"
1340
+ ulog "CONFIG: RT kernel boot entries OK"
1341
+ else
1342
+ spin_warn "RT kernel boot entries missing in config.txt!"
1343
+ ulog "WARN: RT kernel boot entries missing"
1344
+ fi
1345
+
1346
+ # 2. RT initramfs freshness — resync if the source initrd is newer
1347
+ spin_start "RT initramfs freshness"
1348
+ _RT_KVER="$(uname -r)"
1349
+ _SRC_INITRD="/boot/initrd.img-${_RT_KVER}"
1350
+ _FW_INITRD="/boot/firmware/initramfs8-rt"
1351
+ if [[ -f "$_SRC_INITRD" && -f "$_FW_INITRD" ]] \
1352
+ && [[ "$_SRC_INITRD" -nt "$_FW_INITRD" ]]; then
1353
+ sudo cp "$_SRC_INITRD" "$_FW_INITRD" \
1354
+ && spin_ok "initramfs8-rt was stale — resynced" \
1355
+ || spin_warn "initramfs8-rt resync failed"
1356
+ ulog "FIXED: stale initramfs8-rt resynced"
1357
+ else
1358
+ spin_ok "initramfs8-rt up to date"
1359
+ ulog "CONFIG: initramfs8-rt fresh"
1360
+ fi
1361
+
1362
+ # 3. Boot cmdline parameters
1363
+ spin_start "Boot cmdline (quiet splash)"
1364
+ _CMD_MISSING=""
1365
+ for _param in "quiet" "splash" "logo.nologo" "systemd.show_status=0"; do
1366
+ grep -q "$_param" /boot/firmware/cmdline.txt 2>/dev/null \
1367
+ || _CMD_MISSING="${_CMD_MISSING} ${_param}"
1368
+ done
1369
+ if [[ -n "$_CMD_MISSING" ]]; then
1370
+ for _param in $_CMD_MISSING; do
1371
+ sudo sed -i "s/\$/ ${_param}/" /boot/firmware/cmdline.txt
1372
+ done
1373
+ spin_ok "Restored missing cmdline params:${_CMD_MISSING}"
1374
+ ulog "FIXED: cmdline params restored:${_CMD_MISSING}"
1375
+ else
1376
+ spin_ok "All boot cmdline params present"
1377
+ ulog "CONFIG: cmdline OK"
1378
+ fi
1379
+
1380
+ # 4. Plymouth theme still set
1381
+ spin_start "Plymouth theme"
1382
+ _CUR_THEME=$(sudo plymouth-set-default-theme 2>/dev/null || echo "")
1383
+ if [[ "$_CUR_THEME" != "saismruth" ]]; then
1384
+ sudo plymouth-set-default-theme saismruth 2>/dev/null \
1385
+ && spin_ok "Theme was '${_CUR_THEME:-none}' — reset to saismruth" \
1386
+ || spin_warn "Could not set theme"
1387
+ ulog "FIXED: plymouth theme reset (was: ${_CUR_THEME:-none})"
1388
+ else
1389
+ spin_ok "saismruth active"
1390
+ ulog "CONFIG: plymouth theme OK"
1391
+ fi
1392
+
1393
+ # 5. Non-essential services stay disabled (upgrades can re-enable them)
1394
+ spin_start "Non-essential services"
1395
+ _RE_DISABLED=""
1396
+ for _svc in bluetooth avahi-daemon cups apt-daily.timer apt-daily-upgrade.timer man-db.timer; do
1397
+ if systemctl is-enabled "$_svc" &>/dev/null; then
1398
+ sudo systemctl disable --now "$_svc" &>/dev/null \
1399
+ && _RE_DISABLED="${_RE_DISABLED} ${_svc}"
1400
+ fi
1401
+ done
1402
+ if [[ -n "$_RE_DISABLED" ]]; then
1403
+ spin_ok "Re-disabled:${_RE_DISABLED}"
1404
+ ulog "FIXED: re-disabled services:${_RE_DISABLED}"
1405
+ else
1406
+ spin_ok "All non-essential services off"
1407
+ ulog "CONFIG: services OK"
1408
+ fi
1409
+
1410
+ # ── Restart services ──────────────────────────────────────────────────────
1411
+ spin_start "Restarting services"
1412
+ sudo systemctl restart plc_watcher plc_web 2>/dev/null
1413
+ sleep 2
1414
+ spin_ok "Services restarted"
1415
+
1416
+ # ── Final status ──────────────────────────────────────────────────────────
1417
+ echo ""
1418
+ _UW=$(systemctl is-active plc_watcher 2>/dev/null || echo "inactive")
1419
+ _UWb=$(systemctl is-active plc_web 2>/dev/null || echo "inactive")
1420
+ [[ "$_UW" == "active" ]] && echo -e " ${G}✓${NC} plc_watcher active" \
1421
+ || echo -e " ${R}✗${NC} plc_watcher ${_UW}"
1422
+ [[ "$_UWb" == "active" ]] && echo -e " ${G}✓${NC} plc_web active" \
1423
+ || echo -e " ${R}✗${NC} plc_web ${_UWb}"
1424
+ echo ""
1425
+ ok "Update complete (${_PREV_SHORT} → ${_NEW_SHORT})"
1426
+ [[ $_PLY_CHANGED -eq 1 ]] && warn "Reboot required for boot splash changes to take effect."
1427
+ ulog "SERVICES: plc_watcher=${_UW} plc_web=${_UWb}"
1428
+ ulog "=== Update complete: ${_PREV_SHORT} -> ${_NEW_SHORT} ==="
1429
+ info "Report: ${ULOG}"
1430
+
1431
+ # ── Push update report to SMB share ───────────────────────────────────────
1432
+ _SMB_HOST=$(smb_get "SMB_HOST")
1433
+ _SMB_SHARE=$(smb_get "SMB_SHARE")
1434
+ _SMB_USER=$(smb_get "SMB_USERNAME")
1435
+ _SMB_PASS=$(smb_get "SMB_PASSWORD")
1436
+ _SMB_EN=$(grep "^SMB_ENABLED" "${SMB_CFG}" 2>/dev/null | grep -qi "true" && echo "1" || echo "0")
1437
+
1438
+ if [[ "$_SMB_EN" == "1" && -n "$_SMB_HOST" && -n "$_SMB_USER" ]] \
1439
+ && command -v smbclient &>/dev/null; then
1440
+ spin_start "Pushing update report to SMB share"
1441
+ _ULOG_BASE="$(basename "$ULOG")"
1442
+ if smbclient "//${_SMB_HOST}/${_SMB_SHARE}" \
1443
+ -U "${_SMB_USER}%${_SMB_PASS}" \
1444
+ -c "mkdir logs; put ${ULOG} logs/${_ULOG_BASE}" &>/dev/null 2>&1; then
1445
+ spin_ok "Report pushed → //${_SMB_HOST}/${_SMB_SHARE}/logs/${_ULOG_BASE}"
1446
+ else
1447
+ spin_warn "SMB push failed — report saved locally at ${ULOG}"
1448
+ fi
1449
+ fi
1450
+ echo ""
1451
+ ;;
1452
+
1129
1453
  # ─────────────────────────────────────────────────────────────────────────────
1130
1454
  # UNINSTALL — two modes: software-only or full drive wipe
1131
1455
  # ─────────────────────────────────────────────────────────────────────────────
@@ -1342,7 +1666,7 @@ uninstall)
1342
1666
  echo " ╔══════════════════════════════════════════════════════════╗"
1343
1667
  echo " ║ Uninstall complete. ║"
1344
1668
  echo " ║ ║"
1345
- echo " ║ A reboot is needed to apply kernel revert. ║"
1669
+ echo " ║ A reboot is needed to apply kernel revert. ║"
1346
1670
  echo " ╚══════════════════════════════════════════════════════════╝"
1347
1671
  echo -e "${NC}"
1348
1672
  [[ "${KEEP_REPORTS^^}" == "Y" ]] && info "PDFs still at: ${REPORTS_DIR} (remove manually if needed)"
@@ -1389,6 +1713,7 @@ help|--help|-h)
1389
1713
  echo -e " ${W}Configuration${NC}"
1390
1714
  echo " smb-config Interactively update SMB delivery target"
1391
1715
  echo " push-test Push latest PDF to SMB target now"
1716
+ echo " update Pull latest code from GitHub and restart services"
1392
1717
  echo ""
1393
1718
  echo -e " ${W}Display${NC}"
1394
1719
  echo " display on Enable display (start LightDM)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plc-checkweigher",
3
- "version": "1.21.0",
3
+ "version": "1.23.0",
4
4
  "description": "One-command installer for the PLC Check-Weigher system on Raspberry Pi (PREEMPT_RT kernel, Python stack, WiFi, SMB, systemd RT services)",
5
5
  "scripts": {
6
6
  "postinstall": "node bin/cli.js"
package/setup.sh CHANGED
@@ -16,7 +16,7 @@
16
16
  # 7. SMB — enter host IP, share name, credentials → smb_config.py
17
17
  # 8. NetworkManager-wait-online
18
18
  # 9. systemd services (plc_watcher + plc_web)
19
- # 10. Boot logo — Plymouth theme with logo.png + "SAI SAMARTH ENGG"
19
+ # 10. Boot logo — Plymouth theme with logo.png + "Sai Samarth Engineering"
20
20
  # 11. Display — LightDM priority, CPU isolation, utmpx
21
21
  # 11b. VS Code — priority daemon: cores 0-2, Nice=-5
22
22
  # 12. PREEMPT_RT kernel ← installed last so only one reboot is needed
@@ -97,8 +97,11 @@ preflight() {
97
97
 
98
98
  # ── 1. System packages ────────────────────────────────────────────────────────
99
99
  install_system_packages() {
100
- step "System packages ..."
100
+ step "System update + packages ..."
101
101
  DEBIAN_FRONTEND=noninteractive apt-get update -qq
102
+ echo " Upgrading all packages to latest (this can take several minutes) ..."
103
+ DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y -qq
104
+ ok "System fully upgraded"
102
105
  DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
103
106
  git python3-venv python3-pip python3-dev \
104
107
  samba-client cifs-utils network-manager curl build-essential
@@ -158,6 +161,8 @@ install_cli() {
158
161
  if [[ -f "${CLI_SRC}" ]]; then
159
162
  chmod +x "${CLI_SRC}"
160
163
  cp "${CLI_SRC}" "${CLI_DEST}"
164
+ # Remove stale ~/.local/bin/ copy — it takes PATH priority and can shadow updates
165
+ rm -f "/home/pi/.local/bin/plc_checkweigher" 2>/dev/null || true
161
166
  ok "plc_checkweigher → ${CLI_DEST}"
162
167
  ok "Run: plc_checkweigher status (full system diagnostic)"
163
168
  else
@@ -407,16 +412,45 @@ setup_boot_logo() {
407
412
  THEME_DIR="/usr/share/plymouth/themes/saismruth"
408
413
  mkdir -p "${THEME_DIR}"
409
414
 
410
- # ── Logo: resize assets/logo.png to 256×256 and copy into theme ──────────
415
+ # ── Logo (400×400) + pre-rendered text PNG ───────────────────────────────
411
416
  LOGO_SRC="${INSTALL_DIR}/assets/logo.png"
412
417
  if [[ -f "${LOGO_SRC}" ]]; then
413
418
  "${VENV_DIR}/bin/python3" - << PYEOF
414
- from PIL import Image
419
+ from PIL import Image, ImageDraw, ImageFont
420
+ import os, sys
421
+
422
+ THEME = "${THEME_DIR}"
423
+ TEXT = "Sai Samarth Engineering"
424
+
425
+ # Logo — 400×400 (more visible on HD display)
415
426
  img = Image.open("${LOGO_SRC}").convert("RGBA")
416
- img.thumbnail((256, 256), Image.LANCZOS)
417
- img.save("${THEME_DIR}/logo.png", "PNG")
427
+ img = img.resize((400, 400), Image.LANCZOS)
428
+ img.save(os.path.join(THEME, "logo.png"), "PNG")
429
+
430
+ # Pre-rendered company name (white on transparent) — avoids font issues in initramfs
431
+ FONT_PATHS = [
432
+ "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
433
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
434
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
435
+ ]
436
+ font = None
437
+ for fp in FONT_PATHS:
438
+ if os.path.exists(fp):
439
+ font = ImageFont.truetype(fp, 30)
440
+ break
441
+ if font is None:
442
+ font = ImageFont.load_default()
443
+
444
+ bbox = font.getbbox(TEXT)
445
+ tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
446
+ pad = 10
447
+ canvas = Image.new("RGBA", (tw + pad * 2, th + pad * 2), (0, 0, 0, 0))
448
+ ImageDraw.Draw(canvas).text((pad - bbox[0], pad - bbox[1]), TEXT,
449
+ fill=(255, 255, 255, 255), font=font)
450
+ canvas.save(os.path.join(THEME, "text.png"), "PNG")
451
+ print(" logo 400x400, text", canvas.size)
418
452
  PYEOF
419
- ok "Logo installed (256×256) → ${THEME_DIR}/logo.png"
453
+ ok "Logo + text images installed → ${THEME_DIR}/"
420
454
  else
421
455
  warn "assets/logo.png not found — splash will show text only"
422
456
  fi
@@ -424,8 +458,8 @@ PYEOF
424
458
  # ── Theme config file ─────────────────────────────────────────────────────
425
459
  cat > "${THEME_DIR}/saismruth.plymouth" << 'EOF'
426
460
  [Plymouth Theme]
427
- Name=SAI SAMARTH ENGG
428
- Description=PLC Check-Weigher Boot Screen — SAI SAMARTH ENGG
461
+ Name=Sai Samarth Engineering
462
+ Description=PLC Check-Weigher Boot Screen — Sai Samarth Engineering
429
463
  ModuleName=script
430
464
 
431
465
  [script]
@@ -433,30 +467,30 @@ ImageDir=/usr/share/plymouth/themes/saismruth
433
467
  ScriptFile=/usr/share/plymouth/themes/saismruth/saismruth.script
434
468
  EOF
435
469
 
436
- # ── Plymouth script: logo centred, text below ─────────────────────────────
470
+ # ── Plymouth script: logo + pre-rendered text, both centred ──────────────
437
471
  cat > "${THEME_DIR}/saismruth.script" << 'EOF'
438
- # ── SAI SAMARTH ENGG — Boot Splash ────────────────────────────────────────────
472
+ # Sai Samarth Engineering — Boot Splash
439
473
  Window.SetBackgroundTopColor(0.0, 0.0, 0.0);
440
474
  Window.SetBackgroundBottomColor(0.0, 0.0, 0.0);
441
475
 
442
476
  screen_w = Window.GetWidth();
443
477
  screen_h = Window.GetHeight();
444
478
 
445
- # ── Logo (centred, slightly above middle to leave room for text) ──────────────
479
+ # Logo centred, slightly above middle
446
480
  logo_img = Image("logo.png");
447
481
  logo_w = logo_img.GetWidth();
448
482
  logo_h = logo_img.GetHeight();
449
483
  logo_x = (screen_w - logo_w) / 2;
450
- logo_y = (screen_h - logo_h) / 2 - 40;
484
+ logo_y = (screen_h - logo_h) / 2 - 50;
451
485
 
452
486
  logo_sprite = Sprite(logo_img);
453
487
  logo_sprite.SetPosition(logo_x, logo_y, 0);
454
488
 
455
- # ── Company name centred below logo ───────────────────────────────────────────
456
- text_img = Image.Text("SAI SAMARTH ENGG", 1.0, 1.0, 1.0, 1.0, "Sans Bold 20");
489
+ # Company name — pre-rendered PNG, centred below logo
490
+ text_img = Image("text.png");
457
491
  text_w = text_img.GetWidth();
458
492
  text_x = (screen_w - text_w) / 2;
459
- text_y = logo_y + logo_h + 22;
493
+ text_y = logo_y + logo_h + 24;
460
494
 
461
495
  text_sprite = Sprite(text_img);
462
496
  text_sprite.SetPosition(text_x, text_y, 1);
@@ -466,6 +500,23 @@ EOF
466
500
  plymouth-set-default-theme saismruth
467
501
  ok "Plymouth theme set → saismruth"
468
502
 
503
+ # ── Suppress all other boot visuals ──────────────────────────────────────
504
+ # Hide Pi firmware rainbow square
505
+ grep -q "^disable_splash=1" /boot/firmware/config.txt \
506
+ || echo "disable_splash=1" >> /boot/firmware/config.txt
507
+
508
+ # Hide kernel Tux logo + reduce loglevel so only our splash is visible
509
+ if ! grep -q "logo.nologo" /boot/firmware/cmdline.txt; then
510
+ sed -i 's/$/ logo.nologo/' /boot/firmware/cmdline.txt
511
+ fi
512
+ sed -i 's/loglevel=3/loglevel=1/' /boot/firmware/cmdline.txt 2>/dev/null || true
513
+ # Silence systemd service status lines + udev on the console
514
+ for _param in "systemd.show_status=0" "rd.systemd.show_status=0" "udev.log_level=3"; do
515
+ grep -q "$_param" /boot/firmware/cmdline.txt \
516
+ || sed -i "s/\$/ ${_param}/" /boot/firmware/cmdline.txt
517
+ done
518
+ ok "Boot cmdline patched (logo.nologo, loglevel=1, silent systemd, disable_splash=1)"
519
+
469
520
  # Rebuild current initramfs so Plymouth is included.
470
521
  # The RT kernel's post-install will create its own initramfs with Plymouth
471
522
  # already installed, so initramfs8-rt will also carry the theme.
@@ -642,6 +693,43 @@ lock_source_files() {
642
693
  }
643
694
 
644
695
  # ── 12. RT kernel — installed LAST so only one reboot is needed ───────────────
696
+ # ── 11d. System optimization — disable everything not needed by the tool ─────
697
+ setup_system_optimize() {
698
+ step "System optimization (disable non-essential services) ..."
699
+
700
+ # Services NOT used by: PLC stack, web UI, WiFi, SSH, SMB push (client-only),
701
+ # LightDM kiosk, VS Code, or Raspberry Pi Connect.
702
+ # KEEP: NetworkManager, ssh, lightdm, rpi-connect, systemd-timesyncd,
703
+ # fstrim.timer (SD card health), e2scrub (fs health).
704
+ local _DISABLE=(
705
+ bluetooth # no BT devices used
706
+ hciuart # BT UART helper
707
+ ModemManager # no cellular modem
708
+ triggerhappy # hotkey daemon
709
+ avahi-daemon # mDNS discovery — tool uses direct IPs
710
+ cups # no printing
711
+ cups-browsed
712
+ apt-daily.timer # no background auto-update (manual: tool update)
713
+ apt-daily-upgrade.timer
714
+ man-db.timer # man page reindexing
715
+ packagekit # GUI package manager backend
716
+ )
717
+ for _svc in "${_DISABLE[@]}"; do
718
+ if systemctl list-unit-files "${_svc}"* 2>/dev/null | grep -q "${_svc}"; then
719
+ systemctl disable --now "${_svc}" &>/dev/null \
720
+ && ok "disabled ${_svc}" \
721
+ || true
722
+ fi
723
+ done
724
+
725
+ # Power off the Bluetooth radio entirely at boot
726
+ grep -q "^dtoverlay=disable-bt" "${BOOT_FW}/config.txt" \
727
+ || echo "dtoverlay=disable-bt" >> "${BOOT_FW}/config.txt"
728
+ ok "Bluetooth radio disabled at boot (dtoverlay=disable-bt)"
729
+
730
+ ok "System optimized — PLC stack, WiFi, SSH, Pi Connect untouched"
731
+ }
732
+
645
733
  install_rt_kernel() {
646
734
  step "PREEMPT_RT kernel (final step before reboot) ..."
647
735
 
@@ -746,10 +834,11 @@ main() {
746
834
  setup_smb # 7 — interactive SMB config → smb_config.py
747
835
  setup_network_online # 8
748
836
  install_services # 9
749
- setup_boot_logo # 10 — Plymouth: logo + "SAI SAMARTH ENGG"
837
+ setup_boot_logo # 10 — Plymouth: logo + "Sai Samarth Engineering"
750
838
  setup_display # 11 — LightDM priority, CPU isolation, utmpx
751
839
  setup_vscode_priority # 11b — VS Code: cores 0-2, Nice=-5
752
840
  lock_source_files # 11c — root:root on .py, pi:pi on data/
841
+ setup_system_optimize # 11d — disable bluetooth/avahi/cups/apt-timers
753
842
  install_rt_kernel # 12 — LAST, so only one reboot needed
754
843
  do_reboot # 12 — single reboot applies everything
755
844
  }
package/uninstall.sh CHANGED
File without changes