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






4.47/5 (4投票s)
本文是关于在智能家居中控制 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 在创建这篇文章和之前的文章中的帮助。