Skip to content

Commit db1d99e

Browse files
author
Igor Demyanov
committed
The settings save values ​​other than the default values.
1 parent 6ed9eac commit db1d99e

9 files changed

Lines changed: 197 additions & 64 deletions

File tree

backend/lib/config.dart

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:convert';
22
import 'dart:io';
3+
import 'package:collection/collection.dart';
34
import 'package:dslideshow_backend/src/service/mqtt/mqtt_config.dart';
45
import 'package:dslideshow_backend/src/service/ota/ota_config.dart';
56
import 'package:dslideshow_backend/src/service/storage/storages_config.dart';
@@ -20,7 +21,6 @@ export 'package:dslideshow_backend/src/service/storage/googlephoto/gphoto_storag
2021
export 'package:dslideshow_backend/src/service/mqtt/mqtt_config.dart';
2122
export 'package:dslideshow_backend/src/web_server/web_server_config.dart';
2223

23-
2424
part 'config.g.dart';
2525

2626
@JsonSerializable()
@@ -91,13 +91,27 @@ class AppConfig {
9191
}
9292
}
9393

94+
static Map<String, dynamic>? _defaultJSON;
95+
9496
static final prettyPrintJSONEncode = new JsonEncoder.withIndent(' ');
9597
void toFile([String? filenameOutput]) {
9698
final fileName = filenameOutput ?? fullConfigFilename;
9799
_log.info('Saving to "$fileName"');
100+
var json = this.toJson();
101+
try {
102+
if (_defaultJSON == null) {
103+
_log.fine("Create default AppConfig json");
104+
final defaultConfig = AppConfig.fromJson({});
105+
defaultConfig.storages.fillEmptyStorages();
106+
_defaultJSON = defaultConfig.toJson();
107+
}
108+
json = stripDefaults(json, _defaultJSON!);
109+
} catch (e) {
110+
_log.severe("Can'not delete default config value", e);
111+
}
98112
File(fileName)
99113
..openWrite()
100-
..writeAsStringSync(prettyPrintJSONEncode.convert(this.toJson()));
114+
..writeAsStringSync(prettyPrintJSONEncode.convert(json));
101115
_log.info('Saved to "$fileName"');
102116
}
103117

@@ -158,6 +172,34 @@ class AppConfig {
158172
}
159173
}
160174

