diff --git a/README.md b/README.md
index 8be8986..0719fe8 100644
--- a/README.md
+++ b/README.md
@@ -13,17 +13,17 @@ Open source Linux interface for iCUE LINK Hub and other devices.
- Take care and have fun!
## Supported devices
-| Device | VID | PID | Sub Devices |
-|------------------------|--------|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| iCUE LINK System Hub | `1b1c` | `0c3f` | iCUE LINK QX Fan
iCUE LINK RX Fan
iCUE LINK RX RGB Fan
iCUE LINK RX MAX Fan
iCUE LINK H100i
iCUE LINK H115i
iCUE LINK H150i
iCUE LINK H170i
XC7 Elite
XG7
XD5 Elite
XD5 Elite LCD
VRM Cooling Module
iCUE LINK TITAN H100i
iCUE LINK TITAN H150i
iCUE LINK TITAN H115i
iCUE LINK TITAN H170i | | |
-| iCUE COMMANDER Core | `1b1c` | `0c32`
`0c1c` | iCUE H100i ELITE CAPELLIX
iCUE H115i ELITE CAPELLIX
iCUE H150i ELITE CAPELLIX
iCUE H170i ELITE CAPELLIX
H100i ELITE LCD
H150i ELITE LCD
H170i ELITE LCD
iCUE H100i ELITE CAPELLIX XT
iCUE H115i ELITE CAPELLIX XT
iCUE H150i ELITE CAPELLIX XT
iCUE H170i ELITE CAPELLIX XT
1x Temperature Probe
4-LED RGB Fan
8-LED RGB Fan
QL Fan Series
LL Fan Series
ML Fan Series
Any PWM Fan |
-| iCUE COMMANDER Core XT | `1b1c` | `0c2a` | External RGB Hub
2x Temperature Probe
4-LED RGB Fan
8-LED RGB Fan
QL Fan Series
LL Fan Series
ML Fan Series
Any PWM Fan
H55 RGB AIO
H100 RGB AIO
H150 RGB AIO |
-| iCUE H100i RGB ELITE | `1b1c` | `0c35`
`0c40` | |
-| iCUE H115i RGB ELITE | `1b1c` | `0c36` | |
-| iCUE H150i RGB ELITE | `1b1c` | `0c37`
`0c41` | |
-| Lighting Node CORE | `1b1c` | `0c1a` | HD RGB Series Fan
LL RGB Series Fan
ML PRO RGB Series Fan
QL RGB Series Fan
8-LED Series Fan
SP RGB Series Fan |
-| Lighting Node PRO | `1b1c` | `0c0b` | 2x External RGB Hub
HD RGB Series Fan
LL RGB Series Fan
ML PRO RGB Series Fan
QL RGB Series Fan
8-LED Series Fan
SP RGB Series Fan |
-| Commander PRO | `1b1c` | `0c10` | 2x External RGB Hub
4x Temperature Probe
Any PWM Fan |
+| Device | VID | PID | Sub Devices |
+|------------------------|--------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| iCUE LINK System Hub | `1b1c` | `0c3f` | iCUE LINK QX Fan
iCUE LINK RX Fan
iCUE LINK RX RGB Fan
iCUE LINK RX MAX Fan
iCUE LINK H100i
iCUE LINK H115i
iCUE LINK H150i
iCUE LINK H170i
XC7 Elite
XG7
XD5 Elite
XD5 Elite LCD
VRM Cooling Module
iCUE LINK TITAN H100i
iCUE LINK TITAN H150i
iCUE LINK TITAN H115i
iCUE LINK TITAN H170i | | |
+| iCUE COMMANDER Core | `1b1c` | `0c32`
`0c1c` | H100i ELITE CAPELLIX
H115i ELITE CAPELLIX
H150i ELITE CAPELLIX
H170i ELITE CAPELLIX
H100i ELITE LCD
H150i ELITE LCD
H170i ELITE LCD
H100i ELITE LCD XT
H115i ELITE LCD XT
H150i ELITE LCD XT
H170i ELITE LCD XT
H100i ELITE CAPELLIX XT
H115i ELITE CAPELLIX XT
H150i ELITE CAPELLIX XT
H170i ELITE CAPELLIX XT
1x Temperature Probe
4-LED RGB Fan
8-LED RGB Fan
QL Fan Series
LL Fan Series
ML Fan Series
Any PWM Fan |
+| iCUE COMMANDER Core XT | `1b1c` | `0c2a` | External RGB Hub
2x Temperature Probe
4-LED RGB Fan
8-LED RGB Fan
QL Fan Series
LL Fan Series
ML Fan Series
Any PWM Fan
H55 RGB AIO
H100 RGB AIO
H150 RGB AIO |
+| iCUE H100i RGB ELITE | `1b1c` | `0c35`
`0c40` | |
+| iCUE H115i RGB ELITE | `1b1c` | `0c36` | |
+| iCUE H150i RGB ELITE | `1b1c` | `0c37`
`0c41` | |
+| Lighting Node CORE | `1b1c` | `0c1a` | HD RGB Series Fan
LL RGB Series Fan
ML PRO RGB Series Fan
QL RGB Series Fan
8-LED Series Fan
SP RGB Series Fan |
+| Lighting Node PRO | `1b1c` | `0c0b` | 2x External RGB Hub
HD RGB Series Fan
LL RGB Series Fan
ML PRO RGB Series Fan
QL RGB Series Fan
8-LED Series Fan
SP RGB Series Fan |
+| Commander PRO | `1b1c` | `0c10` | 2x External RGB Hub
4x Temperature Probe
Any PWM Fan |
## Installation
@@ -83,9 +83,11 @@ echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS
# Allow hidraw communication as non-root - iCUE Commander Core
echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c32\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/99-corsair-cc-64.rules
+echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c39\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/99-corsair-elite-lcd.rules
# Allow hidraw communication as non-root - iCUE Commander Core
echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c1c\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/99-corsair-cc-96.rules
+echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c39\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/99-corsair-elite-lcd.rules
# Allow hidraw communication as non-root - iCUE Commander Core XT
echo "KERNEL==\"hidraw*\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c2a\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/99-corsair-ccxt.rules
diff --git a/go.mod b/go.mod
index b7aa359..95bccaf 100644
--- a/go.mod
+++ b/go.mod
@@ -4,10 +4,12 @@ go 1.22.2
require (
github.com/NVIDIA/go-nvml v0.12.4-0
+ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/gorilla/mux v1.8.1
github.com/sirupsen/logrus v1.9.3
github.com/sstallion/go-hid v0.14.1
github.com/zcalusic/sysinfo v1.0.2
+ golang.org/x/image v0.19.0
)
require (
diff --git a/go.sum b/go.sum
index 5d66089..9633904 100644
--- a/go.sum
+++ b/go.sum
@@ -3,6 +3,8 @@ github.com/NVIDIA/go-nvml v0.12.4-0/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@@ -19,6 +21,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc=
github.com/zcalusic/sysinfo v1.0.2/go.mod h1:kluzTYflRWo6/tXVMJPdEjShsbPpsFRyy+p1mBQPC30=
+golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
+golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/src/controller/controller.go b/src/controller/controller.go
index d1bb40e..7b574b9 100644
--- a/src/controller/controller.go
+++ b/src/controller/controller.go
@@ -3,6 +3,7 @@ package controller
import (
"OpenLinkHub/src/config"
"OpenLinkHub/src/devices"
+ "OpenLinkHub/src/devices/lcd"
"OpenLinkHub/src/logger"
"OpenLinkHub/src/rgb"
"OpenLinkHub/src/server"
@@ -16,6 +17,7 @@ func Start() {
config.Init() // Configuration
logger.Init() // Logger
rgb.Init() // RGB
+ lcd.Init() // LCD
temperatures.Init() // Temperatures
devices.Init() // Devices
server.Init() // REST & WebUI
diff --git a/src/devices/cc/cc.go b/src/devices/cc/cc.go
index 8e50ebb..97d2f5c 100644
--- a/src/devices/cc/cc.go
+++ b/src/devices/cc/cc.go
@@ -15,6 +15,13 @@ package cc
// - H100i ELITE LCD
// - H150i ELITE LCD
// - H170i ELITE LCD
+// - H100i ELITE CAPELLIX
+// - H150i ELITE CAPELLIX
+// - H170i ELITE CAPELLIX
+// - iCUE H100i ELITE LCD XT
+// - iCUE H115i ELITE LCD XT
+// - iCUE H150i ELITE LCD XT
+// - iCUE H170i ELITE LCD XT
// - iCUE H100i ELITE CAPELLIX XT
// - iCUE H115i ELITE CAPELLIX XT
// - iCUE H150i ELITE CAPELLIX XT
@@ -24,6 +31,7 @@ package cc
import (
"OpenLinkHub/src/common"
"OpenLinkHub/src/config"
+ "OpenLinkHub/src/devices/lcd"
"OpenLinkHub/src/logger"
"OpenLinkHub/src/rgb"
"OpenLinkHub/src/temperatures"
@@ -75,6 +83,7 @@ var (
headerWriteSize = 4
authRefreshChan = make(chan bool)
speedRefreshChan = make(chan bool)
+ lcdRefreshChan = make(chan bool)
deviceRefreshInterval = 1000
defaultSpeedValue = 50
temperaturePullingInterval = 3000
@@ -82,7 +91,11 @@ var (
maxBufferSizePerRequest = 61
timer = &time.Ticker{}
timerSpeed = &time.Ticker{}
+ lcdTimer = &time.Ticker{}
internalLedDevices = make(map[int]*LedChannel, 7)
+ lcdHeaderSize = 8
+ lcdBufferSize = 1024
+ maxLCDBufferSizePerRequest = lcdBufferSize - lcdHeaderSize
aioList = []AIOList{
{Name: "H100i ELITE CAPELLIX", PumpVersion: 1, RadiatorSize: 240},
{Name: "H100i ELITE CAPELLIX", PumpVersion: 2, RadiatorSize: 240},
@@ -90,18 +103,35 @@ var (
{Name: "H150i ELITE CAPELLIX", PumpVersion: 1, RadiatorSize: 360},
{Name: "H150i ELITE CAPELLIX", PumpVersion: 2, RadiatorSize: 360},
{Name: "H170i ELITE CAPELLIX", PumpVersion: 1, RadiatorSize: 420},
- {Name: "H100i ELITE LCD", PumpVersion: 3, RadiatorSize: 240},
- {Name: "H150i ELITE LCD", PumpVersion: 3, RadiatorSize: 360},
- {Name: "H170i ELITE LCD", PumpVersion: 3, RadiatorSize: 420},
- {Name: "H100i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 240},
- {Name: "H100i ELITE LCD XT", PumpVersion: 6, RadiatorSize: 240},
- {Name: "H115i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 280},
- {Name: "H150i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 360},
- {Name: "H150i ELITE LCD XT", PumpVersion: 6, RadiatorSize: 360},
- {Name: "H170i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 420},
+ {Name: "H100i ELITE LCD", PumpVersion: 3, RadiatorSize: 240, LCD: true},
+ {Name: "H150i ELITE LCD", PumpVersion: 3, RadiatorSize: 360, LCD: true},
+ {Name: "H170i ELITE LCD", PumpVersion: 3, RadiatorSize: 420, LCD: true},
+ {Name: "H100i ELITE CAPELLIX", PumpVersion: 3, RadiatorSize: 240, LCD: false},
+ {Name: "H150i ELITE CAPELLIX", PumpVersion: 3, RadiatorSize: 360, LCD: false},
+ {Name: "H170i ELITE CAPELLIX", PumpVersion: 3, RadiatorSize: 420, LCD: false},
+ {Name: "H100i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 240, LCD: true},
+ {Name: "H100i ELITE LCD XT", PumpVersion: 6, RadiatorSize: 240, LCD: true},
+ {Name: "H115i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 280, LCD: true},
+ {Name: "H150i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 360, LCD: true},
+ {Name: "H150i ELITE LCD XT", PumpVersion: 6, RadiatorSize: 360, LCD: true},
+ {Name: "H170i ELITE LCD XT", PumpVersion: 5, RadiatorSize: 420, LCD: true},
+ {Name: "H100i ELITE CAPELLIX XT", PumpVersion: 5, RadiatorSize: 240, LCD: false},
+ {Name: "H100i ELITE CAPELLIX XT", PumpVersion: 6, RadiatorSize: 240, LCD: false},
+ {Name: "H115i ELITE CAPELLIX XT", PumpVersion: 5, RadiatorSize: 280, LCD: false},
+ {Name: "H150i ELITE CAPELLIX XT", PumpVersion: 5, RadiatorSize: 360, LCD: false},
+ {Name: "H150i ELITE CAPELLIX XT", PumpVersion: 6, RadiatorSize: 360, LCD: false},
+ {Name: "H170i ELITE CAPELLIX XT", PumpVersion: 5, RadiatorSize: 420, LCD: false},
}
)
+// AIOList struct for supported AIO devices
+type AIOList struct {
+ Name string
+ PumpVersion int16
+ RadiatorSize int16
+ LCD bool
+}
+
// DeviceMonitor struct contains the shared variable and synchronization primitives
type DeviceMonitor struct {
Status byte
@@ -109,20 +139,23 @@ type DeviceMonitor struct {
Cond *sync.Cond
}
+// LedChannel struct for LED pump and fan data
type LedChannel struct {
Total uint8
Command byte
}
+// DeviceProfile struct contains all device profile
type DeviceProfile struct {
Product string
Serial string
+ LCDMode uint8
RGBProfiles map[int]string
SpeedProfiles map[int]string
Labels map[int]string
}
-// Devices contain information about devices connected to a Commander Core
+// Devices struct contain information about connected devices
type Devices struct {
ChannelId int `json:"channelId"`
Type byte `json:"type"`
@@ -145,8 +178,10 @@ type Devices struct {
IsTemperatureProbe bool
}
+// Device struct contains primary device data
type Device struct {
dev *hid.Device
+ lcd *hid.Device
Manufacturer string `json:"manufacturer"`
Product string `json:"product"`
Serial string `json:"serial"`
@@ -159,12 +194,10 @@ type Device struct {
profileConfig string
activeRgb *rgb.ActiveRGB
Template string
-}
-
-type AIOList struct {
- Name string
- PumpVersion int16
- RadiatorSize int16
+ HasLCD bool
+ VendorId uint16
+ LCDModes map[int]string
+ LCDMode int
}
/*
@@ -200,6 +233,14 @@ func Init(vendorId, productId uint16, serial string) *Device {
d := &Device{
dev: dev,
Template: "cc.html",
+ VendorId: vendorId,
+ LCDModes: map[int]string{
+ 0: "Liquid Temperature",
+ 1: "Pump Speed",
+ 2: "CPU Temperature",
+ 3: "GPU Temperature",
+ 4: "Combined",
+ },
}
// There are 2 CCs. One has a packet size of 64 and the other has 96.
@@ -214,6 +255,7 @@ func Init(vendorId, productId uint16, serial string) *Device {
d.getManufacturer() // Manufacturer
d.getProduct() // Product
d.getSerial() // Serial
+ d.getDeviceLcd() // Check if LCD pump cover is installed
d.setProfileConfig() // Device profile
d.getDeviceProfile() // Get device profile if any
d.getDeviceFirmware() // Firmware
@@ -260,6 +302,48 @@ func (d *Device) Stop() {
logger.Log(logger.Fields{"error": err}).Fatal("Unable to close HID device")
}
}
+
+ if d.lcd != nil {
+ lcdRefreshChan <- true
+ lcdTimer.Stop()
+ err := d.lcd.Close()
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Fatal("Unable to close LCD HID device")
+ }
+ }
+}
+
+// getDeviceLcd will check if AIO has LCD pump cover
+func (d *Device) getDeviceLcd() {
+ lcdSerialNumber := ""
+ var lcdProductId uint16 = 3129
+
+ enum := hid.EnumFunc(func(info *hid.DeviceInfo) error {
+ if info.InterfaceNbr == 0 {
+ d.HasLCD = true
+ lcdSerialNumber = info.SerialNbr
+ }
+ return nil
+ })
+
+ // Enumerate all Corsair devices
+ err := hid.Enumerate(d.VendorId, lcdProductId, enum)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err, "vendorId": d.VendorId, "serial": d.Serial}).Fatal("Unable to enumerate LCD devices")
+ return
+ }
+
+ if d.HasLCD {
+ logger.Log(logger.Fields{"vendorId": d.VendorId, "productId": lcdProductId, "serial": d.Serial, "lcdSerial": lcdSerialNumber}).Info("LCD pump cover detected")
+ lcdPanel, e := hid.Open(d.VendorId, lcdProductId, lcdSerialNumber)
+ if e != nil {
+ d.HasLCD = false // We failed
+ logger.Log(logger.Fields{"error": err, "vendorId": d.VendorId, "productId": lcdProductId, "serial": d.Serial}).Error("Unable to open LCD HID device")
+ return
+ }
+ d.lcd = lcdPanel
+ d.setupLCD()
+ }
}
// getManufacturer will return device manufacturer
@@ -1046,7 +1130,7 @@ func (d *Device) getDeviceData() {
status := currentSensor[0]
if status == 0x00 {
temp := float32(int16(binary.LittleEndian.Uint16(currentSensor[1:3]))) / 10.0
- if temp > 1 {
+ if temp > 1 && temp < 100 {
if i == 0 {
if _, ok := d.Devices[i]; ok {
d.Devices[i].Temperature = float32(int16(binary.LittleEndian.Uint16(currentSensor[1:3]))) / 10.0
@@ -1130,6 +1214,20 @@ func (d *Device) UpdateDeviceLabel(channelId int, label string) uint8 {
return 1
}
+// UpdateDeviceLcd will update device LCD
+func (d *Device) UpdateDeviceLcd(mode uint8) uint8 {
+ mutex.Lock()
+ defer mutex.Unlock()
+
+ if d.HasLCD {
+ d.DeviceProfile.LCDMode = mode
+ d.saveDeviceProfile()
+ return 1
+ } else {
+ return 2
+ }
+}
+
// UpdateSpeedProfile will update device channel speed.
// If channelId is 0, all device channels will be updated
func (d *Device) UpdateSpeedProfile(channelId int, profile string) uint8 {
@@ -1176,15 +1274,17 @@ func (d *Device) UpdateSpeedProfile(channelId int, profile string) uint8 {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -1194,6 +1294,7 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
d.activeRgb = nil
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// initLedPorts will prep LED physical ports for reading
@@ -1265,6 +1366,7 @@ func (d *Device) saveDeviceProfile() {
// First save, assign saved profile to a device
if d.DeviceProfile == nil {
+ // RGB
for _, device := range d.Devices {
if device.IsTemperatureProbe {
continue
@@ -1272,10 +1374,19 @@ func (d *Device) saveDeviceProfile() {
rgbProfiles[device.ChannelId] = "static"
}
+ // Labels
for _, device := range d.Devices {
labels[device.ChannelId] = "Not Set"
}
+
+ // LCD
+ if d.HasLCD {
+ deviceProfile.LCDMode = 0
+ }
+
d.DeviceProfile = deviceProfile
+ } else {
+ deviceProfile.LCDMode = d.DeviceProfile.LCDMode
}
// Convert to JSON
@@ -1457,6 +1568,94 @@ func (d *Device) write(endpoint, bufferType, data []byte, extra bool) []byte {
return bufferR
}
+// setupLcd will activate and configure LCD
+func (d *Device) setupLCD() {
+ lcdTimer = time.NewTicker(1000 * time.Millisecond)
+ lcdRefreshChan = make(chan bool)
+ go func() {
+ for {
+ select {
+ case <-lcdTimer.C:
+ switch d.DeviceProfile.LCDMode {
+ case lcd.DisplayCPU:
+ {
+ buffer := lcd.GenerateScreenImage(lcd.DisplayCPU, int(temperatures.GetCpuTemperature()), 0, 0)
+ d.transferToLcd(buffer)
+ }
+ case lcd.DisplayGPU:
+ {
+ buffer := lcd.GenerateScreenImage(lcd.DisplayGPU, int(temperatures.GetGpuTemperature()), 0, 0)
+ d.transferToLcd(buffer)
+ }
+ case lcd.DisplayLiquid:
+ {
+ for _, device := range d.Devices {
+ if device.ContainsPump {
+ buffer := lcd.GenerateScreenImage(lcd.DisplayLiquid, int(device.Temperature), 0, 0)
+ d.transferToLcd(buffer)
+ }
+ }
+ }
+ case lcd.DisplayPump:
+ {
+ for _, device := range d.Devices {
+ if device.ContainsPump {
+ buffer := lcd.GenerateScreenImage(lcd.DisplayPump, int(device.Rpm), 0, 0)
+ d.transferToLcd(buffer)
+ }
+ }
+ }
+ case lcd.DisplayAllInOne:
+ {
+ liquidTemp := 0
+ cpuTemp := 0
+ pumpSpeed := 0
+ for _, device := range d.Devices {
+ if device.ContainsPump {
+ liquidTemp = int(device.Temperature)
+ pumpSpeed = int(device.Rpm)
+ }
+ }
+
+ cpuTemp = int(temperatures.GetCpuTemperature())
+ buffer := lcd.GenerateScreenImage(lcd.DisplayAllInOne, liquidTemp, cpuTemp, pumpSpeed)
+ d.transferToLcd(buffer)
+ }
+ }
+ case <-lcdRefreshChan:
+ lcdTimer.Stop()
+ return
+ }
+ }
+ }()
+}
+
+// transferToLcd will transfer data to LCD panel
+func (d *Device) transferToLcd(buffer []byte) {
+ mutex.Lock()
+ defer mutex.Unlock()
+ chunks := common.ProcessMultiChunkPacket(buffer, maxLCDBufferSizePerRequest)
+ for i, chunk := range chunks {
+ bufferW := make([]byte, lcdBufferSize)
+ bufferW[0] = 0x02
+ bufferW[1] = 0x05
+
+ // The last packet needs to end with 0x01 in order for display to render data
+ if len(chunk) < maxLCDBufferSizePerRequest {
+ bufferW[3] = 0x01
+ }
+
+ bufferW[4] = byte(i)
+ binary.LittleEndian.PutUint16(bufferW[6:8], uint16(len(chunk)))
+ copy(bufferW[8:], chunk)
+
+ if _, err := d.lcd.Write(bufferW); err != nil {
+ logger.Log(logger.Fields{"error": err, "serial": d.Serial}).Error("Unable to write to a device")
+ break
+ }
+ }
+}
+
// transfer will send data to a device and retrieve device output
func (d *Device) transfer(endpoint, buffer, bufferType []byte) ([]byte, error) {
// Packet control, mandatory for this device
diff --git a/src/devices/ccxt/ccxt.go b/src/devices/ccxt/ccxt.go
index dceacf6..165f586 100644
--- a/src/devices/ccxt/ccxt.go
+++ b/src/devices/ccxt/ccxt.go
@@ -901,15 +901,17 @@ func (d *Device) ResetSpeedProfiles(profile string) {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -919,10 +921,11 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
d.activeRgb = nil
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// UpdateExternalHubDeviceType will update a device type connected to the external-LED hub
-func (d *Device) UpdateExternalHubDeviceType(externalType int) int {
+func (d *Device) UpdateExternalHubDeviceType(externalType int) uint8 {
if d.DeviceProfile != nil {
if d.getExternalLedDevice(externalType) != nil {
d.DeviceProfile.ExternalHubDeviceType = externalType
@@ -944,7 +947,7 @@ func (d *Device) UpdateExternalHubDeviceType(externalType int) int {
}
// UpdateExternalHubDeviceAmount will update device amount connected to an external-LED hub and trigger RGB reset
-func (d *Device) UpdateExternalHubDeviceAmount(externalDevices int) int {
+func (d *Device) UpdateExternalHubDeviceAmount(externalDevices int) uint8 {
if d.DeviceProfile != nil {
if d.DeviceProfile.ExternalHubDeviceType > 0 {
d.DeviceProfile.ExternalHubDeviceAmount = externalDevices
diff --git a/src/devices/cpro/cpro.go b/src/devices/cpro/cpro.go
index 6e6a828..e600b6b 100644
--- a/src/devices/cpro/cpro.go
+++ b/src/devices/cpro/cpro.go
@@ -358,15 +358,17 @@ func (d *Device) setSpeed(data map[int]byte) {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -378,6 +380,7 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
}
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// UpdateDeviceSpeed will change device speed profile
@@ -791,7 +794,7 @@ func (d *Device) ResetRgb() {
}
// UpdateExternalHubDeviceType will update a device type connected to the external-LED hub
-func (d *Device) UpdateExternalHubDeviceType(portId, externalType int) int {
+func (d *Device) UpdateExternalHubDeviceType(portId, externalType int) uint8 {
if d.DeviceProfile != nil {
if externalType == 0 {
d.DeviceProfile.ExternalHubs[portId].ExternalHubDeviceType = externalType
@@ -810,7 +813,7 @@ func (d *Device) UpdateExternalHubDeviceType(portId, externalType int) int {
}
// UpdateExternalHubDeviceAmount will update device amount connected to an external-LED hub and trigger RGB reset
-func (d *Device) UpdateExternalHubDeviceAmount(portId, externalDevices int) int {
+func (d *Device) UpdateExternalHubDeviceAmount(portId, externalDevices int) uint8 {
if d.DeviceProfile != nil {
if _, ok := d.DeviceProfile.ExternalHubs[portId]; ok {
// Store current amount
diff --git a/src/devices/devices.go b/src/devices/devices.go
index 188e5e3..55b1bd0 100644
--- a/src/devices/devices.go
+++ b/src/devices/devices.go
@@ -100,7 +100,7 @@ func Stop() {
}
// UpdateExternalHubDeviceType will update a device type connected to an external-LED hub
-func UpdateExternalHubDeviceType(deviceId string, portId, deviceType int) int {
+func UpdateExternalHubDeviceType(deviceId string, portId, deviceType int) uint8 {
if device, ok := devices[deviceId]; ok {
switch device.ProductType {
case productTypeCCXT:
@@ -133,7 +133,7 @@ func UpdateExternalHubDeviceType(deviceId string, portId, deviceType int) int {
}
// UpdateExternalHubDeviceAmount will update a device amount connected to an external-LED hub
-func UpdateExternalHubDeviceAmount(deviceId string, portId, deviceType int) int {
+func UpdateExternalHubDeviceAmount(deviceId string, portId, deviceType int) uint8 {
if device, ok := devices[deviceId]; ok {
switch device.ProductType {
case productTypeCCXT:
@@ -165,6 +165,21 @@ func UpdateExternalHubDeviceAmount(deviceId string, portId, deviceType int) int
return 0
}
+// UpdateDeviceLcd will update device LCD
+func UpdateDeviceLcd(deviceId string, mode uint8) uint8 {
+ if device, ok := devices[deviceId]; ok {
+ switch device.ProductType {
+ case productTypeCC:
+ {
+ if device.CC != nil {
+ return device.CC.UpdateDeviceLcd(mode)
+ }
+ }
+ }
+ }
+ return 0
+}
+
// UpdateDeviceLabel will set / update device label
func UpdateDeviceLabel(deviceId string, channelId int, label string) uint8 {
if device, ok := devices[deviceId]; ok {
@@ -295,54 +310,54 @@ func UpdateManualSpeed(deviceId string, channelId int, value uint16) uint8 {
}
// UpdateRgbProfile will update device RGB profile
-func UpdateRgbProfile(deviceId string, channelId int, profile string) {
+func UpdateRgbProfile(deviceId string, channelId int, profile string) uint8 {
if device, ok := devices[deviceId]; ok {
switch device.ProductType {
case productTypeLinkHub:
{
if device.LinkSystemHub != nil {
- device.LinkSystemHub.UpdateRgbProfile(channelId, profile)
+ return device.LinkSystemHub.UpdateRgbProfile(channelId, profile)
}
}
case productTypeCC:
{
if device.CC != nil {
- device.CC.UpdateRgbProfile(channelId, profile)
+ return device.CC.UpdateRgbProfile(channelId, profile)
}
}
case productTypeCCXT:
{
if device.CCXT != nil {
- device.CCXT.UpdateRgbProfile(channelId, profile)
+ return device.CCXT.UpdateRgbProfile(channelId, profile)
}
}
case productTypeElite:
{
if device.Elite != nil {
- device.Elite.UpdateRgbProfile(channelId, profile)
+ return device.Elite.UpdateRgbProfile(channelId, profile)
}
}
case productTypeLNCore:
{
if device.LnCore != nil {
- device.LnCore.UpdateRgbProfile(channelId, profile)
+ return device.LnCore.UpdateRgbProfile(channelId, profile)
}
}
case productTypeLnPro:
{
if device.LnPro != nil {
- device.LnPro.UpdateRgbProfile(channelId, profile)
+ return device.LnPro.UpdateRgbProfile(channelId, profile)
}
}
case productTypeCPro:
{
if device.CPro != nil {
- device.CPro.UpdateRgbProfile(channelId, profile)
+ return device.CPro.UpdateRgbProfile(channelId, profile)
}
}
}
-
}
+ return 0
}
// ResetSpeedProfiles will reset the speed profile on each available device
diff --git a/src/devices/elite/elite.go b/src/devices/elite/elite.go
index dcda39e..0c6217d 100644
--- a/src/devices/elite/elite.go
+++ b/src/devices/elite/elite.go
@@ -653,15 +653,17 @@ func (d *Device) UpdateSpeedProfile(channelId int, profile string) uint8 {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -671,6 +673,7 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
d.activeRgb = nil
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// getDevices will fetch all devices connected to a hub
diff --git a/src/devices/lcd/lcd.go b/src/devices/lcd/lcd.go
new file mode 100644
index 0000000..4f1fb47
--- /dev/null
+++ b/src/devices/lcd/lcd.go
@@ -0,0 +1,212 @@
+package lcd
+
+import (
+ "OpenLinkHub/src/common"
+ "OpenLinkHub/src/logger"
+ "bytes"
+ "github.com/golang/freetype"
+ "github.com/golang/freetype/truetype"
+ _ "golang.org/x/image/font"
+ "image"
+ "image/color"
+ "image/draw"
+ "image/jpeg"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// Package: LCD Controller
+// This is the primary package for LCD pump covers.
+// All device actions are controlled from this package.
+// Author: Nikola Jurkovic
+// License: GPL-3.0 or later
+
+const (
+ DisplayLiquid uint8 = 0
+ DisplayPump uint8 = 1
+ DisplayCPU uint8 = 2
+ DisplayGPU uint8 = 3
+ DisplayAllInOne uint8 = 4
+)
+
+var (
+ pwd, _ = os.Getwd()
+ location = pwd + "/static/img/lcd/"
+ fontLocation = pwd + "/static/fonts/teko.ttf"
+ mutex sync.Mutex
+)
+
+type LCD struct {
+ images map[uint8]image.Image
+ font *truetype.Font
+}
+
+var lcdConfiguration LCD
+
+// Init will initialize LCD data
+func Init() {
+ lcdImages := make(map[uint8]image.Image, 0)
+
+ // Read folder content
+ files, err := os.ReadDir(location)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err, "location": location}).Error("Unable to read content of a folder")
+ }
+
+ // Load LCD images
+ for _, fi := range files {
+ if fi.IsDir() {
+ continue // Exclude folders if any
+ }
+
+ // Define a full path of filename
+ profileLocation := location + fi.Name()
+
+ // Check if filename has .json extension
+ if !common.IsValidExtension(profileLocation, ".jpg") {
+ continue
+ }
+
+ // Open image
+ file, e := os.Open(profileLocation)
+ if e != nil {
+ logger.Log(logger.Fields{"error": err, "location": location}).Error("Unable to open LCD image")
+ continue
+ }
+
+ // Decode the image
+ img, e := jpeg.Decode(file)
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": location}).Error("Unable to decode LCD image")
+ e = file.Close()
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": location}).Error("Unable to close LCD image")
+ }
+ continue
+ }
+
+ fileInfo, e := file.Stat()
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": location}).Error("Unable to get LCD image fileinfo")
+ e = file.Close()
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": location}).Error("Unable to close LCD image")
+ }
+ continue
+ }
+
+ imageType := strings.Split(fileInfo.Name(), ".")[0]
+ switch imageType {
+ case "cpu":
+ lcdImages[DisplayCPU] = img
+ case "gpu":
+ lcdImages[DisplayGPU] = img
+ case "liquid":
+ lcdImages[DisplayLiquid] = img
+ case "pump":
+ lcdImages[DisplayPump] = img
+ case "aio":
+ lcdImages[DisplayAllInOne] = img
+ }
+ }
+
+ // Load LCD font
+ fontBytes, e := os.ReadFile(fontLocation) // Provide the path to your .ttf font file
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": fontLocation}).Error("Unable to get LCD font")
+ return
+ }
+ fontParsed, e := freetype.ParseFont(fontBytes)
+ if e != nil {
+ logger.Log(logger.Fields{"error": e, "location": fontLocation}).Error("Unable to parse LCD font")
+ }
+
+ lcd := &LCD{
+ images: lcdImages,
+ font: fontParsed,
+ }
+ lcdConfiguration = *lcd
+}
+
+// GenerateScreenImage will generate LCD screen image with given value
+func GenerateScreenImage(imageType uint8, value, value1, value2 int) []byte {
+ mutex.Lock()
+ defer mutex.Unlock()
+ if img, ok := lcdConfiguration.images[imageType]; ok {
+ rgba := image.NewRGBA(img.Bounds())
+ draw.Draw(rgba, rgba.Bounds(), img, image.Point{}, draw.Src)
+
+ c := freetype.NewContext()
+ c.SetDPI(72)
+ c.SetFont(lcdConfiguration.font)
+ c.SetFontSize(220)
+ c.SetClip(rgba.Bounds())
+ c.SetDst(rgba)
+ c.SetSrc(image.NewUniform(color.RGBA{R: 255, G: 255, B: 253, A: 255}))
+
+ pt := freetype.Pt(135+int(c.PointToFixed(24)>>6), 280+int(c.PointToFixed(24)>>6))
+
+ switch imageType {
+ case DisplayLiquid, DisplayGPU, DisplayCPU:
+ {
+ pt = freetype.Pt(140+int(c.PointToFixed(24)>>6), 280+int(c.PointToFixed(24)>>6))
+ _, err := c.DrawString(strconv.Itoa(value), pt)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to generate LCD image")
+ return nil
+ }
+ }
+ case DisplayPump:
+ {
+ pt = freetype.Pt(90+int(c.PointToFixed(24)>>6), 280+int(c.PointToFixed(24)>>6))
+ _, err := c.DrawString(strconv.Itoa(value), pt)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to generate LCD image")
+ return nil
+ }
+ }
+ case DisplayAllInOne:
+ {
+ c.SetDPI(72)
+ c.SetFont(lcdConfiguration.font)
+ c.SetFontSize(100)
+ c.SetClip(rgba.Bounds())
+ c.SetDst(rgba)
+ c.SetSrc(image.NewUniform(color.RGBA{R: 255, G: 255, B: 253, A: 255}))
+
+ pt = freetype.Pt(120+int(c.PointToFixed(24)>>6), 200+int(c.PointToFixed(24)>>6))
+ _, err := c.DrawString(strconv.Itoa(value), pt)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to generate LCD image")
+ return nil
+ }
+
+ pt = freetype.Pt(270+int(c.PointToFixed(24)>>6), 200+int(c.PointToFixed(24)>>6))
+ _, err = c.DrawString(strconv.Itoa(value1), pt)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to generate LCD image")
+ return nil
+ }
+
+ pt = freetype.Pt(160+int(c.PointToFixed(24)>>6), 290+int(c.PointToFixed(24)>>6))
+ _, err = c.DrawString(strconv.Itoa(value2), pt)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to generate LCD image")
+ return nil
+ }
+ }
+ }
+
+ // Buff it and return
+ buffer := new(bytes.Buffer)
+ err := jpeg.Encode(buffer, rgba, nil)
+ if err != nil {
+ logger.Log(logger.Fields{"error": err}).Error("Unable to encode LCD image")
+ return nil
+ }
+ return buffer.Bytes()
+ }
+ return nil
+}
diff --git a/src/devices/linksystemhub/linksystemhub.go b/src/devices/linksystemhub/linksystemhub.go
index 6a5a4d1..0c22638 100644
--- a/src/devices/linksystemhub/linksystemhub.go
+++ b/src/devices/linksystemhub/linksystemhub.go
@@ -170,6 +170,7 @@ func Init(vendorId, productId uint16, serial string) *Device {
d := &Device{
dev: dev,
Template: "lsh.html",
+ VendorId: vendorId,
}
// Bootstrap
@@ -454,15 +455,17 @@ func (d *Device) UpdateSpeedProfile(channelId int, profile string) uint8 {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -472,6 +475,7 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
d.activeRgb = nil
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// getLiquidTemperature will fetch temperature from AIO device
diff --git a/src/devices/lncore/lncore.go b/src/devices/lncore/lncore.go
index 8cfe55a..67c6675 100644
--- a/src/devices/lncore/lncore.go
+++ b/src/devices/lncore/lncore.go
@@ -413,15 +413,17 @@ func (d *Device) getExternalLedDevice(index int) *ExternalLedDevice {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -431,10 +433,11 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
d.activeRgb = nil
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// UpdateExternalHubDeviceType will update a device type connected to the external-LED hub
-func (d *Device) UpdateExternalHubDeviceType(externalType int) int {
+func (d *Device) UpdateExternalHubDeviceType(externalType int) uint8 {
if d.DeviceProfile != nil {
if d.getExternalLedDevice(externalType) != nil {
d.DeviceProfile.ExternalHubDeviceType = externalType
@@ -454,7 +457,7 @@ func (d *Device) UpdateExternalHubDeviceType(externalType int) int {
}
// UpdateExternalHubDeviceAmount will update device amount connected to an external-LED hub and trigger RGB reset
-func (d *Device) UpdateExternalHubDeviceAmount(externalDevices int) int {
+func (d *Device) UpdateExternalHubDeviceAmount(externalDevices int) uint8 {
if d.DeviceProfile != nil {
if d.DeviceProfile.ExternalHubDeviceType > 0 {
d.DeviceProfile.ExternalHubDeviceAmount = externalDevices
diff --git a/src/devices/lnpro/lnpro.go b/src/devices/lnpro/lnpro.go
index a3d86e1..3f89e2d 100644
--- a/src/devices/lnpro/lnpro.go
+++ b/src/devices/lnpro/lnpro.go
@@ -425,15 +425,17 @@ func (d *Device) getExternalLedDevice(index int) *ExternalLedDevice {
}
// UpdateRgbProfile will update device RGB profile
-func (d *Device) UpdateRgbProfile(channelId int, profile string) {
+func (d *Device) UpdateRgbProfile(channelId int, profile string) uint8 {
if rgb.GetRgbProfile(profile) == nil {
logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile")
- return
+ return 0
}
if _, ok := d.Devices[channelId]; ok {
// Update channel with new profile
d.Devices[channelId].RGB = profile
+ } else {
+ return 0
}
d.DeviceProfile.RGBProfiles[channelId] = profile // Set profile
@@ -445,6 +447,7 @@ func (d *Device) UpdateRgbProfile(channelId int, profile string) {
}
}
d.setDeviceColor() // Restart RGB
+ return 1
}
// ResetRgb will reset the current rgb mode
@@ -461,7 +464,7 @@ func (d *Device) ResetRgb() {
}
// UpdateExternalHubDeviceType will update a device type connected to the external-LED hub
-func (d *Device) UpdateExternalHubDeviceType(portId, externalType int) int {
+func (d *Device) UpdateExternalHubDeviceType(portId, externalType int) uint8 {
if d.DeviceProfile != nil {
if externalType == 0 {
d.DeviceProfile.ExternalHubs[portId].ExternalHubDeviceType = externalType
@@ -494,7 +497,7 @@ func (d *Device) UpdateDeviceLabel(channelId int, label string) uint8 {
}
// UpdateExternalHubDeviceAmount will update device amount connected to an external-LED hub and trigger RGB reset
-func (d *Device) UpdateExternalHubDeviceAmount(portId, externalDevices int) int {
+func (d *Device) UpdateExternalHubDeviceAmount(portId, externalDevices int) uint8 {
if d.DeviceProfile != nil {
if _, ok := d.DeviceProfile.ExternalHubs[portId]; ok {
// Store current amount
diff --git a/src/server/requests/requests.go b/src/server/requests/requests.go
index 0af803f..57c239f 100755
--- a/src/server/requests/requests.go
+++ b/src/server/requests/requests.go
@@ -244,6 +244,46 @@ func ProcessChangeSpeed(r *http.Request) *Payload {
return &Payload{Message: "Unable to apply speed profile", Code: http.StatusOK, Status: 0}
}
+// ProcessLcdChange will process POST request from a client for LCD mode change
+func ProcessLcdChange(r *http.Request) *Payload {
+ req := &Payload{}
+ err := json.NewDecoder(r.Body).Decode(&req)
+ if err != nil {
+ logger.Log(map[string]interface{}{"error": err}).Error("Unable to decode JSON")
+ return &Payload{
+ Message: "Unable to validate your request. Please try again!",
+ Code: http.StatusOK,
+ Status: 0,
+ }
+ }
+
+ if req.Mode < 0 || req.Mode > 4 {
+ return &Payload{Message: "Invalid LCD mode", Code: http.StatusOK, Status: 0}
+ }
+
+ if len(req.DeviceId) < 0 {
+ return &Payload{Message: "Non-existing device", Code: http.StatusOK, Status: 0}
+ }
+
+ if m, _ := regexp.MatchString("^[a-zA-Z0-9]+$", req.DeviceId); !m {
+ return &Payload{Message: "Non-existing device", Code: http.StatusOK, Status: 0}
+ }
+
+ if devices.GetDevice(req.DeviceId) == nil {
+ return &Payload{Message: "Non-existing device", Code: http.StatusOK, Status: 0}
+ }
+
+ // Run it
+ status := devices.UpdateDeviceLcd(req.DeviceId, req.Mode)
+ switch status {
+ case 1:
+ return &Payload{Message: "LCD mode successfully changed", Code: http.StatusOK, Status: 1}
+ case 2:
+ return &Payload{Message: "Unable to change LCD mode. Either LCD is offline or you do not have LCD", Code: http.StatusOK, Status: 0}
+ }
+ return &Payload{Message: "Unable to change lcd mode", Code: http.StatusOK, Status: 0}
+}
+
// ProcessLabelChange will process POST request from a client for label change
func ProcessLabelChange(r *http.Request) *Payload {
req := &Payload{}
@@ -367,9 +407,15 @@ func ProcessChangeColor(r *http.Request) *Payload {
}
// Run it
- devices.UpdateRgbProfile(req.DeviceId, req.ChannelId, req.Profile)
+ status := devices.UpdateRgbProfile(req.DeviceId, req.ChannelId, req.Profile)
- return &Payload{Message: "Device RGB profile is successfully changed", Code: http.StatusOK, Status: 1}
+ switch status {
+ case 0:
+ return &Payload{Message: "Unable to change device RGB profile", Code: http.StatusOK, Status: 0}
+ case 1:
+ return &Payload{Message: "Device RGB profile is successfully changed", Code: http.StatusOK, Status: 1}
+ }
+ return &Payload{Message: "Unable to change device RGB profile", Code: http.StatusOK, Status: 0}
}
// ProcessExternalHubDeviceType will process POST request from a client for external-LED hub
diff --git a/src/server/server.go b/src/server/server.go
index 10d2e42..d3f53d1 100644
--- a/src/server/server.go
+++ b/src/server/server.go
@@ -212,6 +212,17 @@ func setDeviceLabel(w http.ResponseWriter, r *http.Request) {
resp.Send(w)
}
+// setDeviceLcd handles device LCD changes
+func setDeviceLcd(w http.ResponseWriter, r *http.Request) {
+ request := requests.ProcessLcdChange(r)
+ resp := &Response{
+ Code: request.Code,
+ Status: request.Status,
+ Message: request.Message,
+ }
+ resp.Send(w)
+}
+
// setManualDeviceSpeed handles manual device speed changes
func setManualDeviceSpeed(w http.ResponseWriter, r *http.Request) {
request := requests.ProcessManualChangeSpeed(r)
@@ -412,6 +423,8 @@ func setRoutes() *mux.Router {
HandlerFunc(setExternalHubDeviceAmount)
r.Methods(http.MethodPost).Path("/api/label").
HandlerFunc(setDeviceLabel)
+ r.Methods(http.MethodPost).Path("/api/lcd").
+ HandlerFunc(setDeviceLcd)
if config.GetConfig().Frontend {
// Frontend
diff --git a/src/temperatures/temperatures.go b/src/temperatures/temperatures.go
index 4e94562..6035158 100644
--- a/src/temperatures/temperatures.go
+++ b/src/temperatures/temperatures.go
@@ -108,17 +108,17 @@ var (
Sensor: 2,
Profiles: []TemperatureProfile{
{Id: 1, Min: 0, Max: 30, Mode: 0, Fans: 30, Pump: 50},
- {Id: 1, Min: 30, Max: 32, Mode: 0, Fans: 30, Pump: 50},
- {Id: 2, Min: 32, Max: 34, Mode: 0, Fans: 40, Pump: 50},
- {Id: 3, Min: 34, Max: 36, Mode: 0, Fans: 40, Pump: 50},
- {Id: 4, Min: 36, Max: 38, Mode: 0, Fans: 40, Pump: 60},
- {Id: 5, Min: 38, Max: 40, Mode: 0, Fans: 40, Pump: 60},
- {Id: 6, Min: 40, Max: 42, Mode: 0, Fans: 50, Pump: 60},
- {Id: 7, Min: 42, Max: 44, Mode: 0, Fans: 60, Pump: 70},
- {Id: 8, Min: 44, Max: 46, Mode: 0, Fans: 70, Pump: 80},
- {Id: 9, Min: 46, Max: 48, Mode: 0, Fans: 80, Pump: 90},
- {Id: 10, Min: 48, Max: 50, Mode: 0, Fans: 90, Pump: 90},
- {Id: 11, Min: 50, Max: 60, Mode: 0, Fans: 100, Pump: 100}, // Critical
+ {Id: 2, Min: 30, Max: 32, Mode: 0, Fans: 30, Pump: 50},
+ {Id: 3, Min: 32, Max: 34, Mode: 0, Fans: 40, Pump: 50},
+ {Id: 4, Min: 34, Max: 36, Mode: 0, Fans: 40, Pump: 50},
+ {Id: 5, Min: 36, Max: 38, Mode: 0, Fans: 40, Pump: 60},
+ {Id: 6, Min: 38, Max: 40, Mode: 0, Fans: 40, Pump: 60},
+ {Id: 7, Min: 40, Max: 42, Mode: 0, Fans: 50, Pump: 60},
+ {Id: 8, Min: 42, Max: 44, Mode: 0, Fans: 60, Pump: 70},
+ {Id: 9, Min: 44, Max: 46, Mode: 0, Fans: 70, Pump: 80},
+ {Id: 10, Min: 46, Max: 48, Mode: 0, Fans: 80, Pump: 90},
+ {Id: 11, Min: 48, Max: 50, Mode: 0, Fans: 90, Pump: 90},
+ {Id: 12, Min: 50, Max: 60, Mode: 0, Fans: 100, Pump: 100}, // Critical
},
}
)
@@ -391,6 +391,15 @@ func GetNVIDIAGpuTemperature() float32 {
return 0
}
+// GetGpuTemperature will return GPU temperature
+func GetGpuTemperature() float32 {
+ temp := GetNVIDIAGpuTemperature()
+ if temp == 0 {
+ temp = GetAMDGpuTemperature()
+ }
+ return temp
+}
+
// getHwMonTemperature will return temperature for given entry
func getHwMonTemperature(hwmonDir string, entry os.DirEntry) float32 {
tempFile := filepath.Join(hwmonDir, entry.Name(), "temp1_input")
diff --git a/static/fonts/teko.ttf b/static/fonts/teko.ttf
new file mode 100644
index 0000000..d63db06
Binary files /dev/null and b/static/fonts/teko.ttf differ
diff --git a/static/img/lcd/aio.jpg b/static/img/lcd/aio.jpg
new file mode 100644
index 0000000..3f43b0e
Binary files /dev/null and b/static/img/lcd/aio.jpg differ
diff --git a/static/img/lcd/cpu.jpg b/static/img/lcd/cpu.jpg
new file mode 100644
index 0000000..3b4ceb2
Binary files /dev/null and b/static/img/lcd/cpu.jpg differ
diff --git a/static/img/lcd/fan.jpg b/static/img/lcd/fan.jpg
new file mode 100644
index 0000000..72724b7
Binary files /dev/null and b/static/img/lcd/fan.jpg differ
diff --git a/static/img/lcd/gpu.jpg b/static/img/lcd/gpu.jpg
new file mode 100644
index 0000000..453b52a
Binary files /dev/null and b/static/img/lcd/gpu.jpg differ
diff --git a/static/img/lcd/liquid.jpg b/static/img/lcd/liquid.jpg
new file mode 100644
index 0000000..e3f7a52
Binary files /dev/null and b/static/img/lcd/liquid.jpg differ
diff --git a/static/img/lcd/pump.jpg b/static/img/lcd/pump.jpg
new file mode 100644
index 0000000..267a6b9
Binary files /dev/null and b/static/img/lcd/pump.jpg differ
diff --git a/static/js/overview.js b/static/js/overview.js
index a2d9bef..a0d76f7 100644
--- a/static/js/overview.js
+++ b/static/js/overview.js
@@ -184,6 +184,35 @@ document.addEventListener("DOMContentLoaded", function () {
});
});
+ $('.lcdMode').on('change', function () {
+ const deviceId = $("#deviceId").val();
+ const mode = $(this).val()
+
+ const pf = {};
+ pf["deviceId"] = deviceId;
+ pf["mode"] = parseInt(mode);
+
+ const json = JSON.stringify(pf, null, 2);
+
+ $.ajax({
+ url: '/api/lcd',
+ type: 'POST',
+ data: json,
+ cache: false,
+ success: function(response) {
+ try {
+ if (response.status === 1) {
+ toast.success(response.message);
+ } else {
+ toast.warning(response.message);
+ }
+ } catch (err) {
+ toast.warning(response.message);
+ }
+ }
+ });
+ });
+
$('#deviceSpeed').on('change', function () {
const deviceId = $("#deviceId").val();
const pf = {};
diff --git a/web/cc.html b/web/cc.html
index 99e5211..e993033 100644
--- a/web/cc.html
+++ b/web/cc.html
@@ -85,6 +85,10 @@
LCD
++ +
+