diff --git a/ConsoleTest/ConsoleTest.csproj b/ConsoleTest/ConsoleTest.csproj
index b15406f..1d063e8 100644
--- a/ConsoleTest/ConsoleTest.csproj
+++ b/ConsoleTest/ConsoleTest.csproj
@@ -5,10 +5,6 @@
netcoreapp2.0
-
-
-
-
diff --git a/ConsoleTest/Program.cs b/ConsoleTest/Program.cs
index 11a544a..85c4223 100644
--- a/ConsoleTest/Program.cs
+++ b/ConsoleTest/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -28,10 +29,6 @@ static void Main(string[] args)
// .GetAwaiter()
// .GetResult();
- //Program.RmTest()
- // .GetAwaiter()
- // .GetResult();
-
//Program.RmTemperatureTest()
// .GetAwaiter()
// .GetResult();
@@ -49,6 +46,8 @@ static void Main(string[] args)
Program.RmRfSignalTest()
.GetAwaiter()
.GetResult();
+
+ //TestIrLearning().Wait();
}
catch(Exception e)
{
@@ -64,6 +63,32 @@ static void Main(string[] args)
#region "TestOK"
+ private static async Task TestIrLearning()
+ {
+ Console.WriteLine("Searching for Rm2Pro devices...");
+ var devs = await Broadlink.Discover(1);
+ var rm = (Rm2Pro)devs.FirstOrDefault(d => d.DeviceType == DeviceType.Rm2Pro);
+
+ if (rm == null)
+ throw new Exception("Rm2Pro Not Found");
+ else Console.WriteLine($"Rm2Pro found at {rm.Host}");
+
+ if (!await rm.Auth())
+ throw new Exception("Auth Failure");
+
+ Console.WriteLine("Learning command, press remote now");
+ var command = await rm.LearnIRCommnad(CancellationToken.None);
+ if (command == null || command.Length == 0)
+ throw new Exception("Failed to learn command");
+
+ Console.WriteLine("Command learned, press any key to send the command now...");
+ Console.ReadKey();
+
+ Console.WriteLine("Sending IR command");
+ await rm.SendData(command);
+ Console.WriteLine("IR command sent");
+ }
+
private static async Task RmRfSignalTest()
{
Console.WriteLine("Searching for Rm2Pro devices...");
@@ -87,13 +112,27 @@ private static async Task RmRfSignalTest()
byte[] command = null;
try
{
- command = await rm.LearnRfCommand(cancellationSource.Token, () =>
+ Action learnInstructionHandler = (instructions) =>
+ {
+ //Get description from the enum - any other text will do
+ var msg = instructions.GetType().GetField(instructions.ToString())
+ ?.GetCustomAttributes(typeof(DescriptionAttribute), true)
+ .OfType()
+ .FirstOrDefault()
+ ?.Description
+ ?? instructions.ToString();
+
+ Console.WriteLine(msg);
+ Console.ReadKey(true);
+ };
+
+ command = await rm.LearnRfCommand(learnInstructionHandler, cancellationSource.Token, () =>
{
Console.Write('.');
});
if (command == null || command.Length == 0)
- throw new InvalidOperationException("Failed to learn RF command");
+ throw new InvalidOperationException("Failed to learn RF command");
}
catch(TaskCanceledException)
{
@@ -108,7 +147,7 @@ private static async Task RmRfSignalTest()
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
Console.Write("Sending RF command... ");
- await rm.SendRfData(command);
+ await rm.SendData(command);
Console.WriteLine("RF command sent");
}
@@ -138,26 +177,6 @@ private static async Task AuthTest()
return true;
}
- private static async Task RmTest()
- {
- var devs = await Broadlink.Discover();
-
- var rm = (Rm)devs[0];
-
- if (!await rm.Auth())
- throw new Exception("Auth Failure");
-
- await rm.EnterLearning();
-
- var data1 = await rm.CheckData();
-
- var data2 = await rm.CheckData();
-
- await rm.SendData(data2);
-
- return true;
- }
-
private static async Task RmTemperatureTest()
{
var devs = await Broadlink.Discover();
diff --git a/SharpBroadlink/Broadlink.cs b/SharpBroadlink/Broadlink.cs
index 85d094c..2f679c9 100644
--- a/SharpBroadlink/Broadlink.cs
+++ b/SharpBroadlink/Broadlink.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Linq;
using System.Net;
+using System.Threading;
using System.Threading.Tasks;
namespace SharpBroadlink
@@ -18,16 +19,33 @@ public enum WifiSecurityMode
WPA12 = 4
}
+ public static async Task> Discover(int timeout = 0)
+ {
+ var result = new List();
+ if (timeout == 0)
+ {
+ await Discover(result);
+ }
+ else
+ {
+ using (var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)))
+ await Discover(result, null, cancellationSource.Token);
+ }
+
+ return result;
+ }
+
///
/// Find devices on the LAN.
///
- ///
+ /// Devices result collection
+ /// Optional Cancellation Token. If not provided the method will exit after discovering at least one device or after a timeout
///
///
///
/// https://github.com/mjg59/python-broadlink/blob/56b2ac36e5a2359272f4af8a49cfaf3e1891733a/broadlink/__init__.py#L61-L138
///
- public static async Task Discover(int timeout = 0, IPAddress localIpAddress = null)
+ public static async Task Discover(ICollection devices, IPAddress localIpAddress = null, CancellationToken cancellationToken = default)
{
if (localIpAddress == null)
localIpAddress = IPAddress.Any;
@@ -45,7 +63,6 @@ public enum WifiSecurityMode
{
var port = cs.LocalPort;
var startTime = DateTime.Now;
- var devices = new List();
var timezone = (int)((System.TimeZoneInfo.Local).BaseUtcOffset.TotalSeconds / -3600);
var year = startTime.Year;
var subYear = year % 100;
@@ -95,13 +112,14 @@ public enum WifiSecurityMode
packet[0x20] = (byte)(checksum & 0xff);
packet[0x21] = (byte)(checksum >> 8);
- var isRecievedOnce = false;
+ var isReceivedOnce = false;
cs.OnRecieved += (object sender, Xb.Net.RemoteData rdata) =>
{
// Get mac
// 0x3a-0x3f, Little Endian
var mac = new byte[6];
Array.Copy(rdata.Bytes, 0x3a, mac, 0, 6);
+ Array.Reverse(mac);
// Get IP address
byte[] addr;
@@ -176,29 +194,31 @@ public enum WifiSecurityMode
var devType = (rdata.Bytes[0x34] | rdata.Bytes[0x35] << 8);
devices.Add(Devices.Factory.GenDevice(devType, host, mac));
- isRecievedOnce = true;
+ isReceivedOnce = true;
};
await cs.SendToAsync(packet, IPAddress.Broadcast, 80);
- await Task.Run(() =>
+ try
{
- while (true)
+ await Task.Run(async () =>
{
- if ((timeout <= 0 && isRecievedOnce)
- || (timeout > 0
- && (DateTime.Now - startTime).TotalSeconds > timeout)
- )
- break;
-
- Task.Delay(100)
- .ConfigureAwait(false)
- .GetAwaiter()
- .GetResult();
- }
- });
+ while (true)
+ {
+ if ((cancellationToken.Equals(CancellationToken.None) && (isReceivedOnce || (DateTime.Now - startTime).TotalSeconds > 10))
+ || cancellationToken.IsCancellationRequested)
+ break;
- return devices.ToArray();
+ await Task.Delay(100).ConfigureAwait(false);
+ }
+ });
+ }
+ catch (TaskCanceledException)
+ {
+ }
+ catch (OperationCanceledException)
+ {
+ }
}
}
diff --git a/SharpBroadlink/Devices/LearnInstructions.cs b/SharpBroadlink/Devices/LearnInstructions.cs
new file mode 100644
index 0000000..02f023c
--- /dev/null
+++ b/SharpBroadlink/Devices/LearnInstructions.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+
+namespace SharpBroadlink.Devices
+{
+ public enum LearnInstructions
+ {
+ [Description("Press and hold the remote button")]
+ PressAndHoldRemotedButton,
+ [Description("Release the remote button")]
+ ReleaseRemoteButton,
+ [Description("Press shortly the remote button you want to learn")]
+ PressRemoteButtonShortly,
+ }
+}
diff --git a/SharpBroadlink/Devices/Rm.cs b/SharpBroadlink/Devices/Rm.cs
index ec2c62d..5549dae 100644
--- a/SharpBroadlink/Devices/Rm.cs
+++ b/SharpBroadlink/Devices/Rm.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -18,7 +17,7 @@ public class Rm : Device
{
#region Properties
- public static TimeSpan IRLearnIterval { get; set; } = TimeSpan.FromMilliseconds(100);
+ public static TimeSpan IRLearnIterval { get; set; } = TimeSpan.FromMilliseconds(1000);
#endregion Properties
@@ -39,47 +38,6 @@ public Rm(IPEndPoint host, byte[] mac, int devType) : base(host, mac, devType)
#region Public Methods
- ///
- /// Into IR Learning mode
- ///
- ///
- public async Task EnterLearning()
- {
- var packet = new byte[16];
- packet[0] = 0x03;
-
- await SendPacket(0x6a, packet);
-
- return true;
- }
-
- ///
- /// Check recieved IR signal-data
- ///
- ///
- public async Task CheckData()
- {
- var packet = new byte[16];
- packet[0] = 0x04;
-
- var response = await SendPacket(0x6a, packet);
- if (response == null || response.Length <= 0x38)
- return null;
-
- var err = response[0x22] | (response[0x23] << 8);
- if (err == 0)
- {
- var payload = Decrypt(response, 0x38);
- if (payload.Length <= 0x04)
- return null;
-
- return payload.Skip(0x04).Take(int.MaxValue).ToArray();
- }
-
- // failure
- return null;
- }
-
///
/// Cancel IR Learning mode
///
@@ -180,19 +138,24 @@ public async Task SendPronto(string data)
/// IR command
/// In case of failure
/// In case learning has benn cancelled
- public Task LearnIRCommnad(CancellationToken cancellationToken)
- {
+ public Task LearnIRCommnad(Action learnInstructions, CancellationToken cancellationToken)
+ {
return Task.Run(async () =>
{
try
{
- if (!await EnterLearning())
+ if (!await CancelLearning())
+ throw new InvalidOperationException("Failed to cancel any previous learning");
+
+ if (!await EnterIrLearning())
throw new InvalidOperationException("Failed to enter IR learning");
cancellationToken.ThrowIfCancellationRequested();
+ learnInstructions?.Invoke(LearnInstructions.PressRemoteButtonShortly);
+ cancellationToken.ThrowIfCancellationRequested();
byte[] command = null;
- while(null == (command = await CheckData()))
+ while (null == (command = await CheckData()))
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(IRLearnIterval);
@@ -208,5 +171,54 @@ public Task LearnIRCommnad(CancellationToken cancellationToken)
}
#endregion Public Methods
+
+ #region Protected Methods
+
+ ///
+ /// Check recieved IR signal-data
+ ///
+ ///
+ protected async Task CheckData()
+ {
+ var packet = new byte[16];
+ packet[0] = 0x04;
+
+ var response = await SendPacket(0x6a, packet);
+ if (response == null || response.Length <= 0x38)
+ return null;
+
+ var err = response[0x22] | (response[0x23] << 8);
+ if (err == 0)
+ {
+ var payload = Decrypt(response, 0x38);
+ if (payload.Length <= 0x04)
+ return null;
+
+ return payload.Skip(0x04).Take(int.MaxValue).ToArray();
+ }
+
+ // failure
+ return null;
+ }
+
+ #endregion Protected Methods
+
+ #region Private Methods
+
+ ///
+ /// Into IR Learning mode
+ ///
+ ///
+ private async Task EnterIrLearning()
+ {
+ var packet = new byte[16];
+ packet[0] = 0x03;
+
+ await SendPacket(0x6a, packet);
+
+ return true;
+ }
+
+ #endregion Private Methods
}
}
diff --git a/SharpBroadlink/Devices/Rm2Pro.cs b/SharpBroadlink/Devices/Rm2Pro.cs
index c877f19..cf1f0bf 100644
--- a/SharpBroadlink/Devices/Rm2Pro.cs
+++ b/SharpBroadlink/Devices/Rm2Pro.cs
@@ -1,8 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -15,8 +12,8 @@ public class Rm2Pro : Rm
{
#region Properties
- public static TimeSpan RfFrequencyLearnInterval { get; set; } = TimeSpan.FromMilliseconds(100);
- public static TimeSpan RfCommandLearnInterval { get; set; } = TimeSpan.FromMilliseconds(100);
+ public static TimeSpan RfFrequencyLearnInterval { get; set; } = TimeSpan.FromMilliseconds(1000);
+ public static TimeSpan RfCommandLearnInterval { get; set; } = TimeSpan.FromMilliseconds(1000);
#endregion Properties
@@ -36,97 +33,25 @@ public Rm2Pro(IPEndPoint host, byte[] mac, int devType) : base(host, mac, devTyp
#endregion Constructors
#region Public Methods
-
- ///
- /// Switch into RF Learning mode
- ///
- ///
- public async Task SweepFrequencies()
- {
- var packet = new byte[16];
- packet[0] = 0x19;
-
- await SendPacket(0x6a, packet);
-
- return true;
- }
- ///
- /// Enter RF frequency learning
- ///
- /// True if entering learning was successful
- public async Task LearnRfFrequency()
- {
- var packet = new byte[16];
- packet[0] = 0x1a;
-
- var response = await SendPacket(0x6a, packet);
- if (response == null || response.Length <= 0x38)
- return false;
-
- var err = response[0x22] | (response[0x23] << 8);
-
- if (err == 0)
- {
- var payload = Decrypt(response, 0x38);
- if (payload.Length <= 0x04)
- return false;
-
- return payload[0x04] == 1;
- }
-
- // failure
- return false;
- }
-
- ///
- /// Cancel RF Learning mode
- ///
- /// True if the succedded, false otherwise
- public async Task CancelRfLearning()
- {
- return await CancelLearning();
- }
-
- ///
- /// [Pilot Implementation] Send RF signal-data
- ///
- ///
- ///
- ///
- /// ** Warning **
- /// This is a pilot implementation.
- /// Learning of RF signal does NOT WORKS.
- ///
- /// I have not done it.
- /// Because signal data could not be acquired.
- ///
- public async Task SendRfData(byte[] data)
- {
- var packet = new List() { 0x02, 0x00, 0x00, 0x00 };
- packet.AddRange(data);
-
- await SendPacket(0x6a, packet.ToArray());
-
- return true;
- }
-
///
/// Async method for learning RF commands
///
/// User should press and hold remote button until learning is finished
+ /// When this action is invoked, instructions should be show to the user
/// Used to cancel RF learning
/// Learned RF command
/// In case of failure
/// In case learning has benn cancelled
- public async Task LearnRfCommand(CancellationToken cancellationToken, Action learnProgress = null)
- {
+ public async Task LearnRfCommand(Action learnInstructions, CancellationToken cancellationToken, Action learnProgress = null)
+ {
return await Task.Run(async () =>
{
try
{
- if (!await CancelRfLearning())
+ if (!await CancelLearning())
throw new InvalidOperationException("Failed to cancel any previous learning");
+ learnInstructions?.Invoke(LearnInstructions.PressAndHoldRemotedButton);
if (!await SweepFrequencies())
throw new InvalidOperationException("Failed to sweep frequencies");
@@ -134,26 +59,29 @@ public async Task LearnRfCommand(CancellationToken cancellationToken, Ac
//Learn RF frequency - remote should be held all the time
while (!await LearnRfFrequency())
{
- learnProgress?.Invoke();
cancellationToken.ThrowIfCancellationRequested();
+ learnProgress?.Invoke();
await Task.Delay(RfFrequencyLearnInterval, cancellationToken);
}
+ learnInstructions?.Invoke(LearnInstructions.ReleaseRemoteButton);
+
//Move to step 2 - learning the actual command
- cancellationToken.ThrowIfCancellationRequested();
+ await FindRfPacket();
+ learnInstructions?.Invoke(LearnInstructions.PressRemoteButtonShortly);
byte[] commandData = null;
- while(null == (commandData = await CheckData()))
+ while (null == (commandData = await CheckData()))
{
- learnProgress?.Invoke();
cancellationToken.ThrowIfCancellationRequested();
+ learnProgress?.Invoke();
await Task.Delay(RfCommandLearnInterval, cancellationToken);
}
return commandData;
}
finally
{
- await CancelRfLearning();
+ await CancelLearning();
}
}, cancellationToken);
}
@@ -163,28 +91,73 @@ public async Task LearnRfCommand(CancellationToken cancellationToken, Ac
#region Private Methods
///
- /// This doesn't seem to be required for RF learning at all. I'm leaving it because of phyton lib campatibility
+ /// Switch into RF Learning mode
///
///
- private async Task find_rf_packet()
+ private async Task SweepFrequencies()
{
var packet = new byte[16];
- packet[0] = 0x1b;
+ packet[0] = 0x19;
+
+ await SendPacket(0x6a, packet);
+
+ return true;
+ }
+
+ ///
+ /// Enter RF frequency learning
+ ///
+ /// True if entering learning was successful
+ private async Task LearnRfFrequency()
+ {
+ var packet = new byte[16];
+ packet[0] = 0x1a;
var response = await SendPacket(0x6a, packet);
if (response == null || response.Length <= 0x38)
return false;
var err = response[0x22] | (response[0x23] << 8);
+
if (err == 0)
{
var payload = Decrypt(response, 0x38);
+ if (payload.Length <= 0x04)
+ return false;
+
return payload[0x04] == 1;
}
+ // failure
return false;
- }
+ }
+
+ ///
+ /// Starts RF packet discovery
+ ///
+ /// Return value doesn't seem to be correct, it's always incorrect
+ /// Ignore?
+ private async Task FindRfPacket()
+ {
+ var packet = new byte[16];
+ packet[0] = 0x1b;
+
+ var response = await SendPacket(0x6a, packet);
+ if (response == null || response.Length <= 0x39)
+ return false;
+
+ var err = response[0x22] | (response[0x23] << 8);
+ if (err == 0)
+ {
+ var payload = Decrypt(response, 0x38);
+ if (payload.Length < 5)
+ return false;
+ return payload[0x04] == 1;
+ }
+ return false;
+ }
+
#endregion Private Methods
}
}
diff --git a/SharpBroadlink/SharpBroadlink.csproj b/SharpBroadlink/SharpBroadlink.csproj
index 5766de6..5942676 100644
--- a/SharpBroadlink/SharpBroadlink.csproj
+++ b/SharpBroadlink/SharpBroadlink.csproj
@@ -15,10 +15,10 @@ This is a port of python-broadlink to C# .Net Standard.
ja
true
1.0.2.0
+ latest
-