Introduction
The basis for this article is the description in my previous article: Controlling Shelly-Devices - Part 1.
This is a series of articles - this also includes: Controlling Shelly-Devices - Part 2.
In this article I present two more components, a few features that I think are useful, and then, remembering DRY (Don't Repeat Yourself), I modified my basic routines from the first two articles a little. So it makes sense (if you are interested) to download the archive of this article and replace the previous components. For reasons of compatibility, I leave the previous articles unchanged on this point.
The new components are:
ShellySceneComponent
– here you can define a scene that can consist of many actions but is only assigned to one control (button). ShellyScenesComponent
– Here you can define many scenes, which can consist of many actions and are assigned to corresponding controls (buttons).
Both components work with the standard controls without having to be adjusted.
As a feature, I added a TypeConverter
that allows you to use a list selection instead of an input for the IpAdress
property, which accesses the addresses that are basically known and therefore predefined for your own project.
I have also revised the ShellyCom
routines and added another modification of these routines as ShellyCom2
. Personally, I think the ShellyCom2
variant is more elegant.
The ShellySceneComponent
This component allows you to assign any number of actions to a control, which can then be triggered with a click. The basic function of this component does not differ significantly from the ShellyActionComponent
presented in the article Controlling Shelly-Devices - Part-2. Simply select a control and then assign any number of actions to it (see screenshot).
This component does not cause animation of the associated control.
The ShellyScenesComponent
This component allows you to assign any number of actions to many controls, which can then be triggered with a click of the respective control. The basic function of this component does not differ significantly from the previously mentioned components - except that another collection is called in a collection (see screenshots).
I present the structure of this constellation here as code:
<TypeConverter(GetType(ExpandableObjectConverter))>
Partial Public Class SceneAssignmentDefinition
<TypeConverter(GetType(DropDownConverter)), DropDownConverterData("myIpAdresses")>
<Category("Shelly"), Description("IpAdress of the Shelly-Device to work with")>
<RefreshProperties(RefreshProperties.All)>
Property IpAdress As String
Get
Return myIPAdress
End Get
Set(value As String)
If System.Net.IPAddress.TryParse(value, myIP) Then
myShellyType = ShellyCom.Shelly_GetType(value)
If myShellyType <> Shelly.ShellyType.None Then myIPAdress = value
If myShellyType = Shelly.ShellyType.Shelly_Dimmer2 Then myOutput = 0
End If
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
ReadOnly Property myIpAdresses As String()
Get
Return Shelly.My.IpAdresses
End Get
End Property
Private myIPAdress As String = ""
Private myIP As System.Net.IPAddress
<Category("Shelly"), Description("shows the Type of the connected Shelly-Device")>
ReadOnly Property ShellyType As String
Get
Return myShellyType.ToString
End Get
End Property
Private myShellyType As Shelly.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(Shelly.ActionDefinition), "none")>
Property Action As Shelly.ActionDefinition
Get
Return myAction
End Get
Set(value As Shelly.ActionDefinition)
myAction = value
End Set
End Property
Private myAction As Shelly.ActionDefinition = Shelly.ActionDefinition.none
Public Sub New()
End Sub
Public Overrides Function toString() As String
Return myIPAdress + ", " + myAction.ToString + ", O:" + myOutput.ToString.Trim + ", Val:" + myValue.ToString.Trim
End Function
End Class
<TypeConverter(GetType(ExpandableObjectConverter))>
Partial Public Class ControlAssignmentDefinition
<Category("Control"), Description("the Control which should do the Action(s)")>
Property SelectedControl As Control
Get
Return mySelectedControl
End Get
Set(value As Control)
mySelectedControl = value
End Set
End Property
Private mySelectedControl As Control = Nothing
Public savedBackColor As Color
Public savedForeColor As Color
<Category("Control"), Description("says that this Scene was last activated")>
<DefaultValue(False)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Property isActivated As Boolean
Get
Return myActivated
End Get
Set(value As Boolean)
myActivated = value
End Set
End Property
Private myActivated As Boolean = False
<Category("Control-Settings"), Description("Enables the Animation of this Control")>
<DefaultValue(True)>
Property EnableAnimation As Boolean
Get
Return my_EnableAnimation
End Get
Set(value As Boolean)
my_EnableAnimation = value
End Set
End Property
Private my_EnableAnimation As Boolean = True
<Category("Control"), Description("Assignment to the Shelly-Devices")>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
ReadOnly Property ShellyActions As ScenesCollection
Get
Return myShellyActions
End Get
End Property
Private myShellyActions As New ScenesCollection
Public Overrides Function toString() As String
If mySelectedControl IsNot Nothing Then Return mySelectedControl.Name
Return "[-]"
End Function
End Class
[TypeConverter(typeof(ExpandableObjectConverter))]
public partial class SceneAssignmentDefinition
{
[TypeConverter(typeof(DropDownConverter))]
[DropDownConverterData("myIpAdresses")]
[Category("Shelly")]
[Description("IpAdress of the Shelly-Device to work with")]
[RefreshProperties(RefreshProperties.All)]
public string IpAdress
{
get { return myIPAdress; }
set
{
if (System.Net.IPAddress.TryParse(value, out myIP))
{
myShellyType = ShellyCom.Shelly_GetType(value);
if (myShellyType != Shelly.ShellyType.None)
myIPAdress = value;
if (myShellyType == Shelly.ShellyType.Shelly_Dimmer2)
myOutput = 0;
}
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string[] myIpAdresses
{
get { return Shelly.My.IpAdresses; }
}
private string myIPAdress = "";
private System.Net.IPAddress myIP;
[Category("Shelly")]
[Description("shows the Type of the connected Shelly-Device")]
public string ShellyType
{
get { return Convert.ToString(myShellyType); }
}
private Shelly.ShellyType myShellyType;
[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(Shelly.ActionDefinition), "none")]
public Shelly.ActionDefinition Action
{
get { return myAction; }
set { myAction = value; }
}
private Shelly.ActionDefinition myAction = Shelly.ActionDefinition.none;
public SceneAssignmentDefinition()
{
}
public override string ToString()
{
return myIPAdress + ", " + Convert.ToString(myAction) + ", O:" + myOutput.ToString().Trim() + ", Val:" + myValue.ToString().Trim();
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public partial class ControlAssignmentDefinition
{
[Category("Control")]
[Description("the Control which should do the Action(s)")]
public Control SelectedControl
{
get { return mySelectedControl; }
set { mySelectedControl = value; }
}
private Control mySelectedControl = null;
public Color savedBackColor;
public Color savedForeColor;
[Category("Control")]
[Description("says that this Scene was last activated")]
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool isActivated
{
get { return myActivated; }
set { myActivated = value; }
}
private bool myActivated = false;
[Category("Control-Settings")]
[Description("Enables the Animation of this Control")]
[DefaultValue(true)]
public bool EnableAnimation
{
get { return my_EnableAnimation; }
set { my_EnableAnimation = value; }
}
private bool my_EnableAnimation = true;
[Category("Control")]
[Description("Assignment to the Shelly-Devices")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ScenesCollection ShellyActions
{
get { return myShellyActions; }
}
private ScenesCollection myShellyActions = new ScenesCollection();
public override string ToString()
{
if (mySelectedControl != null)
return mySelectedControl.Name;
return "[-]";
}
}
These were initially the two base classes for the two collections.
As you can see, the ScenesCollection
is already integrated into the ControlAssignmentDefinition
class.
These are the associated collections:
Partial Public Class ActionCollection
Inherits CollectionBase
Public Sub Add(ByVal item As ControlAssignmentDefinition)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then AddHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.Add(item)
End Sub
Public Sub Insert(ByVal item As ControlAssignmentDefinition)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then AddHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.Add(item)
End Sub
Public Sub Remove(ByVal index As Integer)
If (index > -1) Then
Dim item As ControlAssignmentDefinition = List.Item(index)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then RemoveHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.RemoveAt(index)
End If
End Sub
Public Property Item(ByVal index As Integer) As ControlAssignmentDefinition
Get
Return List(index)
End Get
Set(ByVal value As ControlAssignmentDefinition)
List(index) = value
End Set
End Property
Public ReadOnly Property Item(ByVal ControlName As String) As ControlAssignmentDefinition
Get
For i As Integer = 0 To List.Count - 1
If Item(i).SelectedControl.Name = ControlName Then Return Item(i)
Next
Return Nothing
End Get
End Property
Public Shadows Sub Clear()
For i As Integer = 0 To List.Count - 1
RemoveHandler Item(i).SelectedControl.Click, AddressOf ControlClickHandler
Next
List.Clear()
End Sub
Overrides Function ToString() As String
Return "[...]"
End Function
Public Sub Dispose()
Me.Clear()
End Sub
Property Enabled As Boolean
Get
Return my_Enabled
End Get
Set(value As Boolean)
my_Enabled = value
End Set
End Property
Private my_Enabled As Boolean = True
Private Sub ControlClickHandler(sender As System.Object, e As System.EventArgs)
If Not my_Enabled Then Exit Sub
Dim myControl As Control = sender
Dim myItem As ControlAssignmentDefinition = Item(myControl.Name)
If myItem IsNot Nothing Then
For i As Integer = 0 To List.Count - 1
Item(i).isActivated = False
Next
myItem.isActivated = True
For i As Integer = 0 To myItem.ShellyActions.Count - 1
Dim myAction As SceneAssignmentDefinition = myItem.ShellyActions.Item(i)
If myAction IsNot Nothing Then
If myAction.Action <> Shelly.ActionDefinition.none Then
Select Case myAction.Action
Case Shelly.ActionDefinition.SetOut
Dim myState As Boolean = myAction.Value > 0
ShellyCom.Shelly_SetOutput(myAction.IpAdress, myAction.OutputNr, myState)
Case Shelly.ActionDefinition.ToggleOut
ShellyCom.Shelly_ToggleOutput(myAction.IpAdress, myAction.OutputNr)
Case Shelly.ActionDefinition.SetDimmer
ShellyCom.Shelly_SetDimmer(myAction.IpAdress, myAction.Value)
Case Shelly.ActionDefinition.SetRoller
ShellyCom.Shelly_SetRoller(myAction.IpAdress, myAction.Value)
Case Shelly.ActionDefinition.ToggleRoller
ShellyCom.Shelly_ToggleRoller(myAction.IpAdress)
Case Shelly.ActionDefinition.none
End Select
End If
End If
Next
End If
End Sub
End Class
Partial Public Class ScenesCollection
Inherits CollectionBase
Public Sub Add(ByVal item As SceneAssignmentDefinition)
List.Add(item)
End Sub
Public Sub Insert(ByVal item As SceneAssignmentDefinition)
List.Add(item)
End Sub
Public Sub Remove(ByVal index As Integer)
If (index > -1) Then
Dim item As SceneAssignmentDefinition = List.Item(index)
List.RemoveAt(index)
End If
End Sub
Public Property Item(ByVal index As Integer) As SceneAssignmentDefinition
Get
Return List(index)
End Get
Set(ByVal value As SceneAssignmentDefinition)
List(index) = value
End Set
End Property
Public Shadows Sub Clear()
List.Clear()
End Sub
Overrides Function ToString() As String
Return "[...]"
End Function
Public Sub Dispose()
List.Clear()
End Sub
End Class
public class ActionCollection : System.Collections.Generic.List<ControlAssignmentDefinition>
{
public void Add(ControlAssignmentDefinition item)
{
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click += ControlClickHandler;
base.Add(item);
}
public void Insert(ControlAssignmentDefinition item)
{
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click += ControlClickHandler;
base.Add(item);
}
public void Remove(int index)
{
if ((index > -1))
{
ControlAssignmentDefinition item = (ControlAssignmentDefinition)this[index];
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click -= ControlClickHandler;
base.RemoveAt(index);
}
}
public ControlAssignmentDefinition Item(int index)
{ return (ControlAssignmentDefinition)this[index]; }
public ControlAssignmentDefinition Item(String ControlName)
{
for (int i = 0; i <= Count - 1; i++)
{
ControlAssignmentDefinition myItem = (ControlAssignmentDefinition)this[i];
if (myItem.SelectedControl.Name == ControlName)
return myItem;
}
return null;
}
public new void Clear()
{
for (int i = 0; i <= Count - 1; i++)
Item(i).SelectedControl.Click -= ControlClickHandler;
base.Clear();
}
public override string ToString()
{ return "[...]"; }
public void Dispose()
{ this.Clear(); }
public bool Enabled
{
get { return my_Enabled; }
set { my_Enabled = value; }
}
private bool my_Enabled = true;
private void ControlClickHandler(System.Object sender, System.EventArgs e)
{
if (!my_Enabled)
return;
Control myControl = (Control)sender;
ControlAssignmentDefinition myItem = Item(myControl.Name);
if (myItem != null)
{
for (int i = 0; i <= Count - 1; i++)
Item(i).isActivated = false;
myItem.isActivated = true;
for (int i = 0; i <= myItem.ShellyActions.Count - 1; i++)
{
SceneAssignmentDefinition myAction = myItem.ShellyActions.Item(i);
if (myAction != null)
{
if (myAction.Action != Shelly.ActionDefinition.none)
{
switch (myAction.Action)
{
case ActionDefinition.SetOut:
{
bool myState = myAction.Value > 0;
ShellyCom.Shelly_SetOutput(myAction.IpAdress, myAction.OutputNr, myState);
break;
}
case ActionDefinition.ToggleOut:
{
ShellyCom.Shelly_ToggleOutput(myAction.IpAdress, myAction.OutputNr);
break;
}
case ActionDefinition.SetDimmer:
{
ShellyCom.Shelly_SetDimmer(myAction.IpAdress, myAction.Value);
break;
}
case ActionDefinition.SetRoller:
{
ShellyCom.Shelly_SetRoller(myAction.IpAdress, myAction.Value);
break;
}
case ActionDefinition.ToggleRoller:
{
ShellyCom.Shelly_ToggleRoller(myAction.IpAdress);
break;
}
case ActionDefinition.none:
{ break; }
}
}
}
}
}
}
}
public partial class ScenesCollection : System.Collections.Generic.List<SceneAssignmentDefinition>
{
public void Add(SceneAssignmentDefinition item)
{ base.Add(item); }
public void Insert(SceneAssignmentDefinition item)
{ base.Add(item); }
public void Remove(int index)
{
if ((index > -1))
{ base.RemoveAt(index); }
}
public SceneAssignmentDefinition Item(int index)
{ return (SceneAssignmentDefinition)this[index]; }
public new void Clear()
{ base.Clear(); }
public override string ToString()
{ return "[...]"; }
public void Dispose()
{ base.Clear(); }
}
Inside the ActionCollection
, the click of the respective control is evaluated (ControlClickHandler
) and the assigned actions of the subordinate collection are executed.
Here, however, there is again the possibility of animating the triggering control. This is again done using a timer in the component itself.
The TypeConverter (DropDownConverter)
Here I show how to use the converter at the property:
<TypeConverter(GetType(DropDownConverter)), DropDownConverterData("myIpAdresses")>
<Category("Shelly"), Description("IpAdress of the Shelly-Device to work with")>
<RefreshProperties(RefreshProperties.All)>
Property IpAdress As String
Get
Return myIPAdress
End Get
Set(value As String)
If System.Net.IPAddress.TryParse(value, myIP) Then
myShellyType = ShellyCom.Shelly_GetType(value)
If myShellyType <> Shelly.ShellyType.None Then myIPAdress = value
If myShellyType = Shelly.ShellyType.Shelly_Dimmer2 Then myOutput = 0
End If
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
ReadOnly Property myIpAdresses As String()
Get
Return Shelly.My.IpAdresses
End Get
End Property
Private myIPAdress As String = ""
Private myIP As System.Net.IPAddress
[TypeConverter(typeof(DropDownConverter, DropDownConverterData("myIpAdresses")]
[Category("Shelly")]
[Description("IpAdress of the Shelly-Device to work with")]
[RefreshProperties(RefreshProperties.All)]
public string IpAdress
{
get { return myIPAdress; }
set
{
if (System.Net.IPAddress.TryParse(value, out myIP))
{
myShellyType = ShellyCom.Shelly_GetType(value);
if (myShellyType != Shelly.ShellyType.None)
myIPAdress = value;
if (myShellyType == Shelly.ShellyType.Shelly_Dimmer2)
myOutput = 0;
}
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string[] myIpAdresses
{
get { return Shelly.My.IpAdresses; }
}
private string myIPAdress = "";
private System.Net.IPAddress myIP;
The line with the assignment of the TypeConverter
determines whether a combo box function is activated when the property is selected in which the string array passed by the property myIpAddresses
is offered for selection, or if the line is commented out the standard function of the property takes place.
The IP addresses that I want to display are specified in the Definitions
file.
I've already posted the DropDownConverter
as a solution once or twice in the Q&A section at CodeProject.
Imports System.ComponentModel
Public Class DropDownConverter
Inherits StringConverter
Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overloads Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
Private EntriesArray(-1) As String
Public Overrides Function CanConvertFrom(context As System.ComponentModel.ITypeDescriptorContext, sourceType As System.Type) As Boolean
If sourceType Is GetType(String) Then Return True
Return MyBase.CanConvertFrom(context, sourceType)
End Function
Public Overloads Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As StandardValuesCollection
Dim myPD As PropertyDescriptor = Nothing
Dim myAttribute As DropDownConverterDataAttribute = context.PropertyDescriptor.Attributes(GetType(DropDownConverterDataAttribute))
If myAttribute IsNot Nothing Then
myPD = TypeDescriptor.GetProperties(context.Instance)(myAttribute.DataArray)
Else
Dim HostPropertyName As String = context.PropertyDescriptor.Name
Dim HostPropertyArrayName As String = HostPropertyName + "Array"
myPD = TypeDescriptor.GetProperties(context.Instance)(HostPropertyArrayName)
End If
If myPD IsNot Nothing Then
If myPD.PropertyType Is GetType(String()) Then
EntriesArray = myPD.GetValue(context.Instance)
ElseIf myPD.PropertyType Is GetType(List(Of String)) Then
Dim myList As List(Of String) = myPD.GetValue(context.Instance)
ReDim EntriesArray(myList.Count - 1)
For i As Integer = 0 To myList.Count - 1
EntriesArray(i) = myList(i)
Next
ElseIf myPD.PropertyType Is GetType(Collection) Then
Dim myCollection As Collection = myPD.GetValue(context.Instance)
ReDim EntriesArray(myCollection.Count - 1)
For i As Integer = 0 To myCollection.Count - 1
EntriesArray(i) = myCollection.Item(i + 1)
Next
End If
End If
Return New StandardValuesCollection(EntriesArray)
End Function
End Class
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Method)> _
Public Class DropDownConverterDataAttribute
Inherits Attribute
Public Sub New(DataArray_PropertyName As String)
my_DataArray = DataArray_PropertyName
End Sub
Public ReadOnly Property DataArray() As String
Get
Return my_DataArray
End Get
End Property
Private my_DataArray As String
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
public class DropDownConverter : StringConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{ return true; }
private string[] EntriesArray = new string[0];
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
PropertyDescriptor myPD = null;
DropDownConverterDataAttribute myAttribute = (DropDownConverterDataAttribute)context.PropertyDescriptor.Attributes[typeof(DropDownConverterDataAttribute)];
if (myAttribute != null)
myPD = TypeDescriptor.GetProperties(context.Instance)[myAttribute.DataArray];
else
{
string HostPropertyName = context.PropertyDescriptor.Name;
string HostPropertyArrayName = HostPropertyName + "Array";
myPD = TypeDescriptor.GetProperties(context.Instance)[HostPropertyArrayName];
}
if (myPD != null)
{
if (myPD.PropertyType == typeof(string[]))
EntriesArray = (string[]) myPD.GetValue(context.Instance );
else if (myPD.PropertyType == typeof(List<string>))
{
List<string> myList = (List<string>) myPD.GetValue(context.Instance);
EntriesArray = new string[myList.Count - 1 + 1];
for (int i = 0; i <= myList.Count - 1; i++)
EntriesArray[i] = myList[i];
}
}
return new TypeConverter.StandardValuesCollection(EntriesArray);
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public class DropDownConverterDataAttribute : Attribute
{
public DropDownConverterDataAttribute(string DataArray_PropertyName)
{ my_DataArray = DataArray_PropertyName; }
public string DataArray
{
get { return my_DataArray; }
}
private string my_DataArray;
}
Although I created the converter myself, I can only write a little about the internal functions.
In the lower part I created my own Attribute
that allows the converter to specify the data source. The converter itself now accesses the content of this attribute and forwards it to the property by using the PropertyDescriptor
. At the same time, the converter tells the PropertyGrid
that a combobox should be selected for the output.
I can pass the StringArray
and List(of String) types to the converter itself and, under VB.NET, the collection type as a data source.
ShellyCom und ShellyCom2
The two libraries do not differ in their basic functionality. The difference is in the status output.
With ShellyCom
there are individual variables for the status of the inputs and outputs. For the ShellyCom2
I have defined arrays that are as large as the actual inputs and outputs.
DRY – Don’t Repeat Yourself
Due to the different functions of the components, there would have been a repetition of various ENums
.
I have now moved this to the Definitions
file in the Shelly
namespace. This also changes the addressing when used in the components.
Finally – last words
This would be the end of my mini-series for now. However, I am open to suggestions or stimulations and maybe there will be a Part 4 after all.
If I use additional Shelly’s or gain access to them, I would expand/adapt the libraries accordingly.
I would like to thank (again) @sean-ewington for the help in creating this and the previous articles.