Skip to content

Commit 8cbf76c

Browse files
committed
Reduce whitespace, fix keyboard input, and slow refresh
Display improvements: - Reduce header/controls from 2 to 1 line each - Remove all panel padding (0,0 instead of 0,1) - Compact irradiance text (remove spaces) - Reduce refresh rate from 1Hz to 0.5Hz - Increase update interval from 1s to 5s Keyboard input fixes: - Pass display reference to ControlHandler - Pause live display when prompting for input - Resume display after input completes - Increase success message delay to 2s for visibility - Add pause/resume methods to EmulatorDisplay This fixes the issue where keyboard input values weren't being applied because the display kept refreshing and overwriting the input prompts. Now the display pauses during input and resumes after, allowing values to be changed properly.
1 parent 98111d4 commit 8cbf76c

3 files changed

Lines changed: 58 additions & 21 deletions

File tree

emulator/controls.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,16 @@
3737
class ControlHandler:
3838
"""Handles keyboard controls for the emulator."""
3939

40-
def __init__(self, simulator, on_quit: Optional[Callable] = None):
40+
def __init__(self, simulator, display=None, on_quit: Optional[Callable] = None):
4141
"""Initialize control handler.
4242
4343
Args:
4444
simulator: InverterSimulator instance
45+
display: EmulatorDisplay instance (optional, for pausing)
4546
on_quit: Callback function when user quits
4647
"""
4748
self.simulator = simulator
49+
self.display = display
4850
self.on_quit = on_quit
4951
self.running = False
5052
self.input_thread = None
@@ -141,6 +143,8 @@ def _handle_key(self, key: str) -> None:
141143

142144
def _prompt_irradiance(self) -> None:
143145
"""Prompt for irradiance value."""
146+
if self.display:
147+
self.display.pause()
144148
try:
145149
print("\n\n")
146150
print("=" * 50)
@@ -152,14 +156,19 @@ def _prompt_irradiance(self) -> None:
152156
value = float(input())
153157
self.simulator.set_irradiance(value)
154158
print(f"✓ Irradiance set to {self.simulator.solar_irradiance:.0f} W/m²")
155-
time.sleep(1)
159+
time.sleep(2)
156160

157161
except (ValueError, EOFError, KeyboardInterrupt):
158162
print("✗ Invalid input")
159163
time.sleep(1)
164+
finally:
165+
if self.display:
166+
self.display.resume()
160167

161168
def _prompt_clouds(self) -> None:
162169
"""Prompt for cloud cover."""
170+
if self.display:
171+
self.display.pause()
163172
try:
164173
print("\n\n")
165174
print("=" * 50)
@@ -171,14 +180,19 @@ def _prompt_clouds(self) -> None:
171180
value = float(input()) / 100.0
172181
self.simulator.set_cloud_cover(value)
173182
print(f"✓ Cloud cover set to {self.simulator.cloud_cover * 100:.0f}%")
174-
time.sleep(1)
183+
time.sleep(2)
175184

176185
except (ValueError, EOFError, KeyboardInterrupt):
177186
print("✗ Invalid input")
178187
time.sleep(1)
188+
finally:
189+
if self.display:
190+
self.display.resume()
179191

180192
def _prompt_load(self) -> None:
181193
"""Prompt for house load."""
194+
if self.display:
195+
self.display.pause()
182196
try:
183197
print("\n\n")
184198
print("=" * 50)
@@ -190,14 +204,19 @@ def _prompt_load(self) -> None:
190204
value = float(input())
191205
self.simulator.set_house_load(value)
192206
print(f"✓ House load set to {self.simulator.house_load:.0f}W")
193-
time.sleep(1)
207+
time.sleep(2)
194208

195209
except (ValueError, EOFError, KeyboardInterrupt):
196210
print("✗ Invalid input")
197211
time.sleep(1)
212+
finally:
213+
if self.display:
214+
self.display.resume()
198215

199216
def _prompt_time_speed(self) -> None:
200217
"""Prompt for time multiplier."""
218+
if self.display:
219+
self.display.pause()
201220
try:
202221
print("\n\n")
203222
print("=" * 50)
@@ -209,14 +228,19 @@ def _prompt_time_speed(self) -> None:
209228
value = float(input())
210229
self.simulator.set_time_multiplier(value)
211230
print(f"✓ Time speed set to {self.simulator.time_multiplier}x")
212-
time.sleep(1)
231+
time.sleep(2)
213232

214233
except (ValueError, EOFError, KeyboardInterrupt):
215234
print("✗ Invalid input")
216235
time.sleep(1)
236+
finally:
237+
if self.display:
238+
self.display.resume()
217239

218240
def _prompt_battery(self) -> None:
219241
"""Prompt for battery control."""
242+
if self.display:
243+
self.display.pause()
220244
try:
221245
print("\n\n")
222246
print("=" * 50)
@@ -250,8 +274,11 @@ def _prompt_battery(self) -> None:
250274
self.simulator.set_battery_override(0)
251275
print("✓ Battery set to IDLE")
252276

253-
time.sleep(1)
277+
time.sleep(2)
254278

255279
except (ValueError, EOFError, KeyboardInterrupt):
256280
print("✗ Invalid input")
257281
time.sleep(1)
282+
finally:
283+
if self.display:
284+
self.display.resume()

emulator/display.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ def create_layout(self) -> Layout:
3232
layout = Layout()
3333

3434
layout.split_column(
35-
Layout(name="header", size=2),
35+
Layout(name="header", size=1),
3636
Layout(name="main"),
37-
Layout(name="controls", size=2),
37+
Layout(name="controls", size=1),
3838
)
3939

4040
layout["main"].split_row(
@@ -63,7 +63,7 @@ def generate_header(self) -> Panel:
6363
header_text.append(f"{status}", style=f"bold {status_color}")
6464
header_text.append(f" | Port {self.simulator.port}", style="cyan")
6565

66-
return Panel(header_text, style="bold", padding=(0, 1))
66+
return Panel(header_text, style="bold", padding=(0, 0))
6767

6868
def generate_pv_panel(self) -> Panel:
6969
"""Generate PV generation panel."""
@@ -109,14 +109,14 @@ def generate_pv_panel(self) -> Panel:
109109
f"[bold green]{pv.get('total', 0):.0f}W[/bold green]"
110110
)
111111

112-
# Add irradiance info below table
113-
irradiance_text = Text.from_markup(f"☀️ {self.simulator.solar_irradiance:.0f}W/m² ☁️ {self.simulator.cloud_cover * 100:.0f}%")
112+
# Add irradiance info below table (compact)
113+
irradiance_text = Text.from_markup(f"☀️{self.simulator.solar_irradiance:.0f} ☁️{self.simulator.cloud_cover * 100:.0f}%")
114114

115115
return Panel(
116116
Group(table, irradiance_text),
117117
title="[bold]PV[/bold]",
118118
border_style="green",
119-
padding=(0, 1)
119+
padding=(0, 0)
120120
)
121121

122122
def generate_ac_panel(self) -> Panel:
@@ -142,7 +142,7 @@ def generate_ac_panel(self) -> Panel:
142142
table.add_row("Frequency", "50.0Hz")
143143
table.add_row("[bold]Power[/bold]", f"[bold yellow]{ac_power:.0f}W[/bold yellow]")
144144

145-
return Panel(table, title="[bold]AC[/bold]", border_style="yellow", padding=(0, 1))
145+
return Panel(table, title="[bold]AC[/bold]", border_style="yellow", padding=(0, 0))
146146

147147
def generate_battery_panel(self) -> Optional[Panel]:
148148
"""Generate battery panel (if model has battery)."""
@@ -175,7 +175,7 @@ def generate_battery_panel(self) -> Optional[Panel]:
175175
table.add_row("Power", power_text)
176176
table.add_row("Ch/Disch", f"{self.simulator.battery_charge_today:.1f}/{self.simulator.battery_discharge_today:.1f}kWh")
177177

