Control Remote Mains Switches using Windows Phone – Tinkerforge

- Getting Started
  - Primer
  - Tutorials
  - FAQ
- Hardware
  - Bricks
  - Bricklets
  - Master Extensions
  - Power Supplies
  - Accessories
  - Timeline
- Software
  - Brick Daemon (brickd)
  - Brick Viewer (brickv)
  - API Bindings
  - Firmwares and Plugins
  - Device Identifier
  - Source Code and Bug Tracking
  - Programming Interface
- Starter Kits
  - Weather Station
  - Hardware Hacking
    - Features
    - Description
    - Technical Specifications
    - Resources
    - Required Tools
    - Firmware updating and first tests
    - How it works
    - Hardware Hacking for Beginners
    - Examples
      - Control Mains Switches Remotely
        - Hardware Setup
        - Using Android
        - Using Windows Phone
          - Goals
          - Step 1: Creating the GUI
          - Step 2: Discover Bricks and Bricklets
          - Step 3: Triggering Switches
          - Step 4: More GUI logic
          - Step 5: Error Handling and Reporting
          - Step 6: Persistent Configuration and State
          - Step 7: Everything put together
        - Using iOS
        - Using C# with GUI
        - Using C/C++
        - Using C#
        - Using Delphi/Lazarus
        - Using Java
        - Using PHP
        - Using Python
        - Using Ruby
        - Using Visual Basic .NET
      - Read out Smoke Detectors
      - Control Garage Doors Remotely
      - Is someone at the Door? - Doorbell Notifier
  - Server Room Monitoring
  - Blinkenlights
  - Internet of Things
  - Camera Slider
- Embedded Boards
  - Raspberry Pi
- Specifications
  - Technical Data
  - TCP/IP Protocol
  - Modbus Protocol
  - Wireshark Dissector

# Control Remote Mains Switches using Windows Phone

For this project we are assuming, that you have the Windows Phone SDK set up and that you have a rudimentary understanding of the C# language.

If you are totally new to C# itself you should start here. If you are new to the Tinkerforge API, you should start here.

We are also assuming that you have a remote control connected to an Industrial Quad Relay Bricklet as described here.

The complete Visual Studio project can be downloaded here. A demo app based on this project is available in the Windows Phone Store.

## Goals

In this project we will create a simple Windows Phone app that resembles the functionality of the actual remote control.

## Step 1: Creating the GUI

After creating a new "Windows Phone App" named "Power Outlet Control" in Visual Studio we start with creating the GUI:

We extend the precreated layout by appending a "StackPanel" to the "LayoutRoot" grid, that will contain the other GUI elements. Three "TextBoxes" allow to enter the host, port and UID of the Industrial Quad Relay Bricklet. For the port a text box with `InputScope="Number"` is used, so Windows Phone will restrict the content of this text box to numbers. Below the text boxes goes a "ProgressBar" (not visible on screenshot) that will be used to indicate that a connection attempt is in progress. The final fife elements are one "Button" to connect and disconnect and four buttons in a horizontal "StackPanel" to trigger the different switches on the remote control. Here is a snippet of the `MainPage.xaml` file:

    <phone:PhoneApplicationPage>
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <!-- [...] -->

            <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <TextBlock TextWrapping="Wrap" Text="Host" VerticalAlignment="Top"/>
                <TextBox x:Name="host" Height="72" TextWrapping="Wrap" Text="192.168.178.46" VerticalAlignment="Top"/>
                <TextBlock TextWrapping="Wrap" Text="Port" VerticalAlignment="Top"/>
                <TextBox x:Name="port" Height="72" TextWrapping="Wrap" Text="4223" VerticalAlignment="Top"
                         InputScope="Number"/>
                <TextBlock TextWrapping="Wrap" Text="UID (Industrial Quad Relay Bricklet)" VerticalAlignment="Top"/>
                <TextBox x:Name="uid" Height="72" TextWrapping="Wrap" Text="ctG" VerticalAlignment="Top"/>
                <ProgressBar x:Name="progress" Height="10" VerticalAlignment="Top"/>
                <Button x:Name="connect" Content="Connect" VerticalAlignment="Top" Click="Connect_Click"/>
                <StackPanel Orientation="Horizontal" Height="100" HorizontalAlignment="Center">
                    <Button x:Name="a_on" Content="A On" HorizontalAlignment="Center" Click="Aon_Click"/>
                    <Button x:Name="a_off" Content="A Off" HorizontalAlignment="Center" Click="Aoff_Click"/>
                    <Button x:Name="b_on" Content="B On" HorizontalAlignment="Center" Click="Bon_Click"/>
                    <Button x:Name="b_off" Content="B Off" HorizontalAlignment="Center" Click="Boff_Click"/>
                </StackPanel>
            </StackPanel>
        </Grid>
    </phone:PhoneApplicationPage>

