healthrian_ble_support_for_p30 3.0.0
healthrian_ble_support_for_p30: ^3.0.0 copied to clipboard
Healthrian BLE support library for the Hativ P30 (6-channel ECG).
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:healthrian_ble_support_for_p30/healthrian_ble_support_for_p30.dart';
import 'package:healthrian_visualization_support/healthrian_visualization_support.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await filter.waitForCompletion();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
appBarTheme: AppBarTheme(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
useMaterial3: true,
),
home: const TesterView(),
);
}
}
class TesterView extends StatefulWidget {
const TesterView({super.key});
@override
State<TesterView> createState() => _TesterViewState();
}
class _TesterViewState extends State<TesterView> {
final _manager = DeviceManager();
late final _module = _manager.module;
String? _serialNumber;
String? _firmware;
// The P30 power-on sequence — set mode plus the 2 s 0xFF keep-alive that
// stops the device sleeping — is declared on the module's commands and run
// by TransportModule.powerOn, which connectAndRegister triggers. Nothing to drive
// from the view.
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
ConnectionMonitor(module: _module),
BatteryMonitor(module: _module),
HeartRateMonitor(module: _module),
EcgMonitor(
module: _module,
isFiltered: true,
// Show only the 2 physical leads (L1, L2); hide channels 2-11.
// (P30 also derives L3/aVR/aVL/aVF — change to
// `[for (var i = 6; i < 12; i++) i]` to show all 6.)
ignoreChannels: [for (var i = 2; i < 12; i++) i],
),
// Left on the default diagram: the P30 wears the four limb
// electrodes, and the six chest circles it does not have are drawn
// inactive from its LeadSet rather than sitting on filler values.
// LeadstateStyle.jv6 draws the four alone if that is wanted instead.
LeadstateMonitor(module: _module),
InkWell(
onTap: () async {
final value = await _module
.ble.points[Characteristics.SERIAL_NUMBER]
?.read();
setState(() => _serialNumber = String.fromCharCodes(value ?? []));
},
child: Text('serial number: $_serialNumber'),
),
InkWell(
onTap: () async {
final value = await _module
.ble.points[Characteristics.FIRMWARE_VERSION]
?.read();
setState(() => _firmware = String.fromCharCodes(value ?? []));
},
child: Text('firmware: $_firmware'),
),
Observer(
builder: (context) => StreamBuilder<dynamic>(
stream: _module.ble.streams.ecg.meta.raw,
builder: (context, snapshot) => Text('${snapshot.data}'),
),
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// connect / start / pause / stop / disconnect. Power-on and the
// keep-alive ride along with connectAndRegister / disconnectDevice.
ControlPanel(module: _module),
],
),
);
}
}