From 50d52793175053c303df560e481dd5f2ac90ac94 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 05:32:16 +0100 Subject: [PATCH 01/15] chore: initial --- .gitignore | 7 ++++ docker-compose.yml | 4 ++ dockerfiles/power_control.Dockerfile | 24 +++++++++++ microservices/power_control/Program.cs | 41 +++++++++++++++++++ .../power_control/power_control.csproj | 13 ++++++ 5 files changed, 89 insertions(+) create mode 100644 dockerfiles/power_control.Dockerfile create mode 100644 microservices/power_control/Program.cs create mode 100644 microservices/power_control/power_control.csproj diff --git a/.gitignore b/.gitignore index 0f5f168..883f4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,10 @@ configuration-production.yaml node_modules/ config/ + +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ + +[Oo]bj/ diff --git a/docker-compose.yml b/docker-compose.yml index 6cd6cd7..344cd34 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -132,6 +132,10 @@ services: restart: unless-stopped volumes: - ${REPO_DIR}/configurations/otel/collector-config.yaml:/etc/otelcol-contrib/config.yaml + power_control: + build: + context: . + dockerfile: ./dockerfiles/power_control.Dockerfile # Only enable if external services present # ports: # - 4317:4317 # OTLP gRPC receiver diff --git a/dockerfiles/power_control.Dockerfile b/dockerfiles/power_control.Dockerfile new file mode 100644 index 0000000..bd3323b --- /dev/null +++ b/dockerfiles/power_control.Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/aspnet:10.0.101-noble-arm64v8 AS base +USER $APP_UID + +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:10.0.101-noble-arm64v8 AS build +COPY ./microservices/power_control/*.csproj /microservices/power_control/ + +WORKDIR /microservices/power_control +RUN dotnet restore + +WORKDIR / +COPY ./microservices/power_control /microservices/power_control + +WORKDIR /microservices/power_control +# This stage is used to publish the service project to be copied to the final stage +FROM build AS publish +ARG BUILD_CONFIGURATION +RUN dotnet publish "./power_control.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "power_control.dll"] diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs new file mode 100644 index 0000000..ee9d65d --- /dev/null +++ b/microservices/power_control/Program.cs @@ -0,0 +1,41 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast"); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/microservices/power_control/power_control.csproj b/microservices/power_control/power_control.csproj new file mode 100644 index 0000000..f5064e1 --- /dev/null +++ b/microservices/power_control/power_control.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + From 2dae2f232b7749ed45b7486d97a740adb33eefc7 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 05:34:18 +0100 Subject: [PATCH 02/15] chore: updated arm image --- dockerfiles/power_control.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/power_control.Dockerfile b/dockerfiles/power_control.Dockerfile index bd3323b..8001f1c 100644 --- a/dockerfiles/power_control.Dockerfile +++ b/dockerfiles/power_control.Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/aspnet:10.0.101-noble-arm64v8 AS base +FROM mcr.microsoft.com/dotnet/aspnet:10.0.1-noble-arm64v8 AS base USER $APP_UID EXPOSE 8080 From 23207c9b4badd62a24b233c24bfd5dddd4c787bf Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 05:47:14 +0100 Subject: [PATCH 03/15] chore: upgrade to .net10 --- microservices/power_control/Program.cs | 54 +++++++------------ .../power_control/power_control.csproj | 2 +- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index ee9d65d..e8e1e7f 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -1,41 +1,27 @@ -var builder = WebApplication.CreateBuilder(args); +using Microsoft.AspNetCore.Mvc; -// Add services to the container. -// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi -builder.Services.AddOpenApi(); +var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.MapOpenApi(); -} +app.MapGet( + "/high_power_alert", + () => + { + Console.WriteLine("high power alert was received."); + return new OkResult(); + } + ) + .WithName("HighPowerAlert"); -app.UseHttpsRedirection(); - -var summaries = new[] -{ - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" -}; - -app.MapGet("/weatherforecast", () => -{ - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecast - ( - DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - Random.Shared.Next(-20, 55), - summaries[Random.Shared.Next(summaries.Length)] - )) - .ToArray(); - return forecast; -}) -.WithName("GetWeatherForecast"); +app.MapGet( + "/low_power_alert", + () => + { + Console.WriteLine("low power alert was received."); + return new OkResult(); + } + ) + .WithName("Low Power Alert"); app.Run(); - -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) -{ - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} diff --git a/microservices/power_control/power_control.csproj b/microservices/power_control/power_control.csproj index f5064e1..63a6d4d 100644 --- a/microservices/power_control/power_control.csproj +++ b/microservices/power_control/power_control.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 enable enable From 16b515de2d6afa4b5a135f8eb95d76f00e70fbc3 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 05:56:07 +0100 Subject: [PATCH 04/15] chore: added Posts --- docker-compose.yml | 6 +++--- microservices/power_control/Program.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 344cd34..9c76557 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -132,10 +132,10 @@ services: restart: unless-stopped volumes: - ${REPO_DIR}/configurations/otel/collector-config.yaml:/etc/otelcol-contrib/config.yaml +# Only enable if external services present +# ports: +# - 4317:4317 # OTLP gRPC receiver power_control: build: context: . dockerfile: ./dockerfiles/power_control.Dockerfile -# Only enable if external services present -# ports: -# - 4317:4317 # OTLP gRPC receiver diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index e8e1e7f..f4d7771 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -4,17 +4,18 @@ var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); -app.MapGet( +app.MapPost( "/high_power_alert", - () => + (object body) => { Console.WriteLine("high power alert was received."); + Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(body)); return new OkResult(); } ) .WithName("HighPowerAlert"); -app.MapGet( +app.MapPost( "/low_power_alert", () => { From de6558ec7ee4926837f245a1e19c577ed6b4186c Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:06:46 +0100 Subject: [PATCH 05/15] chore: updated docker --- docker-compose.yml | 1 + microservices/power_control/Program.cs | 10 +++- .../power_control/example_body_firing.json | 51 ++++++++++++++++++ .../power_control/example_body_resolved.json | 53 +++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 microservices/power_control/example_body_firing.json create mode 100644 microservices/power_control/example_body_resolved.json diff --git a/docker-compose.yml b/docker-compose.yml index 9c76557..6c4b282 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -139,3 +139,4 @@ services: build: context: . dockerfile: ./dockerfiles/power_control.Dockerfile + network_mode: "host" diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index f4d7771..5cab238 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -6,10 +6,13 @@ var app = builder.Build(); app.MapPost( "/high_power_alert", - (object body) => + async (AlertBody body) => { Console.WriteLine("high power alert was received."); Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(body)); + + using var client = new HttpClient(); + await client.GetAsync("http://192.168.1.121/control?cmd=heatpump&set_power_mode=off"); return new OkResult(); } ) @@ -26,3 +29,8 @@ app.MapPost( .WithName("Low Power Alert"); app.Run(); + +record AlertBody +{ + string status; +} diff --git a/microservices/power_control/example_body_firing.json b/microservices/power_control/example_body_firing.json new file mode 100644 index 0000000..8d3fe01 --- /dev/null +++ b/microservices/power_control/example_body_firing.json @@ -0,0 +1,51 @@ +{ + "receiver": "REST API", + "status": "firing", + "alerts": [ + { + "status": "firing", + "labels": { + "alertname": "Alert if peak is too high", + "domain": "sensor", + "entity_id": "thienpont_serruys_load_grid", + "grafana_folder": "Power" + }, + "annotations": { + "description": "Disable some devices to lower the peak.", + "summary": "Peak is too high!" + }, + "startsAt": "2025-12-29T04:58:50Z", + "endsAt": "0001-01-01T00:00:00Z", + "generatorURL": "http://localhost:3000/alerting/grafana/ef6m5j923ruv4f/view?orgId=1", + "fingerprint": "2c3f0f9ffb7d8c5d", + "silenceURL": "http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=__alert_rule_uid__%3Def6m5j923ruv4f\u0026matcher=domain%3Dsensor\u0026matcher=entity_id%3Dthienpont_serruys_load_grid\u0026orgId=1", + "dashboardURL": "http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984335061", + "panelURL": "http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984335061\u0026viewPanel=3", + "values": { "B": 4015.25, "C": 1 }, + "valueString": "[ var=\u0027B\u0027 labels={domain=sensor, entity_id=thienpont_serruys_load_grid} value=4015.25 ], [ var=\u0027C\u0027 labels={domain=sensor, entity_id=thienpont_serruys_load_grid} value=1 ]", + "orgId": 1 + } + ], + "groupLabels": { + "alertname": "Alert if peak is too high", + "grafana_folder": "Power" + }, + "commonLabels": { + "alertname": "Alert if peak is too high", + "domain": "sensor", + "entity_id": "thienpont_serruys_load_grid", + "grafana_folder": "Power" + }, + "commonAnnotations": { + "description": "Disable some devices to lower the peak.", + "summary": "Peak is too high!" + }, + "externalURL": "http://localhost:3000/", + "version": "1", + "groupKey": "{}/{__grafana_autogenerated__=\u0022true\u0022}/{__grafana_receiver__=\u0022REST API\u0022}:{alertname=\u0022Alert if peak is too high\u0022, grafana_folder=\u0022Power\u0022}", + "truncatedAlerts": 0, + "orgId": 1, + "title": "[FIRING:1] Alert if peak is too high Power (sensor thienpont_serruys_load_grid)", + "state": "alerting", + "message": "**Firing**\n\nValue: B=4015.25, C=1\nLabels:\n - alertname = Alert if peak is too high\n - domain = sensor\n - entity_id = thienpont_serruys_load_grid\n - grafana_folder = Power\nAnnotations:\n - description = Disable some devices to lower the peak.\n - summary = Peak is too high!\nSource: http://localhost:3000/alerting/grafana/ef6m5j923ruv4f/view?orgId=1\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=__alert_rule_uid__%3Def6m5j923ruv4f\u0026matcher=domain%3Dsensor\u0026matcher=entity_id%3Dthienpont_serruys_load_grid\u0026orgId=1\nDashboard: http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984335061\nPanel: http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984335061\u0026viewPanel=3\n" +} diff --git a/microservices/power_control/example_body_resolved.json b/microservices/power_control/example_body_resolved.json new file mode 100644 index 0000000..866b6c5 --- /dev/null +++ b/microservices/power_control/example_body_resolved.json @@ -0,0 +1,53 @@ +{ + "receiver": "REST API", + "status": "resolved", + "alerts": [ + { + "status": "resolved", + "labels": { + "alertname": "Alert if peak is too high", + "domain": "sensor", + "entity_id": "thienpont_serruys_load_grid", + "grafana_folder": "Power" + }, + "annotations": { + "description": "Disable some devices to lower the peak.", + "grafana_state_reason": "Updated", + "summary": "Peak is too high!" + }, + "startsAt": "2025-12-29T04:58:50Z", + "endsAt": "2025-12-29T04:59:25.018228513Z", + "generatorURL": "http://localhost:3000/alerting/grafana/ef6m5j923ruv4f/view?orgId=1", + "fingerprint": "2c3f0f9ffb7d8c5d", + "silenceURL": "http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=__alert_rule_uid__%3Def6m5j923ruv4f\u0026matcher=domain%3Dsensor\u0026matcher=entity_id%3Dthienpont_serruys_load_grid\u0026orgId=1", + "dashboardURL": "http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984365018", + "panelURL": "http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984365018\u0026viewPanel=3", + "values": null, + "valueString": "[ var=\u0027B\u0027 labels={domain=sensor, entity_id=thienpont_serruys_load_grid} value=3826.9 ], [ var=\u0027C\u0027 labels={domain=sensor, entity_id=thienpont_serruys_load_grid} value=1 ]", + "orgId": 1 + } + ], + "groupLabels": { + "alertname": "Alert if peak is too high", + "grafana_folder": "Power" + }, + "commonLabels": { + "alertname": "Alert if peak is too high", + "domain": "sensor", + "entity_id": "thienpont_serruys_load_grid", + "grafana_folder": "Power" + }, + "commonAnnotations": { + "description": "Disable some devices to lower the peak.", + "grafana_state_reason": "Updated", + "summary": "Peak is too high!" + }, + "externalURL": "http://localhost:3000/", + "version": "1", + "groupKey": "{}/{__grafana_autogenerated__=\u0022true\u0022}/{__grafana_receiver__=\u0022REST API\u0022}:{alertname=\u0022Alert if peak is too high\u0022, grafana_folder=\u0022Power\u0022}", + "truncatedAlerts": 0, + "orgId": 1, + "title": "[RESOLVED] Alert if peak is too high Power (sensor thienpont_serruys_load_grid)", + "state": "ok", + "message": "**Resolved**\n\nValue: [no value]\nLabels:\n - alertname = Alert if peak is too high\n - domain = sensor\n - entity_id = thienpont_serruys_load_grid\n - grafana_folder = Power\nAnnotations:\n - description = Disable some devices to lower the peak.\n - grafana_state_reason = Updated\n - summary = Peak is too high!\nSource: http://localhost:3000/alerting/grafana/ef6m5j923ruv4f/view?orgId=1\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=__alert_rule_uid__%3Def6m5j923ruv4f\u0026matcher=domain%3Dsensor\u0026matcher=entity_id%3Dthienpont_serruys_load_grid\u0026orgId=1\nDashboard: http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984365018\nPanel: http://localhost:3000/d/2990096a-3ff9-4217-a629-2152a260b6b6?from=1766980730000\u0026orgId=1\u0026to=1766984365018\u0026viewPanel=3\n" +} From 99b6663a278d57ec87aedaf25ca4f775e7528ab7 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:12:26 +0100 Subject: [PATCH 06/15] chore: remove network mode --- .gitignore | 2 ++ docker-compose.yml | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 883f4ef..51e5839 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ config/ [Rr]eleases/ [Oo]bj/ +.aider* +.env diff --git a/docker-compose.yml b/docker-compose.yml index 6c4b282..9c76557 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -139,4 +139,3 @@ services: build: context: . dockerfile: ./dockerfiles/power_control.Dockerfile - network_mode: "host" From 742da36a10514bfb16f0deac01a6541f8d677b94 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:14:46 +0100 Subject: [PATCH 07/15] chore: fix power_control --- microservices/power_control/Program.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index 5cab238..0ed7b0e 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -2,6 +2,12 @@ using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); +// Add JSON serialization services +builder.Services.ConfigureHttpJsonOptions(options => +{ + options.SerializerOptions.PropertyNameCaseInsensitive = true; +}); + var app = builder.Build(); app.MapPost( @@ -30,7 +36,4 @@ app.MapPost( app.Run(); -record AlertBody -{ - string status; -} +record AlertBody(string Status); From 32a5f7102fa5366e95ae045b82234e644fe29d66 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:21:58 +0100 Subject: [PATCH 08/15] chore: first working version --- microservices/power_control/Program.cs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index 0ed7b0e..e4906cf 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -15,10 +15,26 @@ app.MapPost( async (AlertBody body) => { Console.WriteLine("high power alert was received."); - Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(body)); - using var client = new HttpClient(); - await client.GetAsync("http://192.168.1.121/control?cmd=heatpump&set_power_mode=off"); + using var client = new HttpClient(); + switch (body.Status) + { + case "firing": + await client.GetAsync( + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" + ); + Console.WriteLine("Peak too high. Shutting down heat pump."); + break; + case "resolved": + await client.GetAsync( + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" + ); + Console.WriteLine("Peak acceptable. Re enabling heat pump."); + break; + default: + Console.WriteLine("Unknown body type"); + break; + } return new OkResult(); } ) From fc6bdab674c7a306596350125510c8d4dfc5f881 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:28:30 +0100 Subject: [PATCH 09/15] chore: fix rule --- microservices/power_control/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index e4906cf..ace1cb7 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -21,13 +21,13 @@ app.MapPost( { case "firing": await client.GetAsync( - "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" ); Console.WriteLine("Peak too high. Shutting down heat pump."); break; case "resolved": await client.GetAsync( - "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" ); Console.WriteLine("Peak acceptable. Re enabling heat pump."); break; From a2057255d46544f4ba8c4d8aca5bae03daed3dd3 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:45:42 +0100 Subject: [PATCH 10/15] chore: send message to discord --- microservices/power_control/Program.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index ace1cb7..609abad 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -20,17 +20,25 @@ app.MapPost( switch (body.Status) { case "firing": + Console.WriteLine("Peak too high. Shutting down heat pump."); await client.GetAsync( "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" ); - Console.WriteLine("Peak too high. Shutting down heat pump."); + await client.PostAsJsonAsync( + "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", + new DiscordMessage("Peak too high. Shutting down heat pump.", "willemBot") + ); break; case "resolved": + Console.WriteLine("Peak acceptable. Re enabling heat pump."); await client.GetAsync( "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" ); - Console.WriteLine("Peak acceptable. Re enabling heat pump."); - break; + await client.PostAsJsonAsync( + "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", + new DiscordMessage("Peak is acceptable. Re enabling heat pump.", "willemBot") + ); + break; default: Console.WriteLine("Unknown body type"); break; @@ -53,3 +61,5 @@ app.MapPost( app.Run(); record AlertBody(string Status); + +record DiscordMessage(string content, string userName); From 6f0b60f56b27cde00528ca843b53379e2e42f09c Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 06:47:58 +0100 Subject: [PATCH 11/15] chore: added emojis --- microservices/power_control/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index 609abad..bf4e7bc 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -26,17 +26,17 @@ app.MapPost( ); await client.PostAsJsonAsync( "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", - new DiscordMessage("Peak too high. Shutting down heat pump.", "willemBot") + new DiscordMessage("Peak too high. Shutting down heat pump. 🥶", "willemBot") ); break; case "resolved": - Console.WriteLine("Peak acceptable. Re enabling heat pump."); + Console.WriteLine("Peak acceptable. Re enabling heat pump. "); await client.GetAsync( "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" ); await client.PostAsJsonAsync( "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", - new DiscordMessage("Peak is acceptable. Re enabling heat pump.", "willemBot") + new DiscordMessage("Peak is acceptable. Re enabling heat pump. 🌶️", "willemBot") ); break; default: From 3cb7e0cac1c5bd91f7837a83d03e6fae4d5e91a4 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 07:12:27 +0100 Subject: [PATCH 12/15] chore: fix --- microservices/power_control/Program.cs | 37 +++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index bf4e7bc..edfd949 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -17,26 +17,43 @@ app.MapPost( Console.WriteLine("high power alert was received."); using var client = new HttpClient(); + var heatPumpState = await client.GetFromJsonAsync("http://192.168.1.121/control"); + Console.WriteLine(heatPumpState.power); switch (body.Status) { case "firing": Console.WriteLine("Peak too high. Shutting down heat pump."); - await client.GetAsync( - "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" - ); + var message = "@everyone peak is too high. Disabling heat pump. 🥶"; + if (heatPumpState.power == "on") + { + await client.GetAsync( + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" + ); + } + else + { + message = "@everyone heat pump could not be disabled because state was not 'ON'"; + } await client.PostAsJsonAsync( "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", - new DiscordMessage("Peak too high. Shutting down heat pump. 🥶", "willemBot") + new DiscordMessage(message, "willemBot") ); break; case "resolved": - Console.WriteLine("Peak acceptable. Re enabling heat pump. "); - await client.GetAsync( - "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" - ); + Console.WriteLine("Peak acceptable. Re enabling heat pump. 🌶️"); + var message = "Re enabled heat pump."; + if (heatPumpState.power == "off") + { + await client.GetAsync( + "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" + ); + } + else { + message = "@everyone Heat pump could not be re enabled because state was not 'ON'"; + } await client.PostAsJsonAsync( "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", - new DiscordMessage("Peak is acceptable. Re enabling heat pump. 🌶️", "willemBot") + new DiscordMessage(message, "willemBot") ); break; default: @@ -63,3 +80,5 @@ app.Run(); record AlertBody(string Status); record DiscordMessage(string content, string userName); + +record HeatPumpStatus(string power); From d467824b719703a4736340177494e6acf51e4092 Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 07:14:02 +0100 Subject: [PATCH 13/15] fix --- microservices/power_control/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index edfd949..780f11c 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -18,12 +18,13 @@ app.MapPost( using var client = new HttpClient(); var heatPumpState = await client.GetFromJsonAsync("http://192.168.1.121/control"); + var message = ""; Console.WriteLine(heatPumpState.power); switch (body.Status) { case "firing": Console.WriteLine("Peak too high. Shutting down heat pump."); - var message = "@everyone peak is too high. Disabling heat pump. 🥶"; + message = "@everyone peak is too high. Disabling heat pump. 🥶"; if (heatPumpState.power == "on") { await client.GetAsync( @@ -41,7 +42,7 @@ app.MapPost( break; case "resolved": Console.WriteLine("Peak acceptable. Re enabling heat pump. 🌶️"); - var message = "Re enabled heat pump."; + message = "Re enabled heat pump."; if (heatPumpState.power == "off") { await client.GetAsync( From 2bde52ed7fbb11b9e654cdb3cdf93cc0a6d0566d Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 07:15:55 +0100 Subject: [PATCH 14/15] fix message --- microservices/power_control/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index 780f11c..16465ea 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -33,7 +33,7 @@ app.MapPost( } else { - message = "@everyone heat pump could not be disabled because state was not 'ON'"; + message = "@everyone high power alert, but heat pump could not be disabled because state was not 'ON'"; } await client.PostAsJsonAsync( "https://discord.com/api/webhooks/1455072237207158981/C9qvSIGMZVc60VwpZizxqKigyzvA182RDSdt9k8qtWTq-fBgzlJHh53wAYIqYkGNkBM3", From 319ef1d5986e87233b3439792ea602adf802687f Mon Sep 17 00:00:00 2001 From: "willem.serruys" Date: Mon, 29 Dec 2025 07:19:34 +0100 Subject: [PATCH 15/15] fixes --- microservices/power_control/Program.cs | 11 +- .../example_heat_pump_status.json | 140 ++++++++++++++++++ 2 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 microservices/power_control/example_heat_pump_status.json diff --git a/microservices/power_control/Program.cs b/microservices/power_control/Program.cs index 16465ea..53b2c75 100644 --- a/microservices/power_control/Program.cs +++ b/microservices/power_control/Program.cs @@ -15,17 +15,16 @@ app.MapPost( async (AlertBody body) => { Console.WriteLine("high power alert was received."); - using var client = new HttpClient(); var heatPumpState = await client.GetFromJsonAsync("http://192.168.1.121/control"); var message = ""; - Console.WriteLine(heatPumpState.power); + Console.WriteLine(heatPumpState.heatpump.power); switch (body.Status) { case "firing": Console.WriteLine("Peak too high. Shutting down heat pump."); message = "@everyone peak is too high. Disabling heat pump. 🥶"; - if (heatPumpState.power == "on") + if (heatPumpState.heatpump.power == "on") { await client.GetAsync( "http://192.168.1.121/control?cmd=heatpump&set_power_mode=off" @@ -43,7 +42,7 @@ app.MapPost( case "resolved": Console.WriteLine("Peak acceptable. Re enabling heat pump. 🌶️"); message = "Re enabled heat pump."; - if (heatPumpState.power == "off") + if (heatPumpState.heatpump.power == "off") { await client.GetAsync( "http://192.168.1.121/control?cmd=heatpump&set_power_mode=on" @@ -82,4 +81,6 @@ record AlertBody(string Status); record DiscordMessage(string content, string userName); -record HeatPumpStatus(string power); +record HeatPumpStatus(HeatPumpStatusBody heatpump); + +record HeatPumpStatusBody(string power); diff --git a/microservices/power_control/example_heat_pump_status.json b/microservices/power_control/example_heat_pump_status.json new file mode 100644 index 0000000..2c8eefa --- /dev/null +++ b/microservices/power_control/example_heat_pump_status.json @@ -0,0 +1,140 @@ +{ + "fw": { + "curr": "3.1.7", + "last": "3.1.7", + "model": "ME_CN105_ATW_WIFI", + "upd": true + }, + "heatpump": { + "boiler_flow_temperature": 25.0, + "boiler_return_temperature": 25.0, + "booster_active": false, + "compressor_frequency": 0, + "compressor_on": false, + "controller_datetime": "2025-12-29 07:02:35", + "controller_version": 3, + "defrost_active": false, + "dhw": { + "flow_target_temperature": 50.0, + "flow_temperature_drop": 10.0, + "forced_active": "off", + "legionella_prevention_target_temp": 60.0, + "mode": "off", + "temperature": 51.5, + "temperature_secondary": 25.0 + }, + "dipswitch": { + "sw1": "10110110", + "sw2": "10101000", + "sw3": "00010000", + "sw4": "00000000", + "sw5": "00111110", + "sw6": "00000000" + }, + "energy": { + "consumed_cooling": 0.0, + "consumed_dhw": 3.6, + "consumed_heating": 72.2, + "delivered_cooling": 0.0, + "delivered_dhw": 8.6, + "delivered_heating": 204.1 + }, + "energy_consumed_increasing": 128.1, + "fault_code": "No error", + "feed_temperature": 38.5, + "flowrate": 26, + "heat_source": 0, + "holiday_mode": "off", + "immersion_active": false, + "in1_thermostat_request": false, + "in5_thermostat_request": false, + "in6_thermostat_request": false, + "indicative_output_power": 0.0, + "max_flow_temperature": 40.0, + "min_flow_temperature": 25.0, + "mixing_tank_temperature": 25.0, + "mixing_valve_status": 4, + "mixing_valve_step": 0, + "mrc_flag": 7, + "multizone_status": 0, + "operation": "heat_on", + "output_power": 5, + "outside_temperature": 0.0, + "power": "on", + "prohibit": { + "cooling_zone1": "off", + "cooling_zone2": "off", + "dhw": "off", + "heating_zone1": "off", + "heating_zone2": "off", + "server_control": "on" + }, + "rcdischargeTemp": 0.0, + "rcdischargesuperheattemp": 0.0, + "rcoucompressorsurfacetemp": 0.0, + "rcouheatsinktemp": 0.0, + "rcouliquidpipetemp": 0.0, + "rcousuctionpipetemp": 0.0, + "rcoutwoPhasepipetemp": 0.0, + "rcsubcooltemp": 0.0, + "refrigerant_condensing_temperature": 40.6, + "refrigerant_liquid_temperature": 29.5, + "return_temperature": 35.5, + "runtime": 8847.0, + "threeway_valve2_active": true, + "threeway_valve_active": false, + "waterpump2_active": true, + "waterpump3_active": false, + "waterpump_active": true, + "zone1": { + "feed_temperature": 36.0, + "flow_target_temperature": 46.5, + "mode": "HEAT_COMPENSATION_CURVE", + "return_temperature": 32.0, + "room_target_temperature": 18.0, + "room_temperature": 18.0 + }, + "zone2": { + "feed_temperature": 25.0, + "flow_target_temperature": 35.0, + "mode": "HEAT_FLOW_TEMP", + "return_temperature": 25.0, + "room_target_temperature": 635.4, + "room_temperature": 0.0 + } + }, + "http": { + "host": "heatpump" + }, + "mqtt": { + "broker": "mqtt://192.168.1.81", + "con": true, + "ena": true, + "ha_auto_disc": true, + "port": 1883, + "pub_sys_data": true, + "sts": "OK", + "user": "admin" + }, + "sys": { + "bl": "V0.5", + "conext": true, + "conhp": true, + "dbg": false, + "frname": "Living room", + "hw": "ME_V4.1", + "id": "273526330095088", + "lang": "ENG", + "name": "HVAC_4FC5F8", + "scale": "C", + "sts": 3, + "stsled": true, + "up": 1799393 + }, + "wifi": { + "ip": "192.168.1.121", + "mac": "F0:F5:BD:4F:C5:F8", + "rssi": -33, + "ssid": "Pladijs124" + } +}