Skip to content
This repository was archived by the owner on Oct 5, 2025. It is now read-only.

Commit c66350f

Browse files
committed
add mqtt and hwinfo integration
1 parent 8d59773 commit c66350f

File tree

10 files changed

+1101
-6
lines changed

10 files changed

+1101
-6
lines changed

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
# fip-ha
22

3-
Now Playing Display for Logitech Flight Instrument Panel for Home Assistant Media Players
3+
Home Assistant integration with Logitech Flight Instrument Panel, HWINFO, MQTT
44

5+
- Now Playing Display for Home Assistant Media Players
6+
- [HWInfo](https://www.hwinfo.com) integration into Home Assistant, via MQTT
7+
8+
If only the HWINFO integration with Home Assistant is needed, then NO Flight Instrument Panel needs to to be connected.
9+
10+
If only the Now Playing Display is required, then no HWINFO or MQTT server needs to be set-up or running (in that case, remove mqtt.config).
11+
12+
HWINFO Sensor Entities will be AUTOMATICALLY added to Home Assistant via the MQTT Discovery process.
13+
14+
# Now Playing
515

616
![Screenshot 1](https://i.imgur.com/UNOTXH2.jpeg)
717

@@ -32,6 +42,42 @@ Also players that should be excluded can be defined
3242
</appSettings>
3343
```
3444

45+
# HWINFO
46+
47+
When HWInfo64 is detected, ALL the available sensors will be written at startup to the data\hwinfo.json file.
48+
49+
The HWINFO.inc file must be modified, to configure what will be sent to MQTT.
50+
The HWINFO.inc file has the same format as used by various [rainmeter](https://www.deviantart.com/pul53dr1v3r/art/Rainformer-2-9-3-HWiNFO-Edition-Rainmeter-789616481) skins.
51+
52+
Note that you don't need to install rainmeter or any rainmeter plugin.
53+
54+
A configuration tool, to link sensor ids to variables in the HWINFO.inc file, can be downloaded from the hwinfo website [here](https://www.hwinfo.com/beta/HWiNFOSharedMemoryViewer.exe.7z) :
55+
56+
![hwinfo tool](https://i.imgur.com/Px6jvw4.png)
57+
58+
The HWINFO sensor data can be sent to an MQTT server that is configured in mqtt.config (this file can be deleted if MQTT is not required)
59+
60+
```
61+
<?xml version="1.0" encoding="utf-8" ?>
62+
<configuration>
63+
<mqtt>
64+
<add key="mqttURI" value="192.168.2.34" />
65+
<add key="mqttUser" value="mqttusername" />
66+
<add key="mqttPassword" value="secretpassword" />
67+
<add key="mqttPort" value="1883" />
68+
<add key="mqttSecure" value="False" />
69+
</mqtt>
70+
</configuration>
71+
```
72+
73+
![MQTT1](https://i.imgur.com/KackkpM.png)
74+
75+
![MQTT2](https://i.imgur.com/p5S3FWw.png)
76+
77+
![MQTT3](https://i.imgur.com/AJBazTy.png)
78+
79+
![MQTT4](https://i.imgur.com/tkaNJDd.png)
80+
3581
Works with these 64 bit Logitech Flight Instrument Panel Drivers (currently not with older saitek drivers) :
3682

3783
https://support.logi.com/hc/en-us/articles/360024848713--Downloads-Flight-Instrument-Panel

fipha/App.xaml.cs

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
using SharpDX.DirectInput;
1818
using System.Collections.Specialized;
1919
using System.Linq;
20+
using System.Text.RegularExpressions;
2021
using System.Windows.Documents;
2122
using HADotNet.Core;
2223
using HADotNet.Core.Clients;
2324
using HADotNet.Core.Models;
25+
using Newtonsoft.Json;
26+
using Newtonsoft.Json.Serialization;
2427

2528

2629
// ReSharper disable StringLiteralTypo
@@ -49,6 +52,9 @@ public partial class App : Application
4952
public static Task HaTask;
5053
private static CancellationTokenSource _haTokenSource = new CancellationTokenSource();
5154

55+
public static Task HWInfoTask;
56+
private static CancellationTokenSource _hwInfoTokenSource = new CancellationTokenSource();
57+
5258
private static Mutex _mutex;
5359

5460
private TaskbarIcon _notifyIcon;
@@ -80,8 +86,7 @@ private static void RunProcess(string fileName)
8086
process.Start();
8187
process.WaitForExit();
8288
}
83-
84-
89+
8590
protected override void OnStartup(StartupEventArgs evtArgs)
8691
{
8792
const string appName = "fipha";
@@ -162,7 +167,16 @@ protected override void OnStartup(StartupEventArgs evtArgs)
162167
{
163168
Log.Info($"{mediaPlayer}");
164169
}
165-
170+
171+
splashScreen.Dispatcher.Invoke(() => splashScreen.ProgressText.Text = "Getting sensor data from HWInfo...");
172+
173+
HWInfo.ReadMem("HWINFO.INC");
174+
175+
if (HWInfo.SensorData.Any())
176+
{
177+
HWInfo.SaveDataToFile(@"Data\hwinfo.json");
178+
}
179+
166180
Dispatcher.Invoke(() =>
167181
{
168182
var window = Current.MainWindow = new MainWindow();
@@ -235,6 +249,79 @@ protected override void OnStartup(StartupEventArgs evtArgs)
235249

236250
}, haToken);
237251

252+
var hwInfoToken = _hwInfoTokenSource.Token;
253+
254+
if (File.Exists(Path.Combine(App.ExePath, "mqtt.config")))
255+
{
256+
257+
HWInfoTask = Task.Run(async () =>
258+
{
259+
var result = await MQTT.Connect();
260+
261+
Log.Info("HWInfo task started");
262+
263+
if (HWInfo.SensorData.Any())
264+
{
265+
266+
foreach (var sensor in HWInfo.SensorData)
267+
{
268+
foreach (var element in sensor.Value.Elements)
269+
{
270+
var mqttValue = JsonConvert.SerializeObject(new HWInfo.MQTTDiscoveryObj
271+
{
272+
device_class = element.Value.DeviceClass,
273+
name = element.Value.Name,
274+
state_topic =
275+
$"homeassistant/{element.Value.Component}/{element.Value.Node}/state",
276+
unit_of_measurement = element.Value.Unit,
277+
value_template = "{{ value_json.value}}",
278+
unique_id = element.Value.Node,
279+
state_class = "measurement"
280+
}, new JsonSerializerSettings
281+
{
282+
NullValueHandling = NullValueHandling.Ignore
283+
});
284+
285+
var task = Task.Run<bool>(async () =>
286+
await MQTT.Publish(
287+
$"homeassistant/{element.Value.Component}/{element.Value.Node}/config",
288+
mqttValue));
289+
290+
}
291+
}
292+
293+
while (true)
294+
{
295+
if (hwInfoToken.IsCancellationRequested)
296+
{
297+
hwInfoToken.ThrowIfCancellationRequested();
298+
}
299+
300+
HWInfo.ReadMem("HWINFO.INC");
301+
302+
foreach (var sensor in HWInfo.SensorData)
303+
{
304+
foreach (var element in sensor.Value.Elements)
305+
{
306+
var mqttValue = JsonConvert.SerializeObject(new HWInfo.MQTTStateObj
307+
{
308+
value = element.Value.NumericValue
309+
});
310+
311+
var task = Task.Run<bool>(async () => await MQTT.Publish($"homeassistant/{element.Value.Component}/{element.Value.Node}/state", mqttValue));
312+
}
313+
314+
}
315+
316+
//!!!FipHandler.RefreshHWInfoPages();
317+
318+
await Task.Delay(5 * 1000, _hwInfoTokenSource.Token); // repeat every 5 seconds
319+
}
320+
}
321+
322+
}, hwInfoToken);
323+
}
324+
238325
});
239326

240327
}
@@ -264,6 +351,24 @@ protected override void OnExit(ExitEventArgs e)
264351
}
265352

266353

354+
355+
_hwInfoTokenSource.Cancel();
356+
357+
var hwInfoToken = _hwInfoTokenSource.Token;
358+
359+
try
360+
{
361+
HWInfoTask?.Wait(hwInfoToken);
362+
}
363+
catch (OperationCanceledException)
364+
{
365+
Log.Info("HWINFO background task ended");
366+
}
367+
finally
368+
{
369+
_hwInfoTokenSource.Dispose();
370+
}
371+
267372
Log.Info("exiting");
268373

269374
base.OnExit(e);

fipha/ChartCircularBuffer.cs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Drawing;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace fipha
9+
{
10+
public class ChartCircularBuffer
11+
{
12+
private readonly LinkedList<float> _buffer = new LinkedList<float>();
13+
private int _maxItemCount = 1000;
14+
private HWInfo.SENSOR_TYPE _sensorType;
15+
private string _unit;
16+
private float _minv;
17+
private float _maxv;
18+
19+
public ChartCircularBuffer(HWInfo.SENSOR_TYPE sensorType, string unit)
20+
{
21+
_sensorType = sensorType;
22+
_unit = unit;
23+
}
24+
25+
public void Put(float item)
26+
{
27+
lock (_buffer)
28+
{
29+
_buffer.AddFirst(item);
30+
if (_buffer.Count > _maxItemCount)
31+
{
32+
_buffer.RemoveLast();
33+
}
34+
}
35+
}
36+
37+
public string MinV()
38+
{
39+
return _minv == _maxv ? "" : HWInfo.NumberFormat(_sensorType, _unit, _minv);
40+
}
41+
42+
public string MaxV()
43+
{
44+
return _minv == _maxv ? "" : HWInfo.NumberFormat(_sensorType, _unit, _maxv);
45+
}
46+
47+
public PointF[] Read(int chartImageDisplayWidth, int chartImageDisplayHeight)
48+
{
49+
lock (_buffer)
50+
{
51+
var c = _buffer.Count;
52+
if (c > chartImageDisplayWidth)
53+
{
54+
c = chartImageDisplayWidth;
55+
}
56+
57+
var minv = (float)1e10;
58+
var maxv = (float)-1e10;
59+
60+
var b = new PointF[c];
61+
62+
var node = _buffer.First;
63+
64+
for (var index = 0; index < c; index++)
65+
{
66+
var y = node.Value;
67+
68+
if (y < minv) minv = y;
69+
if (y > maxv) maxv = y;
70+
71+
node = node.Next;
72+
}
73+
74+
var range = maxv - minv;
75+
76+
if (range > 0)
77+
{
78+
minv -= (float) (range * 0.1);
79+
maxv += (float) (range * 0.1);
80+
}
81+
else
82+
{
83+
minv -= (float)(maxv * 0.1);
84+
maxv += (float)(maxv * 0.1);
85+
}
86+
87+
if (minv < 0) minv = 0;
88+
89+
range = maxv - minv;
90+
91+
if (range > 0)
92+
{
93+
var yFactor = chartImageDisplayHeight / range;
94+
95+
node = _buffer.First;
96+
97+
for (var index = 0; index < c; index++)
98+
{
99+
var y = node.Value;
100+
101+
var x = chartImageDisplayWidth - 1 - index;
102+
103+
b[index] = new PointF(x, chartImageDisplayHeight - (y - minv) * yFactor);
104+
105+
node = node.Next;
106+
}
107+
108+
}
109+
110+
_minv = minv;
111+
_maxv = maxv;
112+
113+
return b;
114+
}
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)