Skip to content

Commit

Permalink
Brightness as light
Browse files Browse the repository at this point in the history
  • Loading branch information
infeeeee committed Sep 21, 2024
1 parent ac7f447 commit 024b4f3
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 112 deletions.
288 changes: 180 additions & 108 deletions IoTuring/Entity/Deployments/Brightness/Brightness.py
Original file line number Diff line number Diff line change
@@ -1,149 +1,221 @@
from IoTuring.Entity.Entity import Entity
from pathlib import Path
from IoTuring.Entity.Entity import Entity
from IoTuring.Configurator.MenuPreset import MenuPreset
from IoTuring.Entity.EntityData import EntitySensor, EntityCommand
from IoTuring.MyApp.SystemConsts.OperatingSystemDetection import OperatingSystemDetection as OsD
from IoTuring.Entity.ValueFormat import ValueFormatterOptions
import subprocess
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD
from IoTuring.MyApp.SystemConsts import DesktopEnvironmentDetection as De
from IoTuring.Entity.ValueFormat import ValueFormatterOptions


import re
import os
import sys


KEY = 'brightness'
KEY_STATE = 'brightness_state'
KEY_CMD = "command"
KEY_STATE = "state"
KEY_BRIGHTNESS = "value"

CONFIG_KEY_GPU = "gpu"


class BrightnessCmds:
def __init__(
self,
scale: float = 100,
decimals: int = 0,
set_command: str = "{}",
get_command: str = "",
) -> None:
self.scale = scale
self.decimals = decimals
self.set_command = set_command
self.get_command = get_command

def Set(self, value: int) -> None:
if not 0 <= value <= 255:
raise Exception("Invalid value")
scaled_value = (value / 255) * self.scale
scaled_value = round(scaled_value, self.decimals)
scaled_value = int(scaled_value) if self.decimals == 0 else scaled_value
self._SetValue(value_str=str(scaled_value))

def _SetValue(self, value_str: str) -> None:
command = self.set_command.format(value_str)
OsD.RunCommand(command, shell=True)

def Get(self) -> int:
scaled_value = float(self._GetValue())
value = (scaled_value / self.scale) * 255
return int(value)

def _GetValue(self) -> str:
value_str = OsD.RunCommand(self.get_command).stdout
return value_str


class Brightness_Macos(BrightnessCmds):
def __init__(self) -> None:
super().__init__(
scale=1,
decimals=2,
set_command="brightness {}",
get_command="brightness -l",
)

def _GetValue(self) -> str:
stdout = super()._GetValue()
brightness = re.findall("display 0: brightness.*$", stdout)[0][22:30]
return brightness


class Brightness_Win(BrightnessCmds):
def __init__(self, monitor_id: int = 0) -> None:
super().__init__()

import pythoncom
import wmi

pythoncom.CoInitialize()
self.monitor_id = monitor_id
self.wmi = wmi.WMI(namespace="wmi")

def _SetValue(self, value_str: str) -> None:
self.wmi.WmiMonitorBrightnessMethods()[self.monitor_id].WmiSetBrightness(
int(value_str), 0
)

def _GetValue(self) -> str:
return self.wmi.WmiMonitorBrightness()[self.monitor_id].CurrentBrightness

CONFIG_KEY_GPU = 'gpu'

VALUEFORMATTER_OPTIONS_PERCENT = ValueFormatterOptions(
ValueFormatterOptions.TYPE_PERCENTAGE)
class Brightness_Linux_ACPI(BrightnessCmds):
def __init__(self, configuredGPU: str) -> None:
self.gpu_path = Path(f"/sys/class/backlight/{configuredGPU}")
scale = int(self.get_from_file("max_brightness"))
super().__init__(scale=scale)

def _SetValue(self, value_str: str) -> None:
with open(self.gpu_path.joinpath("brightness"), "w") as file:
file.write(f"{value_str}\n")

def _GetValue(self) -> str:
return self.get_from_file("brightness")

def get_from_file(self, file_name: str) -> str:
with open(self.gpu_path.joinpath(file_name), "r") as file:
content = file.read()
return content.strip("\n")


class Brightness_Linux_Gnome(BrightnessCmds):
def __init__(self) -> None:
dbus_command = " ".join(
[
"gdbus call --session",
"--dest org.gnome.SettingsDaemon.Power",
"--object-path /org/gnome/SettingsDaemon/Power",
"--method org.freedesktop.DBus.Properties.{}",
"org.gnome.SettingsDaemon.Power.Screen Brightness",
]
)

