Skip to content

Commit

Permalink
Support for Remote API V2 #72
Browse files Browse the repository at this point in the history
  • Loading branch information
michielpost committed Jul 29, 2016
1 parent 0cb4aca commit 83f1736
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 56 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ It's not trivial to convert the light colors to a color system developers like t
## Remote API
There is also a Philips Hue Remote API. It allows you to send commands to a bridge over the internet. You can request access here: http://www.developers.meethue.com/content/remote-api
Q42.HueApi is compatible with the remote API.
You need an Access Token and a Bridge Id. Please refer to the Philips Hue API documentation on how to obtain them. This library does not have support for it yet. Pull Requests are welcome!
You need an Access Token and a Bridge Id. Please refer to the Philips Hue API documentation on how to obtain them.
**NOTE** Remote API support is untested. Testing and PRs are very much appreciated.

IRemoteHueClient remoteHueClient = new RemoteHueClient("access token");
remoteHueClient.Initialize("bridge id");
remoteHueClient.Initialize("bridge id", "whitelist_id");

After the setup, you can send normal commands to the remote API:

Expand Down
4 changes: 4 additions & 0 deletions src/Q42.HueApi.Tests/Q42.HueApi.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Q42.HueApi.ColorConverters\Q42.HueApi.ColorConverters.csproj">
<Project>{C7305601-B583-479D-9A08-D6DA0A63AB85}</Project>
<Name>Q42.HueApi.ColorConverters</Name>
</ProjectReference>
<ProjectReference Include="..\Q42.HueApi\Q42.HueApi.csproj">
<Project>{e59a16b4-6756-4576-ab63-6a9b2cf2892b}</Project>
<Name>Q42.HueApi</Name>
Expand Down
13 changes: 11 additions & 2 deletions src/Q42.HueApi.Tests/Remote/RemoteLightsTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Q42.HueApi.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -13,12 +14,20 @@ public class RemoteLightsTests : LightsTests
[TestInitialize]
public new void Initialize()
{
var remoteBridge = new RemoteHueClient("test");
//remoteBridge.Initialize("bridgeId");
IRemoteHueClient remoteBridge = new RemoteHueClient("test");
remoteBridge.Initialize("bridgeId", "key");

_client = remoteBridge;
}

[TestMethod]
public async Task RegisterBridgeTest()
{
await ((IRemoteHueClient)_client).RegisterAsync("1", "test");
var result = await _client.GetLightAsync("1");
Assert.AreEqual("test", result.Name);
}

[TestMethod]
public async Task SetLightNameTest()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Q42.HueApi.WinRT.Sample/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ private async void GetLightsAction()
private void RedAction()
{
LightCommand command = new LightCommand();
command.TurnOn().SetColor(new RGBColor("FF0000)"));
command.TurnOn().SetColor(new RGBColor("FF0000"));

_hueClient.SendCommandAsync(command);
}
Expand Down
39 changes: 29 additions & 10 deletions src/Q42.HueApi/HueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,45 @@ public partial class HueClient : IHueClient

private readonly int _parallelRequests = 5;

/// <summary>
/// Whitelist ID
/// </summary>
protected string _appKey;


/// <summary>
/// Indicates the HueClient is initialized with an AppKey
/// </summary>
public bool IsInitialized { get; protected set; }

protected HueClient()
{
protected virtual string ApiBase { get; private set; }

}
protected static HttpClient _httpClient;

protected virtual string ApiBase { get; private set; }
protected HueClient()
{

[ThreadStatic]
protected static HttpClient _httpClient;

public static HttpClient GetHttpClient()
}

/// <summary>
/// Initialize client with your app key
/// </summary>
/// <param name="appKey"></param>
public void Initialize(string appKey)
{
if (appKey == null)
throw new ArgumentNullException(nameof(appKey));

_appKey = appKey;

IsInitialized = true;
}

