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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.10/5 (7投票s)

2023年11月23日

CPOL

5分钟阅读

viewsIcon

14014

downloadIcon

304

本文将介绍如何将 Shelly® 继电器集成到我的智能家居系统中,并通过 VB.NET 中的例程来控制这些设备。

在为我的女婿打造了一个智能新家之后,我也想为自己的家添置 Shelly® 继电器。

我主要通过 Amazon Echo Dot®(“Alexa”)来控制这些设备,但出于兴趣,并且因为找不到好的文档,我决定创建基于 .NET 的例程,使其也能控制或查询这些设备。

本文将介绍我为此目的创建的例程,并指出我所使用的模块的一些特殊之处。

我家中使用的是 Shelly 2.5、Shelly Dimmer2、Shelly 1PM 和 Shelly 2PM 模块。我为这些模块创建了例程。当然,还有其他一些模块,但尊贵的读者可能需要自行创建它们的例程,或许可以参考这个模板。

由于我只有 Visual Studio 2010®,这里使用的框架是 .NET 4.0。

基础

基本上,与设备的通信是通过 HTML 命令进行的。设备本身的反馈以 JSON string 的形式提供,我已经将我感兴趣的信息存储在相应的子类中。不幸的是,部分命令因设备而异,因此我不得不为每个设备创建特定的例程。

这里我假设读者具备 JSON string 反序列化的基本知识。我将不再深入讲解 WebClient 的使用。

我“对话”的是哪个设备?

Private Class ShellyTyp
        Public type As String
        Public app As String
 
        ReadOnly Property Typ() As String
            Get
                If type IsNot Nothing Then Return type
                If app IsNot Nothing Then Return app
                Return ""
            End Get
        End Property
 
    End Class
 
    Function Shelly_GetType(IpAdress As String) As ShellyType
 
        Request = "http://" + IpAdress + "/shelly"
        Dim myType As ShellyType = ShellyType.None
 
        Try
            Dim result As String = webClient.DownloadString(Request)
            Dim JSON_Packet As ShellyTyp = _
                JsonConvert.DeserializeObject(Of ShellyTyp)(result)
 
            Select Case JSON_Packet.Typ
                Case "SHSW-25" : myType = ShellyType.Shelly_25
                Case "SHDM-2" : myType = ShellyType.Shelly_Dimmer2
                Case "Plus1PM", "Plus1Mini" : myType = ShellyType.Shelly_1PM
                Case "Plus2PM" : myType = ShellyType.Shelly_2PM
            End Select
 
            Return myType
 
        Catch ex As Exception
            Return ShellyType.None
        End Try
 
    End Function
private class JSON_ShellyType
{
	public string type = "";
	public string app = "";
	public string Typ
	{
		get
		{
			if (type != "")
				return type;
			if (app != "")
				return app;
			return "";
		}
	}
}

public static ShellyType Shelly_GetType(string IpAdress)
{
	Request = "http://" + IpAdress + "/shelly";
	ShellyType myType = ShellyType.None;
	try
	{
		string result = webClient.DownloadString(Request);
		JSON_ShellyType JSON_Packet = JsonConvert.DeserializeObject<JSON_ShellyType>(result);
		switch (JSON_Packet.Typ)
		{
			case "SHSW-25":
			{
				myType = ShellyType.Shelly_25;
				break;
			}

			case "SHDM-2":
			{
				myType = ShellyType.Shelly_Dimmer2;
				break;
			}

			case "Plus1PM":
			case "Plus1Mini":
			{
				myType = ShellyType.Shelly_1PM;
				break;
			}

			case "Plus2PM":
			{
				myType = ShellyType.Shelly_2PM;
				break;
			}
		}

		return myType;
	}
	catch (Exception)
	{
		return ShellyType.None;
	}
}

从这里可以看出,对于所有设备的类型查询,都有一个通用的命令。根据设备的不同,类型响应再次存储在不同的 JSON 属性中,有些设备存储在 “type” 项中,有些设备存储在 “app” 项中。JSON 反序列化程序会将其中一个项填充到我的类中。

