mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 00:22:18 -05:00
manually add device, add device setting per preset, add remote control image
This commit is contained in:
1 parent
cac5180c76
commit
26a91dc7e9
27 files changed
+2027
-862
No files matched your search
@@ -27,7 +27,7 @@
|
||||
<ProductName>ColorControl</ProductName>
|
||||
<PublisherName>Maassoft</PublisherName>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>2.2.0.0</ApplicationVersion>
|
||||
<ApplicationVersion>3.0.0.0</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
@@ -177,6 +177,18 @@
|
||||
<Compile Include="ADLWrapper.cs" />
|
||||
<Compile Include="AmdDisplayInfo.cs" />
|
||||
<Compile Include="AppContext.cs" />
|
||||
<Compile Include="LgDevice.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="RemoteControlPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="RemoteControlPanel.Designer.cs">
|
||||
<DependentUpon>RemoteControlPanel.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UserSessionInfo.cs" />
|
||||
<Compile Include="ServiceBase.cs" />
|
||||
<Compile Include="NvDisplayInfo.cs" />
|
||||
@@ -226,21 +238,22 @@
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Resources\RC_coords.txt" />
|
||||
<None Include="Resources\LG_remote.jpg" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="RemoteControlForm.resx">
|
||||
<DependentUpon>RemoteControlForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="RemoteControlPanel.resx">
|
||||
<DependentUpon>RemoteControlPanel.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="ColorControl_TemporaryKey.pfx" />
|
||||
<Content Include="LgPresets.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class LgDevice
|
||||
{
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public string Name { get; private set; }
|
||||
public string IpAddress { get; private set; }
|
||||
public string MacAddress { get; private set; }
|
||||
public bool IsCustom { get; private set; }
|
||||
[JsonIgnore]
|
||||
public bool IsDummy { get; private set; }
|
||||
|
||||
public bool PowerOnAfterStartup { get; set; }
|
||||
public bool PowerOnAfterResume { get; set; }
|
||||
public bool PowerOffOnShutdown { get; set; }
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private LgTvApi _lgTvApi;
|
||||
[JsonIgnore]
|
||||
private bool _justWokeUp;
|
||||
|
||||
[JsonIgnore]
|
||||
private Dictionary<string, Func<bool>> _invokableActions = new Dictionary<string, Func<bool>>();
|
||||
|
||||
[JsonConstructor]
|
||||
public LgDevice(string name, string ipAddress, string macAddress, bool isCustom = true, bool isDummy = false)
|
||||
{
|
||||
Name = name;
|
||||
IpAddress = ipAddress;
|
||||
MacAddress = macAddress;
|
||||
IsCustom = isCustom;
|
||||
IsDummy = isDummy;
|
||||
|
||||
_invokableActions.Add("WOL", new Func<bool>(Wake));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: ")) + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $" ({IpAddress})" : string.Empty);
|
||||
}
|
||||
|
||||
public async Task<bool> Connect(int retries = 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisposeConnection();
|
||||
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries);
|
||||
|
||||
//Test();
|
||||
return _lgTvApi != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error($"Error while connecting to {IpAddress}: {logMessage}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
return !_lgTvApi?.ConnectionClosed ?? false;
|
||||
}
|
||||
|
||||
internal void DisposeConnection()
|
||||
{
|
||||
if (_lgTvApi != null)
|
||||
{
|
||||
_lgTvApi.Dispose();
|
||||
_lgTvApi = null;
|
||||
}
|
||||
}
|
||||
public async Task<bool> ExecutePreset(LgPreset preset, bool reconnect = false)
|
||||
{
|
||||
var hasApp = !string.IsNullOrEmpty(preset.appId);
|
||||
|
||||
var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasWOL)
|
||||
{
|
||||
var connected = await WakeAndConnect(0);
|
||||
if (!connected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var tries = 0; tries <= 1; tries++)
|
||||
{
|
||||
if (!await Connected(reconnect || tries == 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasApp)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _lgTvApi.LaunchApp(preset.appId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while launching app: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_justWokeUp)
|
||||
{
|
||||
_justWokeUp = false;
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.steps.Any())
|
||||
{
|
||||
if (hasApp)
|
||||
{
|
||||
await Task.Delay(1500);
|
||||
}
|
||||
try
|
||||
{
|
||||
await ExecuteSteps(_lgTvApi, preset);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while executing steps: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ExecuteSteps(LgTvApi api, LgPreset preset)
|
||||
{
|
||||
var mouse = await api.GetMouse();
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
var key = keySpec[0].ToUpper();
|
||||
if (_invokableActions.ContainsKey(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
SendKey(mouse, key, int.Parse(keySpec[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendKey(mouse, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180)
|
||||
{
|
||||
if (key.Length == 1 && int.TryParse(key, out _))
|
||||
{
|
||||
key = "_" + key;
|
||||
}
|
||||
var button = (ButtonType)Enum.Parse(typeof(ButtonType), key);
|
||||
mouse.SendButton(button);
|
||||
Thread.Sleep(delay);
|
||||
}
|
||||
|
||||
public async Task<LgWebOsMouseService> GetMouseAsync()
|
||||
{
|
||||
return await _lgTvApi.GetMouse();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LgApp>> GetApps(bool force = false)
|
||||
{
|
||||
if (!await Connected(force))
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no connection could be made");
|
||||
return new List<LgApp>();
|
||||
}
|
||||
|
||||
return await _lgTvApi.GetApps(force);
|
||||
}
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
{
|
||||
if (!await Connected(true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await _lgTvApi.TurnOff();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool> TestConnection(int retries = 1)
|
||||
{
|
||||
if (!await Connected(true, retries))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _lgTvApi.IsMuted();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("TestConnection: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> WakeAndConnect(int wakeDelay = 5000, int connectDelay = 500)
|
||||
{
|
||||
try
|
||||
{
|
||||
//if (wakeDelay == 0)
|
||||
//{
|
||||
// if (await ConnectToSelectedDevice())
|
||||
// {
|
||||
// Logger.Debug("Already connected, no wake needed?");
|
||||
// return;
|
||||
// };
|
||||
//}
|
||||
|
||||
await Task.Delay(wakeDelay);
|
||||
var result = Wake();
|
||||
if (!result)
|
||||
{
|
||||
Logger.Debug("WOL failed");
|
||||
return false;
|
||||
}
|
||||
Logger.Debug("WOL succeeded");
|
||||
await Task.Delay(connectDelay);
|
||||
result = await Connect();
|
||||
Logger.Debug("Connect succeeded: " + result);
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Wake()
|
||||
{
|
||||
var result = false;
|
||||
|
||||
if (MacAddress != null)
|
||||
{
|
||||
result = WOL.WakeFunction(MacAddress);
|
||||
_justWokeUp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("Cannot wake device: the device has no MAC-address");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<bool> WakeAndConnectWithRetries(int retries = 5)
|
||||
{
|
||||
var wakeDelay = 0;
|
||||
var maxRetries = retries <= 1 ? 5 : retries;
|
||||
|
||||
var result = false;
|
||||
for (var retry = 0; retry < maxRetries && !result; retry++)
|
||||
{
|
||||
Logger.Debug($"WakeAndConnectWithRetries: attempt {retry + 1} of {maxRetries}...");
|
||||
|
||||
var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
result = await WakeAndConnect(retry == 0 ? wakeDelay : 0);
|
||||
ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms;
|
||||
if (!result)
|
||||
{
|
||||
var delay = 2000 - ms;
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay((int)delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<bool>> GetInvokableActions()
|
||||
{
|
||||
return _invokableActions;
|
||||
}
|
||||
|
||||
public async Task PowerOn()
|
||||
{
|
||||
var mouse = await _lgTvApi.GetMouse();
|
||||
mouse.SendButton(ButtonType.POWER);
|
||||
}
|
||||
|
||||
public void Test()
|
||||
{
|
||||
try
|
||||
{
|
||||
//_lgTvApi.GetServiceList();
|
||||
//_lgTvApi?.Test();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("TEST: " + ex.ToLogString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> Connected(bool reconnect = false, int retries = 3)
|
||||
{
|
||||
if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), IpAddress))
|
||||
{
|
||||
if (!await Connect(retries))
|
||||
{
|
||||
Logger.Debug("Cannot apply LG-preset: no connection could be made");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void ConvertToCustom()
|
||||
{
|
||||
IsCustom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -6,10 +7,18 @@ namespace ColorControl
|
||||
{
|
||||
class LgPreset : PresetBase
|
||||
{
|
||||
public static List<LgApp> LgApps { get; set; }
|
||||
public static List<LgDevice> LgDevices { get; set; }
|
||||
|
||||
|
||||
public string appId { get; set; }
|
||||
public List<string> steps { get; set; }
|
||||
public string DeviceMacAddress { get; set; }
|
||||
|
||||
public static List<LgApp> LgApps { get; set; }
|
||||
[JsonIgnore]
|
||||
public int X { get; set; }
|
||||
[JsonIgnore]
|
||||
public int Y { get; set; }
|
||||
|
||||
public LgPreset()
|
||||
{
|
||||
@@ -37,7 +46,7 @@ namespace ColorControl
|
||||
|
||||
public static List<string> GetColumnNames()
|
||||
{
|
||||
return new List<string>() { "Name", "App|200", "Steps|400", "Shortcut" };
|
||||
return new List<string>() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
@@ -45,6 +54,29 @@ namespace ColorControl
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var deviceString = "Global";
|
||||
if (!string.IsNullOrEmpty(DeviceMacAddress))
|
||||
{
|
||||
if (LgDevices != null)
|
||||
{
|
||||
var device = LgDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress));
|
||||
if (device != null)
|
||||
{
|
||||
deviceString = device.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceString = "Unknown: device not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceString = DeviceMacAddress;
|
||||
}
|
||||
}
|
||||
values.Add(deviceString);
|
||||
|
||||
var app = appId;
|
||||
if (LgApps != null)
|
||||
{
|
||||
|
||||
+175
-355
@@ -15,16 +15,21 @@ namespace ColorControl
|
||||
{
|
||||
class LgService : ServiceBase<LgPreset>
|
||||
{
|
||||
internal enum PowerOnOffState
|
||||
{
|
||||
StartUp = 1,
|
||||
Resume = 2,
|
||||
ScreenSaver = 3,
|
||||
ShutDown = 4,
|
||||
StandBy = 5
|
||||
}
|
||||
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static string LgListAppsJson = "listApps.json";
|
||||
|
||||
//private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
//public string FriendlyScreenName { get; private set; }
|
||||
|
||||
public List<PnpDev> Devices { get; private set; }
|
||||
public PnpDev SelectedDevice
|
||||
public List<LgDevice> Devices { get; private set; }
|
||||
public LgDevice SelectedDevice
|
||||
{
|
||||
get { return _selectedDevice; }
|
||||
set
|
||||
@@ -36,17 +41,13 @@ namespace ColorControl
|
||||
|
||||
public LgServiceConfig Config { get; private set; }
|
||||
|
||||
private LgTvApi _lgTvApi;
|
||||
private string _configFilename;
|
||||
private bool _allowPowerOn;
|
||||
private bool _justWokeUp;
|
||||
private PnpDev _selectedDevice;
|
||||
private LgDevice _selectedDevice;
|
||||
private string _rcButtonsFilename;
|
||||
private List<LgPreset> _remoteControlButtons;
|
||||
private List<LgApp> _lgApps = new List<LgApp>();
|
||||
|
||||
private Dictionary<string, Func<bool>> _invokableActions = new Dictionary<string, Func<bool>>();
|
||||
|
||||
private bool _poweredOffByScreenSaver;
|
||||
private int _poweredOffByScreenSaverProcessId;
|
||||
private Task _monitorTask;
|
||||
@@ -56,22 +57,11 @@ namespace ColorControl
|
||||
{
|
||||
_allowPowerOn = allowPowerOn;
|
||||
|
||||
_invokableActions.Add("WOL", new Func<bool>(WakeSelectedDevice));
|
||||
|
||||
LgPreset.LgApps = _lgApps;
|
||||
|
||||
LoadConfig();
|
||||
LoadPresets();
|
||||
LoadRemoteControlButtons();
|
||||
|
||||
//foreach (var screen in Screen.AllScreens)
|
||||
//{
|
||||
// var name = screen.DeviceFriendlyName();
|
||||
// if (name.Contains("LG"))
|
||||
// {
|
||||
// FriendlyScreenName = name;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
~LgService()
|
||||
@@ -165,6 +155,8 @@ namespace ColorControl
|
||||
{
|
||||
Config = new LgServiceConfig();
|
||||
}
|
||||
|
||||
LgPreset.LgDevices = Config.Devices;
|
||||
}
|
||||
|
||||
private void LoadRemoteControlButtons()
|
||||
@@ -192,11 +184,6 @@ namespace ColorControl
|
||||
return _remoteControlButtons;
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<bool>> GetInvokableActions()
|
||||
{
|
||||
return _invokableActions;
|
||||
}
|
||||
|
||||
private List<LgPreset> GenerateDefaultRemoteControlButtons()
|
||||
{
|
||||
var list = new List<LgPreset>();
|
||||
@@ -207,11 +194,18 @@ namespace ColorControl
|
||||
list.Add(GeneratePreset(button.ToString()));
|
||||
}
|
||||
|
||||
list.Add(GeneratePreset("Power", step: "POWER"));
|
||||
list.Add(GeneratePreset("Home", appId: "com.webos.app.home", key: Keys.H));
|
||||
list.Add(GeneratePreset("Settings", appId: "com.palm.app.settings", key: Keys.S));
|
||||
list.Add(GeneratePreset("TV Guide", appId: "com.webos.app.livemenu"));
|
||||
list.Add(GeneratePreset("List", step: "LIST"));
|
||||
list.Add(GeneratePreset("SAP", step: "SAP"));
|
||||
|
||||
list.Add(GeneratePreset("Vol +", step: "VOLUMEUP", key: Keys.VolumeUp));
|
||||
list.Add(GeneratePreset("Vol -", step: "VOLUMEDOWN", key: Keys.VolumeDown));
|
||||
list.Add(GeneratePreset("Mute", step: "MUTE", key: Keys.VolumeMute));
|
||||
list.Add(GeneratePreset("Channel +", step: "CHANNELUP", key: Keys.MediaNextTrack));
|
||||
list.Add(GeneratePreset("Channel -", step: "CHANNELDOWN", key: Keys.MediaPreviousTrack));
|
||||
|
||||
list.Add(GeneratePreset("Up", step: "UP", key: Keys.Up));
|
||||
list.Add(GeneratePreset("Down", step: "DOWN", key: Keys.Down));
|
||||
@@ -222,6 +216,19 @@ namespace ColorControl
|
||||
list.Add(GeneratePreset("Back", step: "BACK", key: Keys.Back));
|
||||
list.Add(GeneratePreset("Exit", step: "EXIT", key: Keys.Escape));
|
||||
|
||||
list.Add(GeneratePreset("Netflix", appId: "netflix"));
|
||||
list.Add(GeneratePreset("Inputs", appId: "com.webos.app.homeconnect"));
|
||||
list.Add(GeneratePreset("Amazon Prime", appId: "amazon"));
|
||||
|
||||
list.Add(GeneratePreset("Red", step: "RED"));
|
||||
list.Add(GeneratePreset("Green", step: "GREEN"));
|
||||
list.Add(GeneratePreset("Yellow", step: "YELLOW"));
|
||||
list.Add(GeneratePreset("Blue", step: "BLUE"));
|
||||
|
||||
list.Add(GeneratePreset("Rakuten TV", appId: "ui30"));
|
||||
list.Add(GeneratePreset("Play", step: "PLAY"));
|
||||
list.Add(GeneratePreset("Pause", step: "PAUSE"));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -258,6 +265,8 @@ namespace ColorControl
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
Config.PowerOnAfterStartup = Devices.Any(d => d.PowerOnAfterStartup);
|
||||
|
||||
var json = JsonConvert.SerializeObject(Config);
|
||||
File.WriteAllText(_configFilename, json);
|
||||
}
|
||||
@@ -270,14 +279,36 @@ namespace ColorControl
|
||||
|
||||
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
|
||||
{
|
||||
var devices = await Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
SetDevices(devices);
|
||||
Devices = Config.Devices;
|
||||
var customIpAddresses = Devices.Where(d => d.IsCustom).Select(d => d.IpAddress);
|
||||
|
||||
var pnpDevices = await Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
|
||||
var autoDevices = pnpDevices.Where(p => !customIpAddresses.Contains(p.IpAddress)).Select(d => new LgDevice(d.Name, d.IpAddress, d.MacAddress, false)).ToList();
|
||||
var autoIpAddresses = pnpDevices.Select(d => d.IpAddress);
|
||||
|
||||
Devices.RemoveAll(d => !d.IsCustom && !autoIpAddresses.Contains(d.IpAddress));
|
||||
|
||||
var newAutoDevices = autoDevices.Where(ad => !Devices.Any(d => d.IpAddress.Equals(ad.IpAddress)));
|
||||
Devices.AddRange(newAutoDevices);
|
||||
|
||||
if (Devices.Any())
|
||||
{
|
||||
var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0];
|
||||
|
||||
SelectedDevice = preferredDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedDevice = null;
|
||||
}
|
||||
|
||||
if (afterStartUp && SelectedDevice == null && Config.PowerOnAfterStartup && !string.IsNullOrEmpty(Config.PreferredMacAddress))
|
||||
{
|
||||
Logger.Debug("No device has been found, trying to wake it first...");
|
||||
|
||||
WakeSelectedDevice(Config.PreferredMacAddress);
|
||||
var tempDevice = new LgDevice("Test", string.Empty, Config.PreferredMacAddress);
|
||||
tempDevice.Wake();
|
||||
|
||||
await Task.Delay(4000);
|
||||
await RefreshDevices();
|
||||
@@ -287,70 +318,17 @@ namespace ColorControl
|
||||
|
||||
if (connect && SelectedDevice != null)
|
||||
{
|
||||
if (Config.PowerOnAfterStartup && _allowPowerOn)
|
||||
if (_allowPowerOn)
|
||||
{
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
WakeAfterStartupOrResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
var _ = ConnectToSelectedDevice();
|
||||
var _ = SelectedDevice.Connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDevices(List<PnpDev> devices)
|
||||
{
|
||||
Devices = devices;
|
||||
if (Devices?.Count > 0)
|
||||
{
|
||||
var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0];
|
||||
|
||||
SelectedDevice = preferredDevice;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectToSelectedDevice(int retries = 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisposeConnection();
|
||||
_lgTvApi = await LgTvApi.CreateLgTvApi(SelectedDevice.IpAddress, retries);
|
||||
|
||||
//_lgTvApi.GetServiceList();
|
||||
return _lgTvApi != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error($"Error while connecting to {SelectedDevice.IpAddress}: {logMessage}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> Connected(bool reconnect = false)
|
||||
{
|
||||
if (SelectedDevice == null)
|
||||
{
|
||||
Logger.Debug("Cannot apply LG-preset: no device has been selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress))
|
||||
{
|
||||
if (!await ConnectToSelectedDevice())
|
||||
{
|
||||
Logger.Debug("Cannot apply LG-preset: no connection could be made");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsConnected()
|
||||
{
|
||||
return !_lgTvApi?.ConnectionClosed ?? false;
|
||||
}
|
||||
|
||||
public async Task<bool> ApplyPreset(string presetName)
|
||||
{
|
||||
var preset = _presets.FirstOrDefault(p => p.name.Equals(presetName, StringComparison.OrdinalIgnoreCase));
|
||||
@@ -366,123 +344,28 @@ namespace ColorControl
|
||||
|
||||
public async Task<bool> ApplyPreset(LgPreset preset, bool reconnect = false)
|
||||
{
|
||||
var hasApp = !string.IsNullOrEmpty(preset.appId);
|
||||
var device = GetPresetDevice(preset);
|
||||
|
||||
var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasWOL)
|
||||
if (device == null)
|
||||
{
|
||||
var connected = await WakeAndConnectToSelectedDevice(0);
|
||||
if (!connected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Logger.Debug("Cannot apply preset: no device has been selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var tries = 0; tries <= 1; tries++)
|
||||
{
|
||||
if (!await Connected(reconnect || tries == 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasApp)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _lgTvApi.LaunchApp(preset.appId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while launching app: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_justWokeUp)
|
||||
{
|
||||
_justWokeUp = false;
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.steps.Any())
|
||||
{
|
||||
if (hasApp)
|
||||
{
|
||||
await Task.Delay(1500);
|
||||
}
|
||||
try
|
||||
{
|
||||
await ExecuteSteps(_lgTvApi, preset);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while executing steps: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return await device.ExecutePreset(preset, reconnect);
|
||||
}
|
||||
|
||||
public async Task PowerOn()
|
||||
public LgDevice GetPresetDevice(LgPreset preset)
|
||||
{
|
||||
var mouse = await _lgTvApi.GetMouse();
|
||||
mouse.SendButton(ButtonType.POWER);
|
||||
}
|
||||
|
||||
private async Task ExecuteSteps(LgTvApi api, LgPreset preset)
|
||||
{
|
||||
var mouse = await api.GetMouse();
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
var key = keySpec[0].ToUpper();
|
||||
if (_invokableActions.ContainsKey(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
SendKey(mouse, key, int.Parse(keySpec[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendKey(mouse, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180)
|
||||
{
|
||||
if (key.Length == 1 && int.TryParse(key, out _))
|
||||
{
|
||||
key = "_" + key;
|
||||
}
|
||||
var button = (ButtonType)Enum.Parse(typeof(ButtonType), key);
|
||||
mouse.SendButton(button);
|
||||
Thread.Sleep(delay);
|
||||
var device = string.IsNullOrEmpty(preset.DeviceMacAddress)
|
||||
? SelectedDevice
|
||||
: Devices.FirstOrDefault(d => d.MacAddress?.Equals(preset.DeviceMacAddress, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
return device;
|
||||
}
|
||||
|
||||
public async Task<LgWebOsMouseService> GetMouseAsync()
|
||||
{
|
||||
return await _lgTvApi.GetMouse();
|
||||
return await SelectedDevice?.GetMouseAsync();
|
||||
}
|
||||
|
||||
public async Task RefreshApps(bool force = false)
|
||||
@@ -490,15 +373,16 @@ namespace ColorControl
|
||||
if (SelectedDevice == null)
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no device has been selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await Connected(force))
|
||||
var apps = await SelectedDevice.GetApps();
|
||||
|
||||
if (apps.Any())
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no connection could be made");
|
||||
_lgApps.Clear();
|
||||
_lgApps.AddRange(apps);
|
||||
}
|
||||
|
||||
_lgApps.Clear();
|
||||
_lgApps.AddRange(await _lgTvApi.GetApps(force));
|
||||
}
|
||||
|
||||
public List<LgApp> GetApps()
|
||||
@@ -506,138 +390,47 @@ namespace ColorControl
|
||||
return _lgApps;
|
||||
}
|
||||
|
||||
internal void DisposeConnection()
|
||||
{
|
||||
if (_lgTvApi != null)
|
||||
{
|
||||
_lgTvApi.Dispose();
|
||||
_lgTvApi = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
{
|
||||
if (!await Connected(true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await _lgTvApi.TurnOff();
|
||||
return true;
|
||||
return await SelectedDevice?.PowerOff();
|
||||
}
|
||||
|
||||
internal async Task<bool> TestConnection()
|
||||
internal Task PowerOffOnShutdownOrResume(PowerOnOffState state = PowerOnOffState.ShutDown)
|
||||
{
|
||||
if (!await Connected(true))
|
||||
var devices = Devices.Where(d => state == PowerOnOffState.ShutDown && d.PowerOffOnShutdown ||
|
||||
state == PowerOnOffState.StandBy && d.PowerOffOnStandby);
|
||||
|
||||
var tasks = new List<Task>();
|
||||
foreach (var device in devices)
|
||||
{
|
||||
return false;
|
||||
var task = device.PowerOff();
|
||||
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _lgTvApi.IsMuted();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("TestConnection: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
return Task.WhenAll(tasks.ToArray());
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice()
|
||||
{
|
||||
return WakeSelectedDevice(SelectedDevice?.MacAddress ?? Config.PreferredMacAddress);
|
||||
return SelectedDevice?.Wake() ?? false;
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice(string macAddress)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
macAddress = string.IsNullOrEmpty(macAddress) ? SelectedDevice?.MacAddress : macAddress;
|
||||
if (macAddress != null)
|
||||
{
|
||||
result = WOL.WakeFunction(macAddress, Config.UseOldNpcapWol);
|
||||
_justWokeUp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("Cannot wake device: no device has been selected or there is no preferred MAC-address stored in the configuration");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void WakeAfterResume()
|
||||
{
|
||||
if (Config.PowerOnAfterResume)
|
||||
{
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> WakeAndConnectToSelectedDeviceWithRetries(bool checkUserSession = true)
|
||||
internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
|
||||
{
|
||||
if (checkUserSession && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: not waking because session info indicates no local session");
|
||||
return false;
|
||||
Logger.Debug($"WakeAfterStartupOrResume: not waking because session info indicates no local session");
|
||||
return;
|
||||
}
|
||||
|
||||
var wakeDelay = 0;
|
||||
var maxRetries = Config.PowerOnRetries <= 1 ? 5 : Config.PowerOnRetries;
|
||||
|
||||
var result = false;
|
||||
for (var retry = 0; retry < maxRetries && !result; retry++)
|
||||
var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup ||
|
||||
state == PowerOnOffState.Resume && d.PowerOnAfterResume ||
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver);
|
||||
foreach (var device in wakeDevices)
|
||||
{
|
||||
Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: attempt {retry + 1} of {maxRetries}...");
|
||||
|
||||
var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
result = await WakeAndConnectToSelectedDevice(retry == 0 ? wakeDelay : 0);
|
||||
ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms;
|
||||
if (!result)
|
||||
{
|
||||
var delay = 2000 - ms;
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay((int)delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> WakeAndConnectToSelectedDevice(int wakeDelay = 5000, int connectDelay = 500)
|
||||
{
|
||||
try
|
||||
{
|
||||
//if (wakeDelay == 0)
|
||||
//{
|
||||
// if (await ConnectToSelectedDevice())
|
||||
// {
|
||||
// Logger.Debug("Already connected, no wake needed?");
|
||||
// return;
|
||||
// };
|
||||
//}
|
||||
|
||||
await Task.Delay(wakeDelay);
|
||||
var result = WakeSelectedDevice();
|
||||
if (!result)
|
||||
{
|
||||
Logger.Debug("WOL failed");
|
||||
return false;
|
||||
}
|
||||
Logger.Debug("WOL succeeded");
|
||||
await Task.Delay(connectDelay);
|
||||
result = await ConnectToSelectedDevice();
|
||||
Logger.Debug("Connect succeeded: " + result);
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString());
|
||||
return false;
|
||||
device.DisposeConnection();
|
||||
var _ = device.WakeAndConnectWithRetries(Config.PowerOnRetries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,7 +456,7 @@ namespace ColorControl
|
||||
Logger.Debug("User switched to local and screen was powered off due to screensaver: waking up");
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
WakeAfterStartupOrResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -680,18 +473,17 @@ namespace ColorControl
|
||||
|
||||
if (powerOn)
|
||||
{
|
||||
DisposeConnection();
|
||||
WakeAfterResume();
|
||||
WakeAfterStartupOrResume(PowerOnOffState.Resume);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config.PowerOffOnStandby)
|
||||
if (Devices.Any(d => d.PowerOffOnStandby))
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
|
||||
try
|
||||
{
|
||||
Logger.Debug("Powering off tv...");
|
||||
var task = PowerOff();
|
||||
var task = PowerOffOnShutdownOrResume(PowerOnOffState.StandBy);
|
||||
Utils.WaitForTask(task);
|
||||
Logger.Debug("Done powering off tv");
|
||||
}
|
||||
@@ -717,11 +509,12 @@ namespace ColorControl
|
||||
|
||||
private void MonitorProcesses()
|
||||
{
|
||||
if (Config.PowerSwitchOnScreenSaver && _monitorTask == null)
|
||||
var enableMonitoring = Devices.Any(d => d.PowerSwitchOnScreenSaver);
|
||||
if (enableMonitoring && _monitorTask == null)
|
||||
{
|
||||
_monitorTask = CheckProcesses();
|
||||
}
|
||||
else if (!Config.PowerSwitchOnScreenSaver && _monitorTask != null)
|
||||
else if (!enableMonitoring && _monitorTask != null)
|
||||
{
|
||||
_monitorTask = null;
|
||||
}
|
||||
@@ -729,12 +522,12 @@ namespace ColorControl
|
||||
|
||||
public async Task CheckProcesses()
|
||||
{
|
||||
var wasConnected = IsConnected();
|
||||
var wasConnected = Devices.Any(d => d.PowerSwitchOnScreenSaver && d.IsConnected());
|
||||
_monitorTaskCounter++;
|
||||
var validCounter = _monitorTaskCounter;
|
||||
var lastProcessId = 0;
|
||||
|
||||
while (Config.PowerSwitchOnScreenSaver && validCounter == _monitorTaskCounter)
|
||||
while (Devices.Any(d => d.PowerSwitchOnScreenSaver) && validCounter == _monitorTaskCounter)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
@@ -758,16 +551,18 @@ namespace ColorControl
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
lastProcessId = 0;
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsConnected())
|
||||
var devices = Devices.Where(d => d.PowerSwitchOnScreenSaver && d.IsConnected()).ToList();
|
||||
|
||||
if (!devices.Any())
|
||||
{
|
||||
if (wasConnected)
|
||||
{
|
||||
Logger.Debug("TV was connected, but not any longer");
|
||||
Logger.Debug("Screensaver check: TV(s) where connected, but not any longer");
|
||||
wasConnected = false;
|
||||
}
|
||||
continue;
|
||||
@@ -775,49 +570,50 @@ namespace ColorControl
|
||||
|
||||
if (!wasConnected)
|
||||
{
|
||||
Logger.Debug("TV was not connected, but connection has now been established");
|
||||
Logger.Debug("Screensaver check: TV(s) where not connected, but connection has now been established");
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
foreach (var process in processes)
|
||||
{
|
||||
var name = process.ProcessName.ToLowerInvariant();
|
||||
if (name.EndsWith(".scr"))
|
||||
{
|
||||
var parent = process.Parent();
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
|
||||
try
|
||||
{
|
||||
Logger.Debug("Test connection...");
|
||||
var test = await TestConnection();
|
||||
Logger.Debug("Test connection result: " + test);
|
||||
var process = processes.FirstOrDefault(p => p.ProcessName.ToLowerInvariant().EndsWith(".scr"));
|
||||
|
||||
Logger.Debug("Powering off tv because of screensaver");
|
||||
_poweredOffByScreenSaver = await PowerOff();
|
||||
_poweredOffByScreenSaverProcessId = process.Id;
|
||||
lastProcessId = 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("CheckProcesses: can't power off: " + e.ToLogString());
|
||||
}
|
||||
if (!IsConnected())
|
||||
{
|
||||
_poweredOffByScreenSaver = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (process == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parent = process.Parent();
|
||||
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
|
||||
try
|
||||
{
|
||||
foreach (var device in devices)
|
||||
{
|
||||
if (process.Id != lastProcessId)
|
||||
Logger.Debug($"Screensaver check: test connection with {device.Name}...");
|
||||
var test = await device.TestConnection();
|
||||
Logger.Debug("Screensaver check: test connection result: " + test);
|
||||
if (!test)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
|
||||
lastProcessId = process.Id;
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Debug($"Screensaver check: powering off tv {device.Name} because of screensaver");
|
||||
_poweredOffByScreenSaver = await device.PowerOff();
|
||||
}
|
||||
|
||||
_poweredOffByScreenSaverProcessId = process.Id;
|
||||
lastProcessId = 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
|
||||
}
|
||||
}
|
||||
else if (process.Id != lastProcessId)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
|
||||
lastProcessId = process.Id;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -826,5 +622,29 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddCustomDevice(LgDevice device)
|
||||
{
|
||||
Devices.Add(device);
|
||||
}
|
||||
|
||||
internal void RemoveCustomDevice(LgDevice device)
|
||||
{
|
||||
Devices.Remove(device);
|
||||
|
||||
if (!string.IsNullOrEmpty(device.MacAddress))
|
||||
{
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress)).ToList();
|
||||
presets.ForEach(preset =>
|
||||
{
|
||||
preset.DeviceMacAddress = null;
|
||||
});
|
||||
}
|
||||
|
||||
if (SelectedDevice == device)
|
||||
{
|
||||
SelectedDevice = Devices.Any() ? Devices.First() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
namespace ColorControl
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class LgServiceConfig
|
||||
{
|
||||
public bool PowerOnAfterStartup { get; set; }
|
||||
|
||||
public bool PowerOnAfterResume { get; set; }
|
||||
|
||||
public bool PowerOffOnShutdown { get; set; }
|
||||
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
|
||||
public int PowerOnDelayAfterResume { get; set; }
|
||||
|
||||
public int PowerOnRetries { get; set; }
|
||||
@@ -22,11 +16,16 @@
|
||||
|
||||
public bool UseOldNpcapWol { get; set; }
|
||||
|
||||
public List<LgDevice> Devices { get; set; }
|
||||
|
||||
public bool ShowRemoteControl { get; set; }
|
||||
|
||||
public LgServiceConfig()
|
||||
{
|
||||
PowerOnDelayAfterResume = 5000;
|
||||
DeviceSearchKey = "[LG]";
|
||||
PowerOnRetries = 10;
|
||||
Devices = new List<LgDevice>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+466
-371
File diff suppressed because it is too large.
Load diff
+204
-44
@@ -37,12 +37,12 @@ namespace ColorControl
|
||||
|
||||
private NotifyIcon _trayIcon;
|
||||
private bool _initialized = false;
|
||||
private bool _disableEvents = false;
|
||||
private Config _config;
|
||||
private bool _setVisibleCalled = false;
|
||||
private RestartDetector _restartDetector;
|
||||
|
||||
private LgService _lgService;
|
||||
private RemoteControlForm _remoteControlForm;
|
||||
|
||||
private MenuItem _nvTrayMenu;
|
||||
private MenuItem _amdTrayMenu;
|
||||
@@ -149,11 +149,6 @@ namespace ColorControl
|
||||
|
||||
FillLgPresets();
|
||||
|
||||
clbLgPower.SetItemChecked(0, _lgService.Config.PowerOnAfterStartup);
|
||||
clbLgPower.SetItemChecked(1, _lgService.Config.PowerOnAfterResume);
|
||||
clbLgPower.SetItemChecked(2, _lgService.Config.PowerOffOnShutdown);
|
||||
clbLgPower.SetItemChecked(3, _lgService.Config.PowerOffOnStandby);
|
||||
clbLgPower.SetItemChecked(4, _lgService.Config.PowerSwitchOnScreenSaver);
|
||||
edtLgMaxPowerOnRetries.Value = _lgService.Config.PowerOnRetries;
|
||||
edtLgDeviceFilter.Text = _lgService.Config.DeviceSearchKey;
|
||||
chkLgOldWolMechanism.Checked = _lgService.Config.UseOldNpcapWol;
|
||||
@@ -171,15 +166,6 @@ namespace ColorControl
|
||||
item.Click += miLgAddButton_Click;
|
||||
}
|
||||
|
||||
var actions = _lgService.GetInvokableActions();
|
||||
foreach (var keyValuePair in actions)
|
||||
{
|
||||
var text = keyValuePair.Key;
|
||||
|
||||
var item = mnuLgActions.DropDownItems.Add(text);
|
||||
item.Click += miLgAddAction_Click;
|
||||
}
|
||||
|
||||
_lgService.InstallEventHandlers();
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -403,7 +389,7 @@ namespace ColorControl
|
||||
|
||||
GlobalSave();
|
||||
|
||||
if (SystemShutdown && _lgService.Config.PowerOffOnShutdown)
|
||||
if (SystemShutdown && _lgService.Devices.Any(d => d.PowerOffOnShutdown))
|
||||
{
|
||||
Logger.Debug($"MainForm_FormClosing: SystemShutdown");
|
||||
|
||||
@@ -418,7 +404,7 @@ namespace ColorControl
|
||||
else
|
||||
{
|
||||
Logger.Debug("Powering off tv...");
|
||||
var task = _lgService.PowerOff();
|
||||
var task = _lgService.PowerOffOnShutdownOrResume();
|
||||
Utils.WaitForTask(task);
|
||||
Logger.Debug("Done powering off tv");
|
||||
//ExecPowerOffPreset(true);
|
||||
@@ -1049,6 +1035,7 @@ namespace ColorControl
|
||||
btnApplyLg.Enabled = enabled;
|
||||
btnCloneLg.Enabled = enabled;
|
||||
edtNameLg.Enabled = enabled;
|
||||
cbxLgPresetDevice.Enabled = enabled;
|
||||
edtShortcutLg.Enabled = enabled;
|
||||
btnSetShortcutLg.Enabled = enabled;
|
||||
edtStepsLg.Enabled = enabled;
|
||||
@@ -1065,6 +1052,20 @@ namespace ColorControl
|
||||
cbxLgApps.SelectedIndex = lgApps == null ? -1 : lgApps.FindIndex(x => x.appId.Equals(preset.appId));
|
||||
edtShortcutLg.Text = preset.shortcut;
|
||||
edtStepsLg.Text = preset.steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
|
||||
var index = -1;
|
||||
|
||||
for (var i = 0; i < cbxLgPresetDevice.Items.Count; i++)
|
||||
{
|
||||
var pnpDev = (LgDevice)cbxLgPresetDevice.Items[i];
|
||||
if ((string.IsNullOrEmpty(preset.DeviceMacAddress) && string.IsNullOrEmpty(pnpDev.MacAddress)) || pnpDev.MacAddress.Equals(preset.DeviceMacAddress, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cbxLgPresetDevice.SelectedIndex = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1072,6 +1073,7 @@ namespace ColorControl
|
||||
cbxLgApps.SelectedIndex = -1;
|
||||
edtShortcutLg.Text = string.Empty;
|
||||
edtStepsLg.Text = string.Empty;
|
||||
cbxLgPresetDevice.SelectedIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1105,6 +1107,16 @@ namespace ColorControl
|
||||
|
||||
preset.name = name;
|
||||
|
||||
if (cbxLgPresetDevice.SelectedIndex == -1)
|
||||
{
|
||||
preset.DeviceMacAddress = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
var device = (LgDevice)cbxLgPresetDevice.SelectedItem;
|
||||
preset.DeviceMacAddress = device.MacAddress;
|
||||
}
|
||||
|
||||
if (cbxLgApps.SelectedIndex == -1)
|
||||
{
|
||||
preset.appId = string.Empty;
|
||||
@@ -1199,6 +1211,15 @@ namespace ColorControl
|
||||
{
|
||||
FillLgDevices();
|
||||
}
|
||||
|
||||
if (scLgController.Panel2.Controls.Count == 0)
|
||||
{
|
||||
var rcPanel = new RemoteControlPanel(_lgService, _lgService.GetRemoteControlButtons());
|
||||
rcPanel.Parent = scLgController.Panel2;
|
||||
rcPanel.Dock = DockStyle.Fill;
|
||||
}
|
||||
chkLgRemoteControlShow.Checked = _lgService.Config.ShowRemoteControl;
|
||||
scLgController.Panel2Collapsed = !_lgService.Config.ShowRemoteControl;
|
||||
}
|
||||
}
|
||||
else if (tcMain.SelectedTab == tabLog)
|
||||
@@ -1236,15 +1257,21 @@ namespace ColorControl
|
||||
|
||||
private void FillLgDevices()
|
||||
{
|
||||
cbxLgDevices.Items.Clear();
|
||||
var devices = _lgService.Devices;
|
||||
foreach (var device in devices)
|
||||
|
||||
cbxLgDevices.Items.Clear();
|
||||
cbxLgDevices.Items.AddRange(devices.ToArray());
|
||||
|
||||
cbxLgPresetDevice.Items.Clear();
|
||||
cbxLgPresetDevice.Items.AddRange(devices.ToArray());
|
||||
var globalDevice = new LgDevice("Globally selected device", string.Empty, string.Empty, true, true);
|
||||
cbxLgPresetDevice.Items.Insert(0, globalDevice);
|
||||
|
||||
var device = _lgService.SelectedDevice;
|
||||
|
||||
if (device != null)
|
||||
{
|
||||
cbxLgDevices.Items.Add(device);
|
||||
}
|
||||
if (_lgService.SelectedDevice != null)
|
||||
{
|
||||
cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(_lgService.SelectedDevice);
|
||||
cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(device);
|
||||
}
|
||||
|
||||
if (!devices.Any())
|
||||
@@ -1252,10 +1279,32 @@ namespace ColorControl
|
||||
MessageForms.WarningOk("It seems there's no LG TV available! Please make sure it's connected to the same network as this PC.");
|
||||
}
|
||||
|
||||
if (cbxLgApps.Items.Count == 0 && _lgService.SelectedDevice != null)
|
||||
if (cbxLgApps.Items.Count == 0 && device != null)
|
||||
{
|
||||
_lgService.RefreshApps().ContinueWith((task) => BeginInvoke(new Action(FillLgApps)));
|
||||
}
|
||||
|
||||
SetLgDevicePowerOptions();
|
||||
}
|
||||
|
||||
private void SetLgDevicePowerOptions()
|
||||
{
|
||||
var device = _lgService.SelectedDevice;
|
||||
clbLgPower.Enabled = device != null;
|
||||
|
||||
_disableEvents = true;
|
||||
try
|
||||
{
|
||||
clbLgPower.SetItemChecked(0, device?.PowerOnAfterStartup ?? false);
|
||||
clbLgPower.SetItemChecked(1, device?.PowerOnAfterResume ?? false);
|
||||
clbLgPower.SetItemChecked(2, device?.PowerOffOnShutdown ?? false);
|
||||
clbLgPower.SetItemChecked(3, device?.PowerOffOnStandby ?? false);
|
||||
clbLgPower.SetItemChecked(4, device?.PowerSwitchOnScreenSaver ?? false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_disableEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void FillLgApps()
|
||||
@@ -1542,9 +1591,13 @@ namespace ColorControl
|
||||
}
|
||||
else
|
||||
{
|
||||
_lgService.SelectedDevice = (PnpDev)cbxLgDevices.SelectedItem;
|
||||
_lgService.SelectedDevice = (LgDevice)cbxLgDevices.SelectedItem;
|
||||
}
|
||||
btnLGRemoteControl.Enabled = _lgService.SelectedDevice != null;
|
||||
chkLgRemoteControlShow.Enabled = _lgService.SelectedDevice != null;
|
||||
btnLgRemoveDevice.Enabled = _lgService.SelectedDevice != null && _lgService.SelectedDevice.IsCustom;
|
||||
btnLgDeviceConvertToCustom.Enabled = _lgService.SelectedDevice != null && !_lgService.SelectedDevice.IsCustom;
|
||||
|
||||
SetLgDevicePowerOptions();
|
||||
}
|
||||
|
||||
private void lvLgPresets_DoubleClick(object sender, EventArgs e)
|
||||
@@ -1620,12 +1673,18 @@ namespace ColorControl
|
||||
|
||||
private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e)
|
||||
{
|
||||
if (!_initialized)
|
||||
if (!_initialized || _disableEvents)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(_lgService.Config.PowerOnAfterResume || _lgService.Config.PowerOnAfterStartup || _lgService.Config.PowerSwitchOnScreenSaver))
|
||||
var device = _lgService?.SelectedDevice;
|
||||
if (device == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerSwitchOnScreenSaver))
|
||||
{
|
||||
MessageForms.InfoOk(
|
||||
@"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV:
|
||||
@@ -1638,11 +1697,11 @@ See Options to test this functionality."
|
||||
|
||||
BeginInvoke(new Action(() =>
|
||||
{
|
||||
_lgService.Config.PowerOnAfterStartup = clbLgPower.GetItemChecked(0);
|
||||
_lgService.Config.PowerOnAfterResume = clbLgPower.GetItemChecked(1);
|
||||
_lgService.Config.PowerOffOnShutdown = clbLgPower.GetItemChecked(2);
|
||||
_lgService.Config.PowerOffOnStandby = clbLgPower.GetItemChecked(3);
|
||||
_lgService.Config.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4);
|
||||
device.PowerOnAfterStartup = clbLgPower.GetItemChecked(0);
|
||||
device.PowerOnAfterResume = clbLgPower.GetItemChecked(1);
|
||||
device.PowerOffOnShutdown = clbLgPower.GetItemChecked(2);
|
||||
device.PowerOffOnStandby = clbLgPower.GetItemChecked(3);
|
||||
device.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4);
|
||||
|
||||
_lgService.InstallEventHandlers();
|
||||
}));
|
||||
@@ -1810,16 +1869,6 @@ Do you want to continue?";
|
||||
|
||||
private void btnLGRemoteControl_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
|
||||
{
|
||||
var buttons = _lgService.GetRemoteControlButtons();
|
||||
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
}
|
||||
|
||||
private void AfterInitialized()
|
||||
@@ -2273,5 +2322,116 @@ Do you want to continue?";
|
||||
Clipboard.SetText(preset.id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void btnLgAddDevice_Click(object sender, EventArgs e)
|
||||
{
|
||||
var values = MessageForms.ShowDialog("Add tv", new[] { "Name", "Ip-address", "MAC-address" }, ValidateAddDevice);
|
||||
if (values.Any())
|
||||
{
|
||||
var device = new LgDevice(values[0], values[1], values[2]);
|
||||
|
||||
var form = MessageForms.ShowProgress("Connecting to device...");
|
||||
|
||||
var result = false;
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
var task = device.TestConnection();
|
||||
|
||||
result = Utils.WaitForTask(task);
|
||||
}
|
||||
finally
|
||||
{
|
||||
form.Close();
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
if (!result && MessageForms.QuestionYesNo("Unable to connect to the device. Are you sure you want to add it?") != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lgService.AddCustomDevice(device);
|
||||
|
||||
FillLgDevices();
|
||||
}
|
||||
}
|
||||
|
||||
private string ValidateAddDevice(List<string> values)
|
||||
{
|
||||
if (values.Any(v => string.IsNullOrEmpty(v)))
|
||||
{
|
||||
return "Please fill in all the fields";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void btnLgRemoveDevice_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageForms.QuestionYesNo("Are you sure you want to remove this device?") != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var device = _lgService.SelectedDevice;
|
||||
|
||||
if (device != null)
|
||||
{
|
||||
_lgService.RemoveCustomDevice(device);
|
||||
|
||||
FillLgDevices();
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuLgButtons_Opening(object sender, CancelEventArgs e)
|
||||
{
|
||||
mnuLgActions.DropDownItems.Clear();
|
||||
|
||||
var preset = GetSelectedLgPreset();
|
||||
|
||||
if (preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var device = _lgService.GetPresetDevice(preset);
|
||||
|
||||
var actions = device?.GetInvokableActions();
|
||||
foreach (var keyValuePair in actions)
|
||||
{
|
||||
var text = keyValuePair.Key;
|
||||
|
||||
var item = mnuLgActions.DropDownItems.Add(text);
|
||||
item.Click += miLgAddAction_Click;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void btnLgDeviceConvertToCustom_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageForms.QuestionYesNo(
|
||||
@"This will convert the automatically detected device to a custom variant.
|
||||
This means that the device will remain here even if it is not detected anymore.
|
||||
Do you want to continue?"
|
||||
) != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lgService.SelectedDevice.ConvertToCustom();
|
||||
|
||||
FillLgDevices();
|
||||
}
|
||||
|
||||
private void btnLgRemoteControlShow_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void chkLgRemoteControlShow_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
scLgController.Panel2Collapsed = !chkLgRemoteControlShow.Checked;
|
||||
_lgService.Config.ShowRemoteControl = chkLgRemoteControlShow.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,9 @@
|
||||
<metadata name="mnuLgButtons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>147, 17</value>
|
||||
</metadata>
|
||||
<metadata name="backgroundWorker1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>419, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
@@ -30,5 +29,91 @@ namespace ColorControl
|
||||
{
|
||||
return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
public static List<string> ShowDialog(string caption, IEnumerable<string> labels, Func<List<string>, string> validateFunc = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
var prompt = new Form()
|
||||
{
|
||||
Width = 500,
|
||||
Height = 90 + (labels.Count() * 55),
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen
|
||||
};
|
||||
|
||||
var top = 10;
|
||||
var boxes = new List<TextBoxBase>();
|
||||
|
||||
foreach (var label in labels)
|
||||
{
|
||||
var textLabel = new Label() { Left = 40, Top = top, Text = label };
|
||||
top += 25;
|
||||
prompt.Controls.Add(textLabel);
|
||||
|
||||
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (label.Contains("Ip-address"))
|
||||
{
|
||||
textBox.Mask = "###.###.###.###";
|
||||
textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
else if (label.Contains("MAC-address"))
|
||||
{
|
||||
textBox.Mask = "AA-AA-AA-AA-AA-AA";
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
top += 30;
|
||||
prompt.Controls.Add(textBox);
|
||||
boxes.Add(textBox);
|
||||
}
|
||||
|
||||
var confirmation = new Button() { Text = "Add", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 };
|
||||
confirmation.Click += (sender, e) =>
|
||||
{
|
||||
values.AddRange(boxes.Select(b => b.Text.Trim()));
|
||||
|
||||
if (validateFunc != null)
|
||||
{
|
||||
var message = validateFunc(values);
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
WarningOk(message);
|
||||
values.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
prompt.Close();
|
||||
};
|
||||
prompt.Controls.Add(confirmation);
|
||||
prompt.AcceptButton = confirmation;
|
||||
|
||||
prompt.ShowDialog(MainForm);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static Form ShowProgress(string caption)
|
||||
{
|
||||
var prompt = new Form()
|
||||
{
|
||||
Width = 300,
|
||||
Height = 100,
|
||||
FormBorderStyle = FormBorderStyle.FixedToolWindow,
|
||||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen,
|
||||
ControlBox = false
|
||||
};
|
||||
var textLabel = new Label() { Left = 20, Top = 25, Text = caption, AutoSize = true };
|
||||
prompt.Controls.Add(textLabel);
|
||||
|
||||
prompt.Show(MainForm);
|
||||
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-5
@@ -1,11 +1,14 @@
|
||||
using Windows.Devices.Enumeration;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Devices.Enumeration;
|
||||
using Windows.Devices.Enumeration.Pnp;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class PnpDev
|
||||
{
|
||||
[JsonIgnore]
|
||||
public DeviceInformation DevInfo { get; private set; }
|
||||
[JsonIgnore]
|
||||
public PnpObject PnpObject { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
@@ -13,18 +16,27 @@ namespace ColorControl
|
||||
|
||||
public string MacAddress { get; private set; }
|
||||
|
||||
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress)
|
||||
[JsonConstructor]
|
||||
public PnpDev(string name, string ipAddress, string macAddress)
|
||||
{
|
||||
DevInfo = devInfo;
|
||||
PnpObject = pnpObject;
|
||||
Name = name;
|
||||
IpAddress = ipAddress;
|
||||
MacAddress = macAddress;
|
||||
}
|
||||
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress) : this(name, ipAddress, macAddress)
|
||||
{
|
||||
DevInfo = devInfo;
|
||||
PnpObject = pnpObject;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name + " (" + IpAddress + ")";
|
||||
return (IsCustom() ? "Custom: " : "Auto detect: ") + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $"({IpAddress})" : string.Empty);
|
||||
}
|
||||
|
||||
public bool IsCustom()
|
||||
{
|
||||
return DevInfo == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
[Flags]
|
||||
enum PresetTrigger
|
||||
{
|
||||
[Description("Startup")]
|
||||
Startup = 1,
|
||||
[Description("Resume")]
|
||||
Resume = 2,
|
||||
[Description("Shutdown")]
|
||||
Shutdown = 4,
|
||||
[Description("Standby")]
|
||||
Standby = 8,
|
||||
[Description("Screensaver")]
|
||||
Screensaver = 16,
|
||||
[Description("Process start")]
|
||||
ProcessStart = 32,
|
||||
[Description("Process exit")]
|
||||
ProcessExit = 64
|
||||
}
|
||||
|
||||
internal abstract class PresetBase
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string shortcut { get; set; }
|
||||
public PresetTrigger Triggers { get; set; }
|
||||
|
||||
private int _id;
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace ColorControl
|
||||
Console.WriteLine("--amdpreset <preset name or id>: execute AMD-preset");
|
||||
Console.WriteLine("--lgpreset <preset name> : execute LG-preset");
|
||||
Console.WriteLine("--help : displays this help info");
|
||||
Console.WriteLine("Options :");
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine("--nogui: starts command from the command line and will not open GUI (is forced when GUI is already running)");
|
||||
|
||||
result = true;
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.2.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.2.0.0")]
|
||||
[assembly: AssemblyVersion("3.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("3.0.0.0")]
|
||||
+55
-26
@@ -8,10 +8,10 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ColorControl.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace ColorControl.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@@ -19,53 +19,82 @@ namespace ColorControl.Properties
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorControl.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap LG_remote {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("LG_remote", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Power;120;132
|
||||
///1;106;228
|
||||
///2;196;228
|
||||
///3;286;228
|
||||
///4;106;296
|
||||
///5;196;296
|
||||
///6;286;296
|
||||
///7;106;362
|
||||
///8;196;362
|
||||
///9;286;362
|
||||
///0;196;425
|
||||
///Vol +;98;513
|
||||
///Mute;198;513
|
||||
///Channel +;298;513
|
||||
///Vol -;98;622
|
||||
///Speech;198;633
|
||||
///Channel -;298;622
|
||||
///Home;78;695
|
||||
///Settings;318;695.
|
||||
/// </summary>
|
||||
internal static string RC_coords {
|
||||
get {
|
||||
return ResourceManager.GetString("RC_coords", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
@@ -60,6 +60,7 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
@@ -68,9 +69,10 @@
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
@@ -85,9 +87,10 @@
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
@@ -109,9 +112,16 @@
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="LG_remote" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LG_remote.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="RC_coords" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\RC_coords.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
||||
</data>
|
||||
</root>
|
||||
+54
-10
@@ -29,21 +29,25 @@ namespace ColorControl
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RemoteControlForm));
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.lblUC = new System.Windows.Forms.Label();
|
||||
this.chkDirectMode = new System.Windows.Forms.CheckBox();
|
||||
this.pnlMouse = new System.Windows.Forms.Panel();
|
||||
this.lblPos = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// flowPanel
|
||||
//
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.Location = new System.Drawing.Point(12, 16);
|
||||
this.flowPanel.Location = new System.Drawing.Point(12, 185);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(787, 164);
|
||||
this.flowPanel.Size = new System.Drawing.Size(817, 120);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
|
||||
@@ -56,7 +60,7 @@ namespace ColorControl
|
||||
this.btnClose.FlatAppearance.BorderSize = 0;
|
||||
this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnClose.ForeColor = System.Drawing.Color.White;
|
||||
this.btnClose.Location = new System.Drawing.Point(796, 0);
|
||||
this.btnClose.Location = new System.Drawing.Point(826, 0);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Size = new System.Drawing.Size(14, 23);
|
||||
this.btnClose.TabIndex = 1;
|
||||
@@ -68,7 +72,7 @@ namespace ColorControl
|
||||
//
|
||||
this.lblUC.AutoSize = true;
|
||||
this.lblUC.ForeColor = System.Drawing.Color.Maroon;
|
||||
this.lblUC.Location = new System.Drawing.Point(130, 0);
|
||||
this.lblUC.Location = new System.Drawing.Point(12, 0);
|
||||
this.lblUC.Name = "lblUC";
|
||||
this.lblUC.Size = new System.Drawing.Size(100, 13);
|
||||
this.lblUC.TabIndex = 0;
|
||||
@@ -78,8 +82,8 @@ namespace ColorControl
|
||||
//
|
||||
this.chkDirectMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.chkDirectMode.AutoSize = true;
|
||||
this.chkDirectMode.ForeColor = System.Drawing.Color.DarkGray;
|
||||
this.chkDirectMode.Location = new System.Drawing.Point(12, 643);
|
||||
this.chkDirectMode.ForeColor = System.Drawing.Color.White;
|
||||
this.chkDirectMode.Location = new System.Drawing.Point(12, 718);
|
||||
this.chkDirectMode.Name = "chkDirectMode";
|
||||
this.chkDirectMode.Size = new System.Drawing.Size(105, 17);
|
||||
this.chkDirectMode.TabIndex = 2;
|
||||
@@ -93,9 +97,9 @@ namespace ColorControl
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.pnlMouse.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlMouse.Location = new System.Drawing.Point(12, 191);
|
||||
this.pnlMouse.Location = new System.Drawing.Point(12, 311);
|
||||
this.pnlMouse.Name = "pnlMouse";
|
||||
this.pnlMouse.Size = new System.Drawing.Size(787, 446);
|
||||
this.pnlMouse.Size = new System.Drawing.Size(817, 401);
|
||||
this.pnlMouse.TabIndex = 3;
|
||||
this.pnlMouse.MouseEnter += new System.EventHandler(this.pnlMouse_MouseEnter);
|
||||
this.pnlMouse.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pnlMouse_MouseMove);
|
||||
@@ -106,17 +110,53 @@ namespace ColorControl
|
||||
this.lblPos.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.lblPos.AutoSize = true;
|
||||
this.lblPos.ForeColor = System.Drawing.Color.Maroon;
|
||||
this.lblPos.Location = new System.Drawing.Point(719, 643);
|
||||
this.lblPos.Location = new System.Drawing.Point(749, 718);
|
||||
this.lblPos.Name = "lblPos";
|
||||
this.lblPos.Size = new System.Drawing.Size(0, 13);
|
||||
this.lblPos.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.ForeColor = System.Drawing.Color.White;
|
||||
this.label1.Location = new System.Drawing.Point(12, 26);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(538, 52);
|
||||
this.label1.TabIndex = 4;
|
||||
this.label1.Text = resources.GetString("label1.Text");
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.ForeColor = System.Drawing.Color.White;
|
||||
this.label2.Location = new System.Drawing.Point(231, 91);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(216, 52);
|
||||
this.label2.TabIndex = 5;
|
||||
this.label2.Text = "Mouse:\r\n- left button: activate/open item\r\n- right button: go back\r\n- wheel: scro" +
|
||||
"ll through menus (only vertically)";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.ForeColor = System.Drawing.Color.White;
|
||||
this.label3.Location = new System.Drawing.Point(12, 91);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(180, 91);
|
||||
this.label3.TabIndex = 6;
|
||||
this.label3.Text = "Keyboard keys:\r\n- arrow keys: navigate the menu\r\n- backspace: go back\r\n- escape: " +
|
||||
"exit\r\n- S: open picture settings\r\n- H: open Home\r\n- media keys: volume up/down/m" +
|
||||
"ute";
|
||||
//
|
||||
// RemoteControlForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Black;
|
||||
this.ClientSize = new System.Drawing.Size(811, 660);
|
||||
this.ClientSize = new System.Drawing.Size(841, 735);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.lblPos);
|
||||
this.Controls.Add(this.pnlMouse);
|
||||
this.Controls.Add(this.chkDirectMode);
|
||||
@@ -132,6 +172,7 @@ namespace ColorControl
|
||||
this.Text = "RemoteControlForm";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.RemoteControlForm_FormClosed);
|
||||
this.Load += new System.EventHandler(this.RemoteControlForm_Load);
|
||||
this.Shown += new System.EventHandler(this.RemoteControlForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyDown);
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp);
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
@@ -149,5 +190,8 @@ namespace ColorControl
|
||||
private System.Windows.Forms.CheckBox chkDirectMode;
|
||||
private System.Windows.Forms.Panel pnlMouse;
|
||||
private System.Windows.Forms.Label lblPos;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
}
|
||||
}
|
||||
@@ -116,24 +116,24 @@ namespace ColorControl
|
||||
{
|
||||
if (chkDirectMode.Checked)
|
||||
{
|
||||
MessageForms.InfoOk(
|
||||
@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
|
||||
Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
|
||||
// MessageForms.InfoOk(
|
||||
//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
|
||||
//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
|
||||
|
||||
Keyboard keys:
|
||||
- arrow keys: navigate the menu
|
||||
- backspace: go back
|
||||
- escape: exit
|
||||
- S: open picture settings
|
||||
- media keys: volume up/down/mute
|
||||
//Keyboard keys:
|
||||
//- arrow keys: navigate the menu
|
||||
//- backspace: go back
|
||||
//- escape: exit
|
||||
//- S: open picture settings
|
||||
//- media keys: volume up/down/mute
|
||||
|
||||
Mouse:
|
||||
- left button: activate/open item
|
||||
- right button: go back
|
||||
- wheel: scroll through menus (only vertically)
|
||||
//Mouse:
|
||||
//- left button: activate/open item
|
||||
//- right button: go back
|
||||
//- wheel: scroll through menus (only vertically)
|
||||
|
||||
Press F1 to stop Direct Mode."
|
||||
);
|
||||
//Press F1 to stop Direct Mode."
|
||||
// );
|
||||
|
||||
if (_mouseService == null)
|
||||
{
|
||||
@@ -216,6 +216,11 @@ Press F1 to stop Direct Mode."
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
chkDirectMode.Checked = true;
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
HandleMouse(e);
|
||||
|
||||
@@ -117,4 +117,10 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="label1.Text" xml:space="preserve">
|
||||
<value>This form allows you to control the TV directly via your keyboard and mouse.
|
||||
Note that your mouse will be captured in a box and that due to pointer acceleration the control will not be perfect.
|
||||
|
||||
Press F1 to stop Direct Mode.</value>
|
||||
</data>
|
||||
</root>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
partial class RemoteControlPanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.pbRemote = new System.Windows.Forms.PictureBox();
|
||||
this.btnRcDirect = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// pbRemote
|
||||
//
|
||||
this.pbRemote.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pbRemote.Image = global::ColorControl.Properties.Resources.LG_remote;
|
||||
this.pbRemote.Location = new System.Drawing.Point(0, 0);
|
||||
this.pbRemote.Name = "pbRemote";
|
||||
this.pbRemote.Size = new System.Drawing.Size(129, 468);
|
||||
this.pbRemote.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pbRemote.TabIndex = 0;
|
||||
this.pbRemote.TabStop = false;
|
||||
this.pbRemote.Click += new System.EventHandler(this.pbRemote_Click);
|
||||
this.pbRemote.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbRemote_MouseMove);
|
||||
//
|
||||
// btnRcDirect
|
||||
//
|
||||
this.btnRcDirect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnRcDirect.Location = new System.Drawing.Point(71, 7);
|
||||
this.btnRcDirect.Name = "btnRcDirect";
|
||||
this.btnRcDirect.Size = new System.Drawing.Size(55, 23);
|
||||
this.btnRcDirect.TabIndex = 3;
|
||||
this.btnRcDirect.Text = "Direct";
|
||||
this.btnRcDirect.UseVisualStyleBackColor = true;
|
||||
this.btnRcDirect.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// RemoteControlPanel
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.Controls.Add(this.btnRcDirect);
|
||||
this.Controls.Add(this.pbRemote);
|
||||
this.Name = "RemoteControlPanel";
|
||||
this.Size = new System.Drawing.Size(129, 468);
|
||||
this.Load += new System.EventHandler(this.RemoteControlPanel_Load);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox pbRemote;
|
||||
private System.Windows.Forms.Button btnRcDirect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
partial class RemoteControlPanel : UserControl
|
||||
{
|
||||
private static int Range = 35;
|
||||
|
||||
private LgService _lgService;
|
||||
private List<LgPreset> _buttons;
|
||||
private RemoteControlForm _remoteControlForm;
|
||||
private Bitmap _backupImage;
|
||||
private LgPreset _lastHoveredPreset;
|
||||
|
||||
public RemoteControlPanel(LgService lgService, List<LgPreset> buttons)
|
||||
{
|
||||
_lgService = lgService;
|
||||
_buttons = buttons;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
_backupImage = new Bitmap(pbRemote.Image);
|
||||
|
||||
var txtCoords = Utils.GetResourceFile("RC_coords.txt");
|
||||
|
||||
foreach (var line in txtCoords.Split('\n'))
|
||||
{
|
||||
var columns = line.Split(';');
|
||||
|
||||
var name = columns[0];
|
||||
var preset = _buttons.FirstOrDefault(p => p.name.Equals(name));
|
||||
if (preset != null)
|
||||
{
|
||||
preset.X = int.Parse(columns[1]);
|
||||
preset.Y = int.Parse(columns[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlPanel_Load(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
if (e is MouseEventArgs)
|
||||
{
|
||||
var preset = (LgPreset)(sender as Button).Tag;
|
||||
|
||||
var _ = _lgService.ApplyPreset(preset);
|
||||
}
|
||||
}
|
||||
|
||||
private void pbRemote_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (e is MouseEventArgs mouseArgs)
|
||||
{
|
||||
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
|
||||
|
||||
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
|
||||
|
||||
if (preset != null)
|
||||
{
|
||||
var _ = _lgService.ApplyPreset(preset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (int, int) GetCoordsInImage(int curX, int curY)
|
||||
{
|
||||
var width = pbRemote.Width;
|
||||
var aspect = 0.27D;
|
||||
var currentAspect = (double)pbRemote.Width / pbRemote.Height;
|
||||
if (currentAspect > aspect)
|
||||
{
|
||||
width = (int)(aspect * pbRemote.Height);
|
||||
curX = curX - ((pbRemote.Width - width) / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var height = (int)(pbRemote.Width / aspect);
|
||||
curY = curY - ((pbRemote.Height - height) / 2);
|
||||
}
|
||||
|
||||
var factor = 405D / width;
|
||||
|
||||
var posX = (int)(curX * factor);
|
||||
var posY = (int)(curY * factor);
|
||||
|
||||
return (posX, posY);
|
||||
}
|
||||
|
||||
private void btnRcDirect_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs)
|
||||
{
|
||||
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
|
||||
|
||||
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
|
||||
|
||||
var refresh = false;
|
||||
|
||||
if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset))
|
||||
{
|
||||
var image = (Bitmap)pbRemote.Image;
|
||||
|
||||
for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++)
|
||||
{
|
||||
for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++)
|
||||
{
|
||||
image.SetPixel(x, y, _backupImage.GetPixel(x, y));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
if (preset != null && preset != _lastHoveredPreset)
|
||||
{
|
||||
var image = (Bitmap)pbRemote.Image;
|
||||
var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2);
|
||||
|
||||
using (var g = Graphics.FromImage(image))
|
||||
{
|
||||
using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White)))
|
||||
{
|
||||
g.FillEllipse(cloud_brush, rec);
|
||||
}
|
||||
}
|
||||
|
||||
//for (var y = preset.Y - 30; y < preset.Y + 30; y++)
|
||||
//{
|
||||
// for (var x = preset.X - 30; x < preset.X + 30; x++)
|
||||
// {
|
||||
// var pixel = image.GetPixel(x, y);
|
||||
|
||||
// image.SetPixel(x, y, Color.FromArgb(150, 0, 0));
|
||||
|
||||
// }
|
||||
//}
|
||||
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
if (refresh)
|
||||
{
|
||||
pbRemote.Refresh();
|
||||
}
|
||||
|
||||
_lastHoveredPreset = preset;
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
|
||||
{
|
||||
var buttons = _lgService.GetRemoteControlButtons();
|
||||
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -0,0 +1,37 @@
|
||||
Power;120;136
|
||||
1;106;228
|
||||
2;196;228
|
||||
3;286;228
|
||||
4;106;296
|
||||
5;196;296
|
||||
6;286;296
|
||||
7;106;362
|
||||
8;196;362
|
||||
9;286;362
|
||||
List;106;425
|
||||
0;196;425
|
||||
SAP;286;425
|
||||
Vol +;98;513
|
||||
Mute;198;513
|
||||
Channel +;298;513
|
||||
Vol -;98;622
|
||||
Channel -;298;622
|
||||
Home;78;695
|
||||
Settings;318;695
|
||||
Up;200;748
|
||||
Left;98;852
|
||||
Enter;200;852
|
||||
Right;304;852
|
||||
Down;200;952
|
||||
Back;80;1000
|
||||
TV Guide;320;1000
|
||||
Netflix;92;1076
|
||||
Inputs;200;1066
|
||||
Amazon Prime;312;1076
|
||||
Red;90;1150
|
||||
Green;164;1150
|
||||
Yellow;240;1150
|
||||
Blue;316;1150
|
||||
Rakuten TV;116;1218
|
||||
Play;202;1218
|
||||
Pause;291;1218
|
||||
+31
-2
@@ -353,16 +353,25 @@ namespace ColorControl
|
||||
return devices;
|
||||
}
|
||||
|
||||
public static T WaitForTask<T>(System.Threading.Tasks.Task<T> task)
|
||||
public static T WaitForTask<T>(Task<T> task)
|
||||
{
|
||||
while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
Thread.Sleep(10);
|
||||
Application.DoEvents();
|
||||
}
|
||||
return task.Result;
|
||||
}
|
||||
|
||||
public static void WaitForTask(System.Threading.Tasks.Task task)
|
||||
{
|
||||
while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete))
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
Application.DoEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetNotifyIconText(NotifyIcon ni, string text)
|
||||
{
|
||||
text = text.Substring(0, Math.Min(text.Length, 127));
|
||||
@@ -686,6 +695,26 @@ namespace ColorControl
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void StartProcess(string fileName, string arguments)
|
||||
{
|
||||
var process = Process.Start(fileName, arguments);
|
||||
}
|
||||
|
||||
public static string GetResourceFile(string resourceName)
|
||||
{
|
||||
resourceName = "ColorControl.Resources." + resourceName;
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var result = reader.ReadToEnd();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProcessExtensions
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace LgTv
|
||||
|
||||
public async Task<bool> Connect()
|
||||
{
|
||||
var ctx = _connection.Connect(new Uri(webSocketUri));
|
||||
var ctx = _connection.Connect(new Uri(webSocketUri));
|
||||
return await ctx;
|
||||
}
|
||||
|
||||
@@ -365,6 +365,12 @@ namespace LgTv
|
||||
return (string)response.sessionId;
|
||||
}
|
||||
|
||||
public async Task Test()
|
||||
{
|
||||
var msg = JObject.Parse(@"{ ""configs"": { ""tv.model.motionProMode"": ""OLED Motion Pro"" } }");
|
||||
var requestMessage = new RequestMessage("luna://com.webos.service.config/setConfigs", msg);
|
||||
var response = await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
||||
@@ -44,12 +44,30 @@ namespace LgTv
|
||||
_connection.MessageReceived += Connection_MessageReceived;
|
||||
|
||||
_connection.Closed += Connection_Closed;
|
||||
await _connection.ConnectAsync(uri);
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token);
|
||||
|
||||
IsConnected?.Invoke(true);
|
||||
cancellationTokenSource.CancelAfter(5000);
|
||||
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
return true;
|
||||
var result = await connectTask.ContinueWith((antecedent) =>
|
||||
{
|
||||
if (antecedent.Status == TaskStatus.RanToCompletion)
|
||||
{
|
||||
// connectTask ran to completion, so we know that the MessageWebSocket is connected.
|
||||
// Add additional code here to use the MessageWebSocket.
|
||||
IsConnected?.Invoke(true);
|
||||
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// connectTask timed out, or faulted.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -33,7 +33,15 @@ namespace LgTv
|
||||
MUTE,
|
||||
MENU,
|
||||
CC,
|
||||
DASH
|
||||
DASH,
|
||||
CHANNELUP,
|
||||
CHANNELDOWN,
|
||||
LIST,
|
||||
AD,
|
||||
SAP,
|
||||
PROGRAM,
|
||||
PLAY,
|
||||
PAUSE
|
||||
}
|
||||
|
||||
public class LgWebOsMouseService : IDisposable
|
||||
|
||||
Reference in new issue
Block a user