Now the GUI layout is finished. The initial GUI configuration is done in the constructor of the `MainPage` class. The progress bar is initially hidden and indeterminate mode is enabled, because the duration of a connection attempt is unknown:

    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();

            progress.Visibility = Visibility.Collapsed;
            progress.IsIndeterminate = true;
        }
    }

## Step 2: Discover Bricks and Bricklets

This step is similar to step 1 in the Read out Smoke Detectors using C# project. We apply some changes to make it work in a GUI program and instead of using the `EnumerateCallback` to discover the Industrial Quad Relay Bricklet its UID has to be specified. This approach allows to pick the correct Industrial Quad Relay Bricklet even if multiple are connected to the same host at once.

We don't want to call the `Connect()` method directly, because it might take a moment and block the GUI during that period of time. Instead `Connect()` will be called from a `BackgroundWorker`, so it will run in the background and the GUI stays responsive:

    public partial class MainPage : PhoneApplicationPage
    {
        private IPConnection ipcon = null;
        private BrickletIndustrialQuadRelay relay = null;
        private BackgroundWorker connectWorker = null;

        public MainPage()
        {
            // [...]

            connectWorker = new BackgroundWorker();
            connectWorker.DoWork += ConnectWorker_DoWork;
        }

        private void ConnectWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string[] argument = e.Argument as string[];

            ipcon = new IPConnection();
            relay = new BrickletIndustrialQuadRelay(argument[2], ipcon);

            ipcon.Connect(argument[0], Convert.ToInt32(argument[1]));
        }

        private void Connect()
        {
            string[] argument = new string[3];

            argument[0] = host.Text;
            argument[1] = port.Text;
            argument[2] = uid.Text;

            connectWorker.RunWorkerAsync(argument);
        }
    }

The `BackgroundWorker` has an `DoWork` event that will be triggered from another thread after `RunWorkerAsync()` was called. The host, port and UID configuration is passed to the `DoWork` event. This is necessary, because the `ConnectWorker_DoWork()` method needs this information, but is not allowed to access the GUI elements. Now the `ConnectWorker_DoWork()` method can create an `IPConnection` and `BrickletIndustrialQuadRelay` object and call the `Connect()` method.

Finally, the `BackgroundWorker` should be started when the connect button is clicked. To do this the `Connect_Click()` method is bound to the `Click` event of the connect button:

    private void Connect_Click(object sender, RoutedEventArgs e)
    {
        Connect();
    }

Host, port and UID can now be configured and a click on the connect button establishes the connection.

## Step 3: Triggering Switches

The connection is established and the Industrial Quad Relay Bricklet is found but there is no logic yet to trigger a switch on the remote control if one of the four trigger buttons is clicked.

According to the hardware setup section the inputs of the remote control should be connected as follows:

Signal | Relay
--- | ---
A | 0
B | 1
ON | 2
OFF | 3

To trigger the switch "A ON" of the remote control the relays 0 and 2 of the Industrial Quad Relay Bricklet have to be closed. This is represented by the selection mask `(1 << 0) | (1 << 2)`.

To do `<X><Y>_Click()` methods are bound to the `Click` events of the trigger buttons. They start another `BackgroundWorker` that in turn calls the `SetMonoflop()` method of the Industrial Quad Relay Bricklet to trigger the corresponding switch on the remote control:

    public partial class MainPage : PhoneApplicationPage
    {
        // [...]

        private BackgroundWorker triggerWorker = null;

        public MainPage()
        {
            // [...]

            triggerWorker = new BackgroundWorker();
            triggerWorker.DoWork += TriggerWorker_DoWork;
        }

        private void TriggerWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int selectionMask = (int)e.Argument;

            relay.SetMonoflop(selectionMask, 15, 500);
        }

        private void Aon_Click(object sender, RoutedEventArgs e)
        {
            triggerWorker.RunWorkerAsync((1 << 0) | (1 << 2));
        }

        private void Aoff_Click(object sender, RoutedEventArgs e)
        {
            triggerWorker.RunWorkerAsync((1 << 0) | (1 << 3));
        }

        private void Bon_Click(object sender, RoutedEventArgs e)
        {
            triggerWorker.RunWorkerAsync((1 << 1) | (1 << 2));
        }

        private void Boff_Click(object sender, RoutedEventArgs e)
        {
            triggerWorker.RunWorkerAsync((1 << 1) | (1 << 3));
        }
    }

