Skip to content

Commit

Permalink
Merge pull request #328 from HomeSeer/release/1.4.3
Browse files Browse the repository at this point in the history
Release v1.4.3
  • Loading branch information
jldubz authored Feb 14, 2023
2 parents 270443f + 0dff0bf commit 3ba5928
Show file tree
Hide file tree
Showing 29 changed files with 1,973 additions and 461 deletions.
19 changes: 13 additions & 6 deletions Events/AbstractActionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public abstract class AbstractActionType {
/// -1 if it is not set
/// </remarks>
protected int SelectedSubActionIndex { get; private set; } = -1;

/// <summary>
/// Initialize a new <see cref="AbstractActionType"/> with the specified ID, Event Ref, and Data byte array.
/// The byte array will be automatically parsed for a <see cref="Page"/>, and a new one will be created if
Expand All @@ -78,8 +78,10 @@ public abstract class AbstractActionType {
/// </para>
/// </summary>
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="subTypeNumber">The action subtype number</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing the definition for a <see cref="Page"/></param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
protected AbstractActionType(int id, int subTypeNumber, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener) {
_id = id;
SelectedSubActionIndex = subTypeNumber;
Expand All @@ -104,6 +106,8 @@ protected AbstractActionType(int id, int subTypeNumber, int eventRef, byte[] dat
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing the definition for a <see cref="Page"/></param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
/// <param name="logDebug">If true debug messages will be written to the console</param>
protected AbstractActionType(int id, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener, bool logDebug = false) {
_id = id;
_eventRef = eventRef;
Expand All @@ -112,7 +116,7 @@ protected AbstractActionType(int id, int eventRef, byte[] dataIn, ActionTypeColl
LogDebug = logDebug;
InflateActionFromData();
}

/// <summary>
/// Initialize a new <see cref="AbstractActionType"/> with the specified ID, Event Ref, and Data byte array.
/// The byte array will be automatically parsed for a <see cref="Page"/>, and a new one will be created if
Expand All @@ -128,6 +132,7 @@ protected AbstractActionType(int id, int eventRef, byte[] dataIn, ActionTypeColl
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing the definition for a <see cref="Page"/></param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
protected AbstractActionType(int id, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener) {
_id = id;
_eventRef = eventRef;
Expand Down Expand Up @@ -294,9 +299,9 @@ internal bool ProcessPostData(Dictionary<string, string> changes) {
continue;
}

var viewType = ConfigPage.GetViewById(viewId).Type;
var originalView = ConfigPage.GetViewById(viewId);
try {
pageChanges.AddViewDelta(viewId, (int) viewType, changes[viewId]);
pageChanges.AddViewDelta(originalView, changes[viewId]);
}
catch (Exception exception) {
//Failed to add view change
Expand All @@ -314,7 +319,7 @@ internal bool ProcessPostData(Dictionary<string, string> changes) {
return true;
}

private byte[] ProcessData(byte[] inData) {
internal virtual byte[] ProcessData(byte[] inData) {
//Is data null/empty?
if (inData == null || inData.Length == 0) {
return new byte[0];
Expand Down Expand Up @@ -382,11 +387,12 @@ private void InflateActionFromData() {
}
}

private byte[] GetData() {
internal virtual byte[] GetData() {
var pageJson = ConfigPage.ToJsonString();
return Encoding.UTF8.GetBytes(pageJson);
}

/// <inheritdoc />
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
Expand All @@ -411,6 +417,7 @@ public override bool Equals(object obj) {
return true;
}

/// <inheritdoc />
public override int GetHashCode() {
return 271828 * _id.GetHashCode() * _eventRef.GetHashCode();
}
Expand Down
149 changes: 149 additions & 0 deletions Events/AbstractActionType2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Text;
using HomeSeer.Jui.Types;
using HomeSeer.Jui.Views;
using HomeSeer.PluginSdk.Devices;
using Newtonsoft.Json;

namespace HomeSeer.PluginSdk.Events {

/// <summary>
/// The base implementation of a plugin action type available for users to select in HomeSeer
/// <para>
/// The difference between <see cref="AbstractActionType2"/> and <see cref="AbstractActionType"/>, is that with
/// <see cref="AbstractActionType2"/> only a collection of view Id/Value pairs is stored in database whereas with
/// <see cref="AbstractActionType"/> the whole <see cref="AbstractActionType.ConfigPage"/> is stored. This allows
/// the plugin to build the views in <see cref="OnInstantiateAction"/> every time an action is instantiated.
/// </para>
/// <para>
/// Inherit from this class to define your own action types and store them in your plugin's <see cref="ActionTypeCollection"/>
/// </para>
/// </summary>
public abstract class AbstractActionType2: AbstractActionType {

/// <summary>
/// Initialize a new <see cref="AbstractActionType2"/> with the specified ID, SubType number, Event Ref, Data byte array and listener.
/// The byte array will be automatically parsed to a collection of view Id/Value pairs, and <see cref="OnInstantiateAction"/> will be called
/// <para>
/// This is called through reflection by the <see cref="ActionTypeCollection"/> class if a class that
/// derives from this type is added to its list.
/// </para>
/// <para>
/// You MUST implement one of these constructors in any class that derives from <see cref="AbstractActionType2"/>
/// </para>
/// </summary>
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="subTypeNumber">The action subtype number</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing a collection of view Id/Value pairs</param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
protected AbstractActionType2(int id, int subTypeNumber, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener)
:base(id, subTypeNumber, eventRef, dataIn, listener) {
}

/// <summary>
/// Initialize a new <see cref="AbstractActionType2"/> with the specified ID, Event Ref, Data byte array, listener, and logDebug flag.
/// The byte array will be automatically parsed to a collection of view Id/Value pairs, and <see cref="OnInstantiateAction"/> will be called
/// <para>
/// This is called through reflection by the <see cref="ActionTypeCollection"/> class if a class that
/// derives from this type is added to its list.
/// </para>
/// <para>
/// You MUST implement one of these constructors in any class that derives from <see cref="AbstractActionType2"/>
/// </para>
/// </summary>
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing a collection of view Id/Value pairs</param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
/// <param name="logDebug">If true debug messages will be written to the console</param>
protected AbstractActionType2(int id, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener, bool logDebug = false) :
base(id, eventRef, dataIn, listener, logDebug) {
}

/// <summary>
/// Initialize a new <see cref="AbstractActionType2"/> with the specified ID, Event Ref, and Data byte array.
/// The byte array will be automatically parsed to a collection of view Id/Value pairs, and <see cref="OnInstantiateAction"/> will be called.
/// <para>
/// This is called through reflection by the <see cref="ActionTypeCollection"/> class if a class that
/// derives from this type is added to its list.
/// </para>
/// <para>
/// You MUST implement one of these constructors in any class that derives from <see cref="AbstractActionType2"/>
/// </para>
/// </summary>
/// <param name="id">The unique ID of this action in HomeSeer</param>
/// <param name="eventRef">The event reference ID that this action is associated with in HomeSeer</param>
/// <param name="dataIn">A byte array containing a collection of view Id/Value pairs</param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
protected AbstractActionType2(int id, int eventRef, byte[] dataIn, ActionTypeCollection.IActionTypeListener listener) :
base(id, eventRef, dataIn, listener) {
}

/// <summary>
/// Initialize a new, unconfigured <see cref="AbstractActionType2"/>
/// <para>
/// This is called through reflection by the <see cref="ActionTypeCollection"/> class if a class that
/// derives from this type is added to its list.
/// </para>
/// </summary>
protected AbstractActionType2() : base() {}

/// <inheritdoc cref="AbstractActionType.OnNewAction" />
/// <remarks>
/// With <see cref="AbstractActionType2"/> there is no need to override this method. <see cref="OnInstantiateAction"/>
/// will be called instead, with an empty Dictionary as parameter.
/// </remarks>
protected override void OnNewAction() {
OnInstantiateAction(new Dictionary<string, string>());
}

/// <summary>
/// Called when an action of this type is being instantiated. Create the <see cref="AbstractActionType.ConfigPage"/> according
/// to the values passed as parameters. If no value is passed it means it's a new action, so initialize the
/// <see cref="AbstractActionType.ConfigPage"/> to the action's starting state so users can begin configuring it.
/// <para>
/// Any JUI view added to the <see cref="AbstractActionType.ConfigPage"/> must use a unique ID as it will
/// be displayed on an event page that could also be housing HTML from other plugins. It is recommended
/// to use the <see cref="AbstractActionType.PageId"/> as a prefix for all views added to ensure that their IDs are unique.
/// </para>
/// <param name="viewIdValuePairs">View Id/Value pairs containing the existing values for this action</param>
/// </summary>
protected abstract void OnInstantiateAction(Dictionary<string, string> viewIdValuePairs);

internal override byte[] ProcessData(byte[] inData) {
//Is data null/empty?
if (inData == null || inData.Length == 0) {
return new byte[0];
}

try {
//Get JSON string from byte[]
var valueMapJson = Encoding.UTF8.GetString(inData);
//Deserialize to values map
var valueMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(valueMapJson);
//Call the plugin to build the ConfigPage
OnInstantiateAction(valueMap);
//Save the data
return inData;
}
catch (Exception exception) {
//Exception is expected if the data is version 1 type or legacy type
if (LogDebug) {
Console.WriteLine($"Exception while trying to execute ProcessData on action data, possibly version 1 or legacy data - {exception.Message}");
}
}

//If deserialization failed try to call the ProcessData method from AbstractActionType so that it is directly deserialized as the ConfigPage
return base.ProcessData(inData);
}

internal override byte[] GetData() {
var valueMap = ConfigPage?.ToValueMap() ?? new Dictionary<string, string>();
var valueMapJson = JsonConvert.SerializeObject(valueMap, Formatting.None);
return Encoding.UTF8.GetBytes(valueMapJson);
}
}

}
36 changes: 28 additions & 8 deletions Events/AbstractTriggerType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected int SelectedSubTriggerIndex {
private readonly byte[] _inData;

/// <summary>
/// Initialize a new <see cref="AbstractTriggerType"/> with the specified ID, Event Ref, and Data byte array.
/// Initialize a new <see cref="AbstractTriggerType"/> with the specified ID, Event Ref, Data byte array, listener, and logDebug flag.
/// The byte array will be automatically parsed for a <see cref="Page"/>, and a new one will be created if
/// the array is empty.
/// <para>
Expand All @@ -86,6 +86,8 @@ protected int SelectedSubTriggerIndex {
/// <param name="eventRef">The event reference ID that this trigger is associated with in HomeSeer</param>
/// <param name="selectedSubTriggerIndex">The 0 based index of the sub-trigger type selected for this trigger</param>
/// <param name="dataIn">A byte array containing the definition for a <see cref="Page"/></param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
/// <param name="logDebug">If true debug messages will be written to the console</param>
protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex, byte[] dataIn, TriggerTypeCollection.ITriggerTypeListener listener, bool logDebug = false) {
_id = id;
_eventRef = eventRef;
Expand All @@ -95,9 +97,9 @@ protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex,
LogDebug = logDebug;
InflateTriggerFromData();
}

/// <summary>
/// Initialize a new <see cref="AbstractTriggerType"/> with the specified ID, Event Ref, and Data byte array.
/// Initialize a new <see cref="AbstractTriggerType"/> with the specified ID, Event Ref, Data byte array and listener.
/// The byte array will be automatically parsed for a <see cref="Page"/>, and a new one will be created if
/// the array is empty.
/// <para>
Expand All @@ -112,6 +114,7 @@ protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex,
/// <param name="eventRef">The event reference ID that this trigger is associated with in HomeSeer</param>
/// <param name="selectedSubTriggerIndex">The 0 based index of the sub-trigger type selected for this trigger</param>
/// <param name="dataIn">A byte array containing the definition for a <see cref="Page"/></param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex, byte[] dataIn, TriggerTypeCollection.ITriggerTypeListener listener) {
_id = id;
_eventRef = eventRef;
Expand All @@ -121,6 +124,21 @@ protected AbstractTriggerType(int id, int eventRef, int selectedSubTriggerIndex,
InflateTriggerFromData();
}

/// <summary>
/// Initialize a new <see cref="AbstractTriggerType"/> from a <see cref="TrigActInfo"/> and with the specified listener, and logDebug flag.
/// The byte array in <paramref name="trigInfo"/> will be automatically parsed for a <see cref="Page"/>, and a new one will be created if
/// the array is empty.
/// <para>
/// This is called through reflection by the <see cref="TriggerTypeCollection"/> class if a class that
/// derives from this type is added to its list.
/// </para>
/// <para>
/// You MUST implement one of these constructor signatures in any class that derives from <see cref="AbstractTriggerType"/>
/// </para>
/// </summary>
/// <param name="trigInfo">The <see cref="TrigActInfo"/> containing all the trigger information</param>
/// <param name="listener">The listener that facilitates the communication with <see cref="AbstractPlugin"/></param>
/// <param name="logDebug">If true debug messages will be written to the console</param>
protected AbstractTriggerType(TrigActInfo trigInfo, TriggerTypeCollection.ITriggerTypeListener listener, bool logDebug = false) {
_id = trigInfo.UID;
_eventRef = trigInfo.evRef;
Expand Down Expand Up @@ -332,9 +350,9 @@ internal bool ProcessPostData(Dictionary<string, string> changes) {
continue;
}

var viewType = ConfigPage.GetViewById(viewId).Type;
var originalView = ConfigPage.GetViewById(viewId);
try {
pageChanges.AddViewDelta(viewId, (int) viewType, changes[viewId]);
pageChanges.AddViewDelta(originalView, changes[viewId]);
}
catch (Exception exception) {
//Failed to add view change
Expand All @@ -352,7 +370,7 @@ internal bool ProcessPostData(Dictionary<string, string> changes) {
return true;
}

private byte[] ProcessData(byte[] inData) {
internal virtual byte[] ProcessData(byte[] inData) {
//Is data null/empty?
if (inData == null || inData.Length == 0) {
return new byte[0];
Expand Down Expand Up @@ -419,11 +437,12 @@ private void InflateTriggerFromData() {
}
}

private byte[] GetData() {
internal virtual byte[] GetData() {
var pageJson = ConfigPage.ToJsonString();
return Encoding.UTF8.GetBytes(pageJson);
}


/// <inheritdoc />
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
Expand All @@ -448,6 +467,7 @@ public override bool Equals(object obj) {
return true;
}

/// <inheritdoc />
public override int GetHashCode() {
return 271828 * _id.GetHashCode() * _eventRef.GetHashCode();
}
Expand Down
Loading

0 comments on commit 3ba5928

Please sign in to comment.