public static HttpClient GetHttpClient()
{
// return per-thread HttpClient
if (_httpClient == null)
_httpClient = new HttpClient();
if (_httpClient == null)
_httpClient = new HttpClient();

return _httpClient;
}
Expand Down
15 changes: 8 additions & 7 deletions src/Q42.HueApi/Interfaces/IHueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ namespace Q42.HueApi.Interfaces
/// </summary>
public interface IHueClient
{


/// <summary>
/// Asynchronously gets all lights registered with the bridge.
/// </summary>
/// <returns>An enumerable of <see cref="WhiteList"/>s registered with the bridge.</returns>
Task<IEnumerable<WhiteList>> GetWhiteListAsync();



/// <summary>
/// Asynchronously gets all lights registered with the bridge.
/// </summary>
/// <returns>An enumerable of <see cref="WhiteList"/>s registered with the bridge.</returns>
Task<IEnumerable<WhiteList>> GetWhiteListAsync();

/// <summary>
/// Get bridge info
Expand Down
22 changes: 11 additions & 11 deletions src/Q42.HueApi/Interfaces/ILocalHueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ namespace Q42.HueApi.Interfaces
/// </summary>
public interface ILocalHueClient : IHueClient
{
/// <summary>
/// Initialize the client with your app key
/// </summary>
/// <param name="appKey"></param>
void Initialize(string appKey);

/// <summary>
/// Register your <paramref name="appName"/> and <paramref name="appKey"/> at the Hue Bridge.
/// </summary>
Expand All @@ -29,11 +23,17 @@ public interface ILocalHueClient : IHueClient
/// <exception cref="ArgumentException"><paramref name="appName"/> or <paramref name="appKey"/> aren't long enough, are empty or contains spaces.</exception>
Task<string> RegisterAsync(string appName, string appKey);

/// <summary>
/// Check if there is a working connection with the bridge
/// </summary>
/// <returns></returns>
Task<bool> CheckConnection();
/// <summary>
/// Initialize the client with your app key
/// </summary>
/// <param name="appKey"></param>
void Initialize(string appKey);

/// <summary>
/// Check if there is a working connection with the bridge
/// </summary>
/// <returns></returns>
Task<bool> CheckConnection();


}
Expand Down
43 changes: 41 additions & 2 deletions src/Q42.HueApi/Interfaces/IRemoteHueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,50 @@

namespace Q42.HueApi.Interfaces
{
/// <summary>
/// Remote Hue Client responsible for interacting with the bridge using the remote API
/// </summary>
public interface IRemoteHueClient : IHueClient
{
/// <summary>
/// Untested
/// </summary>
/// <param name="clientId"></param>
/// <param name="state"></param>
/// <param name="deviceId"></param>
/// <param name="appId"></param>
/// <param name="deviceName"></param>
/// <param name="responseType"></param>
/// <returns></returns>
Task<string> Authorize(string clientId, string state, string deviceId, string appId, string deviceName = null, string responseType = "code");
void Initialize(string bridgeId);
Task<string> RefreshToken(string refreshToken, string clientId, string clientSecret);

/// <summary>
/// Initialize the client with a bridgeId and appKey (whitelist identifier)
/// </summary>
/// <param name="bridgeId"></param>
/// <param name="appKey"></param>
void Initialize(string bridgeId, string appKey);

/// <summary>
/// Registers bridge for remote communication. Returns appKey and Initialized the client with this appkey
/// </summary>
/// <param name="bridgeId"></param>
/// <returns></returns>
Task<string> RegisterAsync(string bridgeId, string appId);

/// <summary>
/// Set the accessToken for the RemoteHueClient
/// </summary>
/// <param name="accessToken"></param>
void SetRemoteAccessToken(string accessToken);

/// <summary>
/// Untested
/// </summary>
/// <param name="refreshToken"></param>
/// <param name="clientId"></param>
/// <param name="clientSecret"></param>
/// <returns></returns>
Task<string> RefreshToken(string refreshToken, string clientId, string clientSecret);
}
}
15 changes: 1 addition & 14 deletions src/Q42.HueApi/LocalHueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public partial class LocalHueClient : HueClient, ILocalHueClient

private readonly string _ip;

private string _appKey;


/// <summary>
Expand Down Expand Up @@ -86,19 +85,7 @@ public LocalHueClient(string ip, string appKey)
}


