-
Notifications
You must be signed in to change notification settings - Fork 0
Automation Types
There are two ways of creating automations. You can either use the IAutomationRegistry
interface or write individual automations. This page covers the later of those two methods. When writing individual automations, simply create a class and implement one of the interface options below.
Currently, there are 3 main automation types: Simple Automations which implement IAutomation
and two types which implement the IDelayableAutomation
interface. Those types implement the IConditionalAutomation
and ISchedulableAutomation
interfaces. All of the above interfaces implement the IAutomationBase
interface:
public interface IAutomationBase
{
EventTiming EventTimings { get => EventTiming.PostStartup; }
IEnumerable<string> TriggerEntityIds();
}
The EventTimings
property tells the framework which events you care about. As you can see, it has a default implementation that only returns events after the application starts. However, at startup, the framework will read all messages from the topic which may or may not be compacted. This means that it will handle the latest entity state for all entities even if your application was not running when the events were created. If the topic has not compacted, it might handle older events. The EventTimings
property will allow you to handle those events if you choose. For example your automation might have a line that looks like this:
public EventTiming EventTimings { get =>
EventTiming.PreStartupSameAsLastCached | EventTiming.PreStartupPostLastCached | EventTiming.PostStartup;
}
This would ensure that your automation would know the current state of an entity even if your application was down when the entity changed state. You may want to use this kind of option if, for example, you restarted your application at sunset, and you want to make sure lights get set appropriately.
See here for an example of how to consume pre-startup events.
All state changes for all entities with an ID that matches any of the ID's returned by this method will cause the Execute
method of your automation to run, assuming the event timing matches as described above.
This is the primary interface used to trigger all calls into your automations. In fact, all automations end up being wrapped in an automation which implements this interface. In addition to the two members above, it exposes the Execute
method.
You can add as many implementations of IAutomation
as you need. All of them will be discovered at startup and will be registered as a singleton into the DI container. If you would like to have multiple instances of your automation, you must decorate your class with the ExcludeFromDiscoveryAttribute
and register them via an IAutomationRegistry
. See Tutorial for a detailed explanation.
public interface IAutomation
{
Task Execute(HaEntityStateChange stateChange, CancellationToken cancellationToken);
}
Assuming the event timing and entity id of the state change matches your automation, the Execute
method will be called with a HaEntityStateChange
model and a CancelationToken
. The state change object has 3 properties: EntityId
, Old
, and New
. The Old and New properties represent the state of an entity at different points in time. The New
property will always represent the current message being handled by the framework, and the Old
will always represent what was previously stored in the cache. There is a possibility that Old
will be null if it wasn't in the cache.
Note: The
stateChange
parameter does not have strongly typed properties for your entities. See State Extension Methods for ways of easily converting to strongly typed states.
Both the IConditionalAutomation
and ISchedulableAutomation
implement the IDelayableAutomation
interface.
public interface IDelayableAutomation : IAutomationBase
{
bool ShouldExecutePastEvents { get => false; }
bool ShouldExecuteOnContinueError { get => false; }
Task<bool> ContinuesToBeTrue(HaEntityStateChange haEntityStateChange, CancellationToken cancellationToken);
Task Execute(CancellationToken cancellationToken);
}
All delayable automations are wrapped in an automation that implements the IAutomation
interface which are in turn wrapped as discussed above. Instead of an Execute
method, a delayable automation responds to state changes through the ContinuesToBeTrue
method.
Internally, HaKafkaNet wraps your IConditionalAutomation
as an IAutomation
. Whereas in the IAutomation
, the Execute
method was called for all state changes, in the IConditionalAutomation
the ContinuesToBeTrue
method is called. If the method ever returns false, the delayed execution is canceled.
When the Execute
method is called, it happens at some amount of time after a state change occurred and there could have been multiple state changes during that time. Therefore, there is no single state change that relates to the execution and no HaEntityStateChange
object is passed in. After this method executes, your automation will be in a state as if ContinuesToBeTrue
had returned false, meaning that the next time ContinuesToBeTrue
returns true, your automation will be scheduled again.
This method does not refer to pre-start up events. Where scheduling is concerned there could be times where the calculated schedule is before DateTime.Now
. This property relates to that. Like in the example above regarding restarting your system at sunset. Perhaps your system was down before sunset, then sunset occurred, then your app started while handling pre-start up events, and your automation says to execute at sunset, which is now in the past. Set this to true if you want to handle that event.
Unlike simple automations that fire immediately upon a state change, delayed automations execute at some scheduled time, and multiple state changes could have triggered. If one succeeds and schedules your automation to run, then another fails, what do you want to do? This property answers that question.
The conditional automation is intended to ensure a state remains constant for a duration of time before it is executed. In addition to the members exposed by IDelayableAutomation
it exposes one more.
public interface IConditionalAutomation
{
TimeSpan For{ get; }
}
You interpret this interface as follows :
If condition ContinuesToBeTrue' for
Foramount of time, then
Execute`.
This represents the amount of time to delay execution after the first time that ContinuesToBeTrue
returns true.
Note, if
ContinuesToBeTrue
always returns true your automation would execute on an interval of theFor
time plus the time between state changes. If you do not want this behavior, it is recommended to recognize the state affected byExecute
.
Note: if
For
returnsTimeSpan.MinValue
the automation will not run; the same as ifContinuesToBeTrue
returned false.
See here for an example of using IConditionalAutomation
Unlike the conditional automation that wants some state to exist for an amount of time before it triggers, the schedulable automation is intended for when information about scheduling exists in the state change itself. For example, when the sun.sun
state changes, it tells you when the next sun rise/set/etc. will occur. You could also use the LastUpdatedDate
property of an entity, which is used by the pre-built durable automations.
In addition to the IDelayableAutomation
members, it exposes two more:
public interface ISchedulableAutomation : IDelayableAutomation
{
bool IsReschedulable { get; }
DateTime? GetNextScheduled();
}
When the sun entity reports state, its sunrise information won't change for roughly 24 hours. So, there is no need to reschedule and this property is false for all the sun based automations. However, if you created an automation based on a calendar, things would be different. Let's say an event on your calendar is scheduled in 4 hours. Your automation then schedules it, then another event is added to the calendar, but it is only 2 hours away. In that case, you would want to reschedule it by setting this to true.
Unlike the conditional automation that uses a TimeSpan
, the schedulable automation uses a DateTime
. Upon a state change, if this automation is not already running or IsReschedulable
is true, this method will be called after ContinuesToBeTrue
is called.
Note: If this method returns null, the automation will not be scheduled; the same as if
ContinuesToBeTrue
returned false.
Scheduling is a complex topic. For more ideas on how you should schedule automations see Scheduling