public static async Task IncrementCounterAsync(string property) { int count = await HomeObject.GetValueAsync <int>(property).ConfigureAwait(false); count++; await HomeObject.SetValueAsync(property, count.ToString()).ConfigureAwait(false); }
internal static async Task <bool> CheckAccessTokenAsync(string token) { var accessToken = await HomeObject.GetValueAsync <string>("AccessToken").ConfigureAwait(false); var tokens = new List <string>(accessToken.Split(',')); return(-1 != tokens.IndexOf(token)); }
private static bool CheckStatus() { try { if (BackgroundTaskManager.Shutdown.IsCancellationRequested) { return(false); } if (IsClientConnected()) { return(true); } if (_homeObject == null) { using (HomeObjectLock.Lock()) { try { _homeObject = _sysClient.ConnectAsync(PremiseServerAddress)?.GetAwaiter().GetResult(); } catch (Exception ex) { Debug.WriteLine(ex.Message); _homeObject = null; } } if (_homeObject == null) { Debug.WriteLine("Cannot connect to Premise server!"); return(false); } } else { _sysClient.Disconnect(); using (homeObjectSubscriptionLock.Lock()) { _asyncEventSubscription = null; } using (subscriptionsLock.Lock()) { subscriptions.Clear(); _sysClient.Subscriptions.Clear(); } using (endpointsLock.Lock()) { endpoints.Clear(); } using (HomeObjectLock.Lock()) { _homeObject = null; try { _homeObject = _sysClient.ConnectAsync(PremiseServerAddress).GetAwaiter().GetResult(); } catch { _homeObject = null; return(false); } } } using (homeObjectSubscriptionLock.Lock()) { enableAsyncEvents = HomeObject.GetValueAsync <bool>("SendAsyncEventsToAlexa").GetAwaiter().GetResult(); } if (IsAsyncEventsEnabled) { Task.Run(SubscribeAllAsync); } SubScribeToHomeObjectEvents(); return(true); } catch (Exception ex) { _homeObject = null; subscriptions.Clear(); endpoints.Clear(); Debug.WriteLine(ex.Message); return(false); } }
private static void SendStateChangeReportsToAlexa() { const int eventID = 20; const string errorPrefix = "Proactive state update error:"; // This queue blocks in the enumerator so this is essentially an infinite loop. foreach (StateChangeReportWrapper item in stateReportQueue) { if (BackgroundTaskManager.Shutdown.IsCancellationRequested) { BackgroundTaskManager.Shutdown.ThrowIfCancellationRequested(); return; } WebRequest request; try { string expiry; using (asyncObjectsLock.Lock()) { expiry = HomeObject.GetValueAsync <string>("AlexaAsyncAuthorizationCodeExpiry").GetAwaiter() .GetResult(); } if (string.IsNullOrEmpty(expiry)) { Task.Run(async() => { await HomeObject.SetValueAsync("SendAsyncEventsToAlexa", "False").ConfigureAwait(false); }); NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix}: no PSU expiry datetime. PSUs are now disabled. Enable premise skill.", eventID + 1).GetAwaiter().GetResult(); } if (!DateTime.TryParse(expiry, out var expiryDateTime)) { Task.Run(async() => { await HomeObject.SetValueAsync("SendAsyncEventsToAlexa", "False").ConfigureAwait(false); }); NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Cannot parse expiry date. PSUs are now disabled. Enable premise skill.", eventID + 1).GetAwaiter().GetResult(); continue; } // refresh auth token if expired if (DateTime.Compare(DateTime.UtcNow, expiryDateTime) >= 0) { RefreshAsyncToken(out string newToken); if (string.IsNullOrEmpty(newToken) ) // Error occurred during refresh - error logged in that method. { continue; } [email protected] = newToken; foreach (var queuedItem in stateReportQueue.InternalQueue) { [email protected] = newToken; } } request = WebRequest.Create(item.uri); request.Method = WebRequestMethods.Http.Post; request.ContentType = @"application/json"; request.ContentLength = item.Json.Length; Stream stream = request.GetRequestStream(); stream.Write(item.Bytes, 0, (int)request.ContentLength); stream.Close(); } catch (Exception ex) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix}: Unexpected error: {ex.Message}", eventID + 1) .GetAwaiter().GetResult(); continue; } try { using (HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse) { if (httpResponse == null || !(httpResponse.StatusCode == HttpStatusCode.OK || httpResponse.StatusCode == HttpStatusCode.Accepted)) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected status ({httpResponse?.StatusCode})", eventID + 2) .GetAwaiter().GetResult(); continue; } string responseString; using (Stream response = httpResponse.GetResponseStream()) { if (response == null) { NotifyErrorAsync(EventLogEntryType.Warning, $"{errorPrefix} Null response from request.", eventID + 3).GetAwaiter().GetResult(); continue; } StreamReader reader = new StreamReader(response, Encoding.UTF8); responseString = reader.ReadToEnd(); } IncrementCounterAsync("AlexaAsyncUpdateCount").GetAwaiter().GetResult(); Debug.WriteLine($"PSU Response Status ({httpResponse.StatusCode}) ContentLength: {httpResponse.ContentLength} Content: {responseString}"); Debug.WriteLine(item.Json); } } catch (WebException e) { using (WebResponse webresponse = e.Response) { HttpWebResponse httpResponse = (HttpWebResponse)webresponse; switch (httpResponse.StatusCode) { case HttpStatusCode.Unauthorized : // The skill is enabled, but the authentication token has expired. RefreshAsyncToken(out string newToken); if (string.IsNullOrEmpty(newToken) ) // Error occurred during refresh - error logged in that method. { continue; } [email protected] = newToken; foreach (var queuedItem in stateReportQueue.InternalQueue) { [email protected] = newToken; } stateReportQueue.Enqueue(item); continue; case HttpStatusCode.Forbidden : // The skill is disabled so disable sending Async events to Alexa Task.Run(async() => { await HomeObject.SetValueAsync("SendAsyncEventsToAlexa", "False").ConfigureAwait(false); }); NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Premise skill has been disabled. PSUs are now disabled. Enable premise skill.", eventID + 4).GetAwaiter().GetResult(); continue; case HttpStatusCode.BadRequest: NotifyErrorAsync(EventLogEntryType.Warning, $"{errorPrefix} The message contains invalid identifying information such as a invalid endpoint Id or correlation token. Message:\r\n {item.Json}", eventID + 5).GetAwaiter().GetResult(); continue; default: NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected status: {e.Message}", eventID + 6).GetAwaiter().GetResult(); continue; } } } catch (Exception ex) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected error: {ex.Message}", eventID + 7) .GetAwaiter().GetResult(); continue; } Thread.Sleep(100); // throttle per spec } }
public static void AlexaPropertyChanged(dynamic @params) { Subscription sub = (Subscription)@params; using (deDupeLock.Lock()) { // Premise can send multiple notifications for a single object, one for each // subscribed property that changes. The state report function here will capture // states of all properties, so the DeDupeDictionary prevents multiple state reports // from being sent for essentially the same event. if (DeDupeDictionary.ContainsKey(sub.sysObjectId)) { return; } DeDupeDictionary.Add(sub.sysObjectId, sub); } Task.Run(() => { // get the endpoint and endpoint capabilities Guid premiseId = new Guid(sub.sysObjectId); IPremiseObject endpoint = HomeObject.GetObjectAsync(premiseId.ToString("B")).GetAwaiter().GetResult(); if (!endpoint.IsValidObject()) { return; } DiscoveryEndpoint discoveryEndpoint = GetDiscoveryEndpointAsync(endpoint).GetAwaiter().GetResult(); if (discoveryEndpoint == null) { return; } // get the authorization code for the notification string authCode; using (asyncObjectsLock.Lock()) { authCode = (string)HomeObject.GetValueAsync("AlexaAsyncAuthorizationCode").GetAwaiter().GetResult(); } // build the change report AlexaChangeReport changeReport = new AlexaChangeReport(); [email protected] = Guid.NewGuid().ToString("D"); [email protected].@namespace = "Alexa"; [email protected] = "3"; [email protected] = "BearerToken"; [email protected] = authCode; [email protected] = premiseId.ToString("D").ToUpper(); [email protected] = discoveryEndpoint.cookie; [email protected] = "PHYSICAL_INTERACTION"; // get the device type and controller (e.g. AlexaAV, AlexaHVAC) IAlexaDeviceType deviceType = null; IAlexaController controller = null; List <AlexaProperty> relatedPropertyStates = null; bool hasScene = false; foreach (IAlexaController controllerToTest in Controllers.Values) { if (!controllerToTest.HasPremiseProperty(sub.propertyName)) { continue; } controller = controllerToTest; Type type = Type.GetType(controller.GetAssemblyTypeName()); if (type == null) { continue; } // found a controller, get an instance of the assembly deviceType = (IAlexaDeviceType)Activator.CreateInstance(type); // Determine if this deviceType supports the desired capability // note: This handles situation where the same property name is used by different // controllers. e.g. "brightness" is used in both ColorController and BrightnessController relatedPropertyStates = deviceType.FindRelatedProperties(endpoint, ""); foreach (AlexaProperty property in relatedPropertyStates) { // if so, this is the correct type if (property.@namespace == controller.GetNameSpace()) { break; } } } // ReSharper disable once ConditionIsAlwaysTrueOrFalse if ((deviceType == null) || (controller == null || relatedPropertyStates == null)) { return; } foreach (AlexaProperty prop in relatedPropertyStates) { if (prop.@namespace == "Alexa.SceneController") { hasScene = true; continue; } if (([email protected] == 0) && (prop.name == controller.MapPremisePropertyToAlexaProperty(sub.propertyName))) { [email protected](prop); } else { string propKey = prop.@namespace + "." + prop.name; changeReport.context.propertiesInternal.Add(propKey, prop); } } [email protected] = "ChangeReport"; // scenes are special case if (hasScene) { AlexaSetSceneController sceneController = new AlexaSetSceneController(endpoint); changeReport = sceneController.AlterChangeReport(changeReport); } StateChangeReportWrapper item = new StateChangeReportWrapper { ChangeReport = changeReport }; stateReportQueue.Enqueue(item); using (deDupeLock.Lock()) { DeDupeDictionary.Remove(sub.sysObjectId); } }); }
private static void RefreshAsyncToken(out string newToken) { const int eventID = 10; const string errorPrefix = "Refresh async token error:"; using (asyncObjectsLock.Lock()) { string previousToken; WebRequest refreshRequest; newToken = ""; try { refreshRequest = WebRequest.Create(AlexaEventTokenRefreshEndpoint); refreshRequest.Method = WebRequestMethods.Http.Post; refreshRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8"; previousToken = HomeObject.GetValueAsync <string>("AlexaAsyncAuthorizationCode").GetAwaiter().GetResult(); string refresh_token = HomeObject.GetValueAsync <string>("AlexaAsyncAuthorizationRefreshToken").GetAwaiter().GetResult(); string client_id = HomeObject.GetValueAsync <string>("AlexaAsyncAuthorizationClientId").GetAwaiter().GetResult(); string client_secret = HomeObject.GetValueAsync <string>("AlexaAsyncAuthorizationSecret").GetAwaiter().GetResult(); if (string.IsNullOrEmpty(refresh_token) || string.IsNullOrEmpty(client_id) || string.IsNullOrEmpty(client_secret)) { Task.Run(async() => { await HomeObject.SetValueAsync("SendAsyncEventsToAlexa", "False").ConfigureAwait(false); }); NotifyErrorAsync(EventLogEntryType.Warning, $"{errorPrefix} Alexa authorization token is missing. PSUs are now disabled. Re-enable Premise skill.", eventID + 1).GetAwaiter().GetResult(); return; } string refreshData = $"grant_type=refresh_token&refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}"; Stream stream = refreshRequest.GetRequestStream(); stream.Write(Encoding.UTF8.GetBytes(refreshData), 0, Encoding.UTF8.GetByteCount(refreshData)); stream.Close(); } catch (Exception e) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected error: {e.Message}", eventID + 2).GetAwaiter().GetResult(); return; } try { using (HttpWebResponse httpResponse = refreshRequest.GetResponse() as HttpWebResponse) { if (httpResponse == null || !(httpResponse.StatusCode == HttpStatusCode.OK || httpResponse.StatusCode == HttpStatusCode.Accepted)) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Null response or unexpected status: ({httpResponse?.StatusCode})", eventID + 3).GetAwaiter().GetResult(); return; } string responseString; using (Stream response = httpResponse.GetResponseStream()) { if (response == null) { NotifyErrorAsync(EventLogEntryType.Warning, $"{errorPrefix} Null response from Amazon.", eventID + 4).GetAwaiter().GetResult(); return; } StreamReader reader = new StreamReader(response, Encoding.UTF8); responseString = reader.ReadToEnd(); } JObject json = JObject.Parse(responseString); newToken = json["access_token"].ToString(); HomeObject.SetValueAsync("AlexaAsyncAuthorizationCode", newToken).GetAwaiter().GetResult(); HomeObject.SetValueAsync("AlexaAsyncAuthorizationRefreshToken", json["refresh_token"].ToString()).GetAwaiter().GetResult(); DateTime expiry = DateTime.UtcNow.AddSeconds((double)json["expires_in"]); HomeObject.SetValueAsync("AlexaAsyncAuthorizationCodeExpiry", expiry.ToString(CultureInfo.InvariantCulture)).GetAwaiter().GetResult(); Debug.WriteLine("async token refresh response:" + responseString); WriteToWindowsApplicationEventLog(EventLogEntryType.Information, $"Alexa async auth token successfully refreshed. Previous Token (hash):{previousToken.GetHashCode()} New Token (hash):{newToken.GetHashCode()}", eventID); } } catch (WebException e) { using (WebResponse webresponse = e.Response) { HttpWebResponse httpResponse = (HttpWebResponse)webresponse; if (httpResponse.StatusCode == HttpStatusCode.Unauthorized ) // skill has ben disabled so disable sending Async events to Alexa { Task.Run(async() => { await HomeObject.SetValueAsync("SendAsyncEventsToAlexa", "False").ConfigureAwait(false); }); string responseString; using (Stream response = e.Response.GetResponseStream()) { if (response == null) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Null response.", eventID + 5).GetAwaiter().GetResult(); return; } StreamReader reader = new StreamReader(response, Encoding.UTF8); responseString = reader.ReadToEnd(); } JObject json = JObject.Parse(responseString); string message = $"{errorPrefix} PSUs are disabled. ErrorInfo: {json["error"]} Description: {json["error_description"]} More info: {json["error_uri"]}"; NotifyErrorAsync(EventLogEntryType.Error, message, eventID + 6).GetAwaiter().GetResult(); return; } NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected error status ({httpResponse.StatusCode})", eventID + 7).GetAwaiter().GetResult(); } } catch (Exception e) { NotifyErrorAsync(EventLogEntryType.Error, $"{errorPrefix} Unexpected error: {e.Message}", eventID + 8).GetAwaiter().GetResult(); } } }