Skip to content

Commit 493d770

Browse files
Add lights support for Nook Glowlight 4 Plus (bnrv1300)
Split bnrv1300 out of the NOOK_GL4 device ID into a new NOOK_GL4PLUS ID with a dedicated NookGL4plusController. Brightness is set via Settings.System (SELinux blocks direct sysfs access from the app process); warmth is set via `su -c echo` to the lm3630a_led color sysfs node (0=cold, 10=warm), which requires the user to grant root access once via Magisk. Fixes koreader/koreader#14574 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent dc24a50 commit 493d770

3 files changed

Lines changed: 97 additions & 3 deletions

File tree

app/src/main/java/org/koreader/launcher/device/DeviceInfo.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ object DeviceInfo {
7878
NABUK,
7979
NOOK,
8080
NOOK_GL4,
81+
NOOK_GL4PLUS,
8182
NOOK_GLPLUS,
8283
OBOOK_P10D,
8384
OBOOK_P78D,
@@ -358,9 +359,13 @@ object DeviceInfo {
358359
MANUFACTURER == "onyx" && MODEL == "nabukreg_hd"
359360
-> Id.NABUK
360361

361-
// Nook Glowlight 4 (4/4e/4plus)
362-
(MANUFACTURER == "barnesandnoble")
363-
&& (MODEL == "bnrv1000" || MODEL == "bnrv1100" || MODEL == "bnrv1300")
362+
// Nook Glowlight 4 Plus
363+
MANUFACTURER == "barnesandnoble" && MODEL == "bnrv1300"
364+
-> Id.NOOK_GL4PLUS
365+
366+
// Nook Glowlight 4 / 4e
367+
MANUFACTURER == "barnesandnoble"
368+
&& (MODEL == "bnrv1000" || MODEL == "bnrv1100")
364369
-> Id.NOOK_GL4
365370

366371
// Nook Glowlight plus 7.8" (2019)

app/src/main/java/org/koreader/launcher/device/LightsFactory.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ object LightsFactory {
106106
logController("TolinoNTXNoWarmth")
107107
TolinoNtxNoWarmthController()
108108
}
109+
DeviceInfo.Id.NOOK_GL4PLUS,
110+
-> {
111+
logController("NookGL4plus")
112+
NookGL4plusController()
113+
}
109114
DeviceInfo.Id.NOOK_GL4,
110115
DeviceInfo.Id.TOLINO_EPOS2,
111116
-> {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.koreader.launcher.device.lights
2+
3+
import android.app.Activity
4+
import android.provider.Settings
5+
import android.util.Log
6+
import org.koreader.launcher.device.LightsInterface
7+
8+
/* Controller for Nook Glowlight 4 Plus (bnrv1300) on Android 8.1.
9+
* Brightness via Settings.System (SELinux blocks direct sysfs writes from the app process).
10+
* Warmth via 'su -c echo' to sysfs color file (SELinux blocks direct ioctl from the app process).
11+
* see https://github.com/koreader/koreader/issues/14574
12+
*/
13+
class NookGL4plusController : LightsInterface {
14+
15+
companion object {
16+
private const val TAG = "Lights"
17+
private const val BRIGHTNESS_MAX = 100
18+
private const val WARMTH_MAX = 10
19+
private const val MIN = 0
20+
private const val COLOR_FILE = "/sys/class/backlight/lm3630a_led/color"
21+
}
22+
23+
private var currentWarmth: Int? = null
24+
25+
override fun getPlatform(): String = "nook"
26+
override fun hasFallback(): Boolean = false
27+
override fun hasWarmth(): Boolean = true
28+
override fun needsPermission(): Boolean = false
29+
override fun hasStandaloneWarmth(): Boolean = false
30+
31+
override fun enableFrontlightSwitch(activity: Activity): Int = 1
32+
33+
override fun getBrightness(activity: Activity): Int {
34+
return try {
35+
Settings.System.getInt(activity.applicationContext.contentResolver,
36+
Settings.System.SCREEN_BRIGHTNESS)
37+
} catch (e: Exception) {
38+
Log.w(TAG, e.toString())
39+
0
40+
}
41+
}
42+
43+
override fun getWarmth(activity: Activity): Int {
44+
return currentWarmth ?: MIN
45+
}
46+
47+
override fun setBrightness(activity: Activity, brightness: Int) {
48+
if (brightness < MIN || brightness > BRIGHTNESS_MAX) {
49+
Log.w(TAG, "brightness value out of range: $brightness")
50+
return
51+
}
52+
Log.v(TAG, "Setting brightness to $brightness")
53+
try {
54+
Settings.System.putInt(activity.applicationContext.contentResolver,
55+
Settings.System.SCREEN_BRIGHTNESS, brightness)
56+
} catch (e: Exception) {
57+
Log.w(TAG, "$e")
58+
}
59+
}
60+
61+
override fun setWarmth(activity: Activity, warmth: Int) {
62+
if (warmth < MIN || warmth > WARMTH_MAX) {
63+
Log.w(TAG, "warmth value out of range: $warmth")
64+
return
65+
}
66+
Log.v(TAG, "Setting warmth to $warmth of $WARMTH_MAX")
67+
try {
68+
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "echo $warmth > $COLOR_FILE"))
69+
val exitCode = process.waitFor()
70+
if (exitCode == 0) {
71+
currentWarmth = warmth
72+
} else {
73+
Log.w(TAG, "su failed (exit $exitCode)")
74+
}
75+
} catch (e: Exception) {
76+
Log.w(TAG, "setWarmth: $e")
77+
}
78+
}
79+
80+
override fun getMinBrightness(): Int = MIN
81+
override fun getMaxBrightness(): Int = BRIGHTNESS_MAX
82+
override fun getMinWarmth(): Int = MIN
83+
override fun getMaxWarmth(): Int = WARMTH_MAX
84+
}

0 commit comments

Comments
 (0)