Announcement

Collapse
No announcement yet.

I struggle to understand how to use Triggers in HS4

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    I struggle to understand how to use Triggers in HS4

    I may be completely stupid, but I struggle to understand how to use Triggers in HS4.

    I want to check if particular trigger should be fired, obviously. So I call one of
    Code:
    hs.TriggerMatches
    hs.GetTriggersByType
    hs.GetTriggersByInterface
    (already confusing - which one?)

    So I get an array of TrigActInfo. Now I need to compare some property of each trigger with some value. How? I need to de-serialize TrigActInfo to AbstractTriggerType, right?

    In your sample plugin you construct SampleTriggerType explicitly because you have only one trigger type. If I have multiple triggers/subtriggers - what do I do? Just try to construct each one from TrigActInfo and see if it fails? I'm lost...

    The wonderful SDK provides wonderful function
    Code:
    private AbstractTriggerType GetObjectFromTrigInfo(TrigActInfo trigInfo)
    That's what I need. But it's PRIVATE?

    #2
    Alex,
    here is what i did.

    #1 I used an event between my generic plugin code and the hspi object. testinfo is a trigactinfo object which i store the triger/subtrig number. dvref is the device i want to check against the trigger

    RaiseEvent onCheckTrigger(TestInfo, dvref)

    #2 here is the code that runs on the Event

    Code:
    Sub CheckTrigger(TestInfo As TrigActInfo, dvref As Integer) ' handles hsapi.onchecktrigger
    
    Dim TrigsToCheck() As TrigActInfo
    
    Try
    
    TrigsToCheck = hs4.GetTriggersByType(Name, InsteonTriggersClass.TriggerNumber)
    
    For Each TrigCheck In TrigsToCheck
    
      Dim ConfiguredTrigger = New InsteonTriggersClass(TrigCheck, Me) ' build the trigger from the data
    
      If ConfiguredTrigger.ShouldTriggerFire(TestInfo, dvref) Then ' this is my custom method in order to pass in the dvref
          hs4.TriggerFire(Id, TrigCheck)
      End If
    Next
    
    Catch ex As Exception
    gLogManager.ReportError("CheckTrigger", ex)
    End Try
    
    End Sub
    #3 here is my custom method in the trigger class this will compare the testinfo and dvref to see if matches the configured trigger. if so, return True

    Code:
    Public Function ShouldTriggerFire(TC As TrigActInfo, dvref As Integer) As Boolean
    
    If TC.SubTANumber = SelectedSubTriggerIndex Then
    
      Select Case SelectedSubTriggerIndex
    
      Case INSTEON_TRIGGER_TYPES.STARTRAMPDOWN, INSTEON_TRIGGER_TYPES.STARTRAMPUP, INSTEON_TRIGGER_TYPES.RAMPUP1, INSTEON_TRIGGER_TYPES.RAMPDOWN1, INSTEON_TRIGGER_TYPES.RAMPSTOP
       Return GetSelectedOptionKey(ConfigPage.GetViewById(DeviceFieldName) ) = dvref.ToString
    hope this helps
    Mark

    HS3 Pro 4.2.19.5
    Hardware: Insteon Serial PLM | AD2USB for Vista Alarm | HAI Omnistat2 | 1-Wire HA7E | RFXrec433 | Dahua Cameras | LiftMaster Internet Gateway | Tuya Smart Plugs
    Plugins: Insteon (mine) | Vista Alarm (mine) | Omnistat 3 | Ultra1Wire3 | RFXCOM | HS MyQ | BLRadar | BLDenon | Tuya | Jon00 Charting | Jon00 Links
    Platform: Windows Server 2022 Standard, i5-12600K/3.7GHz/10 core, 16GB RAM, 500GB SSD

    Comment


      #3
      Thank you Mark,

      That's exactly what I mean - you assume the trigger is InsteonTriggersClass - same as sample plugin. I'm getting there, slowly.
      TrigActInfo has TANumber - which I use to construct proper trigger instance.

      In my calendar class I get all triggers matching the calendar ID:

      Click image for larger version

Name:	Screenshot 2021-01-29 013248.jpg
Views:	561
Size:	49.5 KB
ID:	1452009

      Base abstract controller class (in my base lib):

      Click image for larger version

