65.9K
CodeProject 正在变化。 阅读更多。
Home

智能家居 – 控制 Shelly® 设备 (第 2 部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (4投票s)

2023年12月6日

CPOL

3分钟阅读

viewsIcon

4448

downloadIcon

156

本文是关于在智能家居中控制 Shelly® 设备系列文章的延续。它介绍了一个无需修改即可与标准控件配合使用的组件。

本文的基础是我之前文章的描述 智能家居 – 控制 Shelly® 设备 – 第 1 部分

在本文中,我介绍了一个与标准控件配合使用的组件,无需进行修改。这里,功能被分配,控件通过我在本文中介绍的组件进行动画处理。

可以选中表单的所有或部分控件,并将所需操作分配给它们

在本文中,我假设属性处理,尤其是对于集合对象,是已知的。

基础

与控件不同,组件不知道它属于哪个表单。此信息只能通过一个技巧获得。 我在 StackOverFlow 的这篇文章中找到了基础知识:获取组件父表单

现在我知道了我的组件所属的表单,因此可以访问它包含的所有控件

   Private Sub GetParentForm()
        Dim myHost As IDesignerHost = Nothing
        If MyBase.Site IsNot Nothing Then myHost = CType(MyBase.Site.GetService(GetType(IDesignerHost)), IDesignerHost)
        If myHost IsNot Nothing Then
            myParentForm = CType(myHost.RootComponent, Form)
            Exit Sub
        End If
    End Sub
 
    Public Overrides Property Site() As System.ComponentModel.ISite
        Get
            Return MyBase.Site
        End Get
        Set(ByVal value As System.ComponentModel.ISite)
            MyBase.Site = value
            GetParentForm()
        End Set
    End Property
 
    <Category("Info-Data"), Description("the Parentform of this Component")>
    Property ParentForm() As Form
        Get
            Return myParentForm
        End Get
        Set(value As Form)
            myParentForm = value
        End Set
    End Property
    Private myParentForm As Form
  private void GetParentForm()
    {
        IDesignerHost myHost = null;
        if (base.Site != null)
            myHost = (IDesignerHost)base.Site.GetService(typeof(IDesignerHost));
        if (myHost != null)
        {
            myParentForm = (System.Windows.Forms.Form)myHost.RootComponent;
            return;
        }
    }
 
    public override System.ComponentModel.ISite Site
    {
        Get { return base.Site; }
        set
        {
            base.Site = value;
            GetParentForm();
        }
    }
 
    [Category("Info-Data")]
    [Description("the Parentform of this Component")]
    public Form ParentForm
    {
        get { return myParentForm; }
        set { myParentForm = value; }
    }
    private Form myParentForm;

通过我自己的属性,我现在可以指定用什么颜色来为选定的控件制作动画,以及检测可能的值更改的时间间隔。

工作原理

这个类 ControlAssignmentDefinition 包含了将功能分配给控件的基础

    <TypeConverter(GetType(ExpandableObjectConverter))>
    Public Class ControlAssignmentDefinition
 
        <Category("Control"), Description("the Control which should do the Action")>
        Property SelectedControl As Control
            Get
                Return mySelectedControl
            End Get
            Set(value As Control)
                mySelectedControl = value
                savedBackColor = value.BackColor
                savedForeColor = value.ForeColor
            End Set
        End Property
        Private mySelectedControl As Control = Nothing
 
        Public savedBackColor As Color
        Public savedForeColor As Color
 
        <Category("Shelly"), Description("IpAdress of the Shelly-Device to work with")>
        <RefreshProperties(RefreshProperties.All)>
        Property IpAdress As String
            Get
                Return my_IPAdress
            End Get
            Set(value As String)
                my_ShellyType = ShellyCom.Shelly_GetType(value)
                If my_ShellyType <> ShellyCom.ShellyType.None Then my_IPAdress = value
                If my_ShellyType = ShellyCom.ShellyType.Shelly_Dimmer2 Then myOutput = 0
            End Set
        End Property
        Private my_IPAdress As String = ""
 
        <Category("Shelly"), Description("shows the Type of the connected Shelly-Device")>
        ReadOnly Property ShellyType As String
            Get
                Return my_ShellyType.ToString
            End Get
        End Property
        Private my_ShellyType As ShellyCom.ShellyType
 
        <Category("Shelly"), Description("Output-Number of the Shelly-Device to work with")>
        <DefaultValue(0)>
        Property OutputNr As Integer
            Get
                Return myOutput
            End Get
            Set(value As Integer)
                If (value >= 0) And (value <= 1) Then
                    myOutput = value
                End If
            End Set
        End Property
        Private myOutput As Integer = 0
 
        <Category("Shelly"), Description("the Value which is assigned to the Shelly-Device")>
        <DefaultValue(0)>
        Property Value As Integer
            Get
                Return myValue
            End Get
            Set(value As Integer)
                If (value >= 0) And (value <= 100) Then
                    myValue = value
                End If
 
            End Set
        End Property
        Private myValue As Integer = 0
 
        <Category("Shelly"), Description("the Action which happens with a Control-Click Event")>
        <DefaultValue(GetType(ShellyActionComponent.ActionDefinition), "none")>
        Property Action As ShellyActionComponent.ActionDefinition
            Get
                Return myAction
            End Get
            Set(value As ShellyActionComponent.ActionDefinition)
                myAction = value
            End Set
        End Property
        Private myAction As ShellyActionComponent.ActionDefinition = ShellyActionComponent.ActionDefinition.none
 
        Public Sub New()
        End Sub
 
        Public Overrides Function toString() As String
            If mySelectedControl IsNot Nothing Then Return mySelectedControl.Name + " => " + myAction.ToString
            Return "[-]"
        End Function
 
    End Class
   [TypeConverter(typeof(ExpandableObjectConverter))]
    public class ControlAssignmentDefinition
    {
        [Category("Control")]
        [Description("the Control which should do the Action")]
        public Control SelectedControl
        {
            get { return mySelectedControl; }
            set
            {
                mySelectedControl = value;
                savedBackColor = value.BackColor;
                savedForeColor = value.ForeColor;
            }
        }
        private Control mySelectedControl = null;
 
        public Color savedBackColor;
        public Color savedForeColor;
 
        [Category("Shelly")]
        [Description("IpAdress of the Shelly-Device to work with")]
        [RefreshProperties(RefreshProperties.All)]
        public string IpAdress
        {
            get { return my_IPAdress; }
            set
            {
                my_ShellyType = ShellyCom.Shelly_GetType(value);
                if (my_ShellyType != ShellyCom.ShellyType.None)
                    my_IPAdress = value;
                if (my_ShellyType == ShellyCom.ShellyType.Shelly_Dimmer2)
                    myOutput = 0;
            }
        }
        private string my_IPAdress = "";
 
        [Category("Shelly")]
        [Description("shows the Type of the connected Shelly-Device")]
        public string ShellyType
        {
            get { return Convert.ToString(my_ShellyType); }
        }
        private ShellyCom.ShellyType my_ShellyType;
 
        [Category("Shelly")]
        [Description("Output-Number of the Shelly-Device to work with")]
        [DefaultValue(0)]
        public int OutputNr
        {
            get { return myOutput; }
            set
            {
                if ((value >= 0) & (value <= 1))
                    myOutput = value;
            }
        }
        private int myOutput = 0;
 
        [Category("Shelly")]
        [Description("the Value which is assigned to the Shelly-Device")]
        [DefaultValue(0)]
        public int Value
        {
            get { return myValue; }
            set
            {
                if ((value >= 0) & (value <= 100))
                    myValue = value;
            }
        }
        private int myValue = 0;
 
        [Category("Shelly")]
        [Description("the Action which happens with a Control-Click Event")]
        [DefaultValue(typeof(ShellyActionComponent.ActionDefinition), "none")]
        public ShellyActionComponent.ActionDefinition Action
        {
            get { return myAction; }
            set { myAction = value; }
        }
        private ShellyActionComponent.ActionDefinition myAction = ShellyActionComponent.ActionDefinition.none;
 
        public ControlAssignmentDefinition()
        {
        }
 
        public override string ToString()
        {
            if (mySelectedControl != null)
                return mySelectedControl.Name + " => " + Convert.ToString(myAction);
            return "[-]";
        }
    }

为了能够使用此类处理表单的多个控件,它被嵌入到 ActionCollection 中,作为 List (of ControlAssignmentDefinition).

此集合通过 ShellyActions 属性为表单的设计器脚本提供了对控件的赋值

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    ReadOnly Property ShellyActions As ActionCollection
        Get
            Return my_ShellyActions
        End Get
    End Property
    Private my_ShellyActions As New ActionCollection
    [Category("Shelly")]
    [Description("Assignment to the Controls")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ActionCollection ShellyActions
    {
        get { return my_ShellyActions; }
    }
    private ActionCollection my_ShellyActions = new ActionCollection();

已经提到的 ControlAssignmentDefinition 类的属性具有以下含义

  • SelectedControl - 应该执行分配功能的控件
  • IpAdress – 要使用的 Shelly 设备的 IP 地址
  • ShellyType – 显示所选 IpAdress 的 Shelly 设备的类型
  • Action – 应该发生什么操作? (SetOut, ToggleOut, SetDimmer, SetRoller, ToggleRoller)
  • 输出编号 – 要使用的 Shelly 设备的输出编号
  • Value – 分配给输出的值 – 如果选择的 Action 是 Dimmer 或 Roller,则 Value 是它们的百分比

对于此处定义的每个控件,集合会评估单击事件并将其分配给所选操作。

为了简单起见,我在组件中嵌入了一个计时器来为使用的控件制作动画,其间隔调用了 ShellyRequest 方法,在该方法中,然后遍历集合的元素并检查状态更改。

然后相应地对控件进行动画处理 - 尽管这有不同的逻辑,具体取决于所选功能。

    Private myTimer As New Timer With {.Enabled = False, .Interval = 1000}
 
    Private Sub ShellyRequest(sender As Object, e As System.EventArgs) 'Handles myTimer.Tick
        If myParentForm IsNot Nothing And Not DesignMode Then
            Dim ShellyStatus As ShellyCom.Shelly_IOStatus
            Dim myItem As ControlAssignmentDefinition
            Dim outActive1, outActive2, outActive3, notActive As Boolean
            Dim ControlType As Type
 
            For i As Integer = 0 To my_ShellyActions.Count - 1
                myItem = my_ShellyActions.Item(i)
                If ShellyCom.Shelly_GetType(myItem.IpAdress) <> ShellyCom.ShellyType.None Then
                    ShellyStatus = ShellyCom.Shelly_GetStatus(myItem.IpAdress)
                    notActive = ((myItem.Action = ActionDefinition.SetOut) Or (myItem.Action = ActionDefinition.SetDimmer)) And (myItem.Value = 0)
                    outActive1 = (myItem.Action = ActionDefinition.SetOut) And (myItem.Value <> 0)
                    outActive2 = (myItem.Value <> 0) And (((myItem.OutputNr = 0) And ShellyStatus.Out0) Or ((myItem.OutputNr = 1) And ShellyStatus.Out1))
                    outActive3 = ShellyStatus.RollerState = ShellyCom.ShellyRollerState.Opening Or ShellyStatus.RollerState = ShellyCom.ShellyRollerState.Closing
                    ControlType = myItem.SelectedControl.GetType
 
                    Select Case ControlType
                        Case GetType(Button)
                            If (outActive1 Or outActive2 Or outActive3) And Not notActive Then
                                myItem.SelectedControl.BackColor = my_AnimationBackColor
                                myItem.SelectedControl.ForeColor = my_AnimationForeColor
                            Else
                                myItem.SelectedControl.BackColor = myItem.savedBackColor
                                myItem.SelectedControl.ForeColor = myItem.savedForeColor
                            End If
                        Case GetType(Label), GetType(TextBox)
                            myItem.SelectedControl.Text = ShellyStatus.OutValue.ToString + " %"
                            If ShellyStatus.Mode = ShellyCom.ShellyMode.Roller Then myItem.SelectedControl.Text += " - " + ShellyStatus.RollerState.ToString
                    End Select
 
                End If
            Next
        End If
    End Sub
    private Timer myTimer = new Timer() { Enabled = false, Interval = 1000 };
 
    private void ShellyRequest(object sender, System.EventArgs e) // Handles myTimer.Tick
    {
        if (myParentForm != null & !DesignMode)
        {
            ShellyCom.Shelly_IOStatus ShellyStatus;
            ControlAssignmentDefinition myItem;
            bool outActive1, outActive2, outActive3, notActive;
            Type ControlType;
 
            for (int i = 0; i <= my_ShellyActions.Count - 1; i++)
            {
                myItem = my_ShellyActions.Item(i);
                if (ShellyCom.Shelly_GetType(myItem.IpAdress) != ShellyCom.ShellyType.None)
                {
                    ShellyStatus = ShellyCom.Shelly_GetStatus(myItem.IpAdress);
                    notActive = ((myItem.Action == ActionDefinition.SetOut) | (myItem.Action == ActionDefinition.SetDimmer)) & (myItem.Value == 0);
                    outActive1 = (myItem.Action == ActionDefinition.SetOut) & (myItem.Value != 0);
                    outActive2 = ((myItem.OutputNr == 0) & ShellyStatus.Out0) | ((myItem.OutputNr == 1) & ShellyStatus.Out1);
                    outActive3 = ShellyStatus.RollerState == ShellyCom.ShellyRollerState.Opening | ShellyStatus.RollerState == ShellyCom.ShellyRollerState.Closing; ControlType = myItem.SelectedControl.GetType();
 
                    if (ControlType == typeof(Button))
                    {
                        if ((outActive1 | outActive2 | outActive3) & !notActive)
                        {
                            myItem.SelectedControl.BackColor = my_AnimationBackColor;
                            myItem.SelectedControl.ForeColor = my_AnimationForeColor;
                        }
                        else
                        {
                            myItem.SelectedControl.BackColor = myItem.savedBackColor;
                            myItem.SelectedControl.ForeColor = myItem.savedForeColor;
                        }
                    }
                    else if(ControlType == typeof(Label) | ControlType == typeof(TextBox))
                        myItem.SelectedControl.Text = Convert.ToString(ShellyStatus.OutValue)+ " %";
                        if (ShellyStatus.Mode == ShellyCom.ShellyMode.Roller)
                            myItem.SelectedControl.Text += " - " + Convert.ToString(ShellyStatus.RollerState);
                }
            }
        }
    }

但是,如果要显示百叶窗或调光器的实际值,则只能在标签或文本框上完成。在这种情况下,这些元素也必须包含在 ShellyActions 中。

赋值可能如下所示

最后 – 最后的几句话

我计划将 Button 控件用于执行操作(使用单击事件)以及 Label 和 Textbox 控件用于显示值(使用文本属性)。对于其他控件,连接对我来说似乎没有意义 - 除非你想在这里使用自定义控件

我想感谢 @sean-ewington 在创建这篇文章和之前的文章中的帮助。

© . All rights reserved.