SaveEye Flutter SDK

The SaveEye Flutter SDK provides a Dart API to interact with SaveEye devices for provisioning over BLE, pairing, checking online status, querying historic usage, and subscribing to realtime readings.

Example app

A complete Flutter example is available at https://github.com/saveeye/Flutter-SDK-Example

Installation

Add the SDK to your pubspec.yaml

dependencies:
  saveeye_flutter_sdk: ^x.y.z

Initialization

import 'package:saveeye_flutter_sdk/saveeye_flutter_sdk.dart';

Future<String> getJwtToken() async {
  // Return a fresh Firebase (or other) JWT for the current user
  // e.g. await FirebaseAuth.instance.currentUser?.getIdToken() ?? '';
  return '';
}

void main() {
  SaveEyeClient.instance.initialize(
    'APP_SDK_KEY',
    getJwtToken,
    // environment: SaveEyeEnvironment.prod, // default is prod
    // debug: false, // default is false, set to true to enable debug print statements
  );
}
  • appSDKKey: Your SDK key from the SaveEye Manager portal.
  • onJWTRequest: A callback that must return a fresh JWT when called.
  • environment: Optional. Defaults to SaveEyeEnvironment.prod. This is for SaveEye internal development; partners should use production.
  • debug: Optional. Defaults to false. Set to true to enable debug print statements from the SDK. When false, all SDK print statements are suppressed.

Provisioning

Scan for nearby devices

final devices = await SaveEyeClient.instance.getSaveEyeDevicesNearby();
// devices: List<String> of BLE names like 'SAVEEYE_XXXXXXXX'

Provision a device and list available Wi‑Fi networks

Provide either a QR code string (containing the device id) or a BLE name (e.g., SAVEEYE_XXXXXXXX).

final wifiSsids = await SaveEyeClient.instance.provisionDevice(
  qrCodeOrBLEName,
  /* externalId: */ null, // Optional: pair with this externalId; otherwise the JWT claim defined for the appSDKKey is ued
);

if (wifiSsids != null && wifiSsids.isNotEmpty) {
  // Show available Wi‑Fi networks to the user
}

Provisioning status events

Listen to the provisioning status via a stream of StatusEvent:

final sub = SaveEyeClient.instance.events.listen((event) {
  // event.topic == SaveEyeClient.deviceStatusTopic
  // event.stage is a ConnectionStage
});

ConnectionStage values (in typical sequence):

  • fetchingDeviceConfig
  • fetchedDeviceConfig
  • searching
  • connected
  • pairing
  • paired
  • error

Connect device to Wi‑Fi

await SaveEyeClient.instance.connectToWifi(
  'SAVEEYE_XXXXXXXX', // BLE name
  'YourSSID',
  'YourPassphrase',
);

Pair a device

final paired = await SaveEyeClient.instance.pairDevice(
  qrCodeOrBLEName,
  /* externalId: */ null, // Optional
);

Unpair a device

await SaveEyeClient.instance.unpairDevice(
  deviceId,
  /* externalId: */ null, // Optional
);

Unpairs a device from the current user. This removes the association between the device and the user, allowing the device to be paired with a different user.

Parameters:

  • deviceId: The ID of the device to unpair (String)
  • externalId: (Optional) External ID as a String to unpair the device from. This is mandatory if the app SDK key uses a list claim for externalId (As configured when creating app SDK key in the SaveEye Manager).

Check if a device is online

If a device is found via BLE it's not online and have to be onboarded via provisionDevice

final isOnline = await SaveEyeClient.instance.isDeviceOnline(qrCodeOrBLEName);

User devices

Get my devices

final myDevices = await SaveEyeClient.instance.getMyDevices();
for (final d in myDevices) {
  // See MyDevice below (id, serial, alias, deviceType, ...)
}
// MyDevice response
class MyDevice {
  String id;
  String? alias;
  String serial;
  double allTimesHighConsumption;
  double allTimesHighProduction;
  bool hasProduction;
  int? blinksPerKwh;
  String? errorCode;
  double rmsCurrentMaxPerPhaseAmpere;
  DeviceType deviceType; // name, profile
  List<IdOnly> remoteDevices; // id
  IdOnly? baseDevice; // id
}

