mici network manager / allow uploads / long pitch / cruise offset
This commit is contained in:
Binary file not shown.
@@ -250,6 +250,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"Fahrenheit", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
|
||||
Binary file not shown.
@@ -878,6 +878,10 @@ class FrogPilotVariables:
|
||||
"GMPedalLongitudinal",
|
||||
condition=toggle.car_make == "gm" and toggle.has_pedal,
|
||||
)
|
||||
toggle.long_pitch = self.get_value(
|
||||
"LongPitch",
|
||||
condition=toggle.openpilot_longitudinal and toggle.car_make == "gm",
|
||||
)
|
||||
toggle.remote_start_boots_comma = self.get_value("RemoteStartBootsComma", condition=toggle.car_make == "gm")
|
||||
toggle.remap_cancel_to_distance = self.get_value(
|
||||
"RemapCancelToDistance",
|
||||
|
||||
@@ -8,7 +8,7 @@ from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.gps import get_gps_location_service
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX
|
||||
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHANGE_COST, DANGER_ZONE_COST, J_EGO_COST, STOP_DISTANCE
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import calculate_lane_width, calculate_road_curvature
|
||||
@@ -64,7 +64,10 @@ class FrogPilotPlanner:
|
||||
controls_enabled = sm["selfdriveState"].enabled
|
||||
planner_active = controls_enabled or long_control_active
|
||||
|
||||
v_cruise = min(sm["carState"].vCruise, V_CRUISE_MAX) * CV.KPH_TO_MS
|
||||
v_cruise_kph = sm["carState"].vCruise
|
||||
if 0 < v_cruise_kph < V_CRUISE_UNSET and frogpilot_toggles.set_speed_offset > 0:
|
||||
v_cruise_kph += frogpilot_toggles.set_speed_offset
|
||||
v_cruise = min(v_cruise_kph, V_CRUISE_MAX) * CV.KPH_TO_MS
|
||||
v_ego = max(sm["carState"].vEgo, 0)
|
||||
|
||||
if planner_active:
|
||||
|
||||
@@ -1688,6 +1688,13 @@
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle"
|
||||
},
|
||||
{
|
||||
"key": "LongPitch",
|
||||
"label": "Smooth Pedal Response on Hills",
|
||||
"description": "Smoothen acceleration and braking when driving downhill/uphill.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle"
|
||||
},
|
||||
{
|
||||
"key": "RemoteStartBootsComma",
|
||||
"label": "Remote Start Boots comma",
|
||||
|
||||
@@ -175,6 +175,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent,
|
||||
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
|
||||
{"GMToggles", tr("General Motors Settings"), tr("<b>FrogPilot features for General Motors vehicles.</b>"), ""},
|
||||
{"GMPedalLongitudinal", tr("Use Pedal For Longitudinal"), tr("<b>Use the pedal interceptor for full longitudinal control</b> on supported GM vehicles."), ""},
|
||||
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
|
||||
{"RemoteStartBootsComma", tr("Remote Start Boots comma"), tr("<b>Use the remote-start GM panda firmware at boot.</b><br><br>Required for GM remote-start startup signal behavior."), ""},
|
||||
{"RemapCancelToDistance", tr("Remap Cancel To Distance"), tr("<b>On pedal-interceptor Bolts, remap the steering-wheel CANCEL button to distance/personality input.</b>"), ""},
|
||||
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
|
||||
|
||||
@@ -23,9 +23,9 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> gmKeys = {"GMPedalLongitudinal", "RemoteStartBootsComma", "RemapCancelToDistance", "VoltSNG"};
|
||||
QSet<QString> gmKeys = {"GMPedalLongitudinal", "LongPitch", "RemoteStartBootsComma", "RemapCancelToDistance", "VoltSNG"};
|
||||
QSet<QString> hkgKeys = {"TacoTuneHacks"};
|
||||
QSet<QString> longitudinalKeys = {"FrogsGoMoosTweak", "RemapCancelToDistance", "SNGHack", "VoltSNG"};
|
||||
QSet<QString> longitudinalKeys = {"FrogsGoMoosTweak", "LongPitch", "RemapCancelToDistance", "SNGHack", "VoltSNG"};
|
||||
QSet<QString> subaruKeys = {"SubaruSNG"};
|
||||
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
|
||||
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};
|
||||
|
||||
@@ -370,8 +370,10 @@ class CarController(CarControllerBase):
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = int(min(-100 * stop_accel, self.params.MAX_BRAKE))
|
||||
else:
|
||||
long_pitch_enabled = bool(getattr(frogpilot_toggles, "long_pitch", True))
|
||||
|
||||
if self.is_volt:
|
||||
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
if long_pitch_enabled and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
volt_pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
else:
|
||||
volt_pitch_accel = 0.0
|
||||
@@ -395,7 +397,7 @@ class CarController(CarControllerBase):
|
||||
if self.apply_brake > 0:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
else:
|
||||
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
if long_pitch_enabled and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
else:
|
||||
accel_due_to_pitch = 0.0
|
||||
@@ -424,7 +426,12 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.carFingerprint in CC_ONLY_CAR:
|
||||
# gas interceptor only used for full long control on cars without ACC
|
||||
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
|
||||
pedal_accel_cmd = actuators.accel
|
||||
if (long_pitch_enabled and
|
||||
len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping):
|
||||
pedal_accel_cmd += math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(pedal_accel_cmd, CC.longActive, CS.out.vEgo)
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED and self.apply_gas > self.params.INACTIVE_REGEN and CS.out.cruiseState.standstill:
|
||||
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
extern const uint8_t gitversion[19];
|
||||
const uint8_t gitversion[19] = "DEV-e9100341-DEBUG";
|
||||
const uint8_t gitversion[19] = "DEV-6a639c86-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-e9100341-DEBUG
|
||||
DEV-6a639c86-DEBUG
|
||||
@@ -112,12 +112,6 @@ class VCruiseHelper:
|
||||
else:
|
||||
self.v_cruise_kph += v_cruise_delta * CRUISE_INTERVAL_SIGN[button_type]
|
||||
|
||||
# FrogPilot variables
|
||||
if long_press and frogpilot_toggles.set_speed_offset > 0:
|
||||
self.v_cruise_kph += frogpilot_toggles.set_speed_offset
|
||||
if button_type == ButtonType.decelCruise:
|
||||
self.v_cruise_kph -= v_cruise_delta
|
||||
|
||||
# If set is pressed while overriding, clip cruise speed to minimum of vEgo
|
||||
if CS.gasPressed and button_type in (ButtonType.decelCruise, ButtonType.setCruise):
|
||||
self.v_cruise_kph = max(self.v_cruise_kph, CS.vEgo * CV.MS_TO_KPH)
|
||||
|
||||
Binary file not shown.
@@ -251,6 +251,14 @@ class StarPilotGMVehicleLayout(StarPilotPanel):
|
||||
"color": "#FFC40D",
|
||||
"key": "GMPedalLongitudinal",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Smooth Pedal on Hills"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("LongPitch"),
|
||||
"set_state": lambda s: self._params.put_bool("LongPitch", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "LongPitch",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Remote Start Panda"),
|
||||
"type": "toggle",
|
||||
|
||||
@@ -238,8 +238,8 @@ class NetworkInfoPage(NavWidget):
|
||||
self._connect_btn.set_label("connecting...")
|
||||
self._connect_btn.set_enabled(False)
|
||||
elif self._network.is_connected:
|
||||
self._connect_btn.set_label("connected")
|
||||
self._connect_btn.set_enabled(False)
|
||||
self._connect_btn.set_label("disconnect")
|
||||
self._connect_btn.set_enabled(True)
|
||||
elif self._network.security_type == SecurityType.UNSUPPORTED:
|
||||
self._connect_btn.set_label("connect")
|
||||
self._connect_btn.set_enabled(False)
|
||||
@@ -409,6 +409,10 @@ class WifiUIMici(BigMultiOptionDialog):
|
||||
cloudlog.warning(f"Trying to connect to unknown network: {ssid}")
|
||||
return
|
||||
|
||||
if network.is_connected:
|
||||
self._wifi_manager.disconnect_network(network.ssid)
|
||||
return
|
||||
|
||||
if network.is_saved:
|
||||
self._connecting = network.ssid
|
||||
self._wifi_manager.activate_connection(network.ssid)
|
||||
|
||||
@@ -3,6 +3,7 @@ import numpy as np
|
||||
import pyray as rl
|
||||
from cereal import messaging, car, log
|
||||
from msgq.visionipc import VisionStreamType
|
||||
from openpilot.common.constants import CV
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.mici.onroad import SIDE_PANEL_WIDTH
|
||||
from openpilot.selfdrive.ui.mici.onroad.alert_renderer import AlertRenderer
|
||||
@@ -178,6 +179,93 @@ class ExperimentalModeBanner(Widget):
|
||||
self._label.render(banner_rect)
|
||||
|
||||
|
||||
class MinSteerSpeedBanner(Widget):
|
||||
"""One-shot-per-drive banner shown for the full first below-min-steer interval."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._shown_this_drive = False
|
||||
self._showing_interval = False
|
||||
self._has_been_above_min = False
|
||||
self._was_under_min = False
|
||||
self._last_started_frame = -1
|
||||
self._label = UnifiedLabel(
|
||||
"",
|
||||
34,
|
||||
FontWeight.BOLD,
|
||||
text_color=rl.WHITE,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE,
|
||||
)
|
||||
|
||||
def _reset(self):
|
||||
self._shown_this_drive = False
|
||||
self._showing_interval = False
|
||||
self._has_been_above_min = False
|
||||
self._was_under_min = False
|
||||
|
||||
@staticmethod
|
||||
def _get_message(min_steer_speed: float) -> str:
|
||||
speed_units = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH
|
||||
speed = int(round(min_steer_speed * speed_units))
|
||||
unit = "km/h" if ui_state.is_metric else "mph"
|
||||
return f"Steer Unavailable Under {speed} {unit}"
|
||||
|
||||
def _update_state(self):
|
||||
if not ui_state.started:
|
||||
self._last_started_frame = -1
|
||||
self._reset()
|
||||
return
|
||||
|
||||
if ui_state.started_frame != self._last_started_frame:
|
||||
self._last_started_frame = ui_state.started_frame
|
||||
self._reset()
|
||||
|
||||
sm = ui_state.sm
|
||||
if sm.recv_frame["carParams"] < ui_state.started_frame or sm.recv_frame["carState"] < ui_state.started_frame:
|
||||
return
|
||||
|
||||
min_steer_speed = float(sm["carParams"].minSteerSpeed)
|
||||
if min_steer_speed <= 0:
|
||||
self._showing_interval = False
|
||||
self._was_under_min = False
|
||||
return
|
||||
|
||||
under_min = float(sm["carState"].vEgo) < min_steer_speed
|
||||
if not under_min:
|
||||
self._has_been_above_min = True
|
||||
|
||||
crossed_below = under_min and not self._was_under_min
|
||||
if (not self._shown_this_drive) and crossed_below and self._has_been_above_min:
|
||||
self._showing_interval = True
|
||||
self._shown_this_drive = True
|
||||
|
||||
if self._showing_interval and not under_min:
|
||||
self._showing_interval = False
|
||||
|
||||
self._was_under_min = under_min
|
||||
if self._showing_interval:
|
||||
self._label.set_text(self._get_message(min_steer_speed))
|
||||
|
||||
def _render(self, rect):
|
||||
self._update_state()
|
||||
if not self._showing_interval:
|
||||
return
|
||||
|
||||
banner_width = min(rect.width - 120, 760)
|
||||
banner_height = 72
|
||||
banner_rect = rl.Rectangle(
|
||||
rect.x + (rect.width - banner_width) / 2,
|
||||
rect.y + 22,
|
||||
banner_width,
|
||||
banner_height,
|
||||
)
|
||||
|
||||
rl.draw_rectangle_rounded(banner_rect, 0.3, 12, rl.Color(0, 0, 0, 185))
|
||||
rl.draw_rectangle_rounded_lines_ex(banner_rect, 0.3, 12, 4, rl.Color(218, 111, 37, 255))
|
||||
self._label.render(banner_rect)
|
||||
|
||||
|
||||
class AugmentedRoadView(CameraView):
|
||||
def __init__(self, bookmark_callback=None, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD):
|
||||
super().__init__("camerad", stream_type)
|
||||
@@ -204,6 +292,7 @@ class AugmentedRoadView(CameraView):
|
||||
self._driver_state_renderer = DriverStateRenderer()
|
||||
self._confidence_ball = ConfidenceBall()
|
||||
self._experimental_mode_banner = ExperimentalModeBanner()
|
||||
self._min_steer_speed_banner = MinSteerSpeedBanner()
|
||||
self._offroad_label = UnifiedLabel("start the car to\nuse openpilot", 54, FontWeight.DISPLAY,
|
||||
text_color=rl.Color(255, 255, 255, int(255 * 0.9)),
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
@@ -307,6 +396,8 @@ class AugmentedRoadView(CameraView):
|
||||
self._hud_renderer.render(self._content_rect)
|
||||
if (not in_reverse) and alert_to_render is None:
|
||||
self._experimental_mode_banner.render(self._content_rect)
|
||||
if not in_reverse:
|
||||
self._min_steer_speed_banner.render(self._content_rect)
|
||||
|
||||
# End clipping region
|
||||
rl.end_scissor_mode()
|
||||
|
||||
@@ -3,6 +3,7 @@ import numpy as np
|
||||
import pyray as rl
|
||||
from cereal import log, messaging
|
||||
from msgq.visionipc import VisionStreamType
|
||||
from openpilot.common.constants import CV
|
||||
from openpilot.selfdrive.ui import UI_BORDER_SIZE
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
|
||||
from openpilot.selfdrive.ui.onroad.alert_renderer import AlertRenderer
|
||||
@@ -31,6 +32,95 @@ ROAD_CAM_MIN_SPEED = 15.0 # m/s (34 mph)
|
||||
INF_POINT = np.array([1000.0, 0.0, 0.0])
|
||||
|
||||
|
||||
class MinSteerSpeedBanner:
|
||||
"""One-shot-per-drive banner that stays visible for the first below-min-steer interval."""
|
||||
|
||||
def __init__(self):
|
||||
self._shown_this_drive = False
|
||||
self._showing_interval = False
|
||||
self._has_been_above_min = False
|
||||
self._was_under_min = False
|
||||
self._last_started_frame = -1
|
||||
|
||||
def _reset(self):
|
||||
self._shown_this_drive = False
|
||||
self._showing_interval = False
|
||||
self._has_been_above_min = False
|
||||
self._was_under_min = False
|
||||
|
||||
def _get_message(self, min_steer_speed: float) -> str:
|
||||
speed_units = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH
|
||||
speed = int(round(min_steer_speed * speed_units))
|
||||
unit = "km/h" if ui_state.is_metric else "mph"
|
||||
return f"Steer Unavailable Under {speed} {unit}"
|
||||
|
||||
def _update_state(self):
|
||||
if not ui_state.started:
|
||||
self._last_started_frame = -1
|
||||
self._reset()
|
||||
return
|
||||
|
||||
if ui_state.started_frame != self._last_started_frame:
|
||||
self._last_started_frame = ui_state.started_frame
|
||||
self._reset()
|
||||
|
||||
sm = ui_state.sm
|
||||
if sm.recv_frame["carParams"] < ui_state.started_frame or sm.recv_frame["carState"] < ui_state.started_frame:
|
||||
return
|
||||
|
||||
min_steer_speed = float(sm["carParams"].minSteerSpeed)
|
||||
if min_steer_speed <= 0:
|
||||
self._showing_interval = False
|
||||
self._was_under_min = False
|
||||
return
|
||||
|
||||
under_min = float(sm["carState"].vEgo) < min_steer_speed
|
||||
if not under_min:
|
||||
self._has_been_above_min = True
|
||||
|
||||
crossed_below = under_min and not self._was_under_min
|
||||
if (not self._shown_this_drive) and crossed_below and self._has_been_above_min:
|
||||
self._showing_interval = True
|
||||
self._shown_this_drive = True
|
||||
|
||||
if self._showing_interval and not under_min:
|
||||
self._showing_interval = False
|
||||
|
||||
self._was_under_min = under_min
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
self._update_state()
|
||||
|
||||
if not self._showing_interval:
|
||||
return
|
||||
|
||||
min_steer_speed = float(ui_state.sm["carParams"].minSteerSpeed)
|
||||
if min_steer_speed <= 0:
|
||||
return
|
||||
|
||||
banner_width = min(rect.width - 120, 760)
|
||||
banner_height = 84
|
||||
banner_rect = rl.Rectangle(
|
||||
rect.x + (rect.width - banner_width) / 2,
|
||||
rect.y + 24,
|
||||
banner_width,
|
||||
banner_height,
|
||||
)
|
||||
|
||||
rl.draw_rectangle_rounded(banner_rect, 0.3, 12, rl.Color(0, 0, 0, 185))
|
||||
rl.draw_rectangle_rounded_lines_ex(banner_rect, 0.3, 12, 4, rl.Color(218, 111, 37, 255))
|
||||
|
||||
text = self._get_message(min_steer_speed)
|
||||
font = gui_app.font()
|
||||
font_size = 44
|
||||
text_size = rl.measure_text_ex(font, text, font_size, 0)
|
||||
text_pos = rl.Vector2(
|
||||
banner_rect.x + (banner_rect.width - text_size.x) / 2,
|
||||
banner_rect.y + (banner_rect.height - text_size.y) / 2,
|
||||
)
|
||||
rl.draw_text_ex(font, text, text_pos, font_size, 0, rl.WHITE)
|
||||
|
||||
|
||||
class AugmentedRoadView(CameraView):
|
||||
def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD):
|
||||
super().__init__("camerad", stream_type)
|
||||
@@ -48,6 +138,7 @@ class AugmentedRoadView(CameraView):
|
||||
self._hud_renderer = HudRenderer()
|
||||
self.alert_renderer = AlertRenderer()
|
||||
self.driver_state_renderer = DriverStateRenderer()
|
||||
self._min_steer_speed_banner = MinSteerSpeedBanner()
|
||||
|
||||
# debug
|
||||
self._pm = messaging.PubMaster(['uiDebug'])
|
||||
@@ -88,6 +179,7 @@ class AugmentedRoadView(CameraView):
|
||||
self._hud_renderer.render(self._content_rect)
|
||||
self.alert_renderer.render(self._content_rect)
|
||||
self.driver_state_renderer.render(self._content_rect)
|
||||
self._min_steer_speed_banner.render(self._content_rect)
|
||||
|
||||
# Custom UI extension point - add custom overlays here
|
||||
# Use self._content_rect for positioning within camera bounds
|
||||
|
||||
Binary file not shown.
@@ -134,6 +134,7 @@ log_recv_queue: Queue[str] = queue.Queue()
|
||||
cancelled_uploads: set[str] = set()
|
||||
|
||||
cur_upload_items: dict[int, UploadItem | None] = {}
|
||||
params_store = Params()
|
||||
|
||||
|
||||
def strip_zst_extension(fn: str) -> str:
|
||||
@@ -146,6 +147,10 @@ class AbortTransferException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def always_allow_uploads() -> bool:
|
||||
return params_store.get_bool("AlwaysAllowUploads")
|
||||
|
||||
|
||||
class UploadQueueCache:
|
||||
|
||||
@staticmethod
|
||||
@@ -249,7 +254,7 @@ def cb(sm, item, tid, end_event: threading.Event, sz: int, cur: int) -> None:
|
||||
if not item.allow_cellular:
|
||||
if (time.monotonic() - sm.recv_time['deviceState']) > DEVICE_STATE_UPDATE_INTERVAL:
|
||||
sm.update(0)
|
||||
if sm['deviceState'].networkMetered:
|
||||
if sm['deviceState'].networkMetered and not always_allow_uploads():
|
||||
raise AbortTransferException
|
||||
|
||||
if end_event.is_set():
|
||||
@@ -282,7 +287,7 @@ def upload_handler(end_event: threading.Event) -> None:
|
||||
sm.update(0)
|
||||
metered = sm['deviceState'].networkMetered
|
||||
network_type = sm['deviceState'].networkType.raw
|
||||
if metered and (not item.allow_cellular):
|
||||
if metered and (not item.allow_cellular) and not always_allow_uploads():
|
||||
retry_upload(tid, end_event, False)
|
||||
continue
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -251,6 +251,7 @@ def main(exit_event: threading.Event | None = None) -> None:
|
||||
backoff = 0.1
|
||||
while not exit_event.is_set():
|
||||
sm.update(0)
|
||||
always_allow_uploads = params.get_bool("AlwaysAllowUploads")
|
||||
offroad = params.get_bool("IsOffroad")
|
||||
network_type = sm['deviceState'].networkType if not force_wifi else NetworkType.wifi
|
||||
if network_type == NetworkType.none:
|
||||
@@ -258,7 +259,7 @@ def main(exit_event: threading.Event | None = None) -> None:
|
||||
time.sleep(60 if offroad else 5)
|
||||
continue
|
||||
|
||||
success = uploader.step(sm['deviceState'].networkType.raw, sm['deviceState'].networkMetered)
|
||||
success = uploader.step(sm['deviceState'].networkType.raw, sm['deviceState'].networkMetered and not always_allow_uploads)
|
||||
if success is None:
|
||||
backoff = 60 if offroad else 5
|
||||
elif success:
|
||||
|
||||
@@ -722,11 +722,86 @@ class WifiManager:
|
||||
return
|
||||
|
||||
def is_tethering_active(self) -> bool:
|
||||
if self._fake_networking:
|
||||
for network in self._networks:
|
||||
if network.is_connected:
|
||||
return bool(network.ssid == self._tethering_ssid)
|
||||
return False
|
||||
|
||||
if self._nmcli_networking:
|
||||
try:
|
||||
active = subprocess.run(
|
||||
["nmcli", "-t", "-f", "NAME,TYPE,802-11-wireless.ssid", "connection", "show", "--active"],
|
||||
check=False, capture_output=True, text=True,
|
||||
)
|
||||
for line in active.stdout.splitlines():
|
||||
parts = self._parse_nmcli_line(line)
|
||||
if len(parts) < 3 or parts[1] != "802-11-wireless":
|
||||
continue
|
||||
if _canonicalize_ssid(parts[2]) == _canonicalize_ssid(self._tethering_ssid):
|
||||
return True
|
||||
except Exception as e:
|
||||
cloudlog.warning(f"nmcli tethering state lookup failed: {e}")
|
||||
return False
|
||||
|
||||
if not self._dbus_available:
|
||||
return False
|
||||
|
||||
def decode_ssid(value: Any) -> str:
|
||||
if isinstance(value, bytes):
|
||||
return value.decode("utf-8", "replace")
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, list):
|
||||
try:
|
||||
return bytes(value).decode("utf-8", "replace")
|
||||
except Exception:
|
||||
return str(value)
|
||||
return str(value)
|
||||
|
||||
try:
|
||||
for conn_path in self._get_active_connections():
|
||||
conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE)
|
||||
conn_type = self._router_main.send_and_get_reply(Properties(conn_addr).get('Type')).body[0][1]
|
||||
if conn_type != '802-11-wireless':
|
||||
continue
|
||||
|
||||
settings_conn_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('Connection')).body[0][1]
|
||||
if settings_conn_path != "/":
|
||||
settings = self._get_connection_settings(settings_conn_path)
|
||||
ssid_value = settings.get('802-11-wireless', {}).get('ssid')
|
||||
if isinstance(ssid_value, tuple) and len(ssid_value) > 1:
|
||||
ssid = decode_ssid(ssid_value[1])
|
||||
if _canonicalize_ssid(ssid) == _canonicalize_ssid(self._tethering_ssid):
|
||||
return True
|
||||
|
||||
specific_obj_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('SpecificObject')).body[0][1]
|
||||
if specific_obj_path != "/":
|
||||
ap_addr = DBusAddress(specific_obj_path, bus_name=NM, interface=NM_ACCESS_POINT_IFACE)
|
||||
ap_ssid = decode_ssid(self._router_main.send_and_get_reply(Properties(ap_addr).get('Ssid')).body[0][1])
|
||||
if _canonicalize_ssid(ap_ssid) == _canonicalize_ssid(self._tethering_ssid):
|
||||
return True
|
||||
except Exception as e:
|
||||
cloudlog.warning(f"DBus tethering state lookup failed: {e}")
|
||||
|
||||
for network in self._networks:
|
||||
if network.is_connected:
|
||||
return bool(network.ssid == self._tethering_ssid)
|
||||
return False
|
||||
|
||||
def disconnect_network(self, ssid: str, block: bool = False):
|
||||
if not (self._dbus_available or self._fake_networking or self._nmcli_networking):
|
||||
cloudlog.warning("disconnect_network called with no available networking backend")
|
||||
return
|
||||
|
||||
def worker():
|
||||
self._deactivate_connection(ssid)
|
||||
|
||||
if block:
|
||||
worker()
|
||||
else:
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
def set_tethering_password(self, password: str):
|
||||
if self._fake_networking:
|
||||
self._tethering_password = password
|
||||
@@ -863,6 +938,10 @@ class WifiManager:
|
||||
self._enqueue_callbacks(self._networks_updated, self._networks)
|
||||
return
|
||||
|
||||
# Keep UI state responsive while the DBus update completes.
|
||||
self._current_network_metered = metered
|
||||
self._enqueue_callbacks(self._networks_updated, self._networks)
|
||||
|
||||
def worker():
|
||||
for active_conn in self._get_active_connections():
|
||||
conn_addr = DBusAddress(active_conn, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE)
|
||||
|
||||
Reference in New Issue
Block a user