所示函数会返回相应的类型。我会在所有后续的查询/命令中使用此查询。

设备状态请求

Function Shelly_GetStatus(IpAdress As String) As IO_Status
	Dim myType As ShellyType = Shelly_GetType(IpAdress)
	Select Case myType
		Case ShellyType.Shelly_25
			Return Shelly_25_GetStatus(IpAdress)
		Case ShellyType.Shelly_Dimmer2
			Return Shelly_Dimmer2_GetStatus(IpAdress)
		Case ShellyType.Shelly_1PM
			Return Shelly_1PM_GetStatus(IpAdress)
		Case ShellyType.Shelly_2PM
			Return Shelly_2PM_GetStatus(IpAdress)
		Case ShellyType.None
			Return New IO_Status
	End Select

	Return New IO_Status
End Function

Class IO_Status

	Public Connection As ShellyResult = ShellyResult.None

	Public In0 As Boolean = False

	Public In1 As Boolean = False

	Public Out0 As Boolean = False

	Public Out1 As Boolean = False

	Public Mode As ShellyMode = ShellyMode.none

	Public OutValue As Integer = -1

	Overrides Function toString() As String
		Dim s As String = Connection.ToString
		Dim inActive As String = ""
		   If In0 Then inActive += "0"
		   If In1 Then inActive += "1"
		   If inActive < > "" Then s += ", in:" + inActive 
		Dim outActive As String = ""
			If Out0 Then outActive += "0"
			If Out1 Then outActive += "1"
			If outActive < > "" Then s += ", out:" + outActive 
			If OutValue > = 0 Then s += ", " + Str(OutValue).Trim + "%" 
		If Mode < > ShellyMode.none Then s += ", mode:" + Mode.ToString 
		Return s
    End Function
End Class
public static Shelly_IOStatus Shelly_GetStatus(string IpAdress)
{
	ShellyType myType = Shelly_GetType(IpAdress);
	switch (myType)
	{
		case ShellyType.Shelly_25:
		{
			return Shelly_25_GetStatus(IpAdress);
		}

		case ShellyType.Shelly_Dimmer2:
		{
			return Shelly_Dimmer2_GetStatus(IpAdress);
		}

		case ShellyType.Shelly_1PM:
		{
			return Shelly_1PM_GetStatus(IpAdress);
		}

		case ShellyType.Shelly_2PM:
		{
			return Shelly_2PM_GetStatus(IpAdress);
		}

		case ShellyType.None:
		{
			return new Shelly_IOStatus();
		}
	}

	return new Shelly_IOStatus();
}

public class Shelly_IOStatus
{
	public ShellyResult Connection = ShellyResult.None;
	public bool In0 = false;
	public bool In1 = false;
	public bool Out0 = false;
	public bool Out1 = false;
	public ShellyMode Mode = ShellyMode.none;
	public ShellyRollerState RollerState = ShellyRollerState.none;
	public int OutValue = -1;
	public override string ToString()
	{
		string s = Connection.ToString();
		string inActive = "";
		if (In0)
			inActive += "0";
		if (In1)
			inActive += "1";
		if (inActive != "")
			s += ", in:" + inActive;
		string outActive = "";
		if (Out0)
			outActive += "0";
		if (Out1)
			outActive += "1";
		if (outActive != "")
			s += ", out:" + outActive;
		if (OutValue >= 0)
			s += ", " + Convert.ToString(OutValue).Trim() + "%";
		if (Mode != ShellyMode.none)
			s += ", mode:" + Mode.ToString();
		return s;
	}
}

所示的 Shelly_GetStatus 函数返回指定 IP 地址的 Shelly 设备的当前状态。该函数本身会根据相应的 Shelly 类型分支到相应的子函数。

为了在此实现标准化,所有设备都有相同的 IO 状态,只有不存在的区域在子函数中不被赋值。

设备状态子函数

