PAGES

24

Oct 16

Lync 2013 Conference Lifespan Chronicles



There’s a fun little quirk in Lync 2013  and Skype for Business that I recently did battle with, and came out the other side in one piece.  It has to do with conferences that are kept around for many hours being killed before I want them to be.  Why would you want to keep a conference alive for more than 10 hours, you ask?  Well, that’s ans an entirely different topic – I just figure out the “how”, not the “why”.

The scenario: Conferences hosted by an application die after an hour and a half, even when filled with participants.  Then, following a configuration change, Lync 2013 conferences continued to die after 10 hours.

Death after 1:30

In both Skype for Business and Lync 2013, we found that conferences were dying after an hour and a half.  We had both trusted and non-trusted participants in the conference, joins coming both from the application, and back-to-backed’d calls with external users (both PSTN and domain SIP addresses) – but it was still dying after an hour and a half.  OCS logging indicated this conference termination reason:

ms-diagnostics-public: 3114;reason="Conference Terminated - Enterprise User Absent"

Digging into the RTC database, I found that, in the [rtcdyn].[dbo].[Participant] table, all the joins from our application had a null EnterpriseId!  As it turns out, if you perform Impersonation on a conversation that gets joined to the conference, that conversation is no longer treated as coming from the organization – it’s treated by the Focus as an “Anonymous Participant”.  Therefore, the CsUserServicesConfiguration object’s AnonymousUserGracePeriod kicks in and kills the conference after the configured grace period – which defaults to “01:30:00″.

We found two ways to work around that issue.  The quick fix is to change that configuration setting to a large value.  Unfortunately, only one UserServicesConfiguration can be assigned per Site – which is less than ideal.  You don’t want external users using one of your idle conferences as their dedicated dial-in conferencing line for global calls!  The production fix was to simply have the application join the conference with a call that does not get impersonated – the Participant table then reflects that Participant record with a populated EnterpriseId, and the AnonymousGracePeriod no longer applies to the conference.

For Skype for Business, that was the end of the issue.

Death after 10 hours

For Lync 2013, however, it merely pushed out the conference’s death to 10 hours.  This time, OCS logging indicated this conference termination reason:

ms-diagnostics-public: 3115;reason="Conference Terminated - Inactivity"

Well, inactivity makes sense here – but how can we make the infrastructure think we’re still here?  I won’t bore you with the various combinations I tried, but here’s what I ended up with as the workaround:

  • To keep the Focus from thinking the conference is all Anonymous participants (the previously described issue), join an un-impersonated call to the conference
  • To keep the Focus thinking that the conference is active, join a new SIP address to the conference every 5 hours.  This CAN be an impersonated SIP address (ie sip:Sustainer{random number}@domain.com).  This join does need to stay in the conference (at least until the next randomly impersonated join); I don’t add this until the conference is 5 hours old so that it only affects very long conferences.
  • To keep the A/V MCU thinking that the conference is active, send a DTMF tone through the conference every 2 hours (using an existing call – like the un-impersonated call from the first bullet).  DTMF tones are handled by the MCU, and so update the rtcdyn database’s ActiveMcu table entry’s MCULastActivityTime whenever the tone gets sent.

While troubleshooting the issue, I found it was really helpful to be able to see the state of the conference from the RTCLocal database.  This is the query I ended up using, and a sample output:

select 
    --ActiveConference
    [ActiveConference].[ConfId]
    ,[Title]
    ,[LastEnterprisePartLeaveTime]
    ,'|' as '|'
    --ActiveMCU
    ,[LastActivityTime] MCULastActivityTime
    ,'|' as '|'
    --Conference
    ,[ProvisionTime]
    ,[ExpiryTime]
    ,[LastUpdateTime]
    ,[LastActivateTime]
    ,'|' as '|'
    --Participant
    ,[UserAtHost]
    ,[EnterpriseId]
    ,[Privilege]
    ,[JoinTime]
  FROM [rtcdyn].[dbo].[ActiveConference]
  left outer join [rtcdyn].[dbo].[Participant]
    on [Participant].ConfId = [ActiveConference].ConfId
  left outer join [rtcdyn].[dbo].[ActiveMcu]
    on ActiveMcu.ConfId = ActiveConference.ConfId
    and UserAtHost not like 'CAS%'
    and MediaId = 4
  left outer join [rtc].[dbo].[Conference]
    on Conference.ConfId = ActiveConference.ConfId
  where ActiveConference.ConfId in (select ConfId from [rtcdyn].[dbo].[Participant] where UserAtHost like '%dgardiner.test%')

RTC DB Results

I found this article quite helpful in pushing me in the right direction once I started discovering some of the error messages - http://masteringlync.com/2014/06/06/conferencing-activationdeactivation-deep-dive/

0 comments , permalink


17

Oct 16

Rituals and Chrome Extensions Part 2 – Return of the Icon



My CTA Bustracker extension took care of the commute to work in the morning – but I had another problem. My apartment is close enough to Wrigley Field that I’m affected by Cubs traffic – the drive from the lake to my apartment varies from 3 to 30 minutes, depending on the game schedule. There’s a fantastic site called isthereacubsgametoday.com that I found myself visiting as part of my daily rituals to see if I needed to change my plans accordingly. As you might expect, that led to…

Light bulb number 2 – a Chrome extension to tell me if there’s a Cubs game today

The wonderful website I mentioned earlier hosts an API to give basic information about if there is a cubs game today. It was a trivial matter to use that same API myself to have the information of whether or not there was a game, and if so, at what time.

Building on what I learned with the CTA Extension, I had a timer run once per hour to set the extension’s icon to be either a full-color or a greyscale Cubs logo, depending on if there was a home game today. If there was a home game, I also set the Badge overlay text to be the time of the game (as day games have a very different impact from night games). It was a simple, effective indicator that did precisely what I was looking for.

baseStates

And so I released the extension to the masses – and got an interesting (but in hindsight, shouldn’t have been unexpected) bit of feedback – what about fans of the Cubs? What about people who also care if there’s an away game, and want to know things like who they’re playing, what the score is, and who’s pitching? I already had an extension that showed some basic information about the cubs – why not make it useful to a wider audience of people?

So I began looking for somewhere I could source additional data about cubs games – and quickly found a number of APIs designed for providing large-scale professional sports data, all of which were ludicrously expensive (as they were designed for very different applications). Not to be defeated, I eventually stumbled across a blog post from 2012 about someone doing a much more involved application, but who found another source of data – the MLB scoreboard web page. MLB has a ‘scoreboard’ page available on their website, which polls an API every minute for updated game data, then displays that data for the user. The data that it pulls is just a big JSON blob about all the games going on for the requested day – that was something I could work with! I was concerned about the Terms of Service, but I decided that I was OK, as the requests would be coming from individuals’ browsers, the extension I was writing was free and non-commercial, and I was doing nothing in bulk (in fact, polling much less frequently than their application did, all only for ‘today’). At last, I had a source of all the data that a Cubs fan could want!

