Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions plugins/baseball-scoreboard/baseball.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,17 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
outs = game.get("outs", 0)
inning_half = game["inning_half"]

# Define geometry
base_diamond_size = 7
out_circle_diameter = 3
out_vertical_spacing = 2 # Space between out circles
spacing_between_bases_outs = (
3 # Horizontal space between base cluster and out column
)
# Read configurable game display settings
customization = self.config.get('customization', {})
bases_cfg = customization.get('bases', {})
outs_cfg = customization.get('outs', {})
count_cfg = customization.get('count', {})

# Define geometry (configurable with defaults matching original)
base_diamond_size = bases_cfg.get('diamond_size', 7)
out_circle_diameter = outs_cfg.get('circle_diameter', 3)
out_vertical_spacing = outs_cfg.get('spacing', 2)
spacing_between_bases_outs = outs_cfg.get('distance_from_bases', 3)
base_vert_spacing = 1 # Internal vertical space in base cluster
base_horiz_spacing = 1 # Internal horizontal space in base cluster

Expand All @@ -509,8 +513,9 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
inning_bbox[3] + 0
) # Start immediately below inning text

# Center the BASE cluster horizontally
bases_origin_x = (self.display_width - base_cluster_width) // 2
# Center the BASE cluster horizontally (with optional offset)
bases_origin_x = (self.display_width - base_cluster_width) // 2 + bases_cfg.get('x_offset', 0)
overall_start_y += bases_cfg.get('y_offset', 0)

# Determine relative positions for outs based on inning half
# Only compute outs column position when count data is available
Expand All @@ -531,8 +536,8 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
)

# --- Draw Bases (Diamonds) ---
base_color_occupied = (255, 255, 255)
base_color_empty = (255, 255, 255) # Outline color
base_color_occupied = tuple(bases_cfg.get('occupied_color', [255, 255, 255]))
base_color_empty = tuple(bases_cfg.get('empty_color', [255, 255, 255]))
h_d = base_diamond_size // 2

# 2nd Base (Top center relative to bases_origin_x)
Expand Down Expand Up @@ -582,8 +587,8 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
# --- Draw Outs (Vertical Circles) ---
# Only render outs and count when data is available (ESPN NCAA doesn't provide these)
if has_count_data:
circle_color_out = (255, 255, 255)
circle_color_empty_outline = (100, 100, 100)
circle_color_out = tuple(outs_cfg.get('counted_color', [255, 255, 255]))
circle_color_empty_outline = tuple(outs_cfg.get('empty_color', [100, 100, 100]))

for i in range(3):
cx = outs_column_x
Expand Down Expand Up @@ -624,7 +629,7 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
cluster_bottom_y = (
overall_start_y + base_cluster_height
) # Find the bottom of the taller part (bases)
count_y = cluster_bottom_y + 2 # Start 2 pixels below cluster
count_y = cluster_bottom_y + count_cfg.get('y_offset', 2)

# Center horizontally within the BASE cluster width
count_x = bases_origin_x + (base_cluster_width - count_text_width) // 2
Expand Down Expand Up @@ -656,8 +661,9 @@ def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
)

# Draw main text
count_text_color = tuple(count_cfg.get('text_color', [255, 255, 255]))
self.display_manager._draw_bdf_text(
count_text, count_x, count_y, color=text_color, font=bdf_font
count_text, count_x, count_y, color=count_text_color, font=bdf_font
)
finally:
self.display_manager.draw = original_draw
Expand Down
118 changes: 117 additions & 1 deletion plugins/baseball-scoreboard/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,119 @@
}
},
"additionalProperties": false
},
"bases": {
"type": "object",
"title": "Bases Diamond",
"description": "Customize the base diamond indicators",
"properties": {
"diamond_size": {
"type": "integer",
"default": 7,
"minimum": 3,
"maximum": 15,
"description": "Size of each base diamond in pixels"
},
"occupied_color": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"default": [255, 255, 255],
"minItems": 3,
"maxItems": 3,
"description": "Color when a base is occupied (RGB)"
},
"empty_color": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"default": [255, 255, 255],
"minItems": 3,
"maxItems": 3,
"description": "Outline color when a base is empty (RGB)"
},
"x_offset": {
"type": "integer",
"default": 0,
"minimum": -50,
"maximum": 50,
"description": "Horizontal offset for the bases cluster (positive = right)"
},
"y_offset": {
"type": "integer",
"default": 0,
"minimum": -50,
"maximum": 50,
"description": "Vertical offset for the bases cluster (positive = down)"
}
},
"additionalProperties": false
},
"outs": {
"type": "object",
"title": "Outs Circles",
"description": "Customize the outs indicator circles",
"properties": {
"circle_diameter": {
"type": "integer",
"default": 3,
"minimum": 2,
"maximum": 10,
"description": "Diameter of each out circle in pixels"
},
"counted_color": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"default": [255, 255, 255],
"minItems": 3,
"maxItems": 3,
"description": "Fill color for counted outs (RGB)"
},
"empty_color": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"default": [100, 100, 100],
"minItems": 3,
"maxItems": 3,
"description": "Outline color for remaining outs (RGB)"
},
"spacing": {
"type": "integer",
"default": 2,
"minimum": 0,
"maximum": 10,
"description": "Vertical spacing between out circles in pixels"
},
"distance_from_bases": {
"type": "integer",
"default": 3,
"minimum": 0,
"maximum": 15,
"description": "Horizontal distance from the bases cluster in pixels"
}
},
"additionalProperties": false
},
"count": {
"type": "object",
"title": "Balls-Strikes Count",
"description": "Customize the balls-strikes count text",
"properties": {
"text_color": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"default": [255, 255, 255],
"minItems": 3,
"maxItems": 3,
"description": "Color for the count text (RGB)"
},
"y_offset": {
"type": "integer",
"default": 2,
"minimum": 0,
"maximum": 20,
"description": "Pixels below the base cluster"
}
},
"additionalProperties": false
}
},
"x-propertyOrder": [
Expand All @@ -1365,7 +1478,10 @@
"status_text",
"detail_text",
"rank_text",
"layout"
"layout",
"bases",
"outs",
"count"
],
"additionalProperties": false
}
Expand Down
33 changes: 21 additions & 12 deletions plugins/baseball-scoreboard/game_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,24 @@ def _render_live_game(self, game: Dict) -> Image.Image:
bases_occupied = game.get('bases_occupied', [False, False, False])
outs = game.get('outs', 0)

