diff --git a/UniversalBeaconLibrary/Beacon/Beacon.cs b/UniversalBeaconLibrary/Beacon/Beacon.cs index 05995d6..887f43b 100644 --- a/UniversalBeaconLibrary/Beacon/Beacon.cs +++ b/UniversalBeaconLibrary/Beacon/Beacon.cs @@ -225,20 +225,23 @@ public void UpdateBeacon(BluetoothLEAdvertisementReceivedEventArgs btAdv) // Print the company ID + the raw data in hex format //var manufacturerDataString = $"0x{manufacturerData.CompanyId.ToString("X")}: {BitConverter.ToString(manufacturerData.Data.ToArray())}"; //Debug.WriteLine("Manufacturer data: " + manufacturerDataString); + var manufacturerDataArry = manufacturerData.Data.ToArray(); - if (manufacturerData.CompanyId == 0x4C && manufacturerData.Data.Length >= 23 && - manufacturerDataArry[0] == 0x02) + if (BeaconFrameHelper.IsProximityBeaconPayload(manufacturerData.CompanyId, manufacturerDataArry)) { BeaconType = BeaconTypeEnum.iBeacon; //Debug.WriteLine("iBeacon Frame: " + BitConverter.ToString(manufacturerDataArry)); + + var beaconFrame = new ProximityBeaconFrame(manufacturerDataArry); + // Only one relevant data frame for iBeacons if (BeaconFrames.Any()) { - BeaconFrames[0].Payload = manufacturerDataArry; + BeaconFrames[0].Update(beaconFrame); } else { - BeaconFrames.Add(new UnknownBeaconFrame(manufacturerDataArry)); + BeaconFrames.Add(beaconFrame); } } } diff --git a/UniversalBeaconLibrary/Beacon/BeaconFrameHelper.cs b/UniversalBeaconLibrary/Beacon/BeaconFrameHelper.cs index be06638..d865575 100644 --- a/UniversalBeaconLibrary/Beacon/BeaconFrameHelper.cs +++ b/UniversalBeaconLibrary/Beacon/BeaconFrameHelper.cs @@ -107,5 +107,13 @@ public static byte[] CreateEddystoneHeader(EddystoneFrameType eddystoneType) { return new byte[] {0xAA, 0xFE, (byte)eddystoneType}; } + + public static bool IsProximityBeaconPayload(ushort companyId, byte[] manufacturerData) + { + return companyId == ProximityBeaconFrame.CompanyId && + manufacturerData.Length >= 23 && + manufacturerData[0] == ProximityBeaconFrame.DataType && + manufacturerData[1] == ProximityBeaconFrame.DataLength; + } } } diff --git a/UniversalBeaconLibrary/Beacon/ProximityBeaconFrame.cs b/UniversalBeaconLibrary/Beacon/ProximityBeaconFrame.cs new file mode 100644 index 0000000..169549c --- /dev/null +++ b/UniversalBeaconLibrary/Beacon/ProximityBeaconFrame.cs @@ -0,0 +1,115 @@ +using System; + +namespace UniversalBeaconLibrary.Beacon +{ + public class ProximityBeaconFrame : BeaconFrameBase + { + public const ushort CompanyId = 0x004C; + public const byte DataType = 0x02; + public const byte DataLength = 0x15; + + private Guid _uuid; + private ushort _major; + private ushort _minor; + private sbyte _txPower; + + public ProximityBeaconFrame(byte[] payload) + : base(payload) + { + ParsePayload(); + } + + public string UuidAsString => Uuid.ToString("D"); + + public Guid Uuid + { + get { return _uuid; } + set + { + if (_uuid == value) return; + _uuid = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(UuidAsString)); + } + } + + public string MajorAsString => Major.ToString("x4"); + + public ushort Major + { + get { return _major; } + set + { + if (_major == value) return; + _major = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(MajorAsString)); + } + } + + public string MinorAsString => Minor.ToString("x4"); + + public ushort Minor + { + get { return _minor; } + set + { + if (_minor == value) return; + _minor = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(MinorAsString)); + } + } + + public sbyte TxPower + { + get { return _txPower; } + set + { + if (_txPower == value) return; + _txPower = value; + OnPropertyChanged(); + } + } + + private void ParsePayload() + { + var dataType = Payload[0]; + var dataLength = Payload[1]; + + if (dataType != DataType || dataLength != DataLength) + throw new InvalidOperationException(); + + // need to swap to big-endian + var uuidBytes = new byte[16]; + if (BitConverter.IsLittleEndian) + { + Array.ConstrainedCopy(Payload, 2, uuidBytes, 0, 16); + Array.Reverse(uuidBytes, 0, 4); + Array.Reverse(uuidBytes, 4, 2); + Array.Reverse(uuidBytes, 6, 2); + } + Uuid = new Guid(uuidBytes); + + var majorBytes = new byte[2]; + Array.ConstrainedCopy(Payload, 18, majorBytes, 0, 2); + if (BitConverter.IsLittleEndian) + Array.Reverse(majorBytes); + Major = BitConverter.ToUInt16(majorBytes,0); + + var minorBytes = new byte[2]; + Array.ConstrainedCopy(Payload, 20, minorBytes, 0, 2); + if (BitConverter.IsLittleEndian) + Array.Reverse(minorBytes); + Minor = BitConverter.ToUInt16(minorBytes, 0); + + TxPower = (sbyte) Payload[22]; + } + + public override void Update(BeaconFrameBase otherFrame) + { + base.Update(otherFrame); + ParsePayload(); + } + } +} diff --git a/UniversalBeaconLibrary/UniversalBeaconLibrary.csproj b/UniversalBeaconLibrary/UniversalBeaconLibrary.csproj index 30188e4..6aabb84 100644 --- a/UniversalBeaconLibrary/UniversalBeaconLibrary.csproj +++ b/UniversalBeaconLibrary/UniversalBeaconLibrary.csproj @@ -118,6 +118,7 @@ + diff --git a/WindowsBeacons/Converter/BeaconFrameTypeToTextConverter.cs b/WindowsBeacons/Converter/BeaconFrameTypeToTextConverter.cs index 898a8f2..7c6820d 100644 --- a/WindowsBeacons/Converter/BeaconFrameTypeToTextConverter.cs +++ b/WindowsBeacons/Converter/BeaconFrameTypeToTextConverter.cs @@ -33,6 +33,8 @@ public object Convert(object value, Type targetType, object parameter, string la return "UID Frame"; if (value is UrlEddystoneFrame) return "URL Frame"; + if (value is ProximityBeaconFrame) + return "Proximity Frame"; return null; } diff --git a/WindowsBeacons/FrameTemplateSelector.cs b/WindowsBeacons/FrameTemplateSelector.cs index cc3616b..f6a005e 100644 --- a/WindowsBeacons/FrameTemplateSelector.cs +++ b/WindowsBeacons/FrameTemplateSelector.cs @@ -30,6 +30,8 @@ public class FrameTemplateSelector : DataTemplateSelector public DataTemplate UrlEddystoneFrameTemplate { get; set; } + public DataTemplate ProximityBeaconFrameTemplate { get; set; } + protected override DataTemplate SelectTemplateCore(object item) { var itemType = item.GetType(); @@ -42,6 +44,8 @@ protected override DataTemplate SelectTemplateCore(object item) return UidEddystoneFrameTemplate; if (itemType == typeof(UrlEddystoneFrame)) return UrlEddystoneFrameTemplate; + if (itemType == typeof (ProximityBeaconFrame)) + return ProximityBeaconFrameTemplate; return base.SelectTemplateCore(item); diff --git a/WindowsBeacons/MainPage.xaml b/WindowsBeacons/MainPage.xaml index 7d1773f..4372503 100644 --- a/WindowsBeacons/MainPage.xaml +++ b/WindowsBeacons/MainPage.xaml @@ -10,7 +10,6 @@ mc:Ignorable="d" RequestedTheme="Light"> - @@ -169,6 +168,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -201,7 +257,8 @@ UrlEddystoneFrameTemplate="{StaticResource UrlEddystoneFrameTemplate}" TlmEddystoneFrameTemplate="{StaticResource TlmEddystoneFrameTemplate}" UidEddystoneFrameTemplate="{StaticResource UidEddystoneFrameTemplate}" - UnknownBeaconFrameTemplate="{StaticResource UnknownBeaconFrameTemplate}"/> + ProximityBeaconFrameTemplate="{StaticResource ProximityBeaconFrameTemplate}" + UnknownBeaconFrameTemplate="{StaticResource UnknownBeaconFrameTemplate}" /> diff --git a/WindowsBeacons/Strings/de-DE/Resources.resw b/WindowsBeacons/Strings/de-DE/Resources.resw index b74d997..f2acd8a 100644 --- a/WindowsBeacons/Strings/de-DE/Resources.resw +++ b/WindowsBeacons/Strings/de-DE/Resources.resw @@ -179,6 +179,12 @@ Stelle sicher, dass dein Gerät Bluetooth Smart (LE) unterstützt und dass Bluet https://github.com/andijakl/universal-beacon + + Major: + + + Minor: + Namespace ID: @@ -230,12 +236,18 @@ Stelle sicher, dass dein Gerät Bluetooth Smart (LE) unterstützt und dass Bluet https://twitter.com/andijakl + + TX Power: + Letztes Update: URL: + + UUID: + Version: diff --git a/WindowsBeacons/Strings/en-US/Resources.resw b/WindowsBeacons/Strings/en-US/Resources.resw index 2eba73d..407e328 100644 --- a/WindowsBeacons/Strings/en-US/Resources.resw +++ b/WindowsBeacons/Strings/en-US/Resources.resw @@ -179,9 +179,15 @@ Make sure your device supports Bluetooth Smart (LE) and that Bluetooth is activa https://github.com/andijakl/universal-beacon + + Major: + Major ID + + Minor: + Minor ID @@ -236,6 +242,9 @@ Make sure your device supports Bluetooth Smart (LE) and that Bluetooth is activa https://twitter.com/andijakl + + TX Power: + Updated: