bluetooth_thermometer 0.9.3 copy "bluetooth_thermometer: ^0.9.3" to clipboard
bluetooth_thermometer: ^0.9.3 copied to clipboard

discontinued
retracted[pending analysis]

A Flutter package for connecting to and reading temperature from ThermoWorks Bluetooth thermometers (Thermapen Blue, TempTest Blue).

bluetooth_thermometer #

A Flutter package for connecting to and reading temperature data from Bluetooth Low Energy (BLE) thermometers, specifically designed for ThermoWorks devices including Thermapen Blue and TempTest Blue.

⚠️ Platform Support #

This package is mobile-only (Android and iOS). Desktop platforms (Windows, Linux, macOS) and web are not supported as they require Bluetooth Low Energy (BLE) hardware that is typically only available on mobile devices.

✨ Features #

  • 🔍 Device Discovery: Scan for nearby Bluetooth thermometers with signal strength
  • 🔗 Smart Connection: Auto-connect to previously used devices with signal prioritization
  • 🌡️ Real-time Temperature: Subscribe to live temperature readings
  • 🔋 Battery Monitoring: Automatic low battery warnings and level tracking (probably works; needs further testing)
  • ⚙️ Device Settings: Read/write thermometer settings (brightness, units, etc.)
  • 🎯 Button Detection: Detect physical button presses on supported devices
  • 🔐 Smart Permissions: Lazy permission requests with intelligent error handling
  • 📊 Analytics System: Comprehensive event logging for debugging Bluetooth issues
  • 🔄 Auto-Disconnect: Prevent battery drain with configurable idle timeouts
  • 🔌 Manual Shutdown: Option to manually shut down connected devices
  • 🔍 Deeper Scan: Extended scan mode for devices that don't appear in quick scans

Getting started #

Prerequisites #

  • Flutter SDK (see environment.sdk in pubspec.yaml)
  • Android device with Bluetooth support (Android 6.0+)
  • iOS device with Bluetooth support (iOS 12.0+)

Installation #

Add this package to your pubspec.yaml:

dependencies:
  bluetooth_thermometer: ^0.9.3

See the installation tab for the latest version.

⚠️ Required Permissions #

Your app MUST add these permissions:

Android (android/app/src/main/AndroidManifest.xml):

<!-- Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesFeature="true"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- Android 11 and older -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

iOS (ios/Runner/Info.plist):

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth access to connect to Bluetooth thermometers.</string>

See the repository's main README.md for detailed integration guidance.

⚡ Quick Start (2 minutes) #

The fastest path to a working thermometer UI: add the dependency and permissions above, then:

final client = ThermometerClient();
await client.initializeAndWarmupBluetooth();
// In build:
SimpleThermometerDemo(client: client)

That's it. The widget handles connection, scanning, temperature display, and button capture. See Usage for the full page example and manual integration.

Analytics & Observability #

This package includes a comprehensive analytics system for tracking permission flows and debugging Bluetooth connectivity issues. The analytics system provides detailed event logging with timestamps, making it easy to understand complex asynchronous permission flows.

Analytics Features #

  • Semantic Event Categories: Automatic categorization via EventCategory enum (happyPath, connections, scanning, deviceOps, lifecycle, system, errors, permissions, denialPath, critical)
  • Framework-Agnostic Core: Core analytics system has zero Flutter dependencies, works in any Dart environment
  • UI-Specific Display: Separate DisplayEventCategory enum provides Flutter icons/colors for widgets
  • Device Shutdown Analysis: Comprehensive tracking of intentional vs unintentional shutdowns with detailed explanations
  • Multiple Handlers: In-memory for development, local storage for production, custom handlers for Sentry/Firebase
  • Export Functionality: JSON export for sharing with support teams
  • Zero-Performance Impact: Silent failure if no handler is configured

Event Category Architecture #

The analytics system uses a clean separation between core categorization and UI display:

Core EventCategory Enum (analytics_event.dart)

  • Framework-agnostic categorization logic
  • Zero Flutter dependencies
  • Used by analytics handlers and event processing
  • Available in any Dart environment (CLI tools, servers, etc.)

UI DisplayEventCategory Enum (display_event_category.dart)

  • Flutter-specific visual properties (icons, colors)
  • Used only by UI widgets for display
  • Provides rich visual filtering in AnalyticsLogViewer

