Skip to content

Commit aeafcf3

Browse files
committed
fix(rules): restore export compatibility
Accept the pre-rename MG4Tasker format marker and preserve all action fields during export. Include a disabled example rule set for safe review before activation.
1 parent 2adaf08 commit aeafcf3

7 files changed

Lines changed: 114 additions & 5 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project are documented here. Format follows
44
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
55
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.1.4] - 2026-08-22
8+
9+
### Fixed
10+
11+
- Rule exports made before the MG4Tasker to EVTasker rename are importable again. The
12+
importer accepts the former `mg4tasker-rules` format marker while new exports keep the
13+
`evtasker-rules` marker.
14+
- Rule exports preserve action time ranges, webhook payloads and contact display names.
15+
16+
### Added
17+
18+
- An import-ready, disabled-by-default example set with useful rules for display brightness,
19+
morning media volume and an Eco/high-regeneration parked start.
20+
721
## [2.1.3] - 2026-08-22
822

923
### Fixed

‎README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,11 @@ refuses it whole rather than applying its first case alone.
563563
Renaming a `ConditionType` / `ActionType` constant therefore invalidates older files — the
564564
import reports the unknown entry rather than guessing.
565565

566+
An import-ready [example rule set](examples/useful-rules.json) contains three broadly useful
567+
starting points. They are disabled on import so each driver can review the values before
568+
enabling them. Exports from the former MG4Tasker name (`mg4tasker-rules`) remain accepted;
569+
new exports always use `evtasker-rules`.
570+
566571
---
567572

568573
## Install

‎app/build.gradle.kts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId = "com.evsuite.tasker"
1111
minSdk = 28
1212
targetSdk = 34
13-
versionCode = 7
14-
versionName = "2.1.3"
13+
versionCode = 8
14+
versionName = "2.1.4"
1515
}
1616

1717
// EVTasker doit être signé avec la MÊME clé plateforme que EVProfile : c'est la seule

