Skip to content

Commit

Permalink
0.99
Browse files Browse the repository at this point in the history
  • Loading branch information
nicegamer7 committed Mar 18, 2020
1 parent b3f7450 commit 263f7ed
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 178 deletions.
115 changes: 3 additions & 112 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,113 +1,4 @@
# project
misc/
dist/pygrid/
notes.txt
pygrid.json
gitconf.bat
gitcommit.bat


#vscode
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
#dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.idea/
dist/
pygrid.json
1 change: 0 additions & 1 deletion _run.bat

This file was deleted.

14 changes: 14 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
echo off

rmdir /q /s dist
rmdir /q /s build

pyinstaller -w --icon ui\img\icon.ico pygrid.py

cd dist
"C:\Program Files\7-Zip\7z.exe" a pygrid.zip pygrid

rmdir /q /s pygrid
cd ..
rmdir /q /s build
del /q pygrid.spec
16 changes: 8 additions & 8 deletions controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@


class Controller(QThread):
"""Polls data from Hardware Monitor and Grid, applies control policy to fans, emits update signal to UI."""
"""Polls data from Libre Hardware Monitor and Grid, applies control policy to fans, emits update signal to UI."""
ok = True
errorMessage = ""
appsettings = None
settingsTS = datetime.datetime(year=1900, month=1, day=1) # current timestamp of settings

current_fan_speed = [] # holds latest uploaded fan speeds. Init to -1 to ensure first write-through
new_fan_speed = [] # if new values are same as current, no updates are sent to Grid
movingaverage = []
Expand Down Expand Up @@ -67,15 +67,15 @@ def run(self):
td.now()
remaining = TIME_SLICE - td.ms
if (remaining > MAX_SLEEP): remaining = MAX_SLEEP
if (remaining > 0):
if (remaining > 0):
time.sleep(remaining / 1000.0)
sleep_count += 1

td.now()
slice_time = td.ms
#print ("loop={0}, exec={1}, slice={2}, sleep count={3}".format(counter, exec_time, slice_time, sleep_count))
counter += 1

self.grid.close()
self.hamon.close()
print ("Controller has stopped")
Expand Down Expand Up @@ -131,12 +131,12 @@ def dowork(self):
# save the timestamp of newest settings to track further changes
self.settingsTS = self.appsettings.timestamp

# get recent readings from hardware monitor and apply control policy
# get recent readings from Libre Hardware Monitor and apply control policy
self.hamon.update()
if self.hamon.ok:
self.hamon.updateSignals(self.signals)

if (self.hamon.ok and self.grid.ok):
if (self.hamon.ok and self.grid.ok):
self.control()

# pack data into a dict for visualization, emit signal to UI
Expand All @@ -149,7 +149,7 @@ def dowork(self):
"fans": fans, "fanspeed": self.current_fan_speed[1:NFANS+1]
}
self.uiUpdate.emit(signalData)


def control(self):
"""Loops through all fans, applies control policy to each, sends updates to Grid"""
Expand Down Expand Up @@ -208,7 +208,7 @@ def control_fan(self, fanindex, policy):

tempA, speedA = (temp, 0) # zero default speed if the curve is empty
tempB, speedB = (temp, 0)

# find two points on the curve to the left and to the right of the current temp
for i in range(0, len(curve)):
c = curve[i]
Expand Down
Binary file removed dist/pygrid.zip
Binary file not shown.
20 changes: 10 additions & 10 deletions hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class NZXTGrid():
# Get fan voltage: 84 XX -> C0 00 00 NN NN
# Get fan amperage: 85 XX -> C0 00 00 NN NN
# Get fan RPM: 8A XX -> C0 00 00 RR RR
#
#
# Parameters:
# XX: fan ID (01, 02, 03, 04, 05, 06)
# NN NN: volts/amperes (07 50: 7.80V, 02 12: 2.18 amps)
Expand Down Expand Up @@ -66,7 +66,7 @@ def open(self, port):
self._err("Could not open port {0}. Access denied. The port may be in use by another application.".format(port))
else:
self._err(str(e))


