@@ -1695,9 +1695,24 @@ object MG4Hardware {
16951695 return ok
16961696 }
16971697
1698- fun getSeatHeatLeft (): Int = getIntPropertyHvac(PROP_SEAT_HEAT_L , AREA_HVAC ).coerceAtLeast(0 )
1699- fun getSeatHeatRight (): Int = getIntPropertyHvac(PROP_SEAT_HEAT_R , AREA_HVAC ).coerceAtLeast(0 )
1700- fun isSteeringHeatOn (): Boolean = getIntPropertyHvac(PROP_STEERING_HEAT , AREA_HVAC ) > 0
1698+ /*
1699+ * `…OrNull` readers: null means "could not be read", which is not the same as off/zero.
1700+ *
1701+ * A consumer that drives a switch (MG4Control) wants a Boolean and treats unreadable as
1702+ * off; a consumer that evaluates a rule (MG4Tasker) must not, or a car whose firmware
1703+ * never answers looks exactly like a car with the feature turned off — and the rule
1704+ * writes on that assumption. So the routing lives in the nullable reader and the
1705+ * historical non-null getter is a thin `?: false` / `?: 0` over it, unchanged for its
1706+ * callers.
1707+ */
1708+ fun seatHeatLeftOrNull (): Int? = getIntPropertyHvac(PROP_SEAT_HEAT_L , AREA_HVAC ).takeIf { it >= 0 }
1709+ fun seatHeatRightOrNull (): Int? = getIntPropertyHvac(PROP_SEAT_HEAT_R , AREA_HVAC ).takeIf { it >= 0 }
1710+ fun steeringHeatOnOrNull (): Boolean? =
1711+ getIntPropertyHvac(PROP_STEERING_HEAT , AREA_HVAC ).takeIf { it >= 0 }?.let { it > 0 }
1712+
1713+ fun getSeatHeatLeft (): Int = seatHeatLeftOrNull() ? : 0
1714+ fun getSeatHeatRight (): Int = seatHeatRightOrNull() ? : 0
1715+ fun isSteeringHeatOn (): Boolean = steeringHeatOnOrNull() ? : false
17011716
17021717 fun getDriveMode (): DriveMode ? {
17031718 val cpm = sCarPropertyManager ? : return null
@@ -1747,7 +1762,7 @@ object MG4Hardware {
17471762 // ADAS API (Katman4)
17481763 // -------------------------------------------------------------------------
17491764
1750- fun isOverspeedAlarmOn (): Boolean {
1765+ fun overspeedAlarmOnOrNull (): Boolean? {
17511766 if (FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 ) {
17521767 // VSM priority (CarVehicleSettingClient) — confirmed in SWI132 smali
17531768 // getOverSpeedSoundMode() : 0=OFF, 1/2/3=ON
@@ -1756,11 +1771,13 @@ object MG4Hardware {
17561771 AppLogger .d(TAG , " SWI132 overspeed GET via VSM → $vsm " )
17571772 return vsm > 0
17581773 }
1759- return swi132BinderGet(VSM132_TX_GET_OVERSPEED ) == 1
1774+ return swi132BinderGet(VSM132_TX_GET_OVERSPEED ). takeIf { it >= 0 }?. let { it == 1 }
17601775 }
1761- return getIntPropertyVpm(PROP_OVERSPEED_ALARM ) > 0
1776+ return getIntPropertyVpm(PROP_OVERSPEED_ALARM ). takeIf { it >= 0 }?. let { it > 0 }
17621777 }
17631778
1779+ fun isOverspeedAlarmOn (): Boolean = overspeedAlarmOnOrNull() ? : false
1780+
17641781 @RequiresStandstill
17651782 fun setOverspeedAlarm (on : Boolean ): Boolean {
17661783 if (logEnabled) AppLogger .i(TAG , " setOverspeedAlarm → $on " )
@@ -1780,19 +1797,21 @@ object MG4Hardware {
17801797 return setIntPropertyVpm(PROP_OVERSPEED_ALARM , if (on) 1 else 0 )
17811798 }
17821799
1783- fun isSpeedLimitToneOn (): Boolean {
1800+ fun speedLimitToneOnOrNull (): Boolean? {
17841801 if (FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 ) {
17851802 // getSpeedLimitSoundMode() : 0=OFF, value positive=ON
17861803 val vsm = callVsm(" getSpeedLimitSoundMode" ) as ? Int
17871804 if (vsm != null ) {
17881805 AppLogger .d(TAG , " SWI132 speedLimit GET via VSM → $vsm " )
17891806 return vsm > 0
17901807 }
1791- return swi132BinderGet(VSM132_TX_GET_SPEED_LIMIT ) == 1
1808+ return swi132BinderGet(VSM132_TX_GET_SPEED_LIMIT ). takeIf { it >= 0 }?. let { it == 1 }
17921809 }
1793- return getIntPropertyVpm(PROP_SPEED_LIMIT_TONE ) > 0
1810+ return getIntPropertyVpm(PROP_SPEED_LIMIT_TONE ). takeIf { it >= 0 }?. let { it > 0 }
17941811 }
17951812
1813+ fun isSpeedLimitToneOn (): Boolean = speedLimitToneOnOrNull() ? : false
1814+
17961815 @RequiresStandstill
17971816 fun setSpeedLimitTone (on : Boolean ): Boolean {
17981817 if (logEnabled) AppLogger .i(TAG , " setSpeedLimitTone → $on " )
@@ -1874,12 +1893,14 @@ object MG4Hardware {
18741893 * SWI69/SWI131/SWI132 : getLasWarningSound() (confirmed dans smali SWI132)
18751894 * Values : 2=ON / 1=OFF
18761895 */
1877- fun isSoundWarningOn (): Boolean {
1896+ fun soundWarningOnOrNull (): Boolean? {
18781897 val method = if (FirmwareInfo .isNewGenVsm() || FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 )
18791898 " getLasWarningSound" else " getLaneKeepingWarningSound"
1880- return (( callVsm(method) as ? Int ) ? : 1 ) == 2
1899+ return (callVsm(method) as ? Int )?. let { it == 2 }
18811900 }
18821901
1902+ fun isSoundWarningOn (): Boolean = soundWarningOnOrNull() ? : false
1903+
18831904 /* * SWI68 : setLaneKeepingWarningSound(I) SWI69/SWI131/SWI132 : setLasWarningSound(I) — void */
18841905 @RequiresStandstill
18851906 fun setSoundWarning (on : Boolean ): Boolean {
@@ -1898,16 +1919,18 @@ object MG4Hardware {
18981919 * Verified in SafeSettingsRepository SWI165 — same API as SWI68.
18991920 * SWI69 / SWI131 : getFcwState() — 1=DISABLED, 2=ENABLED
19001921 */
1901- fun isAebEnabled (): Boolean {
1922+ fun aebEnabledOrNull (): Boolean? {
19021923 return when {
19031924 // SWI69 / SWI131 / SWI132 — CarVehicleSettingClient : getFcwState() (1=OFF, 2=ON)
19041925 FirmwareInfo .isNewGenVsm() || FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 ->
1905- (callVsm(" getFcwState" ) as ? Int ) == 2
1906- FirmwareInfo .isVsmBased() -> (callVsm(" getFcwAlarmMode" ) as ? Int ) == 2 // SWI68 / SWI165
1907- else -> getIntPropertyCPM(PROP_AEB_SWITCH , AREA_GLOBAL ) == 0x2 // SWI133
1926+ (callVsm(" getFcwState" ) as ? Int )?. let { it == 2 }
1927+ FirmwareInfo .isVsmBased() -> (callVsm(" getFcwAlarmMode" ) as ? Int )?. let { it == 2 } // SWI68 / SWI165
1928+ else -> getIntPropertyCPM(PROP_AEB_SWITCH , AREA_GLOBAL ). takeIf { it >= 0 }?. let { it == 0x2 } // SWI133
19081929 }
19091930 }
19101931
1932+ fun isAebEnabled (): Boolean = aebEnabledOrNull() ? : false
1933+
19111934 @RequiresStandstill
19121935 fun setAebEnabled (on : Boolean ): Boolean {
19131936 if (logEnabled) AppLogger .i(TAG , " setAebEnabled → $on " )
@@ -1958,6 +1981,15 @@ object MG4Hardware {
19581981 }
19591982 }
19601983
1984+ /* *
1985+ * Same routing as [getAebMode], without the ALARM default the VSM branch falls back to.
1986+ * That default is a sane switch position for a UI, and a lie for a rule: it would make
1987+ * "AEB mode is Alert" true on a car that never answered.
1988+ */
1989+ fun aebModeOrNull (): Int? =
1990+ if (FirmwareInfo .isVsmBased()) (callVsm(" getFcwAutoBrakeMode" ) as ? Int )?.takeIf { it > 0 }
1991+ else getIntPropertyVpm(PROP_AEB_MODE ).takeIf { it >= 1 }
1992+
19611993 @RequiresStandstill
19621994 fun setAebMode (mode : Int ): Boolean {
19631995 if (logEnabled) AppLogger .i(TAG , " setAebMode → $mode " )
@@ -2308,23 +2340,26 @@ object MG4Hardware {
23082340 // TSR — Reconnaissance des panneaux de speed
23092341 // -------------------------------------------------------------------------
23102342
2311- fun isTsrOn (): Boolean = when {
2343+ fun tsrOnOrNull (): Boolean? = when {
23122344 FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 ->
23132345 // CarVehicleSettingClient priority (vehiclesetting binder SELinux-blocked)
23142346 // Convention identique SWI69/SWI131 : 0=ON, 1=OFF
23152347 (callVsm(" getSLIFWarningState" ) as ? Int )?.let { raw ->
23162348 AppLogger .d(TAG , " SWI132 TSR GET via VSM → $raw " )
23172349 raw == 0
2318- } ? : (swi132BinderGet(VSM132_TX_GET_SLIF ) == 1 ) // fallback binder (rarement accessible)
2350+ // fallback binder (rarement accessible)
2351+ } ? : swi132BinderGet(VSM132_TX_GET_SLIF ).takeIf { it >= 0 }?.let { it == 1 }
23192352 FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI133 ->
2320- getIntPropertyVpm(PROP_TSR_MODE ) > 0
2353+ getIntPropertyVpm(PROP_TSR_MODE ). takeIf { it >= 0 }?. let { it > 0 }
23212354 FirmwareInfo .isNewGenVsm() -> // SWI69 + SWI131 — convention invertede : 0=ON, 1=OFF
2322- (callVsm(" getSLIFWarningState" ) as ? Int ) == 0
2355+ (callVsm(" getSLIFWarningState" ) as ? Int )?. let { it == 0 }
23232356 FirmwareInfo .isVsmBased() -> // SWI68 + SWI165
2324- (callVsm(" getSpeedAsstSlifWarning" ) as ? Int ) == 1
2325- else -> false
2357+ (callVsm(" getSpeedAsstSlifWarning" ) as ? Int )?. let { it == 1 }
2358+ else -> null
23262359 }
23272360
2361+ fun isTsrOn (): Boolean = tsrOnOrNull() ? : false
2362+
23282363 @RequiresStandstill
23292364 fun setTsrMode (enabled : Boolean ): Boolean {
23302365 AppLogger .i(TAG , " setTsrMode → $enabled " )
@@ -2410,16 +2445,18 @@ object MG4Hardware {
24102445 // Energy saving (Endurance Mode)
24112446 // -------------------------------------------------------------------------
24122447
2413- fun isEnergySavingOn (): Boolean = when {
2448+ fun energySavingOnOrNull (): Boolean? = when {
24142449 FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI133 ->
2415- getIntPropertyVpm(PROP_ENERGY_SAVING ) == 1
2450+ getIntPropertyVpm(PROP_ENERGY_SAVING ). takeIf { it >= 0 }?. let { it == 1 }
24162451 FirmwareInfo .isNewGenVsm() || FirmwareInfo .getGeneration() == FirmwareInfo .Gen .SWI132 -> // SWI69 + SWI131 + SWI132
2417- (callVsm(" getEnduranceMode" ) as ? Int ) == 1
2452+ (callVsm(" getEnduranceMode" ) as ? Int )?. let { it == 1 }
24182453 FirmwareInfo .isVsmBased() -> // SWI68 + SWI165
2419- (callVsm(" getLongerEndurance" ) as ? Int ) == 1
2420- else -> false
2454+ (callVsm(" getLongerEndurance" ) as ? Int )?. let { it == 1 }
2455+ else -> null
24212456 }
24222457
2458+ fun isEnergySavingOn (): Boolean = energySavingOnOrNull() ? : false
2459+
24232460 @RequiresStandstill
24242461 fun setEnergySavingMode (enabled : Boolean ): Boolean {
24252462 AppLogger .i(TAG , " setEnergySavingMode → $enabled " )
0 commit comments