feat: Wake On LAN

This commit is contained in:
lollipopkit
2024-05-09 23:27:05 +08:00
parent b876981243
commit e2ddd48a79
23 changed files with 290 additions and 94 deletions

View File

@@ -690,7 +690,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -700,7 +700,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -826,7 +826,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -836,7 +836,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -854,7 +854,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -864,7 +864,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -885,7 +885,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -898,7 +898,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -924,7 +924,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -937,7 +937,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -960,7 +960,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -973,7 +973,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -996,7 +996,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1008,7 +1008,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1037,7 +1037,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1049,7 +1049,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -1075,7 +1075,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1087,7 +1087,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;

View File

@@ -1,6 +1,7 @@
import 'package:hive_flutter/hive_flutter.dart';
import 'package:toolbox/data/model/server/custom.dart';
import 'package:toolbox/data/model/server/server.dart';
import 'package:toolbox/data/model/server/wol_cfg.dart';
import 'package:toolbox/data/res/provider.dart';
import '../app/error.dart';
@@ -38,6 +39,9 @@ class ServerPrivateInfo {
@HiveField(10)
final ServerCustom? custom;
@HiveField(11)
final WakeOnLanCfg? wolCfg;
final String id;
const ServerPrivateInfo({
@@ -52,6 +56,7 @@ class ServerPrivateInfo {
this.autoConnect,
this.jumpId,
this.custom,
this.wolCfg,
}) : id = '$user@$ip:$port';
static ServerPrivateInfo fromJson(Map<String, dynamic> json) {
@@ -68,6 +73,9 @@ class ServerPrivateInfo {
final custom = json["customCmd"] == null
? null
: ServerCustom.fromJson(json["custom"].cast<String, dynamic>());
final wolCfg = json["wolCfg"] == null
? null
: WakeOnLanCfg.fromJson(json["wolCfg"].cast<String, dynamic>());
return ServerPrivateInfo(
name: name,
@@ -81,6 +89,7 @@ class ServerPrivateInfo {
autoConnect: autoConnect,
jumpId: jumpId,
custom: custom,
wolCfg: wolCfg,
);
}
@@ -111,6 +120,9 @@ class ServerPrivateInfo {
if (custom != null) {
data["custom"] = custom?.toJson();
}
if (wolCfg != null) {
data["wolCfg"] = wolCfg?.toJson();
}
return data;
}

View File

@@ -28,13 +28,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
autoConnect: fields[8] as bool?,
jumpId: fields[9] as String?,
custom: fields[10] as ServerCustom?,
wolCfg: fields[11] as WakeOnLanCfg?,
);
}
@override
void write(BinaryWriter writer, ServerPrivateInfo obj) {
writer
..writeByte(11)
..writeByte(12)
..writeByte(0)
..write(obj.name)
..writeByte(1)
@@ -56,7 +57,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
..writeByte(9)
..write(obj.jumpId)
..writeByte(10)
..write(obj.custom);
..write(obj.custom)
..writeByte(11)
..write(obj.wolCfg);
}
@override

View File

@@ -0,0 +1,71 @@
import 'dart:io';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:wake_on_lan/wake_on_lan.dart';
@HiveType(typeId: 8)
final class WakeOnLanCfg {
@HiveField(0)
final String mac;
@HiveField(1)
final String ip;
@HiveField(2)
final String? pwd;
const WakeOnLanCfg({
required this.mac,
required this.ip,
this.pwd,
});
(Object?, bool) validate() {
final macValidation = MACAddress.validate(mac);
final ipValidation = IPAddress.validate(
ip,
type: ip.contains(':')
? InternetAddressType.IPv6
: InternetAddressType.IPv4,
);
final pwdValidation = SecureONPassword.validate(pwd);
final valid =
macValidation.state && ipValidation.state && pwdValidation.state;
final err =
macValidation.error ?? ipValidation.error ?? pwdValidation.error;
return (err, valid);
}
Future<void> wake() async {
if (!validate().$2) {
throw Exception('Invalid WakeOnLanCfg');
}
final ip_ = IPAddress(ip);
final mac_ = MACAddress(mac);
final pwd_ = pwd != null ? SecureONPassword(pwd!) : null;
final obj = WakeOnLAN(ip_, mac_, password: pwd_);
await obj.wake(
repeat: 3,
repeatDelay: const Duration(milliseconds: 500),
);
}
static WakeOnLanCfg fromJson(Map<String, dynamic> json) {
return WakeOnLanCfg(
mac: json['mac'] as String,
ip: json['ip'] as String,
pwd: json['pwd'] as String?,
);
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{
'mac': mac,
'ip': ip,
};
if (pwd != null) {
map['pwd'] = pwd;
}
return map;
}
}

View File

@@ -141,8 +141,7 @@ class ServerProvider extends ChangeNotifier {
TryLimiter.reset(s.spi.id);
}
if (!(s.spi.autoConnect ?? true) &&
s.conn == ServerConn.disconnected ||
if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected ||
_manualDisconnectedIds.contains(s.spi.id)) {
return;
}
@@ -277,6 +276,23 @@ class ServerProvider extends ChangeNotifier {
if (s.needGenClient || (s.client?.isClosed ?? true)) {
_setServerState(s, ServerConn.connecting);
final wol = spi.wolCfg;
if (wol != null) {
/// TODO: test it
try {
await wol.wake();
} catch (e) {
// TryLimiter.inc(sid);
// s.status.err = SSHErr(
// type: SSHErrType.connect,
// message: 'Wake on lan failed: $e',
// );
// _setServerState(s, ServerConn.failed);
// Loggers.app.warning('Wake on lan failed', e);
// return;
}
}
try {
final time1 = DateTime.now();
s.client = await genClient(

View File

@@ -2,9 +2,9 @@
class BuildData {
static const String name = "ServerBox";
static const int build = 884;
static const int build = 887;
static const String engine = "3.19.6";
static const String buildAt = "2024-05-09 17:08:49";
static const int modifications = 4;
static const String buildAt = "2024-05-09 23:08:15";
static const int modifications = 22;
static const int script = 46;
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav-Einstellungen sind leer",
"whenOpenApp": "Beim Öffnen der App",
"willTakEeffectImmediately": "Wird sofort angewendet",
"wolTip": "Nach der Konfiguration von WOL (Wake-on-LAN) wird jedes Mal, wenn der Server verbunden wird, eine WOL-Anfrage gesendet.",
"write": "Schreiben",
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav setting is empty",
"whenOpenApp": "When opening the app",
"willTakEeffectImmediately": "Will take effect immediately",
"wolTip": "After configuring WOL (Wake-on-LAN), a WOL request is sent each time the server is connected.",
"write": "Write",
"writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "La configuración de Webdav está vacía",
"whenOpenApp": "Al abrir la App",
"willTakEeffectImmediately": "Los cambios tendrán efecto inmediatamente",
"wolTip": "Después de configurar WOL (Wake-on-LAN), se envía una solicitud de WOL cada vez que se conecta el servidor.",
"write": "Escribir",
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "La configuration Webdav est vide",
"whenOpenApp": "À l'ouverture de l'application",
"willTakEeffectImmediately": "Prendra effet immédiatement",
"wolTip": "Après avoir configuré WOL (Wake-on-LAN), une requête WOL est envoyée à chaque connexion au serveur.",
"write": "Écrire",
"writeScriptFailTip": "L'écriture du script a échoué, peut-être en raison d'un manque de permissions ou parce que le répertoire n'existe pas."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Pengaturan webdav kosong",
"whenOpenApp": "Saat membuka aplikasi",
"willTakEeffectImmediately": "Akan segera berlaku",
"wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.",
"write": "Tulis",
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdavの設定が空です",
"whenOpenApp": "アプリを開くとき",
"willTakEeffectImmediately": "変更は即座に有効になります",
"wolTip": "WOLWake-on-LANを設定した後、サーバーに接続するたびにWOLリクエストが送信されます。",
"write": "書き込み",
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。"
}

View File

@@ -327,6 +327,7 @@
"webdavSettingEmpty": "Webdav-instelling is leeg",
"whenOpenApp": "Bij het openen van de app",
"willTakEeffectImmediately": "Zal onmiddellijk van kracht worden",
"wolTip": "Na het configureren van WOL (Wake-on-LAN), wordt elke keer dat de server wordt verbonden een WOL-verzoek verzonden.",
"write": "Schrijven",
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Configurações de Webdav estão vazias",
"whenOpenApp": "Ao abrir o app",
"willTakEeffectImmediately": "As alterações serão aplicadas imediatamente",
"wolTip": "Após configurar o WOL (Wake-on-LAN), um pedido de WOL é enviado cada vez que o servidor é conectado.",
"write": "Escrita",
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe."
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Настройки Webdav пусты",
"whenOpenApp": "при открытии приложения",
"willTakEeffectImmediately": "Изменения вступят в силу немедленно",
"wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.",
"write": "запись",
"writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует."
}

View File

@@ -323,11 +323,12 @@
"virtKeyHelpIME": "打开/关闭键盘",
"virtKeyHelpSFTP": "在 SFTP 中打开当前路径。",
"waitConnection": "请等待连接建立",
"wakeLock": "保持醒",
"wakeLock": "保持醒",
"watchNotPaired": "没有已配对的 Apple Watch",
"webdavSettingEmpty": "Webdav 设置项为空",
"whenOpenApp": "当打开 App 时",
"willTakEeffectImmediately": "更改将会立即生效",
"wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOl 请求",
"write": "写",
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等"
}

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav 設置項爲空",
"whenOpenApp": "當打開 App 時",
"willTakEeffectImmediately": "更改將會立即生效",
"wolTip": "在配置WOL網絡喚醒每次連接伺服器都會先發送一次WOL請求。",
"write": "写",
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。"
}

View File

@@ -12,6 +12,7 @@ import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/core/utils/ui.dart';
import 'package:toolbox/data/model/app/shell_func.dart';
import 'package:toolbox/data/model/server/custom.dart';
import 'package:toolbox/data/model/server/wol_cfg.dart';
import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/view/widget/expand_tile.dart';
@@ -44,6 +45,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
final _customCmdCtrl = TextEditingController();
final _preferTempDevCtrl = TextEditingController();
final _logoUrlCtrl = TextEditingController();
final _wolMacCtrl = TextEditingController();
final _wolIpCtrl = TextEditingController();
final _wolPwdCtrl = TextEditingController();
final _nameFocus = FocusNode();
final _ipFocus = FocusNode();
@@ -99,6 +103,13 @@ class _ServerEditPageState extends State<ServerEditPage> {
_preferTempDevCtrl.text = custom.preferTempDev ?? '';
_logoUrlCtrl.text = custom.logoUrl ?? '';
}
final wol = spi.wolCfg;
if (wol != null) {
_wolMacCtrl.text = wol.mac;
_wolIpCtrl.text = wol.ip;
_wolPwdCtrl.text = wol.pwd ?? '';
}
}
}
@@ -120,6 +131,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
_customCmdCtrl.dispose();
_preferTempDevCtrl.dispose();
_logoUrlCtrl.dispose();
_wolMacCtrl.dispose();
_wolIpCtrl.dispose();
_wolPwdCtrl.dispose();
}
@override
@@ -380,52 +394,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
hint: 'https://example.com/logo.png',
),
UIs.height7,
const Text('PVE', style: UIs.text13Grey),
..._buildPVEs(),
UIs.height7,
Input(
controller: _pveAddrCtrl,
type: TextInputType.url,
icon: MingCute.web_line,
label: l10n.addr,
hint: 'https://example.com:8006',
),
ListTile(
leading: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(MingCute.certificate_line),
),
title: Text('PVE ${l10n.ignoreCert}'),
subtitle: Text(l10n.pveIgnoreCertTip, style: UIs.text12Grey),
trailing: ListenableBuilder(
listenable: _pveIgnoreCert,
builder: (_, __) => Switch(
value: _pveIgnoreCert.value,
onChanged: (val) {
_pveIgnoreCert.value = val;
},
),
),
).card,
UIs.height7,
Text(l10n.customCmd, style: UIs.text13Grey),
UIs.height7,
Input(
controller: _customCmdCtrl,
type: TextInputType.text,
maxLines: 3,
label: 'JSON',
icon: Icons.code,
hint: '{${l10n.customCmdHint}}',
),
ListTile(
leading: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(MingCute.doc_line),
),
title: Text(l10n.doc),
trailing: const Icon(Icons.open_in_new, size: 17),
onTap: () => openUrl(l10n.customCmdDocUrl),
).card,
..._buildCustomCmds(),
UIs.height7,
Text(l10n.temperature, style: UIs.text13Grey),
UIs.height7,
@@ -436,10 +407,101 @@ class _ServerEditPageState extends State<ServerEditPage> {
icon: MingCute.low_temperature_line,
hint: 'nvme-pci-0400',
),
UIs.height7,
..._buildWOLs(),
],
);
}
List<Widget> _buildPVEs() {
return [
const Text('PVE', style: UIs.text13Grey),
UIs.height7,
Input(
controller: _pveAddrCtrl,
type: TextInputType.url,
icon: MingCute.web_line,
label: l10n.addr,
hint: 'https://example.com:8006',
),
ListTile(
leading: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(MingCute.certificate_line),
),
title: Text('PVE ${l10n.ignoreCert}'),
subtitle: Text(l10n.pveIgnoreCertTip, style: UIs.text12Grey),
trailing: ListenableBuilder(
listenable: _pveIgnoreCert,
builder: (_, __) => Switch(
value: _pveIgnoreCert.value,
onChanged: (val) {
_pveIgnoreCert.value = val;
},
),
),
).card,
];
}
List<Widget> _buildCustomCmds() {
return [
Text(l10n.customCmd, style: UIs.text13Grey),
UIs.height7,
Input(
controller: _customCmdCtrl,
type: TextInputType.text,
maxLines: 3,
label: 'JSON',
icon: Icons.code,
hint: '{${l10n.customCmdHint}}',
),
ListTile(
leading: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(MingCute.doc_line),
),
title: Text(l10n.doc),
trailing: const Icon(Icons.open_in_new, size: 17),
onTap: () => openUrl(l10n.customCmdDocUrl),
).card,
];
}
List<Widget> _buildWOLs() {
return [
const Text('Wake On LAN', style: UIs.text13Grey),
UIs.height7,
ListTile(
leading: const Icon(BoxIcons.bxs_help_circle),
title: Text(l10n.about),
subtitle: Text(l10n.wolTip, style: UIs.text12Grey),
).card,
Input(
controller: _wolMacCtrl,
type: TextInputType.text,
label: 'Mac ${l10n.addr}',
icon: Icons.computer,
hint: '00:11:22:33:44:55',
),
Input(
controller: _wolIpCtrl,
type: TextInputType.text,
label: 'IP ${l10n.addr}',
icon: Icons.network_cell,
hint: '192.168.1.x',
),
Input(
controller: _wolPwdCtrl,
type: TextInputType.text,
obscureText: true,
label: l10n.pwd,
icon: Icons.password,
hint: l10n.pwd,
),
];
}
Widget _buildFAB() {
return FloatingActionButton(
onPressed: _onSave,
@@ -544,6 +606,17 @@ class _ServerEditPageState extends State<ServerEditPage> {
logoUrl: _logoUrlCtrl.text.selfIfNotNullEmpty,
);
final wol = WakeOnLanCfg(
mac: _wolMacCtrl.text,
ip: _wolIpCtrl.text,
pwd: _wolPwdCtrl.text.selfIfNotNullEmpty,
);
final wolValidation = wol.validate();
if (!wolValidation.$2) {
context.showSnackBar('${l10n.failed}: ${wolValidation.$1}');
return;
}
final spi = ServerPrivateInfo(
name: _nameController.text.isEmpty
? _ipController.text
@@ -560,6 +633,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
autoConnect: _autoConnect.value,
jumpId: _jumpServer.value,
custom: custom,
wolCfg: wol,
);
if (widget.spi == null) {

View File

@@ -1135,7 +1135,6 @@ class _SettingPageState extends State<SettingPage> {
Widget _buildWakeLock() {
return ListTile(
leading: const Icon(MingCute.lock_fill),
title: Text(l10n.wakeLock),
trailing: StoreSwitch(prop: _setting.generalWakeLock),
);

View File

@@ -18,21 +18,22 @@ final class IconTextBtn extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: onPressed,
tooltip: text,
icon: orientation == Orientation.landscape ? Row(
children: [
Icon(icon),
UIs.width7,
Text(text, style: UIs.text13Grey),
],
) : Column(
children: [
Icon(icon),
UIs.height7,
Text(text, style: UIs.text13Grey),
],
)
);
onPressed: onPressed,
tooltip: text,
icon: orientation == Orientation.landscape
? Row(
children: [
Icon(icon),
UIs.width7,
Text(text, style: UIs.text13Grey),
],
)
: Column(
children: [
Icon(icon),
UIs.height7,
Text(text, style: UIs.text13Grey),
],
));
}
}

View File

@@ -471,7 +471,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -481,7 +481,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -608,7 +608,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -618,7 +618,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -638,7 +638,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884;
CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
@@ -649,7 +649,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884;
MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -1333,6 +1333,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "13.0.0"
wake_on_lan:
dependency: "direct main"
description:
name: wake_on_lan
sha256: a0db43df0cd05181f476f38ec63345a763b7d3b9d8ab25cabbff45881780cb8e
url: "https://pub.dev"
source: hosted
version: "4.1.1+3"
wakelock_plus:
dependency: "direct main"
description:

View File

@@ -78,6 +78,7 @@ dependencies:
fl_chart: ^0.67.0
wakelock_plus: ^1.2.4
extended_image: ^8.2.0
wake_on_lan: ^4.1.1+3
dev_dependencies:
flutter_native_splash: ^2.1.6