Once I had that, the rest was pretty straightforward. Request data from ‘today’, pull out the game that I cared about from the json blob, pull out the data I was interested in from the game (inning, opponent, location, pitcher, score, etc), and pop that into hover text for the icon I’d already made. If there was an ‘away game, I left the icon greyscale, but added the overlay text of the game time. While a game was in progress, I showed the score in the overlay Badge text, and updated data every couple minutes rather than just every hour to make things real-time. Once a game finished, I would switch the icon to a “W” or “L” flag to indicate if the Cubs won or lost the game. Everything reset each day, so the icon was a representation of ‘today’. In relatively short order, I had a whole new way that the application could be used!

FanStates

This new functionality went a bit against the initial purpose of the extension, so I needed a way to make it opt-in. An entirely custom Settings page like I did with the CTA tracker popup would be overkill for something simple like this, so I poked around in the context menu settings, and found that I could add a checkbox-style option to the context menu on only the icon (no other web pages) to toggle Fan Mode on and off, and my code would be notified accordingly so it could update. To persist it, I added an item to the Chrome synchronized storage store, so that whenever the browser was restarted or the user went to a different computer, the toggle would follow them.

Finally, I got a request from an ex-Chicagoan asking if the game times could be adjustable to different time zones. Following my context menu settings streak, I added 4 radio-style options to the icon’s context menu (for the 4 US time zones) so that a user could select the time zone for the extension, and used the Moment javascript library to handle time zone switching.

CubsSettings

I’m sure I’ll continue making tweaks and updates to those extensions, but it’s been fun diving deeper into the world of Chrome extensions, and for now, I’m pretty happy with how they turned out.

The Chrome extension is available for free in the Chrome Store

True to their original purpose, they’ve my daily rituals just a tad more efficient. Now – what’s next?

0 comments , permalink


10

Oct 16

Rituals and Chrome Extensions



Let’s talk about rituals.

Now I’m not talking about religious rituals, official services, or anything so solemn. I’m talking about the everyday type that you don’t even think about. For example, you probably have a standard set of activities you do every morning – get up, shower, eat breakfast, brush your teeth – fairly standard stuff. Mundane as those activities may seem, that repeated set of behaviors constitutes a ritual – one which you perform every morning.

Now, you’re probably asking yourself, “why is this guy talking about rituals involving toothbrushes on a blog about technology?” Admittedly, that’s a fair question.

One of my ever-present goals is to make life as efficient as possible – even (especially!) if that efficiency takes vastly more effort to implement than it saves. My latest additions to that basket of life-efficiency-enhancers have stemmed from identifying inefficiencies in my rituals, and finding ways to streamline them.

Every morning, I get up and go through my morning routine. Near the end (around 6:50), I check a CTA Bus Tracker application on my phone to see when the next bus will arrive. I want to leave the apartment when the next bus is 2-4 minutes away to minimize time waiting idly at the bus stop, while leaving enough of a buffer that I don’t have to run to catch it. If the bus is predicted to arrive outside that window, I proceed to mash the refresh button on my phone until it gives me a number I like. Unlocking my phone, opening the app, selecting the stop, requesting data, and then potentially repeating that procedure is not an efficient way to determine when I should leave.

Cue lightbulb number 1 – a Chrome extension to tell me when to leave.

I started my foray into Chrome extensions about 6 months ago with a simple one that added an option to the context menu to identify an Instagram photo’s URL. It was about as basic as it could be, with no interface elements besides the single context menu option – but it exposed me to what was possible with the SDK, and taught me the process to build and deploy something to the Chrome store. I had also toyed with the CTA API a while back while looking into building an Amazon Echo application for CTA bus tracking (which never panned out) – which gave me both pieces I needed to build an extension to tell me when to leave.

First, I planned out what the app should do. There would be 2 components – one for selecting the stop to track (the user would initiate tracking by clicking on one of a list of ‘pinned’ stops, and have the ability to add new pinned stops), and one for displaying the status of the selected stop. While tracking, I want to know how long until the next bus comes, how fresh the data is, what bus line I’m tracking, and when one is almost here.

For the first component, I needed a pretty big working area – there are well over 100 CTA routes I’d need to show. To support this, Chrome’s Extension SDK allows you to specify a web page that’s displayed as a ‘Popup’ when you click on the application’s icon. From that page, you can execute a javascript method on the background JS using the Chrome Extension SDK’s getBackgroundPage function – allowing the popup UI to call a method to start a timer on the background to update the icon

CtaPinnedStops

Building out the interface to select a stop was quite simple. The BusTracker API is really well designed; it’s built in such a way that you can get a list of all bus lines, directions, and stops – each based on the result of the previous. Once you have the stop ID, you can request ‘predictions’ about when the next bus will arrive there. It was simple to build out a series of screens to allow selecting a stop, then persisting the saved stop IDs to the Chrome synchronized storage using the Extension SDK.

CtaStopSelectionScreenshot 2016-10-07 14.31.47Screenshot 2016-10-07 14.31.51

For the second component, I knew that I wanted the totality of the interface required for actively tracking state to be shown through the icon at the top of the browser window – having to open a new pane or click on something after tracking has been started would defeat the whole purpose of the project. The challenge, then, was how to fit everything into a 19 pixels square area – a ridiculously small amount of space. For context, that’s roughly the amount of space that 4 letters on this page (in a 2×2 character square) would take up.

CtaAllLetters

Which, at actual size, looks like this

CtaAllLettersSmall

After much mucking about in a 19x19px box in Paint.NET, I came up with a design that covered what I needed. The background would be a bus, with a 13x7px windshield. Using a font that took just 3x5px per number/letter (based on http://www.sum-it.nl/en200351.html), I would then draw the bus line number inside the windshield of the background. The ‘freshness’ of the data (or the time until the next refresh) would be represented by 6 4.4px dots overlaid at the top of the icon, which fit perfectly into the 19px width, with 1px between each dot and on either side. When the bus got close, I would turn the background of the bus’s headlights green (time to go!) – and I would use the icon’s Badge text (generally used for notifications) to indicate the number of minutes until the next arrival.

Great! Now… how do I actually make that happen?

Back in the extension’s background javascript, the Extension SDK allows you to set the icon for the application one of two ways – either by referencing a static image file, or by building the icon in an HTML5 Canvas element, and requesting the icon be loaded from a section of that Canvas. Clearly, to support the notifications being inside the icon, I’d have to build it up using the latter.

To start, on the canvas, if the headlights were supposed to be green, I drew green squares in the spot where the headlights would end up. I then loaded in a background image of the bus, which had transparent headlights and background (but not windshield – we want a light background for the numbers). If the headlights needed to be green, the square I drew in initially would show through the transparent spots in the Bus image. I created image files for each digit and letter that was needed (all 3x5px), and named them appropriately. Based on the number of digits in the bus line (151, 22, etc), I calculated the pixel position of each character, and loaded the appropriate images onto the canvas in the appropriate spot. I then drew little circles along the top to indicate when data will be refreshed, and set the overlay text to the number of minutes until the next predicted arrival. Once that was all complete, I requested that the square of the Canvas into which I’d been drawing be loaded as the Extension’s icon – and that was that!

CtaIcon

Again, at actual size

CtaIcon

Select a pinned stop, and the icon will start refreshing automatically for 10 minutes, with all the information you need visible at a glance. Efficient!

The Chrome extension is available for free in the Chrome store

Efficiency number one – attained.  Stay tuned for part 2!

0 comments , permalink


29

Mar 16

Nuget Packages in ASP.NET Core 1.0



Dependencies are NuGet packages in ASP.NET Core 1.0, which we still can add to our project from the Package Manager Console or the NuGet Dialog, just like in previous versions.

However, ASP.NET Core 1.0 introduces the project.json file, which among other things, contains a list of all the NuGet packages that the project depends on, and allow us to add or remove dependencies directly from the file.

Let’s see a simple example.

Adding packages from the project.json file

Imagine we want to add some logging functionality to our application and we decide to install the log4net library for this purpose.

To do this, we just need to open the project.json file, which should be located in the root of the project, and add a new entry to the dependencies node. This new entry should specify the name and version of the package.

  "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
        "Microsoft.AspNet.Mvc": "6.0.0-beta3",
        "Microsoft.AspNet.StaticFiles": "1.0.0-beta3",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta3",
        "log4net": "2.0.3"
    }