class DeviceType {
  String name;
  int? profile; // optional
}

class IdOnly {
  String id;
}

Device data

Historic usage

import 'package:saveeye_flutter_sdk/gql/schema.graphql.dart' show Enum$IntervalType;

final start = DateTime.utc(2024, 1, 1);
final end = DateTime.utc(2024, 1, 31);

// interval: Enum$IntervalType.DAY | Enum$IntervalType.HOUR | Enum$IntervalType.MONTH

final energy = await SaveEyeClient.instance.getEnergyUsageHistory(
  deviceId,
  start,
  end,
  Enum$IntervalType.DAY,
);

final power = await SaveEyeClient.instance.getPowerUsageHistory(
  deviceId,
  start,
  end,
  Enum$IntervalType.HOUR,
);
// EnergyUsageHistory response
class EnergyUsageHistory {
  String deviceId;
  List<EnergyUsageSummary> energyUsageSummaries; // aggregationPeriod, consumed/produced
  bool hasProduction;
  String intervalType; // e.g., DAY, HOUR, MONTH
  double peakEnergyProductionKWh;
  double peakEnergyConsumptionKWh;
  double lowestEnergyProductionKWh;
  double lowestEnergyConsumptionKWh;
  double totalEnergyConsumedKWh;
  double totalEnergyProducedKWh;
  double allTimesHighEnergyConsumedKWh;
  double allTimesHighEnergyProducedKWh;
}

class EnergyUsageSummary {
  DateTime aggregationPeriod; // UTC start of period
  double energyConsumedKWh;
  double energyProducedKWh;
}

// PowerUsageHistory response
class PowerUsageHistory {
  String deviceId;
  String intervalType; // e.g., DAY, HOUR, MONTH
  List<PowerUsageSummary> powerUsageSummaries;
}

class PowerUsageSummary {
  DateTime aggregationPeriod; // UTC start of period
  double averageConsumptionWatt;
  double maxConsumptionWatt;
  double minConsumptionWatt;
  double averageProductionWatt;
  double maxProductionWatt;
  double minProductionWatt;
}

Set device alarm thresholds

Set alarm thresholds for a device to monitor energy consumption.

await SaveEyeClient.instance.setDeviceAlarmThresholds(
  deviceId,
  alarmMaxWh: 5000, // Optional: Maximum consumption threshold in Wh
);

Sets the alarm threshold for a specific device. This method updates the maximum alarm threshold for the device on the server. When the threshold is reached your backend can be notified. Pass null to clear the threshold.

Realtime

Subscribe to realtime readings

import 'package:saveeye_flutter_sdk/saveeye_flutter_sdk.dart';

await SaveEyeClient.instance.subscribeToRealtimeData(
  deviceId,
  (RealtimeReading? data, WebSocketErrorEvent? error) {
    if (data != null) {
      // Use data.currentConsumptionW.total, data.timestamp, etc.
    } else if (error != null) {
      // Handle websocket error
    }
  },
);

Unsubscribe

await SaveEyeClient.instance.unsubscribeFromRealtimeData();

Platform permissions

iOS

Add the following to your Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Used for connecting to SaveEye devices</string>

If your app scans QR codes, also include:

<key>NSCameraUsageDescription</key>
<string>Camera access is needed for scanning QR codes</string>

Android

Add required permissions to your app manifest:

<!-- BLE permissions for nearby device scanning -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- For Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Camera permission for QR scanning (optional) -->
<uses-permission android:name="android.permission.CAMERA" />

On Android 12+, you must request runtime permissions for BLUETOOTH_SCAN and BLUETOOTH_CONNECT (and location on older versions). Use your preferred permissions package to prompt the user.

Notes

  • Passing either a QR code string (containing a device id) or a BLE name like SAVEEYE_XXXXXXXX is supported where indicated. The SDK resolves the device id internally.

Support

For support or questions, please contact dst@saveeye.dk.