From 4e58d15c2fed81d35b858bb9ef10a4fa4c0c6b8b Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Wed, 27 Apr 2022 21:15:37 -0400 Subject: [PATCH 1/8] Add pitch data to pitches scoreboard item --- data/game.py | 14 ++++++++++---- data/scoreboard/pitches.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/data/game.py b/data/game.py index 30b994eb..ad4ad1da 100644 --- a/data/game.py +++ b/data/game.py @@ -9,9 +9,10 @@ API_FIELDS = ( "gameData,game,id,datetime,dateTime,officialDate,flags,noHitter,perfectGame,status,detailedState,abstractGameState," + "reason,probablePitchers,teams,home,away,abbreviation,teamName,players,id,boxscoreName,fullName,liveData,plays," - + "currentPlay,result,eventType,description,decisions,winner,loser,save,id,linescore,outs,balls,strikes,note," - + "inningState,currentInning,currentInningOrdinal,offense,batter,inHole,onDeck,first,second,third,defense,pitcher," - + "boxscore,teams,runs,players,seasonStats,pitching,wins,losses,saves,era,hits,errors" + + "currentPlay,result,eventType,playEvents,isPitch,pitchData,startSpeed,details,type,code,description,decisions," + + "winner,loser,save,id,linescore,outs,balls,strikes,note,inningState,currentInning,currentInningOrdinal,offense," + + "batter,inHole,onDeck,first,second,third,defense,pitcher,boxscore,teams,runs,players,seasonStats,pitching,wins," + + "losses,saves,era,hits,errors" ) SCHEDULE_API_FIELDS = "dates,date,games,status,detailedState,abstractGameState,reason" @@ -77,7 +78,7 @@ def away_abbreviation(self): return self._data["gameData"]["teams"]["away"]["abbreviation"] def status(self): - return self._status["detailedState"] + return "In Progress" # self._status["detailedState"] def home_score(self): return self._data["liveData"]["linescore"]["teams"]["home"].get("runs", 0) @@ -217,6 +218,11 @@ def strikes(self): def outs(self): return self._data["liveData"]["linescore"].get("outs", 0) + def last_pitch(self): + play = self._data["liveData"]["plays"].get("currentPlay", {}).get("playEvents", [{}])[-1] + if play.get("isPitch", False): + return play["pitchData"].get("startSpeed", 0), play["details"]["type"]["code"] + def note(self): try: return self._data["liveData"]["linescore"]["note"] diff --git a/data/scoreboard/pitches.py b/data/scoreboard/pitches.py index cbbe34d9..9af48be5 100644 --- a/data/scoreboard/pitches.py +++ b/data/scoreboard/pitches.py @@ -5,3 +5,13 @@ class Pitches: def __init__(self, game: Game): self.balls = game.balls() self.strikes = game.strikes() + last_pitch = game.last_pitch() + if last_pitch is None: + self.last_pitch_speed = "0" + self.last_pitch_type = "UK" + else: + self.last_pitch_speed = f"{round(last_pitch[0])}" + self.last_pitch_type = last_pitch[1] + + def __str__(self) -> str: + return f"Count: {self.balls} - {self.strikes}. Last pitch: {self.last_pitch_speed}mph {self.last_pitch_type}" From 4aa3911d6ba029482af9a8ec2616a3c3a585a2cd Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Thu, 28 Apr 2022 20:49:37 -0400 Subject: [PATCH 2/8] Integrated BW's pitch speeed into 128x64, added to colors, coordinates --- colors/scoreboard.json.example | 5 +++++ data/game.py | 12 +++++++----- data/scoreboard/pitches.py | 4 +++- renderers/games/game.py | 23 +++++++++++++++++++++-- renderers/main.py | 1 - 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/colors/scoreboard.json.example b/colors/scoreboard.json.example index b7eeb96b..b4d340b5 100644 --- a/colors/scoreboard.json.example +++ b/colors/scoreboard.json.example @@ -114,6 +114,11 @@ "g": 235, "b": 59 }, + "pitch": { + "r": 255, + "g": 255, + "b": 255 + }, "strikeout": { "r": 255, "g": 235, diff --git a/data/game.py b/data/game.py index ad4ad1da..9278c750 100644 --- a/data/game.py +++ b/data/game.py @@ -78,7 +78,7 @@ def away_abbreviation(self): return self._data["gameData"]["teams"]["away"]["abbreviation"] def status(self): - return "In Progress" # self._status["detailedState"] + return self._status["detailedState"] def home_score(self): return self._data["liveData"]["linescore"]["teams"]["home"].get("runs", 0) @@ -219,10 +219,12 @@ def outs(self): return self._data["liveData"]["linescore"].get("outs", 0) def last_pitch(self): - play = self._data["liveData"]["plays"].get("currentPlay", {}).get("playEvents", [{}])[-1] - if play.get("isPitch", False): - return play["pitchData"].get("startSpeed", 0), play["details"]["type"]["code"] - + try: + play = self._data["liveData"]["plays"].get("currentPlay", {}).get("playEvents", [{}])[-1] + if play.get("isPitch", False): + return play["pitchData"].get("startSpeed", 0), play["details"]["type"]["code"], play["details"]["type"]["description"] + except: + return None def note(self): try: return self._data["liveData"]["linescore"]["note"] diff --git a/data/scoreboard/pitches.py b/data/scoreboard/pitches.py index 9af48be5..23689b56 100644 --- a/data/scoreboard/pitches.py +++ b/data/scoreboard/pitches.py @@ -9,9 +9,11 @@ def __init__(self, game: Game): if last_pitch is None: self.last_pitch_speed = "0" self.last_pitch_type = "UK" + self.last_pitch_type_long = "Unknown" else: self.last_pitch_speed = f"{round(last_pitch[0])}" self.last_pitch_type = last_pitch[1] + self.last_pitch_type_long = last_pitch[2] def __str__(self) -> str: - return f"Count: {self.balls} - {self.strikes}. Last pitch: {self.last_pitch_speed}mph {self.last_pitch_type}" + return f"Count: {self.balls} - {self.strikes}. Last pitch: {self.last_pitch_speed}mph {self.last_pitch_type} {self.last_pitch_long}" diff --git a/renderers/games/game.py b/renderers/games/game.py index 810656a4..0701ad53 100644 --- a/renderers/games/game.py +++ b/renderers/games/game.py @@ -28,6 +28,7 @@ def render_live_game(canvas, layout: Layout, colors: Color, scoreboard: Scoreboa scoreboard.strikeout(), scoreboard.strikeout_looking(), (animation_time // 6) % 2, + scoreboard.pitches ) # Check if we're deep enough into a game and it's a no hitter or perfect game @@ -49,8 +50,9 @@ def render_live_game(canvas, layout: Layout, colors: Color, scoreboard: Scoreboa # --------------- at-bat --------------- -def _render_at_bat(canvas, layout, colors, atbat: AtBat, text_pos, strikeout, looking, animation): +def _render_at_bat(canvas, layout, colors, atbat: AtBat, text_pos, strikeout, looking, animation, pitches: Pitches): plength = __render_pitcher_text(canvas, layout, colors, atbat.pitcher, text_pos) + __render_pitch_text(canvas, layout, colors, pitches) if strikeout: if animation: __render_strikeout(canvas, layout, colors, looking) @@ -108,7 +110,24 @@ def __render_pitcher_text(canvas, layout, colors, pitcher, text_pos): graphics.DrawText(canvas, font["font"], coords["x"], coords["y"], color, "P:") return pos - +def __render_pitch_text(canvas, layout, colors, pitches: Pitches): +#def __render_pitch_text(canvas, layout, colors): + coords = layout.coords("atbat.pitch") + color = colors.graphics_color("atbat.pitch") + font = layout.font("atbat.pitch") + bgcolor = colors.graphics_color("default.background") + if(int(pitches.last_pitch_speed) > 0 and layout.coords("atbat.pitch")["enabled"]): + mph= " " + if(layout.coords("atbat.pitch")["mph"]): + mph="mph " + if(layout.coords("atbat.pitch")["desc_length"]=="Long"): + pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type_long[0:23] + elif(layout.coords("atbat.pitch")["desc_length"]=="Short"): + pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type + else: + pitch_text = None + graphics.DrawText(canvas, font["font"], coords["x"], coords["y"], color, pitch_text) + # --------------- bases --------------- def _render_bases(canvas, layout, colors, bases: Bases, home_run, animation): base_runners = bases.runners diff --git a/renderers/main.py b/renderers/main.py index 46fb2479..7768ad0e 100644 --- a/renderers/main.py +++ b/renderers/main.py @@ -146,7 +146,6 @@ def __draw_game(self): game = self.data.current_game bgcolor = self.data.config.scoreboard_colors.color("default.background") self.canvas.Fill(bgcolor["r"], bgcolor["g"], bgcolor["b"]) - scoreboard = Scoreboard(game) layout = self.data.config.layout colors = self.data.config.scoreboard_colors From 022adabedad86bcd0f75eee5064ec5a84bf5050b Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 09:15:17 -0400 Subject: [PATCH 3/8] Added PITCH_LONG and PITCH_SHORT configurable dict --- data/pitches.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 data/pitches.py diff --git a/data/pitches.py b/data/pitches.py new file mode 100644 index 00000000..64de0516 --- /dev/null +++ b/data/pitches.py @@ -0,0 +1,45 @@ +#A list of mlb pitch types appearing in statcast +#https://www.daktronics.com/en-us/support/kb/DD3312647 +#Dont change the index, but feel free to change +#the long descriptions + +PITCH_LONG = { + "AB": "Automatic Ball", + "AS": "Automatic Strike", + "CH": "Change-up", + "EP": "Eephus", + "FC": "Cutter", + "FF": "4 Seam Fastball", #MLB default is "Four-Seam Fastball" + "FO": "Forkball", + "FS": "Splitter", + "FT": "2 Seam Fastball", #MLB default is "Two-Seam Fastball" + "GY": "Gyroball", + "IN": "Intentional Ball", + "KC": "Knuckle Curve", + "KN": "Knuckleball", + "NP": "No Pitch", + "SC": "Screwball", + "SI": "Sinker", + "SL": "Slider", + "UN": "Unknown" +} +PITCH_SHORT = { + "AB": "AB", + "AS": "AS", + "CH": "CH", + "EP": "EP", + "FC": "FC", + "FF": "FF", + "FO": "FO", + "FS": "FS", + "FT": "FT", + "GY": "GY", + "IN": "IN", + "KC": "KC", + "KN": "KN", + "NP": "NP", + "SC": "SC", + "SI": "SI", + "SL": "SL", + "UN": "UN" +} \ No newline at end of file From 772c212af2e15af438c67a2a1026a0b575a68800 Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 09:15:56 -0400 Subject: [PATCH 4/8] Commit --- data/scoreboard/pitches.py | 6 +++--- renderers/games/game.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/scoreboard/pitches.py b/data/scoreboard/pitches.py index 23689b56..917f0598 100644 --- a/data/scoreboard/pitches.py +++ b/data/scoreboard/pitches.py @@ -1,5 +1,5 @@ from data.game import Game - +import data.pitches class Pitches: def __init__(self, game: Game): @@ -12,8 +12,8 @@ def __init__(self, game: Game): self.last_pitch_type_long = "Unknown" else: self.last_pitch_speed = f"{round(last_pitch[0])}" - self.last_pitch_type = last_pitch[1] - self.last_pitch_type_long = last_pitch[2] + self.last_pitch_type = data.pitches.PITCH_SHORT[last_pitch[1]] + self.last_pitch_type_long = data.pitches.PITCH_LONG[last_pitch[1]] def __str__(self) -> str: return f"Count: {self.balls} - {self.strikes}. Last pitch: {self.last_pitch_speed}mph {self.last_pitch_type} {self.last_pitch_long}" diff --git a/renderers/games/game.py b/renderers/games/game.py index 0701ad53..411453ef 100644 --- a/renderers/games/game.py +++ b/renderers/games/game.py @@ -121,7 +121,7 @@ def __render_pitch_text(canvas, layout, colors, pitches: Pitches): if(layout.coords("atbat.pitch")["mph"]): mph="mph " if(layout.coords("atbat.pitch")["desc_length"]=="Long"): - pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type_long[0:23] + pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type_long elif(layout.coords("atbat.pitch")["desc_length"]=="Short"): pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type else: From e429320c6a0aab70b5ec52ce5dca3bb186545c09 Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 14:47:44 -0400 Subject: [PATCH 5/8] Update 128x64 bord layout to show pitches --- coordinates/w128h64.json.example | 92 ++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/coordinates/w128h64.json.example b/coordinates/w128h64.json.example index b823ebec..cf0537e2 100644 --- a/coordinates/w128h64.json.example +++ b/coordinates/w128h64.json.example @@ -1,22 +1,22 @@ { "defaults": { - "font_name": "9x18B" + "font_name": "7x13" }, "bases": { "1B": { "x": 112, "y": 42, - "size": 14 + "size": 10 }, "2B": { "x": 103, "y": 33, - "size": 14 + "size": 10 }, "3B": { "x": 94, "y": 42, - "size": 14 + "size": 10 } }, "final": { @@ -46,7 +46,7 @@ }, "due_up": { "due": { - "font_name": "7x13B", + "font_name": "5x7", "x": 38, "y": 44 }, @@ -55,13 +55,13 @@ "y": 56 }, "divider": { - "draw": true, + "draw": false, "x": 36, "y_start": 32, "y_end": 64 }, "leadoff": { - "font_name": "7x13B", + "font_name": "7x13", "x": 64, "y": 42 }, @@ -76,8 +76,8 @@ } }, "number": { - "x": 93, - "y": 45, + "x": 87, + "y": 42, "nohit": { "x": 93, "y": 42 @@ -88,46 +88,56 @@ } }, "arrow": { - "size": 7, + "size": 6, "up": { - "x_offset": -12, - "y_offset": -10 + "x_offset": -11, + "y_offset": -8 }, "down": { - "x_offset": -12, - "y_offset": -2 + "x_offset": -11, + "y_offset": -4 } } }, "outs": { "1": { - "x": 102, - "y": 58, - "size": 4 + "x": 95, + "y": 56, + "size": 5 }, "2": { - "x": 108, - "y": 58, - "size": 4 + "x": 106, + "y": 56, + "size": 5 }, "3": { - "x": 114, - "y": 58, - "size": 4 + "x": 118, + "y": 56, + "size": 5 } }, "atbat": { "batter": { - "font_name": "7x13B", + "font_name": "5x7", "x": 1, "y": 60, "width": 41 }, "pitcher": { - "font_name": "7x13B", + "font_name": "5x7", "x": 1, - "y": 44, - "width": 48 + "y": 40, + "width": 41 + }, + "pitch": { + "font_name": "4x6", + "x": 1, + "y": 50, + "width": 41, + "enabled": true, + "mph": true, + "desc_length": "Long" + }, "loop": 68, "strikeout": { @@ -137,7 +147,7 @@ }, "batter_count": { "x": 66, - "y": 60, + "y": 62, "nohit": { "x": 66, "y": 64 @@ -246,27 +256,31 @@ }, "name": { "away": { + "font_name": "9x18B", "x": 4, - "y": 12 + "y": 13 }, "home": { + "font_name": "9x18B", "x": 4, - "y": 28 + "y": 29 } }, "runs": { "runs_hits_errors": { - "show": false, - "compress_digits": false, - "spacing": 3 - }, + "show": true, + "compress_digits": true, + "spacing": 5 + }, "away": { - "x": 120, - "y": 12 + "font_name": "9x18B", + "x": 126, + "y": 13 }, "home": { - "x": 120, - "y": 28 + "font_name": "9x18B", + "x": 126, + "y": 29 } } }, @@ -321,4 +335,4 @@ "y": 31 } } -} \ No newline at end of file +} From b8713cffdb4da24f30372fe3f0e664663cc3af22 Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 14:58:29 -0400 Subject: [PATCH 6/8] Updated readme's for pitch data --- README.md | 2 ++ coordinates/README.md | 5 +++++ coordinates/w128h64.json.example | 1 - data/pitches.py | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 69fd4c43..0bbb0d9d 100755 --- a/README.md +++ b/README.md @@ -169,6 +169,8 @@ A default `config.json.example` file is included for reference. Copy this file t ### Additional Features * Runs/Hits/Errors - Runs are always shown on the games screen, but you can enable or adjust spacing of a "runs, hits, errors" display. Take a look at the [coordinates readme file](/coordinates/README.md) for details. +* Pitch Data - Pitch data can be shown on the game screen, See the [coordinates readme file](/coordinates/README.md) for details. In addition, the Short and Long pitch description can be changed in data/pitches.py + ### Flags You can configure your LED matrix with the same flags used in the [rpi-rgb-led-matrix](https://github.com/hzeller/rpi-rgb-led-matrix) library. More information on these arguments can be found in the library documentation. diff --git a/coordinates/README.md b/coordinates/README.md index 6672ead3..c500b32f 100644 --- a/coordinates/README.md +++ b/coordinates/README.md @@ -18,6 +18,11 @@ The layout can have a couple of different states where things are rendered diffe * `compress_digits` will reduce the space between digits when the number of runs or hits is > 9. * `spacing` is the number of pixels between the runs/hits and hits/errors. +## Pitch Data +* enabled (true/false) turn feature on/off +* mph (true/false) When rendering pitch speed add mph after (99 mph) +* desc_length (Short/Long) The short or long pitch type description, you can change both the short and long description to your liking in data/pitches as long as you do not change the index value. + ## Updates The software develops and releases features with full support for the default layouts, so custom layouts may look unsatisfactory if you update to later versions of the scoreboard. If you as a user decide to create a custom layout file, you are responsible for tweaking the coordinates to your liking with each update. diff --git a/coordinates/w128h64.json.example b/coordinates/w128h64.json.example index cf0537e2..b21d82ee 100644 --- a/coordinates/w128h64.json.example +++ b/coordinates/w128h64.json.example @@ -137,7 +137,6 @@ "enabled": true, "mph": true, "desc_length": "Long" - }, "loop": 68, "strikeout": { diff --git a/data/pitches.py b/data/pitches.py index 64de0516..eab04246 100644 --- a/data/pitches.py +++ b/data/pitches.py @@ -1,7 +1,7 @@ #A list of mlb pitch types appearing in statcast #https://www.daktronics.com/en-us/support/kb/DD3312647 #Dont change the index, but feel free to change -#the long descriptions +#the descriptions PITCH_LONG = { "AB": "Automatic Ball", From ff8fe0a7a3ec38d46deecea297ba1cef95de0ee1 Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 15:07:05 -0400 Subject: [PATCH 7/8] pitch data not display on all except 128x64 by default --- coordinates/w128h32.json.example | 9 +++++++++ coordinates/w32h32.json.example | 9 +++++++++ coordinates/w64h32.json.example | 9 +++++++++ coordinates/w64h64.json.example | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/coordinates/w128h32.json.example b/coordinates/w128h32.json.example index 81bbdf4c..d0aefb87 100644 --- a/coordinates/w128h32.json.example +++ b/coordinates/w128h32.json.example @@ -167,6 +167,15 @@ "y": 30, "width": 40 }, + "pitch": { + "font_name": "4x6", + "x": 1, + "y": 50, + "width": 41, + "enabled": false, + "mph": false, + "desc_length": "Short" + }, "loop": 64, "strikeout": { "x": 84, diff --git a/coordinates/w32h32.json.example b/coordinates/w32h32.json.example index 86401cc7..e4e88529 100644 --- a/coordinates/w32h32.json.example +++ b/coordinates/w32h32.json.example @@ -147,6 +147,15 @@ "y": 33, "width": 12 }, + "pitch": { + "font_name": "4x6", + "x": 1, + "y": 50, + "width": 41, + "enabled": false, + "mph": false, + "desc_length": "Short" + }, "loop": 16, "strikeout": { "x": 33, diff --git a/coordinates/w64h32.json.example b/coordinates/w64h32.json.example index 72574d0b..108819ab 100644 --- a/coordinates/w64h32.json.example +++ b/coordinates/w64h32.json.example @@ -125,6 +125,15 @@ "y": 21, "width": 24 }, + "pitch": { + "font_name": "4x6", + "x": 1, + "y": 50, + "width": 41, + "enabled": false, + "mph": false, + "desc_length": "Short" + }, "loop": 36, "strikeout": { "x": 15, diff --git a/coordinates/w64h64.json.example b/coordinates/w64h64.json.example index f5340894..bb752760 100644 --- a/coordinates/w64h64.json.example +++ b/coordinates/w64h64.json.example @@ -123,6 +123,15 @@ "y": 28, "width": 50 }, + "pitch": { + "font_name": "4x6", + "x": 1, + "y": 50, + "width": 41, + "enabled": false, + "mph": false, + "desc_length": "Short" + }, "loop": 64, "strikeout": { "x": 31, From bb60e72a1bbe89bd50415bcee86cfae84c83baa5 Mon Sep 17 00:00:00 2001 From: Nicholas Saraniti Date: Fri, 29 Apr 2022 20:24:02 -0400 Subject: [PATCH 8/8] Skipped CU (curveball) entry --- data/pitches.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/pitches.py b/data/pitches.py index eab04246..a66606ce 100644 --- a/data/pitches.py +++ b/data/pitches.py @@ -7,6 +7,7 @@ "AB": "Automatic Ball", "AS": "Automatic Strike", "CH": "Change-up", + "CU": "Curveball", "EP": "Eephus", "FC": "Cutter", "FF": "4 Seam Fastball", #MLB default is "Four-Seam Fastball" @@ -27,6 +28,7 @@ "AB": "AB", "AS": "AS", "CH": "CH", + "CU": "CU", "EP": "EP", "FC": "FC", "FF": "FF",