In the last part, we got an XPanel connected to our VC-4 room and made it change state a little. We aren’t going to add much in this part, just a few changes to make things a little nicer for us.
The first problem you might have noticed is when the XPanel first opens, the background is bright and when you click the Power button, nothing happens. If you click again, the power state catches up and the background goes dim. We should fix this so that when the XPanel connects, it synchronizes state.
Keeping Organized
But before we go into that, have you noticed we have a few join numbers sprinkled around our program? It’s manageable for now, but what happens when we have hundreds of joins we’re trying to track. Here’s how a lot of SIMPL# programmers stay organized with that: enums. Lets add a couple to our namespace just above our class:
namespace Vc4Test1
{
public enum SystemJoins
{
PowerOn = 10,
PowerOff = 11,
PowerToggle = 12,
PowerTransition = 13
}
public enum SystemFb
{
PowerOnFb = 10,
PowerOffFb = 11,
PowerToggleFb = 12
}
public class ControlSystem : CrestronControlSystem
{
Now we can go into our program and replace those random join numbers with a meaningful name. And the best part, if we have to move our joins around, we just change the value up at the top of the namespace rather than hunt through our entire program. There are a couple places we need to update:
public override void InitializeSystem()
{
try
{
tp1 = new XpanelForSmartGraphics(0x03, this);
tp1.SigChange += tp_SigChange;
tp1.BooleanOutput[(uint)SystemJoins.PowerToggle].UserObject = new Action<bool>(press => { if (press) ToggleSystemPower(); });
tp1.Register();
}
catch (Exception e)
{
ErrorLog.Error("Error in InitializeSystem: {0}", e.Message);
}
}
void ToggleSystemPower()
{
bSystemPowerOn = !bSystemPowerOn;
tp1.BooleanInput[(uint)SystemFb.PowerOnFb].BoolValue = bSystemPowerOn;
tp1.BooleanInput[(uint)SystemFb.PowerOffFb].BoolValue = !bSystemPowerOn;
}
Notice that we have to cast our join enums to uint
to satisfy the C# compiler. At least now when we glance through this code and see that we’re setting SystemFb.PowerOnFb
we can guess what it does versus only seeing the number 10.
Staying in Sync
Now lets handle synchronizing the XPanel state when it connects:
public override void InitializeSystem()
{
try
{
tp1 = new XpanelForSmartGraphics(0x03, this);
tp1.OnlineStatusChange += tp_OnlineChange;
tp1.UserSpecifiedObject = new Action<bool>(online => { if (online) UpdateFeedback(); });
tp1.SigChange += tp_SigChange;
tp1.BooleanOutput[(uint)SystemJoins.PowerToggle].UserObject = new Action<bool>(press => { if (press) ToggleSystemPower(); });
tp1.Register();
}
catch (Exception e)
{
ErrorLog.Error("Error in InitializeSystem: {0}", e.Message);
}
}
Devices can also have UserObject
s attached to them (but they’re named UserSpecifiedObject
instead). We follow the same pattern: assign an Action<bool>
delegate that will call the UpdateFeedback
method when the device comes online. Let’s define that method now:
void UpdateFeedback()
{
tp1.BooleanInput[(uint)SystemFb.PowerOnFb].BoolValue = bSystemPowerOn;
tp1.BooleanInput[(uint)SystemFb.PowerOffFb].BoolValue = !bSystemPowerOn;
tp1.BooleanInput[(uint)SystemFb.PowerToggleFb].BoolValue = bSystemPowerOn;
}
And we need to define the tp_OnlineChange
method that will call our delegate:
public void tp_OnlineChange(GenericBase dev, OnlineOfflineEventArgs args)
{
var obj = dev.UserSpecifiedObject;
if (obj is Action<bool>)
{
var func = (Action<bool>)obj;
func(args.DeviceOnLine);
}
}
And lastly, we should update our ToggleSystemPower method to call UpdateFeedback instead:
void ToggleSystemPower()
{
bSystemPowerOn = !bSystemPowerOn;
UpdateFeedback();
}
We’re on a roll, but let’s add one more thing…
Finishing Touches
Let’s make sure to send the system state as readable text to the panel AFTER the transition finishes. First we need to add another join to our enum:
public enum SystemJoins
{
PowerOn = 10,
PowerOff = 11,
PowerToggle = 12,
PowerTransition = 13
}
And then we need to add another callback:
public override void InitializeSystem()
{
try
{
tp1 = new XpanelForSmartGraphics(0x03, this);
tp1.OnlineStatusChange += tp_OnlineChange;
tp1.UserSpecifiedObject = new Action<bool>(online => { if (online) UpdateFeedback(); });
tp1.SigChange += tp_SigChange;
tp1.BooleanOutput[(uint)SystemJoins.PowerToggle].UserObject = new Action<bool>(press => { if (press) ToggleSystemPower(); });
tp1.BooleanOutput[(uint)SystemJoins.PowerTransition].UserObject = new Action<bool>(done => { if (done) UpdatePowerStatusText(); });
tp1.Register();
}
catch (Exception e)
{
ErrorLog.Error("Error in InitializeSystem: {0}", e.Message);
}
}
Then create the UpdatePowerStatusText
method:
void UpdatePowerStatusText()
{
if (bSystemPowerOn)
tp1.StringInput[10].StringValue = "ON";
else
tp1.StringInput[10].StringValue = "OFF";
}
Build the program, upload it to the VC-4 server, recreate your room and start it up. Now when the power feedback transitions, you’ll see the new state update on the panel. Not much visual difference from the last stage, but under the hood we’ve done a few changes to make things easier for us later.

As always, you can check out this code on my GitHub.
This stuff is AWESOME!! Thank you for doing this. Learning LOTS. -Greg.
LikeLike
Thank you, Greg! That’s good to hear. I hoping to do more with VC-4 when I can find the time.
LikeLike