This separation ensures the analytics core remains lightweight and framework-independent while providing rich UI experiences.

Share Logs #

Share analytics logs in multiple formats for different use cases:

Quick Sharing (JSON)

import 'package:share_plus/share_plus.dart';

final shareButtonKey = GlobalKey();

IconButton(
  key: shareButtonKey,
  icon: Icon(Icons.share),
  onPressed: () => Share.share(
    BluetoothAnalytics.exportData(),
    subject: 'Analytics Logs (JSON)',
    sharePositionOrigin: shareButtonKey.calculateShareAnchorRect(),
  ),
);

Advanced Sharing with Multiple Formats

import 'package:share_plus/share_plus.dart';
import 'package:bluetooth_thermometer/bluetooth_thermometer.dart';

void shareAnalytics(BuildContext context, GlobalKey shareKey, AnalyticsExportFormat format) {
  Share.share(
    context.getAnalyticsData(format),
    subject: context.getAnalyticsSubject(format),
    sharePositionOrigin: shareKey.calculateShareAnchorRect(),
  );
}

// Usage
shareAnalytics(context, shareButtonKey, AnalyticsExportFormat.csv); // For spreadsheets
shareAnalytics(context, shareButtonKey, AnalyticsExportFormat.text); // For emails
shareAnalytics(context, shareButtonKey, AnalyticsExportFormat.summary); // For quick overview

📊 Analytics Events Reference #

For a complete catalog of all analytics events with detailed explanations and usage examples, see the Analytics Events Reference. This document includes:

  • Complete event catalog organized by category
  • Detailed shutdown detection analysis (intentional vs unintentional)
  • Implementation examples for popular analytics services
  • Event priority levels and usage patterns

Export Formats Available

Format Use Case Sample Output
JSON Complete data, API integration {"events": [...], "categories": {...}}
Text Human-readable, emails, logs Formatted report with timestamps
CSV Spreadsheet analysis, data processing timestamp,key,extraInfo,category,priority
Summary Quick overview, dashboards Compact statistics and insights

Anchor Positioning Utility

The calculateShareAnchorRect() extension handles the confusing anchor positioning:

extension AnalyticsSharing on GlobalKey {
  Rect? calculateShareAnchorRect() {
    final box = currentContext?.findRenderObject() as RenderBox?;
    if (box == null) return null;
    final position = box.localToGlobal(Offset.zero);
    final size = box.size;
    return Rect.fromLTWH(position.dx, position.dy, size.width, size.height);
  }
}

Professional Sharing Example

class AnalyticsSharingWidget extends StatelessWidget {
  final GlobalKey _shareKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<AnalyticsExportFormat>(
      child: IconButton(
        key: _shareKey,
        icon: Icon(Icons.share),
        onPressed: () {}, // Handled by popup menu
      ),
      onSelected: (format) => _shareAnalytics(context, format),
      itemBuilder: (context) => AnalyticsExportFormat.values.map((format) {
        return PopupMenuItem(
          value: format,
          child: Text('Share as ${format.displayName}'),
        );
      }).toList(),
    );
  }

  void _shareAnalytics(BuildContext context, AnalyticsExportFormat format) {
    Share.share(
      context.getAnalyticsData(format),
      subject: context.getAnalyticsSubject(format),
      sharePositionOrigin: _shareKey.calculateShareAnchorRect(),
    );
  }
}

Use AnalyticsLogViewer widget for a complete UI to view, filter, and export logs:

AnalyticsLogViewer(
  title: 'Analytics Logs',
  showExportButton: true,
  showClearButton: true,
)

Third-Party Analytics Integrations #

For complete working examples of integrating with popular analytics services, see the Custom Analytics Handlers Guide.

Quick Examples

Sentry (3 minutes):

// Add: sentry_flutter: ^latest
class SentryAnalyticsHandler extends AnalyticsHandler {
  @override
  void trackEvent(AnalyticsEvent event) {
    Sentry.addBreadcrumb(Breadcrumb(message: event.key, data: {'extra': event.extraInfo}));
  }
}

Firebase (5 minutes):

// Add: firebase_analytics: ^latest
class FirebaseAnalyticsHandler extends AnalyticsHandler {
  @override
  void trackEvent(AnalyticsEvent event) {
    FirebaseAnalytics.instance.logEvent(name: event.key, parameters: {'extra': event.extraInfo});
  }
}