Name:	Screenshot 2021-01-29 013403.jpg
Views:	568
Size:	108.8 KB
ID:	1452010

      And derived Calendar controller:

      Click image for larger version

Name:	Screenshot 2021-01-29 013711.jpg
Views:	547
Size:	19.1 KB
ID:	1452011

      Comment


        #4
        Alex,

        Right now i only have one triggerclass. I thought that was the preferred approach like a single actionclass with sub actions

        I have a list of sub-triggers (about 13) within my single triggerclass.

        If I add a second triggerclass then I will need to do something like you outline above. But that is way down the road from where I am at right now.

        As we discussed, after going through the hs2 to hs3 migration; its nearly impossible to write the perfect abstract layer that will work with the next hs version; So I'm ok with making a few assumptions here and there. don't drive yourself crazy
        Mark

        HS3 Pro 4.2.19.5
        Hardware: Insteon Serial PLM | AD2USB for Vista Alarm | HAI Omnistat2 | 1-Wire HA7E | RFXrec433 | Dahua Cameras | LiftMaster Internet Gateway | Tuya Smart Plugs
        Plugins: Insteon (mine) | Vista Alarm (mine) | Omnistat 3 | Ultra1Wire3 | RFXCOM | HS MyQ | BLRadar | BLDenon | Tuya | Jon00 Charting | Jon00 Links
        Platform: Windows Server 2022 Standard, i5-12600K/3.7GHz/10 core, 16GB RAM, 500GB SSD

        Comment


          #5
          Hi mnsandler , I know this is a few months old but I am struggling with this as well. I think I got most of it right, but you've got me puzzled on the "event" thing: "#1 I used an event between my generic plugin code and the hspi object."

          Can you elaborate on how you've implemented this? Thanks!
          stefxx

          Comment


            #6
            Never mind. The issue was that EventTriggerReceived needs a reference to the HSPI object, which is unavailable outside the HSPI class. So I created a global variable holding a reference to it. Problem solved!
            stefxx

            Comment


              #7
              Originally posted by stefxx View Post
              Never mind. The issue was that EventTriggerReceived needs a reference to the HSPI object, which is unavailable outside the HSPI class. So I created a global variable holding a reference to it. Problem solved!
              Code:
              protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex, byte[] dataIn, TriggerTypeCollection.ITriggerTypeListener listener, bool logDebug = false)
              The listener should be your HSPI object, as I remember you should add ITriggerTypeListener interface to your HSPI for that

              Comment


                #8
                I used it in my some plugins, if you stefxx need - I can dig it out

                Comment


                  #9
                  HI. I have my triggers working now, but being able to trigger an event in the HSPI class, outside of the class is something I still don't quite understand and might come-in handy one day. I wouldn't mind an example on how you've accomplished that. Just as a learning experience for myself!
                  stefxx

                  Comment


                    #10
                    Originally posted by stefxx View Post
                    HI. I have my triggers working now, but being able to trigger an event in the HSPI class, outside of the class is something I still don't quite understand and might come-in handy one day. I wouldn't mind an example on how you've accomplished that. Just as a learning experience for myself!
                    It's actually handy to read docs (comments):

                    /// <summary>
                    /// The base implementation of a trigger type interface that facilitates communication between
                    /// <see cref="AbstractTriggerType"/>s and the <see cref="AbstractPlugin"/> that owns them.
                    /// <para>
                    /// Extend this interface and have your <see cref="AbstractPlugin"/> implementation inherit it to make it
                    /// accessible through the <see cref="AbstractTriggerType.TriggerListener"/> field.
                    /// </para>
                    /// </summary>
                    public interface ITriggerTypeListener {}
                    So you do
                    Code:
                    public interface IMyTriggerTypeListener ITriggerTypeListener
                    {
                        // Your methods [B]declarations [/B]here
                    }
                    
                    public class HSPI : AbstractPlugin, IMyTriggerTypeListener
                    {
                       // Your IMyTriggerTypeListener [B]implementations [/B]here
                    }
                    Then inside your triggers (derived from AbstractTriggerType) you access it via TriggerListener member:

                    public abstract class AbstractTriggerType {
                    /// <summary>
                    /// An interface reference to the plugin that owns this trigger type.
                    /// <para>
                    /// Define your own interface that inherits from <see cref="TriggerTypeCollection.ITriggerTypeListener"/>
                    /// and then cast this as the type you defined to get a reference to your plugin that can handle any methods
                    /// you wish to define.
                    /// </para>
                    /// </summary>
                    public TriggerTypeCollection.ITriggerTypeListener TriggerListener { get; internal set; }
                    }

                    Comment


                      #11
                      Thank you guys for this thread. Reading the docs and source is crazy making.

                      Question: How do I detect changes to Event Triggers and Event Actions?
                      My plugin needs to respond to changes in event changes (specifically trigger settings) (new ones, removing existing ones, changes to existing ones). I need to react to such changes by processing the changed TrigActInfo objects.

                      I have tried reacting in my TriggerType class but the only place where it makes sense (I think) is GetPrettyString() because that is called once the new TrigActInfo object is created and persisted to my plugin's TriggerTypes collection. This seems really lame and profoundly bad. There has to be a better way.

                      I have also tried using HsEvent (I registered for all events) but do not see any related to event trigger/action changes.

                      What am I missing?

                      Comment


                        #12
                        What do you mean by "How do I detect changes to Event Triggers and Event Actions"?

                        I don't think HS provides any trigger if user changes trigger settings (if that's what you mean).

                        When I need to check for changes, I overwrite all AbstractTrigger constructors:

                        Click image for larger version

