mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-15 04:34:56 +00:00
refactor: rearrange and add solution for 4-series
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner
|
||||
{
|
||||
private EssentialsRoomCombinerPropertiesConfig _propertiesConfig;
|
||||
|
||||
private IRoomCombinationScenario _currentScenario;
|
||||
|
||||
private List<IEssentialsRoom> _rooms;
|
||||
|
||||
private bool isInAutoMode;
|
||||
|
||||
private CTimer _scenarioChangeDebounceTimer;
|
||||
|
||||
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
|
||||
|
||||
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
|
||||
: base(key)
|
||||
{
|
||||
_propertiesConfig = props;
|
||||
|
||||
Partitions = new List<IPartitionController>();
|
||||
RoomCombinationScenarios = new List<IRoomCombinationScenario>();
|
||||
|
||||
if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0)
|
||||
{
|
||||
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
|
||||
}
|
||||
|
||||
IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
|
||||
|
||||
// default to auto mode
|
||||
isInAutoMode = true;
|
||||
|
||||
if (_propertiesConfig.defaultToManualMode)
|
||||
{
|
||||
isInAutoMode = false;
|
||||
}
|
||||
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
|
||||
CreateScenarios();
|
||||
|
||||
AddPostActivationAction(() =>
|
||||
{
|
||||
SetupPartitionStateProviders();
|
||||
|
||||
SetRooms();
|
||||
|
||||
if (isInAutoMode)
|
||||
{
|
||||
DetermineRoomCombinationScenario();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CreateScenarios()
|
||||
{
|
||||
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
|
||||
{
|
||||
var scenario = new RoomCombinationScenario(scenarioConfig);
|
||||
RoomCombinationScenarios.Add(scenario);
|
||||
}
|
||||
}
|
||||
|
||||
void SetRooms()
|
||||
{
|
||||
_rooms = new List<IEssentialsRoom>();
|
||||
|
||||
foreach (var roomKey in _propertiesConfig.RoomKeys)
|
||||
{
|
||||
var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
|
||||
if (room != null)
|
||||
{
|
||||
_rooms.Add(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetupPartitionStateProviders()
|
||||
{
|
||||
foreach (var pConfig in _propertiesConfig.Partitions)
|
||||
{
|
||||
var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider;
|
||||
|
||||
var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys);
|
||||
|
||||
partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
|
||||
|
||||
Partitions.Add(partition);
|
||||
}
|
||||
}
|
||||
|
||||
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
StartDebounceTimer();
|
||||
}
|
||||
|
||||
void StartDebounceTimer()
|
||||
{
|
||||
var time = _scenarioChangeDebounceTimeSeconds * 1000;
|
||||
|
||||
if (_scenarioChangeDebounceTimer == null)
|
||||
{
|
||||
_scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time);
|
||||
}
|
||||
else
|
||||
{
|
||||
_scenarioChangeDebounceTimer.Reset(time);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the current room combination scenario based on the state of the partition sensors
|
||||
/// </summary>
|
||||
void DetermineRoomCombinationScenario()
|
||||
{
|
||||
if (_scenarioChangeDebounceTimer != null)
|
||||
{
|
||||
_scenarioChangeDebounceTimer.Dispose();
|
||||
_scenarioChangeDebounceTimer = null;
|
||||
}
|
||||
|
||||
var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>
|
||||
{
|
||||
// iterate the partition states
|
||||
foreach (var partitionState in s.PartitionStates)
|
||||
{
|
||||
// get the partition by key
|
||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
|
||||
|
||||
if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)
|
||||
{
|
||||
// the partition can't be found or the state doesn't match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if it hasn't returned false by now we have the matching scenario
|
||||
return true;
|
||||
});
|
||||
|
||||
if (currentScenario != null)
|
||||
{
|
||||
CurrentScenario = currentScenario;
|
||||
}
|
||||
}
|
||||
|
||||
#region IEssentialsRoomCombiner Members
|
||||
|
||||
public event EventHandler<EventArgs> RoomCombinationScenarioChanged;
|
||||
|
||||
public IRoomCombinationScenario CurrentScenario
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentScenario;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (value != _currentScenario)
|
||||
{
|
||||
// Deactivate the old scenario first
|
||||
if (_currentScenario != null)
|
||||
{
|
||||
_currentScenario.Deactivate();
|
||||
}
|
||||
|
||||
_currentScenario = value;
|
||||
|
||||
// Activate the new scenario
|
||||
if (_currentScenario != null)
|
||||
{
|
||||
_currentScenario.Activate();
|
||||
|
||||
Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name);
|
||||
}
|
||||
|
||||
var handler = RoomCombinationScenarioChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BoolFeedback IsInAutoModeFeedback { get; private set; }
|
||||
|
||||
public void SetAutoMode()
|
||||
{
|
||||
isInAutoMode = true;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
public void SetManualMode()
|
||||
{
|
||||
isInAutoMode = false;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
public void ToggleMode()
|
||||
{
|
||||
isInAutoMode = !isInAutoMode;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
|
||||
|
||||
public List<IPartitionController> Partitions { get; private set; }
|
||||
|
||||
public void TogglePartitionState(string partitionKey)
|
||||
{
|
||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
|
||||
|
||||
if (partition != null)
|
||||
{
|
||||
partition.ToggglePartitionState();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRoomCombinationScenario(string scenarioKey)
|
||||
{
|
||||
if (isInAutoMode)
|
||||
{
|
||||
Debug.Console(0, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the scenario
|
||||
var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey));
|
||||
|
||||
|
||||
// Set the parition states from the scenario manually
|
||||
if (scenario != null)
|
||||
{
|
||||
Debug.Console(0, this, "Manually setting scenario to '{0}'", scenario.Key);
|
||||
foreach (var partitionState in scenario.PartitionStates)
|
||||
{
|
||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
|
||||
|
||||
if (partition != null)
|
||||
{
|
||||
if (partitionState.PartitionPresent)
|
||||
{
|
||||
Debug.Console(0, this, "Manually setting state to Present for: '{0}'", partition.Key);
|
||||
partition.SetPartitionStatePresent();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(0, this, "Manually setting state to Not Present for: '{0}'", partition.Key);
|
||||
partition.SetPartitionStateNotPresent();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Unable to find partition with key: '{0}'", partitionState.PartitionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Unable to find scenario with key: '{0}'", scenarioKey);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory<EssentialsRoomCombiner>
|
||||
{
|
||||
public EssentialsRoomCombinerFactory()
|
||||
{
|
||||
TypeNames = new List<string> { "essentialsroomcombiner" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
|
||||
{
|
||||
Debug.Console(1, "Factory Attempting to create new EssentialsRoomCombiner Device");
|
||||
|
||||
var props = dc.Properties.ToObject<EssentialsRoomCombinerPropertiesConfig>();
|
||||
|
||||
return new EssentialsRoomCombiner(dc.Key, props);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
extern alias Full;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
using Full.Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Config properties for an EssentialsRoomCombiner device
|
||||
/// </summary>
|
||||
public class EssentialsRoomCombinerPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of partitions that device the rooms
|
||||
/// </summary>
|
||||
[JsonProperty("partitions")]
|
||||
public List<PartitionConfig> Partitions {get; set;}
|
||||
|
||||
/// <summary>
|
||||
/// The list of combinations scenarios for the rooms
|
||||
/// </summary>
|
||||
[JsonProperty("scenarios")]
|
||||
public List<RoomCombinationScenarioConfig> Scenarios { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of rooms keys that can be combined
|
||||
/// </summary>
|
||||
[JsonProperty("roomKeys")]
|
||||
public List<string> RoomKeys {get; set;}
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to default to manual mode
|
||||
/// </summary>
|
||||
[JsonProperty("defaultToManualMode")]
|
||||
public bool defaultToManualMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the scenario to default to at system startup if in manual mode
|
||||
/// </summary>
|
||||
[JsonProperty("defaultScenarioKey")]
|
||||
public string defaultScenarioKey { get; set; }
|
||||
|
||||
[JsonProperty("scenarioChangeDebounceTimeSeconds")]
|
||||
public int ScenarioChangeDebounceTimeSeconds { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Config properties for a partition that separates rooms
|
||||
/// </summary>
|
||||
public class PartitionConfig : IKeyName
|
||||
{
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key of the device that implements IPartitionStateProvider to provide the state of the partition
|
||||
/// </summary>
|
||||
[JsonProperty("deviceKey")]
|
||||
public string DeviceKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Keys of the rooms that this partion would be located between
|
||||
/// </summary>
|
||||
[JsonProperty("adjacentRoomKeys")]
|
||||
public List<string> AdjacentRoomKeys { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Config propeties for a room combination scenario
|
||||
/// </summary>
|
||||
public class RoomCombinationScenarioConfig : IKeyName
|
||||
{
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("partitionStates")]
|
||||
public List<PartitionState> PartitionStates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines which UI devices get mapped to which room in this scenario. The Key should be the key of the UI device and the Value should be the key of the room to map to
|
||||
/// </summary>
|
||||
[JsonProperty("uiMap")]
|
||||
public Dictionary<string, string> UiMap { get; set; }
|
||||
|
||||
[JsonProperty("activationActions")]
|
||||
public List<DeviceActionWrapper> ActivationActions { get; set; }
|
||||
|
||||
[JsonProperty("deactivationActions")]
|
||||
public List<DeviceActionWrapper> DeactivationActions { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Config properties to represent the state of a partition sensor in a RoomCombinationScenario
|
||||
/// </summary>
|
||||
public class PartitionState
|
||||
{
|
||||
[JsonProperty("partitionKey")]
|
||||
public string PartitionKey { get; set; }
|
||||
|
||||
[JsonProperty("partitionSensedState")]
|
||||
public bool PartitionPresent { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the functionality for an EssentailsRoomCombiner device
|
||||
/// </summary>
|
||||
public interface IEssentialsRoomCombiner : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the room combination scenario has changed
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> RoomCombinationScenarioChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The current room combination scenario
|
||||
/// </summary>
|
||||
IRoomCombinationScenario CurrentScenario { get; }
|
||||
|
||||
/// <summary>
|
||||
/// When true, indicates the current mode is auto mode
|
||||
/// </summary>
|
||||
BoolFeedback IsInAutoModeFeedback {get;}
|
||||
|
||||
/// <summary>
|
||||
/// Sets auto mode
|
||||
/// </summary>
|
||||
void SetAutoMode();
|
||||
|
||||
/// <summary>
|
||||
/// Sets manual mode
|
||||
/// </summary>
|
||||
void SetManualMode();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the current mode between auto and manual
|
||||
/// </summary>
|
||||
void ToggleMode();
|
||||
|
||||
/// <summary>
|
||||
/// The available room combinatino scenarios
|
||||
/// </summary>
|
||||
List<IRoomCombinationScenario> RoomCombinationScenarios { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The partition
|
||||
/// </summary>
|
||||
List<IPartitionController> Partitions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the state of a manual partition sensor
|
||||
/// </summary>
|
||||
/// <param name="partitionKey"></param>
|
||||
void TogglePartitionState(string partitionKey);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the room combination scenario (if in manual mode)
|
||||
/// </summary>
|
||||
/// <param name="scenarioKey"></param>
|
||||
void SetRoomCombinationScenario(string scenarioKey);
|
||||
}
|
||||
|
||||
public interface IRoomCombinationScenario : IKeyName
|
||||
{
|
||||
/// <summary>
|
||||
/// When true, indicates that this room combination scenario is active
|
||||
/// </summary>
|
||||
BoolFeedback IsActiveFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Activates this room combination scenario
|
||||
/// </summary>
|
||||
void Activate();
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates this room combination scenario
|
||||
/// </summary>
|
||||
void Deactivate();
|
||||
|
||||
/// <summary>
|
||||
/// The state of the partitions that would activate this scenario
|
||||
/// </summary>
|
||||
List<PartitionState> PartitionStates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The mapping of UIs by key to rooms by key
|
||||
/// </summary>
|
||||
Dictionary<string, string> UiMap { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
extern alias Full;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
using Full.Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a room combination scenario
|
||||
/// </summary>
|
||||
public class RoomCombinationScenario: IRoomCombinationScenario
|
||||
{
|
||||
private RoomCombinationScenarioConfig _config;
|
||||
|
||||
public string Key { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<PartitionState> PartitionStates { get; private set; }
|
||||
|
||||
public Dictionary<string, string> UiMap { get; set; }
|
||||
|
||||
private bool _isActive;
|
||||
|
||||
public BoolFeedback IsActiveFeedback { get; private set; }
|
||||
|
||||
private List<DeviceActionWrapper> activationActions;
|
||||
|
||||
private List<DeviceActionWrapper> deactivationActions;
|
||||
|
||||
public RoomCombinationScenario(RoomCombinationScenarioConfig config)
|
||||
{
|
||||
Key = config.Key;
|
||||
|
||||
Name = config.Name;
|
||||
|
||||
PartitionStates = config.PartitionStates;
|
||||
|
||||
UiMap = config.UiMap;
|
||||
|
||||
activationActions = config.ActivationActions;
|
||||
|
||||
deactivationActions = config.DeactivationActions;
|
||||
|
||||
_config = config;
|
||||
|
||||
IsActiveFeedback = new BoolFeedback(() => _isActive);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
Debug.Console(1, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count);
|
||||
|
||||
if (activationActions != null)
|
||||
{
|
||||
foreach (var action in activationActions)
|
||||
{
|
||||
DeviceJsonApi.DoDeviceAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
_isActive = true;
|
||||
IsActiveFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
Debug.Console(1, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count);
|
||||
|
||||
if (deactivationActions != null)
|
||||
{
|
||||
foreach (var action in deactivationActions)
|
||||
{
|
||||
DeviceJsonApi.DoDeviceAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
_isActive = false;
|
||||
IsActiveFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user