99
1010 0x00 digit 0 (leftmost)
1111 0x02 digit 1
12- 0x04 colon (bit 1 = centre colon)
12+ 0x04 colon + indicator dots (bit 1 = centre colon, bit 2 = upper-left
13+ dot, bit 3 = lower-left dot, bit 4 = upper-right dot)
1314 0x06 digit 2
1415 0x08 digit 3 (rightmost)
16+
17+ The lower-left indicator dot doubles as the NTP-sync indicator: lit while the
18+ system clock is synced, dark while it free-runs.
1519"""
1620
1721from __future__ import annotations
4145_DIGIT_ADDR = (0 , 2 , 6 , 8 ) # RAM offset of each of the four digit positions
4246_COLON_ADDR = 4
4347_COLON_BIT = 0x02
48+ _SYNC_DOT_BIT = 0x08 # lower-left indicator dot, shares the colon's RAM byte
4449
4550# HT16K33 command bytes.
4651_CMD_OSCILLATOR_ON = 0x21 # system setup: oscillator on
4752_CMD_DISPLAY_ON = 0x81 # display setup: on, no blink
4853_CMD_DIMMING = 0xE0 # | level (0-15)
4954
5055
51- def build_buffer (text : str , colon : bool ) -> list [int ]:
52- """Encode four characters + colon into the 16-byte HT16K33 RAM buffer.
56+ def build_buffer (text : str , colon : bool , sync_dot : bool = False ) -> list [int ]:
57+ """Encode four characters + colon + sync dot into the 16-byte HT16K33 RAM buffer.
5358
5459 Pure and hardware-free so it can be unit-tested. Raises ValueError on a
5560 wrong-length string or an unsupported character.
@@ -62,14 +67,14 @@ def build_buffer(text: str, colon: bool) -> list[int]:
6267 buf [_DIGIT_ADDR [i ]] = _FONT [char ]
6368 except KeyError :
6469 raise ValueError (f"unsupported character { char !r} in { text !r} " ) from None
65- buf [_COLON_ADDR ] = _COLON_BIT if colon else 0
70+ buf [_COLON_ADDR ] = ( _COLON_BIT if colon else 0 ) | ( _SYNC_DOT_BIT if sync_dot else 0 )
6671 return buf
6772
6873
6974class Display (Protocol ):
7075 """Minimal display interface the app loop depends on."""
7176
72- def show (self , text : str , colon : bool ) -> None : ...
77+ def show (self , text : str , colon : bool , sync_dot : bool = False ) -> None : ...
7378 def set_brightness (self , level : int ) -> None : ...
7479 def clear (self ) -> None : ...
7580 def close (self ) -> None : ...
@@ -78,7 +83,7 @@ def close(self) -> None: ...
7883class NullDisplay :
7984 """No-op display for development and tests off the Pi."""
8085
81- def show (self , text : str , colon : bool ) -> None :
86+ def show (self , text : str , colon : bool , sync_dot : bool = False ) -> None :
8287 pass
8388
8489 def set_brightness (self , level : int ) -> None :
@@ -99,7 +104,7 @@ def __init__(self, bus_num: int = 1, address: int = 0x70, brightness: int = 3) -
99104
100105 self ._address = address
101106 self ._bus = SMBus (bus_num )
102- self ._last : tuple [str , bool ] | None = None
107+ self ._last : tuple [str , bool , bool ] | None = None
103108 self ._last_brightness : int | None = None
104109
105110 try :
@@ -117,11 +122,13 @@ def set_brightness(self, level: int) -> None:
117122 self ._bus .write_byte (self ._address , _CMD_DIMMING | level )
118123 self ._last_brightness = level
119124
120- def show (self , text : str , colon : bool ) -> None :
121- key = (text , colon )
125+ def show (self , text : str , colon : bool , sync_dot : bool = False ) -> None :
126+ key = (text , colon , sync_dot )
122127 if key == self ._last :
123128 return # avoid redundant I2C traffic (e.g. unchanged minute)
124- self ._bus .write_i2c_block_data (self ._address , 0x00 , build_buffer (text , colon ))
129+ self ._bus .write_i2c_block_data (
130+ self ._address , 0x00 , build_buffer (text , colon , sync_dot )
131+ )
125132 self ._last = key
126133
127134 def clear (self ) -> None :
@@ -194,12 +201,12 @@ def _drop(self, exc: OSError) -> None:
194201 except Exception : # best effort after an I/O failure
195202 pass
196203
197- def show (self , text : str , colon : bool ) -> None :
204+ def show (self , text : str , colon : bool , sync_dot : bool = False ) -> None :
198205 dev = self ._connect ()
199206 if dev is None :
200207 return
201208 try :
202- dev .show (text , colon )
209+ dev .show (text , colon , sync_dot )
203210 except OSError as exc :
204211 self ._drop (exc )
205212
0 commit comments