Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BaseConfigUpdateEvent and handle base configuration updates #747

Merged

Conversation

shauneccles
Copy link
Member

@shauneccles shauneccles commented Feb 16, 2024

This pull request adds a new event called BaseConfigUpdateEvent and implements the handling of base configuration updates.

It also includes the necessary changes to setup visualisation events when the base configuration is updated without restarting.

Adds snackbars when base config is updated.

Summary by CodeRabbit

  • New Features
    • Introduced the ability to update certain core configuration settings without needing to restart the application.
    • Added support for dynamic visualization configuration updates.
  • Refactor
    • Enhanced configuration update handling with new methods and event listeners for more seamless user experiences.

Copy link
Contributor

coderabbitai bot commented Feb 16, 2024

Walkthrough

The recent updates focus on enhancing configuration management within the LedFX application. Key improvements include the introduction of mechanisms to dynamically update configurations without necessitating a restart for certain core settings. A new event class, BaseConfigUpdateEvent, has been introduced to manage these updates efficiently. Additionally, the system now distinguishes between configuration keys that require a restart and those that do not, streamlining the process for visualisation configuration updates and ensuring a smoother user experience.

Changes

File(s) Change Summary
ledfx/api/config.py Added handling for configuration updates with potential restarts, including new methods for update management.
ledfx/config.py Introduced core config keys exempt from restart requirements and added keys for visualization configuration.
ledfx/core.py Enhanced visualisation configuration handling with new methods and event listeners.
ledfx/events.py Added BaseConfigUpdateEvent class for managing base configuration update events.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between c502149 and 6771a1e.
Files selected for processing (4)
  • ledfx/api/config.py (3 hunks)
  • ledfx/config.py (1 hunks)
  • ledfx/core.py (5 hunks)
  • ledfx/events.py (2 hunks)
Additional comments: 6
ledfx/events.py (1)
  • 170-178: The implementation of BaseConfigUpdateEvent is concise and follows the established pattern for event classes in this file. It correctly inherits from the Event class and initializes with the BASE_CONFIG_UPDATE event type. This addition is crucial for supporting dynamic configuration updates as outlined in the PR objectives.
ledfx/api/config.py (1)
  • 220-273: The update_config method is well-structured and effectively updates various parts of the configuration, including audio, WLED preferences, melbanks, and core configuration. It also handles the special case for nested dictionaries in wled_preferences. The method concludes by firing a BaseConfigUpdateEvent if there are changes in the core configuration. This method is crucial for applying configuration updates dynamically. Ensure that changes to wled_preferences are thoroughly tested, especially the handling of nested dictionaries, to avoid potential issues with partial updates.
ledfx/core.py (2)
  • 123-143: The addition of handle_base_configuration_update method and its integration with the BASE_CONFIG_UPDATE event listener is a significant improvement. This method ensures that visualisation configuration updates are dynamically applied by re-setting up visualisation events. This aligns with the PR's objectives to enhance the application's adaptability and user interface in response to configuration changes. Ensure that the condition in line 141 is correctly identifying changes to visualization-related configuration keys.
  • 232-244: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [189-241]

The modifications to setup_visualisation_events to remove existing listeners before setting up new ones are crucial for avoiding duplicate listeners and potential performance issues. This change ensures that visualisation updates are handled efficiently, especially after configuration changes that affect visualisation settings. It's a good practice to ensure that event listeners are managed properly to prevent memory leaks and ensure that updates are processed as expected.

ledfx/config.py (2)
  • 38-46: The addition of CORE_CONFIG_KEYS_NO_RESTART is aligned with the PR's objective to handle certain configuration changes dynamically without requiring a restart. This list should be carefully reviewed and updated as the application evolves to ensure it includes all configuration keys that can be updated on the fly. It's also important to ensure that the application logic elsewhere properly checks this list when handling configuration updates.
  • 48-51: The VISUALISATION_CONFIG_KEYS list is introduced to manage visualization-related configuration updates dynamically. This is a good approach to segregate visualization-specific settings, which can enhance the user experience by allowing for immediate updates to visualization parameters without needing a restart. Ensure that the visualization event listeners and the related logic are correctly updated to check against this list when handling configuration changes.