One of the coolest things about this is that Visual Studio provides IntelliSense to help us to select the NuGet package that we want to install.

1

Once the package entry has been added, all we need to do is to save the file, and the package will automatically be added to the project as a reference.

2

We can either select a specific version of the package, or not specify any, in this case, we will always get the latest version.

  "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
        "Microsoft.AspNet.Mvc": "6.0.0-beta3",
        "Microsoft.AspNet.StaticFiles": "1.0.0-beta3",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta3",
        "log4net": ""
    }

And this is all we need to do. Keep in mind that this also works the other way around: if we want to delete a package from our project, remove the package entry from the project.json file.

Targeting multiple frameworks

As you have probably noticed, there are two different sections under the project references. These sections represent the different runtimes which the new ASP.NET is able to run on.

ASP.NET 5.0 is the full .NET CLR and ASP.NET Core 5.0 is the cloud-optimized Core CLR.

4

Adding a dependency to target only one of these runtimes is fairly easy too. The frameworks node of the project.json file contains a node for each of the runtimes: the node aspnet50 correspond to the full .NET CLR and the aspnetcore50 to the Core CLR.

If we only want to add the log4net package to the Core CLR, we just need to add a new dependency node under aspnetcore50, and then create a new package entry, just like we did before.

"frameworks": {
        "aspnet50": { },
        "aspnetcore50": {
            "dependencies": {
                "log4net": "2.0.3"
            }
        }
    }

After saving the file, we should be able to see that the dependency was only added to the Core CLR runtime.

6

Final Note

As we have seen, managing dependencies has become easier with ASP.NET Core 1.0. I like this new approach of adding and removing dependencies by editing a json file. It seems much simpler and cleaner than the approaches offered in previous versions of ASP.NET.

It also removes the need for using Visual Studio. The only tool we need is a regular text editor from which we can update the project.json file.

Comments Off , permalink


23

Mar 16

Bower Packages in ASP.NET Core 1.0



Bower is a Node.js tool to manage front-end dependencies. It’s a package manager just like NuGet, but for front-end libraries. With this tool, we can add things like jQuery, Angular or Bootstrap to our project.

In this post, I will explain how to use Bower to manage front-end dependencies in our ASP.NET Core 1.0 project.

Installing Node.js

To install Bower, we need to have Node.js installed on our machine. Visual Studio 2015 installs Node.js by default, so you may already probably have it on your computer. If not, you can download and install it from here.

To verify that Node.js is installed correctly, open the Command Prompt (or Powershell) and type the following command:

> Node

The output should indicate that we just opened the interactive node.js shell, from which we can start executing JavaScript code directly.

0

If for any reason, we get a _”node is not recognized as an internal or an external command”_ , make sure Node is added to the Windows PATH environment variable.

Installing Bower

Now that Node is installed correctly in our system let’s proceed to install Bower. We are going to do this with the help of NPM, a package manager for Node. With this tool, we can install things like Gulp, Grunt or in our case, Bower.

To install Bower enter the following command:

> npm install g bower

The ‘g’ stands for ‘global’, this option installs the package globally so that we can run it from everywhere as a command.

Managing front-end packages

If everything went correctly, we should now have Bower installed on our machine. It’s time to take advantage of this awesome tool and start adding our favorites front-end libraries to our project.

There are two main options to manage Bower packages in an ASP.NET Core 1.0 project: from the bower.json file or from the Command Prompt.

The bower.json file is the Bower manifest file, from which we can track all the packages that are used by our project. This file is very similar to the project.json file that we use to manage NuGet packages and other dependencies.

We can add a bower.json file to our project very easily from Visual Studio. From the Solution Explorer, right-click on the project> Add> Bower JSON Configuration File

2

Now that the bower.json file is part of our project, let’s add the AngularJs library. To do this, create a new entry under the dependencies section of the bower.json file. This entry will contain the name and the version of the package we want to add.


{
    "name": "BowerApp.Properties",
    "private": true,
    "dependencies": {
        "angular": "1.3.15"
    },
    "exportsOverride": {
    }
}

Notice that Visual Studio provides Intellisense to help us to select among all the Bower packages available. By the way, you can find a list of all the Bower packages that are currently available here.

We now save the changes, and AngularJs should be added automatically to our project. But before we verify this fact we are going to add another package, however, this time we are going to do it from the Command Prompt.

Let’s add for example Font Awesome, a font and CSS toolkit for web applications. We can install this package just by entering the following command:

> bower install font-awesome –save

The new package should now be a dependency of our project. If we go back to our bower.json file we can see that a new entry for the Font Awesome package was added.

{
  "name": "BowerApp",
  "private": true,
  "dependencies": {
    "angular": "1.3.15",
    "font-awesome": "~4.3.0"
  },
  "exportsOverride": {}
}

What if we want to remove an existing dependency? We again have two options; we can either delete the entry for the package from the bower.json file or use the following command from the Command Prompt:

> bower uninstall angular

If what we want is to update the version of one of our dependencies, update the version number directly in the bower.json file, or from the Command Prompt:

> bower update angular

And this will update the package to the latest version available.

Personally I find editing the bower.json file a much easier approach than from the Command Prompt, but both methods are very simple and easy to use.

Viewing the list of Bower dependencies

The easiest way to know what dependencies are included in our project is by looking at the bower.json file. Whatever dependencies are included in this file will be the ones included in our project.

Even if the dependencies are not physically on our machine but are added to this file, they will be downloaded and installed automatically the first time the project is compiled.

This is a very useful feature because it removes the need of including the dependency files when checking in our code to the source control. We only need to check in the bower.json file that will already the list of project dependencies.

We can also view the Bower dependencies included in our project from the Solution Explorer. They are listed under Dependencies -> Bower

7

Or even from the Command Prompt just by typing in the following command:

> bower list

Finally, if what we want is to access the physical files of our Bower packages we should go to the bower-component folder, at the root of our project. This is the place where Bower dependencies are placed by default.

8

Comments Off , permalink


14

Mar 16

Sometimes, Hue just need a quick REST



