reset to pre-modification baseline; restart feature work from clean state
Restoring the working tree to the pristine pre-Claude baseline previously preserved at /data/clearpilot (now /data/clearpilot-baseline). The prior modified-but-broken tree is snapshotted at /data/openpilot-broken-2026-05-03 and tagged here as pre-reset-2026-05-03 for reference. From here, features (UI changes, dashcam, telemetry, GPS, display modes, speed logic, standstill power saving, etc.) will be re-introduced one at a time with proper testing.
This commit is contained in:
+2
-12
@@ -7,27 +7,17 @@
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "camerad: starting\n");
|
||||
|
||||
if (Hardware::PC()) {
|
||||
fprintf(stderr, "camerad: exiting, not meant to run on PC\n");
|
||||
printf("exiting, camerad is not meant to run on PC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret;
|
||||
fprintf(stderr, "camerad: setting realtime priority 53\n");
|
||||
ret = util::set_realtime_priority(53);
|
||||
fprintf(stderr, "camerad: set_realtime_priority ret=%d\n", ret);
|
||||
assert(ret == 0);
|
||||
|
||||
fprintf(stderr, "camerad: setting core affinity to cpu6\n");
|
||||
ret = util::set_core_affinity({6});
|
||||
bool isOffroad = Params().getBool("IsOffroad");
|
||||
fprintf(stderr, "camerad: set_core_affinity ret=%d, IsOffroad=%d\n", ret, isOffroad);
|
||||
assert(ret == 0 || isOffroad); // failure ok while offroad due to offlining cores
|
||||
assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores
|
||||
|
||||
fprintf(stderr, "camerad: starting camerad_thread\n");
|
||||
camerad_thread();
|
||||
fprintf(stderr, "camerad: camerad_thread returned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQtHTzkeRlOXDWyK/IvO2RgjSdoq6V81u3YtcyIxBZVX2zCj1xzE9zWcUcVxloe63rB/DBasChODIRBtp1vGnWb/EkLWAuOqS2V5rzhlcSfM103++TI81e04A7HDspWSNUXRh5OD/mUvwtYIH7S4QAkBiCro5lAgSToXNAOR4b4cXgNQecf+RhPc0Nm3K8Is1wEeQajmlC1E22YWBDDV+uoB3Uagl90e58Psbp8PunCdbeY9EfqQsymyloiTeqzKwHnmHnMXSlZluh7A+ifoKgohDsarT1FixAgxT0LSIxxINORhE4P6em/7y3xpgubPhNpbuQSzDlb3op3fwMoFcAEEYKWg+d9OGOrdiWa13aV0g7UNdW/XmmF/BAaBdSOZeomVNnxmftmmJWfu3jtFdwTDRQpZn7nDYC+aZ1R3Q0Xd4lLuqkA/9smUXLZuiBDJXwM5nDyWQR9tESIwlTLcdKAUpj0gQqpcozVehksNksTekZBAg/mYb6DKyYCTY0ti0=
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCm/Vq50kqf94allqGq9luBGjDh2Do/rOCA719CRlDOErCvdY+ZaYNumQZ5AbFfU5KcPZwirJLBvhEoH/G0lEAg9TUaUgH/VvqBBztlpcmA1eplZHzEFLnTDn0oO4Tk46bXwjL0anOZfNaUGhbaO4Th7m+9+o16WUduEabPiyVbnqD6P44CANsvBJNKlyUDBzsdkE9z5gULp06i1+JqqXiGV81HoFWZe5YCFv4j4QUPvfmFhcBHViVrOFs87hS4Eu0gWNxQmQBhh6R1ZbjaBlGdE5GyDZQZwlofjfuO06e0HvCDuIAELSYqlGFCmUhlM/LZ6YkF79/HFrg5sS3gsuY5
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHbrOZrByUb2Ci21DdJkhWv/4Bz4oghL9joraQYFq4Om
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkbLtbZ6jRwmvYiAtXDp7JZ+IBVJIrxY3ZPJ93aQCha root@concordia
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCWTdewQ+jCUj9+ZJfte5h0meZhXayd7owIGyXHV0ELCeRAyQrurBPsdTTr7QhcugVuibI+Tr8L3BNuCN8nID5DH+BFAUejulGyMEmQ4Vh22p6Nt0niJHkfiJ2stfayPqe3qGRScVCcY3TpQqlzjyBWOvtwI9/118Gq/lKsjN7DYVwVMHhe1Yzh4SDHOKpsnmDfguRvCSzsg3ZeR1AduaqqM2y0sLZ09Cjpj/vJC/QQ2q2EWtzfmQPejFtjdbqvgoDEQ1OttD5dBgwCMOTPmPMmJ5ns6YJ4L+bi6hynO4/xn3efSHS2mSC6ACwiD/LtTMpsjbUVQsJ4pM/5GY08UoIdnH7P1N+6DA/hah+KAJe8U3WznT6OSXdwWXnYyV+hx4VHBz+/3MnbB1CwtoZoJDnQVpnT3IxBK+pnLHzZJh/g+bFrFbbAC50MRDsoV8nbYvHG3HJQ5GvK96S5NvllGTC/6zo/39ARvfrGtK/2NgNU+NZRjNN67cXjgcUIRdu6eJs= root@concordia
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Uses hardware serial as identity check and encryption key
|
||||
serial=$(sed 's/.*androidboot.serialno=\([^ ]*\).*/\1/' /proc/cmdline)
|
||||
if [[ $serial != 3889765b ]]; then
|
||||
echo "Wrong device (serial=$serial)"
|
||||
dongle_id=$(cat /data/params/d/DongleId)
|
||||
if [[ ! $dongle_id == 90bb71* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Encrypt SSH keys if source files exist using the custom encrypt tool
|
||||
if [ -f /data/openpilot/system/clearpilot/dev/id_ed25519.pub ]; then
|
||||
bash /data/openpilot/system/clearpilot/tools/encrypt /data/openpilot/system/clearpilot/dev/id_ed25519.pub /data/openpilot/system/clearpilot/dev/id_ed25519.pub.cpt
|
||||
if [ -f /data/openpilot/system/clearpilot/dev/id_rsa.pub ]; then
|
||||
bash /data/openpilot/system/clearpilot/tools/encrypt /data/openpilot/system/clearpilot/dev/id_rsa.pub /data/openpilot/system/clearpilot/dev/id_rsa.pub.cpt
|
||||
fi
|
||||
|
||||
if [ -f /data/openpilot/system/clearpilot/dev/id_ed25519 ]; then
|
||||
bash /data/openpilot/system/clearpilot/tools/encrypt /data/openpilot/system/clearpilot/dev/id_ed25519 /data/openpilot/system/clearpilot/dev/id_ed25519.cpt
|
||||
if [ -f /data/openpilot/system/clearpilot/dev/id_rsa ]; then
|
||||
bash /data/openpilot/system/clearpilot/tools/encrypt /data/openpilot/system/clearpilot/dev/id_rsa /data/openpilot/system/clearpilot/dev/id_rsa.cpt
|
||||
fi
|
||||
|
||||
if [ -f /data/openpilot/system/clearpilot/dev/reverse_ssh ]; then
|
||||
|
||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
•í-À‘-j¦ñqã A†3ä"|}ôÚÁñžš.\ñ`þQ¥¶ßA^´Ð×~LìbýÊ ÞÔm!Òzï[®�Wí(¯«rýfo¼À˜¦Miê[&ÄoúÏV=�ˆQ�"2�A“i8ÐpÀ"Á!þ1“æ–G:š4ïá<-Ý
|
||||
#
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# tmp for debugging
|
||||
date >> /tmp/dongles
|
||||
echo check dongle >> /tmp/dongles
|
||||
cat /data/params/d/DongleId >> /tmp/dongles
|
||||
echo done >> /tmp/dongles
|
||||
|
||||
dongle_id=$(cat /data/params/d/DongleId)
|
||||
if [[ ! $dongle_id == 90bb71* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo Bringing up brian dev environment
|
||||
|
||||
bash /data/openpilot/system/clearpilot/dev/provision.sh
|
||||
bash /data/openpilot/system/clearpilot/dev/on_start_brian.sh
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
•Í’T4üoŠd¿á¹³€å–³!qús^§‘1E�Œ½—ðÓÉQ¯Åe|0b.7ša|Þ¶$Âï)x‰ ÷9‘Sü8BÌQÛ÷øÃ;TÝ`~?Q!hj2ÔŒwqô/[´ Xðt¬Ç5‡ü,«Ë�ñm¾^v�¯$vf‚ÇH°)J½A
|
||||
²W°n`<@’‹.¬ç&>�&}m8˜‰àÃ;½\$^`Aª›Œ
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Provision script for BrianBot
|
||||
# These actions only occur on BrianBot's comma device.
|
||||
|
||||
# 1. Check the string in /data/params/d/DongleId
|
||||
dongle_id=$(cat /data/params/d/DongleId)
|
||||
if [[ ! $dongle_id == 90bb71* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "BrianBot dongle ID detected."
|
||||
|
||||
# 2. Check if ccrypt is installed, install if not
|
||||
if ! command -v ccrypt >/dev/null 2>&1; then
|
||||
echo "Installing ccrypt..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccrypt
|
||||
fi
|
||||
|
||||
# 3. Decrypt SSH keys if they have not been decrypted yet
|
||||
if [ ! -f /data/openpilot/system/clearpilot/dev/id_rsa.pub ]; then
|
||||
echo "Decrypting SSH keys..."
|
||||
bash /data/openpilot/system/clearpilot/tools/decrypt /data/openpilot/system/clearpilot/dev/id_rsa.pub.cpt /data/openpilot/system/clearpilot/dev/id_rsa.pub
|
||||
bash /data/openpilot/system/clearpilot/tools/decrypt /data/openpilot/system/clearpilot/dev/id_rsa.cpt /data/openpilot/system/clearpilot/dev/id_rsa
|
||||
bash /data/openpilot/system/clearpilot/tools/decrypt /data/openpilot/system/clearpilot/dev/on_start_brian.sh.cpt /data/openpilot/system/clearpilot/dev/on_start_brian.sh
|
||||
fi
|
||||
|
||||
# 4. Ensure .ssh directory and keys exist
|
||||
ssh_dir="/data/ssh/.ssh"
|
||||
if [[ ! -f "$ssh_dir/id_rsa" || ! -f "$ssh_dir/id_rsa.pub" ]]; then
|
||||
echo "Setting up SSH directory and keys..."
|
||||
mkdir -p "$ssh_dir"
|
||||
cp /data/openpilot/system/clearpilot/dev/id_rsa /data/openpilot/system/clearpilot/dev/id_rsa.pub "$ssh_dir"
|
||||
chmod 700 "$ssh_dir"
|
||||
chmod 600 "$ssh_dir/id_rsa" "$ssh_dir/id_rsa.pub"
|
||||
echo hansonxyz > /data/params/d/GithubUsername
|
||||
cat /data/openpilot/system/clearpilot/dev/GithubSshKeys > /data/params/d/GithubSshKeys
|
||||
echo 1 > /data/params/d/SshEnabled
|
||||
sudo systemctl restart ssh
|
||||
cd /data/openpilot
|
||||
git remote remove origin
|
||||
git remote add origin git@privategit.hanson.xyz:brianhansonxyz/clearpilot.git
|
||||
fi
|
||||
|
||||
echo "Script execution complete."
|
||||
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot GPS daemon — reads GPS from Quectel EC25 modem via AT commands
|
||||
and publishes gpsLocation messages for locationd/timed.
|
||||
|
||||
Replaces qcomgpsd which uses the diag interface (broken on this device).
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from cereal import log
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.gpio import gpio_init, gpio_set
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware.tici.pins import GPIO
|
||||
|
||||
|
||||
def _sunrise_sunset_min(lat: float, lon: float, utc_dt: datetime.datetime):
|
||||
"""Compute (sunrise_min, sunset_min) in UTC minutes since midnight of utc_dt's day.
|
||||
Values can be negative or >1440 for western/eastern longitudes. Returns
|
||||
(None, None) for polar night, ('always', 'always') for midnight sun."""
|
||||
n = utc_dt.timetuple().tm_yday
|
||||
gamma = 2 * math.pi / 365 * (n - 1 + (utc_dt.hour - 12) / 24)
|
||||
eqtime = 229.18 * (0.000075 + 0.001868 * math.cos(gamma)
|
||||
- 0.032077 * math.sin(gamma)
|
||||
- 0.014615 * math.cos(2 * gamma)
|
||||
- 0.040849 * math.sin(2 * gamma))
|
||||
decl = (0.006918 - 0.399912 * math.cos(gamma)
|
||||
+ 0.070257 * math.sin(gamma)
|
||||
- 0.006758 * math.cos(2 * gamma)
|
||||
+ 0.000907 * math.sin(2 * gamma)
|
||||
- 0.002697 * math.cos(3 * gamma)
|
||||
+ 0.00148 * math.sin(3 * gamma))
|
||||
lat_rad = math.radians(lat)
|
||||
zenith = math.radians(90.833)
|
||||
cos_ha = (math.cos(zenith) / (math.cos(lat_rad) * math.cos(decl))
|
||||
- math.tan(lat_rad) * math.tan(decl))
|
||||
if cos_ha < -1:
|
||||
return ('always', 'always') # midnight sun
|
||||
if cos_ha > 1:
|
||||
return (None, None) # polar night
|
||||
ha = math.degrees(math.acos(cos_ha))
|
||||
sunrise_min = 720 - 4 * (lon + ha) - eqtime
|
||||
sunset_min = 720 - 4 * (lon - ha) - eqtime
|
||||
return (sunrise_min, sunset_min)
|
||||
|
||||
|
||||
def is_daylight(lat: float, lon: float, utc_dt: datetime.datetime) -> bool:
|
||||
"""Return True if the sun is currently above the horizon at (lat, lon).
|
||||
|
||||
Handles west-of-Greenwich correctly: at UTC midnight it may still be
|
||||
evening local time, and the relevant sunset is the PREVIOUS UTC day's
|
||||
value (which is >1440 min if we re-ref to that day, i.e. it's past
|
||||
midnight UTC). Symmetric case for east-of-Greenwich at the other end.
|
||||
|
||||
Strategy: compute sunrise/sunset for yesterday, today, and tomorrow (each
|
||||
relative to its own UTC midnight), shift them all onto today's clock
|
||||
(yesterday = -1440, tomorrow = +1440), and check if now_min falls inside
|
||||
any of the three [sunrise, sunset] intervals.
|
||||
"""
|
||||
now_min = utc_dt.hour * 60 + utc_dt.minute + utc_dt.second / 60
|
||||
for day_offset in (-1, 0, 1):
|
||||
d = utc_dt + datetime.timedelta(days=day_offset)
|
||||
sr, ss = _sunrise_sunset_min(lat, lon, d)
|
||||
if sr == 'always':
|
||||
return True
|
||||
if sr is None:
|
||||
continue # polar night this day
|
||||
sr += day_offset * 1440
|
||||
ss += day_offset * 1440
|
||||
if sr <= now_min <= ss:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def at_cmd(cmd: str) -> str:
|
||||
try:
|
||||
result = subprocess.check_output(
|
||||
f"mmcli -m any --timeout 10 --command='{cmd}'",
|
||||
shell=True, encoding='utf8', stderr=subprocess.DEVNULL
|
||||
).strip()
|
||||
# mmcli wraps AT responses: response: '+QGPSLOC: ...'
|
||||
# Strip the wrapper to get just the AT response
|
||||
if result.startswith("response: '") and result.endswith("'"):
|
||||
result = result[len("response: '"):-1]
|
||||
return result
|
||||
except subprocess.CalledProcessError:
|
||||
return ""
|
||||
|
||||
|
||||
def wait_for_modem():
|
||||
cloudlog.warning("gpsd: waiting for modem")
|
||||
while True:
|
||||
ret = subprocess.call(
|
||||
"mmcli -m any --timeout 10 --command='AT+QGPS?'",
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True
|
||||
)
|
||||
if ret == 0:
|
||||
return
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
def parse_qgpsloc(response: str):
|
||||
"""Parse AT+QGPSLOC=2 response into a dict.
|
||||
Format: +QGPSLOC: UTC,lat,lon,hdop,alt,fix,cog,spkm,spkn,date,nsat
|
||||
"""
|
||||
if "+QGPSLOC:" not in response:
|
||||
return None
|
||||
try:
|
||||
data = response.split("+QGPSLOC:")[1].strip()
|
||||
fields = data.split(",")
|
||||
if len(fields) < 11:
|
||||
return None
|
||||
|
||||
utc = fields[0] # HHMMSS.S
|
||||
lat = float(fields[1])
|
||||
lon = float(fields[2])
|
||||
hdop = float(fields[3])
|
||||
alt = float(fields[4])
|
||||
fix = int(fields[5]) # 2=2D, 3=3D
|
||||
cog = float(fields[6]) # course over ground
|
||||
spkm = float(fields[7]) # speed km/h
|
||||
spkn = float(fields[8]) # speed knots
|
||||
date = fields[9] # DDMMYY
|
||||
nsat = int(fields[10])
|
||||
|
||||
# Build unix timestamp from UTC + date
|
||||
# utc: "HHMMSS.S", date: "DDMMYY"
|
||||
hh, mm = int(utc[0:2]), int(utc[2:4])
|
||||
ss = float(utc[4:])
|
||||
dd, mo, yy = int(date[0:2]), int(date[2:4]), 2000 + int(date[4:6])
|
||||
dt = datetime.datetime(yy, mo, dd, hh, mm, int(ss),
|
||||
int((ss % 1) * 1e6), datetime.timezone.utc)
|
||||
|
||||
return {
|
||||
"latitude": lat,
|
||||
"longitude": lon,
|
||||
"altitude": alt,
|
||||
"speed": spkm / 3.6, # convert km/h to m/s
|
||||
"bearing": cog,
|
||||
"accuracy": hdop * 5, # rough conversion from HDOP to meters
|
||||
"timestamp_ms": dt.timestamp() * 1e3,
|
||||
"satellites": nsat,
|
||||
"fix": fix,
|
||||
}
|
||||
except (ValueError, IndexError) as e:
|
||||
cloudlog.error(f"gpsd: parse error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
import sys
|
||||
print("gpsd: starting", file=sys.stderr, flush=True)
|
||||
|
||||
# Kill system gpsd which may respawn and interfere with modem access
|
||||
subprocess.run("pkill -f /usr/sbin/gpsd", shell=True,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
wait_for_modem()
|
||||
print("gpsd: modem ready", file=sys.stderr, flush=True)
|
||||
|
||||
# Enable GPS antenna power
|
||||
gpio_init(GPIO.GNSS_PWR_EN, True)
|
||||
gpio_set(GPIO.GNSS_PWR_EN, True)
|
||||
print("gpsd: GPIO power enabled", file=sys.stderr, flush=True)
|
||||
|
||||
# Don't restart GPS if already running (preserve existing fix)
|
||||
resp = at_cmd("AT+QGPS?")
|
||||
print(f"gpsd: QGPS status: {resp}", file=sys.stderr, flush=True)
|
||||
if "QGPS: 1" not in resp:
|
||||
at_cmd('AT+QGPSCFG="dpoenable",0')
|
||||
at_cmd('AT+QGPSCFG="outport","none"')
|
||||
at_cmd("AT+QGPS=1")
|
||||
print("gpsd: GPS started fresh", file=sys.stderr, flush=True)
|
||||
else:
|
||||
print("gpsd: GPS already running, keeping fix", file=sys.stderr, flush=True)
|
||||
|
||||
pm = messaging.PubMaster(["gpsLocation"])
|
||||
clock_set = system_time_valid()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
last_daylight_check = 0.0
|
||||
daylight_computed = False
|
||||
prev_daylight = None # CLEARPILOT: gate IsDaylight write on change
|
||||
print("gpsd: entering main loop", file=sys.stderr, flush=True)
|
||||
|
||||
while True:
|
||||
resp = at_cmd("AT+QGPSLOC=2")
|
||||
fix = parse_qgpsloc(resp)
|
||||
|
||||
if fix:
|
||||
# Set system clock from GPS on first valid fix if clock is invalid
|
||||
if not clock_set:
|
||||
gps_dt = datetime.datetime.utcfromtimestamp(fix["timestamp_ms"] / 1000)
|
||||
ret = subprocess.run(["date", "-s", gps_dt.strftime("%Y-%m-%d %H:%M:%S")],
|
||||
env={**os.environ, "TZ": "UTC"},
|
||||
capture_output=True)
|
||||
if ret.returncode == 0:
|
||||
clock_set = True
|
||||
cloudlog.warning("gpsd: system clock set from GPS: %s", gps_dt)
|
||||
print(f"gpsd: system clock set from GPS: {gps_dt}", file=sys.stderr, flush=True)
|
||||
else:
|
||||
cloudlog.error("gpsd: failed to set clock: %s", ret.stderr.decode().strip())
|
||||
|
||||
msg = messaging.new_message("gpsLocation", valid=True)
|
||||
gps = msg.gpsLocation
|
||||
gps.latitude = fix["latitude"]
|
||||
gps.longitude = fix["longitude"]
|
||||
gps.altitude = fix["altitude"]
|
||||
gps.speed = fix["speed"]
|
||||
gps.bearingDeg = fix["bearing"]
|
||||
gps.horizontalAccuracy = fix["accuracy"]
|
||||
gps.unixTimestampMillis = int(fix["timestamp_ms"])
|
||||
gps.source = log.GpsLocationData.SensorSource.qcomdiag
|
||||
gps.hasFix = fix["fix"] >= 2
|
||||
gps.flags = 1
|
||||
gps.vNED = [0.0, 0.0, 0.0]
|
||||
gps.verticalAccuracy = fix["accuracy"]
|
||||
gps.bearingAccuracyDeg = 10.0
|
||||
gps.speedAccuracy = 1.0
|
||||
pm.send("gpsLocation", msg)
|
||||
|
||||
# CLEARPILOT: daylight calculation for auto display mode switching
|
||||
if clock_set:
|
||||
now_mono = time.monotonic()
|
||||
interval = 1.0 if not daylight_computed else 30.0
|
||||
if (now_mono - last_daylight_check) >= interval:
|
||||
last_daylight_check = now_mono
|
||||
utc_now = datetime.datetime.utcfromtimestamp(fix["timestamp_ms"] / 1000)
|
||||
daylight = is_daylight(fix["latitude"], fix["longitude"], utc_now)
|
||||
# CLEARPILOT: gate on change — daylight flips twice a day, don't rewrite every 30s
|
||||
if daylight != prev_daylight:
|
||||
params_memory.put_bool("IsDaylight", daylight)
|
||||
prev_daylight = daylight
|
||||
|
||||
if not daylight_computed:
|
||||
daylight_computed = True
|
||||
cloudlog.warning("gpsd: initial daylight calc: %s", "day" if daylight else "night")
|
||||
print(f"gpsd: initial daylight calc: {'day' if daylight else 'night'}", file=sys.stderr, flush=True)
|
||||
|
||||
# Auto-transition: only touch states 0 and 1
|
||||
current_mode = params_memory.get_int("ScreenDisplayMode")
|
||||
if current_mode == 0 and not daylight:
|
||||
params_memory.put_int("ScreenDisplayMode", 1)
|
||||
cloudlog.warning("gpsd: auto-switch to nightrider (sunset)")
|
||||
elif current_mode == 1 and daylight:
|
||||
params_memory.put_int("ScreenDisplayMode", 0)
|
||||
cloudlog.warning("gpsd: auto-switch to normal (sunrise)")
|
||||
|
||||
time.sleep(0.5) # 2 Hz polling
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
# Nice monitor — ensures claude processes run at lowest CPU priority.
|
||||
# Checks every 30 seconds and renices any claude process not already at nice 19.
|
||||
|
||||
# Kill other instances of this script
|
||||
for pid in $(pgrep -f 'nice-monitor.sh' | grep -v $$); do
|
||||
kill "$pid" 2>/dev/null
|
||||
done
|
||||
|
||||
while true; do
|
||||
for pid in $(pgrep -f 'claude' 2>/dev/null); do
|
||||
cur=$(awk '{print $19}' /proc/$pid/stat 2>/dev/null)
|
||||
if [ -n "$cur" ] && [ "$cur" != "19" ]; then
|
||||
renice 19 -p "$pid" > /dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
sleep 30
|
||||
done
|
||||
@@ -3,45 +3,5 @@
|
||||
# Install logo
|
||||
bash /data/openpilot/system/clearpilot/startup_logo/set_logo.sh
|
||||
|
||||
# SSH — always, unconditionally, first thing
|
||||
cat /data/openpilot/system/clearpilot/dev/GithubSshKeys > /data/params/d/GithubSshKeys
|
||||
echo -n 1 > /data/params/d/SshEnabled
|
||||
sudo systemctl enable ssh 2>/dev/null
|
||||
sudo systemctl start ssh
|
||||
|
||||
# Decrypt and install SSH identity keys for root (git auth)
|
||||
serial=$(sed 's/.*androidboot.serialno=\([^ ]*\).*/\1/' /proc/cmdline)
|
||||
ssh_dir="/root/.ssh"
|
||||
if [[ $serial == 3889765b ]] && [[ ! -f "$ssh_dir/id_ed25519" || ! -f "$ssh_dir/id_ed25519.pub" ]]; then
|
||||
echo "Decrypting SSH identity keys for root (serial=$serial)..."
|
||||
tmpdir=$(mktemp -d)
|
||||
bash /data/openpilot/system/clearpilot/tools/decrypt /data/openpilot/system/clearpilot/dev/id_ed25519.cpt "$tmpdir/id_ed25519"
|
||||
bash /data/openpilot/system/clearpilot/tools/decrypt /data/openpilot/system/clearpilot/dev/id_ed25519.pub.cpt "$tmpdir/id_ed25519.pub"
|
||||
sudo mkdir -p "$ssh_dir"
|
||||
sudo cp "$tmpdir/id_ed25519" "$tmpdir/id_ed25519.pub" "$ssh_dir/"
|
||||
rm -rf "$tmpdir"
|
||||
sudo chmod 700 "$ssh_dir"
|
||||
sudo chmod 600 "$ssh_dir/id_ed25519"
|
||||
sudo chmod 644 "$ssh_dir/id_ed25519.pub"
|
||||
echo "SSH identity keys installed to $ssh_dir"
|
||||
fi
|
||||
|
||||
# Ensure root SSH config has git.hanson.xyz entry
|
||||
if ! grep -q "Host git.hanson.xyz" "$ssh_dir/config" 2>/dev/null; then
|
||||
sudo tee -a "$ssh_dir/config" > /dev/null <<'SSHCFG'
|
||||
|
||||
Host git.hanson.xyz
|
||||
IdentityFile /root/.ssh/id_ed25519
|
||||
StrictHostKeyChecking no
|
||||
SSHCFG
|
||||
sudo chmod 600 "$ssh_dir/config"
|
||||
echo "SSH config updated for git.hanson.xyz"
|
||||
fi
|
||||
|
||||
# Always ensure WiFi radio is on
|
||||
nmcli radio wifi on 2>/dev/null
|
||||
|
||||
# Provision (packages, git pull, build) if no quick_boot flag
|
||||
if [ ! -f /data/quick_boot ]; then
|
||||
sudo bash /data/openpilot/system/clearpilot/provision.sh
|
||||
fi
|
||||
# Reverse ssh if brianbot dongle id
|
||||
bash /data/openpilot/system/clearpilot/dev/on_start.sh
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ClearPilot provision script
|
||||
# Runs on first boot (no /data/quick_boot) when internet is available.
|
||||
# Installs packages, pulls latest code, and builds.
|
||||
# SSH is handled by on_start.sh before this runs.
|
||||
# Output is displayed on screen via qt_shell.
|
||||
|
||||
mount -o rw,remount /
|
||||
|
||||
# 1. Wait for internet connectivity
|
||||
echo "Waiting for internet connectivity (up to 30s)..."
|
||||
ONLINE=0
|
||||
for i in $(seq 1 30); do
|
||||
if nmcli networking connectivity check 2>/dev/null | grep -q "full"; then
|
||||
echo "Online after ${i}s"
|
||||
ONLINE=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$ONLINE" -eq 0 ]; then
|
||||
echo "No internet after 30s, skipping packages and updates"
|
||||
sleep 3
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 3. Install packages
|
||||
echo "Remounting / read-write..."
|
||||
sudo mount -o remount,rw /
|
||||
echo "Installing packages..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y openvpn curl ccrypt
|
||||
#echo "Installing Node.js 20..."
|
||||
#curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
mount -o rw,remount /
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash
|
||||
cat > /usr/local/bin/claude <<'WRAPPER'
|
||||
#!/bin/bash
|
||||
sudo mount -o rw,remount /
|
||||
exec /root/.local/bin/claude "$@"
|
||||
WRAPPER
|
||||
chmod +x /usr/local/bin/claude
|
||||
echo "Packages installed"
|
||||
# 4. Ensure git remote uses SSH (not HTTPS)
|
||||
cd /data/openpilot
|
||||
EXPECTED_REMOTE="git@git.hanson.xyz:brianhansonxyz/clearpilot.git"
|
||||
CURRENT_REMOTE=$(git remote get-url origin 2>/dev/null)
|
||||
if [ "$CURRENT_REMOTE" != "$EXPECTED_REMOTE" ]; then
|
||||
echo "Fixing git remote: $CURRENT_REMOTE -> $EXPECTED_REMOTE"
|
||||
git remote set-url origin "$EXPECTED_REMOTE"
|
||||
fi
|
||||
|
||||
# 5. Pull latest from remote (remote always wins)
|
||||
echo "Checking for updates..."
|
||||
git fetch origin clearpilot
|
||||
LOCAL=$(git rev-parse HEAD)
|
||||
REMOTE=$(git rev-parse origin/clearpilot)
|
||||
if [ "$LOCAL" != "$REMOTE" ]; then
|
||||
echo "Updating: $(git log --oneline -1 HEAD) -> $(git log --oneline -1 origin/clearpilot)"
|
||||
git reset --hard origin/clearpilot
|
||||
sudo chown -R comma:comma /data/openpilot
|
||||
echo "Update complete"
|
||||
else
|
||||
echo "Already up to date"
|
||||
fi
|
||||
|
||||
# 5. Build
|
||||
echo ""
|
||||
sudo chown -R comma:comma /data/openpilot
|
||||
touch /data/quick_boot
|
||||
|
||||
echo "Provision complete"
|
||||
sleep 2
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
exec bash /data/openpilot/system/clearpilot/provision.sh 2>&1
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@@ -3,9 +3,8 @@
|
||||
# Create a 2160x1080 true color bitmap canvas with black background
|
||||
convert -size 2160x1080 canvas:black /tmp/black_canvas.png
|
||||
|
||||
# Scale logo 140% then center on canvas
|
||||
convert /data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/boot_logo.png -resize 140% /tmp/scaled_logo.png
|
||||
composite -gravity center /tmp/scaled_logo.png /tmp/black_canvas.png /tmp/centered_image.png
|
||||
# Place the image in the center of the canvas, blending the transparent background
|
||||
composite -gravity center /data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/boot_logo.png /tmp/black_canvas.png /tmp/centered_image.png
|
||||
|
||||
# Rotate the image clockwise 90 degrees
|
||||
convert /tmp/centered_image.png -rotate 90 /tmp/rotated_image.png
|
||||
@@ -14,4 +13,4 @@ convert /tmp/centered_image.png -rotate 90 /tmp/rotated_image.png
|
||||
convert /tmp/rotated_image.png -quality 95 /data/openpilot/system/clearpilot/startup_logo/bg.jpg
|
||||
|
||||
# Clean up temporary files
|
||||
rm /tmp/black_canvas.png /tmp/scaled_logo.png /tmp/centered_image.png /tmp/rotated_image.png
|
||||
rm /tmp/black_canvas.png /tmp/centered_image.png /tmp/rotated_image.png
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
set -x
|
||||
|
||||
|
||||
# CLEARPILOT: regenerate bg.jpg if boot_logo.png is newer (handles logo changes)
|
||||
BOOT_LOGO="/data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/boot_logo.png"
|
||||
GENERATED_BG="/data/openpilot/system/clearpilot/startup_logo/bg.jpg"
|
||||
if [ "$BOOT_LOGO" -nt "$GENERATED_BG" ] 2>/dev/null; then
|
||||
bash /data/openpilot/system/clearpilot/startup_logo/generate_logo.sh
|
||||
fi
|
||||
|
||||
# Check if md5sum of /usr/comma/bg.jpg is not equal to md5sum of /data/openpilot/system/clearpilot/startup_logo/bg.jpg
|
||||
if [ "$(md5sum /usr/comma/bg.jpg | awk '{print $1}')" != "$(md5sum /data/openpilot/system/clearpilot/startup_logo/bg.jpg | awk '{print $1}')" ]; then
|
||||
bash /data/openpilot/system/clearpilot/startup_logo/generate_logo.sh
|
||||
|
||||
@@ -10,11 +10,8 @@ fi
|
||||
src="$1"
|
||||
dest="$2"
|
||||
|
||||
# Use hardware serial as decryption key
|
||||
serial=$(sed 's/.*androidboot.serialno=\([^ ]*\).*/\1/' /proc/cmdline)
|
||||
keyfile=$(mktemp)
|
||||
echo -n "$serial" > "$keyfile"
|
||||
# Read DongleId for decryption key
|
||||
dongle_id=/data/params/d/DongleId
|
||||
|
||||
# Decrypt the file
|
||||
cat "$src" | ccrypt -d -k "$keyfile" > "$dest"
|
||||
rm -f "$keyfile"
|
||||
cat "$src" | ccrypt -d -k "$dongle_id" > "$dest"
|
||||
|
||||
@@ -10,11 +10,8 @@ fi
|
||||
src="$1"
|
||||
dest="$2"
|
||||
|
||||
# Use hardware serial as encryption key
|
||||
serial=$(sed 's/.*androidboot.serialno=\([^ ]*\).*/\1/' /proc/cmdline)
|
||||
keyfile=$(mktemp)
|
||||
echo -n "$serial" > "$keyfile"
|
||||
# Read DongleId for encryption key
|
||||
dongle_id=/data/params/d/DongleId
|
||||
|
||||
# Encrypt the file
|
||||
cat "$src" | ccrypt -e -k "$keyfile" > "$dest"
|
||||
rm -f "$keyfile"
|
||||
cat "$src" | ccrypt -e -k "$dongle_id" > "$dest"
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
# VPN monitor — connects OpenVPN when internet is up, disconnects when down.
|
||||
# Drops and reconnects when WiFi SSID changes (stale tunnel prevention).
|
||||
# On non-home networks, resolves VPN hostname via 8.8.8.8 and passes IP directly.
|
||||
# Keepalive: pings gateway through tunnel, two failures 10s apart = reconnect.
|
||||
# SIGTERM: gracefully stops tunnel and exits.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
CONF="$SCRIPT_DIR/vpn.ovpn"
|
||||
VPN_HOST="vpn.hanson.xyz"
|
||||
VPN_PORT="1194"
|
||||
HOME_SSID="risa"
|
||||
VPN_GW="192.168.69.1"
|
||||
CHECK_HOST="1.1.1.1"
|
||||
INTERVAL=30
|
||||
CONNECT_TIMEOUT=30
|
||||
MAX_FAILURES=3
|
||||
PREV_SSID=""
|
||||
FAIL_COUNT=0
|
||||
ACTIVE_VPN_IP=""
|
||||
|
||||
kill_vpn() {
|
||||
killall openvpn 2>/dev/null
|
||||
# Clean up host route to VPN server
|
||||
if [ -n "$ACTIVE_VPN_IP" ]; then
|
||||
ip route del "$ACTIVE_VPN_IP/32" 2>/dev/null
|
||||
ACTIVE_VPN_IP=""
|
||||
fi
|
||||
}
|
||||
|
||||
get_default_gw() {
|
||||
ip route show default | awk '/via/ {print $3; exit}'
|
||||
}
|
||||
|
||||
resolve_vpn() {
|
||||
if [ "$CURR_SSID" != "$HOME_SSID" ]; then
|
||||
dig +short @8.8.8.8 "$VPN_HOST" 2>/dev/null | tail -1
|
||||
else
|
||||
dig +short "$VPN_HOST" 2>/dev/null | tail -1
|
||||
fi
|
||||
}
|
||||
|
||||
# Graceful shutdown on SIGTERM
|
||||
shutdown() {
|
||||
echo "$(date): SIGTERM received, stopping vpn and exiting"
|
||||
kill_vpn
|
||||
exit 0
|
||||
}
|
||||
trap shutdown SIGTERM SIGINT
|
||||
|
||||
# Kill other instances of this script and wait for graceful shutdown
|
||||
for pid in $(pgrep -f 'vpn-monitor.sh' | grep -v $$); do
|
||||
kill "$pid" 2>/dev/null
|
||||
done
|
||||
sleep 5
|
||||
# Force kill any that didn't exit
|
||||
for pid in $(pgrep -f 'vpn-monitor.sh' | grep -v $$); do
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
done
|
||||
|
||||
# Kill any existing VPN and clean up
|
||||
kill_vpn
|
||||
sleep 1
|
||||
|
||||
while true; do
|
||||
CURR_SSID="$(iwgetid -r 2>/dev/null)"
|
||||
|
||||
# Detect SSID change (only when switching between two known networks)
|
||||
if [ -n "$PREV_SSID" ] && [ -n "$CURR_SSID" ] && [ "$PREV_SSID" != "$CURR_SSID" ]; then
|
||||
echo "$(date): wifi changed from '$PREV_SSID' to '$CURR_SSID', dropping vpn"
|
||||
kill_vpn
|
||||
FAIL_COUNT=0
|
||||
sleep 5
|
||||
fi
|
||||
PREV_SSID="$CURR_SSID"
|
||||
|
||||
if ping -c 1 -W 3 "$CHECK_HOST" > /dev/null 2>&1; then
|
||||
# Internet is up — check tunnel health if connected
|
||||
if ip link show tun0 > /dev/null 2>&1; then
|
||||
# Keepalive: ping gateway through tunnel, two failures 10s apart = dead
|
||||
if ! ping -c 1 -W 3 -I tun0 "$VPN_GW" > /dev/null 2>&1; then
|
||||
sleep 10
|
||||
if ! ping -c 1 -W 3 -I tun0 "$VPN_GW" > /dev/null 2>&1; then
|
||||
echo "$(date): keepalive failed twice, dropping vpn"
|
||||
kill_vpn
|
||||
sleep 5
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start VPN if not running
|
||||
if ! ip link show tun0 > /dev/null 2>&1; then
|
||||
if [ "$FAIL_COUNT" -ge "$MAX_FAILURES" ]; then
|
||||
# Back off after repeated failures — just wait for next interval
|
||||
sleep "$INTERVAL"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Resolve VPN server IP (via 8.8.8.8 on non-home networks)
|
||||
RESOLVED_IP="$(resolve_vpn)"
|
||||
if [ -z "$RESOLVED_IP" ]; then
|
||||
echo "$(date): failed to resolve $VPN_HOST (ssid=$CURR_SSID)"
|
||||
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||
sleep "$INTERVAL"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Add host route to VPN server via current default gateway
|
||||
# so VPN traffic survives tun0 coming up
|
||||
GW="$(get_default_gw)"
|
||||
if [ -n "$GW" ]; then
|
||||
ip route replace "$RESOLVED_IP/32" via "$GW"
|
||||
echo "$(date): host route $RESOLVED_IP via $GW"
|
||||
fi
|
||||
ACTIVE_VPN_IP="$RESOLVED_IP"
|
||||
|
||||
echo "$(date): starting openvpn -> $RESOLVED_IP (attempt $((FAIL_COUNT + 1))/$MAX_FAILURES, ssid=$CURR_SSID)"
|
||||
nice -n 19 openvpn --config "$CONF" --remote "$RESOLVED_IP" "$VPN_PORT" --daemon --log-append /tmp/openvpn.log
|
||||
|
||||
# Wait for tunnel to come up
|
||||
CONNECTED=0
|
||||
for i in $(seq 1 "$CONNECT_TIMEOUT"); do
|
||||
if ip link show tun0 > /dev/null 2>&1; then
|
||||
CONNECTED=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$CONNECTED" -eq 1 ]; then
|
||||
echo "$(date): vpn connected (took ${i}s)"
|
||||
FAIL_COUNT=0
|
||||
else
|
||||
echo "$(date): vpn failed to connect within ${CONNECT_TIMEOUT}s, killing"
|
||||
kill_vpn
|
||||
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||
if [ "$FAIL_COUNT" -ge "$MAX_FAILURES" ]; then
|
||||
echo "$(date): $MAX_FAILURES consecutive failures, backing off"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Internet is down — kill VPN if running
|
||||
if ip link show tun0 > /dev/null 2>&1; then
|
||||
echo "$(date): internet down, stopping openvpn"
|
||||
kill_vpn
|
||||
fi
|
||||
FAIL_COUNT=0
|
||||
fi
|
||||
sleep "$INTERVAL"
|
||||
done
|
||||
@@ -1,76 +0,0 @@
|
||||
client
|
||||
dev tun
|
||||
proto udp
|
||||
remote vpn.hanson.xyz 1194
|
||||
resolv-retry infinite
|
||||
nobind
|
||||
persist-key
|
||||
persist-tun
|
||||
remote-cert-tls server
|
||||
cipher AES-256-GCM
|
||||
auth SHA256
|
||||
verb 3
|
||||
pull-filter ignore "redirect-gateway"
|
||||
# pull-filter ignore "route "
|
||||
|
||||
<ca>
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB2jCCAX+gAwIBAgIUFVGjbK1Qb5d3RkkoNPMsXeI/xVAwCgYIKoZIzj0EAwIw
|
||||
HjEcMBoGA1UEAwwTT3BlblZQTi1JbnRlcm5hbC1DQTAgFw0yNjAyMDcwODQ3Mzda
|
||||
GA8yMTI2MDExNDA4NDczN1owHjEcMBoGA1UEAwwTT3BlblZQTi1JbnRlcm5hbC1D
|
||||
QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGb6RWOFnCJ9t7X5q6fqpv0y3Hg/
|
||||
dTU3ky+MAjfPRYfUWfiM7wVKubYOCc+pUHsJXWaghqu7nQoCeSzVDcPXlWGjgZgw
|
||||
gZUwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUNThAWabF1zsqNE19iCKuZMjHBIUw
|
||||
WQYDVR0jBFIwUIAUNThAWabF1zsqNE19iCKuZMjHBIWhIqQgMB4xHDAaBgNVBAMM
|
||||
E09wZW5WUE4tSW50ZXJuYWwtQ0GCFBVRo2ytUG+Xd0ZJKDTzLF3iP8VQMAsGA1Ud
|
||||
DwQEAwIBBjAKBggqhkjOPQQDAgNJADBGAiEA2mPwEK8G4HXlRu6WZVSRdqyCPYYd
|
||||
KffYalCXgw3pZ/sCIQC9qPNckHtubycu8kq4iM8Vl1vYMVEorn7DUFdXJCvtcg==
|
||||
-----END CERTIFICATE-----
|
||||
</ca>
|
||||
|
||||
<cert>
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB2TCCAYCgAwIBAgIRALuRBSB68/ccWM8SASfEIV0wCgYIKoZIzj0EAwIwHjEc
|
||||
MBoGA1UEAwwTT3BlblZQTi1JbnRlcm5hbC1DQTAgFw0yNjA0MTIwMDA1NDhaGA8y
|
||||
MTI2MDMxOTAwMDU0OFowEDEOMAwGA1UEAwwFY29tbWEwWTATBgcqhkjOPQIBBggq
|
||||
hkjOPQMBBwNCAAQ/jN83Z2Ikk+IWVPGxN0CNFCh74Yrb3W6VXAjGWa+ppVxSbdeq
|
||||
YVBWjJl6qSg6n2ZMDivQ5NcKgsxMcY9ly/LEo4GqMIGnMAkGA1UdEwQCMAAwHQYD
|
||||
VR0OBBYEFDIulLc8hAwTkGHq+z8K8eBBM0vVMFkGA1UdIwRSMFCAFDU4QFmmxdc7
|
||||
KjRNfYgirmTIxwSFoSKkIDAeMRwwGgYDVQQDDBNPcGVuVlBOLUludGVybmFsLUNB
|
||||
ghQVUaNsrVBvl3dGSSg08yxd4j/FUDATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNV
|
||||
HQ8EBAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgR/ssLDNLmt1s0WXwGLszBUrlstUu
|
||||
9nhP2PcmdnsOit4CIECFbQ7RHEZLQJWsL2DvKowCCzDtA6ZGDILTVfHwNyDn
|
||||
-----END CERTIFICATE-----
|
||||
</cert>
|
||||
|
||||
<key>
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYolghDmo5ISWxjQy
|
||||
sayXuFRSW5fkiIXJ1SGvSRLnmBmhRANCAAQ/jN83Z2Ikk+IWVPGxN0CNFCh74Yrb
|
||||
3W6VXAjGWa+ppVxSbdeqYVBWjJl6qSg6n2ZMDivQ5NcKgsxMcY9ly/LE
|
||||
-----END PRIVATE KEY-----
|
||||
</key>
|
||||
|
||||
<tls-crypt>
|
||||
#
|
||||
# 2048 bit OpenVPN static key
|
||||
#
|
||||
-----BEGIN OpenVPN Static key V1-----
|
||||
5d6fedacbb44013958eef494b179f21d
|
||||
51b158484c08cb125b8ddd2a919ed44f
|
||||
5cae951b1f85f483f0108b1000fac1e6
|
||||
334ab5b2f3c7352c3a53e814e2e4cdc7
|
||||
f401d5eb2e13449539313f18de53563d
|
||||
a72318979c31ef76caad86317064aede
|
||||
940ab3d799886b9667f4deabb8b159c2
|
||||
12bd7f27c91a7bfd3b9a315dbac3391d
|
||||
fb3c354b7955627937fd6163c1683705
|
||||
e46b252ee9c383507b5a4496462f3d67
|
||||
25dc48bbca8170574efa22b3c37c4bcc
|
||||
ad30e92d39aae5326c59a4484302d388
|
||||
7836837bd5098faeda430aa6db69d8df
|
||||
fe62aeed2bef6afb7c0c742fe8644040
|
||||
3c4e46deb3915467c351018592c58545
|
||||
5b5d7b8c204d37104f9848573d8eb73b
|
||||
-----END OpenVPN Static key V1-----
|
||||
</tls-crypt>
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
}
|
||||
|
||||
static void reboot() { std::system("sudo reboot"); }
|
||||
|
||||
static void soft_reboot() {
|
||||
const std::vector<std::string> commands = {
|
||||
"rm -f /tmp/safe_staging_overlay.lock",
|
||||
@@ -67,7 +68,9 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void poweroff() { std::system("sudo poweroff"); }
|
||||
|
||||
static void set_brightness(int percent) {
|
||||
std::string max = util::read_file("/sys/class/backlight/panel0-backlight/max_brightness");
|
||||
|
||||
@@ -77,6 +80,7 @@ public:
|
||||
brightness_control.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void set_display_power(bool on) {
|
||||
std::ofstream bl_power_control("/sys/class/backlight/panel0-backlight/bl_power");
|
||||
if (bl_power_control.is_open()) {
|
||||
|
||||
@@ -8,15 +8,11 @@ from openpilot.system.loggerd.config import get_available_bytes, get_available_p
|
||||
from openpilot.system.loggerd.uploader import listdir_by_creation
|
||||
from openpilot.system.loggerd.xattr_cache import getxattr
|
||||
|
||||
# CLEARPILOT: increased from 5 GB to 9 GB to reserve space for screen recordings
|
||||
MIN_BYTES = 9 * 1024 * 1024 * 1024
|
||||
MIN_BYTES = 5 * 1024 * 1024 * 1024
|
||||
MIN_PERCENT = 10
|
||||
|
||||
DELETE_LAST = ['boot', 'crash']
|
||||
|
||||
# CLEARPILOT: screen recorder video directory
|
||||
VIDEOS_DIR = '/data/media/0/videos'
|
||||
|
||||
PRESERVE_ATTR_NAME = 'user.preserve'
|
||||
PRESERVE_ATTR_VALUE = b'1'
|
||||
PRESERVE_COUNT = 5
|
||||
@@ -48,103 +44,12 @@ def get_preserved_segments(dirs_by_creation: list[str]) -> list[str]:
|
||||
return preserved
|
||||
|
||||
|
||||
def delete_oldest_video():
|
||||
"""CLEARPILOT: delete oldest dashcam footage when disk space is low.
|
||||
Trip directories are /data/media/0/videos/YYYYMMDD-HHMMSS/ containing .mp4 segments.
|
||||
Deletes entire oldest trip directory first. If only one trip remains (active),
|
||||
deletes individual segments oldest-first within it. Also cleans up legacy flat .mp4 files."""
|
||||
try:
|
||||
if not os.path.isdir(VIDEOS_DIR):
|
||||
return False
|
||||
|
||||
# Collect legacy flat mp4 files and trip directories
|
||||
legacy_files = []
|
||||
trip_dirs = []
|
||||
for entry in os.listdir(VIDEOS_DIR):
|
||||
path = os.path.join(VIDEOS_DIR, entry)
|
||||
if os.path.isfile(path) and entry.endswith('.mp4'):
|
||||
legacy_files.append(entry)
|
||||
elif os.path.isdir(path):
|
||||
trip_dirs.append(entry)
|
||||
|
||||
# Delete legacy flat files first (oldest by name)
|
||||
if legacy_files:
|
||||
legacy_files.sort()
|
||||
delete_path = os.path.join(VIDEOS_DIR, legacy_files[0])
|
||||
cloudlog.info(f"deleting legacy video {delete_path}")
|
||||
os.remove(delete_path)
|
||||
return True
|
||||
|
||||
if not trip_dirs:
|
||||
return False
|
||||
|
||||
trip_dirs.sort() # sorted by timestamp name = chronological order
|
||||
|
||||
# If more than one trip, delete the oldest entire trip directory
|
||||
if len(trip_dirs) > 1:
|
||||
delete_path = os.path.join(VIDEOS_DIR, trip_dirs[0])
|
||||
cloudlog.info(f"deleting trip {delete_path}")
|
||||
shutil.rmtree(delete_path)
|
||||
return True
|
||||
|
||||
# Only one trip left (likely active) — delete oldest segment within it
|
||||
trip_path = os.path.join(VIDEOS_DIR, trip_dirs[0])
|
||||
segments = sorted(f for f in os.listdir(trip_path) if f.endswith('.mp4'))
|
||||
if not segments:
|
||||
return False
|
||||
delete_path = os.path.join(trip_path, segments[0])
|
||||
cloudlog.info(f"deleting segment {delete_path}")
|
||||
os.remove(delete_path)
|
||||
return True
|
||||
except OSError:
|
||||
cloudlog.exception(f"issue deleting video from {VIDEOS_DIR}")
|
||||
return False
|
||||
|
||||
|
||||
# CLEARPILOT: max total size for /data/log2 session logs
|
||||
LOG2_MAX_BYTES = 4 * 1024 * 1024 * 1024
|
||||
|
||||
def cleanup_log2():
|
||||
"""Delete oldest session log directories until /data/log2 is under LOG2_MAX_BYTES."""
|
||||
log_base = "/data/log2"
|
||||
if not os.path.isdir(log_base):
|
||||
return
|
||||
# Get all session dirs sorted oldest first (by name = timestamp)
|
||||
dirs = []
|
||||
for entry in sorted(os.listdir(log_base)):
|
||||
if entry == "current":
|
||||
continue
|
||||
path = os.path.join(log_base, entry)
|
||||
if os.path.isdir(path) and not os.path.islink(path):
|
||||
size = sum(f.stat().st_size for f in os.scandir(path) if f.is_file())
|
||||
dirs.append((entry, path, size))
|
||||
total = sum(s for _, _, s in dirs)
|
||||
# Also count current session
|
||||
current = os.path.join(log_base, "current")
|
||||
if os.path.isdir(current):
|
||||
total += sum(f.stat().st_size for f in os.scandir(current) if f.is_file())
|
||||
# Delete oldest until under quota
|
||||
while total > LOG2_MAX_BYTES and dirs:
|
||||
entry, path, size = dirs.pop(0)
|
||||
try:
|
||||
cloudlog.info(f"deleting log session {path} ({size // 1024 // 1024} MB)")
|
||||
shutil.rmtree(path)
|
||||
total -= size
|
||||
except OSError:
|
||||
cloudlog.exception(f"issue deleting log {path}")
|
||||
|
||||
|
||||
def deleter_thread(exit_event):
|
||||
while not exit_event.is_set():
|
||||
out_of_bytes = get_available_bytes(default=MIN_BYTES + 1) < MIN_BYTES
|
||||
out_of_percent = get_available_percent(default=MIN_PERCENT + 1) < MIN_PERCENT
|
||||
|
||||
if out_of_percent or out_of_bytes:
|
||||
# CLEARPILOT: try deleting oldest video first, then fall back to log segments
|
||||
if delete_oldest_video():
|
||||
exit_event.wait(.1)
|
||||
continue
|
||||
|
||||
dirs = listdir_by_creation(Paths.log_root())
|
||||
|
||||
# skip deleting most recent N preserved segments (and their prior segment)
|
||||
@@ -168,8 +73,6 @@ def deleter_thread(exit_event):
|
||||
cloudlog.exception(f"issue deleting {delete_path}")
|
||||
exit_event.wait(.1)
|
||||
else:
|
||||
# CLEARPILOT: enforce log2 size quota even when disk space is fine
|
||||
cleanup_log2()
|
||||
exit_event.wait(30)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user