/// <summary>
/// Initialize client with your app key
/// </summary>
/// <param name="appKey"></param>
public void Initialize(string appKey)
{
if (appKey == null)
throw new ArgumentNullException(nameof(appKey));

_appKey = appKey;

IsInitialized = true;
}


}
}
51 changes: 50 additions & 1 deletion src/Q42.HueApi/RemoteHueClient-Authentication.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
Expand Down Expand Up @@ -65,5 +66,53 @@ public async Task<string> RefreshToken(string refreshToken, string clientId, str

return string.Empty;
}


public async Task<string> RegisterAsync(string bridgeId, string appId)
{
if (string.IsNullOrEmpty(bridgeId))
throw new ArgumentNullException(nameof(bridgeId));
if (string.IsNullOrEmpty(appId))
throw new ArgumentNullException(nameof(appId));

JObject obj = new JObject();
obj["linkbutton"] = true;

HttpClient client = HueClient.GetHttpClient();
var configResponse = await client.PutAsync(new Uri($"{_apiBase}{bridgeId}/0/config"), new StringContent(obj.ToString())).ConfigureAwait(false);

JObject bridge = new JObject();
bridge["devicetype"] = appId;

var response = await client.PostAsync(new Uri($"{_apiBase}{bridgeId}/"), new StringContent(bridge.ToString())).ConfigureAwait(false);
var stringResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);


JObject result;
try
{
JArray jresponse = JArray.Parse(stringResponse);
result = (JObject)jresponse.First;
}
catch
{
//Not an expected response. Return response as exception
throw new Exception(stringResponse);
}

JToken error;
if (result.TryGetValue("error", out error))
{
if (error["type"].Value<int>() == 101) // link button not pressed
throw new Exception("Link button not pressed");
else
throw new Exception(error["description"].Value<string>());
}

var key = result["success"]["username"].Value<string>();
Initialize(key);

return key;
}
}
}
14 changes: 8 additions & 6 deletions src/Q42.HueApi/RemoteHueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Q42.HueApi
{
public partial class RemoteHueClient : HueClient, IRemoteHueClient
{
private readonly string _apiBase = "https://api.meethue.com/v2/bridges/";
private static string _remoteAccessToken;
private string _bridgeId;

Expand All @@ -25,23 +27,23 @@ public void SetRemoteAccessToken(string accessToken)

var client = GetHttpClient();
if (!string.IsNullOrEmpty(_remoteAccessToken))
_httpClient.DefaultRequestHeaders.Add("access_token", _remoteAccessToken);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _remoteAccessToken);
else
_httpClient.DefaultRequestHeaders.Remove("access_token");
_httpClient.DefaultRequestHeaders.Authorization = null;
}

/// <summary>
/// Initialize client with your app key
/// </summary>
/// <param name="appKey"></param>
public void Initialize(string bridgeId)
public void Initialize(string bridgeId, string appKey)
{
if (bridgeId == null)
throw new ArgumentNullException(nameof(bridgeId));

_bridgeId = bridgeId;

IsInitialized = true;
Initialize(appKey);
}


Expand All @@ -53,7 +55,7 @@ public void Initialize(string bridgeId)
_httpClient = new HttpClient();

if (!string.IsNullOrEmpty(_remoteAccessToken))
_httpClient.DefaultRequestHeaders.Add("access_token", _remoteAccessToken);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _remoteAccessToken);
}

return _httpClient;
Expand All @@ -67,7 +69,7 @@ protected override string ApiBase
{
get
{
return string.Format("https://api.meethue.com/v1/bridges/{0}/", _bridgeId);
return $"{_apiBase}{_bridgeId}/{_appKey}/";
}
}
}
Expand Down

0 comments on commit 83f1736

Please sign in to comment.