Skip to content

Commit

Permalink
feat: setup a wifi hotspot for when the a web-ui input is demanded an…
Browse files Browse the repository at this point in the history
…d the device is not connected to any network - closes #169
  • Loading branch information
sassanh committed Jan 7, 2025
1 parent d650308 commit 5e10935
Show file tree
Hide file tree
Showing 39 changed files with 748 additions and 565 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- refactor(core): rerender screen when rendering on the display is resumed like when returning from viewfinder to avoid artifacts
- refactor(docker): make composition menus responsive: showing spinner, etc
- fix: pass raw bytes to `DisplayRenderEvent` and `DisplayCompressedRenderEvent` to avoid encoding issues
- feat: setup a wifi hotspot for when the a web-ui input is demanded and the device is not connected to any network - closes #169

## Version 1.1.0

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ curl -sSL https://raw.githubusercontent.com/ubopod/ubo-app/main/ubo_app/system/i
Note that as part of the installation process, these debian packages are installed:

- accountsservice
- dhcpcd
- dnsmasq
- git
- hostapd
- i2c-tools
- libasound2-dev
- libcap-dev
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies = [
"gpiozero >=2.0.1 ; platform_machine != 'aarch64'",
"quart >=0.19.6",
"pythonping>=1.1.4",
"netifaces>=0.11.0",
]

[build-system]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-001
c680100b9cf09561e1adb62abfa8eb985a00c0468b392a6a1e772921807b5878
9f05a21f8d8a3ac3b65e10d2a3402c413b93cce20b27dcdf72b41abd9a6d6a4e
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-003
0f13319443f4340c4fc4b753226ba6e1605e569189dc8d58e403b4bc21c4c32a
c680100b9cf09561e1adb62abfa8eb985a00c0468b392a6a1e772921807b5878
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-004
c26e7c4aaeb369597c6cb9426df8f0a5bb6e4fb899dfec04634ff7c2e4b747b1
0f13319443f4340c4fc4b753226ba6e1605e569189dc8d58e403b4bc21c4c32a
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-005
f2a748ff52bd9c34a298ab624ff9a474ecae5883842941aca8ee9559cd92a6d7
c26e7c4aaeb369597c6cb9426df8f0a5bb6e4fb899dfec04634ff7c2e4b747b1
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-006
c26e7c4aaeb369597c6cb9426df8f0a5bb6e4fb899dfec04634ff7c2e4b747b1
f2a748ff52bd9c34a298ab624ff9a474ecae5883842941aca8ee9559cd92a6d7
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-007
12e31e10eadf55d4b824e563c2c18f2705d7261c6d401b6001d2b2e5ba92c808
c26e7c4aaeb369597c6cb9426df8f0a5bb6e4fb899dfec04634ff7c2e4b747b1
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-008
a47eeb0c63e483413e618f7c05dbcf97e832c945a0282876a4ee8c4b70767227
12e31e10eadf55d4b824e563c2c18f2705d7261c6d401b6001d2b2e5ba92c808
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// window-rpi-009
a47eeb0c63e483413e618f7c05dbcf97e832c945a0282876a4ee8c4b70767227
9 changes: 9 additions & 0 deletions tests/flows/test_wireless.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,20 @@ def check_icon(expected_icon: str) -> None:
# Select "Add" to add a new connection
store.dispatch(MenuChooseByLabelAction(label='Add'))
await stability()

# Input method selection should be shown
window_snapshot.take()

# Set QR Code image of the WiFi credentials before camera is started
camera.set_image('qrcode/wifi')

# Select "QR code" input method
store.dispatch(MenuChooseByIconAction(icon='󰄀'))
await stability()

# QR code instructions should be shown
window_snapshot.take()

# Select "QR code" to scan a QR code for credentials
store.dispatch(MenuChooseByIconAction(icon='󰄀'))

