diff --git a/EasyTouchUnit.cs b/EasyTouchUnit.cs index aa4ca0d..4ef8629 100644 --- a/EasyTouchUnit.cs +++ b/EasyTouchUnit.cs @@ -45,5 +45,20 @@ namespace ScreenLogicConnect System.Diagnostics.Debug.WriteLine(e.StackTrace); } } + + public EasyTouchUnit(Messages.GetGatewayData data) + { + try + { + gatewayName = data.GatewayName; + ipAddress = IPAddress.Parse(data.IPAddr); + port = data.Port; + isValid = data.GatewayFound && data.PortOpen; + } + catch (Exception e) + { + System.Diagnostics.Debug.WriteLine(e.StackTrace); + } + } } } diff --git a/FindUnits.cs b/FindUnits.cs index b6ce493..483adca 100644 --- a/FindUnits.cs +++ b/FindUnits.cs @@ -30,7 +30,7 @@ namespace ScreenLogicConnect await udpClient.SendAsync(broadcastData, broadcastData.Length, new IPEndPoint(IPAddress.Broadcast, multicastPort)); var buf = await udpClient.ReceiveAsync().TimeoutAfter(TimeSpan.FromSeconds(1)); - if (buf != null) + if (buf != null && buf.RemoteEndPoint != null) { var findServerResponse = new EasyTouchUnit(buf); if (findServerResponse.isValid) diff --git a/Messages/GetGatewayData.cs b/Messages/GetGatewayData.cs new file mode 100644 index 0000000..132f044 --- /dev/null +++ b/Messages/GetGatewayData.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ScreenLogicConnect.Messages +{ + public class GetGatewayData : HLMessage + { + public string GatewayName; + public bool GatewayFound; + public bool LicenseOK; + public string IPAddr; + public short Port; + public bool PortOpen; + public bool RelayOn; + + public const short HLM_GETGATEWAYDATA = 18003; + + public static GetGatewayData QUERY(string systemName, short senderID = 0) + { + var ret = new GetGatewayData(senderID, HLM_GETGATEWAYDATA); + ret.GatewayName = systemName; + return ret; + } + + public GetGatewayData(short senderID, short msgID) + : base(senderID, msgID) + { + } + + public GetGatewayData(HLMessage msg) + : base(msg) + { + } + + public override byte[] asByteArray() + { + using (var ms = new MemoryStream()) + { + using (var bw = new BinaryWriter(ms)) + { + bw.WritePrefixLength(GatewayName); + bw.WritePrefixLength(GatewayName); + } + + data = ms.ToArray(); + } + + return base.asByteArray(); + } + + protected override void decode() + { + using (var ms = new MemoryStream(data)) + { + using (var br = new BinaryReader(ms)) + { + GatewayFound = br.ReadBoolean(); + LicenseOK = br.ReadBoolean(); + IPAddr = HLMessageTypeHelper.extractString(br); + Port = br.ReadInt16(); + PortOpen = br.ReadBoolean(); + RelayOn = br.ReadBoolean(); + } + } + } + } +} diff --git a/RemoteConnect.cs b/RemoteConnect.cs new file mode 100644 index 0000000..7c29088 --- /dev/null +++ b/RemoteConnect.cs @@ -0,0 +1,22 @@ +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace ScreenLogicConnect +{ + public class RemoteConnect + { + public const int ServerDispatcherPort = 500; + public const string ServerDispatcherURL = "screenlogicserver.pentair.com"; + + public static async Task GetGatewayInfo(string systemName) + { + using (var client = new TcpClient()) + { + client.Connect(ServerDispatcherURL, ServerDispatcherPort); + var ns = client.GetStream(); + ns.SendHLMessage(Messages.GetGatewayData.QUERY(systemName)); + return new EasyTouchUnit(new Messages.GetGatewayData(await UnitConnection.GetMessage(ns))); + } + } + } +} diff --git a/ScreenLogicConnect.csproj b/ScreenLogicConnect.csproj index 7c8f610..a9d3c4c 100644 --- a/ScreenLogicConnect.csproj +++ b/ScreenLogicConnect.csproj @@ -1,7 +1,8 @@ - netcoreapp2.0 + netcoreapp2.2 + latest diff --git a/Test/Program.cs b/Test/Program.cs index 12b6be3..d3dfd01 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; namespace Test @@ -6,53 +7,90 @@ namespace Test class Program { static async Task Main(string[] args) + { + if (args.Length >= 2) + { + await DoRemoteConnect(args[0], args[1]); + } + else + { + await DoLocalConnect(); + } + } + + static async Task DoLocalConnect() { var servers = await ScreenLogicConnect.FindUnits.Find(); if (servers != null) { foreach (var server in servers) { - var connection = new ScreenLogicConnect.UnitConnection(); - await connection.ConnectTo(server); - var status = connection.GetPoolStatus(); - var config = connection.GetControllerConfig(); - var degSymbol = config.m_DegC == 1 ? "C" : "F"; - Console.WriteLine($"Air temp: {status.m_AirTemp} degrees {degSymbol}"); - var currTempList = status.m_CurrentTemp; - int poolTemp = 0; - int spaTemp = 0; - if (currTempList.Length > 0) + if (server.isValid) { - poolTemp = currTempList[0]; + await ConnectToUnit(server); + break; } - if (currTempList.Length > 1) - { - spaTemp = currTempList[1]; - } - if (poolTemp != 0) - { - Console.WriteLine($"Pool temp: {poolTemp} degrees {degSymbol}{(status.isPoolActive() ? "" : " (Last)")}"); - } - else - { - Console.WriteLine("Couldn't get pool temperature."); - } - if (spaTemp != 0) - { - Console.WriteLine($"Spa temp: {spaTemp} degrees {degSymbol}{(status.isSpaActive() ? "" : " (Last)")}"); - } - else - { - Console.WriteLine("Couldn't get spa temperature."); - } - Console.WriteLine($"ORP: {status.m_ORP}"); - Console.WriteLine($"pH: {status.m_PH / 100.0f:0.00}"); - Console.WriteLine($"Salt: {status.m_SaltPPM * 50} PPM"); - Console.WriteLine($"Saturation: {status.m_Saturation / 100.0f}"); - - break; } } + + if (servers == null || servers.Count == 0 || !servers.Any(x => x.isValid)) + { + Console.WriteLine("No local units found."); + } + } + + static async Task DoRemoteConnect(string systemName, string systemPassword) + { + var unit = await ScreenLogicConnect.RemoteConnect.GetGatewayInfo(systemName); + if (unit.isValid) + { + await ConnectToUnit(unit, systemPassword); + } + else + { + Console.WriteLine("Unable to connect to remote unit."); + } + } + + static async Task ConnectToUnit(ScreenLogicConnect.EasyTouchUnit server, string systemPassword = null) + { + var connection = new ScreenLogicConnect.UnitConnection(); + await connection.ConnectTo(server, systemPassword); + var status = await connection.GetPoolStatus(); + var config = await connection.GetControllerConfig(); + var degSymbol = config.m_DegC == 1 ? "C" : "F"; + Console.WriteLine($"Air temp: {status.m_AirTemp} degrees {degSymbol}"); + var currTempList = status.m_CurrentTemp; + int poolTemp = 0; + int spaTemp = 0; + if (currTempList.Length > 0) + { + poolTemp = currTempList[0]; + } + if (currTempList.Length > 1) + { + spaTemp = currTempList[1]; + } + if (poolTemp != 0) + { + Console.WriteLine($"Pool temp: {poolTemp} degrees {degSymbol}{(status.isPoolActive() ? "" : " (Last)")}"); + } + else + { + Console.WriteLine("Couldn't get pool temperature."); + } + if (spaTemp != 0) + { + Console.WriteLine($"Spa temp: {spaTemp} degrees {degSymbol}{(status.isSpaActive() ? "" : " (Last)")}"); + } + else + { + Console.WriteLine("Couldn't get spa temperature."); + } + Console.WriteLine($"ORP: {status.m_ORP}"); + Console.WriteLine($"pH: {status.m_PH / 100.0f:0.00}"); + Console.WriteLine($"Salt: {status.m_SaltPPM * 50} PPM"); + Console.WriteLine($"Saturation: {status.m_Saturation / 100.0f}"); } } } diff --git a/Test/Test.csproj b/Test/Test.csproj index 0481c66..b5a6d13 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.2 diff --git a/UnitConnection.cs b/UnitConnection.cs index cbb6b50..17fce2a 100644 --- a/UnitConnection.cs +++ b/UnitConnection.cs @@ -11,7 +11,7 @@ namespace ScreenLogicConnect { TcpClient client; - public async Task ConnectTo(EasyTouchUnit unit) + public async Task ConnectTo(EasyTouchUnit unit, string password = null) { if (client != null) { @@ -31,36 +31,45 @@ namespace ScreenLogicConnect var recvBuf = new byte[1024]; var readBytes = stream.Read(recvBuf, 0, recvBuf.Length); Debug.WriteLine("read {0}", readBytes); + string challengeStr = null; + using (var ms = new MemoryStream(recvBuf)) + { + using (var br = new BinaryReader(ms)) + { + br.ReadBytes(8); + challengeStr = Messages.HLMessageTypeHelper.extractString(br); + } + } Debug.WriteLine("sending login message"); - stream.SendHLMessage(createLoginMessage(new byte[16])); + stream.SendHLMessage(createLoginMessage(new HLEncoder(password).GetEncryptedPassword(challengeStr))); readBytes = stream.Read(recvBuf, 0, recvBuf.Length); Debug.WriteLine("read {0}", readBytes); } - public Messages.GetPoolStatus GetPoolStatus() + public async Task GetPoolStatus() { Debug.WriteLine("sending status message"); client.GetStream().SendHLMessage(Messages.GetPoolStatus.QUERY(0)); - return new Messages.GetPoolStatus(getMessage(client.GetStream())); + return new Messages.GetPoolStatus(await GetMessage(client.GetStream())); } - public Messages.GetControllerConfig GetControllerConfig() + public async Task GetControllerConfig() { Debug.WriteLine("sending controller config message"); client.GetStream().SendHLMessage(Messages.GetControllerConfig.QUERY(0)); - return new Messages.GetControllerConfig(getMessage(client.GetStream())); + return new Messages.GetControllerConfig(await GetMessage(client.GetStream())); } - public Messages.GetMode GetMode() + public async Task GetMode() { Debug.WriteLine("sending get-mode message"); client.GetStream().SendHLMessage(Messages.GetMode.QUERY(0)); - return new Messages.GetMode(getMessage(client.GetStream())); + return new Messages.GetMode(await GetMessage(client.GetStream())); } - private static Messages.HLMessage getMessage(NetworkStream ns) + public static async Task GetMessage(NetworkStream ns) { int bytesRead = 0; byte[] headerBuffer = new byte[8]; @@ -68,7 +77,7 @@ namespace ScreenLogicConnect { try { - bytesRead += ns.Read(headerBuffer, bytesRead, 8 - bytesRead); + bytesRead += await ns.ReadAsync(headerBuffer, bytesRead, 8 - bytesRead); if (bytesRead < 0) { return null; @@ -87,7 +96,7 @@ namespace ScreenLogicConnect bytesRead = 0; while (bytesRead < msgDataSize) { - bytesRead += ns.Read((byte[])(Array)dataBuffer, bytesRead, msgDataSize - bytesRead); + bytesRead += await ns.ReadAsync((byte[])(Array)dataBuffer, bytesRead, msgDataSize - bytesRead); if (bytesRead < 0) { @@ -100,7 +109,7 @@ namespace ScreenLogicConnect return new Messages.HLMessage(headerBuffer, dataBuffer); } - private static string connectionMessage = "CONNECTSERVERHOST"; + private static string connectionMessage = "CONNECTSERVERHOST\r\n\r\n"; private byte[] CreateConnectServerSoftMessage() { using (var ms = new MemoryStream()) @@ -108,10 +117,6 @@ namespace ScreenLogicConnect using (var bw = new BinaryWriter(ms)) { bw.Write(Encoding.ASCII.GetBytes(connectionMessage)); - bw.Write((byte)'\r'); - bw.Write((byte)'\n'); - bw.Write((byte)'\r'); - bw.Write((byte)'\n'); } return ms.ToArray(); @@ -126,6 +131,11 @@ namespace ScreenLogicConnect login.m_version = "ScreenLogicConnect library"; login.m_procID = 2; + if (encodedPwd == null) + { + encodedPwd = new byte[16]; + } + if (encodedPwd.Length > 16) { login.m_byteArray = new byte[16];