Skip to content

Commit

Permalink
chore(release): v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
anklav24 committed Jan 17, 2022
1 parent 6ed9f8c commit 3b4d451
Show file tree
Hide file tree
Showing 23 changed files with 1,074 additions and 64 deletions.
63 changes: 0 additions & 63 deletions .gitattributes

This file was deleted.

14 changes: 13 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
log.txt
venv/
*.spec

instance/

.pytest_cache/
.coverage
htmlcov/

dist/
build/
*.egg-info/

# User-specific files
*.suo
Expand Down Expand Up @@ -141,7 +154,6 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
Expand Down
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# HWiFO API Server for Zabbix LLD

## Overview

### You need this if you want to grab certain data from HWiNFO

- Apply filters
- Get data into Zabbix (Zabbix Agent, HTTP Agent)
- Use Zabbix template (LLD, Zabbix agent)
- Based on RemoteHWInfo HWiNFO / GPU-Z / MSI Afterburner Remote Monitor HTTP JSON Web Server
- _* GPU-Z and Afterburner data are possible in future_

## Requirements

- Windows 10 or above
- Zabbix server 5.x LTS

## Installation

### Run HWiNFO API server

- [Download Latest Release](https://github.com/anklav24/remotehwinfo-zabbix-integration/releases/latest/download/hwinfo_api_server.zip)
- Rus as administrator: `.hwinfo_api_server.exe`

### Configure Zabbix-host

- Copy `zabbix_agentd.conf.d` to `Zabbix Agent` folder.
- Restart `Zabbix Agent` service.

### Configure Zabbix-server

- Import the template: `zabbix_agentd.conf.d/Template HWiNFO API Server.xml`
- Attach to a host

### Links

- [Postman with examples](https://www.postman.com/martian-trinity-608894/workspace/postman-examples-public/request/14292201-2ee88739-c654-47c0-99f3-e738500304a8)
- [Get docs: http://localhost:50000](http://localhost:50000)
![](docs/docs_screenshot.png)

## Other

### Troubleshooting:

- If `remotehwinfo.exe` shows and just disappear try to change ports
- run tests ` python -m pytest -v -s`
- Allowed Zabbix key parameters
- In zabbix keys replace `space -> %20` `comma -> %2C`

```bash
Special characters "\, ', ", `, *, ?, [, ], {, }, ~, $, !, &, ;, (, ), <, >, |, #, @, 0x0a" are not allowed in the parameters.
```

### For developers

- Run in the Python environment

```powershell or cmd
.\run_hwinfo_api_server.cmd (Run as administrator)
```

- Build server

```powershell or cmd
.\build.ps1
```

### Credits:

- [Demion / remotehwinfo](https://github.com/Demion/remotehwinfo)
- [Remote Sensor Monitor - A RESTful Web Server](https://www.hwinfo.com/forum/threads/introducing-remote-sensor-monitor-a-restful-web-server.1025/)
- [HWiNFO - Professional System Information and Diagnostics](https://www.hwinfo.com/)

### TODO:

- add args for .exe
- debug
- ports
6 changes: 6 additions & 0 deletions api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from flask import Flask

app = Flask(__name__, template_folder='templates', static_folder='static')
app.config.from_object('config')

from api.views import info, lld # noqa: F401
48 changes: 48 additions & 0 deletions api/json_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import requests

from config import REMOTE_HWINFO_IP, REMOTE_HWINFO_PORT

# noinspection HttpUrlsUsage
REMOTE_HWINFO_URL = f'http://{REMOTE_HWINFO_IP}:{REMOTE_HWINFO_PORT}/json.json'


def change_reading_types(json_data: dict) -> dict:
reading_types = {'0': 'None', '1': 'Temp', '2': 'Voltage',
'3': 'Fan', '4': 'Current', '5': 'Power',
'6': 'Clock', '7': 'Usage', '8': 'Other'}
for index, value in enumerate(json_data['readings']):
json_data['readings'][index]['readingTypeName'] = reading_types[str(value['readingType'])]
return json_data


def get_modified_json() -> dict:
json_data = requests.get(REMOTE_HWINFO_URL, verify=False, timeout=5).json()['hwinfo']

for sensor_index, hardware in enumerate(json_data['sensors']):
json_data['sensors'][sensor_index]['sensorIndex'] = sensor_index

for reading_index, reading in enumerate(json_data['readings']):
json_data['readings'][reading_index]['readingIndex'] = reading_index

change_reading_types(json_data)
return json_data


def get_lld_sensors() -> list[dict]:
"""Preprocess JSON to LLD format. Make names more user-friendly"""
json_data = get_modified_json()

datalist = list()
for hardware in json_data['sensors']:
for reading in json_data['readings']:
if reading['sensorIndex'] == hardware['sensorIndex']:
datadict = {"{#HARDWARENAME}": hardware['sensorNameUser'],
"{#HARDWAREINDEX}": hardware['sensorIndex'],
"{#SENSORNAME}": reading['labelUser'],
"{#SENSORINDEX}": reading['readingIndex'],
"{#SENSORTYPENAME}": reading['readingTypeName'],
"{#SENSORTYPEINDEX}": reading['readingType'],
"{#VALUE}": reading['value'],
"{#UNIT}": reading['unit']}
datalist.append(datadict)
return datalist
23 changes: 23 additions & 0 deletions api/static/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
html {
font-family: sans-serif;
background: #eee;
padding: 1rem;
}

table {
border-collapse: collapse;
border: 1px solid black;
}

table tr:first-child{
font-weight: bold;
}

td {
white-space: pre;
padding: 5px;
}

td, th {
border: 1px solid black;
}
17 changes: 17 additions & 0 deletions api/templates/results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

<table>
<tr>
<td> Method</td>
<td> Description</td>
</tr>

{% for method, description in sorted_dict.items() %}
<tr>
<td>{{ method }}</td>
<td>{{ description }}</td>
</tr>

{% endfor %}
</table>
11 changes: 11 additions & 0 deletions api/third_party/HWiNFO32.INI
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Settings]
OpenSensors=1
MinimalizeMainWnd=1
MinimalizeSensors=1
MinimalizeSensorsClose=1
ShowWelcomeAndProgress=0
Autorun=1
OpenSystemSummary=0
AutoUpdateBetaDisable=1
AutoUpdate=0
SensorInterval=1000
Binary file added api/third_party/HWiNFO32.exe
Binary file not shown.
27 changes: 27 additions & 0 deletions api/third_party/process_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import os
import subprocess
import time


def print_waiting(sec: int) -> None:
print("Waiting", end='')
for _ in range(1, sec + 1):
time.sleep(1)
print('.', end='')
print()


def run_processes(remote_hwinfo_port: int) -> None:
os.startfile(r'api\third_party\HWiNFO32.exe', show_cmd=False)
os.startfile(r'api\third_party\remotehwinfo.exe',
arguments=f"-port {remote_hwinfo_port} -log 0 -hwinfo 1 -gpuz 0 -afterburner 0",
show_cmd=False)
subprocess.run('tasklist /fi "imagename eq HWiNFO32.exe"')
subprocess.run('tasklist /fi "imagename eq remotehwinfo.exe"')
print_waiting(15)


def kill_processes() -> None:
os.system("taskkill /f /im remotehwinfo.exe")
os.system("taskkill /f /im HWiNFO32.exe")
print_waiting(3)
Binary file added api/third_party/remotehwinfo.exe
Binary file not shown.
Empty file added api/views/__init__.py
Empty file.
63 changes: 63 additions & 0 deletions api/views/info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import flask
import requests
from flask import render_template

from api import app
from api.json_handler import get_modified_json


@app.route("/", methods=['GET'])
def get_docs():
"""Print documentation."""
func_dict = {}
for rule in app.url_map.iter_rules():
if rule.endpoint != 'static':
if app.view_functions[rule.endpoint].__doc__ is None:
func_dict[rule.rule] = ''
else:
func_dict[rule.rule] = app.view_functions[rule.endpoint].__doc__

sorted_list = sorted(func_dict.items(), key=lambda value: len(value[0]))
sorted_dict = dict(sorted_list)

return render_template('results.html', sorted_dict=sorted_dict)


@app.route('/json')
def get_all_values():
"""Return preprocessed JSON from RemoteHWInfo."""
return flask.jsonify(get_modified_json())


@app.route("/status")
def get_status():
"""Return {"code": 200, "message": "All systems are working"} if all it's alright."""
try:
get_modified_json()
json = {"code": 200, "message": "All systems are working"}
except requests.exceptions.ConnectionError as error:
json = {"code": 500, "message": f'{error} (try to check connection with remotehwinfo.exe)'}
except Exception as error:
json = {"code": 500, "message": f'{error} (try to check connection with HWiNFO32.exe)'}

return flask.jsonify(json)


@app.route('/hardware_inventory')
def get_hardware_inventory():
"""Get a list of hardware on the PC as a text separated by newlines."""
json_data = get_modified_json()

ignore_list = ('Memory Timings', 'RTSS',
'Drive: Msft Virtual Disk',
'Windows Hardware Errors (WHEA)')
hardware_list = list()

for sensor_index, hardware in enumerate(json_data['sensors']):

if hardware['sensorNameUser'] not in ignore_list:
tmp_str = f"{hardware['sensorNameUser']}\n"
hardware_list.append(tmp_str)
hardware_list.sort()

return "".join(hardware_list)
Loading

0 comments on commit 3b4d451

Please sign in to comment.