diff --git a/BUILD.md b/BUILD.md index 847f21e..6de537c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,2 +1,4 @@ ## Build Command: -`dotnet publish HA -r win-x64 -c Release --self-contained /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --output .\ha\bin\Publish\net6.0-windows10.0.17763.0\` +```powersehll +dotnet publish HADC_REBORN -r win-x64 -c Release --self-contained /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --output .\HADC_REBORN\bin\Publish\net8.0-windows10.0.17763.0\ +``` diff --git a/HA.sln b/HA.sln index ea7199d..f0b4438 100644 --- a/HA.sln +++ b/HA.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HA", "HA\HA.csproj", "{F4675A4D-373E-4C21-B77D-529E88181E5F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HADC_REBORN", "HADC_REBORN\HADC_REBORN.csproj", "{3C0F1F5F-1ADE-4CF2-A5B5-348A41ABCD84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{8FC5E1BA-0D50-4D77-B6F9-B395ABCD6259}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +19,14 @@ Global {F4675A4D-373E-4C21-B77D-529E88181E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F4675A4D-373E-4C21-B77D-529E88181E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4675A4D-373E-4C21-B77D-529E88181E5F}.Release|Any CPU.Build.0 = Release|Any CPU + {3C0F1F5F-1ADE-4CF2-A5B5-348A41ABCD84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C0F1F5F-1ADE-4CF2-A5B5-348A41ABCD84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C0F1F5F-1ADE-4CF2-A5B5-348A41ABCD84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C0F1F5F-1ADE-4CF2-A5B5-348A41ABCD84}.Release|Any CPU.Build.0 = Release|Any CPU + {8FC5E1BA-0D50-4D77-B6F9-B395ABCD6259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FC5E1BA-0D50-4D77-B6F9-B395ABCD6259}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FC5E1BA-0D50-4D77-B6F9-B395ABCD6259}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FC5E1BA-0D50-4D77-B6F9-B395ABCD6259}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HA/Class/HomeAssistant/HomeAssistantWS.cs b/HA/Class/HomeAssistant/HomeAssistantWS.cs index 981b762..1908736 100644 --- a/HA/Class/HomeAssistant/HomeAssistantWS.cs +++ b/HA/Class/HomeAssistant/HomeAssistantWS.cs @@ -2,6 +2,7 @@ using HA.Class.HomeAssistant.Objects; using Microsoft.VisualBasic; using Newtonsoft.Json; +using Newtonsoft.Json.Bson; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -46,7 +47,6 @@ public class HomeAssistantWS [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern void keybd_event(uint bVk, uint bScan, uint dwFlags, uint dwExtraInfo); - public HomeAssistantWS(string apiUrl, string webhookId, string apiToken) { url = apiUrl.TrimEnd('/'); @@ -67,9 +67,9 @@ public async Task registerAsync() try { Logger.write("WS INITIALIZATION"); - Uri wsAddress = new Uri(url + "/api/websocket"); var exitEvent = new ManualResetEvent(false); + socket = new ClientWebSocket(); socket.Options.KeepAliveInterval = TimeSpan.Zero; socket.ConnectAsync(wsAddress, CancellationToken.None).Wait(); @@ -110,7 +110,7 @@ public async Task registerAsync() isPingEnabled = true; StartPingAsyncTask(); - + isConnected = true; retryCount = 0; @@ -122,11 +122,11 @@ public async Task registerAsync() Logger.write("WS State " + socket.State); Close(); - if (retryCount <= 5 && socket.State == WebSocketState.Closed) + if (retryCount <= 5 && (socket.State == WebSocketState.Closed || socket.State == WebSocketState.Aborted)) { retryCount++; registerAsync(); - } + } } } @@ -141,18 +141,18 @@ private JObject sendAndRecieveAsync(dynamic payloadObj) socket.SendAsync(BYTEPayload, WebSocketMessageType.Text, true, CancellationToken.None).Wait(); - string JSONRecievedpayload = ""; - - Logger.write("SEND/RECIEVING"); - interactions = interactions + 1; + string JSONRecievedpayload = ""; - WebSocketReceiveResult result = socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None).Result; + Logger.write("SEND/RECIEVING"); + interactions = interactions + 1; - JSONRecievedpayload = Encoding.UTF8.GetString(buffer, 0, result.Count); + WebSocketReceiveResult result = socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None).Result; + + JSONRecievedpayload = Encoding.UTF8.GetString(buffer, 0, result.Count); + + Logger.write("RECIEVE"); + Logger.write(JSONRecievedpayload); - Logger.write("RECIEVE"); - Logger.write(JSONRecievedpayload); - return JObject.Parse(JSONRecievedpayload); } @@ -171,7 +171,7 @@ private async Task RecieveAsync() private async Task Send(dynamic payloadObj) { - try { + try { string JSONPayload = JsonConvert.SerializeObject(payloadObj, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }).ToString(); Logger.write("SEND"); @@ -189,9 +189,9 @@ private async Task Send(dynamic payloadObj) public bool getConectionStatus() { - return (isConnected && isSubscribed); + return (isConnected && isSubscribed); } - + private async Task StartPingAsyncTask() { Logger.write("Initializing Ping"); @@ -293,7 +293,7 @@ private void HandleEvent(JObject payloadObj) app.ShowNotification(msg_title, msg_text, msg_image, msg_audio); } - + if (eventData.ContainsKey("data")) { if (eventData["data"].ToObject().ContainsKey("key")) { @@ -312,11 +312,15 @@ public bool Close() Logger.write("WS state " + socket.State); - updatePingTimer.Stop(); - if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent) + if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent || socket.State == WebSocketState.Aborted) { + updatePingTimer.Stop(); socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + socket.Dispose(); + socket = null; + Thread.Sleep(5000); Logger.write("WS closed"); + registerAsync(); } return true; diff --git a/HA/Class/Sensors/IpLocation.cs b/HA/Class/Sensors/IpLocation.cs index 1282708..8a3326d 100644 --- a/HA/Class/Sensors/IpLocation.cs +++ b/HA/Class/Sensors/IpLocation.cs @@ -5,7 +5,7 @@ namespace HA.Class.Sensors { - internal class IpLocation + internal class Notification { public static string getData() { @@ -29,6 +29,4 @@ public static void test() } } - - } diff --git a/HA/MainWindow.xaml b/HA/MainWindow.xaml index 1013e25..514a871 100644 --- a/HA/MainWindow.xaml +++ b/HA/MainWindow.xaml @@ -45,7 +45,7 @@ - + diff --git a/HA/MainWindow.xaml.cs b/HA/MainWindow.xaml.cs index 6b106c4..5406f8e 100644 --- a/HA/MainWindow.xaml.cs +++ b/HA/MainWindow.xaml.cs @@ -130,5 +130,11 @@ public void settings_Save() loadingScreenStatus.Content = "Initialization Failed!"; loading_Hide(); } + + private void api_status_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + app.ws.Close(); + app.Start(); + } } } diff --git a/HA/configuration.yaml b/HA/configuration.yaml index 54b0e20..ef80549 100644 --- a/HA/configuration.yaml +++ b/HA/configuration.yaml @@ -1,8 +1,8 @@  -websocket: -ip_location: -debug: -keys: +websocket: true +# ip_location: false +debug: true +# keys: false sensor: - platform: wmic diff --git a/HADC_REBORN/App.config b/HADC_REBORN/App.config new file mode 100644 index 0000000..01a0f51 --- /dev/null +++ b/HADC_REBORN/App.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/HADC_REBORN/App.xaml b/HADC_REBORN/App.xaml new file mode 100644 index 0000000..1f079a8 --- /dev/null +++ b/HADC_REBORN/App.xaml @@ -0,0 +1,10 @@ + + + + + diff --git a/HADC_REBORN/App.xaml.cs b/HADC_REBORN/App.xaml.cs new file mode 100644 index 0000000..351445e --- /dev/null +++ b/HADC_REBORN/App.xaml.cs @@ -0,0 +1,284 @@ +using HADC_REBORN.Class.Helpers; +using System.Configuration; +using System.Data; +using System.Windows; +using Microsoft.Toolkit.Uwp.Notifications; + +using From = System.Windows.Forms; +using System.Diagnostics; +using System.Net.Mail; +using AutoUpdaterDotNET; +using System.Reflection.Metadata.Ecma335; +using System.IO; +using System.ComponentModel; +using System.Security.Policy; +using HADC_REBORN.Class.HomeAssistant; +using HADC_REBORN.Class.HomeAssistant.Objects; +using HADC_REBORN.Class.Sensors; +using System.Reflection; +using System.Windows.Threading; +using System.Text.RegularExpressions; +using System.Globalization; +using Windows.Devices.Sensors; +using System.Runtime.ExceptionServices; +using HADC_REBORN.Class.Actions; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using Windows.UI.ViewManagement; +using System.Runtime.InteropServices; +using System.Windows.Interop; +using System.Diagnostics.Eventing.Reader; +using System.Windows.Media.Imaging; + +namespace HADC_REBORN +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : System.Windows.Application + { +#if DEBUG + private string appDir = Directory.GetCurrentDirectory(); +#else + private string appDir = AppDomain.CurrentDomain.BaseDirectory; +#endif + + public static NotifyIcon? icon = null; + public static Logger log = new Logger(); + public static YamlLoader yamlLoader; + public bool initializing = true; + + public ApiConnector? haApiConnector = null; + public static ApiWrapper? apiWrapper = null; + + public WsConnector? haWsConnector = null; + public static WsWrapper? wsWrapper = null; + + public static string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + protected override void OnStartup(StartupEventArgs e) + { + AppDomain.CurrentDomain.FirstChanceException += GlobalExceptionFunction; + + App.icon = new NotifyIcon(); + + icon.DoubleClick += new EventHandler(icon_Click); + icon.Icon = HADC_REBORN.Resource.ha_icon; + + //Count for icon dakt mode change + Theme.setTheme(Theme.isLightTheme()); + + icon.Text = System.AppDomain.CurrentDomain.FriendlyName; + icon.Visible = true; + + icon.ContextMenuStrip = new ContextMenuStrip(); + icon.ContextMenuStrip.Items.Add("Home Assistant", null, OnHomeAssistant_Click); + icon.ContextMenuStrip.Items.Add("Log", null, OnLog_Click); + icon.ContextMenuStrip.Items.Add("Send Test Notification", null, OnTestNotification_Click); + icon.ContextMenuStrip.Items.Add("Quit", null, OnQuit_Click); + + base.OnStartup(e); + + //On Color mode + UISettings settings = new UISettings(); + Theme.setTheme(Theme.isColorLight(settings.GetColorValue(UIColorType.Background))); + settings.ColorValuesChanged += theme_Changed; + + log.writeLine("starting version: " + version); + } + + public void loadYAMLComfig(bool force = false) + { + if (yamlLoader != null && !force) + { + return; + } + + log.writeLine("looking for 'configuration.yaml'"); + string configFilePath = Path.Combine(appDir, "configuration.yaml"); + if (!File.Exists(configFilePath)) + { + log.writeLine("'configuration.yaml' not found creating new one!"); + File.WriteAllBytes(configFilePath, HADC_REBORN.Resource.configuration); + } + else + { + log.writeLine("'configuration.yaml' found!"); + } + yamlLoader = new YamlLoader(configFilePath); + } + + public Dictionary>>>> getYAMLComfig() + { + return yamlLoader.getConfigurationData(); + } + + private void theme_Changed(UISettings sender, object args) + { + Theme.setTheme(Theme.isColorLight(sender.GetColorValue(UIColorType.Background))); + } + + static void GlobalExceptionFunction(object source, FirstChanceExceptionEventArgs eventArgs) + { + log.writeLine("[" + AppDomain.CurrentDomain.FriendlyName + "]" + eventArgs.Exception.ToString(), 3); + } + + private void Application_Exit(object sender, ExitEventArgs e) + { + + } + + private void Application_Startup(object sender, StartupEventArgs e) + { + AutoUpdater.Start("https://github.com/GamerClassN7/HA_Desktop_Companion/releases/latest/download/meta.xml"); + AutoUpdater.Synchronous = true; + AutoUpdater.ShowRemindLaterButton = false; + } + + public bool Start() + { + loadYAMLComfig(true); + + Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + string url = config.AppSettings.Settings["url"].Value; + string token = config.AppSettings.Settings["token"].Value; + log.setSecreets(new string[] { token, url.Replace("http://", "").Replace("https://", "")}); + + + if (String.IsNullOrEmpty(url) || String.IsNullOrEmpty(token)) + { + log.writeLine("URL or Token not fount!!"); + return false; + } + + //int pingLoopIndex = 0; + //do + //{ + // log.writeLine("Waiting ntil server response!"); + // pingLoopIndex++; + //} while (!Network.PingHost((new Uri(url)).Host) && pingLoopIndex < 5); + + try + { + log.writeLine(url); + haApiConnector = new ApiConnector(url, token); + apiWrapper = new ApiWrapper(yamlLoader, haApiConnector, config); + apiWrapper.connect(); + log.writeLine("RestAPI registered"); + log.setSecreets(new string[] { token, url.Replace("http://", "").Replace("https://", ""), haApiConnector.getSecret(), haApiConnector.getWebhookID() }); + } + catch (Exception ex) + { + log.writeLine("Failed to initialize RestAPI" + ex.Message); + return false; + } + + if (String.IsNullOrEmpty(haApiConnector.getWebhookID())) + { + log.writeLine("Failed to get webhook_id from RestAPI"); + return false; + } + + try + { + string wsUrl = url.Replace("http", "ws"); + log.writeLine(wsUrl); + haWsConnector = new WsConnector(wsUrl, token, haApiConnector.getWebhookID()); + wsWrapper = new WsWrapper(yamlLoader, haWsConnector); + wsWrapper.Connect(); + log.writeLine("Websocket registered"); + } + catch (Exception ex) + { + log.writeLine("Failed to initialize WebbSocket" + ex.Message); + return false; + } + + NetworkChange.NetworkAvailabilityChanged += GetNetworkChange_NetworkAvailabilityChanged; + + try + { + Autostart.register(); + log.writeLine("Autostart registered"); + } + catch (Exception ex) + { + log.writeLine("Autostart registration failed" + ex.Message); + return false; + } + + log.writeLine("Initialization Compleeted"); + return true; + } + + private void GetNetworkChange_NetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e) + { + if (e.IsAvailable) + { + log.writeLine("Network Connected!"); + wsWrapper.restart(); + apiWrapper.restart(); + } + else + { + log.writeLine("Network Disconnected!"); + wsWrapper.Disconnect(); + apiWrapper.disconnect(); + } + } + + public void Stop() + { + log.writeLine("stoping RestAPI"); + } + + public bool isRunning() + { + return (haApiConnector.connected() && haWsConnector.connected()); + } + + protected override void OnExit(ExitEventArgs e) + { + icon.Dispose(); + + base.OnExit(e); + } + + private void OnLog_Click(object? sender, EventArgs e) + { + Process.Start("notepad", log.getLogPath()); + } + + private void OnHomeAssistant_Click(object? sender, EventArgs e) + { + Process.Start("explorer", "https://google.com"); + } + + private void OnQuit_Click(object? sender, EventArgs e) + { + Close(); + } + + private void OnTestNotification_Click(object? sender, EventArgs e) + { + Notification.Spawn("test"); + } + + private void icon_Click(Object? sender, EventArgs e) + { + MainWindow main = App.Current.Windows.OfType().FirstOrDefault(); + if (main != null) + { + main.Focus(); + } + else + { + new MainWindow().Show(); + } + } + + public static void Close() + { + Environment.Exit(0); + } + } +} diff --git a/HADC_REBORN/AssemblyInfo.cs b/HADC_REBORN/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/HADC_REBORN/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/HADC_REBORN/Class/Actions/Keyboard.cs b/HADC_REBORN/Class/Actions/Keyboard.cs new file mode 100644 index 0000000..90c0578 --- /dev/null +++ b/HADC_REBORN/Class/Actions/Keyboard.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class +{ + class Keyboard + { + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] + public static extern void keybd_event(uint bVk, uint bScan, uint dwFlags, uint dwExtraInfo); + + public static void SendKey(string Key) + { + if (!App.yamlLoader.getConfigurationData().ContainsKey("keys")) + { + return; + } + + try + { + App.log.writeLine("Typing key code: " + Key); + uint ukey = (uint)System.Convert.ToUInt32(Key); + keybd_event(ukey, 0, 0, 0); + keybd_event(ukey, 0, 2, 0); + + } + catch (Exception) + { + App.log.writeLine("ERROR Type Key"); + } + } + } +} diff --git a/HADC_REBORN/Class/Actions/Notification.cs b/HADC_REBORN/Class/Actions/Notification.cs new file mode 100644 index 0000000..6f4c7fb --- /dev/null +++ b/HADC_REBORN/Class/Actions/Notification.cs @@ -0,0 +1,67 @@ +using Microsoft.Toolkit.Uwp.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Media; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Actions +{ + class Notification + { + public static void Spawn(string body = "", string title = "", string imageUrl = "", string audioUrl = "", int duration = 500) + { + ToastContentBuilder toast = new ToastContentBuilder(); + toast.AddText(body); + + if (!String.IsNullOrEmpty(title)) + { + toast.AddText(title); + } + + if (!String.IsNullOrEmpty(imageUrl)) + { + //TODO: REFACTORING + string fileName = string.Format("{0}{1}.png", System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + if (imageUrl.StartsWith("http")) + { + WebClient wc = new WebClient(); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + wc.DownloadFile(imageUrl, fileName); + } + + toast.AddInlineImage(new Uri("file:///" + fileName)); + } + + if (!String.IsNullOrEmpty(audioUrl)) + { + //TODO: REFACTORING + string fileName = string.Format("{0}{1}.wav", System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + if (audioUrl.StartsWith("http")) + { + WebClient wc = new WebClient(); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + wc.DownloadFile(audioUrl, fileName); + } + + playAudio(fileName, duration); + } + + toast.Show(); + } + + private static void playAudio(string fileName, int duration) + { + new Thread(() => + { + SoundPlayer player = new SoundPlayer(); + player.SoundLocation = fileName; + player.Play(); + Thread.Sleep(duration); + player.Stop(); + }).Start(); + } + } +} diff --git a/HADC_REBORN/Class/Helpers/Autostart.cs b/HADC_REBORN/Class/Helpers/Autostart.cs new file mode 100644 index 0000000..2b08801 --- /dev/null +++ b/HADC_REBORN/Class/Helpers/Autostart.cs @@ -0,0 +1,36 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Helpers +{ + internal class Autostart + { +#if DEBUG + private static string appDir = Directory.GetCurrentDirectory(); +#else + private static string appDir = AppDomain.CurrentDomain.BaseDirectory; +#endif + + public static void register() + { + try + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run", true)) + { + string exePath = Path.Combine(appDir, System.AppDomain.CurrentDomain.FriendlyName + ".exe"); + key.SetValue(System.AppDomain.CurrentDomain.FriendlyName, "\"" + exePath + "\""); + key.Close(); + } + } + catch (Exception) + { + throw new Exception("Autostart Registration Failed!"); + } + } + } +} diff --git a/HADC_REBORN/Class/Helpers/Logger.cs b/HADC_REBORN/Class/Helpers/Logger.cs new file mode 100644 index 0000000..c9c8a01 --- /dev/null +++ b/HADC_REBORN/Class/Helpers/Logger.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Printing; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Helpers +{ + public class Logger + { + private static bool isInistialized = false; +#if DEBUG + private string appDir = Directory.GetCurrentDirectory(); +#else + private string appDir = AppDomain.CurrentDomain.BaseDirectory; +#endif + private string logFilePath; + private DateTime lastInitializeDateTime; + private static string[] secreetsStrings = new string[] { }; + + public Logger() { + logFilePath = initialize(); + } + + public void setSecreets(string[] strings) + { + if (!isInistialized) + { + logFilePath = initialize(); + } + secreetsStrings = strings.Where(x => !string.IsNullOrEmpty(x)).ToArray(); + } + + public string getLogPath() + { + return logFilePath; + } + + private string initialize() + { + string logFolderPath = Path.Combine(appDir, "logs"); + Debug.WriteLine(logFolderPath); + + if (!Directory.Exists(logFolderPath)) + { + Directory.CreateDirectory(logFolderPath); + } + + string logFileName = "log_" + ((DateTime.Now).ToString("MM_dd_yyyy")) + ".log"; + string logFilePath = Path.Combine(logFolderPath, logFileName); + + if (!File.Exists(logFilePath)) + { + File.WriteAllText(logFilePath, getLogMessage("Initializing",0), System.Text.Encoding.UTF8); + } + + removeOldLogFiles(logFolderPath); + + lastInitializeDateTime = DateTime.Now; + isInistialized = true; + + return logFilePath; + } + + private void removeOldLogFiles(string rootLogFolderPath, int daysBack = -3) + { + string logFileName = "log_"+((DateTime.Now).AddDays(daysBack).ToString("MM_dd_yyyy"))+".log"; + string LogFileToDelete = Path.Combine(rootLogFolderPath, logFileName); + if (File.Exists(LogFileToDelete)) + { + File.Delete(LogFileToDelete); + } + } + + private string getLogMessage(string text, int type = 0) + { + string parsedText = text; + if (secreetsStrings.Length > 0) + { + foreach (string secret in secreetsStrings) + { + parsedText = parsedText.Replace(secret, "***SECRET****"); + } + } + + return DateTime.Now.ToString("[MM/dd/yyyy-HH:mm.ss]") + "[" + type + "]" + parsedText + "\n"; + } + + public void writeLine(string msg, int type = 0) + { + int InitilizedBeforeNumberOfDays = (int)(DateTime.Now - lastInitializeDateTime).TotalDays; + if (!isInistialized || InitilizedBeforeNumberOfDays > 0) + { + logFilePath = initialize(); + } + + Debug.WriteLine(msg); + + FileStream fileStream = new FileStream(logFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + StreamWriter streamWriter = new StreamWriter(fileStream); + streamWriter.Write(getLogMessage(msg, type)); + streamWriter.Dispose(); + fileStream.Dispose(); + } + } +} diff --git a/HADC_REBORN/Class/Helpers/Network.cs b/HADC_REBORN/Class/Helpers/Network.cs new file mode 100644 index 0000000..41c7991 --- /dev/null +++ b/HADC_REBORN/Class/Helpers/Network.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Helpers +{ + class Network + { + public static bool PingHost(string nameOrAddress) + { + bool pingable = false; + Ping pinger = null; + + try + { + pinger = new Ping(); + PingReply reply = pinger.Send(nameOrAddress); + pingable = reply.Status == IPStatus.Success; + } + catch (PingException) + { + // Discard PingExceptions and return false; + } + finally + { + if (pinger != null) + { + pinger.Dispose(); + } + } + + return pingable; + } + } +} diff --git a/HADC_REBORN/Class/Helpers/Theme.cs b/HADC_REBORN/Class/Helpers/Theme.cs new file mode 100644 index 0000000..a885965 --- /dev/null +++ b/HADC_REBORN/Class/Helpers/Theme.cs @@ -0,0 +1,75 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Interop; +using System.Windows.Media.Imaging; +using System.Windows; +using Windows.UI; +using Windows.UI.ViewManagement; + +namespace HADC_REBORN.Class.Helpers +{ + class Theme + { + public static bool isLightTheme() + { + using var key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"); + var value = key?.GetValue("AppsUseLightTheme"); + return value is int i && i > 0; + } + + public static bool isColorLight(Windows.UI.Color clr) + { + return ((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128); + } + + [DllImport("dwmapi.dll")] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; + private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + + public static bool UseImmersiveDarkMode(IntPtr handle, bool enabled) + { + if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17763)) + { + var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; + if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18985)) + { + attribute = DWMWA_USE_IMMERSIVE_DARK_MODE; + } + + int useImmersiveDarkMode = enabled ? 1 : 0; + return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0; + } + + return false; + } + + public static void setTheme(bool isLight = true) + { + Icon iconResource = HADC_REBORN.Resource.ha_icon; + if (isLight) + { + iconResource = HADC_REBORN.Resource.ha_icon_dark; + } + + setIcons(iconResource); + } + + private static void setIcons(Icon iconResource) + { + App.icon.Icon = iconResource; + MainWindow main = App.Current.Windows.OfType().FirstOrDefault(); + if (main != null) + { + main.Icon = Imaging.CreateBitmapSourceFromHBitmap(iconResource.ToBitmap().GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); + } + } + } +} diff --git a/HADC_REBORN/Class/Helpers/YamlLoader.cs b/HADC_REBORN/Class/Helpers/YamlLoader.cs new file mode 100644 index 0000000..c43a108 --- /dev/null +++ b/HADC_REBORN/Class/Helpers/YamlLoader.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Shapes; + +namespace HADC_REBORN.Class.Helpers +{ + public class YamlLoader + { + private string configurationFilePath; + private Dictionary>>>> configurationData; + + public YamlLoader(string configurationFileFullPath) + { + if (!File.Exists(configurationFileFullPath)) + { + throw new Exception("'configuration.yaml' not found"); + } + + configurationFilePath = configurationFileFullPath; + load(); + } + + private bool load() + { + try + { + string[] text = File.ReadAllLines(configurationFilePath, System.Text.Encoding.UTF8); + + string section = ""; //ROOT + int num = 0; + string subSection = ""; + string category = ""; + string subArray = ""; + Dictionary>>>> configurationDataTemp = new Dictionary>>>>(); + + foreach (string line in text) + { + string localLine = line; + + if (line.Contains('#') == true) + { + localLine = line.Replace('#' + line.Split('#')[1], ""); + } + + if (localLine.Contains(':') == true) + { + string[] splitedString = localLine.Split(':'); + string parameter = splitedString[0].Trim(); + string value = splitedString[1].Trim(); + + if (parameter == "") + { + continue; + } + + if (subArray != "" && !splitedString[0].Contains(" ")) + { + subArray = ""; + } + + if (splitedString.Length > 2) + { + for (int i = 2; i < splitedString.Length; i++) + { + value += ":" + splitedString[i].Trim(); + } + } + + if (value == "") + { + if (splitedString[0].Contains(" ")) + { + subArray = parameter; + continue; + } + else + { + subArray = ""; + } + if (section != "") + { + if (!configurationDataTemp.ContainsKey(section)) + { + configurationDataTemp[section] = null; + } + } + section = parameter; + num = 0; + category = ""; + subSection = ""; + continue; + } + + if (parameter.Contains("- ") == true) + { + if (subArray == "") + { + subSection = parameter.Replace("- ", ""); + + if (category == value) + { + num++; + } + else + { + if (!configurationDataTemp.ContainsKey(section) || !configurationDataTemp[section].ContainsKey(subSection) || !configurationDataTemp[section][subSection].ContainsKey(value) || configurationDataTemp[section][subSection][value].Count <= 0) + { + num = 0; + } + else + { + num = configurationDataTemp[section][subSection][value].Count; + } + } + + category = value; + continue; + } + else + { + parameter = parameter.Replace("- ", ""); + } + } + + if (!configurationDataTemp.ContainsKey(section)) + { + configurationDataTemp[section] = new Dictionary>>>(); + } + + if (!configurationDataTemp[section].ContainsKey(subSection)) + { + configurationDataTemp[section][subSection] = new Dictionary>>(); + } + + if (!configurationDataTemp[section][subSection].ContainsKey(category)) + { + configurationDataTemp[section][subSection][category] = new List>(100); + } + + if (configurationDataTemp[section][subSection][category].Count <= num) + { + configurationDataTemp[section][subSection][category].Add(new Dictionary()); + } + + if (subArray == "") + { + configurationDataTemp[section][subSection][category][num][parameter] = value.Replace("\"", ""); + } + else + { + if (!configurationDataTemp[section][subSection][category][num].ContainsKey(subArray)) + { + configurationDataTemp[section][subSection][category][num][subArray] = new Dictionary(); + } + configurationDataTemp[section][subSection][category][num][subArray][parameter] = value.Replace("\"", ""); + } + } + } + + configurationData = configurationDataTemp; + return true; + } + catch + { + return false; + } + } + + public Dictionary>>>> getConfigurationData() + { + return configurationData; + } + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/ApiConnector.cs b/HADC_REBORN/Class/HomeAssistant/ApiConnector.cs new file mode 100644 index 0000000..f007a10 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/ApiConnector.cs @@ -0,0 +1,202 @@ +using HADC_REBORN.Class.HomeAssistant.Objects; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Windows; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Net.Sockets; +using System.Security.Policy; + +namespace HADC_REBORN.Class.HomeAssistant +{ + public class ApiConnector + { + private string url; + private string token; + + //Data From Registration + private string webhookId = null; + private string secret = null; + + private HttpClient client = new HttpClient(); + + //Erro Handling + private int failedAttempts = 0; + private List sensorsBuffer = new List(); + + public ApiConnector(string apiRootUrl, string apiToken) + { + // if (!testApiUrl(apiRootUrl)) + //{ + // throw new Exception("unnabůle to connect to" + apiRootUrl); + //} + + url = apiRootUrl.TrimEnd('/'); + token = apiToken; + } + + public string getWebhookID() + { + return webhookId; + } + + public bool connected() + { + if (failedAttempts > 5) + return false; + + return true; + } + + public string getSecret() + { + return secret; + } + + public void setWebhookID(string apiWebhookId) + { + webhookId = apiWebhookId; + } + + public void setSecret(string apiSecret) + { + secret = apiSecret; + } + + public Uri getUrl() + { + return new Uri(url); + } + + private HttpContent sendApiRequest(string endpoint) + { + inicialize(); + HttpResponseMessage response = client.GetAsync(endpoint).Result; + if (response.IsSuccessStatusCode) + { + App.log.writeLine("[API] RESPONSE CODE <" + (int)response.StatusCode + "> " + response.StatusCode.ToString()); + + return response.Content; + // usergrid.ItemsSource = users; + //.ReadAsAsync>().Result + } + else + { + failedAttempts++; + throw new Exception("Error Code" + response.StatusCode + " : Message - " + response.ReasonPhrase); + } + } + + private HttpContent sendApiPOSTRequest(string endpoint, object payload) + { + inicialize(); + string content = JsonConvert.SerializeObject(payload, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }).ToString(); + + App.log.writeLine("[API] SEND:"); + App.log.writeLine(content); + + var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); + + HttpResponseMessage response = client.PostAsync(endpoint, stringContent).Result; + App.log.writeLine("[API] RESPONSE CODE <" + (int)response.StatusCode + "> " + response.StatusCode.ToString()); + + if (response.IsSuccessStatusCode) + { + failedAttempts = 0; + return response.Content; + } + if (response.StatusCode == HttpStatusCode.Unauthorized) + { + failedAttempts = 6; + throw new Exception("Error Code" + response.StatusCode + " : Message - " + response.ReasonPhrase); + } + else + { + failedAttempts++; + throw new Exception("Error Code" + response.StatusCode + " : Message - " + response.ReasonPhrase); + } + } + + public string GetVersion() + { + var jObject = JObject.Parse(sendApiRequest("/api/config").ReadAsStringAsync().Result); + return jObject["version"].ToString(); + } + + public string RegisterDevice(ApiDevice device) + { + //https://developers.home-assistant.io/docs/api/native-app-integration/setup + var jObject = JObject.Parse(sendApiPOSTRequest("/api/mobile_app/registrations", device).ReadAsStringAsync().Result); + webhookId = jObject["webhook_id"].ToString(); + secret = jObject["secret"].ToString(); + + return jObject.ToString(); + } + + public string RegisterSensorData(ApiSensor senzor) + { + ApiRequest request = new ApiRequest(); + request.SetData(senzor); + request.SetType("register_sensor"); + + var jObject = JObject.Parse(sendApiPOSTRequest("/api/webhook/" + webhookId, request).ReadAsStringAsync().Result); + return jObject.ToString(); + } + + public void AddSensorData(ApiSensor senzor) + { + sensorsBuffer.Add(senzor); + } + + public string sendSensorBuffer() + { + if (sensorsBuffer.Count < 1) + { + App.log.writeLine("No data to send!"); + return ""; + } + + ApiRequest request = new ApiRequest(); + + request.SetData(sensorsBuffer); + request.SetType("update_sensor_states"); + JObject jObject = new JObject(); + + try + { + jObject = JObject.Parse(sendApiPOSTRequest("/api/webhook/" + webhookId, request).ReadAsStringAsync().Result); + Debug.Write(jObject.ToString()); + sensorsBuffer.Clear(); + failedAttempts = 0; + } + catch (Exception ex) + { + failedAttempts++; + App.log.writeLine("[API] " + ex.Message); + } + + return jObject.ToString(); + } + + private void inicialize(){ + if (client == null) + { + return; + } + + client = new HttpClient(); + client.BaseAddress = new Uri(url); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + } + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/ApiWrapper.cs b/HADC_REBORN/Class/HomeAssistant/ApiWrapper.cs new file mode 100644 index 0000000..4620448 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/ApiWrapper.cs @@ -0,0 +1,444 @@ +using HADC_REBORN.Class.Helpers; +using HADC_REBORN.Class.HomeAssistant.Objects; +using HADC_REBORN.Class.Sensors; +using Microsoft.VisualBasic.Logging; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration; +using System.Globalization; +using System.Linq; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Policy; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace HADC_REBORN.Class.HomeAssistant +{ + public class ApiWrapper + { + static Dictionary sensorUpdatedAtList = new Dictionary(); + static Dictionary sensorLastValues = new Dictionary(); + //static Dictionary sensorFailed= new Dictionary(); + + private YamlLoader yamlLoader; + private ApiConnector apiConnector; + private Configuration config; + + private BackgroundWorker apiWorker = new BackgroundWorker(); + + private DispatcherTimer apiTimer = new DispatcherTimer(); + + public ApiWrapper(YamlLoader yamlLoaderDependency, ApiConnector apiConnectorDependency, Configuration configDependency) { + yamlLoader = yamlLoaderDependency; + apiConnector = apiConnectorDependency; + config = configDependency; + } + + private static string applySenzorValueFilters(string senzorType, Dictionary sensorDefinition, string sensorData) + { + if (senzorType == "binary_sensor") + { + return sensorData; + } + + if (string.IsNullOrEmpty(sensorData)) + { + sensorData = "0"; + } + + if (sensorDefinition.ContainsKey("value_map")) + { + string[] valueMap = sensorDefinition["value_map"].Split("|"); + sensorData = valueMap[(Int32.Parse((sensorData).ToString()))]; + //Logger.write(JsonConvert.SerializeObject(valueMap)); + } + + if (sensorDefinition.ContainsKey("filters")) + { + bool isNumeric = double.TryParse(sensorData, out _); + Dictionary filters = sensorDefinition["filters"]; + + if (isNumeric) + { + if (filters.ContainsKey("multiply")) + { + sensorData = (double.Parse(sensorData) * float.Parse(filters["multiply"], CultureInfo.InvariantCulture.NumberFormat)).ToString(); + } + + if (filters.ContainsKey("divide")) + { + sensorData = (double.Parse(sensorData) / float.Parse(filters["divide"], CultureInfo.InvariantCulture.NumberFormat)).ToString(); + } + + if (filters.ContainsKey("deduct")) + { + sensorData = (double.Parse(sensorData) - float.Parse(filters["deduct"], CultureInfo.InvariantCulture.NumberFormat)).ToString(); + } + + if (filters.ContainsKey("add")) + { + sensorData = (double.Parse(sensorData) + float.Parse(filters["add"], CultureInfo.InvariantCulture.NumberFormat)).ToString(); + } + } + + } + + if (sensorDefinition.ContainsKey("accuracy_decimals")) + { + if (Regex.IsMatch(sensorData.ToString(), @"^[0-9]+.[0-9]+$") || Regex.IsMatch(sensorData.ToString(), @"^\d$")) + { + sensorData = Math.Round(double.Parse(sensorData), Int32.Parse(sensorDefinition["accuracy_decimals"] ?? 0)).ToString(); + } + } + + return sensorData; + } + + public async Task queryAndSendSenzorData() + { + Dictionary> senzorsQuerys = new Dictionary>(); + + Dictionary senzorTypes = getSensorsConfiguration(); + foreach (var item in senzorTypes) + { + string senzorType = item.Key; + foreach (var platform in (Dictionary>>>)senzorTypes[senzorType]) + { + foreach (var integration in (Dictionary>>)platform.Value) + { + foreach (var sensorDefinition in (List>)integration.Value) + { + string sensorUniqueId = sensorDefinition["unique_id"]; + if (senzorsQuerys.ContainsKey(sensorUniqueId)) + { + continue; + } + + if (sensorUpdatedAtList.ContainsKey(sensorUniqueId) && sensorDefinition.ContainsKey("update_interval")) + { + TimeSpan difference = DateTime.Now.Subtract(sensorUpdatedAtList[sensorUniqueId]); + if (difference.TotalSeconds < Double.Parse(sensorDefinition["update_interval"])) + { + App.log.writeLine("Skiping: " + sensorUniqueId + " sensor Update time not Reached"); + continue; + } + } + + //if (sensorFailed.ContainsKey(sensorUniqueId) && sensorFailed[sensorUniqueId] != false) + //{ + // App.log.writeLine("Skiping previouselly failed: " + sensorUniqueId + " sensor"); + // continue; + //} + + senzorsQuerys.Add(sensorUniqueId, getSenzorValue(integration, sensorDefinition)); + } + } + } + } + + //TODO, Create Sensor list to iterate ower when building request to server + + await Task.WhenAll(senzorsQuerys.Values.ToArray()); + if (senzorsQuerys.Count < 1) + { + App.log.writeLine("no senzor scheduled!"); + } + App.log.writeLine("all task query Done!"); + + foreach (var item in senzorTypes) + { + string senzorType = item.Key; + foreach (var platform in (Dictionary>>>)senzorTypes[senzorType]) + { + foreach (var integration in (Dictionary>>)platform.Value) + { + foreach (var sensorDefinition in (List>)integration.Value) + { + string sensorUniqueId = sensorDefinition["unique_id"]; + if (!senzorsQuerys.ContainsKey(sensorUniqueId)) + { + continue; + } + + string sensorData = senzorsQuerys[sensorUniqueId].Result; + sensorData = applySenzorValueFilters(senzorType, sensorDefinition, sensorData); + App.log.writeLine("Filtered Value " + sensorUniqueId + " - " + sensorData); + + if (string.IsNullOrEmpty(sensorData)) + { + App.log.writeLine("No Data Returned to sensor " + sensorUniqueId); + continue; + } + + if (sensorLastValues.ContainsKey(sensorDefinition["unique_id"])) + { + if (sensorData == sensorLastValues[sensorDefinition["unique_id"]]) + { + //App.log.writeLine("Skiping! Same Data Already Send " + sensorData); + continue; + } + } + + ApiSensor senzor = new ApiSensor(); + + senzor.unique_id = sensorDefinition["unique_id"]; + senzor.icon = sensorDefinition["icon"]; + senzor.state = convertToType(sensorData); + senzor.type = senzorType; + senzor.unique_id = sensorDefinition["unique_id"]; + + apiConnector.AddSensorData(senzor); + + if (sensorUpdatedAtList.ContainsKey(sensorDefinition["unique_id"])) + { + sensorUpdatedAtList[sensorDefinition["unique_id"]] = DateTime.Now; + } + else + { + sensorUpdatedAtList.Add(sensorDefinition["unique_id"], DateTime.Now); + } + + if (sensorLastValues.ContainsKey(sensorDefinition["unique_id"])) + { + sensorLastValues[sensorDefinition["unique_id"]] = sensorData; + } + else + { + sensorLastValues.Add(sensorDefinition["unique_id"], sensorData); + } + } + } + } + } + + apiConnector.sendSensorBuffer(); + } + + public void restart() + { + disconnect(); + connect(); + } + + public void disconnect() + { + if (apiConnector.connected()) + { + App.log.writeLine("[API] Disconecting"); + App.log.writeLine("[API] Disconected"); + } + if (apiTimer.IsEnabled) + { + App.log.writeLine("[API] Ping stoping"); + apiTimer.Stop(); + App.log.writeLine("[API] Ping Stopped"); + } + } + + public void connect() + { + int pingLoopIndex = 0; + do + { + App.log.writeLine("Waiting ntil server response!"); + pingLoopIndex++; + } while (!Network.PingHost(apiConnector.getUrl().Host) && pingLoopIndex < 5); + + string webhookId = config.AppSettings.Settings["webhook_id"].Value; + string secret = config.AppSettings.Settings["secret"].Value; + + if (String.IsNullOrEmpty(webhookId)) + { + + string prefix = ""; + /*if (App.yamlLoader.getConfigurationData().ContainsKey("debug")) + { + prefix = "DEBUG_"; + }*/ + + ApiDevice devideForRegistration = new ApiDevice() + { + device_name = (prefix + Environment.MachineName), + device_id = (prefix + Environment.MachineName).ToLower(), + app_id = Assembly.GetEntryAssembly().GetName().Version.ToString().ToLower(), + app_name = Assembly.GetExecutingAssembly().GetName().Name, + app_version = Assembly.GetEntryAssembly().GetName().Version.ToString(), + manufacturer = Wmic.GetValue("Win32_ComputerSystem", "Manufacturer", "root\\CIMV2"), + model = Wmic.GetValue("Win32_ComputerSystem", "Model", "root\\CIMV2"), + os_name = Wmic.GetValue("Win32_OperatingSystem", "Caption", "root\\CIMV2"), + os_version = Environment.OSVersion.ToString(), + app_data = new + { + push_websocket_channel = true, + }, + supports_encryption = false + }; + apiConnector.RegisterDevice(devideForRegistration); + + Dictionary senzorTypes = getSensorsConfiguration(); + foreach (var item in senzorTypes) + { + string senzorType = item.Key; + foreach (var platform in (Dictionary>>>)senzorTypes[senzorType]) + { + foreach (var integration in (Dictionary>>)platform.Value) + { + foreach (var sensorDefinition in (List>)integration.Value) + { + ApiSensor senzor = new ApiSensor(); + + senzor.type = senzorType; + senzor.name = sensorDefinition["name"]; + senzor.unique_id = sensorDefinition["unique_id"]; + + if (sensorDefinition.ContainsKey("device_class")) + senzor.device_class = sensorDefinition["device_class"]; + + if (sensorDefinition.ContainsKey("icon")) + senzor.icon = sensorDefinition["icon"]; + + if (sensorDefinition.ContainsKey("unit_of_measurement")) + senzor.unit_of_measurement = sensorDefinition["unit_of_measurement"]; + + if (sensorDefinition.ContainsKey("state_class")) + senzor.state_class = sensorDefinition["state_class"]; + + if (sensorDefinition.ContainsKey("entity_category")) + senzor.entity_category = sensorDefinition["entity_category"]; + + if (sensorDefinition.ContainsKey("disabled")) + senzor.device_class = sensorDefinition["disabled"]; + + if (senzorType == "binary_sensor") + senzor.state = false; + + apiConnector.RegisterSensorData(senzor); + } + } + } + } + + webhookId = apiConnector.getWebhookID(); + secret = apiConnector.getSecret(); + + config.AppSettings.Settings["webhook_id"].Value = webhookId; + config.AppSettings.Settings["secret"].Value = secret; + + config.Save(ConfigurationSaveMode.Modified); + } + + apiConnector.setWebhookID(webhookId); + apiConnector.setSecret(secret); + + apiWorker.DoWork += apiWorker_DoWork; + + apiTimer.Interval = TimeSpan.FromSeconds(5); + apiTimer.Tick += updateSensors; + apiTimer.Start(); + } + + private void apiWorker_DoWork(object? sender, DoWorkEventArgs e) + { + queryAndSendSenzorData(); + } + + private async void updateSensors(object? sender, EventArgs e) + { + if (apiWorker.IsBusy != true) + { + apiWorker.RunWorkerAsync(); + } + } + + private static dynamic convertToType(dynamic variable) + { + //ADD double + string variableStr = variable.ToString(); + // Logger.write("BEFORE CONVERSION" + variableStr); + if (Regex.IsMatch(variableStr, "^(?:tru|fals)e$", RegexOptions.IgnoreCase)) + { + //Logger.write("AFTER CONVERSION (Bool)" + variableStr.ToString()); + return bool.Parse(variableStr); + } + else if (Regex.IsMatch(variableStr, @"^[0-9]+.[0-9]+$") && (variableStr.Contains(".") || variableStr.Contains(","))) + { + //Logger.write("AFTER CONVERSION (double)" + variableStr.ToString()); + return double.Parse(variableStr); + } + else if (Regex.IsMatch(variableStr, @"^\d+$")) + { + //Logger.write("AFTER CONVERSION (int)" + variableStr.ToString()); + return double.Parse(variableStr); + } + + //Logger.write("AFTER CONVERSION" + variableStr.ToString()); + return variableStr; + } + + public Dictionary getSensorsConfiguration() + { + Dictionary senzorTypes = new Dictionary(); + senzorTypes.Add("sensor", yamlLoader.getConfigurationData()["sensor"]); + + if (yamlLoader.getConfigurationData().ContainsKey("binary_sensor")) + senzorTypes.Add("binary_sensor", yamlLoader.getConfigurationData()["binary_sensor"]); + + return senzorTypes; + } + + private static Task getSenzorValue(KeyValuePair>> integration, Dictionary sensorDefinition) + { + string className = "HADC_REBORN.Class.Sensors."; + string sensorUniqueId = sensorDefinition["unique_id"]; + + foreach (var methodNameSegment in integration.Key.Split("_")) + { + className += methodNameSegment[0].ToString().ToUpper() + methodNameSegment.Substring(1); + } + + Type SensorTypeClass = Type.GetType(className); + if (SensorTypeClass == null) + { + App.log.writeLine(className + " Class Not Found"); + throw new Exception(className + " Class Not Found"); + } + + MethodInfo method = SensorTypeClass.GetMethod("GetValue"); + if (method == null) + { + App.log.writeLine("GetValue Method Not Found on " + className); + throw new Exception("GetValue Method Not Found on " + className); + } + + ParameterInfo[] pars = method.GetParameters(); + List parameters = new List(); + + foreach (ParameterInfo p in pars) + { + if (p == null) + { + continue; + } + + if (p.Name != null && sensorDefinition.ContainsKey(p.Name)) + { + parameters.Insert(p.Position, sensorDefinition[p.Name]); + } + else if (p.IsOptional && p.DefaultValue != null) + { + parameters.Insert(p.Position, p.DefaultValue); + } + } + + return Task.Run(() => { + return method.Invoke(null, parameters.ToArray()).ToString(); + }); + } + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/ApiDevice.cs b/HADC_REBORN/Class/HomeAssistant/Objects/ApiDevice.cs new file mode 100644 index 0000000..6ef27f0 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/ApiDevice.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + public class ApiDevice + { + public string device_id = null; + public string app_id = null; + public string app_name = null; + public string app_version = null; + public string device_name = null; + public string manufacturer = null; + public string model = null; + public string os_name = null; + public string os_version = null; + public bool supports_encryption = true; + + public object? app_data = null; + + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/ApiLocation.cs b/HADC_REBORN/Class/HomeAssistant/Objects/ApiLocation.cs new file mode 100644 index 0000000..430bffa --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/ApiLocation.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + class ApiLocation + { + public double[] gps = { 0.00, 0.00 }; + public int gps_accuracy = 0; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/ApiRequest.cs b/HADC_REBORN/Class/HomeAssistant/Objects/ApiRequest.cs new file mode 100644 index 0000000..6c5a619 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/ApiRequest.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + class ApiRequest + { + public string type = ""; + public object data = new object(); + + public void SetData(object payloadData) + { + data = payloadData; + } + + public void SetType(string payloadType) + { + type = payloadType; + } + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/ApiSensor.cs b/HADC_REBORN/Class/HomeAssistant/Objects/ApiSensor.cs new file mode 100644 index 0000000..6c6b0c5 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/ApiSensor.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + public class ApiSensor + { + public string device_class = null; + public string icon = "mdi:bug"; + public string name = null; + public dynamic state = null; + public string type = "sensor"; + public string unique_id = null; + public string unit_of_measurement = null; + public string state_class = null; + public string entity_category = null; + public bool? disabled = null; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/ApiWebhookRegistrationRequest.cs b/HADC_REBORN/Class/HomeAssistant/Objects/ApiWebhookRegistrationRequest.cs new file mode 100644 index 0000000..63d8f98 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/ApiWebhookRegistrationRequest.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + class ApiWebhookRegistrationRequest + { + public string token = ""; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/WsAuth.cs b/HADC_REBORN/Class/HomeAssistant/Objects/WsAuth.cs new file mode 100644 index 0000000..d35ee38 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/WsAuth.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + internal class WsAuth + { + public string type = "auth"; + public string access_token = ""; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/WsPing.cs b/HADC_REBORN/Class/HomeAssistant/Objects/WsPing.cs new file mode 100644 index 0000000..2f66fae --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/WsPing.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + internal class WsPing + { + public int id = 1; + public string type = "ping"; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/Objects/WsRequest.cs b/HADC_REBORN/Class/HomeAssistant/Objects/WsRequest.cs new file mode 100644 index 0000000..12e18c1 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/Objects/WsRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + internal class WsRequest + { + public int id = 1; + public string type = ""; + public string webhook_id = null; + public bool support_confirm = false; + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/WsConnector.cs b/HADC_REBORN/Class/HomeAssistant/WsConnector.cs new file mode 100644 index 0000000..96152f0 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/WsConnector.cs @@ -0,0 +1,190 @@ +using HADC_REBORN.Class.HomeAssistant.Objects; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http.Headers; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Net.WebSockets; +using System.Runtime.InteropServices; +using System.Windows.Threading; + +namespace HADC_REBORN.Class.HomeAssistant +{ + public class WsConnector + { + private string url; + private string token; + private string webhook; + + private ClientWebSocket socket = new ClientWebSocket(); + private byte[] buffer = new byte[2048]; + private int interactions = 1; + + private int failedAttempts = 0; + private bool isConnected = false; + + private Task recieveLoopObject; + private static DispatcherTimer updatePingTimer = new DispatcherTimer(); + + public WsConnector(string apiUrl, string apiToken, string webhookId) + { + url = apiUrl.TrimEnd('/'); + token = apiToken; + webhook = webhookId; + } + + public void register() + { + Uri wsAddress = new Uri(url + "/api/websocket"); + ManualResetEvent exitEvent = new ManualResetEvent(false); + socket = new ClientWebSocket(); + socket.Options.KeepAliveInterval = TimeSpan.Zero; + + socket.ConnectAsync(wsAddress, CancellationToken.None).Wait(); + App.log.writeLine("[WS] ADDRESS:" + wsAddress); + + JObject initialization = RecieveAsync(); + if (initialization["type"].ToString() != "auth_required") + { + throw new Exception("Server dont asked for authorization"); + } + + WsAuth authObj = new WsAuth() { }; + authObj.access_token = token; + + JObject registration = sendAndRecieveAsync(authObj); + if (registration["type"].ToString() != "auth_ok") + { + throw new Exception("Server dont accepted authorization"); + } + + WsRequest subscribeObj = new WsRequest { }; + subscribeObj.id = interactions; + subscribeObj.webhook_id = webhook; + subscribeObj.type = "mobile_app/push_notification_channel"; + + JObject subscription = sendAndRecieveAsync(subscribeObj); + if (bool.Parse(subscription["success"].ToString()) != true) + { + throw new Exception("Server authorization failed"); + } + + isConnected = true; + } + + public void receive(Action callbackRecieveEvent) + { + var localBuffer = new ArraySegment(new byte[2048]); + bool error = false; + + do + { + try + { + WebSocketReceiveResult localResult; + using (var ms = new MemoryStream()) + { + do + { + Task receiveTask = socket.ReceiveAsync(localBuffer, CancellationToken.None); + receiveTask.Wait(); + + localResult = receiveTask.Result; + ms.Write(localBuffer.Array, localBuffer.Offset, localResult.Count); + } while (!localResult.EndOfMessage); + + if (localResult.MessageType == WebSocketMessageType.Close) + break; + + ms.Seek(0, SeekOrigin.Begin); + using (var reader = new StreamReader(ms, Encoding.UTF8)) + { + string jsonPayload = reader.ReadToEnd(); + if (callbackRecieveEvent != null) + { + callbackRecieveEvent.Invoke(JObject.Parse(jsonPayload)); + } + App.log.writeLine("[WS] Recieved in LOOP: " + jsonPayload); + } + } + } + catch (Exception e) + { + App.log.writeLine("[WS] ERROR: " + e.Message); + error = true; + } + } while (!error); + } + + public void ping() + { + WsPing pingObj = new WsPing { }; + pingObj.type = "ping"; + pingObj.id = interactions; + Send(pingObj); + } + + public void disconnect() + { + if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent || socket.State == WebSocketState.Aborted) + { + if (socket.State == WebSocketState.Aborted) + { + socket.Abort(); + } + else + { + socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + } + + if (socket != null) + { + socket.Dispose(); + socket = null; + } + } + } + + public bool connected() + { + return (isConnected && (failedAttempts < 5) && socket != null); + } + + private JObject sendAndRecieveAsync(dynamic payloadObj) + { + Send(payloadObj).Wait(); + + return RecieveAsync(); + } + + private JObject RecieveAsync() + { + WebSocketReceiveResult result = socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None).Result; + string JSONRecievedpayload = Encoding.UTF8.GetString(buffer, 0, result.Count); + + App.log.writeLine("[WS] RECIEVED:"); + App.log.writeLine(JSONRecievedpayload); + + return JObject.Parse(JSONRecievedpayload); + } + + private async Task Send(dynamic payloadObj) + { + string JSONPayload = JsonConvert.SerializeObject(payloadObj, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }).ToString(); + + App.log.writeLine("[WS] SEND:"); + App.log.writeLine(JSONPayload); + + ArraySegment BYTEPayload = new ArraySegment(Encoding.UTF8.GetBytes(JSONPayload)); + socket.SendAsync(BYTEPayload, WebSocketMessageType.Text, true, CancellationToken.None).Wait(); + + interactions = interactions + 1; + } + } +} diff --git a/HADC_REBORN/Class/HomeAssistant/WsWrapper.cs b/HADC_REBORN/Class/HomeAssistant/WsWrapper.cs new file mode 100644 index 0000000..38f3163 --- /dev/null +++ b/HADC_REBORN/Class/HomeAssistant/WsWrapper.cs @@ -0,0 +1,172 @@ +using HADC_REBORN.Class.Actions; +using HADC_REBORN.Class.Helpers; +using Microsoft.VisualBasic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Management; +using System.Net.Sockets; +using System.Net.WebSockets; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace HADC_REBORN.Class.HomeAssistant.Objects +{ + public class WsWrapper + { + private YamlLoader yamlLoader; + private WsConnector wsConnector; + + private BackgroundWorker wsWorkerRecieverer = new BackgroundWorker(); + private BackgroundWorker wsWorkerPinger = new BackgroundWorker(); + + private DispatcherTimer updatePingTimer = new DispatcherTimer(); + public WsWrapper(YamlLoader yamlLoaderDependency, WsConnector wsConnectorDependency) + { + yamlLoader = yamlLoaderDependency; + wsConnector = wsConnectorDependency; + } + + public void Connect() + { + + if (!wsConnector.connected()) + { + wsConnector.register(); + if (wsWorkerRecieverer.IsBusy) + { + throw new Exception("Already Registered !!!"); + } + + wsWorkerRecieverer.DoWork += wsWorkerReciever_DoWork; + wsWorkerRecieverer.RunWorkerCompleted += wsWorkerReciever_Completed; + + wsWorkerRecieverer.RunWorkerAsync(); + + wsWorkerPinger.DoWork += wsWorkePinger_DoWork; + + updatePingTimer.Interval = TimeSpan.FromMinutes(5); + updatePingTimer.Tick += UpdatePing_Tick; + updatePingTimer.Start(); + + App.log.writeLine("[WS] Ping Initialized"); + } + } + + private void wsWorkerReciever_Completed(object? sender, RunWorkerCompletedEventArgs e) + { + Disconnect(); + } + + public void Disconnect() + { + if (wsConnector.connected()) + { + App.log.writeLine("[WS] Disconecting"); + wsConnector.disconnect(); + App.log.writeLine("[WS] Disconected"); + } + if (updatePingTimer.IsEnabled) + { + App.log.writeLine("[WS] Ping stoping"); + updatePingTimer.Stop(); + App.log.writeLine("[WS] Ping Stopped"); + } + + } + + private void UpdatePing_Tick(object? sender, EventArgs e) + { + if (wsWorkerPinger.IsBusy != true) + { + wsWorkerPinger.RunWorkerAsync(); + } + } + + private void wsWorkePinger_DoWork(object? sender, DoWorkEventArgs e) + { + try + { + wsConnector.ping(); + } + catch (Exception ex) + { + App.log.writeLine("[WS] Ping Failed: " + ex.Message); + } + } + + public static void eventReceive(JObject eventData) + { + if (!eventData.ContainsKey("event")) + { + return; + } + + JObject eventPayloadData = eventData["event"].ToObject(); + if (eventPayloadData == null) + { + return; + } + + if (eventPayloadData.ContainsKey("message")) + { + App.log.writeLine("[WS] Event With Message"); + + string msg_text = eventPayloadData["message"].ToString(); + string msg_title = ""; + string msg_image = ""; + string msg_audio = ""; + + if (eventPayloadData.ContainsKey("title")) + { + msg_title = eventPayloadData["title"].ToString(); + } + + if (eventPayloadData.ContainsKey("data") && eventPayloadData["data"].ToObject().ContainsKey("image")) + { + msg_image = eventPayloadData["data"].ToObject()["image"].ToString(); + } + + if (eventPayloadData.ContainsKey("data") && eventPayloadData["data"].ToObject().ContainsKey("audio")) + { + msg_audio = eventPayloadData["data"].ToObject()["audio"].ToString(); + } + + Notification.Spawn(msg_text, msg_title, msg_image, msg_audio); + } + + if (eventPayloadData.ContainsKey("data") && eventPayloadData["data"].ToObject().ContainsKey("key")) + { + Keyboard.SendKey(eventData["data"].ToObject()["key"].ToString()); + } + } + + private void wsWorkerReciever_DoWork(object? sender, DoWorkEventArgs e) + { + try + { + wsConnector.receive(eventReceive); + } + catch (Exception) + { + + } + } + + public void restart() + { + do + { + Disconnect(); + } while (wsWorkerRecieverer.IsBusy); + Connect(); + } + } +} diff --git a/HADC_REBORN/Class/Sensors/ConsentStore.cs b/HADC_REBORN/Class/Sensors/ConsentStore.cs new file mode 100644 index 0000000..479871c --- /dev/null +++ b/HADC_REBORN/Class/Sensors/ConsentStore.cs @@ -0,0 +1,83 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class ConsentStore + { + public static bool GetValue(string consent_category) + { + string[] consentStores = { + @"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\" + consent_category + @"\" , + @"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\" + consent_category + @"\NonPackaged" + }; + + try + { + foreach (string path in consentStores) + { + using (var rootKey = Registry.CurrentUser.OpenSubKey(path)) + { + if (rootKey == null) + { + continue; + } + + foreach (var subKeyName in rootKey.GetSubKeyNames()) + { + using (var subKey = rootKey.OpenSubKey(subKeyName)) + { + if (!subKey.GetValueNames().Contains("LastUsedTimeStop")) + { + continue; + } + + var endTime = (long)subKey.GetValue("LastUsedTimeStop"); + if (endTime == 0) + { + return true; + } + } + } + } + + // Repeat the same search now under "LocalMachine" + using (var rootKey = Registry.LocalMachine.OpenSubKey(path)) + { + if (rootKey == null) + { + continue; + } + + foreach (var subKeyName in rootKey.GetSubKeyNames()) + { + using (var subKey = rootKey.OpenSubKey(subKeyName)) + { + if (!subKey.GetValueNames().Contains("LastUsedTimeStop")) + { + continue; + } + + var endTime = (long)subKey.GetValue("LastUsedTimeStop"); + if (endTime == 0) + { + return true; + } + } + } + } + } + } + catch (Exception e) + { + App.log.writeLine("An error occurred while querying for CONSENT_STORE data: " + e.Message); + } + + return false; + } + } +} diff --git a/HADC_REBORN/Class/Sensors/CurrentWindow.cs b/HADC_REBORN/Class/Sensors/CurrentWindow.cs new file mode 100644 index 0000000..8497557 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/CurrentWindow.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class CurrentWindow + { + public static string GetValue() + { + try + { + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern int GetWindowText(IntPtr hwnd, StringBuilder ss, int count); + + IntPtr handle = IntPtr.Zero; + handle = GetForegroundWindow(); + + const int nChar = 256; + StringBuilder ss = new StringBuilder(nChar); + + ss = new StringBuilder(nChar); + string fullName = ""; + if (GetWindowText(handle, ss, nChar) > 0) + { + fullName = ss.ToString(); + } + + return fullName; + } + catch (Exception) + { + } + + return ""; + } + } +} diff --git a/HADC_REBORN/Class/Sensors/IpLocation.cs b/HADC_REBORN/Class/Sensors/IpLocation.cs new file mode 100644 index 0000000..48c22e9 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/IpLocation.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class IpLocation + { + public static string getData() + { + try + { + string info = new WebClient().DownloadString("http://ipinfo.io/"); + return (string)JObject.Parse(info)["loc"]; + } + catch (Exception) + { + return ""; + } + } + } +} diff --git a/HADC_REBORN/Class/Sensors/NetworkInterface.cs b/HADC_REBORN/Class/Sensors/NetworkInterface.cs new file mode 100644 index 0000000..1cbb0c5 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/NetworkInterface.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class NetworkInterface + { + public static string GetValue(string selector, string deselector = "") + { + try + { + var process = new Process + { + StartInfo = { + FileName = "netsh.exe", + Arguments = "wlan show interfaces", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + } + }; + + process.Start(); + string[] output = process.StandardOutput.ReadToEnd().ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + process.WaitForExit(); + + foreach (var item in output) + { + string[] line = item.Split(":"); + if (line.Length < 2) + { + continue; + } + + string outputResult = Regex.Replace(line[1].Trim(), @"\t|\n|\r", "").Trim(); + + if (!String.IsNullOrEmpty(deselector)) + { + if (item.Contains(selector) && !item.Contains(deselector)) + { + return outputResult; + } + } + + if (item.Contains(selector)) + { + return outputResult; + } + } + } + catch (Exception) { } + + return "Unknown"; + } + } +} diff --git a/HADC_REBORN/Class/Sensors/Ping.cs b/HADC_REBORN/Class/Sensors/Ping.cs new file mode 100644 index 0000000..9cd0275 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/Ping.cs @@ -0,0 +1,19 @@ +using HADC_REBORN.Class.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class Ping + { + public static bool GetValue(string host) + { + return Network.PingHost(host); + } + } +} diff --git a/HADC_REBORN/Class/Sensors/RestartPending.cs b/HADC_REBORN/Class/Sensors/RestartPending.cs new file mode 100644 index 0000000..56f4892 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/RestartPending.cs @@ -0,0 +1,53 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + internal class RestartPending + { + public static bool GetValue() + { + string[] registerStores = { + @"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" , + @"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" + }; + + try + { + + foreach (string path in registerStores) + { + using (var rootKey = Registry.LocalMachine.OpenSubKey(path)) + { + if (rootKey != null) + { + if (rootKey.GetSubKeyNames().Length > 0) + return true; + } + } + } + + using (var rootKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager")) + { + if (rootKey != null) + { + if (rootKey != null || !String.IsNullOrEmpty((string) rootKey.GetValue("PendingFileRenameOperations").ToString())) + { + return true; + } + } + } + } + catch (Exception e) + { + App.log.writeLine("An error occurred while querying for RESTART data: " + e.Message); + } + + return false; + } + } +} diff --git a/HADC_REBORN/Class/Sensors/Uptime.cs b/HADC_REBORN/Class/Sensors/Uptime.cs new file mode 100644 index 0000000..32c8b05 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/Uptime.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HADC_REBORN.Class.Sensors +{ + class Uptime + { + public static double GetValue() + { + try + { + [DllImport("kernel32")] + extern static ulong GetTickCount64(); + return TimeSpan.FromMilliseconds(GetTickCount64()).TotalHours; + } + catch (Exception) + { + } + + return 0; + } + } +} diff --git a/HADC_REBORN/Class/Sensors/Wmic.cs b/HADC_REBORN/Class/Sensors/Wmic.cs new file mode 100644 index 0000000..a889b30 --- /dev/null +++ b/HADC_REBORN/Class/Sensors/Wmic.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Management; +using System.Xml.Linq; + +namespace HADC_REBORN.Class.Sensors +{ + class Wmic + { + private static Dictionary wmicClasses = new Dictionary() { }; + + private static string[] getClasses(string wmic_namespace = @"root\\wmi") + { + if (!Wmic.wmicClasses.ContainsKey(wmic_namespace)) + { + Dictionary class_list = Wmic.wmicClasses; + class_list[wmic_namespace] = new string[] { }; + Wmic.wmicClasses = class_list; + } + + if (Wmic.wmicClasses[wmic_namespace].Length > 0) + { + return Wmic.wmicClasses[wmic_namespace]; + } + + ManagementClass nsClass = new ManagementClass(new ManagementScope(wmic_namespace), new ManagementPath("__namespace"), null); + string[] classList = new string[] { }; + + foreach (ManagementObject ns in nsClass.GetInstances()) + { + if (ns["Name"] == null) + { + continue; + } + + Array.Resize(ref classList, classList.Length + 1); + classList[(classList.Length - 1)] = (string) ns["Name"]; + } + + Wmic.wmicClasses[wmic_namespace] = classList; + App.log.writeLine(String.Join(",", classList)); + + return Wmic.wmicClasses[wmic_namespace]; + } + + public static string GetValue(string wmic_class, string wmic_selector, string wmic_namespace = @"root\\wmi", int wmic_iterator_index = 0) + { + //string result = getClasses(wmic_namespace).FirstOrDefault(x => x == wmic_class); + //if (result == null) + //{ + // App.log.writeLine("Wmic Class '" + wmic_class + "' not found! in namespace " + wmic_namespace); + // return ""; + //} + + App.log.writeLine("NAMESPACE " + wmic_namespace); + App.log.writeLine("SELECT " + wmic_selector + " FROM " + wmic_class + "[" + wmic_iterator_index + "]"); + App.log.writeLine("ITERATOR " + wmic_iterator_index); + + ManagementScope scope = new ManagementScope(wmic_namespace); + scope.Connect(); + + WqlObjectQuery query = new WqlObjectQuery(("SELECT " + wmic_selector + " FROM " + wmic_class)); + + ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query, null); + int i = 0; + + try + { + foreach (ManagementObject queryObj in searcher.Get()) + { + if (!Equals(queryObj, null) && queryObj != null && queryObj != new ManagementObject() { } && wmic_iterator_index == i) + { + if (queryObj.Properties.Count > 0 && queryObj?[wmic_selector]?.ToString() != null) //TODO: Eary Return + { + App.log.writeLine("OUTPUT: " + queryObj?[wmic_selector]?.ToString()); + return queryObj?[wmic_selector]?.ToString(); + } + } + + i++; + } + } + catch (ManagementException e) + { + App.log.writeLine("ERROR: " + e.Message); + } + + scope.Clone(); + return ""; + } + } +} diff --git a/HADC_REBORN/HADC_REBORN.csproj b/HADC_REBORN/HADC_REBORN.csproj new file mode 100644 index 0000000..a0762dd --- /dev/null +++ b/HADC_REBORN/HADC_REBORN.csproj @@ -0,0 +1,56 @@ + + + + WinExe + net8.0-windows10.0.17763.0 + enable + enable + true + True + Resource\ha_icon.ico + 1.0.0.0 + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resource.resx + + + + + ResXFileCodeGenerator + Resource.Designer.cs + + + diff --git a/HADC_REBORN/MainWindow.xaml b/HADC_REBORN/MainWindow.xaml new file mode 100644 index 0000000..5f2fba6 --- /dev/null +++ b/HADC_REBORN/MainWindow.xaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + +