diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index d5ae979c4..213e58f5d 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -1294,6 +1294,12 @@ abstract class S { /// **'s'** String get second; + /// No description provided for @sensors. + /// + /// In en, this message translates to: + /// **'Sensor'** + String get sensors; + /// No description provided for @sequence. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index b6b5e4c23..7bc7c8a2c 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -627,6 +627,9 @@ class SDe extends S { @override String get second => 's'; + @override + String get sensors => 'Sensor'; + @override String get sequence => 'Sequenz'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index 5a26dd1ba..becc94fdb 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -627,6 +627,9 @@ class SEn extends S { @override String get second => 's'; + @override + String get sensors => 'Sensor'; + @override String get sequence => 'Sequence'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart index 060f23da1..2411f4da3 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart @@ -627,6 +627,9 @@ class SFr extends S { @override String get second => 's'; + @override + String get sensors => 'Capteur'; + @override String get sequence => 'Séquence'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart index 91831270d..1a4b62555 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart @@ -627,6 +627,9 @@ class SId extends S { @override String get second => 'S'; + @override + String get sensors => 'Sensor'; + @override String get sequence => 'Urutan'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index dfac9c3dc..e3fcfa3c6 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -627,6 +627,9 @@ class SZh extends S { @override String get second => '秒'; + @override + String get sensors => '传感器'; + @override String get sequence => '顺序'; @@ -1505,6 +1508,9 @@ class SZhTw extends SZh { @override String get second => '秒'; + @override + String get sensors => '传感器'; + @override String get sequence => '順序'; diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e59e70fe0..04f5c2081 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -586,7 +586,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -596,7 +596,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -720,7 +720,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -730,7 +730,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -748,7 +748,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -758,7 +758,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -779,7 +779,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -792,7 +792,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; @@ -818,7 +818,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -831,7 +831,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -854,7 +854,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -867,7 +867,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -890,7 +890,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -902,7 +902,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; @@ -931,7 +931,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -943,7 +943,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; @@ -969,7 +969,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -981,7 +981,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; diff --git a/lib/core/persistant_store.dart b/lib/core/persistant_store.dart index ed7d82563..e1afe8b60 100644 --- a/lib/core/persistant_store.dart +++ b/lib/core/persistant_store.dart @@ -258,5 +258,11 @@ class PropertyListenable extends ValueListenable { } @override - T get value => box.get(key, defaultValue: defaultValue); + T get value { + final val = box.get(key, defaultValue: defaultValue); + if (val == null || val is! T) { + return defaultValue!; + } + return val; + } } diff --git a/lib/data/model/app/shell_func.dart b/lib/data/model/app/shell_func.dart index cebd7d79c..1b487b8e2 100644 --- a/lib/data/model/app/shell_func.dart +++ b/lib/data/model/app/shell_func.dart @@ -200,6 +200,7 @@ enum StatusCmdType { diskio, battery, nvidia, + sensors, ; } @@ -220,6 +221,7 @@ const _statusCmds = [ 'cat /proc/diskstats', 'for f in /sys/class/power_supply/*/uevent; do cat "\$f"; echo; done', 'nvidia-smi -q -x', + 'sensors -j', ]; enum BSDStatusCmdType { diff --git a/lib/data/model/server/sensors.dart b/lib/data/model/server/sensors.dart new file mode 100644 index 000000000..e1d259a92 --- /dev/null +++ b/lib/data/model/server/sensors.dart @@ -0,0 +1,175 @@ +import 'dart:convert'; + +import 'package:toolbox/core/extension/listx.dart'; +import 'package:toolbox/data/res/logger.dart'; + +final class SensorAdaptor { + final String raw; + + const SensorAdaptor(this.raw); + + static const acpiRaw = 'ACPI interface'; + static const pciRaw = 'PCI adapter'; + static const virtualRaw = 'Virtual device'; + static const isaRaw = 'ISA adapter'; + static const acpi = SensorAdaptor(acpiRaw); + static const pci = SensorAdaptor(pciRaw); + static const virtual = SensorAdaptor(virtualRaw); + static const isa = SensorAdaptor(isaRaw); + + static SensorAdaptor parse(String raw) => switch (raw) { + acpiRaw => acpi, + pciRaw => pci, + virtualRaw => virtual, + isaRaw => isa, + _ => SensorAdaptor(raw), + }; +} + +final class SensorTemp { + final double? current; + final double? max; + final double? min; + + const SensorTemp({this.current, this.max, this.min}); + + @override + String toString() { + return 'SensorTemp{current: $current, max: $max, min: $min}'; + } + + @override + operator ==(Object other) { + if (identical(this, other)) return true; + + return other is SensorTemp && + other.current == current && + other.max == max && + other.min == min; + } + + @override + int get hashCode => current.hashCode ^ max.hashCode ^ min.hashCode; +} + +final class SensorItem { + final String device; + final SensorAdaptor adapter; + final Map props; + + const SensorItem({ + required this.device, + required this.adapter, + required this.props, + }); + + static List parse(String raw) { + final rmErrRaw = raw.split('\n') + ..removeWhere((element) => element.contains('ERROR:')); + final map = json.decode(rmErrRaw.join('\n')) as Map; + final items = []; + for (final key in map.keys) { + try { + final adapter = SensorAdaptor.parse(map[key]['Adapter'] as String); + final props = {}; + for (final subKey in map[key].keys) { + if (subKey == 'Adapter') { + continue; + } + final subMap = map[key][subKey] as Map; + final currentKey = + subMap.keys.toList().firstWhereOrNull((e) => e.endsWith('input')); + final current = subMap[currentKey] as double?; + final maxKey = subMap.keys + .toList() + .firstWhereOrNull((e) => e.endsWith('max') || e.endsWith('crit')); + final max = subMap[maxKey] as double?; + final minKey = + subMap.keys.toList().firstWhereOrNull((e) => e.endsWith('min')); + final min = subMap[minKey] as double?; + if (current == null && max == null && min == null) { + continue; + } + props[subKey] = SensorTemp(current: current, max: max, min: min); + } + items.add(SensorItem(device: key, adapter: adapter, props: props)); + } catch (e, s) { + Loggers.parse.warning(e, s); + continue; + } + } + return items; + } + + static const sensorsRaw = ''' +{ + "coretemp-isa-0000":{ + "Adapter": "ISA adapter", + "Package id 0":{ + "temp1_input": 51.000, + "temp1_max": 105.000, + "temp1_crit": 105.000, + "temp1_crit_alarm": 0.000 + }, + "Core 0":{ + "temp2_input": 40.000, + "temp2_max": 105.000, + "temp2_crit": 105.000, + "temp2_crit_alarm": 0.000 + }, + "Core 1":{ + "temp3_input": 40.000, + "temp3_max": 105.000, + "temp3_crit": 105.000, + "temp3_crit_alarm": 0.000 + }, + "Core 2":{ + "temp4_input": 40.000, + "temp4_max": 105.000, + "temp4_crit": 105.000, + "temp4_crit_alarm": 0.000 + }, + "Core 3":{ + "temp5_input": 40.000, + "temp5_max": 105.000, + "temp5_crit": 105.000, + "temp5_crit_alarm": 0.000 + } + }, + "acpitz-acpi-0":{ + "Adapter": "ACPI interface", + "temp1":{ + "temp1_input": 27.800, + "temp1_crit": 119.000 + } + }, + "iwlwifi_1-virtual-0":{ + "Adapter": "Virtual device", + "temp1":{ +ERROR: Can't get value of subfeature temp1_input: Can't read + + } + }, + "nvme-pci-0400":{ + "Adapter": "PCI adapter", + "Composite":{ + "temp1_input": 35.850, + "temp1_max": 83.850, + "temp1_min": -273.150, + "temp1_crit": 84.850, + "temp1_alarm": 0.000 + }, + "Sensor 1":{ + "temp2_input": 35.850, + "temp2_max": 65261.850, + "temp2_min": -273.150 + }, + "Sensor 2":{ + "temp3_input": 37.850, + "temp3_max": 65261.850, + "temp3_min": -273.150 + } + } +} +'''; +} diff --git a/lib/data/model/server/server.dart b/lib/data/model/server/server.dart index 80ac636fe..f48f21daf 100644 --- a/lib/data/model/server/server.dart +++ b/lib/data/model/server/server.dart @@ -7,6 +7,7 @@ import 'package:toolbox/data/model/server/disk.dart'; import 'package:toolbox/data/model/server/memory.dart'; import 'package:toolbox/data/model/server/net_speed.dart'; import 'package:toolbox/data/model/server/nvdia.dart'; +import 'package:toolbox/data/model/server/sensors.dart'; import 'package:toolbox/data/model/server/server_private_info.dart'; import 'package:toolbox/data/model/server/system.dart'; import 'package:toolbox/data/model/server/temp.dart'; @@ -55,6 +56,7 @@ class ServerStatus { List? nvidia; final List batteries = []; final Map more = {}; + final List sensors = []; ServerStatus({ required this.cpu, diff --git a/lib/data/model/server/server_status_update_req.dart b/lib/data/model/server/server_status_update_req.dart index d9cc2a170..9b18f41af 100644 --- a/lib/data/model/server/server_status_update_req.dart +++ b/lib/data/model/server/server_status_update_req.dart @@ -1,5 +1,6 @@ import 'package:toolbox/data/model/server/battery.dart'; import 'package:toolbox/data/model/server/nvdia.dart'; +import 'package:toolbox/data/model/server/sensors.dart'; import 'package:toolbox/data/model/server/server.dart'; import 'package:toolbox/data/model/server/system.dart'; import 'package:toolbox/data/res/logger.dart'; @@ -143,6 +144,16 @@ Future _getLinuxStatus(ServerStatusUpdateReq req) async { Loggers.parse.warning(e, s); } + try { + final sensors = SensorItem.parse(StatusCmdType.sensors.find(segments)); + if (sensors.isNotEmpty) { + req.ss.sensors.clear(); + req.ss.sensors.addAll(sensors); + } + } catch (e, s) { + Loggers.parse.warning(e, s); + } + return req.ss; } diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index b71dd944a..803fc1e25 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 773; - static const String engine = "3.19.1"; - static const String buildAt = "2024-02-22 11:55:07"; - static const int modifications = 8; + static const int build = 775; + static const String engine = "3.19.0"; + static const String buildAt = "2024-02-23 09:28:28"; + static const int modifications = 2; static const int script = 38; } diff --git a/lib/data/res/default.dart b/lib/data/res/default.dart index 508ab9735..0699ada8e 100644 --- a/lib/data/res/default.dart +++ b/lib/data/res/default.dart @@ -12,6 +12,7 @@ abstract final class Defaults { 'gpu', 'disk', 'net', + 'sensor', 'temp', 'battery' ]; diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 12b44260e..1cc5d20b3 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -199,6 +199,7 @@ "save": "Speichern", "saved": "Gerettet", "second": "s", + "sensors": "Sensor", "sequence": "Sequenz", "server": "Server", "serverDetailOrder": "Reihenfolge der Widgets auf der Detailseite", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b1445fd7a..f82abbef7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -199,6 +199,7 @@ "save": "Save", "saved": "Saved", "second": "s", + "sensors": "Sensor", "sequence": "Sequence", "server": "Server", "serverDetailOrder": "Detail page widget order", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index b7bd72ba7..13f757f4a 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -199,6 +199,7 @@ "save": "Enregistrer", "saved": "Enregistré", "second": "s", + "sensors": "Capteur", "sequence": "Séquence", "server": "Serveur", "serverDetailOrder": "Ordre des widgets de la page de détails", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index f2aecb5dd..e17765ce1 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -199,6 +199,7 @@ "save": "Menyimpan", "saved": "Diselamatkan", "second": "S", + "sensors": "Sensor", "sequence": "Urutan", "server": "Server", "serverDetailOrder": "Detail pesanan widget halaman", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index c7fd4c7be..70551fc36 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -199,6 +199,7 @@ "save": "保存", "saved": "已保存", "second": "秒", + "sensors": "传感器", "sequence": "顺序", "server": "服务器", "serverDetailOrder": "详情页部件顺序", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index c762039dc..9f077016c 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -199,6 +199,7 @@ "save": "保存", "saved": "已保存", "second": "秒", + "sensors": "传感器", "sequence": "順序", "server": "服務器", "serverDetailOrder": "詳情頁部件順序", diff --git a/lib/view/page/server/detail.dart b/lib/view/page/server/detail.dart index b7d880d24..c624a78c9 100644 --- a/lib/view/page/server/detail.dart +++ b/lib/view/page/server/detail.dart @@ -10,6 +10,7 @@ import 'package:toolbox/data/model/server/cpu.dart'; import 'package:toolbox/data/model/server/disk.dart'; import 'package:toolbox/data/model/server/net_speed.dart'; import 'package:toolbox/data/model/server/nvdia.dart'; +import 'package:toolbox/data/model/server/sensors.dart'; import 'package:toolbox/data/model/server/server_private_info.dart'; import 'package:toolbox/data/model/server/system.dart'; import 'package:toolbox/data/res/store.dart'; @@ -47,6 +48,7 @@ class _ServerDetailPageState extends State _buildGpuView, _buildDiskView, _buildNetView, + _buildSensors, _buildTemperature, _buildBatteries, ], @@ -316,7 +318,7 @@ class _ServerDetailPageState extends State } Widget _buildGpuView(ServerStatus ss) { - if (ss.nvidia == null) return UIs.placeholder; + if (ss.nvidia == null || ss.nvidia?.isEmpty == true) return UIs.placeholder; final children = ss.nvidia?.map((e) => _buildGpuItem(e)).toList() ?? []; return CardX( child: ExpandTile( @@ -331,10 +333,6 @@ class _ServerDetailPageState extends State Widget _buildGpuItem(NvidiaSmiItem item) { final mem = item.memory; final processes = mem.processes; - final children = []; - if (processes.isNotEmpty) { - children.addAll(processes.map((e) => _buildGpuProcessItem(e))); - } return ListTile( title: Text(item.name, style: UIs.text13), leading: Text( @@ -664,6 +662,51 @@ class _ServerDetailPageState extends State ); } + Widget _buildSensors(ServerStatus ss) { + if (ss.sensors.isEmpty) return UIs.placeholder; + return CardX( + child: ExpandTile( + title: Text(l10n.sensors), + leading: const Icon(Icons.thermostat, size: 17), + childrenPadding: const EdgeInsets.only(bottom: 7), + initiallyExpanded: _getInitExpand(ss.sensors.length, 3), + children: ss.sensors.map(_buildSensorItem).toList(), + ), + ); + } + + Widget _buildSensorItem(SensorItem si) { + if (si.props.isEmpty) return UIs.placeholder; + return ListTile( + title: Text(si.device, style: UIs.text15), + subtitle: Column( + children: si.props.keys + .map((e) => _buildSensorDetailItem(e, si.props[e])) + .toList(), + ), + ); + } + + Widget _buildSensorDetailItem(String key, SensorTemp? st) { + if (st == null) return UIs.placeholder; + final text = () { + final current = st.current?.toStringAsFixed(1); + final max = st.max?.toStringAsFixed(1); + final min = st.min?.toStringAsFixed(1); + final currentText = current == null ? '' : '$current°C'; + final maxText = max == null ? '' : ' | ${l10n.max}:$max'; + final minText = min == null ? '' : ' | ${l10n.min}:$min'; + return '$currentText$maxText$minText'; + }(); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(key, style: UIs.text13), + Text(text, style: UIs.text13Grey), + ], + ); + } + Widget _buildAnimatedText(Key key, String text, TextStyle style) { return AnimatedSwitcher( duration: const Duration(milliseconds: 277), diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 29eb1ab84..53326295d 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -439,7 +439,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -449,7 +449,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -574,7 +574,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -584,7 +584,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -604,7 +604,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 771; + CURRENT_PROJECT_VERSION = 775; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; @@ -615,7 +615,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.771; + MARKETING_VERSION = 1.0.775; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/test/sensors_test.dart b/test/sensors_test.dart new file mode 100644 index 000000000..ab49ba88f --- /dev/null +++ b/test/sensors_test.dart @@ -0,0 +1,44 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:toolbox/data/model/server/sensors.dart'; + +void main() { + test('parse sensors', () { + final sensors = SensorItem.parse(SensorItem.sensorsRaw); + expect(sensors.map((e) => e.device), [ + 'coretemp-isa-0000', + 'acpitz-acpi-0', + 'iwlwifi_1-virtual-0', + 'nvme-pci-0400', + ]); + expect(sensors.map((e) => e.adapter), [ + SensorAdaptor.isa, + SensorAdaptor.acpi, + SensorAdaptor.virtual, + SensorAdaptor.pci, + ]); + expect( + sensors.map((e) => e.props), + [ + { + 'Package id 0': const SensorTemp(current: 51, max: 105), + 'Core 0': const SensorTemp(current: 40, max: 105), + 'Core 1': const SensorTemp(current: 40, max: 105), + 'Core 2': const SensorTemp(current: 40, max: 105), + 'Core 3': const SensorTemp(current: 40, max: 105), + }, + { + 'temp1': const SensorTemp(current: 27.8, max: 119), + }, + {}, + { + 'Composite': + const SensorTemp(current: 35.85, max: 83.85, min: -273.15), + 'Sensor 1': + const SensorTemp(current: 35.85, max: 65261.85, min: -273.15), + 'Sensor 2': + const SensorTemp(current: 37.85, max: 65261.85, min: -273.15), + }, + ], + ); + }); +}