Skip to content

Commit 99c5e06

Browse files
Add SmartHomeMonitorWatcherTask (#59)
* Add SmartHomeMonitorWatcherTask * Improve SmartHomeMonitorWatcherTask: CancellationToken propagation, sync, and logging (#60) * Initial plan * Apply PR review feedback: CancellationToken, Unknown state, semaphore, logging improvements Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> Agent-Logs-Url: https://github.com/thomasneuberger/TgHomeBot/sessions/02041d09-0799-491d-ac3a-ccd36acdea63 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 81cfd08 commit 99c5e06

6 files changed

Lines changed: 93 additions & 2 deletions

File tree

TgHomeBot.Scheduling/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ The task generates a summary of EV charging sessions from the last two months, g
172172
- Formatted in German with month names
173173
- Sent to all registered Telegram chats (respects the MonthlyChargingReport feature flag)
174174

175+
### SmartHomeMonitorWatcherTask
176+
177+
A scheduled task that ensures the Smart Home Monitor is running by periodically checking and restarting it if needed:
178+
179+
Configuration file (`SmartHomeMonitorWatcherTask.json`):
180+
```json
181+
{
182+
"taskType": "SmartHomeMonitorWatcherTask",
183+
"cronExpression": "*/5 * * * *",
184+
"enabled": true
185+
}
186+
```
187+
188+
The task runs every 5 minutes and:
189+
- Checks if the Smart Home Monitor is running
190+
- Automatically restarts the monitor if it has stopped
191+
- Logs the current status of the monitor
192+
- Ensures continuous monitoring of smart home devices for state changes
193+
175194
## Integration
176195

177196
To integrate the scheduler into your application:
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.Extensions.Logging;
2+
using TgHomeBot.SmartHome.Contract;
3+
4+
namespace TgHomeBot.Scheduling.Tasks;
5+
6+
/// <summary>
7+
/// Scheduled task to ensure the Smart Home Monitor is running
8+
/// Periodically checks and restarts the monitor if needed
9+
/// </summary>
10+
public class SmartHomeMonitorWatcherTask(ILogger<SmartHomeMonitorWatcherTask> logger, ISmartHomeConnector smartHomeConnector)
11+
: IScheduledTask
12+
{
13+
public string TaskName => "SmartHomeMonitorWatcherTask";
14+
15+
public async Task ExecuteAsync(CancellationToken cancellationToken)
16+
{
17+
cancellationToken.ThrowIfCancellationRequested();
18+
19+
var isRunning = await smartHomeConnector.EnsureMonitorIsRunning(cancellationToken).ConfigureAwait(false);
20+
21+
if (!isRunning)
22+
{
23+
logger.LogWarning("Smart Home Monitor is not running and could not be started.");
24+
}
25+
else
26+
{
27+
logger.LogDebug("Smart Home Monitor is running.");
28+
}
29+
}
30+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"taskType": "SmartHomeMonitorWatcherTask",
3+
"cronExpression": "*/5 * * * *",
4+
"enabled": true
5+
}

TgHomeBot.SmartHome.Contract/ISmartHomeConnector.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ public interface ISmartHomeConnector
88
Task<IReadOnlyList<SmartDevice>> GetDevices(IReadOnlyList<MonitoredDevice> requestedDevices);
99
Task<ISmartHomeMonitor> CreateMonitorAsync(IReadOnlyList<MonitoredDevice> devices,
1010
CancellationToken cancellationToken);
11+
Task<bool> EnsureMonitorIsRunning(CancellationToken cancellationToken);
1112
}

TgHomeBot.SmartHome.HomeAssistant/HomeAssistantConnector.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88

99
namespace TgHomeBot.SmartHome.HomeAssistant;
1010

11-
internal class HomeAssistantConnector(IOptions<HomeAssistantOptions> options, IHttpClientFactory httpClientFactory, IServiceProvider serviceProvider, ILogger<HomeAssistantMonitor> monitorLogger)
11+
internal class HomeAssistantConnector(
12+
IOptions<HomeAssistantOptions> options,
13+
IHttpClientFactory httpClientFactory,
14+
IServiceProvider serviceProvider,
15+
ILogger<HomeAssistantMonitor> monitorLogger,
16+
ILogger<HomeAssistantConnector> logger)
1217
: ISmartHomeConnector
1318
{
1419
internal const string HttpClientName = "HomeAssistant";
@@ -62,6 +67,37 @@ public async Task<ISmartHomeMonitor> CreateMonitorAsync(IReadOnlyList<MonitoredD
6267
}
6368
}
6469

70+
public async Task<bool> EnsureMonitorIsRunning(CancellationToken cancellationToken)
71+
{
72+
try
73+
{
74+
await _semaphore.WaitAsync(cancellationToken);
75+
try
76+
{
77+
switch (_smartHomeMonitor?.State)
78+
{
79+
case MonitorState.Idle:
80+
case MonitorState.Unknown:
81+
await _smartHomeMonitor!.StartMonitoringAsync(cancellationToken);
82+
return true;
83+
case MonitorState.Listening:
84+
return true;
85+
default:
86+
return false;
87+
}
88+
}
89+
finally
90+
{
91+
_semaphore.Release();
92+
}
93+
}
94+
catch (Exception e)
95+
{
96+
logger.LogError(e, "Error ensuring Home Assistant monitor is running");
97+
return false;
98+
}
99+
}
100+
65101
private async Task<string> CallApi(string endpoint, HttpMethod method)
66102
{
67103
var url = options.Value.BaseUrl.TrimEnd('/') + "/api/" + endpoint;

TgHomeBot.SmartHome.HomeAssistant/HomeAssistantMonitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ private async Task WaitForMessages()
152152
logger.LogError(ex, "Error receiving message from Home Assistant web socket: {Exception}", ex.Message);
153153
}
154154
}
155-
logger.LogWarning("Home Assistant Web socket has been closed.");
155+
logger.LogWarning("Home Assistant Web socket has been closed. Reconnect: {Reconnect}", _reconnect);
156156
if (_reconnect)
157157
{
158158
while (_webSocket is null || _webSocket.State == WebSocketState.Closed)

0 commit comments

Comments
 (0)