How can find all devices (in an Azure IoT-Hub) where reported and desired properties differ? - azure-iot-hub

I'm attempting to query an IoT-hub for devices where reported and desired properties differ. The purpose of this is to be able to notify users when devices fail to update for a given period of time. Running this query
SELECT * FROM c WHERE properties.desired != properties.reported
generates alot of false positives since both desired and reported contains a $metadata property with timestamps that always differ.
So to be clear, I want to list all devices where any of the "real" values (not metadata) differ from desired to reported.

The simply workaround to avoid the '$' properties for comparing between the desired and reported properties is to create a separate complex object within the desired and reported properties. This complex object will represent a state between the real and shadow device.
Example:
"Config": {
"abc": 123,
"status": "inprocess",
"battery": {
"level": 90
}
}
In this case, the query string for query all devices where their Config is different from desired to reported properties looks the following:
SELECT deviceId FROM devices WHERE is_defined(properties.desired.Config) and is_defined(properties.reported.Config) and properties.desired.Config != properties.reported.Config
UPDATE:
Another option (workaround) is using an Azure IoT Hub eventing for changes in the device twin. These notification changes can be routed to the custom endpoint e.g. Event Hub and consumed by EventHubTrigger function. The routing query:
is_object($body.properties.reported) OR is_object($body.properties.desired)
The function can easy obtained a device twin and comparing its properties such as desired and reported after their cleanup metadata parts.
The result of the comparing properties can be stored in the device twin tags, e.g.:
"tags": {
"Watchdog": {
"timestamp": "2019-08-12T14:24:36.1805155Z",
"status": "inprocess"
}
}
Note, that the tags property is not visible by device.
Once we have a watchdog status in the device tags, we can query devices for its status, e.g.:
"query": "SELECT deviceId FROM devices WHERE is_defined(devices.tags.Watchdog) and devices.tags.Watchdog.status='inprocess' "
The following code snippet shows an example of the function:
using Microsoft.Azure.Devices;
using Microsoft.Azure.EventHubs;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Text;
using System.Threading.Tasks;
namespace FunctionApp14
{
public static class Function2
{
static RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Environment.GetEnvironmentVariable("AzureIoTHubShariedAccessPolicy"));
[FunctionName("Function2")]
public static async Task Run([EventHubTrigger("%myTwinChanges%", Connection = "myTwinChangesEventHubConnectionString", ConsumerGroup = "local")]EventData message, ILogger log)
{
var msg = Encoding.UTF8.GetString(message.Body.Array);
log.LogInformation($"C# IoT Hub trigger function processed a message: {msg}");
if (message.SystemProperties["iothub-message-source"]?.ToString() == "twinChangeEvents")
{
var twinChnages = JsonConvert.DeserializeAnonymousType(msg, new { properties = new JObject() });
if (twinChnages?.properties != null)
{
// deviceId
var connectionDeviceId = message.SystemProperties["iothub-connection-device-id"].ToString();
// device twin
var twin = await registryManager.GetTwinAsync(connectionDeviceId);
// cleanup and compare the twin properties
twin.Properties.Desired.ClearMetadata();
twin.Properties.Reported.ClearMetadata();
var desired = JObject.Parse(twin.Properties.Desired.ToJson());
var reported = JObject.Parse(twin.Properties.Reported.ToJson());
var status = JToken.DeepEquals(desired, reported) ? "ok" : "inprocess";
log.LogWarning($"desired-reported status = {status}");
// put the state on the tags
var twinPatch = JsonConvert.SerializeObject(new { tags = new { Watchdog = new { timestamp = DateTime.UtcNow, status = status } } });
await registryManager.UpdateTwinAsync(connectionDeviceId, twinPatch, twin.ETag);
}
}
await Task.CompletedTask;
}
}
}

You might need to do this in code. I had a piece of code that I used for something similar, I put it in a GitHub repo for this question.
The code is pretty naive, it compares the string values of the properties (excluding the metadata). It should be pretty easy to change it to check the property keys/values if needed.

Related

I would like to intercept the MS Teams notifications about poor network quality, during calls