REST API (10 minutes):

class RestApiAnalyticsHandler extends AnalyticsHandler {
  @override
  void trackEvent(AnalyticsEvent event) {
    http.post(Uri.parse('your-api-endpoint'), body: {'event': event.key, 'data': event.extraInfo});
  }
}

Then register your handler:

BluetoothAnalytics.initialize(MyAnalyticsHandler());

Permission-Aware Components #

The PermissionAwareBluetoothHandler widget provides intelligent permission management:

  • Lazy Initialization: Permissions only requested when user initiates Bluetooth scanning
  • Smart Dialogs: Uses smart_permission package for battle-tested permission flows
  • On-Resume Detection: Automatically detects when permissions are granted in OS settings
  • Comprehensive Error Handling: Clear user guidance for all permission states

📚 Technical Documentation #

Smart Connectivity Features #

This package includes advanced connection management features designed to improve user experience and reduce common Bluetooth bugs in real-world kitchen environments.

⚡ Auto-Connect #

The autoConnect() method provides a "zero-touch" connection experience by automatically finding and connecting to the most relevant device.

How it works:

  1. Memory: Remembers the last 5 successfully connected devices (LRU cache).
  2. Signal Strength: Scans for known devices and picks the one with the strongest signal (RSSI).
  3. Safety: Only connects to previously trusted devices, preventing accidental connections to other nearby thermometers.

Benefits:

  • Eliminates Selection Fatigue: Users don't need to select their device every time they open the app.
  • Reduces "Wrong Device" Errors: Prioritizes the device physically closest to the tablet/phone.

🔋 Auto-Disconnect #

The package supports patterns for automatic disconnection to prevent battery drain and connection conflicts.

Best Practices Implementation:

  • Idle Timer: Disconnect after a period of inactivity (e.g., 45 seconds). This saves the thermometer's battery.
  • Background Disconnect: Disconnect immediately when the app goes to the background. This releases the Bluetooth lock, allowing other apps or devices to connect if needed.

Real-World Impact:

  • Prevents "Device Busy" Bugs: By releasing the connection when idle, it prevents the common issue where a thermometer is "hijacked" by a backgrounded app, making it invisible to other users.
  • Extends Hardware Life: significantly reduces battery consumption on the Thermapen Blue.

Usage #

See the /example folder for a complete example app.

Quick Start: Embedded Demo #

Use SimpleThermometerDemo when you want a ready-to-use temperature-taking UI with minimal code. Ideal for embedding thermometer functionality into an existing app (e.g. kiq_mobile) or getting up and running quickly without a full app shell.

The widget is self-contained: it wraps content in PermissionAwareBluetoothHandler and ThermometerLifecycleManager, and handles connection status, connect prompt, temperature display, Take Temp button, and physical button capture.

Minimal usage (2 lines setup + 1 in build):

final client = ThermometerClient();
await client.initializeAndWarmupBluetooth();

// In build:
SimpleThermometerDemo(client: client)

Adding to a flutter create project without removing anything: ~35 lines of Dart code. Add a demo page (~25 lines) that holds the client and disposes it, plus a navigation button (~5 lines) and the import. Plus 1 line in pubspec.yaml. The widget handles everything else.

// Example: Add ThermometerDemoPage and navigate from your home screen
class ThermometerDemoPage extends StatefulWidget {
  const ThermometerDemoPage({super.key});
  @override
  State<ThermometerDemoPage> createState() => _ThermometerDemoPageState();
}

class _ThermometerDemoPageState extends State<ThermometerDemoPage> {
  late final ThermometerClient _client;

  @override
  void initState() {
    super.initState();
    _client = ThermometerClient();
    _client.initializeAndWarmupBluetooth();
  }

  @override
  void dispose() {
    _client.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Thermometer Demo')),
      body: SimpleThermometerDemo(client: _client),
    );
  }
}

Basic usage (manual integration) #

import 'package:flutter/foundation.dart';
import 'package:bluetooth_thermometer/bluetooth_thermometer.dart';

// Create a client
final client = ThermometerClient();

// Initialize Bluetooth (required - triggers iOS permission dialog)
await client.initializeAndWarmupBluetooth();

