Skip to content

Read the RSSI of a connected device #919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,24 @@ public void onMethodCall(MethodCall call, Result result) {
break;
}

case "readRssi":
{
String remoteId = (String)call.arguments;
BluetoothGatt gatt;
try {
gatt = locateGatt(remoteId);
if(gatt.readRemoteRssi()) {
result.success(null);
} else {
result.error("readRssi", "gatt.readRemoteRssi returned false", null);
}
} catch(Exception e) {
result.error("readRssi", e.getMessage(), e);
}

break;
}

default:
{
result.notImplemented();
Expand Down Expand Up @@ -964,6 +982,12 @@ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
log(LogLevel.DEBUG, "[onReadRemoteRssi] rssi: " + rssi + " status: " + status);
if(status == BluetoothGatt.GATT_SUCCESS) {
Protos.ReadRssiResult.Builder p = Protos.ReadRssiResult.newBuilder();
p.setRemoteId(gatt.getDevice().getAddress());
p.setRssi(rssi);
invokeMethodUIThread("ReadRssiResult", p.build().toByteArray());
}
}

@Override
Expand Down
77 changes: 14 additions & 63 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,78 +10,29 @@ project 'Runner', {
'Release' => :release,
}

def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
end

target 'Runner' do
# Flutter Pod

copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.

generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];

unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

# Plugin Pods
flutter_ios_podfile_setup

# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true

post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
flutter_additional_ios_build_settings(target)
end
end
12 changes: 3 additions & 9 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
PODS:
- e2e (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_blue (0.0.1):
- Flutter
Expand All @@ -11,7 +9,6 @@ PODS:
- Protobuf (3.11.4)

DEPENDENCIES:
- e2e (from `.symlinks/plugins/e2e/ios`)
- Flutter (from `Flutter`)
- flutter_blue (from `.symlinks/plugins/flutter_blue/ios`)

Expand All @@ -20,19 +17,16 @@ SPEC REPOS:
- Protobuf

EXTERNAL SOURCES:
e2e:
:path: ".symlinks/plugins/e2e/ios"
Flutter:
:path: Flutter
flutter_blue:
:path: ".symlinks/plugins/flutter_blue/ios"

SPEC CHECKSUMS:
e2e: 967b9b1fc533b7636a3b7a719f840c27f301fe1f
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_blue: eeb381dc4727a0954dede73515f683865494b370
Protobuf: 176220c526ad8bd09ab1fb40a978eac3fef665f7

PODFILE CHECKSUM: 3dbe063e9c90a5d7c9e4e76e70a821b9e2c1d271
PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d

COCOAPODS: 1.9.1
COCOAPODS: 1.10.1
19 changes: 16 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,22 @@ class DeviceScreen extends StatelessWidget {
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
leading: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(snapshot.data == BluetoothDeviceState.connected
? Icons.bluetooth_connected
: Icons.bluetooth_disabled),
snapshot.data == BluetoothDeviceState.connected
? FutureBuilder<int>(
future: device.readRssi(),
builder: (context, snapshot) {
return Text(snapshot.hasData ? '${snapshot.data}' : '',
style: Theme.of(context).textTheme.caption);
})
: Text('', style: Theme.of(context).textTheme.caption),
],
),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
Expand Down
16 changes: 16 additions & 0 deletions ios/Classes/FlutterBluePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,15 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
} else if([@"requestMtu" isEqualToString:call.method]) {
result([FlutterError errorWithCode:@"requestMtu" message:@"iOS does not allow mtu requests to the peripheral" details:NULL]);
} else if([@"readRssi" isEqualToString:call.method]) {
NSString *remoteId = [call arguments];
@try {
CBPeripheral *peripheral = [self findPeripheral:remoteId];
[peripheral readRSSI];
result(nil);
} @catch(FlutterError *e) {
result(e);
}
} else {
result(FlutterMethodNotImplemented);
}
Expand Down Expand Up @@ -538,6 +547,13 @@ - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDesc
[_channel invokeMethod:@"WriteDescriptorResponse" arguments:[self toFlutterData:result]];
}

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)rssi error:(NSError *)error {
ProtosReadRssiResult *result = [[ProtosReadRssiResult alloc] init];
[result setRemoteId:[peripheral.identifier UUIDString]];
[result setRssi:[rssi intValue]];
[_channel invokeMethod:@"ReadRssiResult" arguments:[self toFlutterData:result]];
}

//
// Proto Helper methods
//
Expand Down
15 changes: 15 additions & 0 deletions ios/gen/Flutterblue.pbobjc.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions ios/gen/Flutterblue.pbobjc.m

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions lib/gen/flutterblue.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2186,3 +2186,64 @@ class MtuSizeResponse extends $pb.GeneratedMessage {
void clearMtu() => clearField(2);
}

class ReadRssiResult extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ReadRssiResult', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'remoteId')
..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rssi', $pb.PbFieldType.O3)
..hasRequiredFields = false
;

ReadRssiResult._() : super();
factory ReadRssiResult({
$core.String? remoteId,
$core.int? rssi,
}) {
final _result = create();
if (remoteId != null) {
_result.remoteId = remoteId;
}
if (rssi != null) {
_result.rssi = rssi;
}
return _result;
}
factory ReadRssiResult.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ReadRssiResult.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ReadRssiResult clone() => ReadRssiResult()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ReadRssiResult copyWith(void Function(ReadRssiResult) updates) => super.copyWith((message) => updates(message as ReadRssiResult)) as ReadRssiResult; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ReadRssiResult create() => ReadRssiResult._();
ReadRssiResult createEmptyInstance() => create();
static $pb.PbList<ReadRssiResult> createRepeated() => $pb.PbList<ReadRssiResult>();
@$core.pragma('dart2js:noInline')
static ReadRssiResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ReadRssiResult>(create);
static ReadRssiResult? _defaultInstance;

@$pb.TagNumber(1)
$core.String get remoteId => $_getSZ(0);
@$pb.TagNumber(1)
set remoteId($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasRemoteId() => $_has(0);
@$pb.TagNumber(1)
void clearRemoteId() => clearField(1);

@$pb.TagNumber(2)
$core.int get rssi => $_getIZ(1);
@$pb.TagNumber(2)
set rssi($core.int v) { $_setSignedInt32(1, v); }
@$pb.TagNumber(2)
$core.bool hasRssi() => $_has(1);
@$pb.TagNumber(2)
void clearRssi() => clearField(2);
}

11 changes: 11 additions & 0 deletions lib/gen/flutterblue.pbjson.dart
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,14 @@ const MtuSizeResponse$json = const {

/// Descriptor for `MtuSizeResponse`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List mtuSizeResponseDescriptor = $convert.base64Decode('Cg9NdHVTaXplUmVzcG9uc2USGwoJcmVtb3RlX2lkGAEgASgJUghyZW1vdGVJZBIQCgNtdHUYAiABKA1SA210dQ==');
@$core.Deprecated('Use readRssiResultDescriptor instead')
const ReadRssiResult$json = const {
'1': 'ReadRssiResult',
'2': const [
const {'1': 'remote_id', '3': 1, '4': 1, '5': 9, '10': 'remoteId'},
const {'1': 'rssi', '3': 2, '4': 1, '5': 5, '10': 'rssi'},
],
};

/// Descriptor for `ReadRssiResult`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List readRssiResultDescriptor = $convert.base64Decode('Cg5SZWFkUnNzaVJlc3VsdBIbCglyZW1vdGVfaWQYASABKAlSCHJlbW90ZUlkEhIKBHJzc2kYAiABKAVSBHJzc2k=');
Loading