Best Practices
The following best practices should be followed when developing drivers with the SDK. Best practices are grouped by development tasks.
Driver Development
There are numerous different types of device drivers that can be developed using Crestron Driver framework. The framework currently supports
- AV Receiver
- AV Switcher
- Blu-ray Disc® Player
- Cable Box
- Crestron Home® Extension Device
- Flat Panel Display
- Multizone AV Receiver
- Platform (Gateway)
- Projector
- Video Server (Streaming Media)
Additionally, more device types will be added to the framework that will enable developers to add more controllable devices that can be used by all Crestron® control platforms, including Crestron Home® OS and AV Framework™ software.
The following best practices are designed to give you the best possible result when developing drivers:
- Follow the guidelines listed by Microsoft® in C# Coding Conventions at https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions.
- Name drivers in the format of “DeviceType_Manufacturer_BaseModel_Transport”. For example, an AV Receiver driver could be called “AvReceiver_Anthem_MRX-720_IP”.
- Allow the framework to do most of the work:
- If you have added all of the commands into the driver JSON correctly, including any response headers and feedback, the framework should be able to control the device without many changes by the developer.
- If you need to change how the driver either builds or sends commands, or how the driver handles feedback, we have provided a way for this to be accomplished.
- The driver can logically be broken up into four sections.
- Driver Transport: The transport used to send commands and receive responses.
- Driver Protocol: Handles how commands are built and sent to the device
- Driver Response Validation: Used to validate the responses from the connected device.
- Driver JSON: Used to input the data found in the manufacturer’s API, which is used to communicate with the connected device.
- The manufacturer will typically provide information on how to communicate with their devices including commands, responses, and communication details.
- Properly constructed JSON is essential for getting most of the work done in the driver.
- If this is not an IP driver, do not assume that all calls to ResponseValidation and DataHandler will contain the full message; it might come in byte-by-byte. Therefore, the driver should be written to collect the data until a full message is captured. This should be done in ValidateResponse(…).
- Most serial protocols will give a clear delimiter for the end of each response, which generally appears as a carriage return.
- Check the manufacturer documentation to ensure that you gather the response up to and including the delimiter before processing the response.
- The WarmUpTime should consider the amount of time it takes for the device to power on and the amount of time it takes until the driver is able to control the device.
- For example, a TV that uses Wake On LAN to power on might take five seconds to show an image but an additional ten seconds until the TV responds to any queries or it is controllable. The WarmUpTime in this case should be 15 seconds.
- The CoolDownTime should consider the amount of time it takes for the device to completely shut off and it is able to be powered back on again.
- For example, a receiver takes one second to shut off but an additional four seconds until it responds to a PowerPoll and it can power back on. The CoolDownTime in this case should be 5 seconds.
- Lamps used by these devices can easily be damaged if they are not sufficiently cooled before attempting to restart.
- Most manufacturers build in safeguards, but users should not feel as if there could be an issue with their equipment. The cooldown time is one way we can inform a user that the projector requires more time to cool down before it is powered on again.
- The GUID listed under GenralInformation in the JSON should be unique. Duplicates can happen when copying the JSON of one driver to create another driver. Visual Studio can create a new GUID in the registry format under Tools > Create GUID.
- Send commands to the device using the queuing logic instead of directly through the transport. Build the CommandSet and send it using SendCommand().
- If a device requires setup in its settings to enable control, concise instructions should be provided in the User Attributes section in the JSON. Refer to the following sample code.
- Avoid calling functionality that may not have existed in an older version of the driver framework to prevent MissingMethodExceptions when loading driver into systems containing an outdated framework version.
- If a driver uses anything that implements IDisposable (such as a CTimer used in a driver), it must be disposed of in an override of Dispose() and the base method must be called. Dispose() can be overridden in the protocol class.
- Check if EnableLogging is true before sending a string to the Log() method. Although the Log() method already contains a check for EnableLogging, checking beforehand prevents allocating a string in memory that will not be logged if EnableLogging is disabled.
- Methods that have a potential to cause exceptions should be placed in a try catch with a clear logging and a stack trace for easier field debugging.
- As an example, attempting to parse from a string into an int. If there are no numeric values in the string this will throw an exception.
- Visual Studio 2008 does not support TryParse.
- For APIs where a single poll command can specify multiple values, such as mute state, volume level, current input, and power status, the driver should only specify that command as a poll command, and must set the command group of ValidatedRxData in ValidateResponse(…) to the command group of that poll command to prevent time-outs. Along with this, ValidateResponse must directly call Protocol.ChooseDeconstructMethod and Protocol.DeConstruct<Feature> on all data that is not part of the single poll command.
- This is common for drivers that respond using JSON or HTML to have multiple statuses within the response. If this is the case, poll for one status and process all of the statuses at once.
- This can be done by overriding DataHandler, deserializing the response, formating a response for each status, and individually calling base.DataHandler for each.
- Alternatively, deserialize each response in ResponseValidation, then process and call deconstruct for each status except the one polled for. For example, if polling for power status and the device returns power status and mute status, process the mute status and call deconstructMute and return ValidatedRxData with power status.
- Set WaitForResponse to true on all drivers that use HTTP/HTTPS and ensure ValidateResponse(…) always returns ValidatedRxData.Ready/Ignore to true on each call to it.
- Simulating feedback can be done by calling DeConstruct<Feature> methods with the string they expect.
- Simulating feedback may be needed if the device takes a long time to respond to a polling query. This would occur mostly to ensure a good user experience.
- Since all devices are different, the driver developer should a user to feel like all equipment looks and acts the same when using Crestron hardware.
- Sending the expected feedback value to the framework as “fake” is essentially helping the device by telling the user what they did and eventually updating the feedback with the actual status from the device.
- Avoid calls to CrestronEnvironment.Sleep(…).
- This creates a new thread and can be an issue if multiple instances are creating new threads.
- This can have negative consequences depending on how many instances of the driver your program is using.
- When getting or creating an HttpWebRequest object, request.KeepAlive = false; must be added to the code prior to executing the request. This helps to prevent a memory leak in the .Net SDK.
- Each time an HTTPS URL is queried using the HttpWebRequest object, a timer is created that causes the authentication data used for the connection to expire. There is a bug that causes the timer to never fire, so one Timer object and one DelayPromise object will stay in memory for the life of the program or until the system runs out of memory and restarts.
- By setting the KeepAlive (CLR) property to false, it prevents the system from setting up the Timer and DelayPromise objects, preventing the memory leak.
- Ensure that all IsSupported flags are set before the driver is initialized. Updating flags after they have been set is not supported.
"UserAttributes": [
{
"TypeName": "MessageBox",
"ParameterId": "Configuration Notice 1",
"Label": "Setup Instructions",
"Description": "To enable IP Control on the device go to Home > Settings > Wireless and Networks > External Control > IP Control > set to 'ON'",
"Persistent": false,
"RequiredForConnection": "None",
"Data": {
"DataType": "",
"Mask": "",
"DefaultValue": ""
}
}
]
Device Inputs
When selecting Standard Commands, it is important to pick the closest matching Standard Command that is supported in the device SDK.
This is not difficult for devices such as TVs and DVD players because the connectors are straightforward (such as HDMI). However, AV Receiver devices are more complicated. Refer to AV Receiver Inputs for more information.
- For most devices, specify the Standard Command that closely matches the connector type. Refer to Audio/Video Connections for more information.
- The following SD video connections should be omitted from the input and output connector lists due to being phased out in the industry.
- Component
- Composite
- S-Video
- TV Antennas are included as part of the Media Service Provider and not as an Input source.
- If an input supports control or transport functions and/or does not have a physical connection, then it will need to be defined as a Media Service and not an Input.
- If an item is defined as a Media Service, then it should not be included as part of the Input list.
- For video devices that do not carry an audio output or include speakers, the input connections will need to be defined as video only.
- The preferred Standard Command is INPUT1 – INPUTx . The friendly name should resemble the name as notated on the device buttons or menu/device display.
- Input 1 = 1800
- Input 2 = 1801
- Input 3 = 1802
AV Receiver Inputs
AV Receivers use the following rules:
- The only INPUT connector types that should be used are GenericAudio or GenericAV.
- Devices are limited to a maximum of 50 supported Inputs.
- Attempt to choose the Standard Command that closely matches the Button Label or Display Name.
- The preferred Standard Command is INPUT1 – INPUTx . The friendly name should resemble the name as notated on the device buttons or menu/device display.
- Input 1 = 1800
- Input 2 = 1801
- Input 3 = 1802
- All Video inputs that support audio will need to specify with connector values of GenericAV (150) and must be specified under the audio section as GenericAudio (130) with a friendly name that includes “Audio Only."
- If the input is Audio only, then “Audio Only” should not be applied to the friendly name.
- All Video inputs that do not support audio will need to specify with connector values of GenericVideo (160).
- All audio inputs must be specified under the audio section as GenericAudio (130) with a friendly name that includes "Audio Only."
- The “Friendly Name” field is a way to identify the input as it is represented by the device. You may use the display name or name that is printed on the remote button. If it is an audio only or the audio portion of the video input, then "Audio Only" will need to be added to the friendly name.
- If an audio only input has the ability tp assign a video input to that source, such as a CD, Tuner, or a Phono, then these inputs need to be supported as GenericAV. This could be accomplished by using a standard command type of Input1 ~ 15 (or IN1 ~ IN15 for IR) and specifying the friendly name as listed for that input source (such as CD, Tuner or Phono). This would allow data to be passed through the packaging script and would allow support within Crestron Home and other applications.
- Use actual connectors and do not use the GenericAudio and GenericAV Audio Video Enums to define outputs.
- Multizone Receiver Inputs should be mapped exactly the same as the main zone.
- All available audio and video inputs must use the same standard commands as their main zone selections use. This will ensure that all input routes can be properly made in products that consume the drivers.
- Be aware that not all inputs available in the main zone can be selected in the remote zones. Refer to the manufacturer's user manual to determine which inputs can be selected from the remote zones.
- Check if the receiver will also allow for video source inputs to be selected in the remote zones.
Driver Overrides
Each device type is written with abstract classes. The methods are defined with the virtual keyword, which allows the developer to override any base class method that needs more attention than normal. When a method is overridden in a derived class, the developer must ensure that the method runs exactly as it would in the base class.
- Overrides give the developer the ability to change something that does not do what is expected from the framework.
- Once you override, you can still call the base after the change, or you can completely disregard the base and take over from here.
- If the base is not called, you must construct your commands properly in order to utilize the command queue. Calls to SendCommand() will be queued. Transport.Send() bypasses the queue.
- Always use the queue and allow the framework to handle everything.
- All commands that the developer creates or builds in the driver should be given a priority so the queue knows how to distribute them.
- Priorities using the CommandPriority enum are:
- Highest
- High
- Normal
- Low
- Lowest
- A Special priority is provided that has a higher priority than Highest. This should only be used in very unique circumstances where the command must take precedence over all other commands.
- Commands like Power should always have Highest priority, followed by Input or AudioInput with High priority. This will ensure that Power will always leave the queue first before Input.
- A common mistake is giving all commands a Normal priority. This will have a significant impact on queuing logic since commands will not be processed in the correct order.
- The following diagram shows a workflow of the queuing process.
File Storage
When drivers need to create and access persistent files, the appropriate file storage locations must be used.
Control Systems
For storing persistent files on a Crestron Home or regular control system, use the /user/Data/ThirdParty/<DeveloperName>/ directory, where <DeveloperName> is the name of the driver developer. Using this directory simplifies the driver logic for persistent file storage.
Example: /user/Data/ThirdParty/DriverDepot/device_config.json
Crestron Virtual Control (VC-4)
The /user/ folder for a Crestron Virtual Control server‑based control system is located behind a variable Linux path based on the room name .
A GetApplicationRootPath() SIMPL# Pro API call is provided that can be used as prefix to your persistent storage path. This call is designed for use in both VC-4 and physical control systems. When used in a VC-4 system, it will return the Linux path that includes the room ID.
Example: /opt/crestron/virtualcontrol/RunningPrograms/DISPLAYDRIVERDEMO
Troubleshooting
Many issues relating to the driver not working properly can be found in the driver JSON. Often, a driver's JSON will pass a validation script, which ensures that the JSON can be parsed. However, if any of the objects are out of place, the driver cannot properly use them.
The following image illustrates a properly constructed JSON schema.
When you run a JSON viewer, the JSON schema should appear as shown in the image above. In the case that your device is not an AV Receiver, your JSON would not have the AvrZoneData object. Otherwise, everything else remains the same.
- Input definitions can cause issues that can be problematic if not properly configured.
- When defining AV Inputs alongside Audio Only inputs, make sure that they use the same Standard Command enum values. These values are referred to as “type” in the definitions. Ensure that when selecting an input, the correct command gets sent for both AV and Audio Only inputs.
- Crestron Home also uses these “types” to map their input routing when configuring a new system.
- The standard command types should map the same through all remote zones as well. An AV Receiver that supports multiple zones should map all input selections to the same standard command types as the main zone.
- Aside from JSON issues, many issues could be related to any method overrides in the driver.
- If you override a method in the driver, you still must make the method run without any errors. If the override is causing any exceptions in the driver, you may need to add a try/catch in order to isolate the issue. If an exception is thrown, at least the driver will continue to run.
- If there is an exception, even in a try/catch, you should eliminate the exception using proper programming.
- Integer conversions are common places where exceptions are thrown if the string you are trying to convert does not contain a number.
- When logging exceptions, log a stack trace on the exception. This is helpful for identifying the issue and to show another developer where to begin.