diff --git a/LGTV Companion Service/Service.cpp b/LGTV Companion Service/Service.cpp index 65f9992..caceeab 100644 --- a/LGTV Companion Service/Service.cpp +++ b/LGTV Companion Service/Service.cpp @@ -695,27 +695,34 @@ void InitDeviceSessions() json j; if (item.key() == JSON_PREFS_NODE) break; - string s; + stringstream s; SESSIONPARAMETERS params; params.DeviceId = item.key(); - s += params.DeviceId; s += ", "; + s << params.DeviceId << ", "; if (item.value()["Name"].is_string()) params.Name = item.value()["Name"].get(); - s += params.Name; - s += ", with IP "; + s << params.Name << ", with IP "; + if(item.value()["IP"].is_string()) params.IP = item.value()["IP"].get(); - s += params.IP; s += " initiated ("; - + s << params.IP << " initiated ("; + if (item.value()["Enabled"].is_boolean()) params.Enabled = item.value()["Enabled"].get(); - s += "Enabled:"; s += params.Enabled ? "yes" : "no"; s += ", "; - + s << "Enabled:" << (params.Enabled?"yes":"no") << ", "; + + if (item.value()["Subnet"].is_string()) + params.Subnet = item.value()["Subnet"].get(); + if (item.value()["WOL"].is_number()) + params.WOLtype = item.value()["WOL"].get(); + s << "WOL:" << params.WOLtype << ", "; + if (params.WOLtype == WOL_SUBNETBROADCAST && params.Subnet != "") + s << "SubnetMask:" << params.Subnet << ", "; if(item.value()["SessionKey"].is_string()) params.SessionKey = item.value()["SessionKey"].get(); - s += "Pairing key:"; s += params.SessionKey =="" ? "n/a" : params.SessionKey; s += ", MAC: "; + s << "Pairing key:" << (params.SessionKey =="" ? "n/a" : params.SessionKey) << ", MAC: "; j = item.value()["MAC"]; if (!j.empty() && j.size() > 0) @@ -723,18 +730,18 @@ void InitDeviceSessions() for (auto& m : j.items()) { params.MAC.push_back(m.value().get()); - s += m.value().get(); s += " "; + s << m.value().get()<< " "; } - s += ")"; + s << ")"; } else - s += "n/a )"; + s << "n/a )"; params.PowerOnTimeout = Prefs.PowerOnTimeout; CSession S(¶ms); DeviceCtrlSessions.push_back(S); - Log(s); + Log(s.str()); } return; } diff --git a/LGTV Companion Service/Service.h b/LGTV Companion Service/Service.h index 4a95078..9913f3a 100644 --- a/LGTV Companion Service/Service.h +++ b/LGTV Companion Service/Service.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "nlohmann/json.hpp" #include "Handshake.h" @@ -31,7 +32,7 @@ #pragma comment(lib, "Advapi32.lib") #define APPNAME L"LGTV Companion" -#define APPVERSION L"1.3.0" +#define APPVERSION L"1.4.0" #define SVCNAME L"LGTVsvc" #define SVCDISPLAYNAME L"LGTV Companion Service" #define SERVICE_PORT "3000" @@ -65,6 +66,13 @@ #define APP_CMDLINE_AUTOENABLE 3 #define APP_CMDLINE_AUTODISABLE 4 +#define WOL_NETWORKBROADCAST 1 +#define WOL_IPSEND 2 +#define WOL_SUBNETBROADCAST 3 + +#define WOL_DEFAULTSUBNET L"255.255.255.0" + + #define PIPENAME TEXT("\\\\.\\pipe\\LGTVyolo") @@ -92,6 +100,8 @@ struct SESSIONPARAMETERS { int PowerOnTimeout = 40; std::string Name; bool Enabled = true; + std::string Subnet; + int WOLtype = 1; }; class CSession { diff --git a/LGTV Companion Service/Session.cpp b/LGTV Companion Service/Session.cpp index 4a2aa8f..0c9b057 100644 --- a/LGTV Companion Service/Session.cpp +++ b/LGTV Companion Service/Session.cpp @@ -199,7 +199,7 @@ void DisplayPowerOnThread(SESSIONPARAMETERS * CallingSessionParameters, bool * C handshake.replace(ckf, ck.length(), key); } - //try waking up the display ten times + //try waking up the display ten times, but not longer than timeout user preference while (time(0) - origtim < (Timeout+1)) { time_t looptim = time(0); @@ -291,7 +291,11 @@ void WOLthread (SESSIONPARAMETERS* CallingSessionParameters, bool* CallingSessio return; vector MACs = CallingSessionParameters->MAC; + string IP = CallingSessionParameters->IP; string device = CallingSessionParameters->DeviceId; + string subnet = CallingSessionParameters->Subnet; + int WOLtype = CallingSessionParameters->WOLtype; + SOCKET WOLsocket = INVALID_SOCKET; string logmsg; @@ -301,10 +305,68 @@ void WOLthread (SESSIONPARAMETERS* CallingSessionParameters, bool* CallingSessio struct sockaddr_in LANDestination {}; LANDestination.sin_family = AF_INET; LANDestination.sin_port = htons(9); - LANDestination.sin_addr.s_addr = 0xFFFFFFFF; + + stringstream wolstr; + + if (WOLtype == WOL_SUBNETBROADCAST && subnet != "") + { + vector vIP = stringsplit(IP, "."); + vector vSubnet = stringsplit(subnet, "."); + stringstream broadcastaddress; + + if (vIP.size() == 4 && vSubnet.size() == 4) + { + for (int i = 0; i < 4; i++) + { + int a = atoi(vIP[i].c_str()); + int b = atoi(vSubnet[i].c_str()); + int c = 256 + (a | (~b)); + + broadcastaddress << c; + if (i < 3) + broadcastaddress << "."; + } + + stringstream ss; + + wolstr << " using broadcast address: " << broadcastaddress.str(); + + LANDestination.sin_addr.s_addr = inet_addr(broadcastaddress.str().c_str()); + + } + else + { + stringstream ss; + ss << device; + ss << ", ERROR! WOLthread malformed subnet/IP"; + Log(ss.str()); + return; + } + + + LANDestination.sin_addr.s_addr = 0xFFFFFFFF; + } + else if (WOLtype == WOL_IPSEND) + { + LANDestination.sin_addr.s_addr = inet_addr(IP.c_str()); + wolstr << " using IP address: " << IP; + + } + else + { + LANDestination.sin_addr.s_addr = 0xFFFFFFFF; + wolstr << " using network broadcast: 255.255.255.255"; + } + + /* test test test + sockaddr_in host_interface; + host_interface.sin_family = AF_INET; + host_interface.sin_port = htons(0); + host_interface.sin_addr.s_addr = inet_addr("192.168.1.100"); + */ time_t origtim = time(0); - WOLsocket= socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + WOLsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (WOLsocket == INVALID_SOCKET) { @@ -318,6 +380,20 @@ void WOLthread (SESSIONPARAMETERS* CallingSessionParameters, bool* CallingSessio } else { + /* test test test + int iRes = ::bind(WOLsocket, (SOCKADDR*)&host_interface, sizeof(host_interface)); + if ( iRes == SOCKET_ERROR) + { + closesocket(WOLsocket); + int dw = WSAGetLastError(); + stringstream ss; + ss << device; + ss << ", ERROR! WOLthread WS bind(): "; + ss << dw; + Log(ss.str()); + return; + } + */ const bool optval = TRUE; if (setsockopt(WOLsocket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval)) == SOCKET_ERROR) { @@ -336,6 +412,8 @@ void WOLthread (SESSIONPARAMETERS* CallingSessionParameters, bool* CallingSessio logmsg = device; logmsg += ", repeating WOL broadcast started to MAC: "; logmsg += MAC; + if (wolstr.str() != "") + logmsg += wolstr.str(); Log(logmsg); //remove filling from MAC @@ -473,7 +551,7 @@ void DisplayPowerOffThread(SESSIONPARAMETERS* CallingSessionParameters, bool* Ca host += ':' + std::to_string(ep.port()); if (time(0) - origtim > 10) // this thread should not run too long { - Log("DisplayPowerOffThread() forced exit."); + Log("DisplayPowerOffThread() - forced exit"); goto threadoffend; } // Log("DEBUG INFO: DisplayPowerOffThread() setting options..."); @@ -488,7 +566,7 @@ void DisplayPowerOffThread(SESSIONPARAMETERS* CallingSessionParameters, bool* Ca })); if (time(0) - origtim > 10) // this thread should not run too long { - Log("DisplayPowerOffThread() forced exit."); + Log("DisplayPowerOffThread() - forced exit"); goto threadoffend; } diff --git a/LGTV Companion Setup/Product.wxs b/LGTV Companion Setup/Product.wxs index 2351bdd..de45508 100644 --- a/LGTV Companion Setup/Product.wxs +++ b/LGTV Companion Setup/Product.wxs @@ -1,7 +1,7 @@ - + diff --git a/LGTV Companion UI/LGTV Companion UI.cpp b/LGTV Companion UI/LGTV Companion UI.cpp index 1887e0e..100f2da 100644 --- a/LGTV Companion UI/LGTV Companion UI.cpp +++ b/LGTV Companion UI/LGTV Companion UI.cpp @@ -109,9 +109,11 @@ CHANGELOG - Display warning in UI when TV is configured on a subnet different from PC - Add service dependencies - Set service shutdown priority - - Implemented option to automatically check for new application version (off by default) + - Implementation of option to automatically check for new application version (off by default) - Bug fixes and optimisations + v 1.4.0 - Additional device network options for powering on via WOL + LICENSE Copyright (c) 2021 Jörgen Persson @@ -261,6 +263,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE Instance, // Process messages for the main window. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + wstring str; switch (message) { case WM_INITDIALOG: @@ -297,6 +300,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) HWND hDeviceWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DEVICE), hWnd, (DLGPROC)DeviceWndProc); SetWindowText(hDeviceWnd, DEVICEWINDOW_TITLE_ADD); SetWindowText(GetDlgItem(hDeviceWnd, IDOK), L"&Add"); + CheckDlgButton(hDeviceWnd, IDC_RADIO1, BST_CHECKED); + EnableWindow(GetDlgItem(hDeviceWnd, IDC_SUBNET), false); + SetWindowText(GetDlgItem(hDeviceWnd, IDC_SUBNET), WOL_DEFAULTSUBNET); + + EnableWindow(GetDlgItem(hDeviceWnd, IDOK), false); + EnableWindow(hWnd, false); ShowWindow(hDeviceWnd, SW_SHOW); }break; @@ -311,7 +320,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) SetWindowText(GetDlgItem(hDeviceWnd, IDOK), L"&Save"); SetWindowText(GetDlgItem(hDeviceWnd, IDC_DEVICENAME), widen(Devices[sel].Name).c_str()); SetWindowText(GetDlgItem(hDeviceWnd, IDC_DEVICEIP), widen(Devices[sel].IP).c_str()); - wstring str; + + str = L""; for (const auto& item : Devices[sel].MAC) { str += widen(item); @@ -321,6 +331,33 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) SetWindowText(GetDlgItem(hDeviceWnd, IDC_DEVICEMACS), str.c_str()); EnableWindow(GetDlgItem(hDeviceWnd, IDOK), false); + switch (Devices[sel].WOLtype) + { + case 1: + { + CheckDlgButton(hDeviceWnd, IDC_RADIO1, BST_CHECKED); + EnableWindow(GetDlgItem(hDeviceWnd, IDC_SUBNET), false); + }break; + case 2: + { + CheckDlgButton(hDeviceWnd, IDC_RADIO2, BST_CHECKED); + EnableWindow(GetDlgItem(hDeviceWnd, IDC_SUBNET), false); + }break; + case 3: + { + CheckDlgButton(hDeviceWnd, IDC_RADIO3, BST_CHECKED); + EnableWindow(GetDlgItem(hDeviceWnd, IDC_SUBNET), true); + + }break; + default:break; + } + if(Devices[sel].Subnet != "") + SetWindowText(GetDlgItem(hDeviceWnd, IDC_SUBNET), widen(Devices[sel].Subnet).c_str()); + else + SetWindowText(GetDlgItem(hDeviceWnd, IDC_SUBNET), WOL_DEFAULTSUBNET); + + EnableWindow(GetDlgItem(hDeviceWnd, IDOK), false); + EnableWindow(hWnd, false); ShowWindow(hDeviceWnd, SW_SHOW); }break; @@ -416,6 +453,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) temp.Name = narrow(FriendlyName); temp.IP = narrow(IP); temp.MAC.push_back(MAC); + temp.Subnet = narrow(WOL_DEFAULTSUBNET); + temp.WOLtype = WOL_NETWORKBROADCAST; Devices.push_back(temp); ChangesWereMade = true; DevicesAdded++; @@ -523,7 +562,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } if (found < Devices.size()) { - int mb = MessageBox(hWnd, L"One or several devices have been configured to a different subnet. Please note that this might cause problems with waking up the TV. Please check the documentation and the configuration.\n\n Do you want to continue anyway?", L"Warning", MB_YESNO | MB_ICONEXCLAMATION); + int mb = MessageBox(hWnd, L"One or several devices have been configured to a subnet different from the PC. Please note that this might cause problems with waking up the TV. Please check the documentation and the configuration.\n\n Do you want to continue anyway?", L"Warning", MB_YESNO | MB_ICONEXCLAMATION); if (mb == IDNO) { EnableWindow(hWnd, true); @@ -828,6 +867,21 @@ LRESULT CALLBACK DeviceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP SendDlgItemMessage(hWnd, IDC_DEVICENAME, WM_SETFONT, (WPARAM)hEditfont, MAKELPARAM(TRUE, 0)); SendDlgItemMessage(hWnd, IDC_DEVICEIP, WM_SETFONT, (WPARAM)hEditfont, MAKELPARAM(TRUE, 0)); SendDlgItemMessage(hWnd, IDC_DEVICEMACS, WM_SETFONT, (WPARAM)hEditMediumfont, MAKELPARAM(TRUE, 0)); + SendDlgItemMessage(hWnd, IDC_SUBNET, WM_SETFONT, (WPARAM)hEditMediumfont, MAKELPARAM(TRUE, 0)); + }break; + case WM_NOTIFY: + { + switch (((NMHDR*)lParam)->code) + { + case NM_CLICK: + { + // explain the WOL options + if (wParam == IDC_SYSLINK4) + { + MessageBox(hWnd, L"Devices are powered on by means of sending wake-on-Lan magic packets. Depending on your network environment and operating system you may need to adjust these settings.\n\nTypically the application should send to either of:\n\na) the network broadcast address 255.255.255.255 or,\nb) the broadcast address according to device IP and subnet.\n\nThe current subnet mask of the subnet(s) of your PC can be found by using the \"IPCONFIG /all\" command in the command prompt.\n\nIf your devices have difficulties powering on try adjustig these settings.", L"Information", MB_OK | MB_ICONINFORMATION); + } + }break; + } }break; case WM_COMMAND: { @@ -837,7 +891,20 @@ LRESULT CALLBACK DeviceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP { switch (LOWORD(wParam)) { + case IDC_RADIO1: + case IDC_RADIO2: + { + EnableWindow(GetDlgItem(hWnd, IDC_SUBNET), false); + EnableWindow(GetDlgItem(hWnd, IDOK), true); + }break; + case IDC_RADIO3: + { + EnableWindow(GetDlgItem(hWnd, IDC_SUBNET), true); + EnableWindow(GetDlgItem(hWnd, IDOK), true); + + + }break; case IDOK: { HWND hParentWnd = GetParent(hWnd); @@ -890,6 +957,14 @@ LRESULT CALLBACK DeviceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP Devices[sel].MAC = maclines; Devices[sel].Name = narrow(GetWndText(GetDlgItem(hWnd, IDC_DEVICENAME))); Devices[sel].IP = narrow(GetWndText(GetDlgItem(hWnd, IDC_DEVICEIP))); + Devices[sel].Subnet = narrow(GetWndText(GetDlgItem(hWnd, IDC_SUBNET))); + + if (IsDlgButtonChecked(hWnd, IDC_RADIO3)) + Devices[sel].WOLtype = WOL_SUBNETBROADCAST; + else if (IsDlgButtonChecked(hWnd, IDC_RADIO2)) + Devices[sel].WOLtype = WOL_IPSEND; + else + Devices[sel].WOLtype = WOL_NETWORKBROADCAST; int ind = (int)SendMessage(GetDlgItem(hParentWnd, IDC_COMBO), (UINT)CB_DELETESTRING, (WPARAM)sel, (LPARAM)0); SendMessage(GetDlgItem(hParentWnd, IDC_COMBO), (UINT)CB_INSERTSTRING, (WPARAM)sel, (LPARAM)widen(Devices[sel].Name).c_str()); @@ -908,6 +983,16 @@ LRESULT CALLBACK DeviceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP sess.MAC = maclines; sess.Name = narrow(GetWndText(GetDlgItem(hWnd, IDC_DEVICENAME))); sess.IP = narrow(GetWndText(GetDlgItem(hWnd, IDC_DEVICEIP))); + + sess.Subnet = narrow(GetWndText(GetDlgItem(hWnd, IDC_SUBNET))); + + if (IsDlgButtonChecked(hWnd, IDC_RADIO3)) + sess.WOLtype = WOL_SUBNETBROADCAST; + else if (IsDlgButtonChecked(hWnd, IDC_RADIO2)) + sess.WOLtype = WOL_IPSEND; + else + sess.WOLtype = WOL_NETWORKBROADCAST; + Devices.push_back(sess); int index = (int)SendMessage(GetDlgItem(hParentWnd, IDC_COMBO), (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)widen(sess.Name).c_str()); @@ -966,6 +1051,8 @@ LRESULT CALLBACK DeviceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP case IDC_DEVICENAME: case IDC_DEVICEIP: case IDC_DEVICEMACS: + case IDC_SUBNET: + { EnableWindow(GetDlgItem(hWnd, IDOK), true); @@ -1463,6 +1550,12 @@ void ReadDeviceConfig() if (item.value()["SessionKey"].is_string()) params.SessionKey = item.value()["SessionKey"].get(); + if (item.value()["Subnet"].is_string()) + params.Subnet = item.value()["Subnet"].get(); + + if (item.value()["WOL"].is_number()) + params.WOLtype = item.value()["WOL"].get(); + j = item.value()["MAC"]; if (!j.empty() && j.size() > 0) { @@ -1605,12 +1698,18 @@ void WriteConfigFile(void) else prefs[dev.str()]["SessionKey"] = ""; + if (item.Subnet != "") + prefs[dev.str()]["Subnet"] = item.Subnet; + + prefs[dev.str()]["WOL"] = item.WOLtype; prefs[dev.str()]["Enabled"] = (bool)item.Enabled; for (auto& m : item.MAC) prefs[dev.str()]["MAC"].push_back(m); + + deviceid++; } diff --git a/LGTV Companion UI/LGTV Companion UI.h b/LGTV Companion UI/LGTV Companion UI.h index 991c931..a099227 100644 --- a/LGTV Companion UI/LGTV Companion UI.h +++ b/LGTV Companion UI/LGTV Companion UI.h @@ -54,7 +54,7 @@ #define APPNAME_SHORT L"LGTVcomp" #define APPNAME_FULL L"LGTV Companion" -#define APP_VERSION L"1.3.0" +#define APP_VERSION L"1.4.0" #define WINDOW_CLASS_UNIQUE L"YOLOx0x0x0181818" #define NOTIFY_NEW_COMMANDLINE 1 @@ -83,6 +83,12 @@ #define APP_CMDLINE_AUTOENABLE 3 #define APP_CMDLINE_AUTODISABLE 4 +#define WOL_NETWORKBROADCAST 1 +#define WOL_IPSEND 2 +#define WOL_SUBNETBROADCAST 3 + +#define WOL_DEFAULTSUBNET L"255.255.255.0" + #define DEVICEWINDOW_TITLE_ADD L"Add device" #define DEVICEWINDOW_TITLE_MANAGE L"Configure device" @@ -108,6 +114,8 @@ struct SESSIONPARAMETERS { // bool AwayAuto = true; bool Enabled = true; int PowerOnTimeout = 40; + int WOLtype = WOL_NETWORKBROADCAST; + std::string Subnet; }; diff --git a/LGTV Companion UI/LGTV Companion UI.rc b/LGTV Companion UI/LGTV Companion UI.rc index 098e852..7508522 100644 Binary files a/LGTV Companion UI/LGTV Companion UI.rc and b/LGTV Companion UI/LGTV Companion UI.rc differ diff --git a/LGTV Companion UI/resource.h b/LGTV Companion UI/resource.h index 8b3d7be..aad8610 100644 --- a/LGTV Companion UI/resource.h +++ b/LGTV Companion UI/resource.h @@ -22,15 +22,20 @@ #define IDC_DEVICEIP 1005 #define IDC_DEVICEMACS 1006 #define IDC_DEVICENAME 1007 +#define IDC_DEVICEIP2 1008 +#define IDC_SUBNET 1008 #define IDC_SPIN 1009 #define IDC_TIMEOUT 1010 #define IDC_LOGGING 1011 -#define IDC_LOGGING2 1012 #define IDC_AUTOUPDATE 1012 #define IDC_SYSLINK 1014 #define IDC_LIST 1018 #define IDC_SYSLINK3 1019 #define IDC_NEWVERSION 1021 +#define IDC_RADIO1 1022 +#define IDC_RADIO2 1023 +#define IDC_RADIO3 1024 +#define IDC_SYSLINK4 1025 #define ID_ADD_MANAGE 32771 #define ID_ADD_MANAGE32772 32772 #define ID_ADD_REMOVE 32773 @@ -57,7 +62,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 144 #define _APS_NEXT_COMMAND_VALUE 32788 -#define _APS_NEXT_CONTROL_VALUE 1022 +#define _APS_NEXT_CONTROL_VALUE 1026 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif