/
Device.cs
182 lines (161 loc) · 7.76 KB
/
Device.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CoreBluetooth;
using Foundation;
using Plugin.BLE.Abstractions;
using Plugin.BLE.Abstractions.Contracts;
using Plugin.BLE.Abstractions.Utils;
namespace Plugin.BLE.iOS
{
public class Device : DeviceBase<CBPeripheral>
{
private readonly IBleCentralManagerDelegate _bleCentralManagerDelegate;
public Device(Adapter adapter, CBPeripheral nativeDevice, IBleCentralManagerDelegate bleCentralManagerDelegate)
: this(adapter, nativeDevice, bleCentralManagerDelegate, nativeDevice.Name, nativeDevice.RSSI?.Int32Value ?? 0,
new List<AdvertisementRecord>(), true)
{
}
public Device(Adapter adapter, CBPeripheral nativeDevice, IBleCentralManagerDelegate bleCentralManagerDelegate, string name, int rssi, List<AdvertisementRecord> advertisementRecords, bool isConnectable = true)
: base(adapter, nativeDevice)
{
_bleCentralManagerDelegate = bleCentralManagerDelegate;
Id = Guid.ParseExact(NativeDevice.Identifier.AsString(), "d");
Name = name;
Rssi = rssi;
AdvertisementRecords = advertisementRecords;
IsConnectable = isConnectable;
// TODO figure out if this is in any way required,
// https://github.com/xabre/xamarin-bluetooth-le/issues/81
//_nativeDevice.UpdatedName += OnNameUpdated;
}
private void OnNameUpdated(object sender, EventArgs e)
{
Name = ((CBPeripheral)sender).Name;
Trace.Message("Device changed name: {0}", Name);
}
protected override Task<IReadOnlyList<IService>> GetServicesNativeAsync()
{
return GetServicesInternal();
}
protected override async Task<IService> GetServiceNativeAsync(Guid id)
{
var cbuuid = CBUUID.FromString(id.ToString());
var nativeService = NativeDevice.Services?.FirstOrDefault(service => service.UUID.Equals(cbuuid));
if (nativeService != null)
{
return new Service(nativeService, this, _bleCentralManagerDelegate);
}
var services = await GetServicesInternal(cbuuid);
return services?.FirstOrDefault();
}
private Task<IReadOnlyList<IService>> GetServicesInternal(CBUUID id = null)
{
var exception = new Exception($"Device {Name} disconnected while fetching services.");
return TaskBuilder.FromEvent<IReadOnlyList<IService>, EventHandler<NSErrorEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
{
if (NativeDevice.State != CBPeripheralState.Connected)
throw exception;
if (id != null)
{
NativeDevice.DiscoverServices(new[] { id });
}
else
{
NativeDevice.DiscoverServices();
}
},
getCompleteHandler: (complete, reject) => (sender, args) =>
{
// If args.Error was not null then the Service might be null
if (args.Error != null)
{
reject(new Exception($"Error while discovering services {args.Error.LocalizedDescription}"));
}
else if (NativeDevice.Services == null)
{
// No service discovered.
reject(new Exception($"Error while discovering services: returned list is null"));
}
else
{
var services = NativeDevice.Services
.Select(nativeService => new Service(nativeService, this, _bleCentralManagerDelegate))
.Cast<IService>().ToList();
complete(services);
}
},
subscribeComplete: handler => NativeDevice.DiscoveredService += handler,
unsubscribeComplete: handler => NativeDevice.DiscoveredService -= handler,
getRejectHandler: reject => ((sender, args) =>
{
if (args.Peripheral.Identifier == NativeDevice.Identifier)
reject(exception);
}),
subscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral += handler,
unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler);
}
public override Task<bool> UpdateRssiAsync()
{
return TaskBuilder.FromEvent<bool, EventHandler<CBRssiEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () => NativeDevice.ReadRSSI(),
getCompleteHandler: (complete, reject) => (sender, args) =>
{
if (args.Error != null)
{
reject(new Exception($"Error while reading rssi services {args.Error.LocalizedDescription}"));
}
else
{
Rssi = args.Rssi?.Int32Value ?? 0;
complete(true);
}
},
subscribeComplete: handler => NativeDevice.RssiRead += handler,
unsubscribeComplete: handler => NativeDevice.RssiRead -= handler,
getRejectHandler: reject => ((sender, args) =>
{
if (args.Peripheral.Identifier == NativeDevice.Identifier)
reject(new Exception($"Device {Name} disconnected while reading RSSI."));
}),
subscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral += handler,
unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler);
}
protected override DeviceState GetState()
{
switch (NativeDevice.State)
{
case CBPeripheralState.Connected:
return DeviceState.Connected;
case CBPeripheralState.Connecting:
return DeviceState.Connecting;
case CBPeripheralState.Disconnected:
return DeviceState.Disconnected;
case CBPeripheralState.Disconnecting:
return DeviceState.Disconnected;
default:
return DeviceState.Disconnected;
}
}
public void Update(CBPeripheral nativeDevice)
{
Rssi = nativeDevice.RSSI?.Int32Value ?? 0;
//It's maybe not the best idea to updated the name based on CBPeripherial name because this might be stale.
//Name = nativeDevice.Name;
}
protected override async Task<int> RequestMtuNativeAsync(int requestValue)
{
Trace.Message($"Request MTU is not supported on iOS.");
return await Task.FromResult((int)NativeDevice.GetMaximumWriteValueLength(CBCharacteristicWriteType.WithoutResponse));
}
protected override bool UpdateConnectionIntervalNative(ConnectionInterval interval)
{
Trace.Message("Cannot update connection inteval on iOS.");
return false;
}
public override bool IsConnectable { get; protected set; }
public override bool SupportsIsConnectable { get => true; }
}
}