178-
return Panel(table, title="[bold]Battery[/bold]", border_style="blue", padding=(0, 1))
178+
return Panel(table, title="[bold]Battery[/bold]", border_style="blue", padding=(0, 0))
179179

180180
def generate_grid_panel(self) -> Panel:
181181
"""Generate grid panel."""
@@ -201,7 +201,7 @@ def generate_grid_panel(self) -> Panel:
201201
table.add_row("Export", f"{grid_export:.0f}W")
202202
table.add_row("Load", f"[magenta]{self.simulator.house_load:.0f}W[/magenta]")
203203

204-
return Panel(table, title="[bold]Grid[/bold]", border_style="cyan", padding=(0, 1))
204+
return Panel(table, title="[bold]Grid[/bold]", border_style="cyan", padding=(0, 0))
205205

206206
def generate_energy_panel(self) -> Panel:
207207
"""Generate energy totals panel."""
@@ -234,7 +234,7 @@ def generate_energy_panel(self) -> Panel:
234234
f"{self.simulator.load_energy_total:.0f}"
235235
)
236236

237-
return Panel(table, title="[bold]Energy (kWh)[/bold]", border_style="magenta", padding=(0, 1))
237+
return Panel(table, title="[bold]Energy (kWh)[/bold]", border_style="magenta", padding=(0, 0))
238238

239239
def generate_temperature_panel(self) -> Panel:
240240
"""Generate temperature panel."""
@@ -251,7 +251,7 @@ def generate_temperature_panel(self) -> Panel:
251251
table.add_row("IPM", f"{temps.get('ipm', 0):.0f}°C")
252252
table.add_row("Boost", f"{temps.get('boost', 0):.0f}°C")
253253

254-
return Panel(table, title="[bold]Temp[/bold]", border_style="red", padding=(0, 1))
254+
return Panel(table, title="[bold]Temp[/bold]", border_style="red", padding=(0, 0))
255255

256256
def generate_controls_panel(self) -> Panel:
257257
"""Generate controls help panel."""
@@ -274,7 +274,7 @@ def generate_controls_panel(self) -> Panel:
274274
controls_text.append("[Q]", style="bold cyan")
275275
controls_text.append("Quit", style="white")
276276

277-
return Panel(controls_text, border_style="white", padding=(0, 1))
277+
return Panel(controls_text, border_style="white", padding=(0, 0))
278278

279279
def render(self) -> Layout:
280280
"""Render the complete display."""
@@ -319,10 +319,20 @@ def render(self) -> Layout:
319319

320320
def start_live_display(self):
321321
"""Start live updating display."""
322-
self.live = Live(self.render(), console=self.console, refresh_per_second=1, screen=True)
322+
self.live = Live(self.render(), console=self.console, refresh_per_second=0.5, screen=True)
323323
return self.live
324324

325325
def stop_live_display(self):
326326
"""Stop live display."""
327327
if self.live:
328328
self.live.stop()
329+
330+
def pause(self):
331+
"""Pause the live display for user input."""
332+
if self.live:
333+
self.live.stop()
334+
335+
def resume(self):
336+
"""Resume the live display after user input."""
337+
if self.live:
338+
self.live.start()

growatt_emulator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def __init__(self, model_key: str, port: int = 502):
5858
self.simulator = InverterSimulator(self.model, port)
5959
self.modbus_server = ModbusEmulatorServer(self.simulator, port)
6060
self.display = EmulatorDisplay(self.simulator)
61-
self.controls = ControlHandler(self.simulator, on_quit=self.stop)
61+
self.controls = ControlHandler(self.simulator, display=self.display, on_quit=self.stop)
6262

6363
def start(self) -> None:
6464
"""Start the emulator."""
@@ -90,7 +90,7 @@ def start(self) -> None:
9090
with self.display.start_live_display() as live:
9191
while self.running:
9292
live.update(self.display.render())
93-
time.sleep(1.0) # Update every second
93+
time.sleep(5.0) # Update every 5 seconds
9494

9595
except KeyboardInterrupt:
9696
print("\n\n⚠️ Interrupted by user")

0 commit comments

Comments
 (0)