(Warning: This post contains terrible jokes about “Hue” sounding like “you”.  Prepare knee for slapping.)

Hue caught my eye as soon as Hue got here. Herein lies the epic of my trials and tribulations with Hue, my Friends.

Episode One: The One Where Hue get invited to the party

I grew up in theater, both on- and back-stage. From there, I gained a love for being able to set the mood to anything you can imagine just by tweaking the colors around you. It only makes sense then, that my apartment became my palate. I started decorating with light around 2010, before Hue was a glimmer – and I did it using RGB LED stage lighting fixtures. For those unfamiliar, stage lighting elements are generally controlled using a protocol called DMX512. I found a USB>DMX interface, and wrote an application to control the variety of styles of lights scattered around my apartment. I won’t go into too much detail about that here, because I already have a blog post about it over at Fervent Geek.

Once Hue was released, I was immediately suckered in and started acquiring additional colorful lighting apparatuses – this time communicating using Hue’s own protocols rather than DMX. Sadly, this meant that I had two completely independent applications controlling the lights in my apartment – not the greatest scenario. To add to the complexity, everything in my apartment is automated. The lights turn on white when I need to wake up, green when I need to finish breakfast, red when I need to be out the door, off during the day, back on white in the evening, blue when I need to go to bed, and off when I should be in bed. Configuring that setup on multiple independent platforms was a pain. Changing it for holidays where I didn’t want that schedule to occur was untenable. Something would have to be done. But first, Hue had some expanding to do.

Episode Two: The One Where Hue Learn a New Language

About a year ago, I was able to take on a project to decorate the game room at Clarity’s office. Given my penchant for light-decorated rooms, it was only natural for me to select some Hue lighting to allow the room to take on a festive slant. Once it was in place, it was simple enough to set the lighting schemes using the mobile app on my personal phone to set the scene. However, I began getting inquiries from other people that were interested in writing custom applications to control the lights in various creative ways.

Hue has a great API available that allows some pretty advanced control – but there’s an application registration and authentication process that adds a bit of a barrier to entry. To make it as simple as possible for people to use the lights in their applications, I wrote a REST wrapper for the Hue API that exposed POST and GET web interfaces to allow control of the lights using ridiculously simple web requests. Anyone in the office could now get the state of all the lights, and set the lights to any color they wanted – with next to no effort.

Episode Three: The One where Hue Plays Nicely with Others

This wrapper application wasn’t just useful at the office. By running the same bridging application for the Hue network at home, and creating a new type of lighting fixture within my DMX lighting control application that set the state of lights using web requests, I was able to add the Hue lights into the same application that controls the DMX lights – bringing all the lighting in my apartment back under a single point of control. Finally.

To further expand the capabilities, I extended the DMX control application itself (not just the Hue bits) to expose the same interface for requesting and setting the state of the DMX lights that it controls. Now, the Hue lights and the DMX lights can all be controlled using the same URL formats (just hitting different URLS, one hosted by the Hue Bridge application, and one hosted by the DMX control application). This opens the doors for some pretty fancy web-based control applications that integrate multiple types of lights, across multiple locations. I haven’t written a UI for that yet, but it’s on my to-do list.

Episode Four: The One Where Hue Get a Job

I discussed in a previous post an application I wrote to track the state of conference rooms. Hue lights were a natural fit to be an indicator of the status of conference rooms for people waiting outside, and a means for notifying the participants inside. I already had a Hue API wrapper written for the lights in the game room, so all I had to do was allow the application to run multiple instances against different Hue networks (since they’re too far apart to connect to each other), and I could use the same simple web interface from the Conference Companion to set the lights to indicate all sorts of things for the conference rooms.

Episode Five: The One Where Hue’re Forgetful

When native Hue bulbs (as opposed to Friends of Hue) lose power, they always default to bright white when power is restored. When you have a bunch of Hue bulbs in a room in the office that’s on a motion sensor, this is really annoying, as I keep finding them white when they’re supposed to be festive. Unfortunately, this is not something that Phillips plans to change, according to Steve. So what’s a developer to do? Find a workaround, of course!

I already had the Bridge application running that had the ability to query for the state of lights. One of the properties that you can get is ‘Reachable’ – ie can the Hue network reach the bulb. Presumably, if a bulb is unreachable, it has lost power. So, I just set up a periodic poll of the system to track when bulbs go from reachable to un-reachable. When they do lose connection, I store the previous state they were in, and once the bulb becomes reachable once again, set the state of the bulb to what it was before it became un-reachable. Additionally, if the bridge application receives a request to change the color of a bulb while it is unreachable, it will save that color and set the bulb to it once it’s reachable again. Since the core color recovery is not dependent on an internal ‘scene’ that the bulb should be (but rather the observed state before power loss), if another person changes the color of the lights, the color that they recover to will be whatever the other person set it to be.

So there’s where we find ourselves.  Hue continues to provide lots of opportunities for interesting projects – the limit is only what you can come up with to use it for (well, and budget).  Don’t be surprised if Hue come up again…

Comments Off , permalink


9

Mar 16

Dependency injection in ASP.NET Core 1.0



Dependency injection is at the heart of ASP.NET Core 1.0 Much has been written about it since last year, but in this article I would like to provide a simple example of how to use dependency injection to inject a service in an ASP.NET Core 1.0 Controller.

To achieve this, we will create an ASP.NET Core 1.0 application that displays the weather forecast. This application will be able to use two different services: WeatherService, which uses WeatherNet to provide real weather forecast, and WeatherService2, which as you will see later, is not a very reliable forecast provider.

Later in the article, I will demonstrate how easy it is to modify our application and switch between these two services.

Creating the service

The first thing we need to do is to create an interface that our services can implement. This interface will only have one method, which will provide the weather forecast based on a location.

public interface IWeatherService
{
    string GetWeather(string city, string country);
}

Once we have the interface, we create a new class that will contain the service implementation. This service we will retrieve and return the weather forecast using WeatherNet, based on the city and country parameters.

public class WeatherService :IWeatherService
{

    public string GetWeather(string city, string country)
    {
        var wClient = new WeatherNet.Clients.CurrentWeather();
        var weather = wClient.GetByCityName(city, country)
                      .Item.Description;
        
        return string.Format(
           "WeatherService {0} in {1} ({2})",
            weather, city, country);
    }
}

Now, we add the WeatherService2 implementation in a new class. This service will not retrieve the weather using any third party. Instead, it will make the assumption that there is heavy snow everywhere. A little bit scary, right? ;)

public class WeatherService2 : IWeatherService
{

    public string GetWeather(string city, string country)
    {
        var weather = "Heavy Snow";
        
        return string.Format(
           "WeatherService2 {0} in {1} ({2})",
            weather, city, country);
    }
}

In our sample application, I put the three classes in three different files under a Service folder created in the root folder.

1

Injecting the service into the controller

It’s time to inject the service to our Controller. To do this, we create a Controller constructor, which is going to take an IWeatherService implementation as a parameter. We also create a private field so that it’s available to the rest of the methods of the Controller.

IWeatherService _weatherService;
public HomeController(IWeatherService weatherService)
{
     _weatherService = weatherService;
}
 