def close(self):
if (not self.com.closed):
Expand All @@ -79,7 +79,7 @@ def _err(self, errtext):
print (errtext)
self.ok = False
self.errorCount += 1


def _cmd(self, data, response_length=1):
"""Sends an arbitrary command to Grid and returns a response"""
Expand Down Expand Up @@ -180,7 +180,7 @@ def poll(self, pollrpm=True, pollvoltage=True, pollamperage=True):
# http://timgolden.me.uk/python/wmi/cookbook.html

class Hamon():
"""Provides WMI interface to Hardware Monitor and its temperature readings"""
"""Provides WMI interface to Libre Hardware Monitor and its temperature readings"""
ok = True
errorMessage = ""
initialized = False
Expand All @@ -191,10 +191,10 @@ class Hamon():
def __init__(self):
try:
CoInitialize()
self.hamon = wmi.WMI(namespace="root\OpenHardwareMonitor")
self.hamon = wmi.WMI(namespace="root\LibreHardwareMonitor")
self.initialized = True
except Exception as e:
self._err ("Error: Hardware monitor not installed.\nPlease install Hardware Monitor and restart the application.")
self._err ("Error: Libre Hardware Monitor not installed.\nPlease install it and restart the application.")


def _err(self, errtext):
Expand All @@ -206,7 +206,7 @@ def _err(self, errtext):
def update(self):
# run only if initialization was successful
if (self.initialized):
# it is possible that at boot time Hardware Monitor starts later than the app,
# it is possible that at boot time Libre Hardware Monitor starts later than the app,
# so the sensor readings will not be available immediately - we need to retry until the monitor is loaded.
self.ok = True # reset error

Expand Down Expand Up @@ -251,9 +251,9 @@ def updateSignals(self, signals):
s.update(val)
totalsum += val

# if no readings were found assume Hardware Monitor is not (yet) running
# if no readings were found assume Libre Hardware Monitor is not (yet) running
if (totalsum == 0):
self._err("The data from Hardware Monitor is unavailable.\nCheck if Monitor is started.")
self._err("The data from Libre Hardware Monitor is unavailable.\nPlease check if it is running.")


def getSignalValue(self, fn, sensors):
Expand All @@ -273,7 +273,7 @@ def getSignalValue(self, fn, sensors):
if (val > max): max = val
avg += val
count += 1

res = max
if (fn == "avg"): res = avg / float(count)
return res
Expand Down
12 changes: 6 additions & 6 deletions pygrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from util import StrStream


APP_TITLE = "PyGrid 0.98"
APP_TITLE = "PyGrid 0.99"


class MainWindow(QMainWindow):
Expand Down Expand Up @@ -65,7 +65,7 @@ def __init__(self, app):
self.controller.uiUpdate.connect(self.update)

startminimized = False
if (self.appsettings.ok):
if (self.appsettings.ok):
startminimized = self.appsettings.settings["app"]["startminimized"]
self.controller.start()
else:
Expand All @@ -77,11 +77,11 @@ def __init__(self, app):
print(self.appsettings.errorMessage)
self.ui.statusEdit.setStyleSheet(self.COLOR_ERR)
self.ui.statusEdit.setPlainText(x.data)
if (not startminimized):

if (not startminimized):
self.show()
self.controller.enableUICallbacks = True


