-
Notifications
You must be signed in to change notification settings - Fork 0
State Extension Methods
By default, all entity states are sent to your automations of type HaEntityState
which derives from HaEntityState<string, JsonElement>
. This means that if you need to get a non-string object from the state, or a value from the JSON attributes, you need to take extra steps to get the values. Fortunately, HaKafkaNet has several extension methods to make working with states easier, and IHaEntityProvider
also has several for retrieving typed states from Home Assistant directly.
When working with an HaEntityState
use these methods for retrieving strongly typed values.
public static T? GetState<T>(this HaEntityState state)
public static T? GetStateEnum<T>(this HaEntityState state) where T: struct, Enum
public static T? GetAttributes<T>(this HaEntityState state)
public static T? GetFromAttributes<T>(this HaEntityState state, string key)
The GetState<T>
and GetStateEnum<T>
methods will return you the State
property parsed into type you specify. If parsing fails, null is returned.
The GetAttributes<T>
method will return you the entire Attributes
object parsed into a type of your choosing. You can use one of the built-in types or create your own.
The GetFromAttributes<T>
method is for those times you have a an entity from an integration not natively supported and you want to retrieve a single value from the attributes without needing to create a model to parse all the attributes.
For a myriad of reasons, entities go off-line or become unresponsive. The Bad()
method is for quickly identifying those scenarios. The following Boolean assignments are functionally equivalent.
var entity = await _services.EntityProvider.GetEntity("domain.id");
bool badEasy = entity.Bad();
bool badHard =
entity is null ||
entity.State is null ||
entity.State.Equals("unknown", StringComparison.OrdinalIgnoreCase) ||
entity.State.Equals("unavailable", StringComparison.OrdinalIgnoreCase);
State changes passed to your automations are of type HaEntityStateChange
which contains old and new states for an entity where those states are of type HaEntityState
which have string State
and JsonElement Attributes
properties. The methods described below will convert the HaEntityStateChange
to a type containing more usefule types for the State
and Attribues
properties.
The first three methods allow you to convert to any types you like. Use these methods if none of the other built-in methods fit your needs.
public static HaEntityStateChange<HaEntityState<Tstate,JsonElement>> ToStateTyped<Tstate>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<string,Tatt>> ToAttributeTyped<Tatt>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<Tstate,Tatt>> ToTyped<Tstate, Tatt>(this HaEntityStateChange change)
- If you only care about the
State
property and don't need information from the attributes, use theToStateTyped<Tstate>
method. - If you only care about the
Attributes
and not the state, use theToAttributeTyped<Tatt>
method. - If you wish to convert both the
State
property and theAttributes
properties, us theToTyped<Tstate, Tatt>
method.
Fortunately, there are other extension methods to handle most scenarios. Below will detail several categories of method for you to choose from.
Many entities will have their State
property set to a specific subset of values. HaKafkaNet has several built in. They include OnOff
, BatteryState
, and SunState
. For sun states, there is a singular method to use which combines the state with a type for sun attributes. The others have both a generic and non-generic method. Use the generic method when you want to also change the Attributes
property type.
public static HaEntityStateChange<HaEntityState<OnOff, JsonElement>> ToOnOff(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<OnOff, T>> ToOnOff<T>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<BatteryState, JsonElement>> ToBatteryState(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<BatteryState, T>> ToBatteryState<T>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<SunState, SunAttributes>> ToSun(this HaEntityStateChange change)
The ToOnOff
method is suitable for contact and presence sensors, switches, helper toggle entities, and more. The following statements are semantically identical.
if (stateChange.New.State.Equals("on", StringComparison.OrdinalIgnoreCase))
{ /* do work */}
var onOffState = stateChange.ToOnOff();
if (onOffState.New.State == OnOff.On)
{ /* do work */}
The OnOff
and BatteryState
enumerations also have values representing "unknown" and "unavailable" for those times when an entity goes offline.
Several types of sensors expose a value type in the state property. This includes illumination sensors, power meters, and more.
public static HaEntityStateChange<HaEntityState<int?, JsonElement>> ToIntTyped(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<int?, T>> ToIntTyped<T>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<double?, JsonElement>> ToDoubleTyped(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<double?, T>> ToDoubleTyped<T>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<float?, JsonElement>> ToFloatTyped(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<float?, T>> ToFloatTyped<T>(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<DateTime?, JsonElement>> ToDateTimeTyped(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<DateTime?, T>> ToDateTimeTyped<T>(this HaEntityStateChange change)
Similar to the enumeration methods, these have both a non-generic and generic method. The generic version is for converting the attributes. As an example, the following two if statements are semantically identical:
if (int.TryParse(stateChange.New.State, out int stateValue) && stateValue > myThreshold)
{ /* do work */}
var intState = stateChange.ToIntTyped();
if (intState.New.State > myThreshold)
{ /* do work */}
There are three more methods for working with lights and scene controllers.
public static HaEntityStateChange<HaEntityState<OnOff, LightModel>> ToLight(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<OnOff, ColorLightModel>> ToColorLight(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<DateTime?, SceneControllerEvent>> ToSceneControllerEvent(this HaEntityStateChange change)
For clarity only, the following 2 lines are functionally equivalent.
var lightState1 = stateChange.ToTyped<OnOff, LightModel>();
var lightState2 = stateChange.ToLight()
The method for scene controllers is particularly useful. The following example is for demonstration purposes to show possibilities. It assumes that your automation is listening to all the button entities of a scene controller. For brevity, error handling is omitted. See Scene Controllers for more information.
public Task Execute(HaEntityStateChange stateChange, CancellationToken cancellationToken)
{
var sceneState = stateChange.ToSceneControllerEvent();
var key = sceneState.New.Attributes?.GetKeyPress();
var btn = stateChange.EntityId.Last();
return (btn,key) switch
{
{btn: '1', key: KeyPress.KeyPressed } => _services.Api.Toggle("light.light_1"),
{btn: '1', key: KeyPress.KeyHeldDown } => _services.Api.LightTurnOn(new LightTurnOnModel(){EntityId = ["lightlight_1"], BrightnessStepPct = 10}),
{btn: '1', key: KeyPress.KeyPressed2x } => _services.Api.LightTurnOn(new LightTurnOnModel(){EntityId = ["lightlight_1"], BrightnessStepPct = -25}),
{btn: '2', key: KeyPress.KeyPressed } => _services.Api.Toggle("light.light_2"),
{btn: '2', key: KeyPress.KeyHeldDown } => _services.Api.LightTurnOn(new LightTurnOnModel(){EntityId = ["lightlight_2"], BrightnessStepPct = 10}),
{btn: '2', key: KeyPress.KeyPressed2x } => _services.Api.LightTurnOn(new LightTurnOnModel(){EntityId = ["lightlight_2"], BrightnessStepPct = -25}),
_ => Task.CompletedTask // unassigned
};
}
There are three methods to help with types related to geolocation
public static HaEntityStateChange<HaEntityState<string, DeviceTrackerModel>> ToDeviceTracker(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<string, PersonModel>> ToPerson(this HaEntityStateChange change)
public static HaEntityStateChange<HaEntityState<int, ZoneModel>> ToZone(this HaEntityStateChange change)
There is one method for working with calendars.
public static HaEntityStateChange<HaEntityState<OnOff, CalendarModel>> ToCalendar(this HaEntityStateChange change)
Caution: When working with scheduled automations and calendars, be sure to set IsReschedulable
to true. See Durable Automations and Schedulable automations for more details.
If you have native home assistant automations, and you want to run an automation based on changes to that automation, you can add automation
as a domain in your kafka integraion filter, and trigger HaKafkaNet automations from them.
public static HaEntityStateChange<HaEntityState<OnOff, HaAutomationModel>> ToHaAutomation(this HaEntityStateChange change)