我将在此使用其中一个设备的示例来描述子函数本身。所有其他设备仅在命令和响应中收到的 JSON string 上有所不同。

在以下示例中,我将查询 Shelly-1PM 的状态。

Private Class JSON_Shelly12PM_Status

	 < Newtonsoft.Json.JsonProperty("switch:0") >  
     Public Switch0 As cRelay
	 < Newtonsoft.Json.JsonProperty("switch:1") >  
     Public Switch1 As cRelay
	 < Newtonsoft.Json.JsonProperty("cover:0") >  
     Public Cover0 As cCover
	 < Newtonsoft.Json.JsonProperty("input:0") >  
     Public Input0 As cInput
	 < Newtonsoft.Json.JsonProperty("input:1") >  
     Public Input1 As cInput

	Partial Public Class cRelay
		Public output As Boolean
	End Class

	Partial Public Class cCover
		Public state As String
		Public last_direction As String
		Public current_pos As Integer
	End Class

	Partial Public Class cInput
		Public state As Object
	End Class

	ReadOnly Property RelayState As Boolean()
		Get
			Dim myState(1) As Boolean
			If Switch0 IsNot Nothing Then myState(0) = Switch0.output
			If Switch1 IsNot Nothing Then myState(1) = Switch1.output
			If Cover0 IsNot Nothing Then
				Select Case Cover0.state
					Case "stopped"
						myState(0) = False
						myState(1) = False
					Case "opening"
						myState(0) = True
						myState(1) = False
					Case "closing"
						myState(0) = False
						myState(1) = True
				End Select
			End If

			Return myState
		End Get
	End Property

	ReadOnly Property InputState As Boolean()
		Get
			Dim myState(1) As Boolean
			If Not Boolean.TryParse(Input0.state, myState(0)) Then myState(0) = False
			If Not Boolean.TryParse(Input1.state, myState(1)) Then myState(1) = False
			Return myState
		End Get
	End Property

	ReadOnly Property Mode As ShellyMode
		Get
			If Switch0 IsNot Nothing Then Return ShellyMode.Relay
			If Cover0 IsNot Nothing Then Return ShellyMode.Roller
			Return ShellyMode.none
		End Get
	End Property

	ReadOnly Property RollerState As ShellyRollerState
		Get
			If Cover0 IsNot Nothing Then
				If(Cover0.state = "stop") And (Cover0.last_direction = "opening") Then  Return ShellyRollerState.Stop_AfterOpening  
                If(Cover0.state = "closing") Then Return ShellyRollerState.Closing
				If(Cover0.state = "stop") And (Cover0.last_direction = "closing") Then  Return ShellyRollerState.Stop_AfterClosing  
                If(Cover0.state = "opening") Then Return ShellyRollerState.Opening
			End If

			Return ShellyRollerState.none
	    End Get
	End Property
End Class

Function Shelly_1PM_GetStatus(IpAdress As String) As IO_Status
	Dim myStatus As New IO_Status
	Request = "http://" + IpAdress + "/rpc/Shelly.GetStatus"
	Try
		Dim result As String = webClient.DownloadString(Request)
		Dim JSON_Packet As JSON_Shelly12PM_Status =
		 JsonConvert.DeserializeObject(Of JSON_Shelly12PM_Status)(result)
		myStatus.Out0 = JSON_Packet.RelayState(0)
		myStatus.Out0 = False
		myStatus.OutValue = -1
		myStatus.Mode = "Relay"
		myStatus.In0 = JSON_Packet.InputState(0)
		myStatus.In1 = False
		myStatus.Connection = ShellyResult.Connected
		Return myStatus

	Catch ex As Exception
		myStatus.Connection = ShellyResult.ErrorConnection
		Return myStatus
	End Try