In order to diagnose a network condition I am trying to intercept (at least on Windows) the notifications about poor network quality that Teams pops-up sometimes, during a call.
For testing, I am using "clumsy", in order to generate iffiness in the network, while in a teams audiovideo call to my phone.
I was wondering if an API would provide a local interface to teams an allow me to subscribe to those events.
So far if found a type library in C:\Users\xxx\AppData\Local\Microsoft\TeamsPresenceAddin\Uc.tlb that looks promising because of the idl file that the oleviewer generates from it:
ModalityProperty in uc.tlb is an enum and contains these two values:
ucAVModalityAudioNetworkQuality = 0x300b001d,
ucAVModalityVideoNetworkQuality = 0x300b0021,
That enum is used by the interface _IAVModalityEvents and that looks to be fired by the coclass AVModality:
[
uuid(BA2BD6F3-7676-42E3-89C6-10CEB3F7E106),
helpstring("AVModality class defines the audio video modality.This class handles the events defined in the interface IAVModalityEvents."),
noncreatable,
custom(5047D0E3-86FD-4EB4-A500-AC4F5B4E17E1, "relns=Conversation.AudioVideo")
]
coclass AVModality {
[default] interface IAVModality;
interface IAVModality2;
**[default, source] dispinterface _IAVModalityEvents;**
};
Does anyone have an idea on how to connect to the running Teams instance (tried ROT but it doesn't seem to be registered) and how to enum modalities in order to get to the AVModality and subscribe to its events ?
Update 1:
I actually got some partial results with the Uc.tlb.
First I ran: TlbImp.exe Uc.tlb to create a C# assembly from it.
I then built the following C# app to intercept conversation changes:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Xml;
using UCCollaborationLib;
class Program
{
//Teams CLSID harvested from registry
// Take a looksie at: https://stackoverflow.com/questions/56865704/com-object-for-teams-client-microsoft-office-uc
[ComImport, Guid("00425F68-FFC1-445F-8EDF-EF78B84BA1C7")]
public class TeamsOfficeIntegration
{
}
static void Main(string[] args)
{
var version = "15.0.0.0";
IUCOfficeIntegration teamsOfficeIntegration = (IUCOfficeIntegration) new TeamsOfficeIntegration();
if (teamsOfficeIntegration == null) {
Console.WriteLine("Cannot instantiate UCOfficeIntegration for Teams");
return;
}
IClient teamsClient = (IClient) teamsOfficeIntegration.GetInterface(version, OIInterface.oiInterfaceILyncClient);
if (teamsClient == null) {
Console.WriteLine("Cannot retrieve Teams Client interface");
return;
}
var teamsConversationManager = teamsClient.ConversationManager;
if (teamsConversationManager == null) {
Console.WriteLine("Cannot retrieve Teams ConversationManager interface");
return;
}
// Handle Teams Conversation Collection events
teamsConversationManager.OnConversationAdded += (ConversationManager _eventSource, ConversationManagerEventData _eventData) => { Console.WriteLine("Conversation added");};
teamsConversationManager.OnConversationRemoved += (ConversationManager _eventSource, ConversationManagerEventData _eventData) => { Console.WriteLine("Conversation removed");};
//iterate SelfParticipant or Modalities -> iterate IAVModality -> IConnectionPoint
}
}
The good news is that, when Teams is stopped it tries to start it so it does CoCreateInstance on the Teams LocalServer so it should be able to interact with it. The bad news is that even if the Teams app starts or is being attached to the creation fails with the following entry in the EventViewer/AdministrativeEvents:
"The server {00425F68-FFC1-445F-8EDF-EF78B84BA1C7} did not register with DCOM within the required timeout."

UpdateReportedPropertiesAsync with complex types?

I'm trying to update some Azure IoT Device Twin properties like this:
static async void MainAsync()
{
DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(connectionString);
TwinCollection reportedProperties = new TwinCollection();
dynamic heatingModes = new[]
{
new { Id="OUT2", Name="Comfort" },
new { Id="OUT", Name="Away" },
};
reportedProperties["heatingMode"] = "AWAY";
reportedProperties["supportedHeatingModes"] = heatingModes;
await deviceClient.UpdateReportedPropertiesAsync(reportedProperties);
}
The above code does not work and none of the Device Twin properties are not updated.
If I comment out this line everything works fine and the heatingMode property is updated as expected:
reportedProperties["supportedHeatingModes"] = heatingModes;
I have also tried to use a regular (not dynamic) type for heatingModes, but it does not work either.
I also tried to manually serialize the object to JSON:
reportedProperties["supportedHeatingModes"] = JsonConvert.SerializeObject(heatingModes);
But, the resulting JSON was kind of ugly with escaped quotes:
Why doesn't the updating the supportedHeatingModes property work for objects based on complex types?
Any other workarounds?
Have a look at the MSDN document Understand and use device twins in IOT Hub , where is described:
All values in JSON objects can be of the following JSON types: boolean, number, string, object. Arrays are not allowed.

Genesys Platform : Get Call Details From Sip Server

I want to get Call Details from Genesys Platform SIP Server.
And Genesys Platform has Platform SDK for .NET .
Anybod has a SIMPLE sample code which shows how to get call details using Platform SDK for .NET [ C# ] from SIP Server?
Extra Notes:
Call Details : especially i wanted to get AgentId for a given call
and
From Sip Server : I am not sure if Sip Server is the best candiate to
take call details. So open to other suggestions/ alternatives
You can build a class that monitor DN actions. Also you watch specific DN or all DN depending what you had to done. If its all about the call, this is the best way to this.
Firstly, you must define a TServerProtocol, then you must connect via host,port and client info.
var endpoint = new Endpoint(host, port, config);
//Endpoint backupEndpoint = new Endpoint("", 0, config);
protocol = new TServerProtocol(endpoint)
{
ClientName = clientName
};
//Sync. way;
protocol.Open();
//Async way;
protocol.BeginOpen();
I always use async way to do this. I got my reason thou :) You can detect when connection open with event that provided by SDK.
protocol.Opened += new EventHandler(OnProtocolOpened);
protocol.Closed += new EventHandler(OnProtocolClosed);
protocol.Received += new EventHandler(OnMessageReceived);
protocol.Error += new EventHandler(OnProtocolError);
Here there is OnMessageReceived event. This event where the magic happens. You can track all of your call events and DN actions. If you go genesys support site. You'll gonna find a SDK reference manual. On that manual quiet easy to understand there lot of information about references and usage.
So in your case, you want agentid for a call. So you need EventEstablished to do this. You can use this in your recieve event;
var message = ((MessageEventArgs)e).Message;
// your event-handling code goes here
switch (message.Id)
{
case EventEstablished.MessageId:
var eventEstablished = message as EventEstablished;
var AgentID = eventEstablished.AgentID;
break;
}
You can lot of this with this usage. Like dialing, holding on a call inbound or outbound even you can detect internal calls and reporting that genesys platform don't.
I hope this is clear enough.
If you have access to routing strategy and you can edit it. You can add some code to strategy to send the details you need to some web server (for example) or to DB. We do such kind of stuff in our strategy. After successful routing block as a post routing strategy sends values of RTargetPlaceSelected and RTargetAgentSelected.
Try this:
>
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent
JirayuGetInteractionContent =
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent.Create();
JirayuGetInteractionContent.InteractionId = "004N4aEB63TK000P";
Genesyslab.Platform.Commons.Protocols.IMessage respondingEventY =
contactserverProtocol.Request(JirayuGetInteractionContent);
Genesyslab.Platform.Commons.Collections.KeyValueCollection keyValueCollection =
((Genesyslab.Platform.Contacts.Protocols.ContactServer.Events.EventGetInteractionContent)respondingEventY).InteractionAttributes.AllAttributes;
We are getting AgentID and Place as follows,
Step-1:
Create a Custome Command Class and Add Chain of command In ExtensionSampleModule class as follows,
class LogOnCommand : IElementOfCommand
{
readonly IObjectContainer container;
ILogger log;
ICommandManager commandManager;
public bool Execute(IDictionary<string, object> parameters, IProgressUpdater progress)
{
if (Application.Current.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
object result = Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new ExecuteDelegate(Execute), parameters, progress);
return (bool)result;
}
else
{
// Get the parameter
IAgent agent = parameters["EnterpriseAgent"] as IAgent;
IIdentity workMode = parameters["WorkMode"] as IIdentity;
IAgent agentManager = container.Resolve<IAgent>();
Genesyslab.Desktop.Modules.Core.Model.Agents.IPlace place = agentManager.Place;
if (place != null)
{
string Place = place.PlaceName;
}
else
log.Debug("Place object is null");
CfgPerson person = agentManager.ConfPerson;
if (person != null)
{
string AgentID = person.UserName;
log.DebugFormat("Place: {0} ", AgentID);
}
else
log.Debug("AgentID object is null");
}
}
}
// In ExtensionSampleModule
readonly ICommandManager commandManager;
commandManager.InsertCommandToChainOfCommandAfter("MediaVoiceLogOn", "LogOn", new
List<CommandActivator>() { new CommandActivator()
{ CommandType = typeof(LogOnCommand), Name = "OnEventLogOn" } });
enter code here
IInteractionVoice interaction = (IInteractionVoice)e.Value;
switch (interaction.EntrepriseLastInteractionEvent.Id)
{
case EventEstablished.MessageId:
var eventEstablished = interaction.EntrepriseLastInteractionEvent as EventEstablished;
var genesysCallUuid = eventEstablished.CallUuid;
var genesysAgentid = eventEstablished.AgentID;
.
.
.
.
break;
}

