22
33<p align =" center " ><img src =" docs/logo.svg " width =" 440 " alt =" MG4Tasker " ></p >
44
5- [ ![ Tests] ( ../../actions/workflows/tests.yml/badge.svg )] ( ../../actions/workflows/tests.yml )
6- [ ![ Security] ( ../../actions/workflows/security.yml/badge.svg )] ( ../../actions/workflows/security.yml )
7- [ ![ Release] ( ../../actions/workflows/release.yml/badge.svg )] ( ../../actions/workflows/release.yml )
5+ [ ![ Tests] ( https://github.com/malys/MG4Tasker/actions/workflows/tests.yml/badge.svg )] ( https://github.com/malys/MG4Tasker/actions/workflows/tests.yml )
6+ [ ![ Security] ( https://github.com/malys/MG4Tasker/actions/workflows/security.yml/badge.svg )] ( https://github.com/malys/MG4Tasker/actions/workflows/security.yml )
7+ [ ![ Unstable] ( https://github.com/malys/MG4Tasker/actions/workflows/unstable.yml/badge.svg )] ( https://github.com/malys/MG4Tasker/actions/workflows/unstable.yml )
8+ [ ![ Release] ( https://img.shields.io/github/v/release/malys/MG4Tasker?include_prereleases& ; sort=semver )] ( https://github.com/malys/MG4Tasker/releases )
89[ ![ License: MIT] ( https://img.shields.io/badge/License-MIT-blue.svg )] ( LICENSE )
910
1011> ⚠️ ** This app changes vehicle settings automatically and runs on a car.** Read
@@ -79,15 +80,25 @@ MG4Tasker runs its **own** persistent service: it initialises MG4Hardware, liste
7980ignition directly (no dependency on MG4Control), reads the vehicle and applies the rules'
8081actions through MG4Hardware. Started at boot and on app open.
8182
82- ** Triggers: vehicle start and vehicle switch-off.** Each rule chooses one; a rule written
83- before triggers existed runs at start, as it always did. Switching off costs nothing extra —
83+ ** Triggers: vehicle start and vehicle switch-off.** Physical buttons are conditions, not a
84+ third “Runs at” choice: adding one makes that rule event-driven and hides the ignition choice.
85+ Button rules can choose short or long press on phone, center, directional/OK, source,
86+ volume up/down, next/previous, mute, left star, right star, or assistant. Decoding and press-state handling live in the
87+ shared MG4Hardware library.
88+ A long press fires once and suppresses the following short-release event. The known right-button
89+ codes (` 286 ` and ` 18 ` ) are both accepted for firmware compatibility; the assistant uses the
90+ OEM voice keycode ` 287 ` . The SAIC broadcast is
91+ accepted only from a signature-authorized sender because the action itself is not protected.
92+
93+ Each ignition rule chooses one trigger; a rule written before triggers existed runs at start,
94+ as it always did. Switching off costs nothing extra —
8495the service already received every ignition transition and simply stopped reading at RUN, so
8596there is no second listener, no extra bind and no polling. At switch-off the car is powering
8697down: settings that persist (charge limit, locks, windows) land, and anything the vehicle
8798drops with the ignition is reported by the history rather than assumed.
8899
89- The "Test now" button replays the whole path on demand and addresses ** every ** rule whatever
90- it is wired to — that is what testing means . A master switch on the Rules screen disables
100+ The "Test now" button replays the whole path on demand for the ** selected rule only ** , while
101+ ignoring its ignition trigger . A master switch on the Rules screen disables
91102automation without deleting rules.
92103
93104---
@@ -129,7 +140,7 @@ an action is refused, the history gives the exact reason instead of staying sile
129140
130141### Raising the threshold
131142
132- The Rules screen lets you move the limit from 0 to 50 km/h. ** 0 — apply only when stopped —
143+ The Configuration tab lets you move the limit from 0 to 50 km/h. ** 0 — apply only when stopped —
133144is the default** , and the value that needs no justification: a car creeping in a car park
134145reads as moving, and a rule refused there is the most common false negative.
135146
@@ -169,6 +180,18 @@ Gate refusals are now kept and re-attempted at the first standstill. Three limit
169180
170181- ** Only gate refusals.** "Moving" and "speed unreadable" mean * not now* . An unsupported
171182 action or a failed write is not retried — waiting adds no missing firmware feature.
183+
184+ Transient action failures are different: each failed action is retried independently up to
185+ three times with exponential backoff (250 ms, then 500 ms). Actions that already succeeded
186+ are never replayed.
187+
188+ A rule's actions run ** one after the other, in the order shown in the editor** , and each one
189+ is finished before the next starts. That order is part of the rule: the arrows on every
190+ action row move it up or down. Where one write must land before the next is asked for —
191+ switching the climate on, then setting the fan level — the ** Wait** action (1 to 60 seconds)
192+ holds the sequence in between. It touches nothing in the car, and appears in the history at
193+ its place in the sequence. A rule made only of waits is refused at save time: it would hold
194+ the cycle and change nothing.
172195- ** 15 minutes, and never across an ignition cycle.** A rule that fired for this drive
173196 belongs to this drive. At switch-off the queue is dropped, not applied: the car is stopped
174197 then, which is exactly what makes it the wrong moment to change the setting the next
@@ -200,7 +223,7 @@ temperature, A/C, AUTO, recirculation, fan level, front and rear defrosters), **
200223(charge limit, allow charging, scheduled charging and its window, battery pre-heating),
201224** audio** (volume, the fine controls, play the radio), ** driver assistance** , and ** system**
202225(launch an app, show a message, speak through the head unit's text-to-speech engine,
203- navigate to a destination, call a number). ADAS state — AEB, ELK, ACC/TJA, TSR, overspeed and
226+ navigate to a destination, call a number, wait ). ADAS state — AEB, ELK, ACC/TJA, TSR, overspeed and
204227so on — is fully covered as gated actions.
205228
206229** Near a place** compares the car's last known fix with a point stored in the rule and a
@@ -224,7 +247,7 @@ generations it works on, derived from MG4Hardware's `FirmwareInfo` and per-gener
224247routing. That annotation is the single source of truth for three things:
225248
2262491 . ** Self-documenting source** — the support set sits next to the entry.
227- 2 . ** The compatibility matrix** — [ docs/firmware-matrix.md] ( docs/firmware-matrix.md ) is
250+ 2 . ** The compatibility matrix** — [ MG4Hardware/ docs/firmware-matrix.md] ( MG4Hardware/ docs/firmware-matrix.md) is
228251 * generated* from the annotations by ` FirmwareMatrix ` , checked by a unit test. It is
229252 never edited by hand.
2302533 . ** The runtime filter** — the editor offers only the entries supported on the connected
@@ -245,8 +268,7 @@ Highlights (see the generated matrix for the full grid):
245268- ** Lane-departure sound + vibration** — SWI132 only.
246269- ** Fine audio** (Bose, balance, fader, tone, 3D, speed volume) — SWI69, SWI131, SWI132: the
247270 SAIC ` caradapter ` audio helper these go through is bound on the A9 platform only.
248- - ** Climate, charging, radio and calls** — SWI68 and SWI165, the generations the decompiled
249- head-unit APKs in ` apks/ ` come from. They run on SAIC vendor services rather than on
271+ - ** Climate, charging, radio and calls** — SWI68 and SWI165. They run on SAIC vendor services rather than on
250272 property ids, and whether a given car answers is a bind, not a table: the Diagnostic tab's
251273 * Vehicle services* row reports which of the four responded, and the matrix widens once a
252274 car outside that set reports one.
@@ -255,12 +277,12 @@ Highlights (see the generated matrix for the full grid):
255277
256278## Vehicle services: climate, charging, radio, calls
257279Climate and charging used to be read-only and unverified — standard AOSP property ids the
258- R69 sources name, that no MG4 confirmed. Writing one of those would have been a guess, so
280+ that no MG4 confirmed. Writing one of those would have been a guess, so
259281there were no write actions.
260282
261283They now go through the ** SAIC vendor services** instead: the same binder interfaces the
262- car's own HVAC, charging, radio and hands-free apps use, read off the decompiled head-unit
263- APKs in [ ` apks/ ` ] ( ../apks ) . One bound service (` com.saicmotor.service.vehicle ` ) exposes a hub
284+ car's own HVAC, charging, radio and hands-free apps use. One bound service
285+ (` com.saicmotor.service.vehicle ` ) exposes a hub
264286that hands out ` aircondition ` , ` vehiclecharging ` , ` vehiclecontrol ` and ` vehiclecondition ` ;
265287radio and telephony are their own services. What those calls do is not in doubt — they are what the car does to itself.
266288
@@ -277,6 +299,9 @@ That buys four things the property ids could not:
277299 DAB is out: ` tuneDab ` takes a service and ensemble id, not a frequency.
278300- ** Calls** — placed by the car's hands-free stack on the paired phone. The head unit has no
279301 SIM and no dialer, so ` ACTION_CALL ` would find nothing to handle it.
302+ A rule can store a typed number or select a name/number from the phone book explicitly
303+ shared over Bluetooth PBAP. Contact access is requested only while configuring that action;
304+ execution uses the stored number and does not need the phone book to remain available.
280305
281306Climate ** conditions** now read from the same service where it answers, falling back to the
282307AOSP ids elsewhere — so what a rule tests and what an action writes are the same signal.
@@ -285,6 +310,8 @@ Window **writes** stay absent: no vendor service exposes them.
285310
286311Navigation is the exception with no vendor API — ` NAVIGATE_TO ` uses the standard ` geo: `
287312intent, then ` google.navigation: ` , and reports honestly when the head unit has neither.
313+ Its destination can be either ` latitude,longitude ` or a place name/address. HTTPS webhooks
314+ are available as GET and POST actions; POST accepts an optional JSON body.
288315
289316---
290317
@@ -323,7 +350,7 @@ reach it.
323350### Testing a rule
324351
325352** Test now** on the Rules screen runs one full cycle — the exact ignition path, so a passing
326- test proves the real thing — and then ** shows what it decided** : one line per rule with its
353+ test proves the real thing — and then ** shows what it decided** for that rule: its
327354outcome (applied, conditions not met, cannot be evaluated, failed), the per-action verdict
328355behind a failure, and the name of any signal that could not be read. It used to say "test
329356running" and stop there, which answers nothing for a button whose whole purpose is to answer
@@ -397,7 +424,7 @@ unparsed. Import stays the deliberate way to replace the set.
397424---
398425
399426## Import and export (USB stick)
400- ** Export** and ** Import** on the Rules screen move the whole rule set to and from a JSON
427+ ** Export** and ** Import** , on the Configuration tab, move the whole rule set to and from a JSON
401428file — a backup before a reinstall, or the same rules on a second car, without retyping
402429anything on the on-screen keyboard.
403430
@@ -521,7 +548,7 @@ app/src/main/java/com/mg4/tasker/
521548 model/ Rule, Condition, Action, catalogues, @SupportedOn, FirmwareMatrix
522549 engine/ condition and rule evaluation — no Android dependency
523550 store/ JSON persistence of rules and history, rules file format, storage browsing
524- ui/ list, generic editor, history, diagnostic, console, file browser
551+ ui/ list, generic editor, configuration, history, diagnostic, console, file browser
525552 service/ run cycle (foreground service)
526553 receiver/ ignition wake-up
527554 debug/ AppLogger ring buffer, CrashLogger, diagnostic verdicts + report export
0 commit comments