├── HueTests ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── HueTests.csproj ├── Hue ├── Properties │ ├── Settings.settings │ ├── AssemblyInfo.cs │ └── Settings.Designer.cs ├── .gitattributes ├── UrlProvider.cs ├── app.config ├── HttpRestHelper.cs ├── HueBridgeLocator.cs ├── UPnP.cs ├── .gitignore ├── Hue.csproj ├── Contracts │ ├── Contracts.cs │ └── HueLight.cs ├── JSON │ └── DynamicJsonConverter.cs └── HueBridge.cs ├── .gitattributes ├── readme.md ├── Hue.sln └── .gitignore /HueTests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Hue/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /Hue/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /HueTests/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HueTests 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | var bridge = Hue.HueBridgeLocator.Locate(); 10 | Console.WriteLine((bridge == null) ? "Bridge was not found." : "Bridge found at " + bridge.IP); 11 | 12 | if (bridge != null) 13 | { 14 | bridge.PushButtonOnBridge += bridge_PushButtonOnBridge; 15 | 16 | bridge.FlashLights(); 17 | } 18 | 19 | Console.ReadKey(); 20 | } 21 | 22 | static void bridge_PushButtonOnBridge(object sender, EventArgs e) 23 | { 24 | Console.WriteLine("Please press the button on the bridge to register the application in the next minute."); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Hue/UrlProvider.cs: -------------------------------------------------------------------------------- 1 | using Hue.Properties; 2 | 3 | namespace Hue 4 | { 5 | public class UrlProvider 6 | { 7 | private string ip; 8 | 9 | public UrlProvider(string ip) 10 | { 11 | this.ip = ip; 12 | 13 | if (!this.ip.StartsWith("http://")) 14 | this.ip = "http://" + this.ip; 15 | 16 | this.ip = this.ip.TrimEnd('/'); 17 | this.ip = this.ip.Replace("/description.xml",""); 18 | } 19 | 20 | internal string GetStatusUrl() 21 | { 22 | return ip + "/api/" + Settings.Default.BridgeApiKey; 23 | } 24 | 25 | internal string GetRegisterUrl() 26 | { 27 | return ip + "/api"; 28 | } 29 | 30 | internal string GetLampUrl(string lightKey) 31 | { 32 | return ip + "/api/" + Settings.Default.BridgeApiKey + "/lights/" + lightKey + "/state"; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Philips Hue lights .Net C# wrapper library 2 | ## A wrapper for Philips Hue lights rest api for C# 4.5 3 | 4 | This is a library for the philips hue lights written in C#. 5 | 6 | Eample code: 7 | 8 | var hue = new HueBridge("192.168.1.113"); 9 | hue.FlashLights(); 10 | 11 | 12 | ## SSDP Locator 13 | 14 | Automatically find the Philips Hue bridge in a network like this: 15 | 16 | var bridge = Hue.HueBridgeLocator.Locate(); 17 | if (bridge != null) 18 | bridge.FlashLights(); 19 | 20 | ## Automatic registration 21 | 22 | To control the lights, the client needs to authenticate with the bridge. The client fires the PushButtonOnBridge event if it needs registration: 23 | 24 | bridge.PushButtonOnBridge += bridge_PushButtonOnBridge; 25 | ... 26 | 27 | static void bridge_PushButtonOnBridge(object sender, EventArgs e) 28 | { 29 | Console.WriteLine("Please press the button on the bridge to register the application in the next minute."); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Hue/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Hue/HttpRestHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Hue 7 | { 8 | /// 9 | /// Send Async REST Json Requests 10 | /// 11 | internal static class HttpRestHelper 12 | { 13 | public static async Task Post(string url, string body) 14 | { 15 | var result = await (new HttpClient()).PostAsync(url, new StringContent(body, Encoding.UTF8, "application/json")) 16 | .ContinueWith(response => response.Result.Content.ReadAsStringAsync()); 17 | string responseFromServer = result.Result; 18 | return responseFromServer; 19 | } 20 | 21 | public static async Task Put(string url, string body) 22 | { 23 | var result = await (new HttpClient()).PutAsync(url, new StringContent(body, Encoding.UTF8, "application/json")); 24 | if (result.IsSuccessStatusCode) 25 | { 26 | var r = await result.Content.ReadAsStringAsync(); 27 | if (r.Contains("error")) 28 | System.Diagnostics.Trace.Write(r); 29 | return r; 30 | } 31 | return ""; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Hue/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Hue")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Hue")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4e8a6584-a966-4214-8c53-65868072987a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HueTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HueTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HueTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8190baf3-be01-49e5-ba64-cd492f8de322")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Hue/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Hue.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string BridgeApiKey { 30 | get { 31 | return ((string)(this["BridgeApiKey"])); 32 | } 33 | set { 34 | this["BridgeApiKey"] = value; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Hue.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hue", "Hue\Hue.csproj", "{FA43AF0D-9F77-4695-849D-FD26367C9089}" 5 | EndProject 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{01F03A38-91B7-40CD-83F2-4024FC588850}" 7 | ProjectSection(SolutionItems) = preProject 8 | readme.md = readme.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HueTests", "HueTests\HueTests.csproj", "{F7445D45-3870-44D6-94D7-569DBE483B2B}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {FA43AF0D-9F77-4695-849D-FD26367C9089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {FA43AF0D-9F77-4695-849D-FD26367C9089}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {FA43AF0D-9F77-4695-849D-FD26367C9089}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {FA43AF0D-9F77-4695-849D-FD26367C9089}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {F7445D45-3870-44D6-94D7-569DBE483B2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {F7445D45-3870-44D6-94D7-569DBE483B2B}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {F7445D45-3870-44D6-94D7-569DBE483B2B}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {F7445D45-3870-44D6-94D7-569DBE483B2B}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Hue/HueBridgeLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace Hue 12 | { 13 | /// 14 | /// Locates the Philips Hue lights bridge using SSDP 15 | /// 16 | public static class HueBridgeLocator 17 | { 18 | public static HueBridge Locate() 19 | { 20 | //https://www.meethue.com/api/nupnp 21 | //return LocateAsync().Result; 22 | return new HueBridge("192.168.0.102"); 23 | } 24 | 25 | public static async Task LocateAsync() 26 | { 27 | if (UPnP.NAT.Discover()) 28 | { 29 | var endpoints = UPnP.NAT.DiscoveredEndpoints 30 | .Where(s => s.EndsWith("/description.xml")).ToList(); 31 | foreach (var endpoint in endpoints) 32 | { 33 | if (await IsHue(endpoint)) 34 | { 35 | var ip = endpoint.Replace("http://", "").Replace("/description.xml", ""); 36 | return new HueBridge(ip); 37 | } 38 | } 39 | return null; 40 | } 41 | return null; 42 | } 43 | 44 | // http://www.nerdblog.com/2012/10/a-day-with-philips-hue.html - description.xml retrieval 45 | private static async Task IsHue(string discoveryUrl) 46 | { 47 | var http = new HttpClient {Timeout = TimeSpan.FromMilliseconds(2000)}; 48 | try { 49 | var res = await http.GetStringAsync(discoveryUrl); 50 | if (!string.IsNullOrWhiteSpace(res)) 51 | { 52 | if (res.Contains("Philips hue bridge")) 53 | return true; 54 | } 55 | } catch 56 | { 57 | return false; 58 | } 59 | return false; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Hue/UPnP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Net.Sockets; 5 | using System.Net; 6 | 7 | namespace UPnP 8 | { 9 | public class NAT 10 | { 11 | static readonly TimeSpan timeout = TimeSpan.FromSeconds(3); 12 | 13 | public static List DiscoveredEndpoints = new List(); 14 | 15 | public static bool Discover() 16 | { 17 | var s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 18 | s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); 19 | 20 | string req = "M-SEARCH * HTTP/1.1\r\n" + 21 | "HOST: 239.255.255.250:1900\r\n" + 22 | "ST:upnp:rootdevice\r\n" + 23 | "MAN:\"ssdp:discover\"\r\n" + 24 | "MX:3\r\n\r\n"; 25 | 26 | var data = Encoding.ASCII.GetBytes(req); 27 | var ipe = new IPEndPoint(IPAddress.Broadcast, 1900); 28 | var buffer = new byte[0x1000]; 29 | 30 | var start = DateTime.Now; 31 | 32 | DiscoveredEndpoints = new List(); 33 | do 34 | { 35 | s.SendTo(data, ipe); 36 | 37 | int length = 0; 38 | do 39 | { 40 | if (s.Available == 0) 41 | break;// nothing more to read 42 | 43 | length = s.Receive(buffer); 44 | var resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); 45 | 46 | var location = resp.Substring(resp.ToLower().IndexOf("location:", System.StringComparison.Ordinal) + 9); 47 | resp = location.Substring(0, location.IndexOf("\r", System.StringComparison.Ordinal)).Trim(); 48 | 49 | if (!DiscoveredEndpoints.Contains(resp)) 50 | DiscoveredEndpoints.Add(resp); 51 | 52 | } 53 | while (length > 0); 54 | } 55 | while (DateTime.Now.Subtract(start) < timeout); // try for thee seconds 56 | 57 | if (DiscoveredEndpoints.Count != 0) 58 | return true; 59 | 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /Hue/.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /HueTests/HueTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F7445D45-3870-44D6-94D7-569DBE483B2B} 8 | Exe 9 | Properties 10 | HueTests 11 | HueTests 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {fa43af0d-9f77-4695-849d-fd26367c9089} 54 | Hue 55 | 56 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /Hue/Hue.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FA43AF0D-9F77-4695-849D-FD26367C9089} 8 | Library 9 | Properties 10 | Hue 11 | Hue 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | True 54 | True 55 | Settings.settings 56 | 57 | 58 | 59 | 60 | 61 | SettingsSingleFileGenerator 62 | Settings.Designer.cs 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /Hue/Contracts/Contracts.cs: -------------------------------------------------------------------------------- 1 | //using System.Xml.Serialization; 2 | 3 | //namespace Hue.Contracts 4 | //{ 5 | // /// 6 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 7 | // [XmlRoot(Namespace = "urn:schemas-upnp-org:device-1-0", IsNullable = false)] 8 | // public class root 9 | // { 10 | // /// 11 | // public rootSpecVersion specVersion { get; set; } 12 | 13 | // /// 14 | // public string URLBase { get; set; } 15 | 16 | // /// 17 | // public rootDevice device { get; set; } 18 | // } 19 | 20 | // /// 21 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 22 | // public class rootSpecVersion 23 | // { 24 | // /// 25 | // public byte major { get; set; } 26 | 27 | // /// 28 | // public byte minor { get; set; } 29 | // } 30 | 31 | // /// 32 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 33 | // public class rootDevice 34 | // { 35 | // /// 36 | // public string deviceType { get; set; } 37 | 38 | // /// 39 | // public string friendlyName { get; set; } 40 | 41 | // /// 42 | // public string manufacturer { get; set; } 43 | 44 | // /// 45 | // public string manufacturerURL { get; set; } 46 | 47 | // /// 48 | // public string modelDescription { get; set; } 49 | 50 | // /// 51 | // public string modelName { get; set; } 52 | 53 | // /// 54 | // public ulong modelNumber { get; set; } 55 | 56 | // /// 57 | // public string modelURL { get; set; } 58 | 59 | // /// 60 | // public uint serialNumber { get; set; } 61 | 62 | // /// 63 | // public string UDN { get; set; } 64 | 65 | // /// 66 | // public rootDeviceServiceList serviceList { get; set; } 67 | 68 | // /// 69 | // public string presentationURL { get; set; } 70 | 71 | // /// 72 | // [XmlArrayItem("icon", IsNullable = false)] 73 | // public rootDeviceIcon[] iconList { get; set; } 74 | // } 75 | 76 | // /// 77 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 78 | // public class rootDeviceServiceList 79 | // { 80 | // /// 81 | // public rootDeviceServiceListService service { get; set; } 82 | // } 83 | 84 | // /// 85 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 86 | // public class rootDeviceServiceListService 87 | // { 88 | // /// 89 | // public string serviceType { get; set; } 90 | 91 | // /// 92 | // public string serviceId { get; set; } 93 | 94 | // /// 95 | // public string controlURL { get; set; } 96 | 97 | // /// 98 | // public string eventSubURL { get; set; } 99 | 100 | // /// 101 | // public string SCPDURL { get; set; } 102 | // } 103 | 104 | // /// 105 | // [XmlType(AnonymousType = true, Namespace = "urn:schemas-upnp-org:device-1-0")] 106 | // public class rootDeviceIcon 107 | // { 108 | // /// 109 | // public string mimetype { get; set; } 110 | 111 | // /// 112 | // public byte height { get; set; } 113 | 114 | // /// 115 | // public byte width { get; set; } 116 | 117 | // /// 118 | // public byte depth { get; set; } 119 | 120 | // /// 121 | // public string url { get; set; } 122 | // } 123 | //} -------------------------------------------------------------------------------- /Hue/JSON/DynamicJsonConverter.cs: -------------------------------------------------------------------------------- 1 | namespace Hue.JSON 2 | { 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Collections.ObjectModel; 7 | using System.Dynamic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Web.Script.Serialization; 11 | 12 | public sealed class DynamicJsonConverter : JavaScriptConverter 13 | { 14 | public static dynamic Parse(string json) 15 | { 16 | var serializer = new JavaScriptSerializer(); 17 | serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); 18 | 19 | dynamic obj = serializer.Deserialize(json, typeof(object)); 20 | return obj; 21 | } 22 | 23 | public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) 24 | { 25 | if (dictionary == null) 26 | throw new ArgumentNullException("dictionary"); 27 | 28 | return type == typeof(object) ? new DynamicJsonObject(dictionary) : null; 29 | } 30 | 31 | public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | public override IEnumerable SupportedTypes 37 | { 38 | get { return new ReadOnlyCollection(new List(new[] { typeof(object) })); } 39 | } 40 | 41 | #region Nested type: DynamicJsonObject 42 | 43 | private sealed class DynamicJsonObject : DynamicObject 44 | { 45 | private readonly IDictionary _dictionary; 46 | 47 | public DynamicJsonObject(IDictionary dictionary) 48 | { 49 | if (dictionary == null) 50 | throw new ArgumentNullException("dictionary"); 51 | _dictionary = dictionary; 52 | } 53 | 54 | public override string ToString() 55 | { 56 | var sb = new StringBuilder("{"); 57 | ToString(sb); 58 | return sb.ToString(); 59 | } 60 | 61 | private void ToString(StringBuilder sb) 62 | { 63 | var firstInDictionary = true; 64 | foreach (var pair in _dictionary) 65 | { 66 | if (!firstInDictionary) 67 | sb.Append(","); 68 | firstInDictionary = false; 69 | var value = pair.Value; 70 | var name = pair.Key; 71 | if (value is string) 72 | { 73 | sb.AppendFormat("{0}:\"{1}\"", name, value); 74 | } 75 | else if (value is IDictionary) 76 | { 77 | new DynamicJsonObject((IDictionary)value).ToString(sb); 78 | } 79 | else if (value is ArrayList) 80 | { 81 | sb.Append(name + ":["); 82 | var firstInArray = true; 83 | foreach (var arrayValue in (ArrayList)value) 84 | { 85 | if (!firstInArray) 86 | sb.Append(","); 87 | firstInArray = false; 88 | if (arrayValue is IDictionary) 89 | new DynamicJsonObject((IDictionary)arrayValue).ToString(sb); 90 | else if (arrayValue is string) 91 | sb.AppendFormat("\"{0}\"", arrayValue); 92 | else 93 | sb.AppendFormat("{0}", arrayValue); 94 | 95 | } 96 | sb.Append("]"); 97 | } 98 | else 99 | { 100 | sb.AppendFormat("{0}:{1}", name, value); 101 | } 102 | } 103 | sb.Append("}"); 104 | } 105 | 106 | public override bool TryGetMember(GetMemberBinder binder, out object result) 107 | { 108 | if (!_dictionary.TryGetValue(binder.Name, out result)) 109 | { 110 | // return null to avoid exception. caller can check for null this way... 111 | result = null; 112 | return true; 113 | } 114 | 115 | var dictionary = result as IDictionary; 116 | if (dictionary != null) 117 | { 118 | result = new DynamicJsonObject(dictionary); 119 | return true; 120 | } 121 | 122 | var arrayList = result as ArrayList; 123 | if (arrayList != null && arrayList.Count > 0) 124 | { 125 | if (arrayList[0] is IDictionary) 126 | result = new List(arrayList.Cast>().Select(x => new DynamicJsonObject(x))); 127 | else 128 | result = new List(arrayList.Cast()); 129 | } 130 | 131 | return true; 132 | } 133 | } 134 | 135 | #endregion 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Hue/Contracts/HueLight.cs: -------------------------------------------------------------------------------- 1 | namespace Hue.Contracts 2 | { 3 | public class HueLight 4 | { 5 | public HueLightState state { get; set; } 6 | public string type { get; set; } 7 | public string name { get; set; } 8 | public string modelid { get; set; } 9 | public long swversion { get; set; } 10 | 11 | public static HueLight Parse(dynamic d) 12 | { 13 | var instance = new HueLight(); 14 | instance.state = HueLightState.Parse(d["state"]); 15 | instance.type = d["type"]; 16 | instance.name = d["name"]; 17 | instance.modelid = d["modelid"]; 18 | instance.swversion = long.Parse(d["swversion"]); 19 | return instance; 20 | } 21 | } 22 | 23 | public class HueLightState 24 | { 25 | public bool on { get; set; } 26 | public int bri { get; set; } 27 | public int hue { get; set; } 28 | public int sat { get; set; } 29 | public decimal[] xy { get; set; } 30 | public int ct { get; set; } 31 | public string alert { get; set; } 32 | public string effect { get; set; } 33 | public string colormode { get; set; } 34 | public bool reachable { get; set; } 35 | 36 | public static HueLightState Parse(dynamic d) 37 | { 38 | var instance = new HueLightState(); 39 | 40 | instance.on = d["on"]; 41 | instance.bri = d["bri"]; 42 | instance.hue = d["hue"]; 43 | instance.sat = d["sat"]; 44 | instance.xy = new decimal[] { d["xy"][0], d["xy"][1] }; 45 | instance.alert = d["alert"]; 46 | instance.effect = d["effect"]; 47 | instance.colormode = d["colormode"]; 48 | instance.reachable = d["reachable"]; 49 | return instance; 50 | } 51 | 52 | public string AsJsonCommand() 53 | { 54 | return JsonDiff(new HueLightState()); 55 | } 56 | 57 | public string JsonDiff(HueLightState update) 58 | { 59 | var result = "{"; 60 | 61 | if (bri != update.bri) result += "\"bri\": " + update.bri + ", "; 62 | if (on != update.on) result += "\"on\": " + update.on.ToString().ToLowerInvariant() + ", "; 63 | //if (hue != update.hue) result += "\"hue\": " + update.hue + ", "; 64 | //if (sat != update.sat) result += "\"sat\": " + update.sat + ", "; 65 | //if (alert != update.alert) result += "\"alert\": " + update.alert + ", "; 66 | //instance.xy = new decimal[] { d["xy"][0], d["xy"][1] }; 67 | //instance.alert = d["alert"]; 68 | //instance.effect = d["effect"]; 69 | //instance.colormode = d["colormode"]; 70 | //instance.reachable = d["reachable"]; 71 | 72 | result = result.TrimEnd(',', ' '); 73 | result += "}"; 74 | 75 | if (result == "{}") 76 | return ""; 77 | return result; 78 | } 79 | 80 | internal static string JsonCommand(HueLightState hueLightState1, HueLightState hueLightState2) 81 | { 82 | return hueLightState1.JsonDiff(hueLightState2); 83 | } 84 | } 85 | } 86 | /* 87 | { 88 | "lights": { 89 | "1": { 90 | "state": { 91 | "on": true, 92 | "bri": 219, 93 | "hue": 33863, 94 | "sat": 49, 95 | "xy": [ 96 | 0.368, 97 | 0.3686 98 | ], 99 | "ct": 231, 100 | "alert": "none", 101 | "effect": "none", 102 | "colormode": "ct", 103 | "reachable": true 104 | }, 105 | "type": "Extended color light", 106 | "name": "Hue Lamp 1", 107 | "modelid": "LCT001", 108 | "swversion": "65003148", 109 | "pointsymbol": { 110 | "1": "none", 111 | "2": "none", 112 | "3": "none", 113 | "4": "none", 114 | "5": "none", 115 | "6": "none", 116 | "7": "none", 117 | "8": "none" 118 | } 119 | }, 120 | "2": { 121 | "state": { 122 | "on": true, 123 | "bri": 219, 124 | "hue": 33863, 125 | "sat": 49, 126 | "xy": [ 127 | 0.368, 128 | 0.3686 129 | ], 130 | "ct": 231, 131 | "alert": "none", 132 | "effect": "none", 133 | "colormode": "ct", 134 | "reachable": true 135 | }, 136 | "type": "Extended color light", 137 | "name": "Hue Lamp 2", 138 | "modelid": "LCT001", 139 | "swversion": "65003148", 140 | "pointsymbol": { 141 | "1": "none", 142 | "2": "none", 143 | "3": "none", 144 | "4": "none", 145 | "5": "none", 146 | "6": "none", 147 | "7": "none", 148 | "8": "none" 149 | } 150 | }, 151 | "3": { 152 | "state": { 153 | "on": true, 154 | "bri": 219, 155 | "hue": 33863, 156 | "sat": 49, 157 | "xy": [ 158 | 0.368, 159 | 0.3686 160 | ], 161 | "ct": 231, 162 | "alert": "none", 163 | "effect": "none", 164 | "colormode": "ct", 165 | "reachable": true 166 | }, 167 | "type": "Extended color light", 168 | "name": "Hue Lamp 3", 169 | "modelid": "LCT001", 170 | "swversion": "65003148", 171 | "pointsymbol": { 172 | "1": "none", 173 | "2": "none", 174 | "3": "none", 175 | "4": "none", 176 | "5": "none", 177 | "6": "none", 178 | "7": "none", 179 | "8": "none" 180 | } 181 | } 182 | }, 183 | "groups": { 184 | 185 | }, 186 | "config": { 187 | "name": "Philips hue", 188 | "mac": "00:17:88:09:62:40", 189 | "dhcp": true, 190 | "ipaddress": "192.168.0.113", 191 | "netmask": "255.255.255.0", 192 | "gateway": "192.168.0.1", 193 | "proxyaddress": "", 194 | "proxyport": 0, 195 | "UTC": "2012-11-15T03:08:08", 196 | "whitelist": { 197 | "c20aca42279b2898bb1ce2a470da6d64": { 198 | "last use date": "2012-11-14T23:41:41", 199 | "create date": "2012-11-07T03:00:06", 200 | "name": "Dmitri Sadakov\u2019s iPhone" 201 | }, 202 | "3b268b59109f63d7319c8f9d2a9d2edb": { 203 | "last use date": "2012-11-07T04:31:07", 204 | "create date": "2012-11-07T04:28:27", 205 | "name": "soapui" 206 | }, 207 | "2cb1ac173bc8aa7f2cae5a073a11fa8f": { 208 | "last use date": "2012-11-12T02:40:02", 209 | "create date": "2012-11-07T04:28:44", 210 | "name": "soapui" 211 | }, 212 | "26edc9a619306aa4b473ff22165751f": { 213 | "last use date": "2012-11-07T03:00:06", 214 | "create date": "2012-11-07T04:28:45", 215 | "name": "soapui" 216 | }, 217 | "343855a103b881726d398c68ac6333": { 218 | "last use date": "2012-11-07T21:56:21", 219 | "create date": "2012-11-07T19:22:04", 220 | "name": "python_hue" 221 | }, 222 | "b7a7e52143446771752ae6e1c69b0a3": { 223 | "last use date": "2012-11-07T21:56:21", 224 | "create date": "2012-11-13T04:31:39", 225 | "name": "WinHueApp" 226 | }, 227 | "1ec60546129895441850019217b1753f": { 228 | "last use date": "2012-11-07T21:56:21", 229 | "create date": "2012-11-15T01:35:34", 230 | "name": "winhueapp" 231 | }, 232 | "3fa667052b1747071bc90d137472433": { 233 | "last use date": "2012-11-07T21:56:21", 234 | "create date": "2012-11-15T02:20:50", 235 | "name": "winhueapp" 236 | }, 237 | "28fd5ecc3add810fa0aaaa41e1db8a7": { 238 | "last use date": "2012-11-07T21:56:21", 239 | "create date": "2012-11-15T02:23:55", 240 | "name": "winhueapp" 241 | }, 242 | "2c68b67e2d21c1c73e826292701a5eb": { 243 | "last use date": "2012-11-07T21:56:21", 244 | "create date": "2012-11-15T02:25:20", 245 | "name": "winhueapp" 246 | }, 247 | "15706f6e1d8b9167d32b2822fe99f8b": { 248 | "last use date": "2012-11-15T02:31:25", 249 | "create date": "2012-11-15T02:30:31", 250 | "name": "winhueapp" 251 | }, 252 | "1db73d762d1d8ea73c14bbda7fac1bb": { 253 | "last use date": "2012-11-07T21:56:21", 254 | "create date": "2012-11-15T03:00:44", 255 | "name": "winhueapp" 256 | }, 257 | "f86f8213eacc771e26889e19d01083": { 258 | "last use date": "2012-11-15T03:08:08", 259 | "create date": "2012-11-15T03:07:53", 260 | "name": "winhueapp" 261 | } 262 | }, 263 | "swversion": "01003542", 264 | "swupdate": { 265 | "updatestate": 0, 266 | "url": "", 267 | "text": "", 268 | "notify": false 269 | }, 270 | "linkbutton": true, 271 | "portalservices": true 272 | }, 273 | "schedules": { 274 | 275 | } 276 | } 277 | */ -------------------------------------------------------------------------------- /Hue/HueBridge.cs: -------------------------------------------------------------------------------- 1 | using Hue.Contracts; 2 | using Hue.JSON; 3 | using Hue.Properties; 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Net.Http; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Web.Script.Serialization; 10 | 11 | namespace Hue 12 | { 13 | public class HueBridge 14 | { 15 | public UrlProvider Urls; 16 | public ConcurrentDictionary Lights { get; set; } 17 | 18 | public event EventHandler PushButtonOnBridge; 19 | 20 | public readonly string IP; 21 | 22 | private readonly string appname = "winhueapp"; 23 | private Timer timer; 24 | private bool IsAuthenticated = false; 25 | 26 | public HueBridge(string ip) 27 | { 28 | Urls = new UrlProvider(ip); 29 | IP = ip; 30 | // not needed - clock for every 1 sec update status. 31 | //timer = new Timer(StatusCheckEvent, null, 0, 1000); 32 | InitializeRouter(); 33 | } 34 | 35 | public async Task InitializeRouter() 36 | { 37 | if (!string.IsNullOrEmpty(Settings.Default.BridgeApiKey)) 38 | { 39 | TryUpdateLights(); 40 | if (IsAuthenticated) return true; 41 | } 42 | 43 | return await Register(); 44 | } 45 | 46 | private void StatusCheckEvent(object state) 47 | { 48 | // read state of lamps 49 | if (IsAuthenticated) 50 | TryUpdateLights(); 51 | } 52 | 53 | private void TryUpdateLights() 54 | { 55 | var url = Urls.GetStatusUrl(); 56 | var statusResponse = new HttpClient().GetStringAsync(url).Result; 57 | 58 | // error response: 59 | //[{"error":{"type":1,"address":"/lights","description":"unauthorized user"}}] 60 | if (!statusResponse.Contains("unauthorized user")) 61 | { 62 | ParseLights(statusResponse); 63 | //{"lights":{"1":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 1", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }},"2":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 2", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }},"3":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 3", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }}},"groups":{},"config":{"name": "Philips hue","mac": "00:17:88:09:62:40","dhcp": true,"ipaddress": "192.168.0.113","netmask": "255.255.255.0","gateway": "192.168.0.1","proxyaddress": "","proxyport": 0,"UTC": "2012-11-15T03:08:08","whitelist":{"c20aca42279b2898bb1ce2a470da6d64":{"last use date": "2012-11-14T23:41:41","create date": "2012-11-07T03:00:06","name": "Dmitri Sadakov’s iPhone"},"3b268b59109f63d7319c8f9d2a9d2edb":{"last use date": "2012-11-07T04:31:07","create date": "2012-11-07T04:28:27","name": "soapui"},"2cb1ac173bc8aa7f2cae5a073a11fa8f":{"last use date": "2012-11-12T02:40:02","create date": "2012-11-07T04:28:44","name": "soapui"},"26edc9a619306aa4b473ff22165751f":{"last use date": "2012-11-07T03:00:06","create date": "2012-11-07T04:28:45","name": "soapui"},"343855a103b881726d398c68ac6333":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-07T19:22:04","name": "python_hue"},"b7a7e52143446771752ae6e1c69b0a3":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-13T04:31:39","name": "WinHueApp"},"1ec60546129895441850019217b1753f":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T01:35:34","name": "winhueapp"},"3fa667052b1747071bc90d137472433":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:20:50","name": "winhueapp"},"28fd5ecc3add810fa0aaaa41e1db8a7":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:23:55","name": "winhueapp"},"2c68b67e2d21c1c73e826292701a5eb":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:25:20","name": "winhueapp"},"15706f6e1d8b9167d32b2822fe99f8b":{"last use date": "2012-11-15T02:31:25","create date": "2012-11-15T02:30:31","name": "winhueapp"},"1db73d762d1d8ea73c14bbda7fac1bb":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T03:00:44","name": "winhueapp"},"f86f8213eacc771e26889e19d01083":{"last use date": "2012-11-15T03:08:08","create date": "2012-11-15T03:07:53","name": "winhueapp"}},"swversion": "01003542","swupdate":{"updatestate":0,"url":"","text":"","notify": false},"linkbutton": true,"portalservices": true},"schedules":{}} 64 | // /lights: {"1":{"name": "Hue Lamp 1"},"2":{"name": "Hue Lamp 2"},"3":{"name": "Hue Lamp 3"}} 65 | IsAuthenticated = true; 66 | } 67 | } 68 | 69 | private void ParseLights(string json) 70 | { 71 | //{"lights":{"1":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 1", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }},"2":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 2", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }},"3":{"state": {"on":true,"bri":219,"hue":33863,"sat":49,"xy":[0.3680,0.3686],"ct":231,"alert":"none","effect":"none","colormode":"ct","reachable":true}, "type": "Extended color light", "name": "Hue Lamp 3", "modelid": "LCT001", "swversion": "65003148", "pointsymbol": { "1":"none", "2":"none", "3":"none", "4":"none", "5":"none", "6":"none", "7":"none", "8":"none" }}},"groups":{},"config":{"name": "Philips hue","mac": "00:17:88:09:62:40","dhcp": true,"ipaddress": "192.168.0.113","netmask": "255.255.255.0","gateway": "192.168.0.1","proxyaddress": "","proxyport": 0,"UTC": "2012-11-15T03:08:08","whitelist":{"c20aca42279b2898bb1ce2a470da6d64":{"last use date": "2012-11-14T23:41:41","create date": "2012-11-07T03:00:06","name": "Dmitri Sadakov’s iPhone"},"3b268b59109f63d7319c8f9d2a9d2edb":{"last use date": "2012-11-07T04:31:07","create date": "2012-11-07T04:28:27","name": "soapui"},"2cb1ac173bc8aa7f2cae5a073a11fa8f":{"last use date": "2012-11-12T02:40:02","create date": "2012-11-07T04:28:44","name": "soapui"},"26edc9a619306aa4b473ff22165751f":{"last use date": "2012-11-07T03:00:06","create date": "2012-11-07T04:28:45","name": "soapui"},"343855a103b881726d398c68ac6333":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-07T19:22:04","name": "python_hue"},"b7a7e52143446771752ae6e1c69b0a3":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-13T04:31:39","name": "WinHueApp"},"1ec60546129895441850019217b1753f":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T01:35:34","name": "winhueapp"},"3fa667052b1747071bc90d137472433":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:20:50","name": "winhueapp"},"28fd5ecc3add810fa0aaaa41e1db8a7":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:23:55","name": "winhueapp"},"2c68b67e2d21c1c73e826292701a5eb":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T02:25:20","name": "winhueapp"},"15706f6e1d8b9167d32b2822fe99f8b":{"last use date": "2012-11-15T02:31:25","create date": "2012-11-15T02:30:31","name": "winhueapp"},"1db73d762d1d8ea73c14bbda7fac1bb":{"last use date": "2012-11-07T21:56:21","create date": "2012-11-15T03:00:44","name": "winhueapp"},"f86f8213eacc771e26889e19d01083":{"last use date": "2012-11-15T03:08:08","create date": "2012-11-15T03:07:53","name": "winhueapp"}},"swversion": "01003542","swupdate":{"updatestate":0,"url":"","text":"","notify": false},"linkbutton": true,"portalservices": true},"schedules":{}} 72 | 73 | var jss = new JavaScriptSerializer(); 74 | var d = jss.Deserialize(json); 75 | var lights = d["lights"]; 76 | 77 | Lights = new ConcurrentDictionary(); 78 | foreach (var light in lights) 79 | { 80 | Lights.TryAdd(light.Key, HueLight.Parse(light.Value)); 81 | } 82 | } 83 | 84 | private async Task Register() 85 | { 86 | var retryCount = 0; 87 | const int retryMax = 60; 88 | const int pauseMilliseconds = 1000; 89 | 90 | while (retryCount < retryMax) // wait a minute, check each second 91 | { 92 | var body = "{\"devicetype\":\"" + appname + "\"}"; 93 | var responseFromServer = await HttpRestHelper.Post(Urls.GetRegisterUrl(), body); 94 | 95 | if (responseFromServer.Contains("link button not pressed")) 96 | { 97 | //responseFromServer = "[{\"error\":{\"type\":7,\"address\":\"/username\",\"description\":\"invalid value, winhueapp, for parameter, username\"}},{\"error\":{\"type\":101,\"address\":\"\",\"description\":\"link button not pressed\"}}]" 98 | // link button not pressed, inform on first try only 99 | if (retryCount == 0 && PushButtonOnBridge != null) 100 | PushButtonOnBridge(this, null); 101 | 102 | Thread.Sleep(pauseMilliseconds); // sleep for a second, then retry 103 | retryCount++; 104 | } 105 | else 106 | { 107 | dynamic obj = DynamicJsonConverter.Parse(responseFromServer); 108 | // sample response: [{"error":{"type":7,"address":"/username","description":"invalid value, WinHueApp, for parameter, username"}},{"success":{"username":"b7a7e52143446771752ae6e1c69b0a3"}}] 109 | 110 | string key = ((dynamic[])obj)[0].success.username; 111 | 112 | if (!string.IsNullOrWhiteSpace(key)) 113 | { 114 | Settings.Default.BridgeApiKey = key; 115 | Settings.Default.Save(); 116 | 117 | IsAuthenticated = true; 118 | return true; 119 | } 120 | } 121 | } 122 | 123 | IsAuthenticated = false; 124 | return false; 125 | } 126 | 127 | private async void SetLightStatus(string lightKey, string json) 128 | { 129 | await HttpRestHelper.Put(Urls.GetLampUrl(lightKey), json); 130 | } 131 | 132 | public void AlertAllLights() 133 | { 134 | if (Lights != null && IsAuthenticated) 135 | { 136 | foreach (var light in Lights) 137 | SetLightStatus(light.Key, "{\"alert\": \"select\" }"); 138 | } 139 | } 140 | 141 | public void FlashLights() 142 | { 143 | if (Lights != null && IsAuthenticated) 144 | { 145 | foreach (var light in Lights) 146 | { 147 | SetLightStatus(light.Key, "{\"bri\": 254, \"on\": true }"); 148 | } 149 | Thread.Sleep(1000); 150 | foreach (var light in Lights) 151 | { 152 | SetLightStatus(light.Key, "{\"bri\": 0, \"on\": true }"); 153 | } 154 | } 155 | } 156 | 157 | public void TurnOffLights() 158 | { 159 | if (Lights != null && IsAuthenticated) 160 | { 161 | // push PUT request to /api/key/lights/1/state 162 | foreach (var light in Lights) 163 | { 164 | SetLightStatus(light.Key, "{\"on\": false }"); 165 | //HueLightState.JsonCommand(new HueLightState{ on = true }, new HueLightState() { on = false })); 166 | } 167 | } 168 | } 169 | } 170 | } --------------------------------------------------------------------------------