Name:	Screenshot 2022-03-22 152031.png
Views:	362
Size:	57.6 KB
ID:	1533028

                        Comment


                          #13
                          Thanks, Alexbk66. That makes sense. The sheer volume of calls to the constructors concerns me if I am to perform some heavy weight processing. At one point I sat down and walked through various constructor calls for a simple creation of an HS Event via the web UI. It was something like 12 various constructor calls.

                          What I was hoping to find was some HsEvent() being fired when a user modifies an existing Event's action and/or trigger settings. I see events fired when someone changes the actual trigger (eg change manual trigger to a Sonos4 trigger). But just modification of existing trigger configs do not fire an event.

                          The way I solved it was by checking the return value of my AbstracTriggerType's OnConfigItemUpdate(). If it is true then firing a generic event that my trigger listener monitors which then performs the heavy weighted processing. Since the true value has not yet been returned (therefore the new TrigActInfo not yet persisted) I queue it on the task threadpool and delay it by some short amount of time before firing the event.

                          Ideally HsEvent() would receive a callback event Constants.HSEvent.CONFIG_CHANGE for any changes to an existing trigger or action. That would be much more reliable.

                          Thanks for your help.

                          Comment


                            #14
                            Originally posted by mrslother View Post
                            Thanks, Alexbk66. That makes sense. The sheer volume of calls to the constructors concerns me if I am to perform some heavy weight processing. At one point I sat down and walked through various constructor calls for a simple creation of an HS Event via the web UI. It was something like 12 various constructor calls.
                            This will (should) be fixed in 4.2.11.0 https://github.com/HomeSeer/Plugin-SDK/issues/132

                            Comment


                              #15
                              alexbk66: That issue on GitHub is closed, but it ends with you going to check. I wonder if the issue needs to be reopened. I'm relatively new to triggers, so I'm not sure it's just me. What I do know is that I avoided IsFullyConfigured(). I believe I used GetPrettyString() instead as a workaround, to lower the amount of calls. Which is madness. If anyone can confirm, the issue should just be reopened for better performance.

                              Here is what I see when I add Console.WriteLine(DateTime.Now) to IsFullyConfigured() in the Sample Plugin CS as a test:
                              - Create an event and pick Sample Trigger as a trigger: called 3 times within the same second
                              - Pick any of its 4 subtriggers: 3 calls
                              - Save the trigger: 4 calls(!)
                              - Opening the Events page: 1 call(?)
                              - Reopening the specific event: 3 calls
                              Plugins I developed for HS4: Somfy Local, MiLight (LimitlessLED), Updates, Volvo (VoC), OpenTherm Gateway (OTGW)
                              Running HS4 Pro on Windows 10 Pro on a Synology VM, with Node-RED running as a container.

                              Comment

                              Working...
                              X