mirror of
https://github.com/ckaczor/HomeMonitor.git
synced 2026-01-13 17:22:54 -05:00
Add alerts when devices reconnect after stopping
This commit is contained in:
@@ -193,4 +193,5 @@
|
|||||||
</TypePattern>
|
</TypePattern>
|
||||||
</Patterns></s:String>
|
</Patterns></s:String>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kaczor/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mqtt/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=mqtt/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -100,12 +100,23 @@ public class Database(IConfiguration configuration)
|
|||||||
return await connection.QueryFirstOrDefaultAsync<Device>(query, new { Name = deviceName }).ConfigureAwait(false);
|
return await connection.QueryFirstOrDefaultAsync<Device>(query, new { Name = deviceName }).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetDeviceLastUpdatedAsync(string deviceName, DateTimeOffset? lastUpdated)
|
public async Task<bool> SetDeviceLastUpdatedAsync(string deviceName, DateTimeOffset? lastUpdated)
|
||||||
{
|
{
|
||||||
await using var connection = CreateConnection();
|
await using var connection = CreateConnection();
|
||||||
|
|
||||||
var query = ResourceReader.GetString("ChrisKaczor.HomeMonitor.Environment.Service.Data.Queries.SetDeviceLastUpdated.psql");
|
var query = ResourceReader.GetString("ChrisKaczor.HomeMonitor.Environment.Service.Data.Queries.SetDeviceLastUpdated.psql");
|
||||||
|
|
||||||
await connection.ExecuteAsync(query, new { Name = deviceName, LastUpdated = lastUpdated }).ConfigureAwait(false);
|
var stoppedReporting = await connection.QueryFirstAsync<bool>(query, new { Name = deviceName, LastUpdated = lastUpdated }).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return stoppedReporting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetDeviceStoppedReportingAsync(string deviceName, bool stoppedReporting)
|
||||||
|
{
|
||||||
|
await using var connection = CreateConnection();
|
||||||
|
|
||||||
|
var query = ResourceReader.GetString("ChrisKaczor.HomeMonitor.Environment.Service.Data.Queries.SetDeviceStoppedReporting.psql");
|
||||||
|
|
||||||
|
await connection.ExecuteAsync(query, new { Name = deviceName, StoppedReporting = stoppedReporting }).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
INSERT INTO device(
|
INSERT INTO device(
|
||||||
name,
|
name,
|
||||||
last_updated
|
last_updated,
|
||||||
|
stopped_reporting
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
@Name,
|
@Name,
|
||||||
@LastUpdated
|
@LastUpdated,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
ON CONFLICT (name)
|
ON CONFLICT (name)
|
||||||
DO UPDATE
|
DO UPDATE
|
||||||
SET last_updated = EXCLUDED.last_updated
|
SET last_updated = EXCLUDED.last_updated,
|
||||||
|
stopped_reporting = false
|
||||||
|
RETURNING
|
||||||
|
(SELECT stopped_reporting FROM device WHERE name = @Name);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
UPDATE
|
||||||
|
device
|
||||||
|
SET
|
||||||
|
stopped_reporting = @StoppedReporting
|
||||||
|
WHERE
|
||||||
|
name = @Name;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
device
|
||||||
|
ADD COLUMN
|
||||||
|
stopped_reporting boolean NOT NULL DEFAULT false;
|
||||||
@@ -1,26 +1,21 @@
|
|||||||
using ChrisKaczor.HomeMonitor.Environment.Service.Data;
|
using ChrisKaczor.HomeMonitor.Environment.Service.Data;
|
||||||
using RestSharp;
|
|
||||||
|
|
||||||
namespace ChrisKaczor.HomeMonitor.Environment.Service;
|
namespace ChrisKaczor.HomeMonitor.Environment.Service;
|
||||||
|
|
||||||
public class DeviceCheckService(Database _database, IConfiguration _configuration) : IHostedService
|
public class DeviceCheckService(Database database, IConfiguration configuration, TelegramSender telegramSender) : IHostedService
|
||||||
{
|
{
|
||||||
private Timer? _timer;
|
private Timer? _timer;
|
||||||
private TimeSpan _warningInterval;
|
private TimeSpan _warningInterval;
|
||||||
|
|
||||||
private readonly string _botToken = _configuration["Telegram:BotToken"]!;
|
|
||||||
private readonly string _chatId = _configuration["Telegram:PersonalChatId"]!;
|
|
||||||
private readonly RestClient _restClient = new();
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
WriteLog("DeviceCheckService started");
|
WriteLog("DeviceCheckService started");
|
||||||
|
|
||||||
_warningInterval = TimeSpan.Parse(_configuration["Environment:DeviceWarningInterval"]!);
|
_warningInterval = TimeSpan.Parse(configuration["Environment:DeviceWarningInterval"]!);
|
||||||
|
|
||||||
var checkInterval = TimeSpan.Parse(_configuration["Environment:DeviceCheckInterval"]!);
|
var checkInterval = TimeSpan.Parse(configuration["Environment:DeviceCheckInterval"]!);
|
||||||
|
|
||||||
_timer = new Timer((state) => DoWork().Wait(), null, TimeSpan.Zero, checkInterval);
|
_timer = new Timer(_ => DoWork().Wait(cancellationToken), null, TimeSpan.Zero, checkInterval);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -29,7 +24,7 @@ public class DeviceCheckService(Database _database, IConfiguration _configuratio
|
|||||||
{
|
{
|
||||||
WriteLog("Checking devices started");
|
WriteLog("Checking devices started");
|
||||||
|
|
||||||
var devices = await _database.GetDevicesAsync();
|
var devices = await database.GetDevicesAsync();
|
||||||
|
|
||||||
foreach (var device in devices)
|
foreach (var device in devices)
|
||||||
{
|
{
|
||||||
@@ -50,11 +45,9 @@ public class DeviceCheckService(Database _database, IConfiguration _configuratio
|
|||||||
|
|
||||||
if (message.Length > 0)
|
if (message.Length > 0)
|
||||||
{
|
{
|
||||||
var encodedMessage = Uri.EscapeDataString(message);
|
await database.SetDeviceStoppedReportingAsync(device.Name, true);
|
||||||
|
|
||||||
var restRequest = new RestRequest($"https://api.telegram.org/bot{_botToken}/sendMessage?chat_id={_chatId}&text={encodedMessage}");
|
await telegramSender.SendMessageAsync(message);
|
||||||
|
|
||||||
await _restClient.GetAsync(restRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ public class MessageHandler : IHostedService
|
|||||||
private readonly MqttFactory _mqttFactory;
|
private readonly MqttFactory _mqttFactory;
|
||||||
private readonly string _topic;
|
private readonly string _topic;
|
||||||
private readonly HubConnection? _hubConnection;
|
private readonly HubConnection? _hubConnection;
|
||||||
|
private readonly TelegramSender _telegramSender;
|
||||||
|
|
||||||
public MessageHandler(IConfiguration configuration, Database database)
|
public MessageHandler(IConfiguration configuration, Database database, TelegramSender telegramSender)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_database = database;
|
_database = database;
|
||||||
|
_telegramSender = telegramSender;
|
||||||
|
|
||||||
_topic = _configuration["Mqtt:Topic"] ?? string.Empty;
|
_topic = _configuration["Mqtt:Topic"] ?? string.Empty;
|
||||||
|
|
||||||
@@ -77,9 +79,16 @@ public class MessageHandler : IHostedService
|
|||||||
|
|
||||||
await _database.StoreMessageAsync(message);
|
await _database.StoreMessageAsync(message);
|
||||||
|
|
||||||
await _database.SetDeviceLastUpdatedAsync(message.Name, message.Timestamp);
|
var hadStoppedReporting = await _database.SetDeviceLastUpdatedAsync(message.Name, message.Timestamp);
|
||||||
|
|
||||||
await SendMessage(message);
|
await SendMessage(message);
|
||||||
|
|
||||||
|
if (hadStoppedReporting)
|
||||||
|
{
|
||||||
|
var telegramMessage = $"Device now reporting: {message.Name}";
|
||||||
|
|
||||||
|
await _telegramSender.SendMessageAsync(telegramMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SendMessage(DeviceMessage message)
|
private async Task SendMessage(DeviceMessage message)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public static class Program
|
|||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
builder.Services.AddTransient<Database>();
|
builder.Services.AddTransient<Database>();
|
||||||
|
builder.Services.AddTransient<TelegramSender>();
|
||||||
builder.Services.AddHostedService<MessageHandler>();
|
builder.Services.AddHostedService<MessageHandler>();
|
||||||
builder.Services.AddHostedService<DeviceCheckService>();
|
builder.Services.AddHostedService<DeviceCheckService>();
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,10 @@
|
|||||||
<EmbeddedResource Include="Data\Queries\CreateReading.psql" />
|
<EmbeddedResource Include="Data\Queries\CreateReading.psql" />
|
||||||
<EmbeddedResource Include="Data\Queries\GetDevices.psql" />
|
<EmbeddedResource Include="Data\Queries\GetDevices.psql" />
|
||||||
<EmbeddedResource Include="Data\Queries\GetDevice.psql" />
|
<EmbeddedResource Include="Data\Queries\GetDevice.psql" />
|
||||||
|
<EmbeddedResource Include="Data\Queries\SetDeviceStoppedReporting.psql" />
|
||||||
<EmbeddedResource Include="Data\Queries\SetDeviceLastUpdated.psql" />
|
<EmbeddedResource Include="Data\Queries\SetDeviceLastUpdated.psql" />
|
||||||
<EmbeddedResource Include="Data\Schema\1-Initial Schema.psql" />
|
<EmbeddedResource Include="Data\Schema\1-Initial Schema.psql" />
|
||||||
|
<EmbeddedResource Include="Data\Schema\3-Device Table - StoppedReporting.psql" />
|
||||||
<EmbeddedResource Include="Data\Schema\2-Device Table.psql" />
|
<EmbeddedResource Include="Data\Schema\2-Device Table.psql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@
|
|||||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.1" />
|
||||||
<PackageReference Include="MQTTnet.AspNetCore" Version="4.3.3.952" />
|
<PackageReference Include="MQTTnet.AspNetCore" Version="4.3.3.952" />
|
||||||
<PackageReference Include="RestSharp" Version="111.1.0" />
|
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
19
Environment/Service/TelegramSender.cs
Normal file
19
Environment/Service/TelegramSender.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace ChrisKaczor.HomeMonitor.Environment.Service;
|
||||||
|
|
||||||
|
public class TelegramSender(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
private readonly string _botToken = configuration["Telegram:BotToken"]!;
|
||||||
|
private readonly string _chatId = configuration["Telegram:PersonalChatId"]!;
|
||||||
|
private readonly RestClient _restClient = new();
|
||||||
|
|
||||||
|
public async Task SendMessageAsync(string message)
|
||||||
|
{
|
||||||
|
var encodedMessage = Uri.EscapeDataString(message);
|
||||||
|
|
||||||
|
var restRequest = new RestRequest($"https://api.telegram.org/bot{_botToken}/sendMessage?chat_id={_chatId}&text={encodedMessage}");
|
||||||
|
|
||||||
|
await _restClient.GetAsync(restRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user