base_diamond_size = 7
out_circle_diameter = 3
out_vertical_spacing = 2
spacing_between_bases_outs = 3
# Read configurable game display settings
customization = self.config.get('customization', {})
bases_cfg = customization.get('bases', {})
outs_cfg = customization.get('outs', {})
count_cfg = customization.get('count', {})

base_diamond_size = bases_cfg.get('diamond_size', 7)
out_circle_diameter = outs_cfg.get('circle_diameter', 3)
out_vertical_spacing = outs_cfg.get('spacing', 2)
spacing_between_bases_outs = outs_cfg.get('distance_from_bases', 3)
base_vert_spacing = 1
base_horiz_spacing = 1

base_cluster_height = base_diamond_size + base_vert_spacing + base_diamond_size
base_cluster_width = base_diamond_size + base_horiz_spacing + base_diamond_size

overall_start_y = inning_bbox[3] + 1
bases_origin_x = (self.display_width - base_cluster_width) // 2
overall_start_y = inning_bbox[3] + 1 + bases_cfg.get('y_offset', 0)
bases_origin_x = (self.display_width - base_cluster_width) // 2 + bases_cfg.get('x_offset', 0)

# Outs column position (only needed when count data is available)
has_count_data = game.get('has_count_data', True)
Expand All @@ -288,8 +294,8 @@ def _render_live_game(self, game: Dict) -> Image.Image:

# Draw bases as diamond polygons
h_d = base_diamond_size // 2
base_fill = (255, 255, 255)
base_outline = (255, 255, 255)
base_fill = tuple(bases_cfg.get('occupied_color', [255, 255, 255]))
base_outline = tuple(bases_cfg.get('empty_color', [255, 255, 255]))

# 2nd base (top center)
c2x = bases_origin_x + base_cluster_width // 2
Expand All @@ -313,14 +319,16 @@ def _render_live_game(self, game: Dict) -> Image.Image:

# Outs circles (only when count data is available)
if has_count_data:
outs_fill = tuple(outs_cfg.get('counted_color', [255, 255, 255]))
outs_empty = tuple(outs_cfg.get('empty_color', [100, 100, 100]))
for i in range(3):
cx = outs_column_x
cy = outs_column_start_y + i * (out_circle_diameter + out_vertical_spacing)
coords = [cx, cy, cx + out_circle_diameter, cy + out_circle_diameter]
if i < outs:
draw.ellipse(coords, fill=(255, 255, 255))
draw.ellipse(coords, fill=outs_fill)
else:
draw.ellipse(coords, outline=(100, 100, 100))
draw.ellipse(coords, outline=outs_empty)

# Balls-strikes count (below bases, only when count data is available)
if has_count_data:
Expand All @@ -329,9 +337,10 @@ def _render_live_game(self, game: Dict) -> Image.Image:
count_text = f"{balls}-{strikes}"
count_font = self.fonts['detail']
count_width = draw.textlength(count_text, font=count_font)
count_y = overall_start_y + base_cluster_height + 2
count_y = overall_start_y + base_cluster_height + count_cfg.get('y_offset', 2)
count_x = bases_origin_x + (base_cluster_width - count_width) // 2
self._draw_text_with_outline(draw, count_text, (int(count_x), count_y), count_font)
count_text_color = tuple(count_cfg.get('text_color', [255, 255, 255]))
self._draw_text_with_outline(draw, count_text, (int(count_x), count_y), count_font, fill=count_text_color)

# Score (centered between logos, below bases/count cluster)
score_font = self.fonts['score']
Expand Down