Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions SenseApi/SenseApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<Description>Wrapper for the Sense (Energy Monitor) API</Description>
<AssemblyVersion>0.1.0.0</AssemblyVersion>
<FileVersion>0.1.0.0</FileVersion>
<Version>0.1.0.1-dev0139</Version>
<Version>0.1.0.1-dev0331</Version>
</PropertyGroup>

<ItemGroup>
Expand All @@ -24,7 +24,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
247 changes: 89 additions & 158 deletions SenseApi/SenseApiWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,31 @@ public class SenseApiWrapper
public MonitorStatus MonitorStatus { get; set; }
public List<Device> DeviceList { get; set; }
public static IConfigurationRoot Config { get; private set; }

private readonly string apiAddress;
private readonly JsonSerializerSettings serializerSettings;

public SenseApiWrapper()
public SenseApiWrapper(JsonSerializerSettings serializerSettings = null)
{
Config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();

apiAddress = Config["api-url"];

if (serializerSettings == null)
{
serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
}

this.serializerSettings = serializerSettings;
}

/// <summary>
/// Authenticate with the Sense API using your email and password
/// </summary>
Expand Down Expand Up @@ -69,15 +82,14 @@ public async Task<AuthorizationResponse> Authenticate(string email, string passw

var json = await response.Content.ReadAsStringAsync();

AuthorizationResponse = JsonConvert.DeserializeObject<AuthorizationResponse>(json);
AuthorizationResponse = JsonConvert.DeserializeObject<AuthorizationResponse>(json, serializerSettings);

// Update AccessToken and Monitor ID in appsettings.json file
var appSettingsJson = File.ReadAllText("appsettings.json");
dynamic jsonObj = JsonConvert.DeserializeObject(appSettingsJson);
dynamic jsonObj = JsonConvert.DeserializeObject(appSettingsJson, serializerSettings);
jsonObj["accesstoken"] = AuthorizationResponse.AccessToken;
jsonObj["monitor-ids"] = string.Join(",", AuthorizationResponse.Monitors.Select(x => x.Id));
string output =
JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
File.WriteAllText("appsettings.json", output);

Config.Reload();
Expand All @@ -94,22 +106,11 @@ public async Task<AuthorizationResponse> Authenticate(string email, string passw
/// <returns>Monitor Status Object</returns>
public async Task<MonitorStatus> GetMonitorStatus(int monitorId)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/monitors/{monitorId}/status";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();
var callData = $"{apiAddress}/app/monitors/{monitorId}/status";

MonitorStatus = JsonConvert.DeserializeObject<MonitorStatus>(json);
MonitorStatus = await ApiCallAsync<MonitorStatus>(callData);

return MonitorStatus;
}
return MonitorStatus;
}

/// <summary>
Expand All @@ -119,22 +120,11 @@ public async Task<MonitorStatus> GetMonitorStatus(int monitorId)
/// <returns>List of Devices with some details</returns>
public async Task<List<Device>> GetDeviceList(int monitorId)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/monitors/{monitorId}/devices";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();
var callData = $"{apiAddress}/app/monitors/{monitorId}/devices";

DeviceList = JsonConvert.DeserializeObject<List<Device>>(json);
DeviceList = await ApiCallAsync<List<Device>>(callData);

return DeviceList;
}
return DeviceList;
}

/// <summary>
Expand All @@ -146,32 +136,13 @@ public async Task<List<Device>> GetDeviceList(int monitorId)
/// <returns>Device Details for the specified device.</returns>
public async Task<DeviceDetails> GetDeviceDetails(int monitorId, string deviceId)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";
var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();

var deviceDetails = JsonConvert.DeserializeObject<DeviceDetails>(json);

if (DeviceList == null || DeviceList.Count <= 0) return deviceDetails;

var device = DeviceList.FirstOrDefault(x => x.Id == deviceId);
if (device == null) return deviceDetails;
var deviceDetails = await ApiCallAsync<DeviceDetails>(callData);