def keyPressClosure(self, uiControl):
"""Wraps event with a closure to avoid subclassing PlainTextEdit"""
Expand Down Expand Up @@ -207,7 +207,7 @@ def update(self, data):
if (portsandsensors and self.appsettings.gridstats):
print("\nGrid reads: {0}, writes: {1}, errors: {2}".format(
self.controller.grid.readCount,
self.controller.grid.writeCount,
self.controller.grid.writeCount,
self.controller.grid.errorCount))
else:
self.ui.statusEdit.setStyleSheet(self.COLOR_ERR)
Expand Down
21 changes: 10 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# PyGrid
PyGrid allows to control fan speeds using [NZXT Grid+ V2](https://www.nzxt.com/products/grid-plus-v2) device. PyGrid is a simple and stable alternative to NZXT CAM software when it comes to fan control.
PyGrid allows you to control fan speeds using an [NZXT Grid+ V2](https://www.nzxt.com/products/grid-plus-v2). PyGrid is a simple and stable alternative to NZXT CAM software when it comes to fan control.

<img src="https://github.com/andyatgh/pygrid/blob/master/screenshots/pygrid1.png">
<img src="https://github.com/andyatgh/pygrid/blob/master/screenshots/pygrid2.png" width="320">
<img src="https://github.com/nicegamer7/pygrid/blob/master/screenshots/pygrid1.png">
<img src="https://github.com/nicegamer7/pygrid/blob/master/screenshots/pygrid2.png" width="320">

## PyGrid key features
* Very simple interface, all settings are edited as text.
Expand All @@ -11,12 +11,12 @@ PyGrid allows to control fan speeds using [NZXT Grid+ V2](https://www.nzxt.com/p
* Rich customization of temperature signals and fan curves allows lots of flexibility in fan control.

## Installation
1. Download and launch [Open Hardware Monitor](http://openhardwaremonitor.org/downloads/). Make sure it starts with Windows. PyGrid uses temperature data from OHM.
2. Download [PyGrid executable](https://github.com/andyatgh/pygrid/tree/master/dist) and launch it from any folder. The app will open and register itself for startup with Windows.
1. PyGrid uses temperature data from Libre Hardware Monitor. Download and launch [Libre Hardware Monitor](https://ci.appveyor.com/project/LibreHardwareMonitor/librehardwaremonitor/build/artifacts). Make sure it starts with Windows.
2. Download [PyGrid](https://github.com/nicegamer7/pygrid/releases) and launch it from any folder. The app will open and register itself for startup with Windows.
3. PyGrid will initialize the settings and create default fan curves for all fans - edit them in the Settings panel, setup other parameters the way you think is best for your system, hit Ctrl+Enter, settings will be saved and applied immediately.

<img src="https://github.com/andyatgh/pygrid/blob/master/screenshots/hamon1.png" width="320">
<img src="https://github.com/andyatgh/pygrid/blob/master/screenshots/pygrid3.png" width="320">
<img src="https://github.com/nicegamer7/pygrid/blob/master/screenshots/hamon1.png" width="320">
<img src="https://github.com/nicegamer7/pygrid/blob/master/screenshots/pygrid3.png" width="320">


## PyGrid settings
Expand Down Expand Up @@ -99,13 +99,12 @@ The app itself is stable but I noticed two minor issues with the Grid hardware i
* Very rarely the controller may not execute the command given to it (setting fan speed may fail or data polling may not return voltage, rpm or amperage). It may stop responding for a few seconds and then come back to life. This will be properly handled by PyGrid which will reconnect to the device, but a brief warning message may be displayed in the Status panel.

## Build
The code works with Python 3.6 and 3.5. The executable was built with Pyinstaller 3.3 using Python 3.6. The basic dependencies were part of Anaconda distribution. The corresponding batch files for installing additional dependencies and building the executable are supplied with the source code. Pyinstaller 3.3 has some issues handling PyQT dependencies, so one DLL had to be copied over to the dist directory in order for the app to start - this is done in the batch file as part of the build process.

PyGrid is compatible with Python 3.5, 3.6, and 3.7. The required dependencies are listed in the dependencies.txt file. After those packages are installed, it is as simple as running the build.bat file.

## Acknowledgements
I would like to thank [akej74](https://github.com/akej74) and [RoelGo](https://github.com/RoelGo) for the awesome work they did in the similar projects and whose source code helped me understand the way Grid operates. Project references:
* [Grid Control](https://github.com/akej74/grid-control) by [akej74](https://github.com/akej74)
* [CamSucks](https://github.com/RoelGo/CamSucks) by [RoelGo](https://github.com/RoelGo)

#
<img src="https://github.com/andyatgh/pygrid/blob/master/ui/img/icon.png" width="96">
#
<img src="https://github.com/nicegamer7/pygrid/blob/master/ui/img/icon.png" width="96">
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PyInstaller
pyqt5-stubs
pyserial
pywin32
WMI
24 changes: 0 additions & 24 deletions z-build.bat

This file was deleted.

Loading

0 comments on commit 263f7ed

Please sign in to comment.