175+
Map<String, dynamic> stripDefaults(
176+
Map<String, dynamic> json,
177+
Map<String, dynamic> defaults,
178+
) {
179+
final result = Map<String, dynamic>.of(json);
180+
const deepEq = DeepCollectionEquality();
181+
182+
for (final key in json.keys) {
183+
if (!defaults.containsKey(key)) continue;
184+
185+
final jsonValue = json[key];
186+
final defaultValue = defaults[key];
187+
188+
if (jsonValue is Map<String, dynamic> &&
189+
defaultValue is Map<String, dynamic>) {
190+
final strippedChild = stripDefaults(jsonValue, defaultValue);
191+
if (strippedChild.isEmpty) {
192+
result.remove(key);
193+
} else {
194+
result[key] = strippedChild;
195+
}
196+
} else if (deepEq.equals(jsonValue, defaultValue)) {
197+
result.remove(key);
198+
}
199+
}
200+
return result;
201+
}
202+
161203
@JsonSerializable()
162204
class LogConfig {
163205
@JsonKey(name: "web", fromJson: _parseLogLevel, toJson: _logLevelToJson)

backend/lib/src/service/slideshow/slideshow_config.g.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/lib/src/service/storage/storages_config.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class StoragesConfig {
2121

2222
Map<String, dynamic> toJson() => _$StoragesConfigToJson(this);
2323

24+
void fillEmptyStorages(){
25+
StorageType.values.forEach(getOrCreateEmpty);
26+
}
27+
2428
T getOrCreateEmpty<T extends AbstractStorageConfig>(StorageType type) {
2529
return storages.putIfAbsent(type, () {
2630
switch (type) {

backend/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: 'dslideshow_backend'
2-
version: 9.1.0+1
2+
version: 9.2.0+1
33
description: A sample command-line application
44
publish_to: none
55
environment:

backend/test/json_strip_test.dart

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import 'package:dslideshow_backend/config.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() {
5+
group('stripDefaults', () {
6+
test('Removes simple fields (String, int, bool) if they match defaults', () {
7+
final json = {
8+
'name': 'Alex',
9+
'age': 25,
10+
'isAdmin': false,
11+
};
12+
final defaults = {
13+
'name': 'Alex', // match -> remove
14+
'age': 18, // diff -> keep
15+
'isAdmin': false, // match -> remove
16+
};
17+
18+
final result = stripDefaults(json, defaults);
19+
20+
expect(result, equals({'age': 25}));
21+
});
22+
23+
test('Preserves fields that do not exist in defaults', () {
24+
final json = {
25+
'id': 100, // not in defaults
26+
'name': 'Alex',
27+
};
28+
final defaults = {
29+
'name': 'Alex',
30+
};
31+
32+
final result = stripDefaults(json, defaults);
33+
34+
expect(result, equals({'id': 100}));
35+
});
36+
37+
test('Handles null values correctly', () {
38+
final json = {'desc': null, 'title': 'Test'};
39+
final defaults = {'desc': null, 'title': null};
40+
41+
final result = stripDefaults(json, defaults);
42+
43+
// desc: null == null -> remove
44+
// title: 'Test' != null -> keep
45+
expect(result, equals({'title': 'Test'}));
46+
});
47+
48+
test('Removes Lists if they are identical', () {
49+
final json = {
50+
'tags': ['a', 'b'],
51+
'ids': [1, 2, 3],
52+
};
53+
final defaults = {
54+
'tags': ['a', 'b'], // match
55+
'ids': [1, 2], // diff length
56+
};
57+
58+
final result = stripDefaults(json, defaults);
59+
60+
expect(result, equals({'ids': [1, 2, 3]}));
61+
});
62+
63+
test('Recursion: Removes parent key if nested object matches completely', () {
64+
final json = {
65+
'settings': {
66+
'theme': 'dark',
67+
'sound': true,
68+
},
69+
'other': 1
70+
};
71+
final defaults = {
72+
'settings': {
73+
'theme': 'dark',
74+
'sound': true,
75+
},
76+
'other': 1
77+
};
78+
79+
final result = stripDefaults(json, defaults);
80+
81+
expect(result, isEmpty);
82+
});
83+
84+
test('Recursion: Preserves parent key if nested object has differences', () {
85+
final json = {
86+
'settings': {
87+
'theme': 'light', // diff
88+
'sound': true, // match
89+
}
90+
};
91+
final defaults = {
92+
'settings': {
93+
'theme': 'dark',
94+
'sound': true,
95+
}
96+
};
97+
98+
final result = stripDefaults(json, defaults);
99+
100+
// 'sound' removed, 'theme' kept -> 'settings' kept
101+
expect(result, equals({
102+
'settings': {'theme': 'light'}
103+
}));
104+
});
105+
106+
test('Complex nested scenario', () {
107+
final json = {
108+
'user': {
109+
'meta': {'v': 1, 'flag': 'A'},
110+
'data': [1, 2]
111+
},
112+
'active': true
113+
};
114+
115+
final defaults = {
116+
'user': {
117+
'meta': {'v': 1, 'flag': 'B'}, // flag diff
118+
'data': [1, 2] // data match
119+
},
120+
'active': true // match
121+
};
122+
123+
final result = stripDefaults(json, defaults);
124+
125+
expect(result, equals({
126+
'user': {
127+
'meta': {'flag': 'A'}
128+
}
129+
}));
130+
});
131+
});
132+
}

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 9.2.0
2+
The settings save values ​​other than the default values.
3+
4+
# 9.1.0
5+
Support web upload files
6+
17
# 9.0.1
28
New menu UX
39

common/lib/version.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
abstract class ApplicationInfo{
2-
static const String frontendVersion = '9.1.0+1';
3-
static const String backendVersion = '9.1.0+1';
2+
static const String frontendVersion = '9.2.0+1';
3+
static const String backendVersion = '9.2.0+1';
44
}

dslideshow_flutter/config.json

Lines changed: 6 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,21 @@
11
{
22
"log": {
3-
"web": "INFO",
4-
"main": "INFO",
5-
"ota": "INFO",
6-
"hw_frame": "INFO"
3+
"main": "ALL"
74
},
85
"hardware": {
9-
"smoothingGPIOMs": 100,
10-
"pinButton0": 17,
11-
"pinButton1": 27,
12-
"pinButton3": 23,
13-
"pinButton2": 22,
14-
"pinPIRSensor": 15,
15-
"pinPowerLED": 14,
16-
"systemDiskDev": "/dev/disk3s1s1",
17-
"systemIfConfigScript": "ifconfig",
18-
"sensorsScript": "./scripts/sensorsScript.sh",
19-
"screenPowerOffScript": "./scripts/screenOff.sh",
20-
"screenPowerOnScript": "./scripts/screenOn.sh",
21-
"screenPowerOnTimerSec": 120
6+
"systemDiskDev": "/dev/disk3s1s1"
227
},
238
"slideshow": {
24-
"buttons": {
25-
"button0": "SlideshowAction.pause",
26-
"button1": "SlideshowAction.showMenu",
27-
"button2": "SlideshowAction.toggleScreen",
28-
"button3": "SlideshowAction.showInfo"
29-
},
30-
"displayTimeMs": 2000,
31-
"fadeTimeMs": 2000,
32-
"transitionTimeMs": 1000,
33-
"allowedEffects": [],
34-
"isBlurredBackground": true,
35-
"backgroundBlurSigma": 20,
36-
"backgroundOpacity": 0.9,
37-
"backgroundColor": "ffffffff"
9+
"allowedEffects": [
10+
"Default"
11+
]
3812
},
3913
"welcome": {
40-
"text": "Welcome",
41-
"size": 100.0,
42-
"delayMs": 2000
14+
"text": "Привет"
4315
},
4416
"web": {
4517
"port": 8081,
4618
"alwaysEnabled": true,
4719
"permanentCode": "123"
48-
},
49-
"mqtt": {
50-
"port": 1883,
51-
"enabled": false,
52-
"keepAlivePeriod": 600,
53-
"deviceId": "dslideshow",
54-
"user": "user",
55-
"pass": "pass",
56-
"server": "smarthome.local",
57-
"clientId": "dslideshow",
58-
"deviceName": "PhotoFrame1",
59-
"discovery_prefix": "homeassistant",
60-
"configuration_topic": "config",
61-
"command_topic": "set",
62-
"state_topic": "state"
63-
},
64-
"storages": {
65-
"selected": "DiskStorage",
66-
"storages": {}
67-
},
68-
"wifi": {
69-
"devId": "wlan0",
70-
"scanWiFiScript": "./scripts/scanWiFi.sh"
7120
}
7221
}

dslideshow_flutter/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: dslideshow_flutter
22
description: Flutter SlideShow
3-
version: 9.1.0+1
3+
version: 9.2.0+1
44
publish_to: none
55

66
environment:

0 commit comments

Comments
 (0)