// Listen for discovered devices
client.devices.listen((devices) {
  debugPrint('Found ${devices.length} devices');
});

// Scan for devices
await client.scanForDevices(timeout: Duration(seconds: 10));

// Connect to a device
client.connect(devices.first);

// Listen for temperature readings
client.temperatureStream.listen((temperature) {
  debugPrint('Temperature: $temperature');
});

// Clean up when done
client.dispose();

Pre-built Widgets #

Widget Use Case
ThermometerSelector Status chip + bottom sheet; shows Retry if not initialized
BluetoothNotInitializedWidget Error state with optional Retry button
SimpleThermometerDemo Ready-to-use temperature UI (connect, take temp, button capture)
AsyncFilledButton Button that shows centered progress indicator when loading
PermissionAwareBluetoothHandler Lazy permission requests with smart dialogs
ThermometerLifecycleManager Auto-connect on resume, idle disconnect

Device Compatibility #

⚠️ IMPORTANT: This package is designed specifically for ThermoWorks thermometers including Thermapen Blue and TempTest Blue.

Supported Features by Device #

Feature Thermapen Blue TempTest Blue Other Devices
Temperature Reading ✅ Full Support ✅ Full Support ⚠️ May work if using same UUIDs
Settings Read/Write ✅ Full Support ✅ Full Support NOT SUPPORTED
Button Press Detection ✅ Full Support ✅ Full Support NOT SUPPORTED
Battery Level Monitoring ⚠️ Probably works; needs further testing ⚠️ Probably works; needs further testing NOT SUPPORTED

Both Thermapen Blue and TempTest Blue support all features. Other Thermapen models (Thermapen ONE, Classic, etc.) and other manufacturers' thermometers may not support advanced features like settings and battery monitoring.

Safety Note: Reading settings (readSettings()) appears safe with no known limits - you can probably read as many times as needed. Only writing settings has known limits (~10,000-100,000 write cycles). The only known impact of reading is very minor battery usage (negligible for normal usage).

Manufacturer Documentation #

ThermoWorks Official Resources #

Bluetooth Specifications #

This package uses proprietary service and characteristic UUIDs specific to ThermoWorks thermometers:

  • Service UUID: 45544942-4c55-4554-4845-524db87ad700
  • Temperature Characteristic: 45544942-4c55-4554-4845-524db87ad701
  • Settings Characteristic: 45544942-4c55-4554-4845-524db87ad709 (ThermoWorks devices)
  • Command Characteristic: 45544942-4c55-4554-4845-524db87ad705 (ThermoWorks devices)
  • Battery Characteristic: 00002A19-0000-1000-8000-00805F9B34FB (Standard BLE)

⚠️ Note: These UUIDs are not publicly documented by ThermoWorks. They were reverse-engineered from the device's BLE implementation.

Testing #

Integration Tests #

The demo app includes comprehensive integration tests for different testing scenarios:

UI Workflow Test (integration_test/ui_workflow_test.dart):

  • Tests Bluetooth UI interactions and state management
  • Validates modal dialogs, scanning workflow, and proper UI responses
  • Works on any Android device (no Bluetooth hardware required)

Live Device Temperature Test (integration_test/live_device_temperature_test.dart):

  • Complete end-to-end test with physical Thermapen Blue hardware
  • Verifies app launch, permissions, device discovery, connection, and live temperature reading
  • Requires physical Android device + Thermapen Blue thermometer

ADB Wrapper Scripts:

  • run_live_device_test.sh: Automated testing with real hardware
  • Grants permissions via ADB and runs the live device test

Additional information #

This package is designed specifically for ThermoWorks thermometers (Thermapen Blue and TempTest Blue) and uses their proprietary service and characteristic UUIDs. It may work with other BLE thermometers that use the same service structure for temperature reading, but this is not guaranteed. Advanced features (settings read/write, button press detection, battery monitoring) are only supported on ThermoWorks devices. Battery monitoring probably works but needs further testing.

0
likes
0
points
--
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter package for connecting to and reading temperature from ThermoWorks Bluetooth thermometers (Thermapen Blue, TempTest Blue).

Repository (GitHub)
View/report issues

License

(pending) (license)

Dependencies

clock, flutter, flutter_reactive_ble, logger, permission_handler, shared_preferences, smart_permission

More

Packages that depend on bluetooth_thermometer