saveeye_flutter_sdk 1.0.3
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 tofalse. Set totrueto 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):
fetchingDeviceConfigfetchedDeviceConfigsearchingconnectedpairingpairederror
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_XXXXXXXXis supported where indicated. The SDK resolves the device id internally.
Support #
For support or questions, please contact dst@saveeye.dk.