Below is a walkthrough of how I migrated by HS3 event trigger to a HS4 event trigger. The intent is to try to provide others some insight into what I have learned in this area that should be useful to others. I derived this information from the HST Netcam and SamplePlugin implementations. The most valuable aspect of this should be the storyline of how it fits together. Once there is a grasp on the bigger picture then it becomes much easier to know what needs to be done in each conversion. This is the aspect of the HS4 development where HST engineers have an advantage over the external developers.
For each Trigger type that is to be shown in the HS Event trigger create a class to hold it. In the HSPI Initialize() procedure register it with such as for the class “MQTTReceiveTrigger”
The class has a name where it provides whatever is to be shown in the HS Event trigger list and subtrigger names that may be a null list
The class will contain three New(..) constructors with signatures to handle various ways it can be used
It will have a method that is invoked when the user selects this trigger type for an event. At this time the ConfigPage property is set to a Jui View Page that can be build using PageFactory. This replaces the HS3 TriggerBuildUI.
This the time that a unique ID for this page is established. This is made from the PageId implicitly given by HS and some text that will this class from other trigger classes supported by the plugin.
Since OnNewTrigger() was initiated by use action on the UI the Event page will be showing and now new rows will appear that shows what the user needs to complete. This information is what was setup in ConfigPage. HS4 will be calling IsFullyConfigured() to see if the user has had sufficient information completed. The user inputs are visible in the ConfigPage Views (i.e. controls setup when trigger class is initialized). For some controls such as a select list selection or a toggle click the user input will be available immediately. In others such as text input they will only be visible when the Save icon for the trigger is clicked. In IsFullyConfigured() return true if the user inputs are sufficient to define the trigger.
When a viable trigger is defined by user then HS4 will call the trigger class to format it for easy reading. This was TriggerFormatUI in HS3 and is now GetPrettyString() in HS4. Same code can be largely reused.
At this point a new trigger has been defined by the user. HS will store away as serialized data what we know as TrigActInfo from HS3. When it needs to be read back the user trigger setup the next time a user wants to edit the existing trigger, or the next time that HS starts and trigger info needs to be initialized then it needs to get and deserialize the data. The deserialization is done in ProcessData, but HS3 and HS4 formats are different so need to have your own ProcessData to handle the case where the data was originally created in HS3 and has not yet been modified by the user.
While your plugin is running it will detect a condition that matches the trigger criteria of this trigger class. To do this it first needs to know all the triggers that have been setup in HS4 for your plugin which is obtained by using TriggerMatches(). In my case I get this list when HS4 starts and then trap in HSEvent when the user makes a change to an event that has my trigger setup. I use a dictionary (oMessageTrigger1Dictionary) to cache all the trigger classes that need to be searched in a manner similar to what was done in HS3. Note in extract below “hs” is “HomeseerSystem” in the HST examples and PLUGIN_NAME is what was INTERFACE in HS3.
When the trigger conditions has been satisfied then HS4 is informed using the same TriggerFire() method as HS3.
My HS3 trigger code was developed from the original HS3 sample program and had much code related to serialization and deserialization and determining which of multiple types of triggers the trigger data actually represented. Methods like TriggerFromInfo(), TriggerFromData(), Add_Update_Trigger(), DeSerializeTrigger(), SerializeTrigger(), GetTrigs() are no longer used and the code not transferred to the HS4 implementation.
For each Trigger type that is to be shown in the HS Event trigger create a class to hold it. In the HSPI Initialize() procedure register it with such as for the class “MQTTReceiveTrigger”
Code:
TriggerTypes.AddTriggerType(GetType(MQTTReceiveTrigger))
Code:
Protected Overrides Function GetName() As String Return TriggerName End Function Protected Overrides Property SubTriggerTypeNames As List(Of String) = New List(Of String)
Code:
Public Sub New(ByVal trigInfo As TrigActInfo, ByVal inListener As TriggerTypeCollection.ITriggerTypeListener, Optional ByVal debugLog As Boolean = False) MyBase.New(trigInfo, inListener, debugLog) End Sub Public Sub New(ByVal id As Integer, ByVal eventRef As Integer, ByVal selectedSubTriggerIndex As Integer, ByVal dataIn As Byte(), ByVal inListener As TriggerTypeCollection.ITriggerTypeListener, Optional ByVal debugLog As Boolean = False) MyBase.New(id, eventRef, selectedSubTriggerIndex, dataIn, inListener, debugLog) End Sub Public Sub New() MyBase.New End Sub
This the time that a unique ID for this page is established. This is made from the PageId implicitly given by HS and some text that will this class from other trigger classes supported by the plugin.
Code:
Protected Overrides Sub OnNewTrigger() ConfigPage = InitializeMQTTReceivePage().Page End Sub Private Function InitializeMQTTReceivePage() As PageFactory Dim cpf As PageFactory = PageFactory.CreateEventTriggerPage(MQTTReceiveTriggerId, TriggerName) cpf.WithInput("TriggerTopic", "MQTT Topic to match", "", HomeSeer.Jui.Types.EInputType.Text) cpf.WithInput("TriggerPayload", "Payload to match", "", HomeSeer.Jui.Types.EInputType.Text) Return cpf End Function Private ReadOnly Property MQTTReceiveTriggerId As String Get Return $"{PageId}-MqttReceiveTrigger" End Get End Property
Code:
Public Overrides Function IsFullyConfigured() As Boolean For Each view As AbstractView In ConfigPage.Views Dim id As String = view.Id If id = "TriggerTopic" Then Dim inputView As InputView inputView = TryCast(view, InputView) Dim value As String = InputView?.GetStringValue If value <> "" Then Return True End If End If Next Return False End Function
Code:
Public Overrides Function GetPrettyString() As String Dim sTopic As String = "" Dim sPayload As String = "" For Each view As AbstractView In ConfigPage.Views Dim id As String = view.Id 'e.g. TriggerTopic If id = "TriggerTopic" Then Dim inputView As InputView inputView = TryCast(view, InputView) sTopic = inputView?.GetStringValue ElseIf id = "TriggerPayload" Then Dim inputView As InputView inputView = TryCast(view, InputView) sPayload = inputView?.GetStringValue End If Next If sPayload = "" Then Return "MQTT Topic “”" & sTopic & q & " received" Else Return "MQTT Topic “”" & sTopic & q & " received with Payload "”” & sPayload & “”” End If
Code:
Protected Overrides Function ProcessData(inData As Byte()) As Byte() Dim legacyTrigger As MyTrigger1MqttReceive = Nothing If inData IsNot Nothing AndAlso inData.Length > 0 Then Try 'attempt to deserialize the byte array to your custom class legacyTrigger = TrigActInfo.DeserializeLegacyData(Of MyTrigger1MqttReceive)(inData) Catch ex As Exception 'if there was a problem - set the legacy data object to null legacyTrigger = Nothing End Try End If If legacyTrigger Is Nothing Then 'if there is no legacy data object - call through to the default implementation Return MyBase.ProcessData(inData) End If Return Data End Function
Code:
Private Sub InitializeTriggers() TriggerTypes.AddTriggerType(GetType(MQTTReceiveTrigger)) BuildTriggerList() End Sub Public Sub BuildTriggerList() Dim TrigsToCheck() As HomeSeer.PluginSdk.Events.TrigActInfo = hs.TriggerMatches(PLUGIN_NAME, 1, -1) Dim Trig1 As MQTTReceiveTrigger 'MyTrigger1MqttReceive oMessageTrigger1Dictionary.Clear() If TrigsToCheck IsNot Nothing Then 'AndAlso TrigsToCheck.Count > 0 Then For Each TC As HomeSeer.PluginSdk.Events.TrigActInfo In TrigsToCheck Try Trig1 = New MQTTReceiveTrigger(TC.UID, TC.evRef, TC.SubTANumber - 1, TC.DataIn, Me, LogDebug) oMessageTrigger1Dictionary.Add(Trig1.MyTrigger1MqttReceive.TriggerUID & Trig1.MyTrigger1MqttReceive.EvRef.ToString, Trig1.MyTrigger1MqttReceive) Catch ex As Exception End Try Next End If End Sub
Code:
hs.TriggerFire(PLUGIN_NAME, Trig1)
Comment