‎app/src/main/java/com/evsuite/tasker/store/RuleTransfer.kt‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ object RuleTransfer {
3030

3131
const val FORMAT = "evtasker-rules"
3232

33+
/** Format written before MG4Tasker was renamed to EVTasker. Import remains compatible. */
34+
private const val LEGACY_FORMAT = "mg4tasker-rules"
35+
3336
/** The format this build writes and reads. Version 2 added the "else if" / "else" branches. */
3437
const val VERSION = 2
3538

@@ -107,7 +110,11 @@ object RuleTransfer {
107110
type = action.type.name,
108111
number = action.number,
109112
flag = action.flag,
110-
text = action.text
113+
text = action.text,
114+
payload = action.payload,
115+
displayName = action.displayName,
116+
minutesFrom = action.minutesFrom,
117+
minutesTo = action.minutesTo
111118
)
112119

113120
fun decode(json: String): Result {
@@ -119,7 +126,9 @@ object RuleTransfer {
119126
return Result.NotARulesFile
120127
} ?: return Result.NotARulesFile
121128

122-
if (envelope.format != FORMAT) return Result.NotARulesFile
129+
if (envelope.format != FORMAT && envelope.format != LEGACY_FORMAT) {
130+
return Result.NotARulesFile
131+
}
123132

124133
val version = envelope.version ?: return Result.Invalid(Reason.MALFORMED)
125134
if (version > VERSION) return Result.Invalid(Reason.VERSION)
@@ -262,6 +271,8 @@ object RuleTransfer {
262271
number = raw.number ?: 0,
263272
flag = raw.flag ?: true,
264273
text = raw.text.orEmpty(),
274+
payload = raw.payload,
275+
displayName = raw.displayName,
265276
minutesFrom = raw.minutesFrom ?: 0,
266277
minutesTo = raw.minutesTo ?: 0
267278
)
@@ -318,6 +329,8 @@ object RuleTransfer {
318329
val number: Int? = null,
319330
val flag: Boolean? = null,
320331
val text: String? = null,
332+
val payload: String? = null,
333+
val displayName: String? = null,
321334
val minutesFrom: Int? = null,
322335
val minutesTo: Int? = null
323336
)

‎app/src/test/java/com/evsuite/tasker/store/RuleFilesTest.kt‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,14 @@ class RuleFilesTest {
112112

113113
assertEquals(RuleTransfer.Result.NotARulesFile, RuleFiles.read(other))
114114
}
115+
116+
@Test
117+
fun `the bundled useful rules example is importable`() {
118+
val example = File("../examples/useful-rules.json")
119+
120+
assertTrue(example.absolutePath, example.isFile)
121+
val imported = RuleFiles.read(example) as RuleTransfer.Result.Ok
122+
assertEquals(3, imported.rules.size)
123+
assertTrue(imported.rules.all { !it.enabled })
124+
}
115125
}

‎app/src/test/java/com/evsuite/tasker/store/RuleTransferTest.kt‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,35 @@ class RuleTransferTest {
4949

5050
@Test
5151
fun `round trip preserves every field`() {
52-
val rules = listOf(rule(), rule(id = "r2", name = "Weekend"))
52+
val rules = listOf(
53+
rule().copy(
54+
actions = listOf(
55+
Action(
56+
type = ActionType.WEBHOOK,
57+
text = "https://example.invalid/hook",
58+
payload = "{\"ready\":true}",
59+
displayName = "Example webhook",
60+
minutesFrom = 1320,
61+
minutesTo = 360
62+
)
63+
)
64+
),
65+
rule(id = "r2", name = "Weekend")
66+
)
5367

5468
val decoded = decodeOf(RuleTransfer.encode(rules))
5569

5670
assertEquals(RuleTransfer.Result.Ok(rules), decoded)
5771
}
5872

73+
@Test
74+
fun `an MG4Tasker export remains importable after the rename`() {
75+
val legacy = RuleTransfer.encode(listOf(rule()))
76+
.replace("\"format\":\"evtasker-rules\"", "\"format\":\"mg4tasker-rules\"")
77+
78+
assertEquals(RuleTransfer.Result.Ok(listOf(rule())), decodeOf(legacy))
79+
}
80+
5981
@Test
6082
fun `json from another app is not a rules file`() {
6183
assertEquals(RuleTransfer.Result.NotARulesFile, decodeOf("""{"some":"other file"}"""))

‎examples/useful-rules.json‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"format": "evtasker-rules",
3+
"version": 1,
4+
"rules": [
5+
{
6+
"id": "example-night-display",
7+
"name": "Night: dim display",
8+
"enabled": false,
9+
"match": "ALL",
10+
"conditions": [
11+
{ "type": "TIME_OF_DAY", "op": "EQ", "minutesFrom": 1200, "minutesTo": 390 }
12+
],
13+
"actions": [
14+
{ "type": "SET_SCREEN_BRIGHTNESS", "number": 25 }
15+
]
16+
},
17+
{
18+
"id": "example-quiet-morning",
19+
"name": "Weekday morning: quiet media",
20+
"enabled": false,
21+
"match": "ALL",
22+
"conditions": [
23+
{ "type": "TIME_OF_DAY", "op": "EQ", "minutesFrom": 360, "minutesTo": 540 },
24+
{ "type": "DAY_OF_WEEK", "op": "EQ", "days": [2, 3, 4, 5, 6] }
25+
],
26+
"actions": [
27+
{ "type": "SET_MEDIA_VOLUME", "number": 8 }
28+
]
29+
},
30+
{
31+
"id": "example-eco-start",
32+
"name": "Parked start: Eco and high regen",
33+
"enabled": false,
34+
"match": "ALL",
35+
"conditions": [
36+
{ "type": "IN_PARK", "op": "EQ", "flag": true },
37+
{ "type": "SPEED", "op": "EQ", "number": 0 }
38+
],
39+
"actions": [
40+
{ "type": "SET_DRIVE_MODE", "number": 2 },
41+
{ "type": "SET_REGEN_LEVEL", "number": 2 }
42+
]
43+
}
44+
]
45+
}

0 commit comments

Comments
 (0)