The call to `SetMonoflop(selectionMask, 15, 500)` closes the selected relays for 0.5s then opens them again.

That's it. If we would copy these three steps together in one project, we would have a working app that allows a smart phone to control remote mains switches using their hacked remote control!

We don't have a disconnect button yet and the trigger buttons can be clicked before the connection is established. We need some more GUI logic!

## Step 4: More GUI logic

There is no button to close the connection again after it got established. The connect button could do this. When the connection is established it should allow to disconnect it again:

    public partial class MainPage : PhoneApplicationPage
    {
        // [...]

        public MainPage()
        {
            // [...]

            connectWorker.RunWorkerCompleted += ConnectWorker_RunWorkerCompleted;
        }

        private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            connect.Content = "Disconnect";
        }
    }

The `ConnectWorker_RunWorkerCompleted()` method is called after `ConnectWorker_DoWork()`. It changes the text on the button to "Disconnect". The `Connect_Click()` method now decides dynamically what to do. If there is no connection it calls `Connect()`, if there is a connection is runs the disconnect background worker:

    private void Connect_Click(object sender, RoutedEventArgs e)
    {
        if (ipcon == null || ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_DISCONNECTED)
        {
            Connect();
        }
        else
        {
            disconnectWorker.RunWorkerAsync();
        }
    }

The disconnect background worker calls the `Disconnect()` method in the background, because it might take a moment and block the GUI during that period of time:

    public partial class MainPage : PhoneApplicationPage
    {
        // [...]

        private BackgroundWorker disconnectWorker = null;

        public MainPage()
        {
            // [...]

            disconnectWorker = new BackgroundWorker();
            disconnectWorker.DoWork += DisconnectWorker_DoWork;
            disconnectWorker.RunWorkerCompleted += DisconnectWorker_RunWorkerCompleted;
        }

        private void DisconnectWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            ipcon.Disconnect();
        }

        private void DisconnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            connect.Content = "Connect";
        }
    }

Finally, the user should not be able to change the content of the text fields during the time the connection gets established and the trigger buttons should not be clickable if there is no connection.

The `connectWorker` and the `disconnectWorker` are extended to disable and enable the GUI elements according to the current connection state:

    private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // [...]

        connect.IsEnabled = true;
        a_on.IsEnabled = true;
        a_off.IsEnabled = true;
        b_on.IsEnabled = true;
        b_off.IsEnabled = true;
    }

    private void DisconnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // [...]

        host.IsEnabled = true;
        port.IsEnabled = true;
        uid.IsEnabled = true;
        connect.IsEnabled = true;
    }

    private void Connect()
    {
        host.IsEnabled = false;
        port.IsEnabled = false;
        uid.IsEnabled = false;
        connect.IsEnabled = false;
        a_on.IsEnabled = false;
        a_off.IsEnabled = false;
        b_on.IsEnabled = false;
        b_off.IsEnabled = false;

        // [...]
    }

But the program is not yet robust enough. What happens if it can't connect? What happens if there is no Industrial Quad Relay Bricklet with the given UID?

What we need is error handling!

## Step 5: Error Handling and Reporting

We will use similar principals as in step 4 of the Read out Smoke Detectors using C# project, but with some changes to make it work in a GUI program.

We can't just use `System.Console.WriteLine()` for error reporting because there is no console window in an app. Instead message boxes are used.

The `Connect()` method has to validate the user input before using it. An `MessageBox` is used to report possible problems. Also the progress bar is made visible to indicate that a connection attempt is in progress:

    private void Connect()
    {
        if (host.Text.Length == 0 || port.Text.Length == 0 || uid.Text.Length == 0)
        {
            MessageBox.Show("Host/Port/UID cannot be empty", "Error", MessageBoxButton.OK);
            return;
        }

        progress.Visibility = Visibility.Visible;

        // [...]
    }

    private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progress.Visibility = Visibility.Collapsed;

        // [...]
    }

