Skip to content

Commit

Permalink
Feat/ping devices from dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
herzhenr committed Aug 20, 2023
1 parent 0c9a7c4 commit e7ad34a
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 70 deletions.
4 changes: 4 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class AppConstants {
static const add = Icon(Icons.add);
static const sort = Icon(Icons.sort);

// Home Ping Timeouts and Intervals for scanning
static const homePingTimeout = 1;
static const homePingInterval = 12;

// Wake Up Dialog Elements
static const errorMessageColor = Colors.red;
static const successMessageColor = Colors.green;
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"homeDeviceListTitle": "Devices",
"homeDeviceCardWakeButton": "Wake Up",
"homeDeviceCardEditButton": "Edit",
"homeDeviceCardOnline": "Device is online",
"homeDeviceCardOffline": "Device is offline",
"homeWolCardTitle": "Waking up...",
"@DISCOVER": {},
"discoverTitle": "Discover Devices",
Expand Down
41 changes: 22 additions & 19 deletions lib/screens/home/bottom_sheet_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import '../../widgets/universal_ui_components.dart';
abstract class ModularBottomFormPage extends StatefulWidget {
final String title;
final Device device;
final Function(List<StorageDevice>) onSubmitDeviceCallback;
final List<StorageDevice> devices;
final Function(List<StorageDevice>, String?) onSubmitDeviceCallback;
final bool deleteButton;

ModularBottomFormPage(
{Key? key,
required this.device,
required this.devices,
required this.title,
required this.onSubmitDeviceCallback,
this.deleteButton = false})
Expand Down Expand Up @@ -57,6 +59,7 @@ abstract class ModularBottomFormPage extends StatefulWidget {
macAddress: controllerMac.text,
modified: DateTime.now(),
wolPort: wolPort,
isOnline: storageDevice.isOnline,
deviceType: deviceType);
} else {
return NetworkDevice(
Expand All @@ -74,14 +77,13 @@ abstract class ModularBottomFormPage extends StatefulWidget {

/// dataOperationOnSave() is an abstract method that is implemented in the child classes and is called when the submitButton is pressed
/// it saves the device to the json file and returns the updated [StorageDevice] list
Future<List<StorageDevice>> dataOperationOnSave();
Future<(List<StorageDevice>, StorageDevice)> dataOperationOnSave();

/// dataOperationOnDelete() is triggered when the delete button is pressed and delete a device from the json file and returns the updated [StorageDevice] list
Future<List<StorageDevice>> dataOperationOnDelete() async {
StorageDevice device = getDevice as StorageDevice;
List<StorageDevice> devices = await deviceStorage.deleteDevice(
device.id,
);
List<StorageDevice> devices =
await deviceStorage.deleteDevice(device.id, this.devices);
return devices;
}

Expand Down Expand Up @@ -213,10 +215,11 @@ class _ModularBottomFormPageState extends State<ModularBottomFormPage> {
onPressed: () => {
validateFormFields(onSubmitDeviceCallback: () async {
Navigator.popUntil(context, (route) => route.isFirst);
List<StorageDevice> device =
(List<StorageDevice>, StorageDevice) updatedDevices =
await widget.dataOperationOnSave();
// sent device to callback function in order to update the UI
widget.onSubmitDeviceCallback(device);
widget.onSubmitDeviceCallback(
updatedDevices.$1, updatedDevices.$2.id);
})
},
text: AppLocalizations.of(context)!.formApplyButtonText,
Expand Down Expand Up @@ -529,9 +532,9 @@ class _ModularBottomFormPageState extends State<ModularBottomFormPage> {
rightColor: Theme.of(context).colorScheme.error,
rightOnPressed: () async {
Navigator.popUntil(context, (route) => route.isFirst);
List<StorageDevice> device = await widget.dataOperationOnDelete();
List<StorageDevice> devices = await widget.dataOperationOnDelete();
// sent device to callback function in order to update the UI
widget.onSubmitDeviceCallback(device);
widget.onSubmitDeviceCallback(devices, null);
},
);
},
Expand All @@ -558,15 +561,15 @@ class NetworkDeviceFormPage extends ModularBottomFormPage {
NetworkDeviceFormPage(
{super.key,
required super.device,
required super.devices,
required super.title,
required super.onSubmitDeviceCallback});

@override
Future<List<StorageDevice>> dataOperationOnSave() async {
List<StorageDevice> devices = await deviceStorage.addDevice(
getDevice as NetworkDevice,
);
return devices;
Future<(List<StorageDevice>, StorageDevice)> dataOperationOnSave() async {
(List<StorageDevice>, StorageDevice) updatedDevices =
await deviceStorage.addDevice(getDevice as NetworkDevice, devices);
return updatedDevices;
}
}

Expand All @@ -576,15 +579,15 @@ class EditDeviceFormPage extends ModularBottomFormPage {
{super.key,
required super.device,
required super.title,
required super.devices,
required super.onSubmitDeviceCallback})
: super(deleteButton: true);

@override
Future<List<StorageDevice>> dataOperationOnSave() async {
List<StorageDevice> devices = await deviceStorage.updateDevice(
getDevice as StorageDevice,
);
return devices;
Future<(List<StorageDevice>, StorageDevice)> dataOperationOnSave() async {
(List<StorageDevice>, StorageDevice) updatedDevices =
await deviceStorage.updateDevice(getDevice as StorageDevice, devices);
return updatedDevices;
}
}

Expand Down
9 changes: 7 additions & 2 deletions lib/screens/home/discover.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import '../../widgets/layout_elements.dart';
import '../../widgets/universal_ui_components.dart';

class DiscoverPage extends StatefulWidget {
final Function(List<StorageDevice>) updateDevicesList;
final Function(List<StorageDevice>, String?) updateDevicesList;
final List<StorageDevice> devices;

const DiscoverPage({Key? key, required this.updateDevicesList})
const DiscoverPage(
{Key? key, required this.updateDevicesList, required this.devices})
: super(key: key);

@override
Expand Down Expand Up @@ -105,6 +107,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
title:
AppLocalizations.of(context)!.discoverAddDeviceAlertTitle,
device: NetworkDevice(),
devices: widget.devices,
onSubmitDeviceCallback: widget.updateDevicesList)),
text: AppLocalizations.of(context)!.discoverAddCustomDeviceButton,
icon: const Icon(Icons.add)),
Expand Down Expand Up @@ -167,6 +170,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
.discoverAddDeviceAlertTitle,
device: _devices[index]
.copyWith(wolPort: 9),
devices: widget.devices,
onSubmitDeviceCallback:
widget.updateDevicesList)),
);
Expand Down Expand Up @@ -221,6 +225,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
builder: (context) => NetworkDeviceFormPage(
title: title,
device: device.copyWith(wolPort: port),
devices: widget.devices,
onSubmitDeviceCallback: widget.updateDevicesList),
);
}
Expand Down
142 changes: 109 additions & 33 deletions lib/screens/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,44 @@ class _HomePageState extends State<HomePage> {

late SortingOrder selectedMenu = widget.selectedMenu;

Timer? _pingDevicesTimer;

@override
void initState() {
super.initState();
_loadDevices().then((value) => {
filterDevicesByType(),
sortDevices(),
_pingDevices(),
});
}

@override
void dispose() {
_pingDevicesTimer?.cancel();
super.dispose();
}

/// loads a list of devices from the device storage
Future<void> _loadDevices() async {
setState(() {
_isLoading = true;
});

try {
final devices = await _deviceStorage.loadDevices();
setState(() {
_devicesRaw = devices;
});
} on PlatformException catch (e) {
debugPrint('Failed to load devices: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}

/// sort Devices by chipsDeviceTypes selection
void filterDevicesByType() {
List<StorageDevice> sortedDevices = [];
Expand Down Expand Up @@ -102,22 +131,31 @@ class _HomePageState extends State<HomePage> {
}
}

/// loads a list of devices from the device storage
Future<void> _loadDevices() async {
setState(() {
_isLoading = true;
/// ping devices periodically in the background to get the current status
/// of the devices and update the ui accordingly
void _pingDevices() {
checkAllDevicesStatus();
_pingDevicesTimer = Timer.periodic(
const Duration(seconds: AppConstants.homePingInterval), (timer) {
checkAllDevicesStatus();
});
}

try {
final devices = await _deviceStorage.loadDevices();
setState(() {
_devicesRaw = devices;
});
} on PlatformException catch (e) {
debugPrint('Failed to load devices: $e');
} finally {
/// updates the status of all devices in [_devices]
Future<void> checkAllDevicesStatus() async {
for (StorageDevice device in _devices) {
checkDeviceStatus(device);
}
}

/// ping a device and update the ui accordingly
/// [device] is the device to ping
/// if the widget is not mounted anymore, the function will stop
Future<void> checkDeviceStatus(StorageDevice device) async {
bool isOnline = await pingDevice(ipAddress: device.ipAddress);
if (mounted) {
setState(() {
_isLoading = false;
device.isOnline = isOnline;
});
}
}
Expand Down Expand Up @@ -170,6 +208,7 @@ class _HomePageState extends State<HomePage> {
MaterialPageRoute(
builder: (context) => DiscoverPage(
updateDevicesList: updateDevicesList,
devices: _devices,
)),
);
if (newDevice != null) {
Expand All @@ -184,33 +223,53 @@ class _HomePageState extends State<HomePage> {
);
}

updateDevicesList(devices) {
/// callback function for updating the list of devices
/// [devices] is the list of devices
/// [deviceId] is the changed device id. This devices gets pinged additionally to the background timer to get the current status.
/// If it is set to null, no device gets pinged (e.g. if device gets deleted, this devices doesn't need to get pinged)
updateDevicesList(List<StorageDevice> devices, String? deviceId) {
setState(() {
//_devices.add(device);
_devicesRaw = devices;
filterDevicesByType();
sortDevices();
if (deviceId != null) {
StorageDevice device =
devices.firstWhere((element) => element.id == deviceId);
// set online state to null because online state is not known yet
device.isOnline = null;
checkDeviceStatus(device);
}
});
}

Widget buildListview() {
return ListView(
padding: AppConstants.screenPaddingScrollView,
children: [
TextTitle(
title: AppLocalizations.of(context)!.homeFilterDevicesTitle,
children: [
SizedBox(
height: 50,
child: filterDevicesChipsV2(),
),
],
),
TextTitle(
title: AppLocalizations.of(context)!.homeDeviceListTitle,
children: [buildDeviceList()],
),
],
return RefreshIndicator(
onRefresh: () async {
_pingDevicesTimer?.cancel();
// set online state for all devices to null because online state is not known yet
for (StorageDevice device in _devices) {
device.isOnline = null;
}
_pingDevices();
},
child: ListView(
padding: AppConstants.screenPaddingScrollView,
children: [
TextTitle(
title: AppLocalizations.of(context)!.homeFilterDevicesTitle,
children: [
SizedBox(
height: 50,
child: filterDevicesChipsV2(),
),
],
),
TextTitle(
title: AppLocalizations.of(context)!.homeDeviceListTitle,
children: [buildDeviceList()],
),
],
),
);
}

Expand Down Expand Up @@ -325,6 +384,7 @@ class _HomePageState extends State<HomePage> {
title = device.ipAddress;
}
return DeviceCard(
isOnline: device.isOnline,
title: title,
subtitle: subtitle,
deviceType: device.deviceType,
Expand Down Expand Up @@ -364,15 +424,30 @@ class _HomePageState extends State<HomePage> {
required String subtitle2}) {
return customDualChoiceAlertdialog(
title: title != "" ? title : null,
child: (subtitle1 != "" || subtitle2 != "")
child: (subtitle1 != "" || subtitle2 != "" || device.isOnline != null)
? Column(
children: [
if (device.isOnline != null)
Text(
device.isOnline!
? AppLocalizations.of(context)!.homeDeviceCardOnline
: AppLocalizations.of(context)!
.homeDeviceCardOffline,
style: TextStyle(
color: device.isOnline!
? AppConstants.successMessageColor
: Theme.of(context).colorScheme.error)),
if (subtitle1 != "") Text(subtitle1),
if (subtitle2 != "") Text(subtitle2),
],
)
: null,
icon: getIcon(device.deviceType),
iconColor: device.isOnline != null
? device.isOnline!
? AppConstants.successMessageColor
: Theme.of(context).colorScheme.error
: null,
leftText: AppLocalizations.of(context)!.homeDeviceCardWakeButton,
rightText: AppLocalizations.of(context)!.homeDeviceCardEditButton,
leftIcon: AppConstants.wakeUp,
Expand All @@ -385,6 +460,7 @@ class _HomePageState extends State<HomePage> {
formPage: EditDeviceFormPage(
title: "Edit Device",
device: device,
devices: _devices,
onSubmitDeviceCallback: updateDevicesList))
});
}
Expand Down
Loading

0 comments on commit e7ad34a

Please sign in to comment.