Now we want to use the injected service in the Index method of our Controller to return the weather forecast. To keep it simple, the parameters will be hard coded in this method.

[HttpGet("/")]
public string Index()
{
    return _weatherService.GetWeather("Miami", "US");
}

Registering the service

Now let’s do some magic and use the new dependency injection feature that comes with ASP.NET Core 1.0.

In the Startup.cs file we have a ConfigureService method, which we need to add the following line to register our service in the application:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddInstance(new WeatherService());
}

This line means that every time our app needs an instance of IWeatherService, the WeatherService implementation will be used.

Notice that we have used the AddInstance method to register our service, this is going to provide the same service instance for each HTTP request that uses the service. However, in some cases we may want to create a new instance every time that the service is used. In this case, we can use something like the AddTransient method, which will provide a new instance of the service every time it’s needed:

services.AddTransient<IWeatherService>(x=> new WeatherService());

Running the application!

We now run our application and observe how, as expected, the sky is clear in Miami. This seems right to me, and means that our WeatherService implementation was used.

2

If, for any reason, we want to use WeatherService2, we just need to modify the ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
      services.AddMvc();
      services.AddInstance(new WeatherService2());
}

Let’s run our application again, and voilà. Heavy snow in Miami, which means that this time our application used the WeatherService2 implementation.

3

Comments Off , permalink


29

Feb 16

PowerShell Skype Trusted Pool Creator



In one of my previous posts, I shared a PowerShell tool to wrap the management of Skype Trusted Applications within a Windows Forms GUI, provided via a PowerShell script. In it, I alluded to another tool I’d written that wraps the process of creating Trusted Application Pools and Applications – well here it is!

This is another full Windows Forms application, embedded within a PowerShell scriptlet. You can create Trusted Application Pools (selecting the registrar from a dropdown), add servers to any Pool you’ve created, create Trusted Applications assigned to any pool, and add initial Trusted Endpoints to and Pool. All these actions can be done via script fairly easily, this just abstracts away a lot of the lookups that often need to happen for syntax and related server information.

EndpointCreator

Like the other scriptlet, loading the commands can be done either by opening Skype PowerShell and launching the scriptlet manually, or creating a new Skype PowerShell shortcut that loads both the Skype or Lync scriptlets into PowerShell as well as the application scriptlet. You can do this my adding &’{filePath.ps1}’ to the end of the default Skype for Business PowerShell shortcut target path.

And the script:

# Set up the form
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”)
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = “Clarity Connect Lync Application Creator”
$objForm.Size = New-Object System.Drawing.Size(820,230)
$objForm.StartPosition = “CenterScreen”
$registrars = Invoke-Expression ‘Get-CsPool | Where-Object {$_.Services –match “Registrar:”} | Select-Object Fqdn’ #identity has name info
$sites = Invoke-Expression “Get-CsSite | Select-Object SiteId”
$trustedAppPools = Invoke-Expression “Get-CsTrustedApplicationPool | Select-Object PoolFqdn”
$trustedApps = Invoke-Expression “Get-CsTrustedApplication | Select-Object ApplicationId”
$endpoints = Invoke-Expression “get-cstrustedapplicationendpoint | Select-Object SipAddress”
# Set up Create Pool area ==============================================================
$poolLabel = New-Object System.Windows.Forms.Label
$poolLabel.Location = New-Object System.Drawing.Size(0,0)
$poolLabel.Size = New-Object System.Drawing.Size(200,20)
$poolLabel.Text = “Create Pool”
$objForm.Controls.Add($poolLabel)
$poolFqdn = New-Object System.Windows.Forms.TextBox
$poolFqdn.Location = New-Object System.Drawing.Size(20,20)
$poolFqdn.Size = New-Object System.Drawing.Size(200,30)
$poolFqdn.Text = “Pool FQDN”
$objForm.Controls.Add($poolFqdn)
$poolRegistrar = New-Object System.Windows.Forms.ComboBox
$poolRegistrar.Location = New-Object System.Drawing.Size(240,20)
$poolRegistrar.Size = New-Object System.Drawing.Size(200,30)
[void] $poolRegistrar.Items.Clear()
foreach ($registrar in $registrars)
  {
    [void] $poolRegistrar.Items.Add($registrar.Fqdn)
  }
$objForm.Controls.Add($poolRegistrar)
$poolServerFqdn = New-Object System.Windows.Forms.TextBox
$poolServerFqdn.Location = New-Object System.Drawing.Size(460,20)
$poolServerFqdn.Size = New-Object System.Drawing.Size(200,30)
$poolServerFqdn.Text = “Server FQDN”
$objForm.Controls.Add($poolServerFqdn)
$poolCreate = New-Object System.Windows.Forms.Button
$poolCreate.Location = New-Object System.Drawing.Size(700,20)
$poolCreate.Size = New-Object System.Drawing.Size(80,20)
$poolCreate.Text = “Create Pool”
$objForm.Controls.Add($poolCreate)
$poolCreate.add_Click(
  {
    $poolLocalFqdn = $poolFqdn.Text
    $poolLocalRegistrar = $poolRegistrar.Text
    $poolRegistrarSiteData = Invoke-Expression “Get-CsPool -Identity $poolLocalRegistrar | Select-Object Site”
    $poolRegistrarSite = $poolRegistrarSiteData.Site
    $poolLocalSiteData = Invoke-Expression “Get-CsSite -Identity `”$poolRegistrarSite`” | Select-Object SiteId”
    $poolLocalSite = $poolLocalSiteData.SiteId
    $poolLocalServerFqdn = $poolServerFqdn.Text
    $items = Invoke-Expression “New-CsTrustedApplicationPool -Identity $poolLocalFqdn -Registrar $poolLocalRegistrar -Site $poolLocalSite -ComputerFqdn $poolLocalServerFqdn”
    $trustedAppPools = Invoke-Expression “Get-CsTrustedApplicationPool | Select-Object PoolFqdn”
    [void] $serverFqdn.Items.Clear()
    foreach ($trustedAppPool in $trustedAppPools)
      {
        [void] $serverFqdn.Items.Add($trustedAppPool.PoolFqdn)
      }
    [void] $appPool.Items.Clear()
    foreach ($trustedAppPool in $trustedAppPools)
      {
        [void] $appPool.Items.Add($trustedAppPool.PoolFqdn)
      }
  })
# Set up Add Pool Server area ==============================================================
$serverLabel = New-Object System.Windows.Forms.Label
$serverLabel.Location = New-Object System.Drawing.Size(0,50)
$serverLabel.Size = New-Object System.Drawing.Size(200,20)
$serverLabel.Text = “Add Server to Pool”
$objForm.Controls.Add($serverLabel)
$serverPool = New-Object System.Windows.Forms.ComboBox
$serverPool.Location = New-Object System.Drawing.Size(20,70)
$serverPool.Size = New-Object System.Drawing.Size(200,30)
[void] $serverPool.Items.Clear()
foreach ($trustedAppPool in $trustedAppPools)
  {
    [void] $serverPool.Items.Add($trustedAppPool.PoolFqdn)
  }