super().__init__(
set_command=dbus_command.format("Set") + ' "<int32 {}>"',
get_command=dbus_command.format("Get"),
)

def _GetValue(self) -> str:
stdout = super()._GetValue()
brightness = re.findall(r"<(\d*)", stdout)[0]
return brightness


class Brightness(Entity):
NAME = "Brightness"
ALLOW_MULTI_INSTANCE = True

brightness_cmds: BrightnessCmds

def Initialize(self):

def Initialize(self):
self.RegisterEntitySensor(
EntitySensor(self, KEY_STATE,
supportsExtraAttributes=False,
valueFormatterOptions=VALUEFORMATTER_OPTIONS_PERCENT))



EntitySensor(
self,
KEY_STATE,
valueFormatterOptions=ValueFormatterOptions(
value_type=ValueFormatterOptions.TYPE_BINARY
),
)
)
self.RegisterEntitySensor(EntitySensor(self, KEY_BRIGHTNESS))
self.RegisterEntityCommand(
EntityCommand(
self,
KEY_CMD,
self.Callback,
connectedEntitySensorKeys=[KEY_STATE, KEY_BRIGHTNESS],
)
)

if OsD.IsWindows():
self.specificGetBrightness = self.GetBrightness_Win
self.specificSetBrightness = self.SetBrightness_Win
import wmi
import pythoncom
if OsD.IsMacos():
self.specificGetBrightness = self.GetBrightness_macOS
self.specificSetBrightness = self.SetBrightness_macOS
if OsD.IsLinux():
self.configuredGPU: str = self.GetFromConfigurations(CONFIG_KEY_GPU)
self.specificGetBrightness = self.GetBrightness_Linux
self.specificSetBrightness = self.SetBrightness_Linux
self.brightness_cmds = Brightness_Win()
elif OsD.IsMacos():
self.brightness_cmds = Brightness_Macos()
elif OsD.IsLinux():
if De.GetDesktopEnvironment() == "gnome":
self.brightness_cmds = Brightness_Linux_Gnome()
else:
configuredGPU: str = self.GetFromConfigurations(CONFIG_KEY_GPU)
self.brightness_cmds = Brightness_Linux_ACPI(configuredGPU)
else:
self.Log(self.Logger.LOG_WARNING,
'No brightness sensor available for this operating system')

self.RegisterEntityCommand(EntityCommand(self, KEY, self.Callback, KEY_STATE))
raise Exception("Unsupported OS!")

def Callback(self, message):
state = message.payload.decode("utf-8")
try:
# Value from 0 and 100
self.specificSetBrightness(int(state))
except ValueError: # Not int -> not a message for that function
return
except Exception as e:
raise Exception("Error during brightness set: " + str(e))

self.brightness_cmds.Set(int(state))

def Update(self):
brightness = self.specificGetBrightness()
self.SetEntitySensorValue(KEY_STATE, brightness)

def SetBrightness_macOS(self, value: str|int):
value = value/100 # cause I need it from 0 to 1
command = 'brightness ' + str(value)
subprocess.Popen(command.split(), stdout=subprocess.PIPE)

def SetBrightness_Linux(self, value):
# use acpi to controll backlight
with open(f'/sys/class/backlight/{self.configuredGPU}/brightness', 'w') as file:
file.write(f'{str(value)}\n')
value = self.brightness_cmds.Get()
self.SetEntitySensorValue(KEY_BRIGHTNESS, value)
self.SetEntitySensorValue(KEY_STATE, 1 if value > 0 else 0)

def SetBrightness_Win(self, value):
pythoncom.CoInitialize()
return wmi.WMI(namespace='wmi').WmiMonitorBrightnessMethods()[0].WmiSetBrightness(value, 0)

def GetBrightness_macOS(self) -> float:
try:
command = 'brightness -l'
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
stdout = process.communicate()[0]
brightness = re.findall(
'display 0: brightness.*$', str(stdout))[0][22:30]
brightness = float(brightness)*100 # is between 0 and 1
return brightness
except:
raise Exception(
'You sure you installed Brightness from Homebrew ? (else try checking you PATH)')

def GetBrightness_Linux(self) -> int:
# get the content of the file /sys/class/backlight/intel_backlight/brightness
with open(f'/sys/class/backlight/{self.configuredGPU}/brightness', 'r') as file:
content = file.read()
brightness = int(content.strip('\n'))
return self.ConvertBrightness(brightness, from_scale=255, to_scale=100)

