Udostępnij za pośrednictwem


Smart bulbs or how to be lazier (part 3): AllJoyn client

In the previous posts I showed how to use AllJoyn bridges in order to make a universal hub based on Raspberry Pi 2 that is running Windows 10. Here are the links to my previous posts:

Smart bulbs or how to be lazier (part 1)

Smart bulbs or how to be lazier (part 2)

Additionally, I would like to share the link to my second module on Channel 9 about IoT.

Ok. I assume that you already have the hub and you can manage all ZigBee/Z-Wave/AllJoyn devices using IoT Explorer for AllJoyn from the Store. But IoT Explorer doesn’t have a really cool interface and it wasn’t designed for your smart home only – it’s just a universal tool that shows to developers how to use existing SDK to get access to AllJoyn devices. So, it’s time to develop own client application that will work in our local network.

We already know that AllJoyn devices publish contacts that we can use in order to understand how the devices work. In general, contracts are in XML format and we can easy download and parse them. But it works fine if you are going to build an application that can work with all possible devices (existing and future). In our case, we are going to build an application that uses AllJoyn API to connect the LED bulb from General Electric and I want to minimize number of actions around it. So, before to create anything, I would recommend to install AllJoyn Studio that is a special Visual Studio plug-in. Thanks to AllJoyn Studio, you can easy generate proxy-classes for all available AllJoyn devices in your network. Using these proxy classes, you will be able to concentrate all your attention around user interface only.

So, once you install AllJoyn Studio, you will be able to create your first project using new AllJoyn templates:

clip_image002[4]

Once you click OK, Visual Studio will show one more dialog that allows you to select an XML that describe a contact:

clip_image004[4]

Using this window, you can reference any existing XML on your disk or list all AllJoyn connected devices in your local network. So, I simply can select my bulb from the list.

You can see that even standard ZigBee bulb supports several interfaces (thanks to the bridge). There are two different type of interfaces: standard AllJoyn interfaces that are described in AllSeen Lighting Framework documents and some interfaces that were published by Microsoft bridge. In general, you will work with standard AllJoyn interfaces in order to connect any AllJoyn bulb. But in my case, I am going to use ZigBee bulbs only via the bridge, so, I can use Microsoft interfaces that contain less complex methods.

You can modify the set of the selected interfaces any time using AllJoyn menu item in Visual Studio.

clip_image005[4]

For each of the selected interfaces, Visual Studio creates a separate project in C++ that wraps all tasks related to AllJoyn. You simply can use it.

I have decided to create a very basic application that will be able to switch on and off my bulb by request. And I assume that there is one bulb only. It will help me to simplify my code.

(Rebuild the application before to start coding)

I created a very basic interface that contains a check box and progress ring:

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel HorizontalAlignment="Center" 
                VerticalAlignment="Center" Orientation="Vertical"
                Name="initPanel">
        <ProgressRing Height="100" Width="100" IsActive="True"></ProgressRing>
        <TextBlock Text="Looking for devices..."></TextBlock>
    </StackPanel>
    <StackPanel HorizontalAlignment="Center" 
                VerticalAlignment="Center" Orientation="Vertical"
                Name="devicePanel" Visibility="Collapsed">
        <CheckBox Content="GE Bulb" Name="bulbCheckBox" IsEnabled="False"></CheckBox>
    </StackPanel>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="Initial"></VisualState>
            <VisualState x:Name="Active">
                <VisualState.Setters>
                    <Setter Target="devicePanel.Visibility" Value="Visible"></Setter>
                    <Setter Target="initPanel.Visibility" Value="Collapsed"></Setter>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

There are two states. The first one I use in order to show the progress ring until I find my bulb. And the second one – check box itself, that provides the current state and allows you to change the state of the bulb.

For the project I use just two interfaces: MainInterface and OnOff. The first one allows me to send commands to the bulb and the second one – read state of the bulb.

In general, in order to work with these interfaces, you have to do the following things:

· Create an object that will represent AllJoyn connection. You can do it, using the AllJoynBusAttachment class from the  Windows.Devices.AllJoyn namespace;

· Define references to consumers for the selected AllJoyn interfaces. Visual Studio already generated all needed classes – just look at consumer suffix in Object Browser;

· Create watchers for the selected interfaces and start them (use watcher suffix in order to find the right classes);

· Once a watcher find an interface, try to join it and implement your own logic that will update user interface, read data and so on.

The code itself looks like:

 AllJoynBusAttachment bus;

MainInterfaceWatcher mainwatcher;
MainInterfaceConsumer mainconsumer;

onoffConsumer onoffconsumer;
onoffWatcher onoffwatcher;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    bus = new AllJoynBusAttachment();

    mainwatcher = new MainInterfaceWatcher(bus);
    mainwatcher.Added += MainWatcher_Added;
    mainwatcher.Start();

    onoffwatcher = new onoffWatcher(bus);
    onoffwatcher.Added += Onoffwatcher_Added;
    onoffwatcher.Start();

    base.OnNavigatedTo(e);
}

private async void Onoffwatcher_Added(onoffWatcher sender, 
    AllJoynServiceInfo args)
{
    onoffJoinSessionResult result = await onoffConsumer.JoinSessionAsync(
        args, sender);
    if (result.Status==AllJoynStatus.Ok)
    {
        onoffconsumer = result.Consumer;

        UpdateInterface(); 
    }
}

private async void MainWatcher_Added(MainInterfaceWatcher sender, 
    AllJoynServiceInfo args)
{
    MainInterfaceJoinSessionResult result = 
        await MainInterfaceConsumer.JoinSessionAsync(args, sender);
    if (result.Status==AllJoynStatus.Ok)
    {
        mainconsumer = result.Consumer;

        bulbCheckBox.IsEnabled = true;
    }
}

private async void UpdateInterface()
{
    VisualStateManager.GoToState(this, "Active", true);
    if (onoffconsumer!=null)
    {
        var res=await onoffconsumer.GetOnOffAsync();
        bulbCheckBox.IsChecked = res.OnOff;
        bulbCheckBox.Click += BulbCheckBox_Click;
    }
}

private async void BulbCheckBox_Click(object sender, RoutedEventArgs e)
{
    if (mainconsumer!=null)
    {
        if (bulbCheckBox.IsChecked == true)
            await mainconsumer.OnOffOnAsync();
        else
            await mainconsumer.OnOffOffAsync();
    }
}

Running this code, you will see a check box that you can use in order to switch on and off your bulb.

My application has just one page and I use just one bulb. So, my code is not so complex. But once you have several bulbs, you have to work with a list of devices and additionally, you have to check any status change. Just try to do it.