From 66f5b7ee0a897ab24d7ab506f23ce5f6c2b7bf8d Mon Sep 17 00:00:00 2001 From: Chris Kaczor Date: Mon, 26 Mar 2018 16:22:22 -0400 Subject: [PATCH] Initial commit to GitHub --- .gitignore | 157 +++++++++++++++++++++++++++++++ App.config | 24 +++++ App.xaml | 7 ++ App.xaml.cs | 34 +++++++ HomeStatusWindow.csproj | 116 +++++++++++++++++++++++ HomeStatusWindow.sln | 22 +++++ LICENSE.md | 21 +++++ Properties/AssemblyInfo.cs | 55 +++++++++++ Properties/Resources.Designer.cs | 136 ++++++++++++++++++++++++++ Properties/Resources.resx | 145 ++++++++++++++++++++++++++++ Properties/Settings.Designer.cs | 62 ++++++++++++ Properties/Settings.settings | 15 +++ README.md | 11 +++ Resources/Home.ico | Bin 0 -> 168695 bytes Status.cs | 12 +++ WindowSource.cs | 127 +++++++++++++++++++++++++ 16 files changed, 944 insertions(+) create mode 100644 .gitignore create mode 100644 App.config create mode 100644 App.xaml create mode 100644 App.xaml.cs create mode 100644 HomeStatusWindow.csproj create mode 100644 HomeStatusWindow.sln create mode 100644 LICENSE.md create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.settings create mode 100644 README.md create mode 100644 Resources/Home.ico create mode 100644 Status.cs create mode 100644 WindowSource.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e352b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,157 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store + +.vs/ \ No newline at end of file diff --git a/App.config b/App.config new file mode 100644 index 0000000..3d87d88 --- /dev/null +++ b/App.config @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + True + + + + + + http://chip + + + + \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..fad5dfd --- /dev/null +++ b/App.xaml @@ -0,0 +1,7 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..923c519 --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,34 @@ +using FloatingStatusWindowLibrary; +using HomeStatusWindow.Properties; +using System.Diagnostics; +using System.Windows; + +namespace HomeStatusWindow +{ + public partial class App + { + private WindowSource _windowSource; + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + StartManager.ManageAutoStart = true; + StartManager.AutoStartEnabled = !Debugger.IsAttached && Settings.Default.AutoStart; + StartManager.AutoStartChanged += (value => + { + Settings.Default.AutoStart = value; + Settings.Default.Save(); + }); + + _windowSource = new WindowSource(); + } + + protected override void OnExit(ExitEventArgs e) + { + _windowSource.Dispose(); + + base.OnExit(e); + } + } +} diff --git a/HomeStatusWindow.csproj b/HomeStatusWindow.csproj new file mode 100644 index 0000000..7b6dfb4 --- /dev/null +++ b/HomeStatusWindow.csproj @@ -0,0 +1,116 @@ + + + + + Debug + AnyCPU + {8F5381F5-9192-4607-93D0-E415AF6BDD00} + WinExe + Properties + HomeStatusWindow + HomeStatusWindow + v4.6 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + App.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + Designer + + + + + + + + 1.0.0.9 + + + 1.0.5 + + + + + + + + \ No newline at end of file diff --git a/HomeStatusWindow.sln b/HomeStatusWindow.sln new file mode 100644 index 0000000..aab33a2 --- /dev/null +++ b/HomeStatusWindow.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HomeStatusWindow", "HomeStatusWindow.csproj", "{8F5381F5-9192-4607-93D0-E415AF6BDD00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8F5381F5-9192-4607-93D0-E415AF6BDD00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F5381F5-9192-4607-93D0-E415AF6BDD00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F5381F5-9192-4607-93D0-E415AF6BDD00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F5381F5-9192-4607-93D0-E415AF6BDD00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..de8cb70 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Chris Kaczor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e8ef6fa --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("HomeStatusWindow")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("HomeStatusWindow")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[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) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..8e2b58e --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HomeStatusWindow.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // 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.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + 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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HomeStatusWindow.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon ApplicationIcon { + get { + object obj = ResourceManager.GetObject("ApplicationIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized string similar to <font color='red'>Disconnected</font>. + /// + internal static string Disconnected { + get { + return ResourceManager.GetString("Disconnected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dryer: {0}. + /// + internal static string DryerStatus { + get { + return ResourceManager.GetString("DryerStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Loading.... + /// + internal static string Loading { + get { + return ResourceManager.GetString("Loading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Home Monitor. + /// + internal static string Name { + get { + return ResourceManager.GetString("Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <font color='green'>Off</font>. + /// + internal static string Off { + get { + return ResourceManager.GetString("Off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <font color='yellow'>On</font>. + /// + internal static string On { + get { + return ResourceManager.GetString("On", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Washer: {0}. + /// + internal static string WasherStatus { + get { + return ResourceManager.GetString("WasherStatus", resourceCulture); + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..26837c6 --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Home.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + <font color='red'>Disconnected</font> + + + Dryer: {0} + + + Loading... + + + Home Monitor + + + <font color='green'>Off</font> + + + <font color='yellow'>On</font> + + + Washer: {0} + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..eaf1078 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HomeStatusWindow.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool AutoStart { + get { + return ((bool)(this["AutoStart"])); + } + set { + this["AutoStart"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WindowSettings { + get { + return ((string)(this["WindowSettings"])); + } + set { + this["WindowSettings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://chip")] + public string ServerAddress { + get { + return ((string)(this["ServerAddress"])); + } + set { + this["ServerAddress"] = value; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..c0c83cc --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,15 @@ + + + + + + True + + + + + + http://chip + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8d89cf --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# HomeStatusWindow + +A "floating" status window that shows the status of various household devices. + +## Authors + +* **Chris Kaczor** - *Initial work* - https://github.com/ckaczor - https://chriskaczor.com + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. diff --git a/Resources/Home.ico b/Resources/Home.ico new file mode 100644 index 0000000000000000000000000000000000000000..e68bd6fd4277cca39251207dd5434c72b4257d44 GIT binary patch literal 168695 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
~vMGIHo)fZbvyEf`KAZKtuKX+-DCX#8dKD%?h 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 literal 0 HcmV?d00001 diff --git a/Status.cs b/Status.cs new file mode 100644 index 0000000..6d4999a --- /dev/null +++ b/Status.cs @@ -0,0 +1,12 @@ +namespace HomeStatusWindow +{ + // ReSharper disable once ClassNeverInstantiated.Global + public class Status + { + // ReSharper disable once UnusedAutoPropertyAccessor.Global + public bool? Washer { get; set; } + + // ReSharper disable once UnusedAutoPropertyAccessor.Global + public bool? Dryer { get; set; } + } +} diff --git a/WindowSource.cs b/WindowSource.cs new file mode 100644 index 0000000..16e4a41 --- /dev/null +++ b/WindowSource.cs @@ -0,0 +1,127 @@ +using FloatingStatusWindowLibrary; +using HomeStatusWindow.Properties; +using Newtonsoft.Json; +using Quobject.SocketIoClientDotNet.Client; +using System; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace HomeStatusWindow +{ + public class WindowSource : IWindowSource, IDisposable + { + private readonly FloatingStatusWindow _floatingStatusWindow; + private readonly Dispatcher _dispatcher; + + private Socket _socket; + + internal WindowSource() + { + _dispatcher = Dispatcher.CurrentDispatcher; + + _floatingStatusWindow = new FloatingStatusWindow(this); + _floatingStatusWindow.SetText(Resources.Loading); + + Task.Factory.StartNew(Initialize); + } + + public void Dispose() + { + Terminate(); + + _floatingStatusWindow.Save(); + _floatingStatusWindow.Dispose(); + } + + public void ShowSettings() + { + } + + public void Refresh() + { + } + + public void ShowAbout() + { + } + + public string Name => Resources.Name; + + public bool HasSettingsMenu => false; + public bool HasRefreshMenu => false; + public bool HasAboutMenu => false; + + public System.Drawing.Icon Icon => Resources.ApplicationIcon; + + public string WindowSettings + { + get => Settings.Default.WindowSettings; + set + { + Settings.Default.WindowSettings = value; + Settings.Default.Save(); + } + } + + private readonly Status _fullStatus = new Status(); + + private void Initialize() + { + // Create the socket + _socket = IO.Socket(Settings.Default.ServerAddress); + + // Setup for status events + _socket.On("status", UpdateText); + + _socket.On(Socket.EVENT_CONNECT, () => _socket.Emit("getStatus")); + _socket.On(Socket.EVENT_DISCONNECT, () => SetText(Resources.Disconnected)); + } + + private void Terminate() + { + _socket?.Disconnect(); + } + + private void UpdateText(object data) + { + var json = (string)data; + + var status = JsonConvert.DeserializeObject(json); + + if (status.Dryer.HasValue) + _fullStatus.Dryer = status.Dryer; + + if (status.Washer.HasValue) + _fullStatus.Washer = status.Washer; + + var text = GetText(_fullStatus); + + SetText(text); + } + + private void SetText(string text) + { + // Update the window on the main thread + _dispatcher.Invoke(() => _floatingStatusWindow.SetText(text)); + } + + private static string GetText(Status status) + { + try + { + var output = new StringBuilder(); + + output.AppendFormat(Resources.DryerStatus, status.Dryer.GetValueOrDefault() ? Resources.On : Resources.Off); + output.AppendLine(); + output.AppendFormat(Resources.WasherStatus, status.Washer.GetValueOrDefault() ? Resources.On : Resources.Off); + + return output.ToString(); + } + catch (Exception ex) + { + return ex.Message; + } + } + } +}