RavenDB IsOperationAllowedOnDocument not supported in Embedded Mode

RavenDB throws InvalidOperationException when IsOperationAllowedOnDocument is called using embedded mode.
I can see in the IsOperationAllowedOnDocument implementation a clause checking for calls in embedded mode.
namespace Raven.Client.Authorization
{
public static class AuthorizationClientExtensions
{
public static OperationAllowedResult[] IsOperationAllowedOnDocument(this ISyncAdvancedSessionOperation session, string userId, string operation, params string[] documentIds)
{
var serverClient = session.DatabaseCommands as ServerClient;
if (serverClient == null)
throw new InvalidOperationException("Cannot get whatever operation is allowed on document in embedded mode.");
Is there a workaround for this other than not using embedded mode?
Thanks for your time.
I encountered the same situation while writing some unit tests. The solution James provided worked; however, it resulted in having one code path for the unit test and another path for the production code, which defeated the purpose of the unit test. We were able to create a second document store and connect it to the first document store which allowed us to then access the authorization extension methods successfully. While this solution would probably not be good for production code (because creating Document Stores is expensive) it works nicely for unit tests. Here is a code sample:
using (var documentStore = new EmbeddableDocumentStore
{ RunInMemory = true,
UseEmbeddedHttpServer = true,
Configuration = {Port = EmbeddedModePort} })
{
documentStore.Initialize();
var url = documentStore.Configuration.ServerUrl;
using (var docStoreHttp = new DocumentStore {Url = url})
{
docStoreHttp.Initialize();
using (var session = docStoreHttp.OpenSession())
{
// now you can run code like:
// session.GetAuthorizationFor(),
// session.SetAuthorizationFor(),
// session.Advanced.IsOperationAllowedOnDocument(),
// etc...
}
}
}
There are couple of other items that should be mentioned:
The first document store needs to be run with the UseEmbeddedHttpServer set to true so that the second one can access it.
I created a constant for the Port so it would be used consistently and ensure use of a non reserved port.
I encountered this as well. Looking at the source, there's no way to do that operation as written. Not sure if there's some intrinsic reason why since I could easily replicate the functionality in my app by making a http request directly for the same info:
HttpClient http = new HttpClient();
http.BaseAddress = new Uri("http://localhost:8080");
var url = new StringBuilder("/authorization/IsAllowed/")
.Append(Uri.EscapeUriString(userid))
.Append("?operation=")
.Append(Uri.EscapeUriString(operation)
.Append("&id=").Append(Uri.EscapeUriString(entityid));
http.GetStringAsync(url.ToString()).ContinueWith((response) =>
{
var results = _session.Advanced.DocumentStore.Conventions.CreateSerializer()
.Deserialize<OperationAllowedResult[]>(
new RavenJTokenReader(RavenJToken.Parse(response.Result)));
}).Wait();

log4javascript - obtain history of messages programmatically?

I'm looking into using a javascript logging framework in my app.
I quite like the look of log4javascript (http://log4javascript.org/) but I have one requirement which I'm not sure that it satisfies.
I need to be able to ask the framework for all messages which have been logged.
Perhaps I could use an invisible InPageAppender (http://log4javascript.org/docs/manual.html#appenders) to log to a DOM element, then scrape out the messages from that DOM element - but that seems pretty heavy.
Perhaps I need to write my own "InMemoryAppender"?
There's an ArrayAppender used in log4javascript's unit tests that stores all log messages it receives in an array accessible via its logMessages property. Hopefully it should show up in the main distribution in the next version. Here's a standalone implementation:
var ArrayAppender = function(layout) {
if (layout) {
this.setLayout(layout);
}
this.logMessages = [];
};
ArrayAppender.prototype = new log4javascript.Appender();
ArrayAppender.prototype.layout = new log4javascript.NullLayout();
ArrayAppender.prototype.append = function(loggingEvent) {
var formattedMessage = this.getLayout().format(loggingEvent);
if (this.getLayout().ignoresThrowable()) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
this.logMessages.push(formattedMessage);
};
ArrayAppender.prototype.toString = function() {
return "[ArrayAppender]";
};
Example use:
var log = log4javascript.getLogger("main");
var appender = new ArrayAppender();
log.addAppender(appender);
log.debug("A message");
alert(appender.logMessages);