Announcement

Collapse
No announcement yet.

Converting an HS3 device to HS4 root+feature

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

    Converting an HS3 device to HS4 root+feature

    I have my HS4 plugin working as I want using the HS4 framework. I want to be able to convert HS3 devices (on/off) to an HS4 root+feature since my new plugin is dependent on that approach. I tried
    1. changing the pluginid to the new plugind
    2. changing the relationship to device
    3. Changing the TypeInfo
    4. adding feature (created with the feature factory)
    I can't seem to make it work. Any advice or clues? Anyone else had success converting the HS3 device to HS4 that can share some code?
    James

    Running HS 3 on Win10 .

    #2
    What do you mean by "I can't seem to make it work"? What's the problem?

    Comment


      #3
      I have the HS3 device but when I add features nothing changes. The feature doesn't get created and the associated devices remains zero. I don't get an error or any indication that something is missing.
      James

      Running HS 3 on Win10 .

      Comment


        #4
        I've tried other variations but here is simplest version of the code where I am trying to take an exisitng HS3 device and make it HS4 root with feature. I am trying to avoid having to delete/add devices and redo events by my users with the new plugin

        Any help or insight would be appreciated



        Code:
        hs3Dev.Interface = Util.gPlugin_id;
        hs3Dev.Relationship = HomeSeer.PluginSdk.Devices.Identification.ERelationship.Devi ce;
        hs3Dev.TypeInfo.ApiType = EApiType.NotSpecified;
        hs3Dev.TypeInfo.SubType = 0;
        hs3Dev.TypeInfo.SubTypeDescription = string.Empty;
        hs3Dev.TypeInfo.Summary = string.Empty;
        hs3Dev.TypeInfo.Type = 0;
        PlugExtraData PED = Util.preparePED(tpDev.Hostname, tpDev.Type, tpDev.IsColor, tpDev.isMeter, tpDev.IsDimmmableSwitch);
        hs3Dev.PlugExtraData.RemoveAllNamed();
        hs3Dev.PlugExtraData = PED;
        Util.HS.UpdateDeviceByRef(hs3Dev.Ref, hs3Dev.Changes);
        
        FeatureFactory ff = FeatureFactory.CreateGenericBinaryControl(Util.gPlugin_id, $"{name} Controls",
        "On", "Off",
        100, 0); ;
        NewFeatureData dfd = ff.PrepareForHsDevice(hs3Dev.Ref);
        Util.HS.UpdateDeviceByRef(hs3Dev.Ref, hs3Dev.Changes);
        James

        Running HS 3 on Win10 .

        Comment


          #5
          Here's my code:

          Code:
          /// <summary>
          /// Create HS device
          /// </summary>
          /// <param name="name"></param>
          /// <param name="addr"></param>
          void CreateDevice(string name, string addr)
          {
             EMiscFlag[] miscFlags = new EMiscFlag[] { EMiscFlag.ShowValues, EMiscFlag.NoLog };
          
             if (String.IsNullOrEmpty(name))
                name = addr;
          
             if (isRoot)
             {
                NewDeviceData deviceData = DeviceFactory.CreateDevice(controller.plugin.Id)
                   .WithName(name)
                   .WithMiscFlags(miscFlags)
                   .PrepareForHs();
          
                   RefId = hs.CreateDevice(deviceData);
             }
             else
             {
                NewFeatureData featureData = FeatureFactory.CreateFeature(controller.plugin.Id, rootRef)
                   .WithName(name)
                   .WithMiscFlags(miscFlags)
                   .PrepareForHs();
          
                RefId = hs.CreateFeatureForDevice(featureData);
             }
          
             Address = addr;
          
             // i.e. "AK Google Cast" => "GoogleCast"
             Location2 = controller.Location2;
          
             hsDevice = controller.GetHSDeviceByRef(RefId);
          }

          Comment


            #6
            I mean that's a function from the base PluginLib I created (almost 4000 lines of code) - on top of HS "SDK"....

            So there are more functions, i.e.

            Code:
            public string Address
            {
                get => GetProperty<string>(EProperty.Address);
                protected set => UpdateProperty(EProperty.Address, value);
            }
            
            
            public string Location
            {
               get => GetProperty<string>(EProperty.Location);
               set => UpdateProperty(EProperty.Location, value);
            }
            
            
            public string Location2
            {
               get => GetProperty<string>(EProperty.Location2);
               protected set => UpdateProperty(EProperty.Location2, value);
            }
            
            /// <summary>
            /// Set HS device EProperty
            /// </summary>
            /// <param name="property"></param>
            /// <param name="value"></param>
            /// <param name="cache">If true - don't update HS value if not changed</param>
            void UpdateProperty(EProperty property, object value, bool cache = false)
            {
               if (controller?.hs == null || RefId <= 0)
                  return;
            
                  if (properties == null)
                     properties = new PropertiesCache(this);
            
                  properties.Set(property, value, cache: cache);
            }
            
            
            /// <summary>
            /// Get HS device EProperty
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="property"></param>
            /// <param name="cache">If true - </param>
            /// <returns></returns>
            T GetProperty<T>(EProperty property, bool cache = false)
            {
               if (controller?.hs == null || RefId <= 0)
                  return default(T);
            
               if (properties == null)
                  properties = new PropertiesCache(this);
            
               return properties.Get<T>(property, cache: cache);
            }
            And

            Code:
            /// <summary>
            /// GetDeviceByAddress or GetFeatureByAddress?
            /// </summary>
            /// <param name="addr"></param>
            /// <returns>AbstractHsDevice</returns>
            public AbstractHsDevice GetHSDeviceByAddr(string addr)
            {
                return hs.GetDeviceByAddress(addr) as AbstractHsDevice ?? hs.GetFeatureByAddress(addr);
            }
            
            /// <summary>
            /// GetDeviceByRef or GetFeatureByRef
            /// </summary>
            /// <param name="RefId"></param>
            /// <returns>AbstractHsDevice</returns>
            public AbstractHsDevice GetHSDeviceByRef(int RefId)
            {
                if ( !VerifyRefID(RefId) )
                    return null;
            
                return hs.IsRefDevice(RefId)? hs.GetDeviceByRef(RefId) as AbstractHsDevice : hs.GetFeatureByRef(RefId);
            }

            Comment


              #7
              Thanks for sharing the code. It looks like you are creating a new device and adding feature which I have no problem doing for "new" devices. I was hoping to take an existing device that was ported over from HS3 and "splitting it into a root/feature" without changing the original dev ref I think what I want to do is probably more problematic than it is worth.

              I appreciate all the responses you provide to everyone this forum.
              James

              Running HS 3 on Win10 .

              Comment


                #8
                I do a slighly different sequence ...


                1. I create a brand new device
                2. Change the old device into a feature
                3. Now add (associate) the old device (which is now a feature) to the newly created device


                Comment


                  #9
                  In my case I looked for all devices from HS3 at startup that did not have parents and then created as necessary to supplement for HS4 compliance. It will not be exactly as you are doing but may give you some ideas.

                  Code:
                  If Not bDoOnlyAccepted Then
                  For Each oMqtt2 As MqttReport In missingDevices
                  With oMqtt2
                  Dim iOldRef As Integer = .Ref
                  .Ref = InitNewSensor(.Source)
                  SaveReceive(oMqtt2)
                  hsWritelog(PLUGIN_NAME, "Device " & iOldRef.ToString & " does not exist. New device " & .Ref.ToString & " created for Topic " & sSource)
                  End With
                  Next
                  missingDevices.Clear()
                  End If
                  
                  
                  
                  Public Function InitNewSensor(ByVal sMTopic As String) As Integer
                  'Create a new device for an accepted MQTT device
                  'Topic is in first parameter and that will be used as the HS3 device name
                  
                  Dim iParentRef As Integer
                  Dim sParentTopic As String
                  'Dim sName As String 
                  
                  Try
                  sParentTopic = FindParentTopic(sMTopic)
                  iParentRef = CreateParent(sParentTopic) ', iTopicLength > 0)
                  
                  Dim iNewRef As Integer = UseNextVirtualDevice(sMTopic, sParentTopic, TypeFromSource(sMTopic), iParentRef, False) 
                  If iNewRef <> -1 Then
                  MQTTReceiveDictionary(sMTopic).Ref = iNewRef
                  AddToMQTTRefDictionary(iNewRef, sMTopic)
                  UpdateDeviceProperties(MQTTReceiveDictionary(sMTopic), True)
                  End If
                  Return iNewRef
                  
                  Catch ex As Exception
                  hsWritelogEx(PLUGIN_NAME, "InitNewSensor", ex.Message)
                  Return ""
                  End Try
                  End Function
                  
                  
                  Public Function UseNextVirtualDevice(ByVal sTopic As String, ByVal sAddress As String, ByVal sType As String, ByVal iParentRefIn As Integer, ByVal bIsParent As Boolean) As Integer
                  
                  'Return Next device ref
                  'Dim dv As HomeSeer.PluginSdk.Devices.HsFeature
                  Dim iRef As Integer = -1
                  Dim sLocation As String = ""
                  Dim sLocation2 As String = ""
                  Dim sName As String = ""
                  Dim sTechnologyAddress As String
                  
                  Try
                  
                  Dim arrS() As String = Split(sTopic, "/")
                  If arrS.Length >= 1 Then
                  sName = arrS(UBound(arrS))
                  End If
                  arrS = Split(sAddress, "/")
                  If arrS.Length = 1 Then
                  sLocation = sAddress
                  sLocation2 = sAddress
                  sTechnologyAddress = sAddress
                  Else
                  sLocation = arrS(1)
                  sLocation2 = arrS(0)
                  sTechnologyAddress = arrS(1)
                  End If
                  If arrS(0) <> "shellies" Then 'special cases
                  sTechnologyAddress = sAddress
                  End If
                  If gDefaultLocationOption = 1 Then
                  sLocation2 = gDefaultFloor
                  sLocation = gDefaultRoom
                  End If
                  If sLocation2 = "" Then
                  sLocation2 = sLocation
                  End If
                  
                  Dim df As HomeSeer.PluginSdk.Devices.DeviceFactory
                  Dim ff As HomeSeer.PluginSdk.Devices.FeatureFactory
                  Dim DeviceType As New HomeSeer.PluginSdk.Devices.Identification.TypeInfo With {
                  .SubType = 0
                  }
                  
                  Dim iParentRef As Integer = iParentRefIn
                  Dim bCreateParent As Boolean = (gDefaultParentOption = 0)
                  If gDefaultParentOption = 1 Then
                  If Not hs.DoesRefExist(gDefaultParent) Then
                  bCreateParent = True
                  If bIsParent Then
                  hsWritelog(PLUGIN_NAME, "New Device created for parent of " & sTopic & " because requested parent " & gDefaultParent.ToString & " does not exist")
                  End If
                  ElseIf Not bIsParent Then
                  iParentRef = gDefaultParent
                  End If
                  End If
                  
                  If bIsParent AndAlso bCreateParent Then
                  df = hsNewDevice(sName)
                  DeviceType.ApiType = HomeSeer.PluginSdk.Devices.Identification.EApiType.Device
                  DeviceType.Type = HomeSeer.PluginSdk.Devices.Identification.EDeviceType.Generi c
                  DeviceType.SubTypeDescription = "MQTT Topic"
                  df.WithLocation(sLocation)
                  df.WithLocation2(sLocation2)
                  'df.WithProductImage(PLUGIN_NAME & "/" & PLUGIN_NAME & ".png")
                  Dim dd As HomeSeer.PluginSdk.Devices.NewDeviceData = df.PrepareForHs()
                  iRef = hs.CreateDevice(dd)
                  ElseIf iParentRef <> -1 Then
                  ff = hsNewFeature(sName)
                  DeviceType.ApiType = HomeSeer.PluginSdk.Devices.Identification.EApiType.Feature
                  DeviceType.Type = HomeSeer.PluginSdk.Devices.Identification.EFeatureType.Gener ic
                  DeviceType.SubTypeDescription = "MQTT-" & sType
                  ff.WithLocation(sLocation)
                  ff.WithLocation2(sLocation2)
                  Dim fd As HomeSeer.PluginSdk.Devices.NewFeatureData
                  fd = ff.PrepareForHsDevice(iParentRef)
                  iRef = hs.CreateFeatureForDevice(fd)
                  ElseIf Not bCreateParent Then
                  Debug.Print(sTopic & " Feature without parent")
                  End If
                  If iParentRef <> -1 Then
                  hs.UpdatePropertyByRef(iRef, HomeSeer.PluginSdk.Devices.EProperty.Address, sTechnologyAddress) 'device.Address = sAddress '.Address(hs) = sAddress
                  hs.UpdatePropertyByRef(iRef, HomeSeer.PluginSdk.Devices.EProperty.Interface, PLUGIN_NAME) 'device.Interface = PLUGIN_NAME
                  hs.UpdatePropertyByRef(iRef, HomeSeer.PluginSdk.Devices.EProperty.LastChange, Now) 'device.LastChange = Now
                  hs.UpdatePropertyByRef(iRef, HomeSeer.PluginSdk.Devices.EProperty.DeviceType, DeviceType)
                  End If
                  
                  'hs.SetDeviceLastChange(iRef, Now)
                  
                  Return iRef
                  
                  Catch ex As Exception
                  hsWritelogEx(PLUGIN_NAME, "UseNextVirtualDevice", ex.Message)
                  Return -1
                  End Try
                  End Function

                  Comment


                    #10
                    I think this is what I was looking for. I will dig in and let you know my results. Much appreciated.
                    James

                    Running HS 3 on Win10 .

                    Comment


                      #11
                      Originally posted by jasv View Post
                      Thanks for sharing the code. It looks like you are creating a new device and adding feature which I have no problem doing for "new" devices. I was hoping to take an existing device that was ported over from HS3 and "splitting it into a root/feature" without changing the original dev ref I think what I want to do is probably more problematic than it is worth.
                      No, I don't create a new "root" device. The function I showed can be used for creating either root, or feature.
                      But in your case (and what I do) - first I check if the device (root) with this address already exists - then I just use it.

                      And then I create a feature - I pass the root RefId (I should've shared the main Create() function):

                      Note rootRef in code in post #5.

                      Code:
                      /// <summary>
                      /// Create device if doesn't exist - with given address
                      /// </summary>
                      /// <param name="addr">Device Address to look for</param>
                      /// <param name="full_type"></param>
                      /// <param name="Device_Type">HS Device type i.e. EDeviceType or EFeatureType</param>
                      /// <param name="Device_SubType">i.e. EGenericDeviceSubType or EGenericDeviceSubType</param>
                      /// <param name="Device_SubType_Description"></param>
                      /// <param name="dev_name">Device name to set for new device, if empty - use 'addr' instead</param>
                      /// <returns></returns>
                      protected bool Create(
                           string addr,
                           string full_type,
                           int Device_Type,
                           int Device_SubType,
                           string Device_SubType_Description = null,
                           string dev_name = null
                      )
                      {
                           this.FullType = full_type;
                      
                           hsDevice = controller.GetHSDeviceByAddr(addr);
                      
                           created = hsDevice == null;
                      
                           if (created)
                           {
                                CreateDevice(dev_name, addr);
                           }
                      
                           if (hsDevice != null)
                                RefId = hsDevice.Ref;
                      
                           SetTypeInfo(Device_Type, Device_SubType, Device_SubType_Description);
                      
                           if (isRoot && children == null)
                                children = new List<DeviceBase>();
                      
                           controller.AddDevice(this);
                      
                           return created;
                      }

                      Notice GetHSDeviceByAddr().

                      Also, second constructor is used for chreating child (feauture) devices and takes parent (root) device:

                      Code:
                      public DeviceBase root { get; protected set; }
                      
                      /// <summary>
                      /// For children features - RefId of the parent device
                      /// </summary>
                      protected int rootRef => root?.RefId ?? -1;
                      
                      /// <summary>
                      /// If root device - use HsDevice functions, else HsFeature
                      /// </summary>
                      public bool isRoot => root == null;
                      
                      
                      /// <summary>
                      /// Constructor for Root device
                      /// </summary>
                      /// <param name="controller">ControllerBase</param>
                      public DeviceBase(ControllerBase controller)
                      {
                           this.controller = controller;
                      }
                      
                      
                      /// <summary>
                      /// Constructor for Child device (Feature)
                      /// </summary>
                      /// <param name="root">Root device</param>
                      public DeviceBase(DeviceBase root)
                           : this(root.controller)
                      {
                           this.root = root;
                      }

                      Comment


                        #12
                        And BTW, I don't bother making any changes to the existing HS3 root device, in HS4 I simply add new child device "devCtrl" and override any function to pass to the devCtrl instead, i.e.

                        Code:
                        /// <summary>
                        /// Redirect SetValue from root device to devCtrl
                        /// </summary>
                        public virtual void SetValue(double v, string deviceString = null)
                        {
                             devCtrl?.SetValue(v, deviceString);
                        }
                        
                        
                        /// <summary>
                        /// Get current device Graphic
                        /// Since Root can't have Graphic - also try devCtrl
                        /// </summary>
                        public virtual string Graphic
                        {
                            get
                            {
                                 var val = Value;
                                 var gr = hs.GetStatusGraphicForValue(RefId, val);
                        
                                 if (gr == null && devCtrl != null)
                                      return devCtrl.Graphic;
                        
                                 return gr?.Graphic;
                            }
                        }

                        Comment


                          #13
                          Code:
                          And BTW, I don't bother making any changes to the existing HS3 root device, in HS4 I simply add new child device "[FONT=Courier New]devCtrl[/FONT]" and override any function to pass to the [FONT=Courier New]devCtrl[/FONT] instead, i.e.
                          That is a good plan B. I was worried about too many places that might need that override code.
                          James

                          Running HS 3 on Win10 .

                          Comment


                            #14
                            Originally posted by dcorsus View Post
                            I do a slighly different sequence ...


                            1. I create a brand new device
                            2. Change the old device into a feature
                            3. Now add (associate) the old device (which is now a feature) to the newly created device

                            I think the sequence is the issue. You are doing a similar thing as Michael. I will give it a shot.
                            James

                            Running HS 3 on Win10 .

                            Comment


                              #15
                              Originally posted by jasv View Post

                              I think the sequence is the issue. You are doing a similar thing as Michael. I will give it a shot.
                              It will keep the existing references for your controls, which cannot exist on a device and this step fixes that.
                              One note of warning, if your HS3 old devices are a bit more complex, say a root and children, you can still apply the sequence I described but there is a bug that I have reported a year ago and stil hasn't been fixed.

                              So if the HS3 old device had a root with some controls on it and a bunch of children with controls on them, the sequence I apply is:

                              1/ create a brand new device
                              2/ remove the old children from their old parent, make them features and now associate them to this brand new device
                              3/ last you take the old root device, assuming it has controls on them, else you can skip this step, change into a feature and now associate with this brand new device.

                              The bug is that you cannot remove the old associations on the old root, apparantly the SDK only allow you to add associations, you can't take them away.

                              Comment

                              Working...
                              X