new icon, some refactoring and first working cycle
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 756 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 867 B |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 756 KiB |
After Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 86 KiB |
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"username": "...",
|
||||
"password": "...",
|
||||
"address": "..."
|
||||
"username": "admin",
|
||||
"password": "nE2Bkf9dwjLASq4VykUNjw7Rivve8W7elowfqYWhuFXaDtLT",
|
||||
"address": "hfu-semester-projekt-oobp.azurewebsites.net"
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/main_page.dart';
|
||||
|
||||
import 'screens/main_screen.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MainPage());
|
||||
runApp(const MainScreen());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:flutter_provisioning_for_iot/objects/bluetooth_object.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_settings.dart';
|
||||
|
||||
class BluetoothDeviceEntry extends ListTile {
|
||||
BluetoothDeviceEntry({
|
||||
super.key,
|
||||
required BuildContext context,
|
||||
required BluetoothDeviceSettings settingsPage,
|
||||
required BluetoothObject bluetoothObject,
|
||||
required BluetoothDevice device,
|
||||
GestureTapCallback? onTap,
|
||||
GestureLongPressCallback? onLongPress,
|
||||
bool enabled = true,
|
||||
}) : super(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
enabled: enabled,
|
||||
leading: Icon(bluetoothObject.isConnected ? Icons.bluetooth_connected : Icons.bluetooth),
|
||||
title: Text(bluetoothObject.name),
|
||||
subtitle: Text(bluetoothObject.address.toString()),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
device.isBonded ? const Icon(Icons.link) : const SizedBox(width: 0, height: 0),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: DefaultTextStyle(
|
||||
style: _computeTextStyle(bluetoothObject.rssi),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(bluetoothObject.rssi.toString()),
|
||||
const Text('dBm'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => settingsPage)); //const BluetoothDeviceEntry()));
|
||||
},
|
||||
icon: const Icon(Icons.settings)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
static TextStyle _computeTextStyle(int rssi) {
|
||||
/**/ if (rssi >= -35) {
|
||||
return TextStyle(color: Colors.greenAccent[700]);
|
||||
} else if (rssi >= -45) {
|
||||
return TextStyle(color: Color.lerp(Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10));
|
||||
} else if (rssi >= -55) {
|
||||
return TextStyle(color: Color.lerp(Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10));
|
||||
} else if (rssi >= -65) {
|
||||
return TextStyle(color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10));
|
||||
} else if (rssi >= -75) {
|
||||
return TextStyle(color: Color.lerp(Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10));
|
||||
} else if (rssi >= -85) {
|
||||
return TextStyle(color: Color.lerp(Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10));
|
||||
} else {
|
||||
return const TextStyle(color: Colors.redAccent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
import '../objects/cloud_service_api.dart';
|
||||
|
||||
class BluetoothObject {
|
||||
late int _rssi = -1;
|
||||
late BluetoothDevice _device;
|
||||
String _address = "";
|
||||
String _name = "";
|
||||
|
||||
String id = "";
|
||||
String _primaryThumbprint = "";
|
||||
String _secondaryThumbprint = "";
|
||||
|
||||
late BluetoothConnection? _connection;
|
||||
late Stream<Uint8List> _connectionStream;
|
||||
late StreamSubscription<Uint8List> _connectionStreamSubscription;
|
||||
|
||||
CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();
|
||||
|
||||
late Uint8List _messageBufferBits;
|
||||
late String _messageBufferChars = "";
|
||||
|
||||
bool _isDisconnecting = false;
|
||||
|
||||
String get primaryThumbprint {
|
||||
return _primaryThumbprint;
|
||||
}
|
||||
|
||||
String get secondaryThumbprint {
|
||||
return _secondaryThumbprint;
|
||||
}
|
||||
|
||||
int get rssi {
|
||||
return _rssi;
|
||||
}
|
||||
|
||||
String get name {
|
||||
return _name;
|
||||
}
|
||||
|
||||
String get address {
|
||||
return _address;
|
||||
}
|
||||
|
||||
BluetoothDevice get device {
|
||||
return _device;
|
||||
}
|
||||
|
||||
BluetoothConnection? get connection {
|
||||
return _connection;
|
||||
}
|
||||
|
||||
bool get isConnected {
|
||||
return (_connection == null ? false : true);
|
||||
}
|
||||
|
||||
BluetoothObject(BluetoothDiscoveryResult result) {
|
||||
_rssi = result.rssi;
|
||||
_device = result.device;
|
||||
_address = _device.address;
|
||||
_name = _device.name ?? "device.name.UNKNOWN";
|
||||
_connection = null;
|
||||
}
|
||||
|
||||
Future<void> bondDevice() async {
|
||||
try {
|
||||
bool bonded = false;
|
||||
if (_device.isBonded) {
|
||||
debugPrint('Unbonding from $_address...');
|
||||
await FlutterBluetoothSerial.instance.removeDeviceBondWithAddress(_address);
|
||||
debugPrint('Unbonding from $_address has succed');
|
||||
} else {
|
||||
debugPrint('Bonding with $_address...');
|
||||
bonded = (await FlutterBluetoothSerial.instance.bondDeviceAtAddress(_address))!;
|
||||
debugPrint('Bonding with $_address has ${bonded ? 'succed' : 'failed'}.');
|
||||
}
|
||||
/*setState(() {
|
||||
_discoveryResults[_discoveryResults.indexOf(
|
||||
result)] =
|
||||
BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
bondState: bonded
|
||||
? BluetoothBondState.bonded
|
||||
: BluetoothBondState.none,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});*/
|
||||
} catch (ex) {
|
||||
/*
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
'Error occured while bonding'),
|
||||
content: Text(ex.toString()),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text("Close"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);*/
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnectDevice() async {
|
||||
_isDisconnecting = true;
|
||||
if (_connection != null) {
|
||||
await _connection!.finish();
|
||||
_connection = null;
|
||||
}
|
||||
_isDisconnecting = false;
|
||||
}
|
||||
|
||||
Future<void> connectDevice() async {
|
||||
if (isConnected) {
|
||||
await _connection!.finish();
|
||||
_connection = null;
|
||||
}
|
||||
|
||||
_connection = await BluetoothConnection.toAddress(_address);
|
||||
debugPrint("Connected to the device");
|
||||
|
||||
_connectionStream = _connection!.input!;
|
||||
_connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
|
||||
_connectionStreamSubscription.onDone(_connectionOnDone);
|
||||
}
|
||||
|
||||
void _connectionOnDone() {
|
||||
if (_isDisconnecting) {
|
||||
debugPrint("Disconnecting locally!");
|
||||
} else {
|
||||
debugPrint("Disconnected remotely!");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectionOnListen(Uint8List data) async {
|
||||
final String dataDecoded = const AsciiDecoder().convert(data);
|
||||
|
||||
debugPrint("received: $data");
|
||||
debugPrint("received decoded: $dataDecoded");
|
||||
|
||||
_messageBufferChars += dataDecoded;
|
||||
|
||||
if (data[data.length - 1] == 10) {
|
||||
debugPrint("received buffer: $_messageBufferChars");
|
||||
int spaceIndex = _messageBufferChars.indexOf(" ");
|
||||
String firstParameter = "";
|
||||
String secondParameter = "";
|
||||
try {
|
||||
firstParameter = _messageBufferChars.substring(0, spaceIndex);
|
||||
secondParameter = _messageBufferChars.substring(spaceIndex + 1, _messageBufferChars.length - 2);
|
||||
} catch (ex) {
|
||||
debugPrint(ex.toString());
|
||||
}
|
||||
debugPrint("we still go on!");
|
||||
_messageBufferChars = "";
|
||||
debugPrint("first: $firstParameter");
|
||||
debugPrint("second: $secondParameter");
|
||||
if (firstParameter == "fingerprint") {
|
||||
debugPrint("received final buffer: $secondParameter");
|
||||
_primaryThumbprint = secondParameter;
|
||||
debugPrint("{ _secondaryThumbprint: $_secondaryThumbprint }");
|
||||
debugPrint("{ id: $id, _primaryThumbprint: $_primaryThumbprint, _secondaryThumbprint: $_secondaryThumbprint }");
|
||||
await _registerDevice();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Allocate buffer for parsed data
|
||||
int backspacesCounter = 0;
|
||||
for (var byte in data) {
|
||||
if (byte == 8 || byte == 127) {
|
||||
backspacesCounter++;
|
||||
}
|
||||
}
|
||||
Uint8List buffer = Uint8List(data.length - backspacesCounter);
|
||||
int bufferIndex = buffer.length;
|
||||
|
||||
// Apply backspace control character
|
||||
backspacesCounter = 0;
|
||||
for (int i = data.length - 1; i >= 0; i--) {
|
||||
if (data[i] == 8 || data[i] == 127) {
|
||||
backspacesCounter++;
|
||||
} else {
|
||||
if (backspacesCounter > 0) {
|
||||
backspacesCounter--;
|
||||
} else {
|
||||
buffer[--bufferIndex] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create message if there is new line character
|
||||
String dataString = String.fromCharCodes(buffer);
|
||||
int index = buffer.indexOf(13);
|
||||
if (~index != 0) {
|
||||
_messageBuffer = dataString.substring(index);
|
||||
} else {
|
||||
_messageBuffer = (backspacesCounter > 0
|
||||
? _messageBuffer.substring(0, _messageBuffer.length - backspacesCounter)
|
||||
: _messageBuffer + dataString);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Future<void> sendData(String output) async {
|
||||
if (_connection == null) return;
|
||||
bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output);
|
||||
// String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
|
||||
// String output = "myDevice123";// !§%&()=?#!?";
|
||||
if (!nameAvailable) return;
|
||||
id = output;
|
||||
_connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
|
||||
await _connection!.output.allSent;
|
||||
debugPrint("sent: $output");
|
||||
}
|
||||
|
||||
Future<void> _registerDevice() async {
|
||||
bool registered = false;
|
||||
registered = await _cloudServiceAPI.createDevice(id, _primaryThumbprint, "");
|
||||
|
||||
String statusText =
|
||||
registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden";
|
||||
Fluttertoast.showToast(
|
||||
msg: statusText,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
timeInSecForIosWeb: 2,
|
||||
backgroundColor: Colors.grey[200],
|
||||
textColor: Colors.black,
|
||||
fontSize: 16.0);
|
||||
}
|
||||
}
|
||||
|
||||
/*setState(() {
|
||||
_discoveryResults[_discoveryResults.indexOf(
|
||||
result)] =
|
||||
BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
isConnected: connected,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});
|
||||
*/
|
|
@ -1,32 +1,117 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import '../schemas/device_info_model.dart';
|
||||
import '../schemas/device_model.dart';
|
||||
|
||||
Future<Map<String, dynamic>> readJson() async {
|
||||
final data = await rootBundle.loadString('config/credentials.json');
|
||||
var data_ = json.decode(data) as Map<String, dynamic>;
|
||||
log("loaded json input $data_");
|
||||
return data_;
|
||||
final String data = await rootBundle.loadString('config/credentials.json');
|
||||
final Map<String, dynamic> dataMap = json.decode(data);
|
||||
log("loaded json input $dataMap");
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/*Future<void> checkNameAvailability(String input) async {
|
||||
class CloudServiceAPI {
|
||||
static late final Map<String, dynamic> credentials;
|
||||
static late String _basicAuth;
|
||||
static late String _address;
|
||||
static late String _username;
|
||||
static late String _password;
|
||||
static late Map<String, String> _headers;
|
||||
static bool _initState = false;
|
||||
|
||||
CloudServiceAPI() {
|
||||
if (!_initState) {
|
||||
loadConfig();
|
||||
_initState = true;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadConfig() async {
|
||||
credentials = await readJson();
|
||||
_username = credentials['username'];
|
||||
_password = credentials['password'];
|
||||
_address = credentials['address'];
|
||||
_basicAuth = 'Basic ${base64.encode(utf8.encode('$_username:$_password'))}';
|
||||
_headers = {
|
||||
'authorization': _basicAuth,
|
||||
'content-type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
log("init Config $_headers");
|
||||
}
|
||||
|
||||
Future<void> reloadConfig() async {
|
||||
_basicAuth = 'Basic ${base64.encode(utf8.encode('$_username:$_password'))}';
|
||||
_headers = {
|
||||
'authorization': _basicAuth,
|
||||
'content-type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
}
|
||||
|
||||
Future<List<DeviceModel>> getDevices() async {
|
||||
Uri url = Uri.https(_address, '/api/devices');
|
||||
Response responnse = await get(url, headers: _headers);
|
||||
dynamic jsonObject = json.decode(responnse.body);
|
||||
Iterable<dynamic> remoteObjectsIterable = Iterable.castFrom(jsonObject);
|
||||
Iterable<DeviceModel> remoteObjectsMap = remoteObjectsIterable.map((data) => DeviceModel.fromJson(data));
|
||||
List<DeviceModel> remoteObjectsList = remoteObjectsMap.toList();
|
||||
return remoteObjectsList;
|
||||
}
|
||||
|
||||
Future<DeviceInfoModel> getDeviceInfo(String deviceID) async {
|
||||
Uri url = Uri.https(_address, '/api/devices/$deviceID');
|
||||
Response response = await get(url, headers: _headers);
|
||||
dynamic jsonObject = json.decode(response.body);
|
||||
DeviceInfoModel deviceInfoModel = DeviceInfoModel.fromJson(jsonObject);
|
||||
return deviceInfoModel;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getInformation() async {
|
||||
Uri url = Uri.https(_address, '/api/app');
|
||||
Response response = await get(url, headers: _headers);
|
||||
return json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<bool> createDevice(String id, String primaryThumbprint, String secondaryThumbprint) async {
|
||||
Uri url = Uri.https(_address, '/api/devices');
|
||||
Response response = await post(url,
|
||||
headers: _headers,
|
||||
body: jsonEncode(<String, String>{
|
||||
'id': id,
|
||||
'primaryThumbprint': primaryThumbprint,
|
||||
'secondaryThumbprint': secondaryThumbprint
|
||||
}));
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
}
|
||||
debugPrint('Error createDevice: ${response.statusCode.toString()}');
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> checkNameAvailability(String inputID) async {
|
||||
// await cloudServiceAPI.loadConfig();
|
||||
List<dynamic> devices = await cloudServiceAPI.getDevices();
|
||||
for (Map<String, dynamic> selected in devices) {
|
||||
if (selected["id"] == input) {
|
||||
await showNameAvailabilityStatus(true);
|
||||
return;
|
||||
List<DeviceModel> devices = await getDevices();
|
||||
for (DeviceModel selected in devices) {
|
||||
if (selected.id == inputID) {
|
||||
debugPrint("ID 1: ${selected.id}");
|
||||
debugPrint("ID 2: $inputID");
|
||||
await showNameAvailabilityStatus(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
await showNameAvailabilityStatus(false);
|
||||
await showNameAvailabilityStatus(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> showNameAvailabilityStatus(bool status) async {
|
||||
String statusText = status
|
||||
? "die eingegebene ID ist verfügbar"
|
||||
: "die eingegebene ID ist nicht verfügbar";
|
||||
String statusText = status ? "die eingegebene ID ist verfügbar" : "die eingegebene ID ist nicht verfügbar";
|
||||
Fluttertoast.showToast(
|
||||
msg: statusText,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
|
@ -35,97 +120,32 @@ Future<Map<String, dynamic>> readJson() async {
|
|||
backgroundColor: Colors.grey[200],
|
||||
textColor: Colors.black,
|
||||
fontSize: 16.0);
|
||||
}*/
|
||||
|
||||
class CloudServiceAPI {
|
||||
static late final Map<String, dynamic> credentials;
|
||||
static late Future loadJson;
|
||||
static late String basicAuth;
|
||||
static late String address;
|
||||
static late String username;
|
||||
static late String password;
|
||||
static late Map<String, String> headers;
|
||||
static bool initState = false;
|
||||
|
||||
CloudServiceAPI(){
|
||||
if(!initState) {
|
||||
loadJson = loadConfig();
|
||||
initState = true;
|
||||
}
|
||||
}
|
||||
|
||||
Future loadConfig() async{
|
||||
credentials = await readJson();
|
||||
username = credentials['username'];
|
||||
password = credentials['password'];
|
||||
address = credentials['address'];
|
||||
basicAuth = 'Basic ${base64.encode(utf8.encode('$username:$password'))}';
|
||||
headers = {
|
||||
'authorization': basicAuth,
|
||||
'content-type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
log("init Config $headers");
|
||||
String get address {
|
||||
return _address;
|
||||
}
|
||||
void reloadConfig() {
|
||||
basicAuth = 'Basic ${base64.encode(utf8.encode('$username:$password'))}';
|
||||
headers = {
|
||||
'authorization': basicAuth,
|
||||
'content-type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
|
||||
String get username {
|
||||
return _username;
|
||||
}
|
||||
Future<List> getDevices() async {
|
||||
Uri url = Uri.https(address, '/api/devices');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as List<dynamic>;
|
||||
|
||||
String get password {
|
||||
return _password;
|
||||
}
|
||||
Future<Map<String, dynamic>> getDeviceInfo(deviceID) async {
|
||||
Uri url = Uri.https(address, '/api/devices/$deviceID');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as Map<String, dynamic>;
|
||||
}
|
||||
Future<Map<String, dynamic>> getInformation() async {
|
||||
Uri url = Uri.https(address, '/api/app');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as Map<String, dynamic>;
|
||||
}
|
||||
Future<bool> createDevice(id, primaryThumbprint, secondaryThumbprint) async{
|
||||
Uri url = Uri.https(address, '/api/devices');
|
||||
Response r = await post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: jsonEncode(<String, String>{
|
||||
'id': id,
|
||||
'primaryThumbprint' : primaryThumbprint,
|
||||
'secondaryThumbprint' : secondaryThumbprint
|
||||
})
|
||||
);
|
||||
if (r.statusCode == 200){
|
||||
return true;
|
||||
}
|
||||
debugPrint('Error createDevice: ${r.statusCode.toString()}');
|
||||
return false;
|
||||
}
|
||||
String getAddress(){
|
||||
return address;
|
||||
}
|
||||
String getUsername(){
|
||||
return username;
|
||||
}
|
||||
String getPassword(){
|
||||
return password;
|
||||
}
|
||||
void setAddress(String input){
|
||||
address = input;
|
||||
|
||||
set address(String input) {
|
||||
_address = input;
|
||||
reloadConfig();
|
||||
}
|
||||
void setUsername(String input){
|
||||
username = input;
|
||||
|
||||
set username(String input) {
|
||||
_username = input;
|
||||
reloadConfig();
|
||||
}
|
||||
void setPassword(String input){
|
||||
password = input;
|
||||
|
||||
set password(String input) {
|
||||
_password = input;
|
||||
reloadConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,3 @@ class CustomColor {
|
|||
return MaterialColor(color.value, swatch);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
|
||||
class DeviceInfoModel {
|
||||
final String id;
|
||||
final String endpoint;
|
||||
final String status;
|
||||
final String connectionState;
|
||||
final String lastActivityTime;
|
||||
final String primaryThumbprint;
|
||||
final String secondaryThumbprint;
|
||||
|
||||
const DeviceInfoModel({
|
||||
required this.id,
|
||||
required this.endpoint,
|
||||
required this.status,
|
||||
required this.connectionState,
|
||||
required this.lastActivityTime,
|
||||
required this.primaryThumbprint,
|
||||
required this.secondaryThumbprint,
|
||||
});
|
||||
|
||||
factory DeviceInfoModel.fromJson(Map<String, dynamic> parsedJson) {
|
||||
return DeviceInfoModel(
|
||||
id: parsedJson['id'].toString(),
|
||||
endpoint: parsedJson['endpoint'],
|
||||
status: parsedJson['status'],
|
||||
connectionState: parsedJson['connectionState'],
|
||||
lastActivityTime: parsedJson['lastActivityTime'],
|
||||
primaryThumbprint: parsedJson['primaryThumbprint'],
|
||||
secondaryThumbprint: parsedJson['secondaryThumbprint']);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "{"
|
||||
"id: $id, "
|
||||
"endpoint: $endpoint, "
|
||||
"status: $status, "
|
||||
"connectionState: $connectionState, "
|
||||
"lastActivityTime: $lastActivityTime, "
|
||||
"primaryThumbprint: $primaryThumbprint, "
|
||||
"secondaryThumbprint: $secondaryThumbprint"
|
||||
"}";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
|
||||
class DeviceModel {
|
||||
final String id;
|
||||
final String endpoint;
|
||||
final String status;
|
||||
final String connectionState;
|
||||
final String lastActivityTime;
|
||||
|
||||
const DeviceModel(
|
||||
{required this.id,
|
||||
required this.endpoint,
|
||||
required this.status,
|
||||
required this.connectionState,
|
||||
required this.lastActivityTime});
|
||||
|
||||
factory DeviceModel.fromJson(Map<String, dynamic> parsedJson) {
|
||||
return DeviceModel(
|
||||
id: parsedJson['id'].toString(),
|
||||
endpoint: parsedJson['endpoint'],
|
||||
status: parsedJson['status'],
|
||||
connectionState: parsedJson['connectionState'],
|
||||
lastActivityTime: parsedJson['lastActivityTime']);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "{"
|
||||
"id: $id, "
|
||||
"endpoint: $endpoint, "
|
||||
"status: $status, "
|
||||
"connectionState: $connectionState, "
|
||||
"lastActivityTime: $lastActivityTime, "
|
||||
"}";
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
|
||||
class BluetoothDeviceListEntry extends ListTile {
|
||||
BluetoothDeviceListEntry({super.key,
|
||||
required BluetoothDevice device,
|
||||
int? rssi,
|
||||
GestureTapCallback? onTap,
|
||||
GestureLongPressCallback? onLongPress,
|
||||
bool enabled = true,
|
||||
}) : super(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
enabled: enabled,
|
||||
leading: Icon(device.isConnected ? Icons.bluetooth_connected : Icons.bluetooth),
|
||||
title: Text(device.name ?? "~ UNKNOWN_DEVICE ~"),
|
||||
subtitle: Text(device.address.toString()),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
rssi != null
|
||||
? Container(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: DefaultTextStyle(
|
||||
style: _computeTextStyle(rssi),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(rssi.toString()),
|
||||
const Text('dBm'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(width: 0, height: 0),
|
||||
device.isBonded
|
||||
? const Icon(Icons.link)
|
||||
: const SizedBox(width: 0, height: 0),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.settings)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
static TextStyle _computeTextStyle(int rssi) {
|
||||
/**/ if (rssi >= -35) {
|
||||
return TextStyle(color: Colors.greenAccent[700]);
|
||||
} else if (rssi >= -45) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10));
|
||||
} else if (rssi >= -55) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10));
|
||||
} else if (rssi >= -65) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10));
|
||||
} else if (rssi >= -75) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10));
|
||||
} else if (rssi >= -85) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10));
|
||||
} else {
|
||||
return const TextStyle(color: Colors.redAccent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,107 +1,121 @@
|
|||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BluetoothDeviceSettings extends StatefulWidget {
|
||||
final BluetoothDevice device;
|
||||
import '../objects/bluetooth_object.dart';
|
||||
import '../widgets/single_section.dart';
|
||||
|
||||
const BluetoothDeviceSettings({super.key, required this.device});
|
||||
class BluetoothDeviceSettings extends StatefulWidget {
|
||||
final BluetoothObject bluetoothObject;
|
||||
|
||||
const BluetoothDeviceSettings({super.key, required this.bluetoothObject});
|
||||
|
||||
@override
|
||||
State<BluetoothDeviceSettings> createState() => _BluetoothDeviceSettings();
|
||||
State<StatefulWidget> createState() => BluetoothDeviceSettingsState();
|
||||
}
|
||||
|
||||
class _BluetoothDeviceSettings extends State<BluetoothDeviceSettings> {
|
||||
class BluetoothDeviceSettingsState extends State<BluetoothDeviceSettings> {
|
||||
late BluetoothObject _bluetoothObject;
|
||||
|
||||
String _messageBuffer = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_bluetoothObject = widget.bluetoothObject;
|
||||
}
|
||||
|
||||
String _textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
String _textOuput = "";
|
||||
|
||||
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint(widget.device.address);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Bluetooth Devices"),
|
||||
title: const Text("Device Settings"),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
body: Center(
|
||||
child: ListView(children: [
|
||||
Column(children: <Widget>[
|
||||
SingleSection(
|
||||
title: "Device Information",
|
||||
children: [
|
||||
Table(
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: <TableRow>[
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom:
|
||||
BorderSide(width: 1.5, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
const TableCell(child: Text("Address")),
|
||||
TableCell(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 50,
|
||||
child: Text(
|
||||
widget.device.address
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom:
|
||||
BorderSide(width: 1.5, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
TableCell(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 50,
|
||||
child: const Text(
|
||||
"Name"
|
||||
)
|
||||
)
|
||||
),
|
||||
TableCell(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 50,
|
||||
child: Text(
|
||||
widget.device.name.toString()
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom:
|
||||
BorderSide(width: 1.5, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
TableCell(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 50,
|
||||
child: const Text(
|
||||
"Connected"
|
||||
)
|
||||
)
|
||||
),
|
||||
TableCell(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 50,
|
||||
child: Text(
|
||||
widget.device.isConnected.toString()
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
],
|
||||
ListTile(
|
||||
title: const Text("Device Name"),
|
||||
subtitle: Text(_bluetoothObject.name),
|
||||
),
|
||||
])));
|
||||
ListTile(
|
||||
title: const Text("Device Adress"),
|
||||
subtitle: Text(_bluetoothObject.address),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text("Device Connection State"),
|
||||
subtitle:
|
||||
Text(_bluetoothObject.isConnected ? "ConnectionState.DISCONECTED" : "ConnectionState.CONNECED"),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||
// const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (!_bluetoothObject.isConnected) {
|
||||
await _bluetoothObject.connectDevice();
|
||||
}
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Connect"),
|
||||
)),
|
||||
const SizedBox(width: 16.0),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (_bluetoothObject.isConnected) {
|
||||
await _bluetoothObject.disconnectDevice();
|
||||
}
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Disconnect"),
|
||||
))
|
||||
])),
|
||||
const Divider(),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
_bluetoothObject.sendData(_textInput);
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Send Data"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
_textOuput = "";
|
||||
});
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Clear Output"),
|
||||
),
|
||||
Text(_textOuput),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||
child: TextField(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Enter the device name',
|
||||
),
|
||||
onChanged: (String newValue) {
|
||||
_textInput = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_list_entry.dart';
|
||||
|
||||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import '../objects/bluetooth_device_entry.dart';
|
||||
import '../objects/bluetooth_object.dart';
|
||||
import '../screens/bluetooth_device_settings.dart';
|
||||
import '../widgets/custom_list_tile.dart';
|
||||
import '../widgets/single_section.dart';
|
||||
|
||||
class BluetoothScreen extends StatefulWidget {
|
||||
final bool start = true;
|
||||
|
@ -23,36 +22,34 @@ class BluetoothScreen extends StatefulWidget {
|
|||
|
||||
// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}");
|
||||
class _BluetoothScreen extends State<BluetoothScreen> {
|
||||
final List<BluetoothObject> _discoveryResults = [];
|
||||
|
||||
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
|
||||
//BluetoothManager bluetoothManager = BluetoothManager();
|
||||
late BluetoothObject? _activeObject;
|
||||
|
||||
late Stream<BluetoothDiscoveryResult> _stream;
|
||||
late StreamSubscription<BluetoothDiscoveryResult> _streamSubscription;
|
||||
late Stream<Uint8List> _connectionStream;
|
||||
late StreamSubscription<Uint8List> _connectionStreamSubscription;
|
||||
BluetoothDevice? _bluetoothDevice;
|
||||
BluetoothConnection? _bluetoothConnection;
|
||||
final List<BluetoothDiscoveryResult> _discoveryResults =
|
||||
List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
String _messageBuffer = "";
|
||||
bool isDiscovering = false;
|
||||
bool isConnecting = false;
|
||||
bool isDisconnecting = false;
|
||||
bool isConnected = false;
|
||||
String textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
String textOuput = "";
|
||||
late Stream<BluetoothDiscoveryResult>? _stream;
|
||||
late StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
|
||||
|
||||
bool _isDiscovering = false;
|
||||
bool _enabledPermissions = false;
|
||||
bool _enabledBluetooth = false;
|
||||
|
||||
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
|
||||
);
|
||||
|
||||
bool get _hasActiveObject {
|
||||
return _activeObject == null ? false : true;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_enablePermissions();
|
||||
_enableBluetooth();
|
||||
super.initState();
|
||||
isDiscovering = widget.start;
|
||||
if (isDiscovering) {
|
||||
_activeObject = null;
|
||||
_streamSubscription = null;
|
||||
if (widget.start) {
|
||||
_startDiscovery();
|
||||
}
|
||||
}
|
||||
|
@ -60,522 +57,216 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
@override
|
||||
void dispose() {
|
||||
// Avoid memory leak (`setState` after dispose) and cancel discovery
|
||||
_streamSubscription.cancel();
|
||||
if (isConnected) {
|
||||
isDisconnecting = true;
|
||||
_bluetoothConnection?.dispose();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
_streamSubscription?.cancel();
|
||||
_activeObject?.disconnectDevice();
|
||||
super.dispose();
|
||||
debugPrint("called dispose");
|
||||
}
|
||||
|
||||
Future<void> _disconnectDevice(BluetoothDevice device) async {
|
||||
if (_bluetoothConnection != null) {
|
||||
await _bluetoothConnection!.finish();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectDevice(BluetoothDevice device) async {
|
||||
String address = device.address;
|
||||
if (_bluetoothConnection != null) {
|
||||
await _bluetoothConnection!.finish();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
_bluetoothConnection = await BluetoothConnection.toAddress(address);
|
||||
debugPrint("Connected to the device");
|
||||
Future<void> _enablePermissions() async {
|
||||
debugPrint("Test");
|
||||
PermissionStatus bluetoothScan = await Permission.bluetoothScan.request();
|
||||
PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request();
|
||||
bool granted = bluetoothScan.isGranted && bluetoothConnect.isGranted;
|
||||
setState(() {
|
||||
isConnecting = false;
|
||||
isDisconnecting = false;
|
||||
_enabledPermissions = granted;
|
||||
});
|
||||
|
||||
_connectionStream = _bluetoothConnection!.input!;
|
||||
_connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
|
||||
_connectionStreamSubscription.onDone(_connectionOnDone);
|
||||
}
|
||||
|
||||
void _connectionOnDone() {
|
||||
/*
|
||||
// Example: Detect which side closed the connection
|
||||
// There should be `isDisconnecting` flag to show are we are (locally)
|
||||
// in middle of disconnecting process, should be set before calling
|
||||
// `dispose`, `finish` or `close`, which all causes to disconnect.
|
||||
// If we except the disconnection, `onDone` should be fired as result.
|
||||
// If we didn't except this (no flag set), it means closing by remote.
|
||||
*/
|
||||
if (isDisconnecting) {
|
||||
debugPrint("Disconnecting locally!");
|
||||
} else {
|
||||
debugPrint("Disconnected remotely!");
|
||||
Future<void> _enableBluetooth() async {
|
||||
if (!_enabledPermissions) {
|
||||
return;
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _connectionOnListen(Uint8List data) {
|
||||
debugPrint("received: $data");
|
||||
debugPrint("received decoded: ${const Utf8Decoder().convert(data)}");
|
||||
bool? enabled = await FlutterBluetoothSerial.instance.requestEnable();
|
||||
enabled ??= false;
|
||||
setState(() {
|
||||
textOuput += const Utf8Decoder().convert(data);
|
||||
_enabledBluetooth = enabled!;
|
||||
});
|
||||
// Allocate buffer for parsed data
|
||||
int backspacesCounter = 0;
|
||||
for (var byte in data) {
|
||||
if (byte == 8 || byte == 127) {
|
||||
backspacesCounter++;
|
||||
}
|
||||
}
|
||||
Uint8List buffer = Uint8List(data.length - backspacesCounter);
|
||||
int bufferIndex = buffer.length;
|
||||
|
||||
// Apply backspace control character
|
||||
backspacesCounter = 0;
|
||||
for (int i = data.length - 1; i >= 0; i--) {
|
||||
if (data[i] == 8 || data[i] == 127) {
|
||||
backspacesCounter++;
|
||||
} else {
|
||||
if (backspacesCounter > 0) {
|
||||
backspacesCounter--;
|
||||
} else {
|
||||
buffer[--bufferIndex] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create message if there is new line character
|
||||
String dataString = String.fromCharCodes(buffer);
|
||||
int index = buffer.indexOf(13);
|
||||
if (~index != 0) {
|
||||
setState(() {
|
||||
_messageBuffer = dataString.substring(index);
|
||||
});
|
||||
} else {
|
||||
_messageBuffer = (backspacesCounter > 0
|
||||
? _messageBuffer.substring(
|
||||
0, _messageBuffer.length - backspacesCounter)
|
||||
: _messageBuffer + dataString);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startDiscovery() async {
|
||||
if (!_enabledPermissions) {
|
||||
return;
|
||||
}
|
||||
setState(() => _isDiscovering = true);
|
||||
_stream = FlutterBluetoothSerial.instance.startDiscovery();
|
||||
_streamSubscription = _stream.listen(_discoveryOnListen);
|
||||
_streamSubscription.onDone(_discoveryOnDone);
|
||||
_streamSubscription = _stream!.listen(_discoveryOnListen);
|
||||
_streamSubscription!.onDone(_discoveryOnDone);
|
||||
}
|
||||
|
||||
void _discoveryOnDone() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
//debugPrint(isDiscovering as String?);
|
||||
});
|
||||
Future<void> _discoveryOnDone() async {
|
||||
setState(() => _isDiscovering = false);
|
||||
}
|
||||
|
||||
void _discoveryOnListen(BluetoothDiscoveryResult event) {
|
||||
Future<void> _cancelDiscovery() async {
|
||||
await _streamSubscription?.cancel();
|
||||
setState(() => _isDiscovering = false);
|
||||
}
|
||||
|
||||
Future<void> _discoveryOnListen(BluetoothDiscoveryResult event) async {
|
||||
setState(() {
|
||||
final int existingIndex = _discoveryResults.indexWhere(
|
||||
(element) => element.device.address == event.device.address);
|
||||
final int existingIndex =
|
||||
_discoveryResults.indexWhere((element) => element.device.address == event.device.address);
|
||||
if (existingIndex >= 0) {
|
||||
_discoveryResults[existingIndex] = event;
|
||||
_discoveryResults[existingIndex] = BluetoothObject(event);
|
||||
} else {
|
||||
_discoveryResults.add(event);
|
||||
}
|
||||
debugPrint("event: ${event.device.address} ${event.device.name}");
|
||||
|
||||
String deviceAddress = "64:BC:58:61:56:B0";
|
||||
if (event.device.address == deviceAddress) {
|
||||
_bluetoothDevice = event.device;
|
||||
_discoveryResults.add(BluetoothObject(event));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _restartDiscovery() async {
|
||||
setState(() {
|
||||
_discoveryResults.clear();
|
||||
isDiscovering = true;
|
||||
});
|
||||
_startDiscovery();
|
||||
_discoveryResults.clear();
|
||||
if (_hasActiveObject) {
|
||||
_discoveryResults.add(_activeObject!);
|
||||
}
|
||||
if (_streamSubscription != null) {
|
||||
await _cancelDiscovery();
|
||||
}
|
||||
await _startDiscovery();
|
||||
}
|
||||
|
||||
Future<void> _sendData() async {
|
||||
//String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
|
||||
String output = textInput;
|
||||
_bluetoothConnection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
|
||||
await _bluetoothConnection!.output.allSent;
|
||||
debugPrint("sent: $output");
|
||||
Future<void> _toggleConnection(BluetoothObject bluetoothObject) async {
|
||||
await _cancelDiscovery();
|
||||
if (_hasActiveObject) {
|
||||
_activeObject!.disconnectDevice();
|
||||
_activeObject = bluetoothObject;
|
||||
} else {
|
||||
_activeObject = bluetoothObject;
|
||||
}
|
||||
_activeObject!.isConnected ? await bluetoothObject.disconnectDevice() : await bluetoothObject.connectDevice();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//bool isDark = true;
|
||||
return Theme(
|
||||
data: ThemeData.dark(), //isDark ? ThemeData.dark() : ThemeData.light(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: isDiscovering
|
||||
? const Text('Bluetooth Devices (searching...)')
|
||||
: const Text('Bluetooth Devices'),
|
||||
actions: <Widget>[
|
||||
isDiscovering
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16.0),
|
||||
child: const CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.replay),
|
||||
onPressed: _restartDiscovery,
|
||||
)
|
||||
],
|
||||
),
|
||||
//stty -F /dev/service 19200 parenb -parodd -cstopb cs8
|
||||
//cat /dev/service | xargs -n 1 /home/script/automaticcloud.sh
|
||||
|
||||
body: // Center(
|
||||
/*child:*/ SingleChildScrollView(
|
||||
physics: const ScrollPhysics(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_SingleSection(
|
||||
title: "Setup",
|
||||
children: [
|
||||
const _CustomListTile(
|
||||
title: "Please Enable Bluetooth",
|
||||
icon: Icons.info_outline_rounded,
|
||||
),
|
||||
_CustomListTile(
|
||||
title: "Bluetooth Settings",
|
||||
icon: Icons.bluetooth_connected,
|
||||
onTap: () async {
|
||||
await AppSettings.openBluetoothSettings();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
Text(textOuput),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "BluetoothText"
|
||||
),
|
||||
initialValue: textInput,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (String newValue) {
|
||||
textInput = newValue;
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await Permission.bluetoothConnect.request();
|
||||
await Permission.bluetoothScan.request();
|
||||
if (await Permission.bluetoothScan.request().isGranted) {
|
||||
// Either the permission was already granted before or the user just granted it.
|
||||
debugPrint("Location Permission is granted");
|
||||
} else {
|
||||
debugPrint("Location Permission is denied.");
|
||||
}
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Request Permissions"),
|
||||
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
textOuput = "";
|
||||
});
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Clear Output"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
_restartDiscovery();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Restart Scan"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
_sendData();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Send Data"),
|
||||
),
|
||||
const Divider(),
|
||||
const _SingleSection(
|
||||
title: "Bluetooth Devices",
|
||||
children: [],
|
||||
),
|
||||
const Divider(),
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: _discoveryResults.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
BluetoothDiscoveryResult result = _discoveryResults[index];
|
||||
final device = result.device;
|
||||
final address = device.address;
|
||||
return BluetoothDeviceListEntry(
|
||||
device: device,
|
||||
rssi: result.rssi,
|
||||
onTap: () {
|
||||
debugPrint(_bluetoothConnection?.isConnected.toString());
|
||||
if (_bluetoothConnection?.isConnected ?? false) {
|
||||
_disconnectDevice(device);
|
||||
} else {
|
||||
_connectDevice(device);
|
||||
}
|
||||
|
||||
//Navigator.of(context).pop(result.device);
|
||||
},
|
||||
onLongPress: () async {
|
||||
try {
|
||||
bool bonded = false;
|
||||
if (device.isBonded) {
|
||||
debugPrint('Unbonding from ${device.address}...');
|
||||
await FlutterBluetoothSerial.instance
|
||||
.removeDeviceBondWithAddress(address);
|
||||
debugPrint(
|
||||
'Unbonding from ${device.address} has succed');
|
||||
} else {
|
||||
debugPrint('Bonding with ${device.address}...');
|
||||
bonded = (await FlutterBluetoothSerial.instance
|
||||
.bondDeviceAtAddress(address))!;
|
||||
debugPrint(
|
||||
'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.');
|
||||
}
|
||||
setState(() {
|
||||
_discoveryResults[_discoveryResults.indexOf(result)] =
|
||||
BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
bondState: bonded
|
||||
? BluetoothBondState.bonded
|
||||
: BluetoothBondState.none,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});
|
||||
} catch (ex) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Error occured while bonding'),
|
||||
content: Text(ex.toString()),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text("Close"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
//),
|
||||
),
|
||||
);
|
||||
}
|
||||
/*
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Bluetooth Devices"),
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () {
|
||||
debugPrint("refreshed");
|
||||
return Future(() => null);
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
//child: Padding(
|
||||
//padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1.5, color: Colors.grey),
|
||||
title: _isDiscovering ? const Text('Bluetooth Devices (searching...)') : const Text('Bluetooth Devices'),
|
||||
actions: <Widget>[
|
||||
_isDiscovering
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16.0),
|
||||
child: const CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.replay),
|
||||
onPressed: _restartDiscovery,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(children: [
|
||||
SingleChildScrollView(
|
||||
physics: const ScrollPhysics(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SingleSection(
|
||||
title: "Setup",
|
||||
children: [
|
||||
const Text(
|
||||
"General",
|
||||
textAlign: TextAlign.start,
|
||||
CustomListTile(
|
||||
title: _enabledBluetooth ? "Bluetooth Enabled" : "Please Enable Bluetooth (click me)",
|
||||
icon: _enabledBluetooth ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
|
||||
onTap: () async {
|
||||
await _enableBluetooth();
|
||||
},
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
_CustomListTile(
|
||||
title: "Dark Mode",
|
||||
icon: CupertinoIcons.moon,
|
||||
trailing:
|
||||
CupertinoSwitch(value: false, onChanged: (value) {})),
|
||||
]
|
||||
CustomListTile(
|
||||
title: _enabledPermissions ? "Permissions Granted" : "Please Grant Permissions (click me)",
|
||||
icon: _enabledPermissions ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
|
||||
onTap: () async {
|
||||
await _enablePermissions();
|
||||
},
|
||||
),
|
||||
TextField(
|
||||
controller: textFieldValueHolder,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Enter the name of your new device',
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
var textFieldValue = textFieldValueHolder.value.text;
|
||||
checkNameAvailability(textFieldValue);
|
||||
setState(() {
|
||||
inputName = textFieldValue;
|
||||
});
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("check name"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
initScan();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("discover devices"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
_sendData();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("send data"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
dispose();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("dispose"),
|
||||
),
|
||||
CustomListTile(
|
||||
title: "App Settings (if permissions denied)",
|
||||
icon: Icons.settings,
|
||||
onTap: () async {
|
||||
await AppSettings.openAppSettings();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1.5, color: Colors.grey),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||
// const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
/*await Permission.bluetoothConnect.request();
|
||||
await Permission.bluetoothScan.request();
|
||||
if (await Permission.bluetoothScan.request().isGranted) {
|
||||
// Either the permission was already granted before or the user just granted it.
|
||||
debugPrint("Location Permission is granted");
|
||||
} else {
|
||||
debugPrint("Location Permission is denied.");
|
||||
}*/
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Get BT Permissions"),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16.0),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
_restartDiscovery();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Restart Scan"),
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
child: Row(children: [
|
||||
const Text(
|
||||
"Toggle Scan",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Expanded(child: Text("")),
|
||||
Switch(
|
||||
value: widgetScanState,
|
||||
onChanged: (bool toggleState) {
|
||||
scanState = toggleState;
|
||||
log("widget state: $scanState");
|
||||
setState(() {
|
||||
widgetScanState = toggleState;
|
||||
});
|
||||
})
|
||||
]),
|
||||
),
|
||||
// BluetoothDiscovery(start: initScan, deviceID: inputName),
|
||||
],
|
||||
),
|
||||
//),
|
||||
),
|
||||
),
|
||||
);
|
||||
}*/
|
||||
}
|
||||
|
||||
class _CustomListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final VoidCallback? onTap;
|
||||
//final Widget? trailing;
|
||||
|
||||
const _CustomListTile({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
this.onTap,
|
||||
//this.trailing,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
leading: Icon(icon),
|
||||
onTap: onTap,
|
||||
//trailing: trailing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SingleSection extends StatelessWidget {
|
||||
final String? title;
|
||||
final List<Widget> children;
|
||||
|
||||
const _SingleSection({
|
||||
Key? key,
|
||||
this.title,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (title != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
title!,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
const Divider(),
|
||||
SingleSection(
|
||||
title: "Bluetooth Devices",
|
||||
children: [
|
||||
const CustomListTile(
|
||||
title: "short Press for connect / disconnect",
|
||||
icon: Icons.info_outline_rounded,
|
||||
),
|
||||
const CustomListTile(
|
||||
title: "long press for pair / unpair",
|
||||
icon: Icons.info_outline_rounded,
|
||||
),
|
||||
const Divider(),
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: _discoveryResults.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
BluetoothObject bluetoothObject = _discoveryResults[index];
|
||||
final device = bluetoothObject.device;
|
||||
return BluetoothDeviceEntry(
|
||||
settingsPage: BluetoothDeviceSettings(bluetoothObject: bluetoothObject),
|
||||
context: context,
|
||||
device: device,
|
||||
bluetoothObject: bluetoothObject,
|
||||
onTap: () async {
|
||||
await _toggleConnection(bluetoothObject);
|
||||
},
|
||||
onLongPress: () async {
|
||||
await bluetoothObject.bondDevice();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: children,
|
||||
),
|
||||
],
|
||||
]),
|
||||
),
|
||||
//drawer: const Sidebar(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
|
||||
import './bluetooth_device_list_entry.dart';
|
||||
|
||||
class BluetoothScreen extends StatefulWidget {
|
||||
/// If true, discovery starts on page start, otherwise user must press action button.
|
||||
final bool start;
|
||||
|
||||
const BluetoothScreen({this.start = true});
|
||||
|
||||
@override
|
||||
_BluetoothScreen createState() => new _BluetoothScreen();
|
||||
}
|
||||
|
||||
class _BluetoothScreen extends State<BluetoothScreen> {
|
||||
StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
|
||||
List<BluetoothDiscoveryResult> results =
|
||||
List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
bool isDiscovering = false;
|
||||
|
||||
_BluetoothScreen();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
isDiscovering = widget.start;
|
||||
if (isDiscovering) {
|
||||
_startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
void _restartDiscovery() {
|
||||
setState(() {
|
||||
results.clear();
|
||||
isDiscovering = true;
|
||||
});
|
||||
|
||||
_startDiscovery();
|
||||
}
|
||||
|
||||
void _startDiscovery() {
|
||||
_streamSubscription =
|
||||
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
|
||||
setState(() {
|
||||
final existingIndex = results.indexWhere(
|
||||
(element) => element.device.address == r.device.address);
|
||||
if (existingIndex >= 0)
|
||||
results[existingIndex] = r;
|
||||
else
|
||||
results.add(r);
|
||||
});
|
||||
});
|
||||
|
||||
_streamSubscription!.onDone(() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO . One day there should be `_pairDevice` on long tap on something... ;)
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Avoid memory leak (`setState` after dispose) and cancel discovery
|
||||
_streamSubscription?.cancel();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: isDiscovering
|
||||
? Text('Discovering devices')
|
||||
: Text('Discovered devices'),
|
||||
actions: <Widget>[
|
||||
isDiscovering
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: new EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
icon: Icon(Icons.replay),
|
||||
onPressed: _restartDiscovery,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemCount: results.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
BluetoothDiscoveryResult result = results[index];
|
||||
final device = result.device;
|
||||
final address = device.address;
|
||||
return BluetoothDeviceListEntry(
|
||||
device: device,
|
||||
rssi: result.rssi,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(result.device);
|
||||
},
|
||||
onLongPress: () async {
|
||||
try {
|
||||
bool bonded = false;
|
||||
if (device.isBonded) {
|
||||
print('Unbonding from ${device.address}...');
|
||||
await FlutterBluetoothSerial.instance
|
||||
.removeDeviceBondWithAddress(address);
|
||||
print('Unbonding from ${device.address} has succed');
|
||||
} else {
|
||||
print('Bonding with ${device.address}...');
|
||||
bonded = (await FlutterBluetoothSerial.instance
|
||||
.bondDeviceAtAddress(address))!;
|
||||
print(
|
||||
'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.');
|
||||
}
|
||||
setState(() {
|
||||
results[results.indexOf(result)] = BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
bondState: bonded
|
||||
? BluetoothBondState.bonded
|
||||
: BluetoothBondState.none,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});
|
||||
} catch (ex) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Error occured while bonding'),
|
||||
content: Text("${ex.toString()}"),
|
||||
actions: <Widget>[
|
||||
new TextButton(
|
||||
child: new Text("Close"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,28 +1,28 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
import 'package:flutter_provisioning_for_iot/widgets/sidebar.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../objects/cloud_service_api.dart';
|
||||
import '../widgets/sidebar.dart';
|
||||
|
||||
Future<Map<String, dynamic>> readJson() async {
|
||||
final String data = await rootBundle.loadString('config/credentials.json');
|
||||
final Map<String, dynamic> dataMap = json.decode(data);
|
||||
log("loaded json input $dataMap");
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
class CloudService extends StatefulWidget {
|
||||
const CloudService({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CloudService> createState() => _CloudService();
|
||||
|
||||
}
|
||||
Future<Map<String, dynamic>> readJson() async {
|
||||
final data = await rootBundle.loadString('config/credentials.json');
|
||||
var data_ = json.decode(data) as Map<String, dynamic>;
|
||||
debugPrint(data_.toString());
|
||||
return data_;
|
||||
}
|
||||
|
||||
class _CloudService extends State<CloudService>{
|
||||
|
||||
class _CloudService extends State<CloudService> {
|
||||
late final Map<String, dynamic> credentials;
|
||||
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
|
||||
static SharedPreferences? preferencesInstance;
|
||||
|
@ -35,18 +35,27 @@ class _CloudService extends State<CloudService>{
|
|||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
onPressed: () async{
|
||||
var respond1 = await cloudServiceAPI.getDevices();
|
||||
debugPrint('Devices: ${respond1.toString()}');
|
||||
var respond2 = await cloudServiceAPI.getInformation();
|
||||
debugPrint('Information: ${respond2.toString()}');
|
||||
var respond3 = await cloudServiceAPI.createDevice('1', 'asdas', 'sdwe1');
|
||||
debugPrint('CreateDevice: ${respond3.toString()}');
|
||||
}, child: const Text("Example"),
|
||||
onPressed: () async {
|
||||
// List<BluetoothObjectRemote> respond1 = await cloudServiceAPI.getDevices();
|
||||
// debugPrint('Devices: ${respond1[0].toString()}');
|
||||
// debugPrint('Devices: ${respond1[0].toString()}');
|
||||
|
||||
//dynamic respond2 = await cloudServiceAPI.getInformation();
|
||||
//debugPrint('Information: ${respond2.toString()}');
|
||||
|
||||
// dynamic respond3 = await cloudServiceAPI.createDevice('1', 'asdas', 'sdwe1');
|
||||
// debugPrint('CreateDevice: ${respond3.toString()}');*/
|
||||
|
||||
//dynamic respond4 = await cloudServiceAPI.getDeviceInfo("PFC200V3-430EB3");
|
||||
//debugPrint('Information: ${respond4.toString()}');
|
||||
|
||||
//bool respond5 = await cloudServiceAPI.createDevice("PFC200V3-430EB2", "60987668030DF277AD7706D7C1FA683F37230D202A80EE5135B05EF928E3BF88", "");
|
||||
//debugPrint('Information: ${respond5.toString()}');
|
||||
},
|
||||
child: const Text("Example"),
|
||||
),
|
||||
),
|
||||
drawer: const Sidebar(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
import 'package:flutter_provisioning_for_iot/widgets/sidebar.dart';
|
||||
|
||||
import '../objects/create_material_color.dart';
|
||||
|
||||
class MainPage extends StatelessWidget {
|
||||
const MainPage({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Provisioning for IOT',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// Try running your application with "flutter run". You'll see the
|
||||
// application has a blue toolbar. Then, without quitting the app, try
|
||||
// changing the primarySwatch below to Colors.green and then invoke
|
||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// is not restarted.
|
||||
primarySwatch: CustomColor.createMaterialColor(const Color(0xff263f8c)),
|
||||
),
|
||||
home: const MyHomePage(title: 'Provisioning for IOT'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text(
|
||||
'You have pushed the button this many times:',
|
||||
),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
drawer: const Sidebar(),// This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../objects/create_material_color.dart';
|
||||
import '../widgets/sidebar.dart';
|
||||
|
||||
class MainScreen extends StatelessWidget {
|
||||
const MainScreen({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const String appName = "Provisioning for IOT";
|
||||
|
||||
return MaterialApp(
|
||||
title: appName,
|
||||
theme: ThemeData(
|
||||
primarySwatch: CustomColor.createMaterialColor(const Color(0xff263f8c)),
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
home: const HomeScreen(title: appName),
|
||||
//debugShowCheckedModeBanner: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
const HomeScreen({super.key, required this.title});
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreen();
|
||||
}
|
||||
|
||||
class _HomeScreen extends State<HomeScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text(
|
||||
"Die HFU präsentiert:",
|
||||
),
|
||||
Text(
|
||||
"Cloud Provisioning for IOT Devices",
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10.0,
|
||||
height: 50.0,
|
||||
),
|
||||
const Image(image: AssetImage('assets/device/PFC200.jpg')),
|
||||
],
|
||||
),
|
||||
),
|
||||
drawer: const Sidebar(), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_provisioning_for_iot/schemas/device_info_model.dart';
|
||||
|
||||
import '../objects/cloud_service_api.dart';
|
||||
// import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../schemas/device_model.dart';
|
||||
import '../widgets/custom_list_tile.dart';
|
||||
import '../widgets/sidebar.dart';
|
||||
import '../widgets/single_section.dart';
|
||||
|
||||
Future<Map<String, dynamic>> readJson() async {
|
||||
final data = await rootBundle.loadString('config/credentials.json');
|
||||
var data_ = json.decode(data) as Map<String, dynamic>;
|
||||
debugPrint(data_.toString());
|
||||
return data_;
|
||||
}
|
||||
|
||||
class RegisteredDevicesScreen extends StatefulWidget {
|
||||
const RegisteredDevicesScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RegisteredDevicesScreen> createState() => _RegisteredDevicesScreen();
|
||||
}
|
||||
|
||||
class _RegisteredDevicesScreen extends State<RegisteredDevicesScreen> {
|
||||
late List<DeviceInfoModel>? _registeredInfoDevices;
|
||||
|
||||
// late final Map<String, dynamic> _credentials;
|
||||
final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();
|
||||
|
||||
// static SharedPreferences? preferencesInstance;
|
||||
bool _isFetching = false;
|
||||
|
||||
Future<void> _fetchRegisteredDevices() async {
|
||||
setState(() {
|
||||
_isFetching = true;
|
||||
});
|
||||
_registeredInfoDevices == null
|
||||
? _registeredInfoDevices = List.empty(growable: true)
|
||||
: _registeredInfoDevices!.clear();
|
||||
List<DeviceModel>? registeredDevices = await _cloudServiceAPI.getDevices();
|
||||
for (DeviceModel deviceModel in registeredDevices) {
|
||||
String deviceID = deviceModel.id;
|
||||
DeviceInfoModel deviceInfoModel = await _cloudServiceAPI.getDeviceInfo(deviceID);
|
||||
setState(() {
|
||||
_registeredInfoDevices!.add(deviceInfoModel);
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
_isFetching = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_registeredInfoDevices = null;
|
||||
_fetchRegisteredDevices();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: _isFetching ? const Text('Registered Devices (searching...)') : const Text('Registered Devices'),
|
||||
actions: <Widget>[
|
||||
_isFetching
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16.0),
|
||||
child: const CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.replay),
|
||||
onPressed: _fetchRegisteredDevices,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(children: [
|
||||
SingleChildScrollView(
|
||||
physics: const ScrollPhysics(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SingleSection(
|
||||
title: "Registered Bluetooth Devices",
|
||||
children: [
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: _registeredInfoDevices == null ? 0 : _registeredInfoDevices!.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
DeviceInfoModel entry = _registeredInfoDevices![index];
|
||||
//DeviceInfoModel entryInfo = await _cloudServiceAPI.getDeviceInfo(entry.id);
|
||||
return CustomListTile(
|
||||
title: entry.id,
|
||||
icon: Icons.devices,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
drawer: const Sidebar(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,24 +2,33 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
|
||||
import '../widgets/sidebar.dart';
|
||||
import '../widgets/single_section.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
const Settings({Key? key}) : super(key: key);
|
||||
static CloudServiceAPI cloudService = CloudServiceAPI();
|
||||
|
||||
static String address = cloudService.getAddress();
|
||||
static String username = cloudService.getUsername();
|
||||
static String password = cloudService.getPassword();
|
||||
static String address = cloudService.address;
|
||||
static String username = cloudService.username;
|
||||
static String password = cloudService.password;
|
||||
|
||||
@override
|
||||
State<Settings> createState() => _SettingsState();
|
||||
|
||||
/// Initializes the Settings with the correct Values from the last session
|
||||
static Future initSettings() async {
|
||||
}
|
||||
static Future initSettings() async {}
|
||||
}
|
||||
|
||||
class _SettingsState extends State<Settings> {
|
||||
bool _passwordVisible = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
debugPrint("PASSWORD ${Settings.password}");
|
||||
super.initState();
|
||||
_passwordVisible = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -29,40 +38,80 @@ class _SettingsState extends State<Settings> {
|
|||
title: const Text("Settings"),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Address for the Device Manager"),
|
||||
initialValue: Settings.address,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (String newValue) {
|
||||
Settings.address = newValue;
|
||||
Settings.cloudService.setAddress(newValue);
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Username"),
|
||||
keyboardType: TextInputType.text,
|
||||
initialValue: Settings.username,
|
||||
onChanged: (String newValue) {
|
||||
Settings.username = newValue;
|
||||
Settings.cloudService.setUsername(newValue);
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Password"),
|
||||
keyboardType: TextInputType.text,
|
||||
initialValue: Settings.password,
|
||||
onChanged: (String newValue) {
|
||||
Settings.password = newValue;
|
||||
Settings.cloudService.setPassword(newValue);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
child: ListView(children: [
|
||||
SingleChildScrollView(
|
||||
physics: const ScrollPhysics(),
|
||||
child: Column(children: <Widget>[
|
||||
SingleSection(
|
||||
title: "Server Settings",
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Enter the server url (no https:// and paths)',
|
||||
labelText: "Address for the Device Manager",
|
||||
),
|
||||
initialValue: Settings.address,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (String newValue) {
|
||||
Settings.address = newValue;
|
||||
Settings.cloudService.address = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Enter the server username',
|
||||
labelText: "Username",
|
||||
),
|
||||
initialValue: Settings.username,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (String newValue) {
|
||||
Settings.username = newValue;
|
||||
Settings.cloudService.username = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: TextFormField(
|
||||
obscureText: _passwordVisible ? false : true,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: 'Enter the server password',
|
||||
labelText: "Password",
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_passwordVisible ? Icons.visibility : Icons.visibility_off),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_passwordVisible = !_passwordVisible;
|
||||
});
|
||||
},
|
||||
),
|
||||
filled: true,
|
||||
),
|
||||
initialValue: Settings.password,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (String newValue) {
|
||||
Settings.password = newValue;
|
||||
Settings.cloudService.password = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SingleSection(
|
||||
title: "Theme Settings",
|
||||
children: <Widget>[],
|
||||
)
|
||||
]),
|
||||
)
|
||||
]),
|
||||
),
|
||||
drawer: const Sidebar());
|
||||
}
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_settings.dart';
|
||||
|
||||
class BluetoothDiscovery extends StatefulWidget {
|
||||
final bool start;
|
||||
final String deviceID;
|
||||
|
||||
const BluetoothDiscovery({
|
||||
super.key,
|
||||
required this.start,
|
||||
required this.deviceID
|
||||
});
|
||||
|
||||
@override
|
||||
State<BluetoothDiscovery> createState() => _BluetoothDiscovery();
|
||||
}
|
||||
|
||||
class _BluetoothDiscovery extends State<BluetoothDiscovery> {
|
||||
|
||||
StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
|
||||
List<BluetoothDiscoveryResult> results = List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
bool isDiscovering = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("test: ${widget.deviceID}");
|
||||
debugPrint("bool: ${widget.start}");
|
||||
if(widget.start) {
|
||||
_startDiscovery();
|
||||
}
|
||||
return RefreshIndicator(
|
||||
onRefresh: () {
|
||||
debugPrint("refreshed");
|
||||
return Future(() => null);
|
||||
},
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
itemCount: results.length,
|
||||
itemExtent: 50.0,
|
||||
itemBuilder:
|
||||
(BuildContext context, int index) {
|
||||
var device = results.elementAt(index).device;
|
||||
var deviceAddress = device.address;
|
||||
var deviceName = device.name;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(Icons.bluetooth_disabled_rounded),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
debugPrint(deviceAddress);
|
||||
},
|
||||
onLongPress: () {
|
||||
debugPrint("no need to press for this long...");
|
||||
},
|
||||
child: Text("$deviceAddress"
|
||||
"${(deviceName != "" ? "" : " | $deviceName")}")),
|
||||
const Expanded(child: Text("")),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
debugPrint("this is a warning!");
|
||||
debugPrint(device.address);
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => BluetoothDeviceSettings(device: device)));
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
isDiscovering = widget.start;
|
||||
if (isDiscovering) {
|
||||
_startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
void _restartDiscovery() {
|
||||
setState(() {
|
||||
results.clear();
|
||||
isDiscovering = true;
|
||||
});
|
||||
|
||||
_startDiscovery();
|
||||
}
|
||||
|
||||
void _startDiscovery() {
|
||||
_streamSubscription =
|
||||
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
|
||||
setState(() {
|
||||
final existingIndex = results.indexWhere(
|
||||
(element) => element.device.address == r.device.address);
|
||||
if (existingIndex >= 0) {
|
||||
results[existingIndex] = r;
|
||||
} else {
|
||||
results.add(r);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
debugPrint("results: $results");
|
||||
|
||||
_streamSubscription!.onDone(() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Avoid memory leak (`setState` after dispose) and cancel discovery
|
||||
_streamSubscription?.cancel();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void initScan() {
|
||||
_streamSubscription =
|
||||
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
|
||||
debugPrint("r: ${r.device.name} ${r.device.address}");
|
||||
setState(() {
|
||||
final existingIndex = results.indexWhere(
|
||||
(element) => element.device.address == r.device.address);
|
||||
if (existingIndex >= 0) {
|
||||
results[existingIndex] = r;
|
||||
} else {
|
||||
results.add(r);
|
||||
}
|
||||
});
|
||||
if (r.device.address == "38:F3:2E:41:82:74") {
|
||||
BluetoothConnection.toAddress(r.device.address).then((connection) {
|
||||
debugPrint('Connected to the device');
|
||||
//connection = _connection;
|
||||
//setState(() {
|
||||
//isConnecting = false;
|
||||
//isDisconnecting = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_streamSubscription!.onDone(() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
//final Widget? trailing;
|
||||
|
||||
const CustomListTile({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
this.onTap,
|
||||
//this.trailing,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
leading: Icon(icon),
|
||||
onTap: onTap,
|
||||
//trailing: trailing,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,54 +1,62 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/bluetooth_screen.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/cloud_service_ui.dart';
|
||||
import '../screens/main_page.dart';
|
||||
|
||||
import '../screens/bluetooth_screen.dart';
|
||||
import '../screens/cloud_service_ui.dart';
|
||||
import '../screens/main_screen.dart';
|
||||
import '../screens/registered_devices_screen.dart';
|
||||
import '../screens/settings.dart';
|
||||
|
||||
class Sidebar extends StatelessWidget {
|
||||
const Sidebar({Key? key}) : super(key: key);
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
//backgroundColor: Colors.black ,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: <Widget>[
|
||||
const DrawerHeader(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.white),
|
||||
child: Center(
|
||||
child: Image(
|
||||
image: AssetImage('assets/logo/m&m_logo.png')
|
||||
),
|
||||
child: Image(image: AssetImage('assets/logo/m&m_logo.png')),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text("Main Page"),
|
||||
leading: const Icon(Icons.home),
|
||||
title: const Text("Home"),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const MainPage()));
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const MainScreen()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text("Settings"),
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => const Settings()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text("Bluetooth Test"),
|
||||
leading: const Icon(Icons.bluetooth_searching),
|
||||
title: const Text("Connect Device"),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const BluetoothScreen()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.devices),
|
||||
title: const Text("Registered Devices"),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisteredDevicesScreen()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.settings),
|
||||
title: const Text("Settings"),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const Settings()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.cloud),
|
||||
title: const Text("Cloud Service"),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const CloudService()));
|
||||
},
|
||||
)
|
||||
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SingleSection extends StatelessWidget {
|
||||
final String? title;
|
||||
final List<Widget> children;
|
||||
|
||||
const SingleSection({
|
||||
Key? key,
|
||||
this.title,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (title != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
title!,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: children,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SwitchWidget extends StatefulWidget {
|
||||
|
||||
const SwitchWidget({super.key});
|
||||
|
||||
@override
|
||||
|
@ -9,7 +8,6 @@ class SwitchWidget extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _SwitchWidget extends State<SwitchWidget> {
|
||||
|
||||
bool switchControl = false;
|
||||
var textHolder = 'Switch is OFF';
|
||||
|
||||
|
@ -41,4 +39,4 @@ class _SwitchWidget extends State<SwitchWidget> {
|
|||
inactiveTrackColor: Colors.grey,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ flutter:
|
|||
assets:
|
||||
- config/credentials.json
|
||||
- assets/logo/m&m_logo.png
|
||||
- assets/device/PFC200.jpg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/main_page.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/main_screen.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:flutter_provisioning_for_iot/main.dart';
|
||||
|
|