Expand Down
5 changes: 3 additions & 2 deletions ubo_app/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ def verbose(


def get_logger(name: str) -> UboLogger:
return cast(UboLogger, logging.getLogger(name))
logger = cast(UboLogger, logging.getLogger(name))
logger.propagate = False
return logger


def supports_truecolor() -> bool:
Expand All @@ -77,7 +79,6 @@ def supports_truecolor() -> bool:


logger = get_logger('ubo-app')
logger.propagate = False


class ExtraFormatter(logging.Formatter):
Expand Down
4 changes: 1 addition & 3 deletions ubo_app/menu_app/menu_notification_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@ def renew_notification(event: NotificationsDisplayEvent) -> None:
notification.is_initialized = True
store.dispatch(
VoiceReadTextAction(
text=event.notification.extra_information.text,
piper_text=event.notification.extra_information.piper_text,
picovoice_text=event.notification.extra_information.picovoice_text,
information=event.notification.extra_information,
),
)

Expand Down
4 changes: 1 addition & 3 deletions ubo_app/services/010-voice/reducer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ def reducer(
state=state,
events=[
VoiceSynthesizeTextEvent(
text=action.text,
piper_text=action.piper_text or action.text,
picovoice_text=action.picovoice_text or action.text,
information=action.information,
speech_rate=action.speech_rate,
),
],
Expand Down
18 changes: 10 additions & 8 deletions ubo_app/services/010-voice/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from ubo_app.store.core.types import RegisterSettingAppAction, SettingsCategory
from ubo_app.store.main import store
from ubo_app.store.services.audio import AudioPlayAudioAction, AudioPlaybackDoneEvent
from ubo_app.store.services.notifications import NotificationExtraInformation
from ubo_app.store.services.voice import (
ReadableInformation,
VoiceEngine,
VoiceReadTextAction,
VoiceSetEngineAction,
Expand Down Expand Up @@ -76,7 +76,7 @@ async def act() -> None:
access_key = (
await ubo_input(
title='Picovoice Access Key',
qr_code_generation_instructions=NotificationExtraInformation(
qr_code_generation_instructions=ReadableInformation(
text='Convert the Picovoice access key to a QR code and hold '
'it in front of the camera to scan it.',
picovoice_text='Convert the Picovoice access key to a '
Expand Down Expand Up @@ -113,7 +113,7 @@ def synthesize_and_play(event: VoiceSynthesizeTextEvent) -> None:
"""Synthesize the text."""
engine = _engine()
if engine == VoiceEngine.PIPER:
text = event.piper_text
text = event.information.piper_text

Check warning on line 116 in ubo_app/services/010-voice/setup.py

View check run for this annotation

Codecov / codecov/patch

ubo_app/services/010-voice/setup.py#L116

Added line #L116 was not covered by tests
if not _context.piper_voice:
return
id = hex(hash(text))
Expand Down Expand Up @@ -153,7 +153,7 @@ def synthesize_and_play(event: VoiceSynthesizeTextEvent) -> None:
rate = _context.picovoice_instance.sample_rate

audio_sequence = _context.picovoice_instance.synthesize(
text=event.picovoice_text,
text=event.information.picovoice_text,
speech_rate=event.speech_rate,
)
sample = b''.join(struct.pack('h', sample) for sample in audio_sequence[0])
Expand Down Expand Up @@ -205,10 +205,12 @@ def _engine_selector() -> None:
store.dispatch(
VoiceSetEngineAction(engine=engine),
VoiceReadTextAction(
text={
VoiceEngine.PIPER: 'Piper voice engine selected',
VoiceEngine.PICOVOICE: 'Picovoice voice engine selected',
}[engine],
information=ReadableInformation(
text={
VoiceEngine.PIPER: 'Piper voice engine selected',
VoiceEngine.PICOVOICE: 'Picovoice voice engine selected',
}[engine],
),
engine=engine,
),
)
Expand Down
6 changes: 3 additions & 3 deletions ubo_app/services/030-wifi/pages/create_wireless_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
Chime,
Notification,
NotificationDisplayType,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.store.services.wifi import WiFiType, WiFiUpdateRequestAction
from ubo_app.utils.async_ import create_task
from ubo_app.utils.input import ubo_input
Expand Down Expand Up @@ -57,9 +57,9 @@ def __init__(
async def create_wireless_connection(self: CreateWirelessConnectionPage) -> None:
try:
_, data = await ubo_input(
input_methods=InputMethod.CAMERA,
input_methods=InputMethod.ALL,
prompt='Enter WiFi connection',
qr_code_generation_instructions=NotificationExtraInformation(
qr_code_generation_instructions=ReadableInformation(
text='Go to your phone settings, choose QR code and hold it in '
'front of the camera to scan it.',
picovoice_text='Go to your phone settings, choose {QR|K Y UW AA R} '
Expand Down
4 changes: 2 additions & 2 deletions ubo_app/services/030-wifi/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
Notification,
NotificationActionItem,
NotificationDisplayType,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.store.services.wifi import (
ConnectionState,
WiFiSetHasVisitedOnboardingAction,
Expand Down Expand Up @@ -85,7 +85,7 @@ async def setup_listeners() -> None:
dismiss_notification=True,
),
],
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text='Press middle button to add WiFi network with QR code.\n'
'If you dismiss this, you can always add WiFi network through '
'Settings → Network → WiFi',
Expand Down
4 changes: 2 additions & 2 deletions ubo_app/services/050-rpi-connect/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
Importance,
Notification,
NotificationDisplayType,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.rpi_connect import (
Expand All @@ -27,6 +26,7 @@
RPiConnectStatus,
RPiConnectUpdateServiceStateAction,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.utils.apt import is_package_installed
from ubo_app.utils.async_ import create_task
from ubo_app.utils.monitor_unit import is_unit_active
Expand Down Expand Up @@ -208,7 +208,7 @@ async def act() -> None:
notification=Notification(
title='RPi-Connect',
content='LightDM is not running',
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text="""\
LightDM is not running so RPi-Connect will run without screen sharing and you can only \
use the remote shell feature. To enable screen sharing, start LightDM service in \
Expand Down
8 changes: 4 additions & 4 deletions ubo_app/services/050-users/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
Notification,
NotificationActionItem,
NotificationDisplayType,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.users import (
Expand All @@ -47,6 +46,7 @@
UsersState,
UserState,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.utils import IS_RPI
from ubo_app.utils.async_ import create_task
from ubo_app.utils.bus_provider import get_system_bus
Expand Down Expand Up @@ -85,7 +85,7 @@ async def create_account() -> None:
importance=Importance.MEDIUM,
icon='󰀈',
display_type=NotificationDisplayType.STICKY,
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text="""\
Note that in order to make ssh works for you, we had to make sure password \
authentication for ssh server is enabled, you may want to disable it later.""",
Expand Down Expand Up @@ -113,7 +113,7 @@ async def delete_account(event: UsersDeleteUserEvent) -> None:
content=f'Delete user "{event.id}"?',
display_type=NotificationDisplayType.STICKY,
is_read=True,
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text='This will delete the system user account and its home '
'directory.',
),
Expand Down Expand Up @@ -190,7 +190,7 @@ async def reset_password(event: UsersResetPasswordEvent) -> None:
importance=Importance.MEDIUM,
icon='󰀈',
display_type=NotificationDisplayType.STICKY,
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text="""\
Note that in order to make ssh works for you, we had to make sure password \
authentication for ssh server is enabled, you may want to disable it later.""",
Expand Down
7 changes: 4 additions & 3 deletions ubo_app/services/080-docker/docker_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from typing import TYPE_CHECKING, Any

from ubo_app.store.services.voice import ReadableInformation

if TYPE_CHECKING:
from collections.abc import Callable, Coroutine

Expand All @@ -14,7 +16,6 @@
from immutable import Immutable

from ubo_app.constants import DEBUG_MODE_DOCKER, GRPC_ENVOY_LISTEN_PORT
from ubo_app.store.services.notifications import NotificationExtraInformation
from ubo_app.utils.input import ubo_input

if TYPE_CHECKING:
Expand Down Expand Up @@ -113,7 +114,7 @@ class CompositionEntry(Immutable):
'NGROK_AUTHTOKEN': lambda: ubo_input(
resolver=lambda code, _: code,
prompt='Enter the Ngrok Auth Token',
qr_code_generation_instructions=NotificationExtraInformation(
qr_code_generation_instructions=ReadableInformation(
text="""\
Follow these steps:
Expand All @@ -135,7 +136,7 @@ class CompositionEntry(Immutable):
command=lambda: ubo_input(
resolver=lambda code, _: code,
prompt='Enter the command, for example: `http 80` or `tcp 22`',
qr_code_generation_instructions=NotificationExtraInformation(
qr_code_generation_instructions=ReadableInformation(
text='This is the command you would enter when running ngrok. '
'Refer to ngrok documentation for further information',
picovoice_text="""\
Expand Down
4 changes: 2 additions & 2 deletions ubo_app/services/080-docker/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
)
from ubo_app.store.services.notifications import (
Notification,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.utils.async_ import create_task

if TYPE_CHECKING:
Expand Down Expand Up @@ -162,7 +162,7 @@ def action() -> PageWidget:
icon='󰋗',
title='Instructions',
content='',
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text=image.instructions,
)
if image.instructions
Expand Down
6 changes: 3 additions & 3 deletions ubo_app/services/080-docker/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
Importance,
Notification,
NotificationDisplayType,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.store.services.voice import ReadableInformation
from ubo_app.utils import secrets
from ubo_app.utils.apt import is_package_installed
from ubo_app.utils.async_ import create_task
Expand Down Expand Up @@ -219,7 +219,7 @@ async def act() -> None:
credentials = (
await ubo_input(
prompt='Enter Docker Credentials',
qr_code_generation_instructions=NotificationExtraInformation(
qr_code_generation_instructions=ReadableInformation(
text="""To generate your QR code for login, format your \
details by separating your service, username, and password with the pipe symbol. For \
example, format it as "docker.io|johndoe|password" and then convert this text into a \
Expand Down Expand Up @@ -295,7 +295,7 @@ async def act() -> None:
notification=Notification(
title='Docker Credentials Error',
content='Invalid credentials',
extra_information=NotificationExtraInformation(
extra_information=ReadableInformation(
text=explanation,
),
importance=Importance.HIGH,
Expand Down
Loading

0 comments on commit 5e10935

Please sign in to comment.