diff --git a/.gitignore b/.gitignore
index 45407ac..9427922 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
obj
.vs/LibreHardwareMonitorExporter
+bin/Debug/net8.0
diff --git a/LibreHardwareMonitorExporter.csproj b/LibreHardwareMonitorExporter.csproj
index 2150e37..6cf7f06 100644
--- a/LibreHardwareMonitorExporter.csproj
+++ b/LibreHardwareMonitorExporter.csproj
@@ -7,4 +7,8 @@
enable
+
+
+
+
diff --git a/Program.cs b/Program.cs
index 3751555..a2057c7 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,2 +1,143 @@
-// See https://aka.ms/new-console-template for more information
-Console.WriteLine("Hello, World!");
+using System.Net;
+using System.Text;
+using LibreHardwareMonitor.Hardware;
+
+var port = Environment.GetEnvironmentVariable("PORT");
+if (!int.TryParse(port, out var httpPort))
+{
+ httpPort = 9646; // default port
+}
+
+var listener = new HttpListener();
+listener.Prefixes.Add($"http://*:{httpPort}/");
+listener.Start();
+
+var computer = new Computer
+{
+ IsCpuEnabled = true,
+ IsGpuEnabled = true,
+ IsMemoryEnabled = true,
+ IsMotherboardEnabled = true,
+ IsControllerEnabled = true,
+ IsNetworkEnabled = true,
+ IsStorageEnabled = true
+};
+
+computer.Open();
+
+Console.WriteLine($"LibreHardwareMonitorExporter listening on http://localhost:{httpPort}/metrics");
+
+while (true)
+{
+ var context = await listener.GetContextAsync();
+ if (context.Request.HttpMethod != "GET")
+ {
+ context.Response.StatusCode = 405;
+ context.Response.Close();
+ continue;
+ }
+
+ if (context.Request.Url?.AbsolutePath != "/metrics")
+ {
+ context.Response.StatusCode = 404;
+ context.Response.Close();
+ continue;
+ }
+
+ var body = new StringBuilder();
+ AppendMetrics(body, computer);
+ var bytes = Encoding.UTF8.GetBytes(body.ToString());
+ context.Response.StatusCode = 200;
+ context.Response.ContentType = "text/plain; version=0.0.4";
+ context.Response.ContentEncoding = Encoding.UTF8;
+ context.Response.ContentLength64 = bytes.Length;
+ await context.Response.OutputStream.WriteAsync(bytes, 0, bytes.Length);
+ context.Response.Close();
+}
+
+static void AppendMetrics(StringBuilder output, Computer computer)
+{
+ var updateVisitor = new UpdateVisitor();
+ computer.Accept(updateVisitor);
+
+ // Basic example metrics for temperature, load, clock, power, fan
+ foreach (var hardware in computer.Hardware)
+ {
+ EmitHardwareMetrics(output, hardware);
+ foreach (var sub in hardware.SubHardware)
+ {
+ EmitHardwareMetrics(output, sub);
+ }
+ }
+}
+
+static void EmitHardwareMetrics(StringBuilder output, IHardware hardware)
+{
+ var hardwareLabel = SanitizeLabel(hardware.Name);
+ foreach (var sensor in hardware.Sensors)
+ {
+ if (sensor.Value is null) continue;
+ var sensorLabel = SanitizeLabel(sensor.Name);
+ var value = sensor.Value.Value;
+ var metricName = sensor.SensorType switch
+ {
+ SensorType.Temperature => "lhm_temperature_celsius",
+ SensorType.Load => "lhm_load_percent",
+ SensorType.Clock => "lhm_clock_mhz",
+ SensorType.Power => "lhm_power_watts",
+ SensorType.Fan => "lhm_fan_rpm",
+ SensorType.Voltage => "lhm_voltage_volts",
+ SensorType.Data => "lhm_data",
+ SensorType.Level => "lhm_level",
+ SensorType.Throughput => "lhm_throughput_bytes_per_sec",
+ SensorType.Factor => "lhm_factor",
+ SensorType.Flow => "lhm_flow_lph",
+ SensorType.Control => "lhm_control_percent",
+ _ => null
+ };
+
+ if (metricName == null) continue;
+ output.Append(metricName);
+ output.Append("{");
+ output.Append($"hardware=\"{hardwareLabel}\",sensor=\"{sensorLabel}\",type=\"{sensor.SensorType}\"");
+ output.Append("} ");
+ output.AppendLine(value.ToString(System.Globalization.CultureInfo.InvariantCulture));
+ }
+}
+
+static string SanitizeLabel(string raw)
+{
+ var sb = new StringBuilder(raw.Length);
+ foreach (var ch in raw)
+ {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == ':' || ch == '.')
+ {
+ sb.Append(ch);
+ }
+ else
+ {
+ sb.Append('_');
+ }
+ }
+ return sb.ToString();
+}
+
+sealed class UpdateVisitor : IVisitor
+{
+ public void VisitComputer(IComputer computer)
+ {
+ computer.Traverse(this);
+ }
+
+ public void VisitHardware(IHardware hardware)
+ {
+ hardware.Update();
+ foreach (var sub in hardware.SubHardware)
+ {
+ sub.Accept(this);
+ }
+ }
+
+ public void VisitSensor(ISensor sensor) { }
+ public void VisitParameter(IParameter parameter) { }
+}