End Function
private class JSON_Shelly12PM_Status
{
	[Newtonsoft.Json.JsonProperty("switch:0")]
	public cRelay Switch0 = null;
	[Newtonsoft.Json.JsonProperty("switch:1")]
	public cRelay Switch1 = null;
	[Newtonsoft.Json.JsonProperty("cover:0")]
	public cCover Cover0 = null;
	[Newtonsoft.Json.JsonProperty("input:0")]
	public cInput Input0 = null;
	[Newtonsoft.Json.JsonProperty("input:1")]
	public cInput Input1 = null;
	public partial class cRelay
	{
		public bool output = false;
	}

	public partial class cCover
	{
		public string state = "";
		public string last_direction = "";
		public int current_pos = 0;
	}

	public partial class cInput
	{
		public object state = null;
	}

	public bool[] RelayState
	{
		get
		{
			bool[] myState = new bool[2];
			if (Switch0 != null)
				myState[0] = Switch0.output;
			if (Switch1 != null)
				myState[1] = Switch1.output;
			if (Cover0 != null)
			{
				switch (Cover0.state)
				{
					case "stopped":
					{
						myState[0] = false;
						myState[1] = false;
						break;
					}

					case "opening":
					{
						myState[0] = true;
						myState[1] = false;
						break;
					}

					case "closing":
					{
						myState[0] = false;
						myState[1] = true;
						break;
					}
				}
			}

			return myState;
		}
	}

	public bool[] InputState
	{
		get
		{
			bool[] myState = new bool[2];
			if (!bool.TryParse(Convert.ToString(Input0.state), out myState[0]))
			{
				myState[0] = false;
			}

			if (!bool.TryParse(Convert.ToString(Input1.state), out myState[1]))
			{
				myState[1] = false;
			}

			return myState;
		}
	}

	public ShellyMode Mode
	{
		get
		{
			if (Switch0 != null)
				return ShellyMode.Relay;
			if (Cover0 != null)
				return ShellyMode.Roller;
			return ShellyMode.none;
		}
	}

	public ShellyRollerState RollerState
	{
		get
		{
			if (Cover0 != null)
			{
				if ((Cover0.state == "stop") & (Cover0.last_direction == "opening"))
					return ShellyRollerState.Stop_AfterOpening;
				if ((Cover0.state == "closing"))
					return ShellyRollerState.Closing;
				if ((Cover0.state == "stop") & (Cover0.last_direction == "closing"))
					return ShellyRollerState.Stop_AfterClosing;
				if ((Cover0.state == "opening"))
					return ShellyRollerState.Opening;
			}

			return ShellyRollerState.none;
		}
	}
}

private static Shelly_IOStatus Shelly_1PM_GetStatus(string IpAdress)
{
	Shelly_IOStatus myStatus = new Shelly_IOStatus();
	Request = "http://" + IpAdress + "/rpc/Shelly.GetStatus";
	try
	{
		string result = webClient.DownloadString(Request);
		JSON_Shelly12PM_Status JSON_Packet = JsonConvert.DeserializeObject<JSON_Shelly12PM_Status>(result);
		myStatus.Out0 = JSON_Packet.RelayState[0];
		myStatus.Out0 = false;
		myStatus.OutValue = -1;
		myStatus.Mode = ShellyMode.Relay;
		myStatus.In0 = JSON_Packet.InputState[0];
		myStatus.In1 = false;
		myStatus.Connection = ShellyResult.Connected;
		return myStatus;
	}
	catch (Exception)
	{
		myStatus.Connection = ShellyResult.ErrorConnection;
		return myStatus;
	}
}

Shelly-1PM 是一个单通道继电器,只有一个输入。但是,设备本身返回的 JSON stringShelly-2PM 的没有区别,这就是为什么我对这两个设备使用相同的类来反序列化 JSON 字符串。

控制设备 / 发送命令

作为示例,我将展示控制 Shelly 继电器的函数。还可以选择控制调光器的亮度并将百叶窗移动到特定位置。但是,这些功能在基本原理上都没有区别。

