I wouldn’t describe the process of learning to write a driver for Crestron Home as a learning curve. It’s more like a learning cliff. Without the help of Daniel Berlin and Oliver Hall, I don’t know if I would have made it through the process. Both were very generous with their time and showed an incredible amount of patience answering my questions. Hall’s company, Ultamation, is offering consulting assistance for organizations that need help developing drivers for Crestron Home. More info on that can be found here.
Why It Is So Hard to Write a Crestron Home Driver
Before explaining how to write a driver for Crestron Home, it is important to understand why it is so challenging. Crestron has invested a lot of time and effort into the development of online documentation for writing Crestron Home drivers. I’m sure that the documentation that Crestron has created works very well for their internal development team who are all experienced C# programmers. However, I believe it is lacking in a number of ways when used by a Crestron programmer working for a CSP or dealer.
First, it is written for people with computer science degrees and more specifically education in object-oriented programming. Many people employed by Crestron dealers work their way up from being an installer to becoming a Crestron programmer. For many years, these people have been successful in programming Crestron systems in SimplWindows and Simpl+. When it comes to learning to write drivers in C# using Microsoft Visual Studio, Crestron appears to be drawing a line in the sand and saying, “If you don’t have an educational background in computer programming, then don’t go here.”
I say this because the documentation isn’t written for beginning C# programmers. It is riddled with the jargon of object-oriented programming that makes it exceedingly difficult for anyone without a computer science degree to understand.
Second, the number of code snippets (short coding examples) in the documentation that show exactly what the documentation is trying to explain to the reader is limited or at least a bit challenging to find. Crestron has invested in writing a full set of sample drivers that you need to dig into to see how anything is done. Unfortunately, the samples are challenging to use as a learning tool.
First, they include very few comments that explain how the code functions. Again, Crestron assumes that the person trying to read through the sample drivers already has a strong background in C# and their class library, so the function of the code is simply self evident.
Second, Crestron tried to pack so much functionality into sample drivers they wrote (I worked primarily with the two extension drivers included in the samples) that figuring out what code is relevant for the specific piece of functionality that you are trying to implement can be very difficult – especially with a lack of comments in the code.
The API Reference documentation is an even worse example of this. The API that Crestron has created for writing drivers is very extensive. However, I haven’t found a single code snippet anywhere in the API Reference. Compare this to Microsoft’s .net documentation where code snippets are almost everywhere and provide a clear picture of how to use an API call.
Development of the user interface (UI) for a driver where data could be displayed for the user or where the user might enter data that affects how the driver operates is very different than what a Crestron programmer who has been using SimplWindows and VTProe to develop Crestron systems is used to.
In the documentation there are welcome examples of how to define the user interface for a Crestron Home driver in the XML file where the UI is specified. In fact, the documentation includes examples of the XML statements to define each different UI element (checkbox, text entry field, etc.). However, there aren’t any examples showing how the C# code in your driver needs to be written to interact with the UI elements. Those are hidden away in another section of the documentation. Without a photographic memory, you won’t remember where you saw these code examples and put the two together. C# code snippets should be included with each XML UI example or, at a minimum, hyperlinks to take the reader to where the C# examples are located.
[More from Jay Basen – How to Use Amazon Alexa for Whole-House Audio System Control]
If this doesn’t deter you from taking on the challenge of writing drivers for Crestron Home, I have created a template that can serve as the starting point for a new extension driver based on the first driver that I wrote. I won’t promise that everything about the template is perfect, as it is based on my current level of understanding of writing drivers for Crestron Home. However, I have used it as the basis for creating additional drivers and it does work.
The remainder of this article looks at the structure of a Crestron Home extension driver and includes code snippets from the template I created to explain how to write a driver from scratch. In the resources section of the article is a link to my GitHub where you can download the template.
I do have to apologize to the C# purists out there that I am a firm believer in white space when it comes to coding. For example, today’s norm for naming something in programming is thisIsTheNameOfSomething. I find that things named using this format make code much more challenging to read and therefore much more challenging to understand.
Imagine if you were trying to read the novel War and Peace by Leo Tolstoy and all 1,225 pages of the novel looked like this:
“WeCanKnowOnlyThatWeKnowNothing.AndThatIsTheHighestDegreeOfHumanWisdom.”
On today’s large computer monitors pixels are not an endangered species that need to be preserved so, as an alternative, I name things in my code using the format This_Is_The_Name_Of_Something, which I personally find makes it much more legible and have been using since I started writing software professionally in the 1970s. Old habits are hard to change or possibly I’ve just become stodgy.
What Kind of Driver Do You Want to Write?
There isn’t just one kind of driver for Crestron home; there are different driver types depending on the type of equipment your driver will interface with. Crestron natively supports the development of drivers for AV receivers, AV switchers, Blu-ray players, cable boxes, flat panel displays, pool controllers, projectors, security systems, and video servers.
If the device that you want to create a driver for doesn’t fit into the above categories, then you either need to write a platform driver or an extension driver.
An extension device would be any device that is outside of the above list where there would be a simple one-to-one relationship between the device and a driver to communicate with it. Examples could be a weather station or an outdoor air quality monitor.
A platform driver can be viewed as a specialized type of extension device. For example, say you wanted to write a driver for a new kind of smart relay. You might have 20 of these relays scattered around in different rooms of a house. So, in Crestron Home you would want to have a user interface for each relay in the rooms where the relays were physically located. In addition, the relays all communicate their status back to the Crestron processor, so you need a common block of code that manages this communication and then passes the status of a relay to the code module in the room where the relay is located. Devices of this type require a “platform driver” to be written.
This article is going to focus on how to develop an extension driver.
The Three Layers of an Extension Driver
The top layer of your driver is the fundamental driver class. This driver must extend AExtensionDevice and for a cloud connected device (as shown in the template) ICloudConnected. The code for this is as follows:
namespace Home_Extension_Template
{
public class Device_Name : AExtensionDevice, ICloudConnected
{
}
}
The Home_Extension_Template class then instantiates the transport and protocol layers of the driver (creates objects of the transport and protocol class type), integrate with the driver’s UI, accept triggers configured in the Actions & Events section of Crestron Home, and trigger Crestron Home sequences.
The transport class is the second layer of the driver. According to the documentation, it sets the communications settings for the driver. In the case of a cloud-connected device this doesn’t amount to much. However, it is important that the transport layer does the following so the driver will function properly.
namespace Home_Extension_Template
{
public class Home_Extension_Template_Transport : ATransportDriver
{
public Device_Name_Transport()
{
IsEthernetTransport = true;
IsConnected = true;
}
}
}
Finally, the protocol layer is where the work to communicate with your cloud connected device is all done. The protocol layer sends commands and receives responses from your device.
namespace Home_Extension_Template
{
public class Home_Extension_Template_Protocol : ABaseDriverProtocol, IDisposable
{
public Device_Name_Protocol(Device_Name_Transport transport, byte id) : base(transport, id)
{
Transport = transport;
}
}
}
More on the functionality of the protocol layer will be shown later in the article.
Additional Required Files
There are three additional files that are required in a driver:
- JSON Data File
- UI Definitions File
- Language Translations File
The JSON Data File contains general information about your driver, including the devices it supports, information about the communications settings required to communicate with your device, and a specification of the user interface that will be displayed when the driver is added to a room by the installer including settings, such as a device ID or IP address needed by your driver to communicate with your device. A good example of a JSON data file is shown in the template.
The UI Definitions File defines the UI (in XML) that the homeowner will see and use to interact with your driver. For example, in the driver I wrote to integrate a WeatherFlow Tempest Smart Weather System with a Crestron Home system, I included a UI to display weather data to the homeowner and to allow the homeowner to enable triggers that could drive sequences for events such as rain and high wind. For example, a text display field in the UI is defined as follows:
<textdisplay id=”DisplayTextID” title=”^DisplayTextLabel” line1label=”{DisplayText}”/>
“^DisplayTextLabel” tells the system to obtain the text to display from the UI Translations file so your driver can be translated into multiple languages. {DisplayText} is the text element that will be displayed for the user. Since it is surrounded by {} the system knows that the actual text will be supplied by the C# code in your driver as follows:
private const string Display_Text_Key = “DisplayText”;
private PropertyValue<string> Display_Text_Property;
Display_Text_Property = CreateProperty<string>(new PropertyDefinition(Display_Text_Key, null, DevicePropertyType.String));
Display_Text_Property.Value = “your text here”;
The Language Translation File includes all the text strings for the UI. Multiple translation files can be included with your driver, each for a different language. Entries in a language translation file take the form:
“DisplayTextLabel”: “This is some text”
Polling
In many cases, a driver needs to periodically poll a device or cloud service to obtain data from the device. Crestron makes this easy to accomplish. First, in your base driver class you will need the inherited methods Connect() and Disconnect(). These methods should call methods in your Protocol class Start() and Stop() as shown below:
public override void Connect()
{
base.Connect();
Connected = true;
Protocol.Start();
}
public override void Disconnect()
{
Protocol.Stop();
base.Disconnect();
Connected = false;
}
Then in your protocol class the Start() and Stop() methods are coded as follows:
public void Start()
{
PollingInterval = 60000;//60 seconds in ms
EnableAutoPolling = true;
}
public void Stop()
{
EnableAutoPolling = false;
}
When EnableAutoPolling is set to true, and the PollingInterval is set to a time period in milliseconds, an inherited method called Poll() will be called repeatedly at the polling interval. It is important to know that Poll will not be called after EnableAutoPolling is set to true until the polling interval has expired. So, if your polling interval is 10 minutes, and you don’t want the user to have to wait to see data from the connected device that you are going to need to first gather data from the device and then enable auto polling to continue data collection starting 10 minutes later. The poll method needs to be defined in your driver as follows:
protected override void Poll()
{
}
One important thing to understand is that polling is dependent on the Crestron Home processor believing that the driver is connected to the device. If you haven’t, for example, set “Connected = true,” then your polling method will never be called. So, unless the system thinks your driver is connected to your device/cloud service, even if you will actually be connecting and disconnecting to it within your polling routine, your driver won’t work.
Sequences and Events
An extension driver can be written to participate in Crestron Home sequences and to create events. Sequences are the Crestron Home equivalent of a SimplWindows Stepper symbol. Through a sequence, a series of actions can be triggered in a program at, for example, a specific time.
Events are triggered by a driver and can trigger other actions in a Crestron Home program; including a sequence.
Providing entry points in your program that can be triggered by a sequence is simple. In the code snippet shown below a method named “Sequence_Triggered_Method” can be called from a sequence. In the Actions & Events dialogs Sequence_Triggered_Method will be given the screen name from the driver’s translation file defined by the keyword “SequenceTriggeredMethodLabel”.
[ProgrammableOperation(“^SequenceTriggeredMethodLabel”)]
public void Sequence_Triggered_Method()
{
//Do Something
}
Again, “^SequenceTriggeredMethodLabel” will pull the actual text displayed from your language translation file.
An Event that can be used to trigger a sequence in a Crestron Home system is defined as follows in a driver.
[ProgrammableEvent]
public event EventHandler Event_ID;
Then the event can be triggered through the following code:
var Event = Event_ID;
Event?.Invoke(this, new EventArgs());
Additional Tricks and Caveats
There are a few other things that are important to understand and may save you time and frustration in your effort to learn to write an extension driver for Crestron Home.
When you download and install the Certified Driver SDK from the Crestron website, you should install it into a directory that doesn’t include the SDK version. For example, “C:\Crestron_SDK”. Then when Crestron releases an update to the SDK you can simply overwrite the version you have been using and all the references in you driver projects will now point to the new SDK. Otherwise you will have to delete all the SDK references in your Visual Studio projects and add them again.
Liberally placing Log statements throughout the code for a driver is exceedingly helpful for debugging. I personally start every method and finish every method with Log statements that tell you where you are in the code in the code when it executes. This has greatly helped me with debugging. You can wrap these statements in regions so they can easily be hidden and won’t distract you from the functionality of your method. Here is an example:
public void My_Method()
{
#region Debug Message
Log(“Project Name – Method Name – Start”);
#endregion Debug Message
//Do Stuff Here
#region Debug Message
Log(“Project Name – Method Name – Finish”);
#endregion Debug Message
}
If you create a driver from scratch, make sure you tell Visual Studio that the translation, UiDefinition, and JSON Data files should be copied to the Output directory and included in your project. Visual Studio will not do this by default and your project won’t run on your processor without them.
If you are creating an extension driver for a cloud connected device, then the CommunicationType should be set to 5 in your driver’s JSON data file. This is the proper setting for a cloud connected device but a value of 5 is not shown in the documentation.
If you’ve specified UserAttributes in your driver’s JSON file to create a user interface where an installer must enter data required for your driver to connect to the device you are interfacing with, such as an IP address, security token, etc., then this trick can save you time during testing. Enter your device’s data in the DefaultValue fields of the UserAttributes so you don’t have to reenter them each time you upload a new version of the driver for testing.
When testing a driver, you need to make sure that the old driver is fully deleted from you Crestron Home installation before adding the new version of the driver to the system. So, you need to use the Crestron Home Setup App to remove the driver from the room you installed it in. Then you need to back your way out of the room the driver was in, back out of the home setup app in general, wait two minutes, and reboot the processor to try and make sure that your system has fully synchronized with the change.
At that point you can use the Toolbox File Manager tool to delete the driver package from your Crestron Home system, use the File Manager tool to upload the new version of the driver to your Crestron Home system, and issue a toolbox console command to reboot your Crestron Home system a second time. Once the processor has finished rebooting, the new version of the driver will be available in the Crestron Home Setup app for installation into a room in Crestron Home. It is all too easy to screw this up or rush the process, and then the driver can get stuck in the room. If this happens, then you will have to fully reset your Crestron Home system to get things straightened out.
It is helpful to have a dedicated Crestron Home system for driver development as you will probably have to factory reset the system more often than you would like. I wasn’t waiting two minutes and rebooting my processor after removing the driver from my system. Sometimes it would work fine without these steps, but other times I ran into problems and had to factory reset my Crestron Home processor. I spoke to Crestron about the issue, and they told me to include the two-minute wait followed by a processor reboot in my development process.
You must remember to enter the command “enableprogramcmd” in the Toolbox Text Console for the Crestron Home system to display log messages in the Toolbox Text Console window so you can debug your driver. If you reset your processor, then you’ll have to remember to enter this command again.
Crestron Driver Resources
- The Crestron driver development documentation can be found here.
- The latest version of the Crestron Drivers SDK can be found on the above web site resources page here.
- The extension driver template can be downloaded from my GitHub here: https://github.com/jbasen/Crestron-Home-Extension-Driver-Template. Unfortunately, because the solution folder for a Crestron Home project is so large I am only able to add the source files to Github instead of a zip file of the entire solution.
Crestron Areas for Improvement
Besides the issues I outlined in the beginning of this article on the documentation and code samples that Crestron has produced, there are a few additional ways that Crestron could improve the process of creating drivers for Crestron Home.
To load your driver onto a Crestron Home processor you need to:
- Use the Crestron Home Setup App to remove any old versions of the driver that have been loaded into rooms on your Crestron Home processor.
- Wait two minutes
- Use Toolbox to reboot your processor
- Use Crestron Toolbox File Manager to delete your driver off of the processor
- Use Toolbox File Manager to load the new version of your driver onto the processor
- Use Toolbox to reboot your processor.
- Use the Crestron Home Setup App to add the driver into a room on the processor
First, this process is way too time consuming – especially the time you sit around waiting for the processor to reboot. Second, if you make a mistake, like forgetting to use the Crestron Home Setup App to remove the driver from a room before you use toolbox to delete it off the processor, you are probably going to need to factory reset your processor to correct the problem. Crestron needs to improve the workflow to make it faster and less prone to mistakes by:
- Allow the process to be automated with a script
- Create a console command to force the processor to delete the driver without having to wait two minutes followed by a reboot of the processor
- Create a console command that would make a driver uploaded to the processor available for use in the Crestron Home Setup App without having to go through the slow process of rebooting the processor a second time
Summary
Crestron does a good job of documenting the process of installing the driver SDK, creating a driver project in Visual Studio, and side loading the drive to your Crestron Home processor. Where the documentation falls down is the lack of C# code snippets to help less experienced programmers come up to speed quickly. Hopefully the template I created and the content of this article will help people over the remainder of the learning cliff required to write their first extension driver.