Skip to content

Commit

Permalink
Refactor classes for better usability (#39)
Browse files Browse the repository at this point in the history
Co-authored-by: x00Pavel <[email protected]>
  • Loading branch information
x00Pavel and PavelVeeamer authored Mar 21, 2024
1 parent 6d282db commit 43a1932
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 79 deletions.
95 changes: 50 additions & 45 deletions src/Core/Automations/AutomationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public abstract class AutomationBase: IAutomationConfig
public event EventHandler<bool>? IsEnabledChanged;
public IEnumerable<IStateChangeTrigger> Triggers { get; set; }
public IEnumerable<ICondition> Conditions { get; set; } = new []{new DefaultCondition()};
public string ServiceAccountId { get; set; }
public required string ServiceAccountId { get; set; }
public TimeSpan WaitTime { get; set; }
public TimeSpan SwitchTimer { get; set; }
public Func<TimeSpan>? StopAtTimeFunc { get; set; }
Expand All @@ -38,26 +38,25 @@ public bool IsEnabled
IsEnabledChanged?.Invoke(this, value);
}
}

/// <summary>
/// Helper observable for making actions when automation is disabled.
/// </summary>
public IObservable<bool> AutomationDisabled => IsEnabledObserver.Where(enabled => !enabled);

/// <summary>
/// Helper observable for making actions when automation is enabled.
/// </summary>
public IObservable<bool> AutomationEnabled => IsEnabledObserver.Where(enabled => enabled);

protected AutomationBase()
{
IsEnabledObserver = Observable.FromEventPattern<bool>(
handler => IsEnabledChanged += handler,
handler => IsEnabledChanged -= handler
).Select(pattern => pattern.EventArgs);
}
}

/// <summary>
/// This class represents a base for all automations.
/// The automation works with certain type of entities and uses Finite State Machine to store and represent the state of the entities.
/// </summary>
/// <typeparam name="TEntity">Type of entities an automation will work with</typeparam>
/// <typeparam name="TFsm">Type of Finite State Machine that will be used for storing and representing the state of TEntity</typeparam>
public abstract class AutomationBase<TEntity, TFsm>: AutomationBase
{
public List<TEntity> EntitiesList { get; set; }
protected List<TFsm> FsmList { get; } = [];

/// <summary>
/// Helper method to trigger an event at a specific time of the day.
/// It uses Observable.Timer to trigger the event.
Expand All @@ -75,32 +74,14 @@ protected void DailyEventAtTime(TimeSpan timeSpan, Action action)
});
Logger.LogDebug("Triggering first event in {Time}", triggerIn);
}

/// <summary>
/// Creates a Finite State Machine for each entity in the room.
/// </summary>
protected void CreateFsm()
{
Logger.LogDebug("Configuring {FsmName} ", typeof(TFsm).Name);
foreach (var entity in EntitiesList)
FsmList.Add(ConfigureFsm(entity));
}

/// <summary>
/// Abstract method to configure a Finite State Machine for a specific entity.
/// Each automation have to implement this method to configure the Finite State Machine for the specific entity.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
protected abstract TFsm ConfigureFsm(TEntity entity);


/// <summary>
/// Observable for all state changes of a specific entity.
/// </summary>
/// <param name="id">HomeAssistant ID of the entity</param>
/// <returns>Observable of StateChange</returns>
protected IObservable<StateChange> EntityEvent(string id) => Context.StateAllChanges().Where(e => e.New?.EntityId == id);

/// <summary>
/// Observable for all state changes of a specific entity initiated by a user.
///
Expand All @@ -122,7 +103,7 @@ protected IObservable<StateChange> UserEvent(string id) => EntityEvent(id)
/// <returns></returns>
protected IObservable<StateChange> AutomationEvent(string id) => EntityEvent(id)
.Where(e => e.IsAutomationInitiated(ServiceAccountId));

/// <summary>
/// Helper function to choose between two actions based on a condition.
/// Practically, it is a shorthand for if-else statement or a ternary operator.
Expand All @@ -131,17 +112,7 @@ protected IObservable<StateChange> AutomationEvent(string id) => EntityEvent(id)
/// <param name="action">Action if condition == true</param>
/// <param name="elseAction">Action on else branch</param>
protected static void ChooseAction(bool condition, Action action, Action elseAction) => (condition ? action : elseAction)();

/// <summary>
/// Helper observable for making actions when automation is disabled.
/// </summary>
public IObservable<bool> AutomationDisabled => IsEnabledObserver.Where(enabled => !enabled);

