Skip to content

Commit

Permalink
feat: Add support for user defined tokens (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
TilmanGriesel authored Dec 9, 2024
1 parent 6612973 commit 6b1a8e7
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 35 deletions.
9 changes: 5 additions & 4 deletions docs/features/graphite-theme-patcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Save the `graphite-theme-patcher.py` script to your `/config/scripts` directory.
**Quick Command:**

```bash
wget -O /config/scripts https://raw.githubusercontent.com/TilmanGriesel/graphite/refs/heads/main/extras/theme-patcher/graphite-theme-patcher.py
wget -O /config/scripts/graphite-theme-patcher.py https://raw.githubusercontent.com/TilmanGriesel/graphite/refs/heads/main/extras/theme-patcher/graphite-theme-patcher.py
```

---
Expand Down Expand Up @@ -170,9 +170,9 @@ python3 graphite-theme-patcher.py "255,158,0"
### Options

```bash
usage: graphite-theme-patcher.py [-h] [--version] [--token TOKEN] [--type {rgb,size,opacity,radius,generic}] [--theme THEME]
[--path PATH]
[value]
usage: graphite-theme-patcher.py [-h] [--version] [--token TOKEN] [--type {rgb,size,opacity,radius,generic}] [--theme THEME] [--path PATH] [--create] [value]

Update token values in theme files. (v1.2.0)

positional arguments:
value Value to set or 'None' to skip
Expand All @@ -185,6 +185,7 @@ options:
Type of token (default: rgb)
--theme THEME Theme name (default: graphite)
--path PATH Base path for themes directory (default: /config/themes)
--create Create token if it doesn't exist
```
---
Expand Down
69 changes: 38 additions & 31 deletions extras/theme-patcher/graphite-theme-patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@
Updates token values in theme files. Created for the Home Assistant Graphite theme.
Version: 1.1.0
Usage:
python3 theme-patcher.py <VALUE> --token <TOKEN_NAME> [--type <TOKEN_TYPE>] [--theme <THEME_NAME>] [--path <BASE_PATH>] [--version]
Examples:
python3 theme-patcher.py "255, 158, 0" --token "token-rgb-primary" --theme "graphite"
python3 theme-patcher.py "18px" --token "token-size-radius-large" --type "size" --theme "my_custom_theme"
python3 theme-patcher.py "0.14" --token "token-opacity-ripple-hover" --type "opacity" --path "/custom/themes"
python3 theme-patcher.py --version
Pass 'None' for VALUE to skip modification.
The --token argument is optional and defaults to "token-rgb-primary".
Default theme is "graphite".
Default path is "/config/themes".
"""

import sys
Expand All @@ -36,9 +21,10 @@
from typing import Optional, List, Union, Dict
from enum import Enum, auto

__version__ = "1.1.0"
__version__ = "1.2.0"
__author__ = "Tilman Griesel"
__changelog__ = {
"1.2.0": "Added support for custom token creation",
"1.1.0": "Added support for size, opacity, and radius token and multiple themes and configurable paths",
"1.0.0": "Initial release with RGG token support",
}
Expand Down Expand Up @@ -150,10 +136,6 @@ def _validate_token(self) -> None:
"""Validate token format."""
if not isinstance(self.token, str) or not self.token.strip():
raise ValidationError("Token must be a non-empty string")
if not re.match(r"^[a-zA-Z0-9-]+$", self.token):
raise ValidationError(
"Token must contain only letters, numbers, and hyphens"
)

def _validate_rgb_value(self, value: str) -> str:
"""Validate RGB color value format."""
Expand Down Expand Up @@ -210,7 +192,9 @@ def _validate_value(self, value: Optional[str]) -> Optional[str]:
except ValidationError as e:
raise ValidationError(f"Invalid value for {self.token_type.name}: {str(e)}")

def _process_yaml_file(self, file_path: Path, value: Optional[str]) -> bool:
def _process_yaml_file(
self, file_path: Path, value: Optional[str], create_token: bool = False
) -> bool:
"""Process and update a YAML file."""
if value is None:
return True
Expand All @@ -219,16 +203,30 @@ def _process_yaml_file(self, file_path: Path, value: Optional[str]) -> bool:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()

# Verify required token exists
if f"{self.token}:" not in content:
# Check if token exists
token_exists = f"{self.token}:" in content

if not token_exists and not create_token:
logger.error(f"Token '{self.token}' not found in {file_path}")
return False

# Prepare update
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
new_value = f"{self.token}: {value} # Modified via Graphite Theme Patcher v{__version__} - {timestamp}"
pattern = f"{self.token}:.*(?:\r\n|\r|\n|$)"
updated_content = re.sub(pattern, new_value + "\n", content)

if token_exists:
# Update existing token
pattern = f"{self.token}:.*(?:\r\n|\r|\n|$)"
updated_content = re.sub(pattern, new_value + "\n", content)
else:
# Append new token at the end of the file
custom_token_comment = (
"\n# Custom tokens added via Graphite Theme Patcher\n"
if not content.find("# Custom tokens") >= 0
else "\n"
)
updated_content = (
content.rstrip() + custom_token_comment + new_value + "\n"
)

# Atomic write
with tempfile.NamedTemporaryFile(
Expand All @@ -239,7 +237,8 @@ def _process_yaml_file(self, file_path: Path, value: Optional[str]) -> bool:
os.fsync(tmp.fileno())

os.replace(tmp.name, file_path)
logger.info(f"Updated {file_path}")
action = "Created" if not token_exists else "Updated"
logger.info(f"{action} token in {file_path}")
return True

except Exception as e:
Expand All @@ -251,7 +250,7 @@ def _process_yaml_file(self, file_path: Path, value: Optional[str]) -> bool:
pass
return False

def set_token_value(self, value: Optional[str]) -> bool:
def set_token_value(self, value: Optional[str], create_token: bool = False) -> bool:
"""Update token value across theme files."""
if value is None:
return True
Expand All @@ -277,7 +276,9 @@ def set_token_value(self, value: Optional[str]) -> bool:
for yaml_file in yaml_files:
logger.info(f"Processing: {yaml_file}")
with file_lock(yaml_file):
if not self._process_yaml_file(yaml_file, validated_value):
if not self._process_yaml_file(
yaml_file, validated_value, create_token
):
success = False

return success
Expand Down Expand Up @@ -327,6 +328,11 @@ def main():
default="/config/themes",
help="Base path for themes directory (default: /config/themes)",
)
parser.add_argument(
"--create",
action="store_true",
help="Create token if it doesn't exist",
)

args = parser.parse_args()

Expand All @@ -342,8 +348,9 @@ def main():
if value is None:
sys.exit(0)

action = "Creating/Updating" if args.create else "Updating"
logger.info(
f"Theme Patcher v{__version__} - Updating {args.token} ({args.type}) "
f"Theme Patcher v{__version__} - {action} {args.token} ({args.type}) "
f"in theme '{args.theme}' to: {value}"
)

Expand All @@ -353,7 +360,7 @@ def main():
theme=args.theme,
base_path=args.path,
)
if not patcher.set_token_value(value):
if not patcher.set_token_value(value, args.create):
logger.error("Update failed")
sys.exit(1)

Expand Down

0 comments on commit 6b1a8e7

Please sign in to comment.