Function Shelly_SetOutput(IpAdress As String, OutNr As Integer, State As Boolean  ) As ShellyResult  
    Dim myType As ShellyType = Shelly_GetType(IpAdress)
	Request = "http://" + IpAdress + "/relay/"
	Select Case myType
		Case ShellyType.Shelly_1PM
			Request += "0?turn="
			If Not State Then
				Request += "off"
			Else
				Request += "on"
			End If

		Case ShellyType.Shelly_2PM, ShellyType.Shelly_25
			Select Case OutNr
				Case 0, 1
					Request += Str(OutNr).Trim
				Case Else
					Return ShellyResult.ErrorShellyType
			End Select

			Request += "?turn="
			If Not State Then
				Request += "off"
			Else
				Request += "on"
			End If

		Case ShellyType.Shelly_Dimmer2
			Request = "http://" + IpAdress + "/light/0?turn="
			If Not State Then
				Request += "off"
			Else
				Request += "on"
			End If

		Case Else
			Return ShellyResult.NoAction
	End Select

	Try
		Dim result As String = webClient.DownloadString(Request)
		Return ShellyResult.Done
	Catch ex As Exception
		Return ShellyResult.ErrorConnection
	End Try

	Return ShellyResult.NoAction
End Function
public static ShellyResult Shelly_SetOutput(string IpAdress, int OutNr, bool State)
{
ShellyType myType = Shelly_GetType(IpAdress);
Request = "http://" + IpAdress + "/relay/";
switch (myType)
{
	case ShellyType.Shelly_1PM:
	{
		Request += "0?turn=";
		if (!State)
			Request += "off";
		else
			Request += "on";
		break;
	}

	case ShellyType.Shelly_2PM:
	case ShellyType.Shelly_25:
	{
		switch (OutNr)
		{
			case 0:
			case 1:
			{
				Request += Convert.ToString(OutNr).Trim();
				break;
			}

			default:
			{
				return ShellyResult.ErrorShellyType;
			}
		}

		Request += "?turn=";
		if (!State)
			Request += "off";
		else
			Request += "on";
		break;
	}

	case ShellyType.Shelly_Dimmer2:
	{
		Request = "http://" + IpAdress + "/light/0?turn=";
		if (!State)
			Request += "off";
		else
			Request += "on";
		break;
	}

	default:
	{
		return ShellyResult.NoAction;
	}
}

try
{
	string result = webClient.DownloadString(Request);
	return ShellyResult.Done;
}
catch (Exception)
{
	return ShellyResult.ErrorConnection;
} //return ShellyResult.NoAction;
}

集成到按钮控件

以下代码展示了如何将这些方法集成到按钮控件中。在这种情况下,我扩展了标准按钮的一些属性,并相应地集成了这些功能。

在单击事件中,按钮现在会调用 Shelly_ToggleOutput 方法,并根据所选 Shelly 设备的输出状态更改其颜色。

Imports System.ComponentModel