var pos = DeviceList.FindIndex(x => x.Id == deviceId);
DeviceList.RemoveAt(pos);
device = deviceDetails.Device;
DeviceList.Insert(pos, device);
UpdateDeviceList(deviceId, deviceDetails);

return deviceDetails;
}
return deviceDetails;
}

/// <summary>
Expand All @@ -183,33 +154,13 @@ public async Task<DeviceDetails> GetDeviceDetails(int monitorId, string deviceId
public async Task<DeviceDetails> GetAlwaysOnDetails(int monitorId)
{
const string deviceId = "always_on";
var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";

using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();

var deviceDetails = JsonConvert.DeserializeObject<DeviceDetails>(json);

if (DeviceList == null || DeviceList.Count <= 0) return deviceDetails;

var device = DeviceList.FirstOrDefault(x => x.Id == deviceId);
if (device == null) return deviceDetails;
var deviceDetails = await ApiCallAsync<DeviceDetails>(callData);

var pos = DeviceList.FindIndex(x => x.Id == deviceId);
DeviceList.RemoveAt(pos);
device = deviceDetails.Device;
DeviceList.Insert(pos, device);
UpdateDeviceList(deviceId, deviceDetails);

return deviceDetails;
}
return deviceDetails;
}

/// <summary>
Expand All @@ -221,33 +172,13 @@ public async Task<DeviceDetails> GetAlwaysOnDetails(int monitorId)
public async Task<DeviceDetails> GetOtherDetails(int monitorId)
{
const string deviceId = "unknown";
var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";

using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/monitors/{monitorId}/devices/{deviceId}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();

var deviceDetails = JsonConvert.DeserializeObject<DeviceDetails>(json);
var deviceDetails = await ApiCallAsync<DeviceDetails>(callData);

if (DeviceList == null || DeviceList.Count <= 0) return deviceDetails;
UpdateDeviceList(deviceId, deviceDetails);

var device = DeviceList.FirstOrDefault(x => x.Id == deviceId);
if (device == null) return deviceDetails;

var pos = DeviceList.FindIndex(x => x.Id == deviceId);
DeviceList.RemoveAt(pos);
device = deviceDetails.Device;
DeviceList.Insert(pos, device);

return deviceDetails;
}
return deviceDetails;
}

/// <summary>
Expand All @@ -260,22 +191,11 @@ public async Task<DeviceDetails> GetOtherDetails(int monitorId)
/// <returns>HistoryRecord object with a list sampling values for the monitor in the chosen granularity interval</returns>
public async Task<HistoryRecord> GetMonitorHistory(int monitorId, Granularity granularity, DateTime startDateTime, int sampleCount)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/history/usage?monitor_id={monitorId}&granularity={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ",CultureInfo.InvariantCulture)}&frames={sampleCount}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);
var callData = $"{apiAddress}/app/history/usage?monitor_id={monitorId}&granularity={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ", CultureInfo.InvariantCulture)}&frames={sampleCount}";

var json = await response.Content.ReadAsStringAsync();

var history = JsonConvert.DeserializeObject<HistoryRecord>(json);
var history = await ApiCallAsync<HistoryRecord>(callData);

return history;
}
return history;
}

/// <summary>
Expand All @@ -289,29 +209,17 @@ public async Task<HistoryRecord> GetMonitorHistory(int monitorId, Granularity gr
/// <returns>HistoryRecord object with a list sampling values for the monitor in the chosen granularity interval</returns>
public async Task<HistoryRecord> GetDeviceHistory(int monitorId, string deviceId, Granularity granularity, DateTime startDateTime, int sampleCount)
{
if (granularity != Granularity.Second && granularity != Granularity.Minute && granularity != Granularity.Hour &&
granularity != Granularity.Day)
if (granularity != Granularity.Second && granularity != Granularity.Minute &&
granularity != Granularity.Hour && granularity != Granularity.Day)
{
throw new InvalidEnumArgumentException($"{granularity.ToString()} is not a valid granularity for this call.");
}

using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData =
$"{apiAddress}/app/history/usage?monitor_id={monitorId}&device_id={deviceId}&granularity={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ", CultureInfo.InvariantCulture)}&frames={sampleCount}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();
var callData = $"{apiAddress}/app/history/usage?monitor_id={monitorId}&device_id={deviceId}&granularity={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ", CultureInfo.InvariantCulture)}&frames={sampleCount}";