$objForm.Controls.Add($serverPool)
$serverFqdn = New-Object System.Windows.Forms.TextBox
$serverFqdn.Location = New-Object System.Drawing.Size(240,70)
$serverFqdn.Size = New-Object System.Drawing.Size(200,30)
$serverFqdn.Text = “Server FQDN”
$objForm.Controls.Add($serverFqdn)
$serverCreate = New-Object System.Windows.Forms.Button
$serverCreate.Location = New-Object System.Drawing.Size(700,70)
$serverCreate.Size = New-Object System.Drawing.Size(80,20)
$serverCreate.Text = “Add Server”
$objForm.Controls.Add($serverCreate)
$serverCreate.add_Click(
  {
    $serverLocalPool = $serverPool.Text
    $serverLocalFqdn = $serverFqdn.Text
    $items = Invoke-Expression “New-CsTrustedApplicationComputer -Identity $serverLocalFqdn -Pool $serverLocalPool”
  })
# Set up Create Application area ==============================================================
$appLabel = New-Object System.Windows.Forms.Label
$appLabel.Location = New-Object System.Drawing.Size(0,100)
$appLabel.Size = New-Object System.Drawing.Size(200,20)
$appLabel.Text = “Create Application”
$objForm.Controls.Add($appLabel)
$appName = New-Object System.Windows.Forms.TextBox
$appName.Location = New-Object System.Drawing.Size(20,120)
$appName.Size = New-Object System.Drawing.Size(200,30)
$appName.Text = “clarityconnect”
$objForm.Controls.Add($appName)
$appPool = New-Object System.Windows.Forms.ComboBox
$appPool.Location = New-Object System.Drawing.Size(240,120)
$appPool.Size = New-Object System.Drawing.Size(200,30)
[void] $appPool.Items.Clear()
foreach ($trustedAppPool in $trustedAppPools)
  {
    [void] $appPool.Items.Add($trustedAppPool.PoolFqdn)
  }
$objForm.Controls.Add($appPool)
$appPort = New-Object System.Windows.Forms.TextBox
$appPort.Location = New-Object System.Drawing.Size(460,120)
$appPort.Size = New-Object System.Drawing.Size(100,30)
$appPort.Text = “10022″
$objForm.Controls.Add($appPort)
$appCreate = New-Object System.Windows.Forms.Button
$appCreate.Location = New-Object System.Drawing.Size(700,120)
$appCreate.Size = New-Object System.Drawing.Size(80,20)
$appCreate.Text = “Create App”
$objForm.Controls.Add($appCreate)
$appCreate.add_Click(
  {
    $appLocalName = $appName.Text
    $appLocalPool = $appPool.Text
    $appLocalPort = $appPort.Text
    $items = Invoke-Expression “New-CsTrustedApplication -ApplicationId $appLocalName -TrustedApplicationPoolFqdn $appLocalPool -Port $appLocalPort”
    $appTopo = Invoke-Expression “Enable-CsTopology”
    $trustedApps = Invoke-Expression “Get-CsTrustedApplication | Select-Object ApplicationId”
    [void] $endpointApplication.Items.Clear()
    foreach ($trustedApp in $trustedApps)
      {
        [void] $endpointApplication.Items.Add($trustedApp.ApplicationId)
      }
  })
# Set up Create Application Endpoint area ==============================================================
$endpointLabel = New-Object System.Windows.Forms.Label
$endpointLabel.Location = New-Object System.Drawing.Size(0,150)
$endpointLabel.Size = New-Object System.Drawing.Size(200,20)
$endpointLabel.Text = “Create Endpoint”
$objForm.Controls.Add($endpointLabel)
$endpointApplication = New-Object System.Windows.Forms.ComboBox
$endpointApplication.Location = New-Object System.Drawing.Size(20,170)
$endpointApplication.Size = New-Object System.Drawing.Size(200,30)
[void] $endpointApplication.Items.Clear()
foreach ($trustedApp in $trustedApps)
  {
    [void] $endpointApplication.Items.Add($trustedApp.ApplicationId)
  }
$objForm.Controls.Add($endpointApplication)
$endpointSip = New-Object System.Windows.Forms.TextBox
$endpointSip.Location = New-Object System.Drawing.Size(240,170)
$endpointSip.Size = New-Object System.Drawing.Size(200,30)
$endpointSip.Text = “sip:clarityconnect@domain.com”
$objForm.Controls.Add($endpointSip)
$endpointDisplayName = New-Object System.Windows.Forms.TextBox
$endpointDisplayName.Location = New-Object System.Drawing.Size(460,170)
$endpointDisplayName.Size = New-Object System.Drawing.Size(100,30)
$endpointDisplayName.Text = “Clarity Connect”
$objForm.Controls.Add($endpointDisplayName)
$endpointCreate = New-Object System.Windows.Forms.Button
$endpointCreate.Location = New-Object System.Drawing.Size(700,170)
$endpointCreate.Size = New-Object System.Drawing.Size(100,20)
$endpointCreate.Text = “Create Endpoint”
$objForm.Controls.Add($endpointCreate)
$endpointCreate.add_Click(
  {
    $endpointapplicationId = $endpointApplication.SelectedItem
    $endpointapplicationData = Invoke-Expression “Get-CsTrustedApplication -Filter *$endpointapplicationId | Select-Object TrustedApplicationPoolFqdn”
    $endpointPool = $endpointapplicationData.TrustedApplicationPoolFqdn
    $endpointLocalDisplay = $endpointDisplayName.Text
    $endpointLocalSip = $endpointSip.Text
    $items = Invoke-Expression “New-CsTrustedApplicationEndpoint -ApplicationId $endpointapplicationId -TrustedApplicationPoolFqdn $endpointPool -SipAddress $endpointLocalSip -DisplayName `”$endpointLocalDisplay`”"
    $endpoints = Invoke-Expression “get-cstrustedapplicationendpoint | Select-Object SipAddress”
    $endpointSip.Text = “sip:clarityconnect@domain.com”
    $endpointDisplayName.Text = “Clarity Connect”
  })
# Activate the form
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

Comments Off , permalink


22

Feb 16

PowerShell Skype Application Endpoint Manager



There’s an interesting limitation placed on Lync and Skype Trusted Application Endpoints, where only one incoming PSTN number can be assigned per endpoint. While there are various ways to work around this with custom applications, in a standard environment, this may mean that you have a lot of endpoints to manage for a given application.

In addition to that, if you work in an environment that has a lot of trusted applications configured (for example, if you work for a company that does UCMA development…), chances are good that you’ve got tens or hundreds of Trusted Applications floating around.

Managing all those applications and endpoints is done through the brilliance of PowerShell Commandlets. While managing them through PowerShell works wonderfully, sometimes you just want a glance-able interface where you can make quick manual changes without searching or scanning through lists and lists of shell results.

Did you know that you can create a full Windows Forms application using Powershell? No? Now you do! To help manage all the endpoints that were configured for applications, I made a Windows Forms application inside a powershell script that wraps the powershell commands to create and maintain endpoints within a more user-friendly interface. I have another similar application that’s used for creating Trusted Application Pools and their Applications, which I’ll include in a future post.

EndpointManager

