• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

net-daemon / netdaemon / 13114973870

03 Feb 2025 01:49PM UTC coverage: 83.55% (-0.08%) from 83.631%
13114973870

Pull #1247

github

web-flow
Merge be37a441e into afb86edc6
Pull Request #1247: Feature/state changes with current

843 of 1139 branches covered (74.01%)

Branch coverage included in aggregate %.

4 of 12 new or added lines in 1 file covered. (33.33%)

3327 of 3852 relevant lines covered (86.37%)

622.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.43
/src/HassModel/NetDeamon.HassModel/Entities/EntityExtensions.cs
1
namespace NetDaemon.HassModel.Entities;
2

3
/// <summary>
4
/// Provides Extension methods for Entities
5
/// </summary>
6
public static class EntityExtensions
7
{
8
    /// <summary>
9
    /// Checks if an EntityState has the state "on"
10
    /// </summary>
11
    /// <param name="entityState">The state to check</param>
12
    /// <returns>true if the state equals "on", otherwise false</returns>
13
    public static bool IsOn([NotNullWhen(true)] this EntityState? entityState) => string.Equals(entityState?.State, "on", StringComparison.OrdinalIgnoreCase);
24!
14

15
    /// <summary>
16
    /// Checks if an EntityState has the state "off"
17
    /// </summary>
18
    /// <param name="entityState">The state to check</param>
19
    /// <returns>true if the state equals "off", otherwise false</returns>
20
    public static bool IsOff([NotNullWhen(true)] this EntityState? entityState) => string.Equals(entityState?.State, "off", StringComparison.OrdinalIgnoreCase);
12!
21

22
    /// <summary>
23
    /// Checks if an Entity has the state "on"
24
    /// </summary>
25
    /// <param name="entity">The state to check</param>
26
    /// <returns>true if the state equals "on", otherwise false</returns>
27
    public static bool IsOn([NotNullWhen(true)] this Entity? entity) => entity?.EntityState?.IsOn() ?? false;
6!
28

29
    /// <summary>
30
    /// Checks if an Entity has the state "off"
31
    /// </summary>
32
    /// <param name="entity">The state to check</param>
33
    /// <returns>true if the state equals "off", otherwise false</returns>
34
    public static bool IsOff([NotNullWhen(true)] this Entity? entity) => entity?.EntityState?.IsOff() ?? false;
6!
35

36
    /// <summary>Gets a NumericEntity from a given Entity</summary>
37
    public static NumericEntity AsNumeric(this Entity entity) => new(entity);
2✔
38

39
    /// <summary>Gets a NumericEntity from a given Entity</summary>
40
    public static NumericEntity<TAttributes>
41
        AsNumeric<TEntity, TEntityState, TAttributes>(this Entity<TEntity, TEntityState, TAttributes> entity)
42
        where TEntity : Entity<TEntity, TEntityState, TAttributes>
43
        where TEntityState : EntityState<TAttributes>
44
        where TAttributes : class
45
        => new(entity);
2✔
46

47
    /// <summary>Gets a new Entity from this Entity with the specified type of attributes</summary>
48
    public static Entity<TAttributes> WithAttributesAs<TAttributes>(this Entity entity)
49
        where TAttributes : class
50
        => new(entity);
1✔
51

52
    /// <summary>Gets a new Entity from this Entity with the specified type of attributes</summary>
53
    public static NumericEntity<TAttributes> WithAttributesAs<TAttributes>(this NumericEntity entity)
54
        where TAttributes : class
55
        => new (entity);
2✔
56

57
    /// <summary>
58
    /// Observable that emits all state changes, including attribute changes. It will also emit the current state upon subscribing.<br/>
59
    /// Use <see cref="System.ObservableExtensions.Subscribe{T}(System.IObservable{T})"/> to subscribe to the returned observable and receive initial state and state changes.
60
    /// </summary>
61
    public static IObservable<StateChange> StateAllChangesWithCurrent(this Entity entity)
62
    {
NEW
63
        ArgumentNullException.ThrowIfNull(entity);
×
64

NEW
65
        return entity.StateAllChanges().WithCurrent(entity);
×
66
    }
67

68
    /// <summary>
69
    /// Observable that emits all state changes, including attribute changes. It will also emit the current state upon subscribing.<br/>
70
    /// Use <see cref="System.ObservableExtensions.Subscribe{T}(System.IObservable{T})"/> to subscribe to the returned observable and receive initial state and state changes.
71
    /// </summary>
72
    /// <example>
73
    /// <code>
74
    /// bedroomLight.StateAllChangesWithCurrent()
75
    ///     .Where(s =&gt; s.Old?.Attributes?.Brightness &lt; 128
76
    ///              &amp;&amp; s.New?.Attributes?.Brightness &gt;= 128)
77
    ///     .Subscribe(e =&gt; HandleBrightnessOverHalf());
78
    /// </code>
79
    /// </example>
80
    public static IObservable<StateChange<TEntity, TEntityState>>
81
        StateAllChangesWithCurrent<TEntity, TEntityState, TAttributes>(
82
            this Entity<TEntity, TEntityState, TAttributes> entity)
83
        where TEntity : Entity<TEntity, TEntityState, TAttributes>
84
        where TEntityState : EntityState<TAttributes>
85
        where TAttributes : class
86
    {
NEW
87
        ArgumentNullException.ThrowIfNull(entity);
×
88

NEW
89
        return entity.StateAllChanges().WithCurrent(entity);
×
90
    }
91

92
    /// <summary>
93
    /// Observable that emits state changes where New.State != Old.State. It will also emit the current state upon subscribing.<br/>
94
    /// Use <see cref="System.ObservableExtensions.Subscribe{T}(System.IObservable{T})"/> to subscribe to the returned observable and receive initial state and state changes.
95
    /// </summary>
96
    public static IObservable<StateChange> StateChangesWithCurrent(this Entity entity)
97
    {
NEW
98
        ArgumentNullException.ThrowIfNull(entity);
×
99

NEW
100
        return entity.StateAllChangesWithCurrent().StateChangesOnly();
×
101
    }
102

103
    /// <summary>
104
    /// Observable that emits state changes where New.State != Old.State. It will also emit the current state upon subscribing.<br/>
105
    /// Use <see cref="System.ObservableExtensions.Subscribe{T}(System.IObservable{T})"/> to subscribe to the returned observable and receive initial state and state changes.
106
    /// </summary>
107
    /// <example>
108
    /// <code>
109
    /// disabledLight.StateChangesWithCurrent()
110
    ///    .Where(s =&gt; s.New?.State == "on")
111
    ///    .Subscribe(e =&gt; e.Entity.TurnOff());
112
    /// </code>
113
    /// </example>
114
    public static IObservable<StateChange<TEntity, TEntityState>>
115
        StateChangesWithCurrent<TEntity, TEntityState, TAttributes>(
116
            this Entity<TEntity, TEntityState, TAttributes> entity)
117
        where TEntity : Entity<TEntity, TEntityState, TAttributes>
118
        where TEntityState : EntityState<TAttributes>
119
        where TAttributes : class
120
    {
NEW
121
        ArgumentNullException.ThrowIfNull(entity);
×
122

NEW
123
        return entity.StateAllChangesWithCurrent().StateChangesOnly();
×
124
    }
125

126
    internal static IObservable<StateChange>
127
        WithCurrent(
128
            this IObservable<StateChange> source, Entity entity)
129
        => Observable.Defer(() => source
11✔
130
            .Prepend(new StateChange(entity, null, entity.EntityState)));
11✔
131

132
    internal static IObservable<StateChange<TEntity, TEntityState>>
133
        WithCurrent<TEntity, TEntityState, TAttributes>(
134
            this IObservable<StateChange<TEntity, TEntityState>> source, Entity<TEntity, TEntityState, TAttributes> entity)
135
        where TEntity : Entity<TEntity, TEntityState, TAttributes>
136
        where TEntityState : EntityState<TAttributes>
137
        where TAttributes : class
138
        => Observable.Defer(() => source
11✔
139
            .Prepend(new StateChange<TEntity, TEntityState>((TEntity)entity, null, entity.EntityState)));
11✔
140

141
    internal static IObservable<T> StateChangesOnly<T>(this IObservable<T> changes) where T : StateChange
142
        => changes.Where(c => c.New?.State != c.Old?.State);
76✔
143

144
    /// <summary>
145
    /// Calls a service using this entity as the target
146
    /// </summary>
147
    /// <param name="entity">The Entity to call the service for</param>
148
    /// <param name="service">Name of the service to call. If the Domain of the service is the same as the domain of the Entity it can be omitted</param>
149
    /// <param name="data">Data to provide</param>
150
    public static void CallService(this IEntityCore entity, string service, object? data = null)
151
    {
152
        ArgumentNullException.ThrowIfNull(entity);
1✔
153
        ArgumentNullException.ThrowIfNull(service);
1✔
154

155
        var (serviceDomain, serviceName) = service.SplitAtDot();
1✔
156

157
        serviceDomain ??= entity.EntityId.SplitAtDot().Left ?? throw new InvalidOperationException("EntityId must be formatted 'domain.name'");
1!
158

159
        entity.HaContext.CallService(serviceDomain, serviceName, ServiceTarget.FromEntity(entity.EntityId), data);
1✔
160
    }
1✔
161

162
    /// <summary>
163
    /// Calls a service using this entity as the target
164
    /// </summary>
165
    /// <param name="entity">The Entity to call the service for</param>
166
    /// <param name="service">Name of the service to call. If the Domain of the service is the same as the domain of the Entity it can be omitted</param>
167
    /// <param name="data">Data to provide</param>
168
    public static Task<JsonElement?> CallServiceWithResponseAsync(this IEntityCore entity, string service, object? data = null)
169
    {
170
        ArgumentNullException.ThrowIfNull(entity);
1✔
171
        ArgumentNullException.ThrowIfNull(service);
1✔
172

173
        var (serviceDomain, serviceName) = service.SplitAtDot();
1✔
174

175
        serviceDomain ??= entity.EntityId.SplitAtDot().Left ?? throw new InvalidOperationException("EntityId must be formatted 'domain.name'");
1!
176

177
        return entity.HaContext.CallServiceWithResponseAsync(serviceDomain, serviceName, ServiceTarget.FromEntity(entity.EntityId), data);
1✔
178
    }
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc