diff --git a/plugins/baseball-scoreboard/baseball.py b/plugins/baseball-scoreboard/baseball.py index c0e7832..67c1136 100644 --- a/plugins/baseball-scoreboard/baseball.py +++ b/plugins/baseball-scoreboard/baseball.py @@ -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 @@ -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 @@ -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) @@ -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 @@ -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 @@ -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 diff --git a/plugins/baseball-scoreboard/config_schema.json b/plugins/baseball-scoreboard/config_schema.json index 8383ab6..ab94d5f 100644 --- a/plugins/baseball-scoreboard/config_schema.json +++ b/plugins/baseball-scoreboard/config_schema.json @@ -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": [ @@ -1365,7 +1478,10 @@ "status_text", "detail_text", "rank_text", - "layout" + "layout", + "bases", + "outs", + "count" ], "additionalProperties": false } diff --git a/plugins/baseball-scoreboard/game_renderer.py b/plugins/baseball-scoreboard/game_renderer.py index 17a19f6..3dc3578 100644 --- a/plugins/baseball-scoreboard/game_renderer.py +++ b/plugins/baseball-scoreboard/game_renderer.py @@ -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) @@ -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 @@ -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: @@ -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']