Loading the commands can be done either by opening Skype PowerShell and launching the scriptlet manually, or creating a new Skype PowerShell shortcut that loads both the Skype or Lync scriptlets into PowerShell as well as the application scriptlet.  You can do this my adding &’{filePath.ps1}’ to the end of the default Skype for Business PowerShell shortcut target path.

The script itself:

[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”)
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = “Clarity Connect Lync Endpoint Manager”
$objForm.Size = New-Object System.Drawing.Size(1190,500)
$objForm.StartPosition = “CenterScreen”
# Set up the application list ==============================================================
$applicationsList = New-Object System.Windows.Forms.Listbox
$applicationsList.Location = New-Object System.Drawing.Size(20,20)
$applicationsList.Size = New-Object System.Drawing.Size(300,400)
$applications = Invoke-Expression “Get-CsTrustedApplication | Select-Object ApplicationId”
[void] $applicationsList.Items.Clear()
[void] $applicationsList.Items.Add(“All”)
foreach ($application in $applications)
  {
      [void] $applicationsList.Items.Add($application.ApplicationId)
  }
$objForm.Controls.Add($applicationsList)
$applicationsList.add_Click(
  {
    $applicationId = $applicationsList.SelectedItem
    $endpointDetails.Text = “”
    $teluriUri.Text = “”
    $displayName.Text = “”
    $teluriSet.Enabled = $False
    $displayNameSet.Enabled = $False
    $endpointList.Items.Clear()
    if($applicationId -eq “All”) {
      $items = Invoke-Expression “Get-CsTrustedApplicationEndpoint | Select-Object SipAddress”
      if ($items –ne $Null)
      {
        foreach ($item in $items)
        {
          [void] $endpointList.Items.Add($item.SipAddress)
        }
        $endpointCreate.Enabled = $False
      }
    }
    else
    {
      if ($applicationId –ne $Null)
      {
        $items = Invoke-Expression “Get-CsTrustedApplicationEndpoint -ApplicationId $applicationId | Select-Object SipAddress”
        if ($items –ne $Null)
        {
          foreach ($item in $items)
          {
            [void] $endpointList.Items.Add($item.SipAddress)
          }
          $endpointCreate.Enabled = $True
        }
      }
    }
  })
# Set up the endpoint box ==============================================================
$endpointList = New-Object System.Windows.Forms.Listbox
$endpointList.Location = New-Object System.Drawing.Size(340,20)
$endpointList.Size = New-Object System.Drawing.Size(300,400)
[void] $endpointList.Items.Clear()
$objForm.Controls.Add($endpointList)
$endpointList.Add_Click(
  {
    $endpointSip = $endpointList.SelectedItem
    if($endpointSip -ne $null)
    {
      $endpointDetails.Text = Invoke-Expression “(Get-CsTrustedApplicationEndpoint -Identity $endpointSip | Format-List | Out-String).Trim()”
      $endpointData = Invoke-Expression “Get-CsTrustedApplicationEndpoint -Identity $endpointSip | Select-Object LineUri, DisplayName”
      $teluriUri.Text = $endpointData.LineUri
      $displayName.Text = $endpointData.DisplayName
      $teluriSet.Enabled = $True
      $displayNameSet.Enabled = $True
    }
  })
# Set up the endpoint details box ==============================================================
$endpointDetails = New-Object System.Windows.Forms.TextBox
$endpointDetails.Location = New-Object System.Drawing.Size(660,20)
$endpointDetails.Size = New-Object System.Drawing.Size(500,200)
$endpointDetails.Multiline = $True
$endpointDetails.Wordwrap = $True
$endpointDetails.ReadOnly = $True
$objForm.Controls.Add($endpointDetails)
# Set up Assign Tel URI area ==============================================================
$teluriLabel = New-Object System.Windows.Forms.Label
$teluriLabel.Location = New-Object System.Drawing.Size(660,230)
$teluriLabel.Size = New-Object System.Drawing.Size(200,20)
$teluriLabel.Text = “Assign Tel URI”
$objForm.Controls.Add($teluriLabel)
$teluriUri = New-Object System.Windows.Forms.TextBox
$teluriUri.Location = New-Object System.Drawing.Size(660,250)
$teluriUri.Size = New-Object System.Drawing.Size(200,30)
$teluriUri.Text = “”
$objForm.Controls.Add($teluriUri)
$teluriSet = New-Object System.Windows.Forms.Button
$teluriSet.Location = New-Object System.Drawing.Size(880,250)
$teluriSet.Size = New-Object System.Drawing.Size(120,20)
$teluriSet.Text = “Set Tel URI”
$teluriSet.Enabled = $False
$objForm.Controls.Add($teluriSet)
$teluriSet.add_Click(
  {
    $items = Invoke-Expression ‘Set-CsTrustedApplicationEndpoint -Identity $endpointSip -LineUri $teluriUri.Text’
    $endpointDetails.Text = Invoke-Expression “(Get-CsTrustedApplicationEndpoint -Identity $endpointSip | Format-List | Out-String).Trim()”
  })
# Set up Assign Display Name area ==============================================================
$displayNameLabel = New-Object System.Windows.Forms.Label
$displayNameLabel.Location = New-Object System.Drawing.Size(660,280)
$displayNameLabel.Size = New-Object System.Drawing.Size(200,20)
$displayNameLabel.Text = “Assign Display Name”
$objForm.Controls.Add($displayNameLabel)
$displayName = New-Object System.Windows.Forms.TextBox
$displayName.Location = New-Object System.Drawing.Size(660,300)
$displayName.Size = New-Object System.Drawing.Size(200,30)
$displayName.Text = “”
$objForm.Controls.Add($displayName)
$displayNameSet = New-Object System.Windows.Forms.Button
$displayNameSet.Location = New-Object System.Drawing.Size(880,300)
$displayNameSet.Size = New-Object System.Drawing.Size(120,20)
$displayNameSet.Text = “Set DisplayName”
$displayNameSet.Enabled = $False
$objForm.Controls.Add($displayNameSet)
$displayNameSet.add_Click(
  {
    $items = Invoke-Expression ‘Set-CsTrustedApplicationEndpoint -Identity $endpointSip -DisplayName $displayName.Text’
    $endpointDetails.Text = Invoke-Expression “(Get-CsTrustedApplicationEndpoint -Identity $endpointSip | Format-List | Out-String).Trim()”
  })
