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

net-daemon / netdaemon / 13448975254

19 Feb 2025 08:05PM UTC coverage: 84.037% (+0.3%) from 83.694%
13448975254

push

github

web-flow
Use MSBuild to load projects, instead of manual parsing (#1266)

For users (like me) who use a property to specify all their netdaemon versions, instead of individual versions, the existing checks are too simple. I've updated it to instead use msbuild to actually load the project file and determine the real version that nuget will see.

843 of 1125 branches covered (74.93%)

Branch coverage included in aggregate %.

5 of 6 new or added lines in 2 files covered. (83.33%)

5 existing lines in 1 file now uncovered.

3337 of 3849 relevant lines covered (86.7%)

417.35 hits per line

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

85.11
/src/Client/NetDaemon.HassClient/Internal/HomeAssistantRunner.cs
1
using NetDaemon.Client.Exceptions;
2

3
namespace NetDaemon.Client.Internal;
4

5
internal class HomeAssistantRunner(IHomeAssistantClient client,
16✔
6
    ILogger<IHomeAssistantRunner> logger) : IHomeAssistantRunner
16✔
7
{
8
    // The internal token source will make sure we
9
    // always cancel operations on dispose
10
    private readonly CancellationTokenSource _internalTokenSource = new();
16✔
11
    private readonly Subject<IHomeAssistantConnection> _onConnectSubject = new();
16✔
12

13
    private readonly Subject<DisconnectReason> _onDisconnectSubject = new();
16✔
14

15
    private readonly TimeSpan MaxTimeoutInSeconds = TimeSpan.FromSeconds(80);
16✔
16

17
    private Task? _runTask;
18

19
    public IObservable<IHomeAssistantConnection> OnConnect => _onConnectSubject;
9✔
20
    public IObservable<DisconnectReason> OnDisconnect => _onDisconnectSubject;
13✔
21
    public IHomeAssistantConnection? CurrentConnection { get; internal set; }
236✔
22

23
    public Task RunAsync(string host, int port, bool ssl, string token, TimeSpan timeout,
24
        CancellationToken cancelToken)
25
    {
26
        return RunAsync(host, port, ssl, token, HomeAssistantSettings.DefaultWebSocketPath, timeout, cancelToken);
×
27
    }
28
    public Task RunAsync(string host, int port, bool ssl, string token, string websocketPath, TimeSpan timeout, CancellationToken cancelToken)
29
    {
30
        _runTask = InternalRunAsync(host, port, ssl, token, websocketPath, timeout, cancelToken);
15✔
31
        return _runTask;
15✔
32
    }
33

34
    private bool _isDisposed;
35
    public async ValueTask DisposeAsync()
36
    {
37
        if (_isDisposed)
7!
38
            return;
×
39
        await _internalTokenSource.CancelAsync();
7✔
40

41
        if (_runTask?.IsCompleted == false)
7!
42
        {
43
            try
44
            {
UNCOV
45
                await Task.WhenAny(
×
UNCOV
46
                    _runTask,
×
UNCOV
47
                    Task.Delay(5000)
×
UNCOV
48
                ).ConfigureAwait(false);
×
UNCOV
49
            }
×
50
            catch
×
51
            {
52
                // Ignore errors
53
            }
×
54
        }
55

56
        _onConnectSubject.Dispose();
7✔
57
        _onDisconnectSubject.Dispose();
7✔
58
        _internalTokenSource.Dispose();
7✔
59
        _isDisposed = true;
7✔
60
    }
7✔
61

62
    private async Task InternalRunAsync(string host, int port, bool ssl, string token, string websocketPath, TimeSpan timeout,
63
        CancellationToken cancelToken)
64
    {
65
        var progressiveTimeout = new ProgressiveTimeout(timeout, MaxTimeoutInSeconds, 2.0);
15✔
66
        var combinedToken = CancellationTokenSource.CreateLinkedTokenSource(_internalTokenSource.Token, cancelToken);
15✔
67
        while (!combinedToken.IsCancellationRequested)
15!
68
        {
69
            try
70
            {
71
                CurrentConnection = await client.ConnectAsync(host, port, ssl, token, websocketPath, combinedToken.Token)
15✔
72
                    .ConfigureAwait(false);
15✔
73
                // We successfully connected so lets reset the progressiveTimeout
74
                progressiveTimeout.Reset();
11✔
75

76
                // Start the event processing before publish the connection
77
                var eventsTask = CurrentConnection.WaitForConnectionToCloseAsync(combinedToken.Token);
11✔
78
                _onConnectSubject.OnNext(CurrentConnection);
11✔
79
                await eventsTask.ConfigureAwait(false);
11✔
80
            }
×
81
            catch (HomeAssistantConnectionException de) when (de.Reason == DisconnectReason.Unauthorized)
2✔
82
            {
83
                logger.LogError("User token unauthorized! Will not retry connecting...");
1✔
84
                await DisposeConnectionAsync();
1✔
85
                _onDisconnectSubject.OnNext(DisconnectReason.Unauthorized);
1✔
86
                return;
1✔
87
            }
88
            catch (HomeAssistantConnectionException de) when (de.Reason == DisconnectReason.NotReady)
1✔
89
            {
90
                logger.LogInformation("Home Assistant is not ready yet!");
1✔
91
                await DisposeConnectionAsync();
1✔
92
                // We have an connection but waiting for Home Assistant to be ready so lets reset the progressiveTimeout
93
                progressiveTimeout.Reset();
1✔
94
                _onDisconnectSubject.OnNext(DisconnectReason.NotReady);
1✔
95
            }
1✔
96
            catch (OperationCanceledException)
12✔
97
            {
98
                await DisposeConnectionAsync();
12✔
99
                if (_internalTokenSource.IsCancellationRequested)
12✔
100
                {
101
                    // We have internal cancellation due to dispose
102
                    // just return without any further due
103
                    return;
1✔
104
                }
105

106
                _onDisconnectSubject.OnNext(cancelToken.IsCancellationRequested
11✔
107
                    ? DisconnectReason.Client
11✔
108
                    : DisconnectReason.Remote);
11✔
109
            }
11✔
110
            catch (Exception e)
1✔
111
            {
112
                // In most cases this is just normal when client fails to connect so we log as debug
113
                logger.LogDebug(e, "Unhandled exception connecting to Home Assistant!");
1✔
114
                await DisposeConnectionAsync();
1✔
115
                _onDisconnectSubject.OnNext(DisconnectReason.Error);
1✔
116
            }
117

118
            await DisposeConnectionAsync();
13✔
119

120
            if (combinedToken.IsCancellationRequested)
13✔
121
                return; // If we are cancelled we should not retry
2✔
122

123
            var waitTimeout = progressiveTimeout.Timeout;
11✔
124
            logger.LogInformation("Client connection failed, retrying in {Seconds} seconds...", waitTimeout.TotalSeconds);
11✔
125
            await Task.Delay(waitTimeout, combinedToken.Token).ConfigureAwait(false);
11✔
126
        }
127
    }
4✔
128

129
    private async Task DisposeConnectionAsync()
130
    {
131
        if (CurrentConnection is not null)
28✔
132
        {
133
            // Just try to dispose the connection silently
134
            try
135
            {
136
                await CurrentConnection.DisposeAsync().ConfigureAwait(false);
11✔
137
            }
11✔
138
            finally
139
            {
140
                CurrentConnection = null;
11✔
141
            }
142
        }
143
    }
28✔
144
}
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