8989 --dbus-signals|--no-dbus-signals
9090 enable D-Bus ddcutil-service VDU-connectivity-change signals
9191 ``--dbus-signals`` is the default
92+ --protect-nvram|--no-protect-nvram
93+ alter options and defaults to minimize VDU NVRAM writes
9294 --create-config-files
9395 if they do not exist, create template config INI files
9496 in $HOME/.config/vdu_controls/
811813 QSplashScreen , QPushButton , QProgressBar , QComboBox , QSystemTrayIcon , QMenu , QStyle , QTextEdit , QDialog , QTabWidget , \
812814 QCheckBox , QPlainTextEdit , QGridLayout , QSizePolicy , QAction , QMainWindow , QToolBar , QToolButton , QFileDialog , \
813815 QWidgetItem , QScrollArea , QGroupBox , QFrame , QSplitter , QSpinBox , QDoubleSpinBox , QInputDialog , QStatusBar , qApp , QShortcut , \
814- QDesktopWidget
816+ QDesktopWidget , QSpacerItem
815817
816818APPNAME = "VDU Controls"
817819VDU_CONTROLS_VERSION = '2.1.0'
@@ -2016,6 +2018,7 @@ def mousePressEvent(self, event):
20162018 '60' : VcpCapability ('60' , QT_TR_NOOP ('input source' ), SNC , causes_config_change = True ),
20172019 'D6' : VcpCapability ('D6' , QT_TR_NOOP ('power mode' ), SNC , causes_config_change = True ),
20182020 'CC' : VcpCapability ('CC' , QT_TR_NOOP ('OSD language' ), SNC ),
2021+ '14' : VcpCapability ('14' , QT_TR_NOOP ('color preset' ), SNC ),
20192022 '0C' : VcpCapability ('0C' , QT_TR_NOOP ('color temperature' ), CON , icon_source = COLOR_TEMPERATURE_SVG , enabled = True ),
20202023 }}
20212024
@@ -2187,7 +2190,7 @@ class ConfOption(Enum): # TODO Enum is used for convenience for scope/iteration
21872190 conf_type = CI .TYPE_LONG_TEXT , cmdline_arg = 'DISALLOWED' ,
21882191 tip = QT_TR_NOOP ('override/cache for ddcutil capabilities text' ))
21892192 PROTECT_NVRAM_ENABLED = conf_opt_def (cname = QT_TR_NOOP ('protect-nvram' ), default = "yes" , restart = True ,
2190- tip = QT_TR_NOOP ('alter options and defaults to maximise VDU NVRAM lifespan ' ))
2193+ tip = QT_TR_NOOP ('alter options and defaults to minimize VDU NVRAM writes ' ))
21912194 UNKNOWN = conf_opt_def (cname = "UNKNOWN" , section = CI .UNKNOWN_SECTION , conf_type = CI .TYPE_BOOL , cmdline_arg = 'DISALLOWED' , tip = '' )
21922195
21932196 def __init__ (self , conf_name : str , section : str , cmdline_arg : str , conf_type : str , default : str | None ,
@@ -4568,7 +4571,7 @@ def update_location(self, location: GeoLocation) -> None:
45684571
45694572class PresetChooseTransitionWidget (QWidget ):
45704573
4571- def __init__ (self , protect_nvram : bool ) -> None :
4574+ def __init__ (self ) -> None :
45724575 super ().__init__ ()
45734576 layout = QHBoxLayout ()
45744577 self .setLayout (layout )
@@ -4577,7 +4580,6 @@ def __init__(self, protect_nvram: bool) -> None:
45774580 self .button_menu = QMenu ()
45784581 self .transition_type = PresetTransitionFlag .NONE
45794582 self .is_setting = False
4580- self .protect_nvram = protect_nvram
45814583
45824584 for transition_type in PresetTransitionFlag .ALWAYS .component_values ():
45834585 action = QAction (transition_type .description (), self .button_menu )
@@ -4598,12 +4600,10 @@ def __init__(self, protect_nvram: bool) -> None:
45984600 def update_value (self , checked : bool ) -> None :
45994601 if self .is_setting :
46004602 return
4601- if checked and self . protect_nvram :
4603+ if checked :
46024604 alert = MessageBox (QMessageBox .Warning )
4603- alert .setText (tr ('Transtions have been deprecated to protect VDU NVRAM.' ))
4604- alert .setInformativeText ('Setting this transition will have no effect '
4605- 'until protect-nvram is disabled in the Settings-Dialog.\n \n '
4606- 'Transitions are slated for removal, please '
4605+ alert .setText (tr ('Transitions have been deprecated to minimize wear on VDU NVRAM.' ))
4606+ alert .setInformativeText ('Transitions are slated for removal, please '
46074607 'contact the developer if you wish to retain them.' )
46084608 alert .exec ()
46094609 for act in self .button_menu .actions ():
@@ -5109,8 +5109,11 @@ def __init__(self, main_controller: VduAppController, main_config: VduControlsCo
51095109 self .editor_layout .addWidget (self .controls_title_widget )
51105110 self .editor_layout .addWidget (self .editor_controls_widget )
51115111
5112- self .editor_transitions_widget = PresetChooseTransitionWidget (self .main_config .is_set (ConfOption .PROTECT_NVRAM_ENABLED ))
5113- self .editor_layout .addWidget (self .editor_transitions_widget )
5112+ self .editor_transitions_widget = PresetChooseTransitionWidget ()
5113+ if self .main_config .is_set (ConfOption .PROTECT_NVRAM_ENABLED ):
5114+ self .editor_layout .addItem (QSpacerItem (1 ,10 ))
5115+ else :
5116+ self .editor_layout .addWidget (self .editor_transitions_widget )
51145117
51155118 self .editor_trigger_widget = PresetChooseElevationWidget (self .main_config )
51165119 self .editor_layout .addWidget (self .editor_trigger_widget )
@@ -6203,7 +6206,7 @@ class LuxAutoWorker(WorkerThread): # Why is this so complicated?
62036206
62046207 _lux_dialog_message_qtsignal = pyqtSignal (str , int , MsgDestination )
62056208
6206- def __init__ (self , auto_controller : LuxAutoController , single_shot : bool = False ) -> None :
6209+ def __init__ (self , auto_controller : LuxAutoController , single_shot : bool = False , protect_nvram : bool = True ) -> None :
62076210 super ().__init__ (task_body = self ._adjust_for_lux , task_finished = self ._adjust_for_lux_finished )
62086211 self .single_shot = single_shot # Called for an on-demand single time assessment with immediate effect.
62096212 self .main_controller = auto_controller .main_controller
@@ -6228,7 +6231,12 @@ def get_prop(prop: str, fallback: bool | int | float | str) -> bool | int | floa
62286231 self .sensitivity_percent = get_prop ('interpolation-sensitivity-percent' , fallback = 10 )
62296232 self .convergence_divisor = get_prop ('convergence-divisor' , fallback = 2 )
62306233 self .step_pause_millis = get_prop ('step-pause-millis' , fallback = 100 )
6231- self .max_brightness_jump = get_prop ('max-brightness-jump' , fallback = 100 )
6234+ if protect_nvram :
6235+ log_info ("LuxAutoWorker: protect-nvram enabled, ignoring max-brightness-jump" )
6236+ self .max_brightness_jump = 100
6237+ else :
6238+ self .max_brightness_jump = get_prop ('max-brightness-jump' , 20 )
6239+ log_warning (f"LuxAutoWorker: protect-nvram={ protect_nvram } max-brightness-jump={ self .max_brightness_jump } " )
62326240 self ._lux_dialog_message_qtsignal .connect (LuxDialog .lux_dialog_message )
62336241 self ._lux_dialog_message_qtsignal .connect (self .main_controller .main_window .status_message )
62346242 self .status_message (f"{ TIMER_RUNNING_SYMBOL } 00:00" , 0 , MsgDestination .COUNTDOWN )
@@ -6279,6 +6287,7 @@ def stepping_brightness(self, lux_config: LuxConfig, lux_meter: LuxMeterDevice)
62796287 change_count , last_change_count = 0 , - 1
62806288 start_of_cycle = True
62816289 profile_preset_name = None
6290+ vdu_changes_count = {}
62826291 while change_count != last_change_count : # while brightness changing
62836292 last_change_count = change_count
62846293 if metered_lux := lux_meter .get_value ():
@@ -6297,10 +6306,12 @@ def stepping_brightness(self, lux_config: LuxConfig, lux_meter: LuxMeterDevice)
62976306 profile_brightness , profile_preset_name = self .determine_brightness (vdu_sid , smoothed_lux , lux_profile )
62986307 if self .step_one_vdu (vdu_sid , profile_brightness , profile_preset_name , lux_summary_text , start_of_cycle ):
62996308 change_count += 1
6309+ vdu_changes_count [vdu_sid ] = vdu_changes_count .get (vdu_sid , 0 ) + 1
6310+ else :
6311+ log_debug (f"LuxAutoWorker: finished { vdu_sid = } VCP-changes={ vdu_changes_count .get (vdu_sid , 0 )} )" )
63006312 start_of_cycle = False
63016313 self .doze (self .step_pause_millis / 1000.0 ) # Let i2c settle down, then continue - TODO is this really necessary?
63026314 if change_count != 0 : # If any work was done in previous steps, finish up the remaining tasks
6303- log_info (f"LuxAutoWorker: stepping completed { change_count } VCP-changes, { profile_preset_name = } " )
63046315 self .status_message (tr ("Brightness adjustment completed" ), timeout = 5000 )
63056316 if profile_preset_name is not None : # if a point had a Preset attached, activate it now
63066317 # Restoring the Preset's non-brightness settings. Invoke now, so it will happen in this thread's sleep period.
@@ -6311,6 +6322,7 @@ def stepping_brightness(self, lux_config: LuxConfig, lux_meter: LuxMeterDevice)
63116322 scheduled_activity = True )
63126323 else : # No work done, no adjustment necessary
63136324 self .status_message (f"{ SUN_SYMBOL } { SUCCESS_SYMBOL } " , timeout = 3000 )
6325+ log_info (f"LuxAutoWorker: adjustments completed { change_count } VCP-changes, { profile_preset_name = } " )
63146326
63156327 def step_one_vdu (self , vdu_sid : VduStableId , profile_brightness : int , profile_preset_name : str | None ,
63166328 lux_summary_text : str , first_step : bool ) -> bool :
0 commit comments