firstContact static method

Future<Bridge?> firstContact({
  1. required String bridgeIpAddr,
  2. Directory? savedBridgesDir,
  3. bool writeToLocal = true,
  4. DiscoveryTimeoutController? controller,
  5. String encrypter(
    1. String plaintext
    )?,
})

Initiates the first contact between this device and the given bridge.

Once this method has been called, the user will have 10 seconds by default, or how ever many have been set in controller, to press the button on their bridge to confirm they have physical access to it.

In the event of a successful pairing, this method returns a bridge object that represents the bridge it just connected to. Before it returns the bridge, if writeToLocal is true, it will write it to local storage to be saved for later.

If writing saved bridges to a non-default location, provide that location with savedBridgesDir.

controller gives more control over this process. It lets you decide how many seconds the user has to press the button on their bridge. It also gives the ability to cancel the discovery process at any time.

encrypter When the bridge is written to local storage, it is encrypted. This parameter allows you to provide your own encryption method. This will be used in addition to the default encryption method. This will be performed after the default encryption method.

If the pairing fails, this method returns null.

Implementation

static Future<Bridge?> firstContact({
  required String bridgeIpAddr,
  Directory? savedBridgesDir,
  bool writeToLocal = true,
  DiscoveryTimeoutController? controller,
  String Function(String plaintext)? encrypter,
}) async {
  final DiscoveryTimeoutController timeoutController =
      controller ?? DiscoveryTimeoutController();

  Map<String, dynamic>? response;

  String? appKey;

  String? clientKey;

  final String body = JsonTool.writeJson(
    {
      ApiFields.deviceType: HueHttpRepo.deviceType,
      ApiFields.generateClientKey: true,
    },
  );

  // Try for [timeoutSeconds] to connect with the bridge.
  int counter = 0;
  await Future.doWhile(
    () => Future.delayed(const Duration(seconds: 1)).then(
      (value) async {
        counter++;

        // Timeout after [timeoutSeconds].
        if (counter > timeoutController.timeoutSeconds) return false;

        // Cancel if called to do so, early.
        if (timeoutController.cancelDiscovery) {
          timeoutController.cancelDiscovery = false;
          return false;
        }

        response = await HueHttpClient.post(
          url: "https://$bridgeIpAddr/api",
          applicationKey: null,
          token: null,
          body: body,
        );

        if (response == null || response!.isEmpty) return true;

        try {
          if (response!.containsKey(ApiFields.error)) {
            return response![ApiFields.error][ApiFields.description] ==
                "link button not pressed";
          } else {
            appKey = response![ApiFields.success][ApiFields.username];
            clientKey = response![ApiFields.success][ApiFields.clientKey];
            return appKey == null || appKey!.isEmpty;
          }
        } catch (_) {
          return true;
        }
      },
    ),
  );

  if (appKey == null) return null;

  // Upon successful connection, get the bridge details.
  Map<String, dynamic>? bridgeJson = await HueHttpRepo.get(
    bridgeIpAddr: bridgeIpAddr,
    applicationKey: appKey!,
    resourceType: ResourceType.bridge,
  );

  if (bridgeJson == null) return null;

  final Bridge bridge = Bridge.fromJson(bridgeJson).copyWith(
    ipAddress: bridgeIpAddr,
    applicationKey: appKey,
    clientKey: clientKey,
  );

  if (bridge.id.isEmpty) return null;

  // Save the bridge locally.
  if (writeToLocal) {
    _writeLocalBridge(
      bridge,
      encrypter: encrypter,
      savedBridgesDir: savedBridgesDir,
    );
  }

  return bridge;
}