Public Class ShellyButton
	Inherits Button

	Sub New()
		MyBase.BackColor = my_DefaultBackColor
		MyBase.ForeColor = my_DefaultForeColor
	End Sub

	 #Region "Properties"
     ' makes the Standard - Property unvisible inside the PropertyGrid   
     < Browsable(False), EditorBrowsable(EditorBrowsableState.Never) >  
     Shadows Property ForeColor As Color

	 ' Replacement for the Standard - Property inside the PropertyGrid   
     < Category("Shelly"), Description("Default ForeColor of the Control") >   
     < DefaultValue(GetType(System.Drawing.Color), "Black") >  
     Property DefaultForeColor As Color
		Get
			Return my_DefaultForeColor
		End Get

		Set(ByVal value As Color)
			my_DefaultForeColor = value
			MyBase.BackColor = value
		End Set
	 End Property

	 Private my_DefaultForeColor As Color = Color.Black

	 < Category("Shelly"), Description("ForeColor of the Control when animated") >   
     < DefaultValue(GetType(System.Drawing.Color), "White") >  
     Property AnimationForeColor As Color
		Get
			Return my_AnimationForeColor
		End Get

		Set(ByVal value As Color)
			my_AnimationForeColor = value
		End Set
	End Property

	Private my_AnimationForeColor As Color = Color.White

	 ' makes the Standard - Property unvisible inside the PropertyGrid   
     < Browsable(False), EditorBrowsable(EditorBrowsableState.Never) >  
     Shadows Property BackColor As Color

	 ' Replacement for the Standard - Property inside the PropertyGrid   
     < Category("Shelly"), Description("Default BackColor of the Control") >   
     < DefaultValue(GetType(System.Drawing.Color), "LightGray") >  
     Property DefaultBackColor As Color
		Get
			Return my_DefaultBackColor
		End Get

		Set(ByVal value As Color)
			my_DefaultBackColor = value
			MyBase.BackColor = value
			Me.Invalidate()
		End Set
	End Property

	Private my_DefaultBackColor As Color = Color.LightGray

	< Category("Shelly"), Description("BackColor of the Control when animated") >
    < DefaultValue(GetType(System.Drawing.Color), "Green") >  
    Property AnimationBackColor As Color
		Get
			Return my_AnimationBackColor
		End Get

		Set(ByVal value As Color)
			my_AnimationBackColor = value
			Me.Invalidate()
		End Set
	End Property

	Private my_AnimationBackColor As Color = Color.Green

	 < Category("Shelly"), Description("Refresh-Interval for the Animation") >   
     < DefaultValue(1000) >  
     Property RefreshInterval As Integer
		Get
			Return my_Timer.Interval
		End Get

		Set(value As Integer)
			If value > 500 Then 
				my_Timer.Interval = value
			End If
		End Set
	End Property

	 < Category("Shelly"), Description("Enables the Refresh of the Animation") >   
     < DefaultValue(False) >  
     Property RefreshEnabled As Boolean
		Get
			Return my_RefreshEnabled
		End Get

		Set(value As Boolean)
			my_RefreshEnabled = value
			If Not DesignMode Then my_Timer.Enabled = value
		End Set
	End Property

	Private my_RefreshEnabled As Boolean = False

	 < 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 = Shelly_GetType(value).ToString
			If my_ShellyType < > "None" Then my_IPAdress = value 
			  End Set
	End Property

	Private my_IPAdress As String = ""

	 < Category("Shelly"), Description("Output-Number of the Shelly-Device to work with") >   
     < DefaultValue(0) >  
     Property ShellyOutputNr As Integer
		Get
			Return my_ShellyOutputNr
		End Get

		Set(value As Integer)
			If(value > = 0) And(value < = 1)  Then my_ShellyOutputNr = value
		End Set
	End Property

	Private my_ShellyOutputNr As Integer = 0

	< Category("Shelly"), Description("shows the Type of the connected Shelly-Device") >  
    ReadOnly Property ShellyType As String
		Get
			Return my_ShellyType
		End Get
	End Property

	Private my_ShellyType As String

	 #End Region

#Region "Methods"
' call the ToggleButton - Method with the Button - Click  
    Protected Overrides Sub OnClick(e As System.EventArgs)
	   Dim result As ShellyResult = Shelly_ToggleOutput(my_IPAdress, my_ShellyOutputNr)
	End Sub

	 ' the Timer - Tick does when activated the Animation of the Button  
     Sub Timer_Tick() Handles my_Timer.Tick
		my_Status = Shelly_GetStatus(my_IPAdress)
		my_OutActive =(my_ShellyOutputNr = 0 And my_Status.Out0)
		 Or  (my_ShellyOutputNr = 1 And my_Status.Out1)  If my_OutActive Then
			MyBase.BackColor = my_AnimationBackColor
			MyBase.ForeColor = my_AnimationForeColor
		Else
			MyBase.BackColor = my_DefaultBackColor
			MyBase.ForeColor = my_DefaultForeColor
		End If
	End Sub

	Private my_Status As Shelly_IOStatus

	Private my_OutActive As Boolean = False

	Private WithEvents my_Timer As New Timer With {.Enabled = False, .Interval = 1000}