def GetBrightness_Win(self) -> int:
return int(wmi.WMI(namespace='wmi').WmiMonitorBrightness()[0].CurrentBrightness)

def ConvertBrightness(self, value, from_scale=255, to_scale=100) -> int:
"""Function to convert brightness values from one scale to another.
Args:
value (int): The brightness value to convert.
from_scale (int): The original scale of the brightness value. Default is 255.
to_scale (int): The target scale of the brightness value. Default is 100.
Returns:
float: The converted brightness value.
"""
return int((value / from_scale) * to_scale)


@classmethod
@classmethod
def ConfigurationPreset(cls):
preset = MenuPreset()
if OsD.IsLinux():
# find all GPUs in /sys/class/backlight by listing all directories
gpus = [gpu for gpu in os.listdir('/sys/class/backlight') if os.path.isdir(f'/sys/class/backlight/{gpu}')]
gpus = [
gpu
for gpu in os.listdir("/sys/class/backlight")
if os.path.isdir(f"/sys/class/backlight/{gpu}")
]

preset.AddEntry(
name="which GPUs backlight you want to control?",
key=CONFIG_KEY_GPU,
question_type='select',
choices=gpus
)
question_type="select",
choices=gpus,
)
return preset

@classmethod
def CheckSystemSupport(cls):
if OsD.IsWindows(): #TODO needs to be tested
if OsD.IsWindows(): # TODO needs to be tested
# if wmi and pythoncom are not available, raise an exception
if ['wmi', 'pythoncom'] not in sys.modules:
if ["wmi", "pythoncom"] not in sys.modules:
raise Exception(
'Brightness not available, have you installed \'wmi\' on pip ?')
elif OsD.IsMacos(): #TODO needs to be tested
if not OsD.CommandExists('brightness'):
"Brightness not available, have you installed 'wmi' on pip ?"
)
elif OsD.IsMacos(): # TODO needs to be tested
if not OsD.CommandExists("brightness"):
raise Exception(
'Brightness not avaidlable, have you installed \'brightness\' on Homebrew ?')
"Brightness not avaidlable, have you installed 'brightness' on Homebrew ?"
)
elif OsD.IsLinux():
if not os.path.exists('/sys/class/backlight'): #TODO check if this dir always exists
if not Path("/sys/class/backlight").exists():
# TODO check if this dir always exists
raise Exception(
'Brightness not available, no backlight found in /sys/class/backlight')
"Brightness not available, no backlight found in /sys/class/backlight"
)
else:
raise NotImplementedError('Brightness not available for this OS')
raise cls.UnsupportedOsException()
9 changes: 9 additions & 0 deletions IoTuring/Entity/ValueFormat/ValueFormatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,18 @@ def _ParseValue(value, options: ValueFormatterOptions | None, includeUnit: bool)
return ValueFormatter.BitPerSecondFormatter(value, options, includeUnit)
elif valueType == ValueFormatterOptions.TYPE_BYTE_PER_SECOND:
return ValueFormatter.BytePerSecondFormatter(value, options, includeUnit)
elif valueType == ValueFormatterOptions.TYPE_BINARY:
return ValueFormatter.BinaryFormatter(value)
else:
return str(value)

@staticmethod
def BinaryFormatter(value):
if int(value) > 0:
return 1
else:
return 0

@staticmethod
def TimeFormatter(value, options: ValueFormatterOptions, includeUnit: bool):
# Get value in seconds, and adjustable
Expand Down
3 changes: 2 additions & 1 deletion IoTuring/Entity/ValueFormat/ValueFormatterOptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ValueFormatterOptions():
class ValueFormatterOptions():
TYPE_NONE = 0
TYPE_BYTE = 1
TYPE_TIME = 2
Expand All @@ -10,6 +10,7 @@ class ValueFormatterOptions():
TYPE_RADIOPOWER = 8
TYPE_BYTE_PER_SECOND = 9
TYPE_BIT_PER_SECOND = 10
TYPE_BINARY = 11

DO_NOT_TOUCH_DECIMALS = -1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,15 @@ TerminalButton:
name: Terminal Button
icon: mdi:console
custom_type: button
Brightness:
Brightness.*command:
command_topic_key:
- brightness_command_topic
- command_topic
name: Display Brightness
unit_of_measurement: "%"
icon: mdi:brightness-6
custom_type: number
custom_type: light
on_command_type: brightness
payload_on: "1"
payload_off: "0"
Brightness.*value:
state_topic_key: brightness_state_topic

0 comments on commit 024b4f3

Please sign in to comment.