Then the `ConnectWorker_DoWork()` method needs to be able to report its result. But it is not allowed to interact with the GUI, but it can assign a value to the `Result` member of the `DoWorkEventArgs` parameter that is then passed to the `ConnectWorker_RunWorkerCompleted()` method. We use this `enum` that represents the three possible outcomes of a connection attempt:

    enum ConnectResult
    {
        SUCCESS,
        NO_CONNECTION,
        NO_DEVICE
    }

- SUCCESS: The connection got established and an Industrial Quad Relay Bricklet with the given UID was found.
- NO_CONNECTION: The connection could not be established.
- NO_DEVICE: The connection got established but there was no Industrial Quad Relay Bricklet with the given UID.

The `GetIdentity()` method is used to check that the device for the given UID really is an Industrial Quad Relay Bricklet. If this is not the case then the connection gets closed:

    private void ConnectWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        string[] argument = e.Argument as string[];

        ipcon = new IPConnection();

        try
        {
            relay = new BrickletIndustrialQuadRelay(argument[2], ipcon);
        }
        catch (ArgumentOutOfRangeException)
        {
            e.Result = ConnectResult.NO_DEVICE;
            return;
        }

        try
        {
            ipcon.Connect(argument[0], Convert.ToInt32(argument[1]));
        }
        catch (System.IO.IOException)
        {
            e.Result = ConnectResult.NO_CONNECTION;
            return;
        }
        catch (ArgumentOutOfRangeException)
        {
            e.Result = ConnectResult.NO_CONNECTION;
            return;
        }

        try
        {
            string uid;
            string connectedUid;
            char position;
            byte[] hardwareVersion;
            byte[] firmwareVersion;
            int deviceIdentifier;

            relay.GetIdentity(out uid, out connectedUid, out position,
                              out hardwareVersion, out firmwareVersion, out deviceIdentifier);

            if (deviceIdentifier != BrickletIndustrialQuadRelay.DEVICE_IDENTIFIER)
            {
                ipcon.Disconnect();
                e.Result = ConnectResult.NO_DEVICE;
                return;
            }
        }
        catch (TinkerforgeException)
        {
            try
            {
                ipcon.Disconnect();
            }
            catch (NotConnectedException)
            {
            }

            e.Result = ConnectResult.NO_DEVICE;
            return;
        }

        e.Result = ConnectResult.SUCCESS;
    }

Now the `ConnectWorker_RunWorkerCompleted()` method has to handle this three outcomes. First the progress bar is dismissed:

    private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        ConnectResult result = (ConnectResult)e.Result;

        progress.Visibility = Visibility.Collapsed;

In case the connection attempt was successful the original logic stays the same:

    if (result == ConnectResult.SUCCESS)
    {
        connect.Content = "Disconnect";
        connect.IsEnabled = true;
        a_on.IsEnabled = true;
        a_off.IsEnabled = true;
        b_on.IsEnabled = true;
        b_off.IsEnabled = true;
    }

In the error case we use a `MessageBox` and set the error message according to the connection result:

    else
    {
        string message;
        MessageBoxResult retry;

        if (result == ConnectResult.NO_CONNECTION) {
            message = "Could not connect to " + host.Text + ":" + port.Text + ". Retry?";
        } else { // ConnectResult.NO_DEVICE
            message = "Could not find Industrial Quad Relay Bricklet [" + uid.Text + "]. Retry?";
        }

        retry = MessageBox.Show(message, "Error", MessageBoxButton.OKCancel);

Retry to connect or cancel the connection attempt, according to the result of the message box:

            if (retry == MessageBoxResult.OK) {
                Connect();
            } else {
                host.IsEnabled = true;
                port.IsEnabled = true;
                uid.IsEnabled = true;
                connect.Content = "Connect";
                connect.IsEnabled = true;
            }
        }
    }

Now the app can connect to an configurable host and port and trigger a button on the remote control of your garage door opener using an Industrial Quad Relay Bricklet.

## Step 6: Persistent Configuration and State

The app doesn't store its configuration yet. Windows Phone provides the `IsolatedStorageSettings` class to take care of this. In `OnNavigatedTo()` the configuration is restored and the connection is reestablished if it was active before:

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        bool connected = false;

        try
        {
            host.Text = settings["host"] as string;
            port.Text = settings["port"] as string;
            uid.Text = settings["uid"] as string;
            connected = settings["connected"].Equals(true);
        }
        catch (KeyNotFoundException)
        {
            settings["host"] = host.Text;
            settings["port"] = port.Text;
            settings["uid"] = uid.Text;
            settings["connected"] = connected;
            settings.Save();
        }

        if (connected &&
            (ipcon == null ||
             ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_DISCONNECTED))
        {
            Connect();
        }
    }