#End Region
End Class
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using ShellyConnect_C;

//using ShellyCom;
public class ShellyButton : System.Windows.Forms.Button
{
	public ShellyButton()
	{
		my_Timer = new System.Windows.Forms.Timer()
		{
			Enabled = false,
			Interval = 1000
		};
		my_Timer.Tick += Timer_Tick;
		base.BackColor = my_DefaultBackColor;
		base.ForeColor = my_DefaultForeColor;
	}

	// makes the Standard -Property unvisible inside the PropertyGrid
    [Browsable(false)][EditorBrowsable(EditorBrowsableState.Never)] 
    public new Color ForeColor { get; set; }

	// Replacement for the Standard-Property inside the PropertyGrid
	[Category("Shelly")]
	[Description("Default ForeColor of the Control ")]
    [DefaultValue(typeof(System.Drawing.Color), "Black")] 
    public new Color DefaultForeColor
	{
		get
		{
			return my_DefaultForeColor;
		}

		set
		{
			my_DefaultForeColor = value;
			base.BackColor = value;
		}
	}

	private Color my_DefaultForeColor = Color.Black;

	[Category("Shelly")]
	[Description("ForeColor of the Control when animated ")]
    [DefaultValue(typeof(System.Drawing.Color), "White")] 
    public Color AnimationForeColor
	{
		get
		{
			return my_AnimationForeColor;
		}

		set
		{
			my_AnimationForeColor = value;
		}
	}

	private Color my_AnimationForeColor = Color.White;

	// makes the Standard -Property unvisible inside the PropertyGrid
   [Browsable(false)][EditorBrowsable(EditorBrowsableState.Never)] 
   public new Color BackColor { get; set; }

	// Replacement for the Standard-Property inside the PropertyGrid
	[Category("Shelly")]
	[Description("Default BackColor of the Control ")]
    [DefaultValue(typeof(System.Drawing.Color), "LightGray")] 
    public new Color DefaultBackColor
	{
		get
		{
			return my_DefaultBackColor;
		}

		set
		{
			my_DefaultBackColor = value;
			base.BackColor = value;
			this.Invalidate();
		}
	}

	private Color my_DefaultBackColor = Color.LightGray;

	[Category("Shelly")]
	[Description("BackColor of the Control when animated ")]
    [DefaultValue(typeof(System.Drawing.Color), "Green")] 
    public Color AnimationBackColor
	{
		get
		{
			return my_AnimationBackColor;
		}

		set
		{
			my_AnimationBackColor = value;
			this.Invalidate();
		}
	}

	private Color my_AnimationBackColor = Color.Green;

	[Category("Shelly")]
	[Description("Refresh-Interval for the Animation ")]
    [DefaultValue(1000)] 
    public int RefreshInterval
	{
		get
		{
			return my_Timer.Interval;
		}

		set
		{
			if (value > 500)
				my_Timer.Interval = value;
		}
	}

	[Category("Shelly")]
	[Description("Enables the Refresh of the Animation")]
	[DefaultValue(false)]
	public bool RefreshEnabled
	{
		get
		{
			return my_RefreshEnabled;
		}

		set
		{
			my_RefreshEnabled = value;
			if (!DesignMode)
				my_Timer.Enabled = value;
		}
	}

	private bool my_RefreshEnabled = false;

	[Category("Shelly")]
	[Description("IpAdress of the Shelly - Device to work with ")]
    [RefreshProperties(RefreshProperties.All)] 
    public string IpAdress
	{
		get
		{
			return my_IPAdress;
		}
		set
		{
			ShellyCom.ShellyType myType = ShellyCom.Shelly_GetType(value);
			my_ShellyType = Convert.ToString(myType);
			if (my_ShellyType != "None")
				my_IPAdress = value;
		}
	}

	private string my_IPAdress = "192.168.178.201";