/// <summary>
/// Helper observable for making actions when automation is enabled.
/// </summary>
public IObservable<bool> AutomationEnabled => IsEnabledObserver.Where(enabled => enabled);


protected bool IsWorkingHours()
{
if (NightMode is { StartAtTimeFunc: not null, StopAtTimeFunc: not null })
Expand All @@ -163,3 +134,37 @@ protected bool IsWorkingHours()
return null;
}
}

public abstract class AutomationBase<TEntity> : AutomationBase
{
public List<TEntity> EntitiesList { get; set; }
}

/// <summary>
/// This class represents a base for all automations.
/// The automation works with certain type of entities and uses Finite State Machine to store and represent the state of the entities.
/// </summary>
/// <typeparam name="TEntity">Type of entities an automation will work with</typeparam>
/// <typeparam name="TFsm">Type of Finite State Machine that will be used for storing and representing the state of TEntity</typeparam>
public abstract class AutomationBase<TEntity, TFsm>: AutomationBase<TEntity>
{
protected List<TFsm> FsmList { get; } = [];

/// <summary>
/// Creates a Finite State Machine for each entity in the room.
/// </summary>
protected void CreateFsm()
{
Logger.LogDebug("Configuring {FsmName} ", typeof(TFsm).Name);
foreach (var entity in EntitiesList)
FsmList.Add(ConfigureFsm(entity));
}

/// <summary>
/// Abstract method to configure a Finite State Machine for a specific entity.
/// Each automation have to implement this method to configure the Finite State Machine for the specific entity.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
protected abstract TFsm ConfigureFsm(TEntity entity);
}
22 changes: 0 additions & 22 deletions src/Core/Configs/AutomationType.cs

This file was deleted.

4 changes: 1 addition & 3 deletions src/Core/Fsm/BlindsFsm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ public struct BlindsStateActivateAction
public Action CloseManuallyAction { get; init; }
}

public class BlindsFsm : FsmBase<BlindsState, BlindsTrigger>
public class BlindsFsm : FsmBase<BlindsState, BlindsTrigger, ICoverEntityCore>
{
// private new ICoverEntityCore Entity { get; set; }

public BlindsFsm(ILogger logger, ICoverEntityCore blinds) : base(logger)
{
Logger = logger;
Expand Down
6 changes: 2 additions & 4 deletions src/Core/Fsm/IFsmBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@ public static class FsmBaseExtensionMethods
public static void FireAllOff(this IEnumerable<IFsmBase> state)
{
foreach (var fsm in state)
{
fsm.FireAllOff();
}
}
}

public abstract class FsmBase<TState, TTrigger>(ILogger logger): IFsmBase
public abstract class FsmBase<TState, TTrigger, TEntity>(ILogger logger): IFsmBase
{
protected StateMachine<TState, TTrigger> _fsm;
protected ILogger Logger = logger;
public CustomTimer Timer = new (logger);
public TState State => _fsm.State;
protected TState DefaultState;
public bool IsEnabled { get; set; } = true;
public IEntityCore Entity { get; init; }
public TEntity Entity { get; init; }
protected string StorageDir => $"storage/{GetType().Name}";
protected string StoragePath { get; init; }

Expand Down
4 changes: 1 addition & 3 deletions src/Core/Fsm/LightFsmBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ public struct LightStateActivateAction
public Action<LightFsmBase> OffBySwitchAction { get; init; }

}
public class LightFsmBase : FsmBase<LightState, LightTrigger>
public class LightFsmBase : FsmBase<LightState, LightTrigger, ILightEntityCore>
{
public LightParameters? LastParams;
public new ILightEntityCore Entity { get; init; }

public LightFsmBase(ILightEntityCore light, ILogger logger) : base(logger)
{
DefaultState = LightState.Off;
Expand Down
3 changes: 1 addition & 2 deletions src/Core/Fsm/MainLightFsmBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ public struct MainLightActivateAction
public Action<MainLightFsmBase> OnAction { get; init; }
}

public class MainLightFsmBase : FsmBase<MainLightState, MainLightTrigger>
public class MainLightFsmBase : FsmBase<MainLightState, MainLightTrigger, ILightEntityCore>
{
public new ILightEntityCore Entity { get; init; }
public MainLightFsmBase(ILightEntityCore light, ILogger logger) : base(logger)
{
DefaultState = MainLightState.Off;
Expand Down

0 comments on commit 43a1932

Please sign in to comment.