Comment on lines +186 to +217

try:
config = await request.json()
except JSONDecodeError:
return await self.json_decode_error()
except Exception as e:
return await self.generic_error(str(e))

audio_config = validate_and_trim_config(
config.pop("audio", {}),
AudioInputSource.AUDIO_CONFIG_SCHEMA.fget(),
"audio",
)
wled_config = validate_and_trim_config(
config.pop("wled_preferences", {}),
WLED_CONFIG_SCHEMA,
"wled_preferences",
)
melbanks_config = validate_and_trim_config(
config.pop("melbanks", {}), Melbanks.CONFIG_SCHEMA, "melbanks"
try:
self.update_config(config)
self._ledfx.events.fire_event(BaseConfigUpdateEvent(config))
save_config(
config=self._ledfx.config, config_dir=self._ledfx.config_dir
)
core_config = validate_and_trim_config(
config, CORE_CONFIG_SCHEMA, "core"
need_restart = self.check_need_restart(config)
if need_restart:
# Ugly - return success to frontend before restarting
try:
return await self.request_success(
type="success",
message="LedFx is restarting to apply the new configuration",
)
finally:
self._ledfx.loop.call_soon_threadsafe(self._ledfx.stop, 4)
return await self.request_success(
type="success", message="Configuration Updated"
)
except (KeyError, vol.MultipleInvalid) as msg:
error_message = f"Error updating config: {msg}"
_LOGGER.warning(error_message)
return await self.invalid_request(error_message)
except Exception as e:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modifications to the put method to handle configuration updates and potential restarts are well-implemented. The method now correctly updates the configuration, fires a BaseConfigUpdateEvent, and checks if a restart is necessary based on the updated configuration. This aligns with the PR's objectives to enhance configuration management and minimize disruptions caused by restarts. However, consider adding more detailed logging for each step to improve traceability and debugging.

+ _LOGGER.info("Updating LedFx configuration.")
+ _LOGGER.info("Firing BaseConfigUpdateEvent.")
+ _LOGGER.info("Checking if restart is needed.")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
try:
config = await request.json()
except JSONDecodeError:
return await self.json_decode_error()
except Exception as e:
return await self.generic_error(str(e))
audio_config = validate_and_trim_config(
config.pop("audio", {}),
AudioInputSource.AUDIO_CONFIG_SCHEMA.fget(),
"audio",
)
wled_config = validate_and_trim_config(
config.pop("wled_preferences", {}),
WLED_CONFIG_SCHEMA,
"wled_preferences",
)
melbanks_config = validate_and_trim_config(
config.pop("melbanks", {}), Melbanks.CONFIG_SCHEMA, "melbanks"
try:
self.update_config(config)
self._ledfx.events.fire_event(BaseConfigUpdateEvent(config))
save_config(
config=self._ledfx.config, config_dir=self._ledfx.config_dir
)
core_config = validate_and_trim_config(
config, CORE_CONFIG_SCHEMA, "core"
need_restart = self.check_need_restart(config)
if need_restart:
# Ugly - return success to frontend before restarting
try:
return await self.request_success(
type="success",
message="LedFx is restarting to apply the new configuration",
)
finally:
self._ledfx.loop.call_soon_threadsafe(self._ledfx.stop, 4)
return await self.request_success(
type="success", message="Configuration Updated"
)
except (KeyError, vol.MultipleInvalid) as msg:
error_message = f"Error updating config: {msg}"
_LOGGER.warning(error_message)
return await self.invalid_request(error_message)
except Exception as e:
try:
config = await request.json()
except JSONDecodeError:
return await self.json_decode_error()
except Exception as e:
return await self.generic_error(str(e))
try:
_LOGGER.info("Updating LedFx configuration.")
self.update_config(config)
_LOGGER.info("Firing BaseConfigUpdateEvent.")
self._ledfx.events.fire_event(BaseConfigUpdateEvent(config))
save_config(
config=self._ledfx.config, config_dir=self._ledfx.config_dir
)
_LOGGER.info("Checking if restart is needed.")
need_restart = self.check_need_restart(config)
if need_restart:
# Ugly - return success to frontend before restarting
try:
return await self.request_success(
type="success",
message="LedFx is restarting to apply the new configuration",
)
finally:
self._ledfx.loop.call_soon_threadsafe(self._ledfx.stop, 4)
return await self.request_success(
type="success", message="Configuration Updated"
)
except (KeyError, vol.MultipleInvalid) as msg:
error_message = f"Error updating config: {msg}"
_LOGGER.warning(error_message)
return await self.invalid_request(error_message)
except Exception as e:

Comment on lines +275 to +297
def check_need_restart(self, config):
"""
Checks if a restart is needed based on the provided configuration.

Args:
config (dict): The configuration to be checked.

Returns:
bool: True if a restart is needed, False otherwise.
"""
core_config = validate_and_trim_config(
config, CORE_CONFIG_SCHEMA, "core"
)

# If core_config is empty, no restart is needed
if not core_config:
return False

need_restart = True
if any(key in core_config for key in CORE_CONFIG_KEYS_NO_RESTART):
need_restart = False

return need_restart
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check_need_restart method correctly assesses whether a restart is necessary based on the provided configuration. It uses CORE_CONFIG_KEYS_NO_RESTART to determine if any of the updated keys allow for dynamic updates without a restart. This logic is essential for minimizing disruptions. However, consider enhancing this method to log which specific configuration changes are triggering a restart, as this could aid in debugging and user understanding.

+ _LOGGER.debug(f"Checking need for restart with updated config keys: {list(config.keys())}")
+ if need_restart:
+     _LOGGER.info("A restart is required due to changes in the core configuration.")
+ else:
+     _LOGGER.info("No restart is required for the applied configuration changes.")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def check_need_restart(self, config):
"""
Checks if a restart is needed based on the provided configuration.
Args:
config (dict): The configuration to be checked.
Returns:
bool: True if a restart is needed, False otherwise.
"""
core_config = validate_and_trim_config(
config, CORE_CONFIG_SCHEMA, "core"
)
# If core_config is empty, no restart is needed
if not core_config:
return False
need_restart = True
if any(key in core_config for key in CORE_CONFIG_KEYS_NO_RESTART):
need_restart = False
return need_restart
def check_need_restart(self, config):
"""
Checks if a restart is needed based on the provided configuration.
Args:
config (dict): The configuration to be checked.
Returns:
bool: True if a restart is needed, False otherwise.
"""
core_config = validate_and_trim_config(
config, CORE_CONFIG_SCHEMA, "core"
)
# If core_config is empty, no restart is needed
if not core_config:
return False
need_restart = True
if any(key in core_config for key in CORE_CONFIG_KEYS_NO_RESTART):
need_restart = False
_LOGGER.debug(f"Checking need for restart with updated config keys: {list(config.keys())}")
if need_restart:
_LOGGER.info("A restart is required due to changes in the core configuration.")
else:
_LOGGER.info("No restart is required for the applied configuration changes.")
return need_restart

Copy link
Contributor

@bigredfrog bigredfrog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rough testing for changes in led count to front end on aggressive matrix and global transitions

@shauneccles shauneccles merged commit cbf71f8 into LedFx:main Feb 17, 2024
20 checks passed
@shauneccles shauneccles deleted the fix/handle_websocket_maxlength_changes branch February 17, 2024 00:22
Copy link

sentry-io bot commented Feb 24, 2024

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

  • ‼️ KeyboardInterrupt asyncio.tasks in __sleep0 View Issue

Did you find this useful? React with a 👍 or 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants