From f986b34d7825d1e82a84fdc1daac1b7926e828f8 Mon Sep 17 00:00:00 2001 From: Pavel Yadlouski <46033323+x00Pavel@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:52:31 +0100 Subject: [PATCH] Merge pull request #37 * Add service for disabling automations in room --- src/Core/Automations/AutomationBase.cs | 77 ++++++++------------- src/Core/Automations/LightAutomationBase.cs | 27 ++++++-- src/Core/RoomManager/Room.cs | 23 +++++- src/Core/RoomManager/RoomManager.csproj | 2 +- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/src/Core/Automations/AutomationBase.cs b/src/Core/Automations/AutomationBase.cs index 63f290b..1028cab 100644 --- a/src/Core/Automations/AutomationBase.cs +++ b/src/Core/Automations/AutomationBase.cs @@ -21,9 +21,24 @@ internal record ServiceData public string? value { get; init; } } -public interface IAutomationBase +public abstract class IAutomationBase { -}/// + private bool _enabled = true; + public IObservable IsEnabledObserver; + public event EventHandler? IsEnabledChanged; + public bool IsEnabled + { + get => _enabled; + set + { + if (_enabled == value) return; + _enabled = value; + IsEnabledChanged?.Invoke(this, value); + } + } +} + +/// /// 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. /// @@ -34,21 +49,9 @@ public abstract class AutomationBase: IAutomationBase protected IHaContext Context { get; set; } protected ILogger Logger { get; set; } protected AutomationConfig Config { get; set; } - protected IEnumerable EntitiesList { get; set; } + protected List EntitiesList { get; set; } protected List FsmList; - protected IObservable IsEnabledObserver; - private bool isEnabled { get; set; } = true; - public event EventHandler? IsEnabledChanged; - public bool IsEnabled - { - get => isEnabled; - set - { - if (isEnabled == value) return; - isEnabled = value; - IsEnabledChanged?.Invoke(this, value); - } - } + protected AutomationBase(IHaContext context, AutomationConfig config, ILogger logger) { Context = context; @@ -59,8 +62,7 @@ protected AutomationBase(IHaContext context, AutomationConfig config, ILogger lo handler => IsEnabledChanged -= handler ).Select(pattern => pattern.EventArgs); FsmList = new List(); - EntitiesList = Config.Entities.OfType().ToArray() ?? []; - InitServices(); + EntitiesList = Config.Entities.OfType().ToList(); if (Config is { StartAtTimeFunc: not null, StopAtTimeFunc: not null }) Logger.LogDebug("Working hours from {Start} - {End}", Config.StartAtTimeFunc() , Config.StopAtTimeFunc()); Logger.LogDebug("Night mode from {Start} - {End}", Config.NightMode.StartAtTimeFunc(), Config.NightMode.StopAtTimeFunc()); @@ -121,7 +123,7 @@ protected void CreateFsm() /// protected IObservable UserEvent(string id) => EntityEvent(id) .Where(e => !e.IsAutomationInitiated(Config.ServiceAccountId)); - + /// /// Observable for all state changes of a specific entity initiated by the automation. /// This method uses ServiceAccountId to determine if the state change was initiated by the automation. @@ -141,36 +143,15 @@ protected IObservable AutomationEvent(string id) => EntityEvent(id) protected static void ChooseAction(bool condition, Action action, Action elseAction) => (condition ? action : elseAction)(); /// - /// This method is used to initialise services for manipulating with the automation from Home Assistant side. - /// It is not yet fully implemented! + /// Helper observable for making actions when automation is disabled. /// - private void InitServices() - { - // Context.RegisterServiceCallBack($"automation_{Config.Name.Replace(' ', '_').ToLower()}_service", - // e => - // { - // if (Enum.TryParse(e.action, ignoreCase: true, out var action)) - // { - // - // Logger.LogInformation("Service called action: {Action}", action); - // IsEnabled = action switch - // { - // ServiceAction.Disable => false, - // ServiceAction.Enable => true, - // ServiceAction.Toggle => !isEnabled, - // _ => isEnabled - // }; - // - // Logger.LogDebug("Automation {AutomationName} is now {AutomationState}", Config.Name, isEnabled ? "enabled" : "disabled"); - // } - // else - // { - // Logger.LogWarning("Service called with unknown action: {Action} value: {value}", - // e.action, e.value); - // } - // }); - } - + public IObservable AutomationDisabled => IsEnabledObserver.Where(enabled => !enabled); + + /// + /// Helper observable for making actions when automation is enabled. + /// + public IObservable AutomationEnabled => IsEnabledObserver.Where(enabled => enabled); + protected bool IsWorkingHours() { if (Config is { StartAtTimeFunc: not null, StopAtTimeFunc: not null }) diff --git a/src/Core/Automations/LightAutomationBase.cs b/src/Core/Automations/LightAutomationBase.cs index 1200f21..f215027 100644 --- a/src/Core/Automations/LightAutomationBase.cs +++ b/src/Core/Automations/LightAutomationBase.cs @@ -4,6 +4,7 @@ using NetDaemon.HassModel.Entities; using NetEntityAutomation.Core.Configs; using NetEntityAutomation.Core.Fsm; +using NetEntityAutomation.Core.Triggers; using NetEntityAutomation.Extensions.ExtensionMethods; using Newtonsoft.Json; @@ -38,27 +39,43 @@ private void ConfigureAutomation() foreach (var sensor in Config.Triggers) { - sensor.On.Subscribe(TurnOnByAutomation); + sensor.On.Subscribe(e => { if (IsEnabled) TurnOnByAutomation(e.Entity); }); } + + AutomationDisabled.Subscribe(e => + { + Logger.LogDebug("Disabling automation {Automation}", nameof(LightAutomationBase)); + foreach (var fsm in LightOnByAutomation) + { + fsm.Timer.Dispose(); + } + }); + + AutomationEnabled.Subscribe(e => + { + Logger.LogDebug("Enabling automation {Automation}", nameof(LightAutomationBase)); + if (Config.Triggers.IsAllOn()) + EntitiesList.ForEach(l => TurnOnByAutomation(l)); + }); } - private void TurnOnByAutomation(StateChange e) + private void TurnOnByAutomation(IEntityCore entityCore) { if (!IsWorkingHours()) { Logger.LogDebug("Turning on lights by motion sensor {Sensor} is not allowed because it's not working hours", - e.Entity.EntityId); + entityCore.EntityId); return; } if (!Config.Conditions.All(c => c.IsTrue())) { Logger.LogDebug("Not all conditions are met to turn on lights by motion sensor {Sensor}", - e.Entity.EntityId); + entityCore.EntityId); return; } - Logger.LogDebug("Turning on lights by motion sensor {Sensor}", e.Entity.EntityId); + Logger.LogDebug("Turning on lights by motion sensor {Sensor}", entityCore.EntityId); switch (Config.NightMode) { case { IsEnabled: true, IsWorkingHours: true }: diff --git a/src/Core/RoomManager/Room.cs b/src/Core/RoomManager/Room.cs index f937e18..6ff3a79 100644 --- a/src/Core/RoomManager/Room.cs +++ b/src/Core/RoomManager/Room.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using NetDaemon.HassModel; +using NetDaemon.HassModel.Integration; using NetEntityAutomation.Core.Automations; using NetEntityAutomation.Core.Configs; @@ -20,6 +21,23 @@ public Room(IRoomConfig roomConfig, IHaContext haContext) _roomConfig.Logger.LogDebug("Creating room {RoomName}", roomConfig.Name); _haContext = haContext; InitAutomations(); + InitServices(); + } + + private void InitServices() + { + var serviceName = _roomConfig.Name.ToLower().Replace(' ', '_') + "_service"; + _haContext.RegisterServiceCallBack("toggle_" + serviceName, data => + { + _automations.ForEach(auto => + { + auto.IsEnabled = !auto.IsEnabled; + _roomConfig.Logger.LogDebug("Toggling automation {AutomationName} to {IsEnabled}", + auto.GetType().Name, + auto.IsEnabled ? "enabled" : "disabled"); + }); + }); + _roomConfig.Logger.LogDebug("Service {ServiceName} initialised", serviceName); } private void InitAutomations() @@ -43,7 +61,10 @@ private void InitAutomations() break; } } - _roomConfig.Logger.LogDebug("Number of automations: {AutomationCount}", _automations.Count); } +} + +internal record RoomData +{ } \ No newline at end of file diff --git a/src/Core/RoomManager/RoomManager.csproj b/src/Core/RoomManager/RoomManager.csproj index a5216b9..c6ab9bd 100644 --- a/src/Core/RoomManager/RoomManager.csproj +++ b/src/Core/RoomManager/RoomManager.csproj @@ -21,7 +21,7 @@ - +