In `OnNavigatedFrom()` the configuration is then stored again:

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        settings["host"] = host.Text;
        settings["port"] = port.Text;
        settings["uid"] = uid.Text;

        if (ipcon != null && ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_CONNECTED)
        {
            settings["connected"] = true;
        }
        else
        {
            settings["connected"] = false;
        }

        settings.Save();
    }

Now the configuration and state is stored persistent across a restart of the app.

## Step 7: Everything put together

That's it! We are done with the app for our hacked garage door opener remote control.

Now all of the above put together (Download):

    using System;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.IO.IsolatedStorage;
    using System.Windows;
    using System.Windows.Navigation;
    using Microsoft.Phone.Controls;
    using Microsoft.Phone.Shell;
    using Tinkerforge;

    namespace PowerOutletControl
    {
        public partial class MainPage : PhoneApplicationPage
        {
            private IPConnection ipcon = null;
            private BrickletIndustrialQuadRelay relay = null;
            private BackgroundWorker connectWorker = null;
            private BackgroundWorker disconnectWorker = null;
            private BackgroundWorker triggerWorker = null;
            private IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;

            enum ConnectResult
            {
                SUCCESS,
                NO_CONNECTION,
                NO_DEVICE
            }

            public MainPage()
            {
                InitializeComponent();

                progress.Visibility = Visibility.Collapsed;
                progress.IsIndeterminate = true;

                a_on.IsEnabled = false;
                a_off.IsEnabled = false;
                b_on.IsEnabled = false;
                b_off.IsEnabled = false;

                connectWorker = new BackgroundWorker();
                connectWorker.DoWork += ConnectWorker_DoWork;
                connectWorker.RunWorkerCompleted += ConnectWorker_RunWorkerCompleted;

                disconnectWorker = new BackgroundWorker();
                disconnectWorker.DoWork += DisconnectWorker_DoWork;
                disconnectWorker.RunWorkerCompleted += DisconnectWorker_RunWorkerCompleted;

                triggerWorker = new BackgroundWorker();
                triggerWorker.DoWork += TriggerWorker_DoWork;
            }

            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                bool connected = false;

                try
                {
                    host.Text = settings["host"] as string;
                    port.Text = settings["port"] as string;
                    uid.Text = settings["uid"] as string;
                    connected = settings["connected"].Equals(true);
                }
                catch (KeyNotFoundException)
                {
                    settings["host"] = host.Text;
                    settings["port"] = port.Text;
                    settings["uid"] = uid.Text;
                    settings["connected"] = connected;
                    settings.Save();
                }

                if (connected &&
                    (ipcon == null ||
                     ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_DISCONNECTED))
                {
                    Connect();
                }
            }

            protected override void OnNavigatedFrom(NavigationEventArgs e)
            {
                settings["host"] = host.Text;
                settings["port"] = port.Text;
                settings["uid"] = uid.Text;

                if (ipcon != null && ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_CONNECTED)
                {
                    settings["connected"] = true;
                }
                else
                {
                    settings["connected"] = false;
                }

                settings.Save();
            }

            private void ConnectWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                string[] argument = e.Argument as string[];

                ipcon = new IPConnection();

                try
                {
                    relay = new BrickletIndustrialQuadRelay(argument[2], ipcon);
                }
                catch (ArgumentOutOfRangeException)
                {
                    e.Result = ConnectResult.NO_DEVICE;
                    return;
                }

                try
                {
                    ipcon.Connect(argument[0], Convert.ToInt32(argument[1]));
                }
                catch (System.IO.IOException)
                {
                    e.Result = ConnectResult.NO_CONNECTION;
                    return;
                }
                catch (ArgumentOutOfRangeException)
                {
                    e.Result = ConnectResult.NO_CONNECTION;
                    return;
                }

                try
                {
                    string uid;
                    string connectedUid;
                    char position;
                    byte[] hardwareVersion;
                    byte[] firmwareVersion;
                    int deviceIdentifier;

                    relay.GetIdentity(out uid, out connectedUid, out position,
                                      out hardwareVersion, out firmwareVersion, out deviceIdentifier);

                    if (deviceIdentifier != BrickletIndustrialQuadRelay.DEVICE_IDENTIFIER)
                    {
                        ipcon.Disconnect();
                        e.Result = ConnectResult.NO_DEVICE;
                        return;
                    }
                }
                catch (TinkerforgeException)
                {
                    try
                    {
                        ipcon.Disconnect();
                    }
                    catch (NotConnectedException)
                    {
                    }

                    e.Result = ConnectResult.NO_DEVICE;
                    return;
                }

                e.Result = ConnectResult.SUCCESS;
            }

            private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                ConnectResult result = (ConnectResult)e.Result;

                progress.Visibility = Visibility.Collapsed;

                if (result == ConnectResult.SUCCESS)
                {
                    connect.Content = "Disconnect";
                    connect.IsEnabled = true;
                    a_on.IsEnabled = true;
                    a_off.IsEnabled = true;
                    b_on.IsEnabled = true;
                    b_off.IsEnabled = true;
                }
                else
                {
                    string message;
                    MessageBoxResult retry;

                    if (result == ConnectResult.NO_CONNECTION) {
                        message = "Could not connect to " + host.Text + ":" + port.Text + ". Retry?";
                    } else { // ConnectResult.NO_DEVICE
                        message = "Could not find Industrial Quad Relay Bricklet [" + uid.Text + "]. Retry?";
                    }

                    retry = MessageBox.Show(message, "Error", MessageBoxButton.OKCancel);

                    if (retry == MessageBoxResult.OK) {
                        Connect();
                    } else {
                        host.IsEnabled = true;
                        port.IsEnabled = true;
                        uid.IsEnabled = true;
                        connect.Content = "Connect";
                        connect.IsEnabled = true;
                    }
                }
            }

            private void DisconnectWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                try
                {
                    ipcon.Disconnect();
                    e.Result = true;
                }
                catch (NotConnectedException)
                {
                    e.Result = false;
                }
            }

            private void DisconnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if ((bool)e.Result)
                {
                    host.IsEnabled = true;
                    port.IsEnabled = true;
                    uid.IsEnabled = true;
                    connect.Content = "Connect";
                }

                connect.IsEnabled = true;
            }

            private void TriggerWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                int selectionMask = (int)e.Argument;

                try
                {
                    relay.SetMonoflop(selectionMask, 15, 500);
                }
                catch (TinkerforgeException)
                {
                }
            }

            private void Connect()
            {
                if (host.Text.Length == 0 || port.Text.Length == 0 || uid.Text.Length == 0)
                {
                    MessageBox.Show("Host/Port/UID cannot be empty", "Error", MessageBoxButton.OK);
                    return;
                }

                host.IsEnabled = false;
                port.IsEnabled = false;
                uid.IsEnabled = false;
                connect.IsEnabled = false;
                a_on.IsEnabled = false;
                a_off.IsEnabled = false;
                b_on.IsEnabled = false;
                b_off.IsEnabled = false;

                progress.Visibility = Visibility.Visible;

                string[] argument = new string[3];

                argument[0] = host.Text;
                argument[1] = port.Text;
                argument[2] = uid.Text;

                connectWorker.RunWorkerAsync(argument);
            }

            private void Connect_Click(object sender, RoutedEventArgs e)
            {
                if (ipcon == null || ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_DISCONNECTED)
                {
                    Connect();
                }
                else
                {
                    connect.IsEnabled = false;
                    a_on.IsEnabled = false;
                    a_off.IsEnabled = false;
                    b_on.IsEnabled = false;
                    b_off.IsEnabled = false;

                    disconnectWorker.RunWorkerAsync();
                }
            }

            private void Aon_Click(object sender, RoutedEventArgs e)
            {
                triggerWorker.RunWorkerAsync((1 << 0) | (1 << 2));
            }

            private void Aoff_Click(object sender, RoutedEventArgs e)
            {
                triggerWorker.RunWorkerAsync((1 << 0) | (1 << 3));
            }

            private void Bon_Click(object sender, RoutedEventArgs e)
            {
                triggerWorker.RunWorkerAsync((1 << 1) | (1 << 2));
            }

            private void Boff_Click(object sender, RoutedEventArgs e)
            {
                triggerWorker.RunWorkerAsync((1 << 1) | (1 << 3));
            }
        }
    }

The content of this page is licensed under Creative Commons Attribution 3.0 Unported License.