# Set up Create Application Endpoint area ==============================================================
$endpointLabel = New-Object System.Windows.Forms.Label
$endpointLabel.Location = New-Object System.Drawing.Size(20,420)
$endpointLabel.Size = New-Object System.Drawing.Size(200,20)
$endpointLabel.Text = “Add Endpoint to Application”
$objForm.Controls.Add($endpointLabel)
$endpointSipUri = New-Object System.Windows.Forms.TextBox
$endpointSipUri.Location = New-Object System.Drawing.Size(20,440)
$endpointSipUri.Size = New-Object System.Drawing.Size(200,30)
$endpointSipUri.Text = “sip:clarityconnect@domain.com”
$objForm.Controls.Add($endpointSipUri)
$endpointDisplayName = New-Object System.Windows.Forms.TextBox
$endpointDisplayName.Location = New-Object System.Drawing.Size(240,440)
$endpointDisplayName.Size = New-Object System.Drawing.Size(200,30)
$endpointDisplayName.Text = “Clarity Connect”
$objForm.Controls.Add($endpointDisplayName)
$endpointCreate = New-Object System.Windows.Forms.Button
$endpointCreate.Location = New-Object System.Drawing.Size(460,440)
$endpointCreate.Size = New-Object System.Drawing.Size(100,20)
$endpointCreate.Text = “Create Endpoint”
$endpointCreate.Enabled = $False
$objForm.Controls.Add($endpointCreate)
$endpointCreate.add_Click(
  {
  if($applicationId -ne “All”) {
      $applicationId = $applicationsList.SelectedItem
      $applicationData = Invoke-Expression “Get-CsTrustedApplication -Filter *$applicationId | Select-Object TrustedApplicationPoolFqdn”
      $poolFqdn = $applicationData.TrustedApplicationPoolFqdn
      $display = $endpointDisplayName.Text
      $sip = $endpointSipUri.Text
      $invoked = Invoke-Expression “New-CsTrustedApplicationEndpoint -ApplicationId $applicationId -TrustedApplicationPoolFqdn $poolFqdn -SipAddress $sip -DisplayName `”$display`”"
      $items = Invoke-Expression “Get-CsTrustedApplicationEndpoint -ApplicationId $applicationId | Select-Object SipAddress”
      [void] $endpointList.Items.Clear()
      if ($items –ne $Null)
      {
        foreach ($item in $items)
        {
          [void] $endpointList.Items.Add($item.SipAddress)
        }
      }
      $endpointDisplayName.Text = “Clarity Connect”
      $endpointSipUri.Text = “sip:clarityconnect@domain.com”
    }
  })
# Activate the form
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

Comments Off , permalink


1

Feb 16

Clarity Conference Companion



Meetings. An ever-present tenant of the average office workday that can be simultaneously a source of extraordinarily useful discussions and fuel for distressingly Dilbertesque tediousness.
As you may know, I’m a fan of efficiency, especially when it involves creating ways to have interactions with inanimate objects. The restrooms in our office are already internet-connected – so why not conference rooms? Once you have a seed of an idea like that, it’s hard for it to not run rampant. Which then gave rise to…

The Clarity Conference Companion

Part 1: Getting the Data

EWS, or Exchange Web Services, is an API that Microsoft Exchange exposes that allows you to query mailboxes, calendars, and other objects hosted by Exchange. In general, if you want to get calendar events for a mailbox, you can perform a FindAppointments request using the ExchangeService with the appropriate filters. However, for that to work, you need to have permissions granted on that account (like your own account, or someone whose calendar you manage). While this is perfect for the normal use scenario, I needed to be able to look at the availability of all the conference room accounts – which I do not have permissions to see.

Enter, the Scheduler. When you’re setting up a new meeting, the Scheduler tab can display a basic set of information for each person on the meeting to help you determine the best time to schedule so that everyone can make it. Importantly, you do not need to have permissions on any of the other mailboxes in order to see the data. By requesting from the ExchangeService GetUserAvailability and specifying a FreeBusyViewType of Detailed, I can get back from mailboxes I don’t have explicit access to all their appointments, as well as the Subjects – which generally maps to the person who organized the meeting.

Spin up a quick console application that uses EWS to grab the schedule for each room, and bam. Data acquired.

Part 2: Setting Status

Our office is heavily invested in Skype for Business, and we use it extensively for both communication and presence. Additionally, each desk has a Busylight, which is a physical representation of each person’s presence state to help real-world passers-by know who’s available to talk and who’s not.

A good first step in this application was to add in the ability to see, from outside the room, what the room’s “Presence” state is at the moment. Since I already had the calendar schedule for the room, it was a relatively trivial task to trigger “something” when a meeting was starting soon (so you don’t go into a room for an ad-hoc meeting, only to find it booked minutes later), currently in progress, or if the room was free.

But what should that “something” be, exactly?

The most obvious thing was to allow people in the office to be able to see the status using their Skype for Business client. Step one was to spin up a UCMA Platform in my application – there are tons of other posts about that, so I won’t wax on about it. Once our system administrator enabled all the room accounts for Skype, I had my UCMA platform register UserEndpoints for each room. Then, when one of those events occurred that should cause ‘something’ to happen, I updated the appropriate UserEndpoint with the new state of the room (green if available, red if booked, yellow if it will be in use shortly), as well as a tag of the current user (if applicable).  If you find that resource accounts are not showing presence, try logging out of and back into your Skype client – I found that was necessary for accounts that had never been logged into using an actual Skype client.

So that covered people sitting at their desks, but what about people wandering through the office looking for an empty room? Enter: Phillips Hue.

I placed a Hue light outside the conference room, and got that hooked up so it could be updated via a simple web request. Adding a bit to my ‘something’ method, I instruct the Hue light to update to the appropriate color at the same time I update the Skype endpoint’s presence state. Then, just like the Busylights we have at desks, we now had a real-world indicator of the state of the room sitting right outside the door.
2016-01-29 12.26.26

Whether virtual or physical, we had status. But now that the basics are down, what else could we do to add value?

Part 3: Notifications

Keeping track of when a meeting in a conference room is getting close to ending can be a real problem. It’s very easy to become embroiled in a discussion and completely forget what time it is, until the next people that have the room reserved are bashing down the door – or a 15 minute standup has escalated to an hour-long dialog. If only there were a way to subtly tell the people in the room that they should start wrapping up their meeting (without derailing a critical discussion…

Once again, Enter: Phillips Hue.

Placing another Hue light inside the room, I had my application set this light using notifications that would be useful to those actually in the meeting room. If the room is available, it’s green. Meeting starting soon? Flip it to yellow to give anyone having an impromptu meeting a heads up that something’s happening soon. It’s turned off while a meeting is in progress, but turns yellow when there are 5 minutes left in the scheduled meeting to remind those inside to start wrapping up. When a meeting is over, it will flash red if another meeting is scheduled immediately afterward, or to green if the room is available and extending the meeting won’t impact anyone else.

It’s a small thing, but giving people in the room a subtle heads up to start wrapping up their meeting has gotten more positive feedback than anything else.

2016-01-29 12.29.052016-01-29 13.34.59

Part 4: Interaction

It’s a simple “fact”: if you have a UserEndpoint that you’re publishing presence for, you should give it a personality too. And so the full Conference Companion was born.

I added logic that would allow each conference room to accept IMs, and respond about the availability of the room based on the request.

If you send a generic IM, it tells if you if it is currently available and the schedule for the rest of the day – including when it’s next occupied. If it’s not available, it tells you when it’s free, and also suggests other rooms that show as available at the moment.

If you include a time in your IM, it tells you about the room’s availability today at that particular time – as well as everything else previously mentioned. If you include a duration (like 30 minutes or 1 hour), it’ll tell you if the room will be free for the duration of your requested meeting – either now or at the time specified, if you did so.

Interaction

Joining the restrooms, Clarity’s conference rooms gained a little bit of intelligence.  What’s next?

1 comment , permalink