You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9f5b4e0
Browse filesBrowse the repository at this point in the historyBrowse files
@@ -192,6 +193,13 @@ switching the climate on, then setting the fan level — the **Wait** action (1
192
193
holds the sequence in between. It touches nothing in the car, and appears in the history at
193
194
its place in the sequence. A rule made only of waits is refused at save time: it would hold
194
195
the cycle and change nothing.
196
+
197
+
One cycle may spend **two minutes waiting in total**, every wait of every rule it evaluates
198
+
counted together — the rules of a cycle run one after the other, so a rule that waits also
199
+
delays the ones after it. Past that budget the wait is cut short and the history says so:
200
+
what follows would otherwise be applied against a snapshot taken before the driver left the
201
+
car. A single rule whose waits exceed the budget is refused at save time, so the limit is
202
+
learned in the editor rather than after the drive.
195
203
-**15 minutes, and never across an ignition cycle.** A rule that fired for this drive
196
204
belongs to this drive. At switch-off the queue is dropped, not applied: the car is stopped
197
205
then, which is exactly what makes it the wrong moment to change the setting the next
@@ -241,6 +249,48 @@ below.
241
249
242
250
---
243
251
252
+
## Cases: if, else if, else
253
+
A rule can carry several cases. They are tried in the order they are written and **exactly
254
+
one of them runs**: the first whose conditions hold wins, and the "else" is what happens when
255
+
none did.
256
+
257
+
```
258
+
IF outside temperature < 5 °C → steering heating 2, seat heating 2
259
+
ELSE IF outside temperature < 18 °C → fan level 1
260
+
ELSE → media volume 12
261
+
```
262
+
263
+
Without cases, that is three rules whose conditions have to exclude each other by hand, and
264
+
the day one range is edited the three stop agreeing. The "else" is optional: a rule that says
265
+
nothing about the remaining situations simply does nothing in them.
266
+
267
+
**An unreadable reading stops the rule where it stands.** It does not fall through to the next
268
+
case, and it does not reach the "else". Unreadable is not false — falling through would mean
269
+
writing to the vehicle *because* a value was missing, which is the one thing the engine never
270
+
does. The history reports the rule as not evaluable and names the missing signal, exactly as
271
+
it does for a rule with a single case.
272
+
273
+
**Each case is edited in its own window**, opened from its card on the rule screen: what is
274
+
checked on the left, what is done on the right, each list with its own scroll and its own
275
+
"add" button. The rule screen itself keeps the shape of the rule — the cases in the order they
276
+
are tried, each with the size of what it checks and of what it does — and the order of the
277
+
"else if" cases is changed with the arrows on their cards. A rule takes at most four "else if"
278
+
cases: past that the chain stops being readable at a glance on a screen at arm's length.
279
+
280
+
The history names the case that ran ("applied — ELSE IF 1"), because "applied" alone leaves
281
+
the one question a branched rule raises after the fact unanswered.
282
+
283
+
**A rule that names a physical button must name one in every case, and takes no "else".** A
284
+
button condition does not only test something — it decides which event stream brings the rule
285
+
its cycles, for the whole rule. A case that named no button would be evaluated on presses of
286
+
buttons it never mentioned, and an "else", which tests nothing at all, would run on every
287
+
press of every button. Refused at save and at import rather than silently rewired.
288
+
289
+
The wait budget compares the **longest** case, not the sum of them: only one case runs, so
290
+
adding them up would refuse a rule that can never wait that long.
291
+
292
+
---
293
+
244
294
## Firmware compatibility
245
295
Every vehicle catalogue entry carries a `@SupportedOn(...)` annotation naming the firmware
246
296
generations it works on, derived from MG4Hardware's `FirmwareInfo` and per-generation
@@ -258,6 +308,14 @@ routing. That annotation is the single source of truth for three things:
258
308
> Diagnostic tab is the source of truth for your own car: it reads each signal and shows
259
309
> "unreadable" where the firmware does not expose it.
260
310
311
+
**When the generation cannot be identified, the editor still offers everything and execution
312
+
refuses.** Hiding entries on a guess would leave a car unable to write rules it can perfectly
313
+
well run, so the picker filters nothing without a positive match. A vehicle action, on the
314
+
other hand, is only attempted on a generation its annotation actually names: anything else is
315
+
reported as `firmware support not confirmed` in the history rather than written to a car we
316
+
could not identify. The Diagnostic tab reports the same verdict, because it runs the same
317
+
check.
318
+
261
319
Highlights (see the generated matrix for the full grid):
262
320
263
321
-**Seat / steering heating** — SWI133, SWI68, SWI165 only (the SWI69/131 trims lack the
@@ -331,6 +389,10 @@ The one step it cannot take is the write itself — applying a drive mode to see
331
389
sticks would change the car under the driver. So for vehicle writes, OK means "everything
332
390
checked before writing passes", and the history reports what the write did afterwards.
333
391
392
+
**The first launch after an upgrade opens on this tab**, because a new build's verdicts about
393
+
this car are what changed. The marker is written by the main screen, never by a background
394
+
receiver: a package replacement must not put an activity in front of a driver who is moving.
395
+
334
396
Three sections:
335
397
336
398
-**Execution context** — the prerequisites that decide whether rules run at all: vehicle
@@ -475,8 +537,8 @@ against the path the user picked:
475
537
|---|---|
476
538
| written by a newer version |`version` above the one this build understands |
477
539
| unknown condition or action | a catalogue entry this build cannot perform — never silently dropped |
478
-
| not a readable rules file | malformed, blank name, no condition, no action, duplicate ids, non-finite threshold, weekday outside 1–7 |
479
-
| more than 20 rules | above `RuleStore.MAX_RULES`|
540
+
| not a readable rules file | malformed, blank name, no condition, no action, duplicate ids, non-finite threshold, weekday outside 1–7, a case with no condition or no action, a button rule whose cases do not all name a button, cases in a file claiming version 1|
541
+
| more than 20 rules | above `RuleStore.MAX_RULES`, or a rule with more than four "else if" cases|
480
542
| no rules | a valid envelope with an empty list |
481
543
482
544
Picking JSON that belongs to another app reports "not a readable rules file". Files over 256 KB
@@ -487,17 +549,30 @@ are refused on their length, before anything is read into memory.
0 commit comments