Skip to content

Commit 2811320

Browse files
authored
dance party! (#97)
1 parent be35b2c commit 2811320

File tree

10 files changed

+134
-9
lines changed

10 files changed

+134
-9
lines changed

crystalfontz/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from crystalfontz.config import Config
88
from crystalfontz.cursor import CursorStyle
99
from crystalfontz.device import Device, DeviceStatus
10-
from crystalfontz.effects import Effect, EffectClient, Marquee, Screensaver
10+
from crystalfontz.effects import DanceParty, Effect, EffectClient, Marquee, Screensaver
1111
from crystalfontz.error import (
1212
ConnectionError,
1313
CrystalfontzError,
@@ -95,6 +95,7 @@
9595
"CursorPositionSet",
9696
"CursorStyle",
9797
"CursorStyleSet",
98+
"DanceParty",
9899
"DataSent",
99100
"DecodeError",
100101
"Device",

crystalfontz/cli.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,8 +1148,11 @@ async def marquee(
11481148
) -> None:
11491149
tick = obj.effect_options.tick if obj.effect_options else None
11501150
for_ = obj.effect_options.for_ if obj.effect_options else None
1151+
11511152
m = client.marquee(row, text, pause=pause, tick=tick)
11521153

1154+
await client.clear_screen()
1155+
11531156
await run_effect(m, client.loop, for_)
11541157

11551158

@@ -1163,6 +1166,23 @@ async def screensaver(obj: Obj, client: Client, text: str) -> None:
11631166
for_ = obj.effect_options.for_ if obj.effect_options else None
11641167
s = client.screensaver(text, tick=tick)
11651168

1169+
await client.clear_screen()
1170+
1171+
await run_effect(s, client.loop, for_)
1172+
1173+
1174+
@effects.command(help="Have a dance party!")
1175+
@click.argument("text")
1176+
@async_command
1177+
@pass_client()
1178+
@click.pass_obj
1179+
async def dance_party(obj: Obj, client: Client, text: str) -> None:
1180+
tick = obj.effect_options.tick if obj.effect_options else None
1181+
for_ = obj.effect_options.for_ if obj.effect_options else None
1182+
s = client.dance_party(text=text, tick=tick)
1183+
1184+
await client.clear_screen()
1185+
11661186
await run_effect(s, client.loop, for_)
11671187

11681188

crystalfontz/client.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ async def main():
9494
)
9595
from crystalfontz.cursor import CursorStyle
9696
from crystalfontz.device import Device, DeviceStatus, lookup_device
97-
from crystalfontz.effects import Marquee, Screensaver
97+
from crystalfontz.effects import DanceParty, Marquee, Screensaver
9898
from crystalfontz.error import (
9999
ConnectionError,
100100
CrystalfontzError,
@@ -1381,6 +1381,26 @@ def screensaver(
13811381
loop=self.loop,
13821382
)
13831383

1384+
def dance_party(
1385+
self: Self,
1386+
text: str,
1387+
tick: Optional[float] = None,
1388+
timeout: Optional[float] = None,
1389+
retry_times: Optional[int] = None,
1390+
) -> DanceParty:
1391+
"""
1392+
Display a dance party effect on the LCD screen.
1393+
"""
1394+
1395+
return DanceParty(
1396+
client=self,
1397+
text=text,
1398+
tick=tick,
1399+
timeout=timeout,
1400+
retry_times=retry_times,
1401+
loop=self.loop,
1402+
)
1403+
13841404

13851405
async def create_connection(
13861406
port: str,

crystalfontz/command.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ class SetContrast(Command):
220220

221221
def __init__(self: Self, contrast: float, device: Device) -> None:
222222
self.contrast = device.contrast(contrast)
223-
print(self.contrast[0], self.contrast[1])
224223

225224
def to_packet(self: Self) -> Packet:
226225
return (self.command, self.contrast)

crystalfontz/dbus/client/cli.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
select_session_bus,
6161
select_system_bus,
6262
)
63-
from crystalfontz.effects import Marquee, Screensaver
63+
from crystalfontz.effects import DanceParty, Marquee, Screensaver
6464
from crystalfontz.gpio import GpioDriveMode, GpioFunction
6565
from crystalfontz.lcd import LcdRegister
6666
from crystalfontz.temperature import (
@@ -961,6 +961,8 @@ async def marquee(
961961

962962
m = Marquee(client=client, row=row, text=text, pause=pause, tick=tick)
963963

964+
await client.clear_screen()
965+
964966
await run_effect(m, asyncio.get_running_loop(), for_)
965967

966968

@@ -975,4 +977,22 @@ async def screensaver(obj: Obj, client: DbusEffectClient, text: str) -> None:
975977

976978
s = Screensaver(client=client, text=text, tick=tick)
977979

980+
await client.clear_screen()
981+
978982
await run_effect(s, asyncio.get_running_loop(), for_)
983+
984+
985+
@effects.command(help="Have a dance party!")
986+
@click.argument("text")
987+
@async_command
988+
@pass_effect_client
989+
@click.pass_obj
990+
async def dance_party(obj: Obj, client: DbusEffectClient, text: str) -> None:
991+
tick = obj.effect_options.tick if obj.effect_options else None
992+
for_ = obj.effect_options.for_ if obj.effect_options else None
993+
994+
d = DanceParty(client=client, text=text, tick=tick)
995+
996+
await client.clear_screen()
997+
998+
await run_effect(d, asyncio.get_running_loop(), for_)

crystalfontz/dbus/effects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async def set_contrast(
9595

9696
async def set_backlight(
9797
self: Self,
98-
lcd_brightness: int,
98+
lcd_brightness: float,
9999
keypad_brightness: Optional[int] = None,
100100
timeout: Optional[float] = None,
101101
retry_times: Optional[int] = None,

crystalfontz/effects.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async def set_contrast(
5353

5454
async def set_backlight(
5555
self: Self,
56-
lcd_brightness: int,
56+
lcd_brightness: float,
5757
keypad_brightness: Optional[int] = None,
5858
timeout: Optional[float] = None,
5959
retry_times: Optional[int] = None,
@@ -182,7 +182,7 @@ async def render(self: Self) -> None:
182182
self.row, 0, buffer, timeout=self.timeout, retry_times=self.retry_times
183183
)
184184
self.shift += 1
185-
if self.shift >= device.columns:
185+
if self.shift > device.columns:
186186
self.shift = 0
187187

188188
def _line(self: Self) -> bytes:
@@ -195,6 +195,11 @@ def _line(self: Self) -> bytes:
195195

196196

197197
class Screensaver(Effect):
198+
"""
199+
A screensaver effect. Prints text at a random position, and moves it around the
200+
screen on an interval.
201+
"""
202+
198203
def __init__(
199204
self: Self,
200205
client: EffectClient,
@@ -207,7 +212,7 @@ def __init__(
207212
device = client.device
208213
buffer = device.character_rom.encode(text)
209214

210-
if len(buffer) >= device.columns:
215+
if len(buffer) > device.columns:
211216
raise ValueError(
212217
f"Text length {len(buffer)} is too long to fit onto the device's "
213218
f"{device.columns} columns"
@@ -236,3 +241,53 @@ async def render(self: Self) -> None:
236241
await self.client.send_data(
237242
row, column, self.text, timeout=self.timeout, retry_times=self.retry_times
238243
)
244+
245+
246+
class DanceParty(Effect):
247+
"""
248+
A dance party effect. Randomly changes the backlight and contrast settings on
249+
an interval.
250+
"""
251+
252+
def __init__(
253+
self: Self,
254+
client: EffectClient,
255+
text: str,
256+
tick: Optional[float] = None,
257+
timeout: Optional[float] = None,
258+
retry_times: Optional[int] = None,
259+
loop: Optional[asyncio.AbstractEventLoop] = None,
260+
) -> None:
261+
device = client.device
262+
buffer = device.character_rom.encode(text.center(device.columns))
263+
264+
if len(buffer) > device.columns:
265+
raise ValueError(
266+
f"Text length {len(buffer)} is too long to fit onto the device's "
267+
f"{device.columns} columns"
268+
)
269+
270+
super().__init__(
271+
client=client,
272+
tick=tick if tick is not None else 0.5,
273+
timeout=timeout,
274+
retry_times=retry_times,
275+
loop=loop,
276+
)
277+
278+
self.text: bytes = buffer
279+
280+
def _random_contrast(self: Self) -> float:
281+
return random.uniform(0.4, 0.6)
282+
283+
def _random_brightness(self: Self) -> float:
284+
return random.uniform(0.2, 0.8)
285+
286+
async def start(self: Self) -> None:
287+
await self.client.send_data(0, 0, self.text)
288+
289+
async def render(self: Self) -> None:
290+
await asyncio.gather(
291+
self.client.set_contrast(self._random_contrast()),
292+
self.client.set_backlight(self._random_brightness()),
293+
)

tests/integration/__snapshots__/test_cli.ambr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# serializer version: 1
22
# name: test_status
3-
b'CFA533 Status:\n--------------\nTemperature sensors enabled: \nKey states:\n up: pressed=no, pressed_since=yes, released_since=yes\n enter: pressed=no, pressed_since=yes, released_since=yes\n exit: pressed=no, pressed_since=yes, released_since=yes\n left: pressed=no, pressed_since=yes, released_since=yes\n right: pressed=no, pressed_since=yes, released_since=yes\n down: pressed=no, pressed_since=yes, released_since=yes\nATX Power Switch Functionality Settings:\n Functions enabled: \n Auto-Polarity Enabled: no\n Reset Inverted: no\n Power Inverted: no\n Power Pulse Length (seconds): None\nWatchdog Counter: 0\nContrast: 0.3137254901960784\nContrast (CFA633 Compatible): 0.24\nBacklight:\n Keypad Brightness: 0.2\n LCD Brightness: 0.2'
3+
b'CFA533 Status:\n--------------\nTemperature sensors enabled: \nKey states:\n up: keypress=KP_UP, pressed=no, pressed_since=yes, released_since=yes\n enter: keypress=KP_ENTER, pressed=no, pressed_since=yes, released_since=yes\n exit: keypress=KP_EXIT, pressed=no, pressed_since=yes, released_since=yes\n left: keypress=KP_LEFT, pressed=no, pressed_since=yes, released_since=yes\n right: keypress=KP_RIGHT, pressed=no, pressed_since=yes, released_since=yes\n down: keypress=KP_DOWN, pressed=no, pressed_since=yes, released_since=yes\nATX Power Switch Functionality Settings:\n Functions enabled: \n Auto-Polarity Enabled: no\n Reset Inverted: no\n Power Inverted: no\n Power Pulse Length (seconds): None\nWatchdog Counter: 0\nContrast: 0.3137254901960784\nContrast (CFA633 Compatible): 0.24\nBacklight:\n Keypad Brightness: 0.2\n LCD Brightness: 0.2'
44
# ---
55
# name: test_versions
66
b'CFA533: h1.4, u1v2'

tests/integration/test_cli.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def test_screensaver_for(cli: Cli) -> None:
9090
cli("effects", "--for", "1.0", "screensaver", "Josh!")
9191

9292

93+
def test_dance_party(cli, confirm) -> None:
94+
with cli.bg("effects", "dance-party", "Carameldansen!!"):
95+
confirm("Is the LCD showing a dance party effect?")
96+
97+
9398
@pytest.mark.skip
9499
def test_read_user_flash() -> None:
95100
raise NotImplementedError("test_read_user_flash")

tests/integration/test_dbus.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def test_screensaver_for(dbus_cli: Cli) -> None:
9090
dbus_cli("effects", "--for", "1.0", "screensaver", "Josh!")
9191

9292

93+
def test_dance_party(dbus_cli, confirm) -> None:
94+
with dbus_cli.bg("effects", "dance-party", "Carameldansen!!"):
95+
confirm("Is the LCD showing a dance party effect?")
96+
97+
9398
@pytest.mark.skip
9499
def test_read_user_flash() -> None:
95100
raise NotImplementedError("test_read_user_flash")

0 commit comments

Comments
 (0)