var history = JsonConvert.DeserializeObject<HistoryRecord>(json);
var history = await ApiCallAsync<HistoryRecord>(callData);

return history;
}
return history;
}

/// <summary>
Expand All @@ -323,28 +231,17 @@ public async Task<HistoryRecord> GetDeviceHistory(int monitorId, string deviceId
/// <returns>TrendData object</returns>
public async Task<TrendData> GetUsageTrendData(int monitorId, Granularity granularity, DateTime startDateTime)
{
if (granularity != Granularity.Hour && granularity != Granularity.Day && granularity != Granularity.Month &&
granularity != Granularity.Year)
if (granularity != Granularity.Hour && granularity != Granularity.Day &&
granularity != Granularity.Month && granularity != Granularity.Year)
{
throw new InvalidEnumArgumentException($"{granularity.ToString()} is not a valid granularity for this call.");
}

using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/app/history/trends?monitor_id={monitorId}&device_id=usage&scale={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ", CultureInfo.InvariantCulture)}";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();
var callData = $"{apiAddress}/app/history/trends?monitor_id={monitorId}&device_id=usage&scale={granularity.ToString().ToLowerInvariant()}&start={startDateTime.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ", CultureInfo.InvariantCulture)}";

var trendData = JsonConvert.DeserializeObject<TrendData>(json);
var trendData = await ApiCallAsync<TrendData>(callData);

return trendData;
}
return trendData;
}

/// <summary>
Expand All @@ -353,26 +250,60 @@ public async Task<TrendData> GetUsageTrendData(int monitorId, Granularity granul
/// <param name="userId">User ID to get the timeline data for</param>
/// <returns>TimelineData object with the serialized json data</returns>
public async Task<TimelineData> GetTimelineData(int userId)
{
var callData = $"{apiAddress}/users/{userId}/timeline";

var timelineData = await ApiCallAsync<TimelineData>(callData);

return timelineData;
}

#region Private methods

/// <summary>
/// If a DeviceList is present this list is also updated with the additional details.
/// </summary>
/// <param name="deviceId">Device ID of the device</param>
/// <param name="deviceDetails">Device Details for the specified device.</param>
private void UpdateDeviceList(string deviceId, DeviceDetails deviceDetails)
{
if (DeviceList != null && DeviceList.Count > 0)
{
var device = DeviceList.FirstOrDefault(x => x.Id == deviceId);
if (device != null)
{
var pos = DeviceList.FindIndex(x => x.Id == deviceId);
DeviceList.RemoveAt(pos);
device = deviceDetails.Device;
DeviceList.Insert(pos, device);
}
}
}

/// <summary>
/// Perform a call to the Sense API
/// </summary>
/// <typeparam name="TR">The type of object that is returned</typeparam>
/// <param name="callData">The detailed call data of the API</param>
/// <returns>An instance of TR from the API call.</returns>
private async Task<TR> ApiCallAsync<TR>(string callData)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config["accesstoken"]);

var callData = $"{apiAddress}/users/{userId}/timeline";

var response = await httpClient.GetAsync(callData);

await CheckResponseStatus(response, httpClient, callData);

var json = await response.Content.ReadAsStringAsync();

var trendData = JsonConvert.DeserializeObject<TimelineData>(json);
var result = JsonConvert.DeserializeObject<TR>(json, serializerSettings);

return trendData;
return result;
}
}

#region Private methods
}

/// <summary>
/// Check the response status of the Http Call and if we get an Unauthorized response try
Expand Down