saveeye_flutter_sdk 1.0.3 copy "saveeye_flutter_sdk: ^1.0.3" to clipboard
saveeye_flutter_sdk: ^1.0.3 copied to clipboard

A Flutter SDK for integrating with SaveEye's device management platform. Provides GraphQL-based access to device data, monitoring, and management features.

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 SaveEye Flutter SDK Example.

Permissions #

iOS: Add to Info.plist: NSBluetoothAlwaysUsageDescription, and if you need Wi‑Fi SSID or local network: NSLocationWhenInUseUsageDescription, NSLocalNetworkUsageDescription. For QR scanning: NSCameraUsageDescription.

Android: Declare INTERNET, ACCESS_NETWORK_STATE, ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, BLUETOOTH, BLUETOOTH_ADMIN, and on Android 12+ BLUETOOTH_SCAN, BLUETOOTH_CONNECT. Request runtime permissions for BLE and location as needed.

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: prod (use dev or staging for internal use)
    // locale: 'en-US',                        // optional: for localized device error descriptions (e.g. 'da-DK')
    // debug: false,                           // set to true to enable SDK debug prints
    // onError: (error, extra) {               // optional: forward SDK errors to Sentry or logging
    //   Sentry.captureException(error, extra: extra);
    // },
  );
}

Parameters:

  • appSDKKey: Your SDK key from the SaveEye Manager portal.
  • onJWTRequest: A callback that must return a fresh JWT when called.
  • environment: Optional. SaveEyeEnvironment.prod (default), .dev, or .staging. Partners should use production.
  • locale: Optional. Locale for API requests (e.g. 'en-US', 'da-DK') for localized device error descriptions.
  • debug: Optional. Defaults to false. Set to true to enable debug print statements from the SDK.
  • onError: Optional. Callback for SDK errors (e.g. to forward to Sentry). Receives (Object error, Map<String, String>? extra).

getInstance #

Use the singleton instance for all SDK calls:

final client = SaveEyeClient.instance;

healthCheck #

Checks if the SaveEye service is healthy and accessible.

final isHealthy = await SaveEyeClient.instance.healthCheck();
if (isHealthy) {
  print('SaveEye service is healthy');
}

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);

Get device ID from QR code or BLE name #

Converts a QR code or BLE name to the actual device ID. Aliases: getDeviceIdFromQrOrBle and getDeviceIdBySerialOrBLEName (matches React Native API).

final deviceId = await SaveEyeClient.instance.getDeviceIdFromQrOrBle(qrCodeOrBLEName);
// or
final deviceId = await SaveEyeClient.instance.getDeviceIdBySerialOrBLEName(serialOrBLEName);
if (deviceId != null) {
  // Use the deviceId for API calls
}

Get available Wi‑Fi SSIDs for a device #

Returns the list of Wi‑Fi SSIDs available for a device (by BLE name). Use after BLE connection during provisioning.

final ssids = await SaveEyeClient.instance.getAvailableSSIDForDevice('SAVEEYE_XXXXXXXX');

Get onboarding session #

Retrieves the current onboarding session status for a specific device. This method is useful for checking the progress or result of the device onboarding process, including timestamps and any error codes.

import 'package:saveeye_flutter_sdk/gql/queries/getOnboardingSession.graphql.dart';

final session = await SaveEyeClient.instance.getOnboardingSession(deviceId);
if (session != null) {
  print('Onboarding status: ${session.status}');
  print('Started on: ${session.startedOn}');
  print('Last updated on: ${session.lastUpdatedOn}');
  print('Completed on: ${session.completedOn}');
  print('Error code: ${session.errorCode}');
}

Parameters:

  • deviceId: The unique identifier of the device whose onboarding session you want to query (String).

Returns:

  • A Future<Fragment$PlusDeviceOnboardingSession?> that resolves to an object containing:
    • status: The current onboarding session status (Enum$PlusDeviceOnboardingSessionStatus).
    • startedOn: The timestamp when onboarding started (DateTime).
    • lastUpdatedOn: The timestamp of the last update to the onboarding session (DateTime).
    • completedOn: The timestamp when onboarding was completed, if available (DateTime?).
    • errorCode: Any error code associated with the onboarding session, if any (String?).

PlusDeviceOnboardingSessionStatus (enum) #

The Enum$PlusDeviceOnboardingSessionStatus enum describes the possible states of a device's onboarding session:

  • BLE_CONNECTED: The device has been connected via Bluetooth Low Energy (BLE).
  • WI_FI_SETUP_DONE: The device has successfully connected to WiFi.
  • FIRMWARE_UPDATE_IN_PROGRESS: The device is currently updating its firmware.
  • FIRMWARE_UPDATE_DONE: The device has completed a firmware update.
  • DONE: The onboarding process is complete.
  • ERROR_MESSAGES: An error occurred during onboarding.

You can use these values to interpret the status field returned by getOnboardingSession().

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
  alarmMinWh: 100,   // Optional: minimum consumption threshold in Wh
);

Device settings #

Get full device settings (alias, MQTT, alarms, etc.) via deviceByIdV2:

final settings = await SaveEyeClient.instance.getDeviceSettings(deviceId);
final device = settings?.deviceByIdV2;
// device?.plusDevice has alias, localMqtt*, blinksPerKwh, rmsCurrentMaxPerPhaseAmpere, consumptionAlarmMaxWh, etc.

Set device alias, local MQTT, RMS current max, or blinks per kWh:

await SaveEyeClient.instance.setDeviceAlias(deviceId, 'Living room');
await SaveEyeClient.instance.setLocalMqttSettings(
  deviceId,
  enabled: true,
  broker: 'mqtt.local',
  port: 1883,
  username: 'user',
  password: 'pass',
);
await SaveEyeClient.instance.setRmsCurrentMaxPerPhase(deviceId, 16.0);
await SaveEyeClient.instance.setBlinksPerKwh(deviceId, 1000);

Pair remote device with base device #

Pairs a remote device with a base device (e.g. after provisioning).

await SaveEyeClient.instance.pairRemoteDeviceWithBaseDevice(
  deviceId,      // remote device
  baseDeviceId,  // base device
  externalId: null, // optional
);

Get device type #

Gets device type info (e.g. for provisioning flows).

final meta = await SaveEyeClient.instance.getDeviceTypeById(deviceId);
// meta?.deviceType, meta?.deviceTypeName, meta?.saveEyePlusDeviceTypeProfile

Realtime #

One WebSocket per device; multiple callbacks per device are supported. If you subscribe again to the same device while already connected, the new callback is added and invoked once with a CONCURRENT_SUBSCRIPTION error.

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) {
      // error.type can be 'CONNECTION_ERROR', 'CONCURRENT_SUBSCRIPTION', 'parse', 'payload', etc.
    }
  },
);

Unsubscribe #

Unsubscribe from one device or all devices:

// Unsubscribe from a specific device
await SaveEyeClient.instance.unsubscribeFromRealtimeData(deviceId);

// Unsubscribe from all devices
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.

0
likes
150
points
508
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter SDK for integrating with SaveEye's device management platform. Provides GraphQL-based access to device data, monitoring, and management features.

Repository (GitHub)

License

MIT (license)

Dependencies

flutter, flutter_hooks, gql, graphql, graphql_flutter, meta, saveeye_provisioning, web_socket_channel

More

Packages that depend on saveeye_flutter_sdk