	[Category("Shelly")]
	[Description("Output-Number of the Shelly - Device to work with ")]
    [DefaultValue(0)] 
    public int ShellyOutputNr
	{
		get
		{
			return my_ShellyOutputNr;
		}
		set
		{
			if ((value >= 0) & (value <= 1))
				my_ShellyOutputNr = value;
		}
	}

	private int my_ShellyOutputNr = 0;

	[Category("Shelly")]
	[Description("shows the Type of the connected Shelly - Device ")]
    public string ShellyType
	{
		get
		{
			return my_ShellyType;
		}
	}

	private string my_ShellyType;

	// call the ToggleButton -Method with the Button-Click 
    protected override void OnClick(System.EventArgs e)
	{
		ShellyCom.ShellyResult result = ShellyCom.Shelly_ToggleOutput(my_IPAdress, my_ShellyOutputNr);
	}

	// the Timer-Tick does when activated the Animation of the Button 
    public void Timer_Tick(object sender, System.EventArgs e)
	{
		my_Status = ShellyCom.Shelly_GetStatus(my_IPAdress);
		my_OutActive = (my_ShellyOutputNr == 0 & my_Status.Out0) | (my_ShellyOutputNr == 1 & my_Status.Out1);
		if (my_OutActive)
		{
			base.BackColor = my_AnimationBackColor;
			base.ForeColor = my_AnimationForeColor;
		}
		else
		{
			base.BackColor = my_DefaultBackColor;
			base.ForeColor = my_DefaultForeColor;
		}
	}

	private ShellyCom.Shelly_IOStatus my_Status;
	private bool my_OutActive = false;
	private Timer my_Timer;
}

关注点

总体而言,包含以下方法:

Shelly_GetStatusString 将完整且格式化的结果字符串传递给选定的请求。
Shelly_GetType 获取指定 IP 地址的 Shelly 设备的类型。
Shelly_GetStatus 将指定 IP 地址的 Shelly 设备的当前状态传输过来,相应的特性在 Shelly_IOStatus 中返回。根据设备类型,会使用子方法:
  • Shelly_25_GetStatus:获取 Shelly 2.5 的状态。
  • Shelly_25_convertJSON:转换 Shelly 2.5 请求的 JSON 字符串。
  • Shelly_Dimmer2_GetStatus:获取 Shelly Dimmer2 的状态。
  • Shelly_Dimmer2_convertJSON:转换 Shelly Dimmer2 请求的 JSON 字符串。
  • Shelly_1PM_GetStatus:获取 Shelly 1PM 的状态。
  • Shelly_1PM_convertJSON:转换 Shelly 1PM 请求的 JSON 字符串。
  • Shelly_2PM_GetStatus:获取 Shelly 2PM 的状态。
  • Shelly_2PM_convertJSON:转换 Shelly 2PM 请求的 JSON 字符串。
Shelly_SetOutput 将指定 IP 地址的 Shelly 设备上选定的输出设置为选定的状态。
Shelly_ToggleOutput 切换指定 IP 地址的 Shelly 设备上选定输出的状态。
Shelly_SetRoller 将指定 IP 地址的 Shelly 设备上的百叶窗/卷帘设置为选定的位置。
Shelly_ToggleRoller 切换指定 IP 地址的 Shelly 设备上百叶窗/卷帘的驱动状态到选定的位置。
Shelly_SetDimmer 将指定 IP 地址的 Shelly 设备上的调光器控制到选定的亮度值。

返回类型包括:

Enum ShellyType 可能的 Shelly 类型。
Enum ShellyResult 请求可能的返回结果。
Enum ShellyMode Shelly 设备可能的操作模式。
Enum ShellyRollerState 百叶窗/卷帘驱动可能的状
Class Shelly IOStatus 已请求的 Shelly 设备的 IO 状态。

最后的话

我感谢 @RichardDeeming 和 @Andre Oosthuizen 在我不知道的某些细节上提供的帮助。

我从 Shelly Support 页面获取了关于设备本身的基本信息。

我通过逆向工程自行确定了查询的项名称。

历史

  • 2023年11月23日:初始版本
© . All rights reserved.