Skip to content

Commit

Permalink
feat(web-ui): process input demands, dispatched on the bus
Browse files Browse the repository at this point in the history
  • Loading branch information
sassanh committed Oct 8, 2024
1 parent b53b971 commit e4fa394
Show file tree
Hide file tree
Showing 39 changed files with 726 additions and 376 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/integration_delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ name: CI/CD

on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
workflow_dispatch:

Expand Down Expand Up @@ -373,17 +377,19 @@ jobs:
name: Publish
if: >-
github.event_name == 'push' && github.ref == 'refs/heads/main' ||
github.event_name == 'pull_request' && github.head_ref == 'main'
github.event_name == 'pull_request' && github.event.pull_request.base.ref
== 'main'
needs:
- type-check
- lint
- test
- build
- images
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/${{ needs.build.outputs.name }}
url:
https://pypi.org/project/${{ needs.build.outputs.name }}/${{
needs.build.outputs.version }}
permissions:
id-token: write
steps:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- chore: remove what has remained from poetry in the codebase
- refactor(core): avoid truncating or coloring logs in log files
- feat(web-ui): add web-ui service
- feat(web-ui): process input demands, dispatched on the bus

## Version 1.0.0

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ This is the fastest, easiest, and recommended way to get started with Ubo App.

If you want to install the image on an existing operating system, then read on. Otherwise, skip this section.

---
______________________________________________________________________

⚠️ **Executing scripts directly from the internet with root privileges poses a significant security risk. It's generally a good practice to ensure you understand the script's content before running it. You can check the content of this particular script [here](https://raw.githubusercontent.com/ubopod/ubo-app/main/ubo_app/system/install.sh) before running it.**

---
______________________________________________________________________

To install ubo, run this command in a terminal shell:

Expand Down Expand Up @@ -177,9 +177,11 @@ uv run poe test

#### QR code

In development environment, the camera is probably not working as it is relying, on `picamera2`, so it may become challenging to test the flows relying on QR code input.
In development environment, the camera is probably not working, as it is relying on `picamera2`, so it may become challenging to test the flows relying on QR code input.

To address this, the `qrcode_input` method, in not-RPi environments, will try to get its input from `/tmp/qrcode_input.txt`. So, whenever you encounter a QR code input, you can write the content of the QR code in that file and the application will read it from there and continue the flow.
To address this, the camera module, in not-RPi environments, will try reading from `/tmp/qrcode_input.txt` and `/tmp/qrcode_input.png` too. So, whenever you encounter a QR code input, you can write the content of the QR code in the text file path or put the qrcode image itself in the image file path and the application will read it from there and continue the flow.

Alternatively you may be able to provide the input in the web-ui (needs refresh at the moment) or provide it by `InputProvideAction` in grpc channel.

## 🔒 License

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ dependencies = [
"platformdirs >=4.2.0",
"dill >=0.3.8",
"simpleaudio >=1.0.4",
"python-redux >=0.17.1",
"python-redux >=0.17.2",
"python-debouncer >=0.1.5",
"python-strtobool >=1.0.0",
"python-fake >=0.1.3",
Expand Down
68 changes: 49 additions & 19 deletions scripts/test_on_device.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o nounset

# Signal handler
function cleanup() {
run_on_pod "killall -9 pytest"
run_on_pod killall -9 pytest
}
trap cleanup ERR
trap cleanup SIGINT
Expand All @@ -17,16 +17,18 @@ run=${run:-"False"}
results=${results:-"False"}

function run_on_pod() {
echo $1
if [ $# -lt 1 ]; then
echo "Usage: run_on_pod_out_of_env <command>"
echo "Usage: run_on_pod <command>"
return 1
fi
if [ $# -eq 1 ]; then
ssh ubo-development-pod "sudo XDG_RUNTIME_DIR=/run/user/\$(id -u ubo) -u ubo bash -c 'cd; source /etc/profile && source \$HOME/.profile && ($1)'"
return 0
fi
return 1

# Use SSH to execute commands read from stdin
ssh ubo-development-pod "sudo XDG_RUNTIME_DIR=/run/user/\$(id -u ubo) -u ubo bash -s" <<EOF
cd
source /etc/profile
source "\$HOME/.profile"
$*
EOF
}

function run_on_pod_as_root() {
Expand All @@ -43,19 +45,47 @@ function run_on_pod_as_root() {

if [ "$copy" == "True" ]; then
# Since rsync is not called with -r, it treats ./scripts as an empty directory and its content are ignored, it could be any other random directory inside "./". It is needed solely to create the root directory with ubo:ubo ownership.
(echo './scripts'; git ls-files --others --exclude-standard --cached) | rsync --rsync-path="sudo rsync" --delete --info=progress2 -ae ssh --files-from=- --ignore-missing-args ./ ubo-development-pod:/home/ubo/test-runner/ --chown ubo:ubo
(echo ./scripts; echo ./ubo_app/_version.py; git ls-files --others --exclude-standard --cached) | rsync --rsync-path="sudo rsync" --delete --info=progress2 -ae ssh --files-from=- --ignore-missing-args ./ ubo-development-pod:/home/ubo/test-runner/ --chown ubo:ubo
fi

if [ "$run" == "True" ] || [ "$deps" == "True" ]; then
run_on_pod "$(if [ "$deps" == "True" ]; then echo "(uv --version ||
curl -LsSf https://astral.sh/uv/install.sh | sh) &&"; fi)
$(if [ "$run" == "True" ]; then echo "killall -9 pytest || true && systemctl --user stop ubo-app || true &&"; fi)
cd ~/test-runner &&
uv venv --system-site-packages &&
uv python pin python3.11 &&
$(if [ "$deps" == "True" ]; then echo "SETUPTOOLS_SCM_PRETEND_VERSION=$(uvx hatch version) uv sync --frozen &&"; fi)
$(if [ "$run" == "True" ]; then echo "uv run poe test --verbosity=2 --capture=no --make-screenshots -n1 $* || true &&"; fi)
true"
if [ "$run" == "True" ] || [ "$deps" == "True" ] || [ "$copy" == "True" ]; then
# Initialize an array to build the command
cmd_list=()

# Conditional commands based on the flags
if [ "$deps" == "True" ]; then
cmd_list+=("(uv --version || curl -LsSf https://astral.sh/uv/install.sh | sh) &&")
fi

if [ "$copy" == "True" ]; then
cmd_list+=('perl -pi -e "s|source = \"vcs\"|path = \"ubo_app/_version.py\"\\npattern = \"version = '\''\(?P<version>[^'\'']+\)'\''\"|" ~/test-runner/pyproject.toml && cd ~/test-runner && uv python pin python3.11 && uv venv --system-site-packages && true')
fi

if [ "$run" == "True" ]; then
cmd_list+=("killall -9 pytest || true && systemctl --user stop ubo-app || true &&")
fi

# Common commands
cmd_list+=("cd ~/test-runner &&")
cmd_list+=("uv venv --system-site-packages &&")
cmd_list+=("uv python pin python3.11 &&")

if [ "$deps" == "True" ]; then
cmd_list+=('SETUPTOOLS_SCM_PRETEND_VERSION=$(uvx hatch version) uv sync --frozen &&')
fi

if [ "$run" == "True" ]; then
cmd_list+=("uv run poe test --verbosity=2 --capture=no --make-screenshots -n1 $* || true &&")
fi

# Add a final true to ensure the command exits successfully
cmd_list+=("true")

# Combine the commands into a single string
cmd="${cmd_list[*]}"

# Execute the command on the pod
run_on_pod $cmd
fi

if [ "$run" == "True" ] || [ "$results" == True ]; then
Expand Down
6 changes: 3 additions & 3 deletions setup_scm_schemes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from setuptools_scm.version import get_local_node_and_date
from setuptools_scm.version import get_local_node_and_date # pyright: ignore
import re
from datetime import datetime
from datetime import datetime, timezone


def local_scheme(version):
Expand All @@ -11,4 +11,4 @@ def local_scheme(version):
)
original_local_version = get_local_node_and_date(version)
numeric_version = original_local_version.replace('+', '').replace('.d', '')
return datetime.utcnow().strftime('%y%m%d') + numeric_version
return datetime.now(timezone.utc).strftime('%y%m%d') + numeric_version
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-001
37ce32a12a80915276ecbf3176fdf9162f152f74a9f67e34e1df640f48e3f741
92b0e1d02d380eb831bcafb57b27c2f2afa023a52a95af44765c17754ca3de38
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// window-rpi-002
37ce32a12a80915276ecbf3176fdf9162f152f74a9f67e34e1df640f48e3f741
92b0e1d02d380eb831bcafb57b27c2f2afa023a52a95af44765c17754ca3de38
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
"_id": "e3e70682c2094cac629f6fbed82c07cd",
"_type": "RootState",
"input": null,
"main": {
"_type": "MainState",
"depth": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
"_id": "e3e70682c2094cac629f6fbed82c07cd",
"_type": "RootState",
"input": null,
"main": {
"_type": "MainState",
"depth": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"usernames": {}
}
},
"input": null,
"ip": {
"_type": "IpState",
"interfaces": [
Expand Down Expand Up @@ -1106,7 +1107,10 @@
"is_pending": false,
"status": null
},
"web_ui": null,
"web_ui": {
"_type": "WebUIState",
"active_inputs": []
},
"wifi": {
"_type": "WiFiState",
"connections": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"usernames": {}
}
},
"input": null,
"ip": {
"_type": "IpState",
"interfaces": [
Expand Down Expand Up @@ -1176,7 +1177,10 @@
"is_pending": false,
"status": null
},
"web_ui": null,
"web_ui": {
"_type": "WebUIState",
"active_inputs": []
},
"wifi": {
"_type": "WiFiState",
"connections": [],
Expand Down
2 changes: 1 addition & 1 deletion ubo_app/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
GRPC_LISTEN_HOST = os.environ.get('UBO_GRPC_LISTEN_HOST', '127.0.0.1')
GRPC_LISTEN_PORT = int(os.environ.get('UBO_GRPC_LISTEN_PORT', '50051'))

WEB_UI_LISTEN_HOST = os.environ.get('UBO_WEB_UI_LISTEN_HOST', '127.0.0.1')
WEB_UI_LISTEN_HOST = os.environ.get('UBO_WEB_UI_LISTEN_HOST', '0.0.0.0') # noqa: S104
WEB_UI_LISTEN_PORT = int(os.environ.get('UBO_WEB_UI_LISTEN_PORT', '21215'))
WEB_UI_DEBUG_MODE = str_to_bool(os.environ.get('UBO_WEB_UI_DEBUG_MODE', 'False')) == 1

Expand Down
22 changes: 15 additions & 7 deletions ubo_app/load_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def __init__(
def register_reducer(self: UboServiceThread, reducer: ReducerType) -> None:
from ubo_app.store.main import root_reducer_id, store

logger.info(
logger.debug(
'Registering ubo service reducer',
extra={
'service_id': self.service_id,
Expand All @@ -205,7 +205,7 @@ def register(
setup: SetupFunction,
) -> None:
if service_id in DISABLED_SERVICES:
logger.info(
logger.debug(

Check warning on line 208 in ubo_app/load_services.py

View check run for this annotation

Codecov / codecov/patch

ubo_app/load_services.py#L208

Added line #L208 was not covered by tests
'Skipping disabled ubo service',
extra={
'service_id': service_id,
Expand All @@ -216,7 +216,7 @@ def register(
return

if WHITE_LIST and service_id not in WHITE_LIST:
logger.info(
logger.debug(

Check warning on line 219 in ubo_app/load_services.py

View check run for this annotation

Codecov / codecov/patch

ubo_app/load_services.py#L219

Added line #L219 was not covered by tests
'Service is not in services white list',
extra={
'service_id': service_id,
Expand All @@ -230,7 +230,7 @@ def register(
self.service_id = service_id
self.setup = setup

logger.info(
logger.debug(
'Ubo service registered!',
extra={
'service_id': self.service_id,
Expand Down Expand Up @@ -306,6 +306,15 @@ def run(self: UboServiceThread) -> None:

self.loop.run_forever()

logger.info(
'Ubo service thread stopped',
extra={
'thread_native_id': self.native_id,
'service_label': self.label,
'service_id': self.service_id,
},
)

def __repr__(self: UboServiceThread) -> str:
return (
f'<UboServiceThread id='
Expand All @@ -332,8 +341,8 @@ def task_wrapper(stack: str) -> None:
async def shutdown(self: UboServiceThread) -> None:
from ubo_app.logging import logger

logger.info(
'Shutting down service thread',
logger.debug(
'Stopping service thread',
extra={
'thread_native_id': self.native_id,
'service_label': self.label,
Expand Down Expand Up @@ -368,7 +377,6 @@ async def shutdown(self: UboServiceThread) -> None:
)
await asyncio.sleep(0.1)

logger.info('Stopping event loop', extra={'thread_': self})
self.loop.stop()

def stop(self: UboServiceThread) -> None:
Expand Down
3 changes: 1 addition & 2 deletions ubo_app/rpc/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
from ubo_app.rpc.generated.ubo.v1 import Event
from ubo_app.rpc.message_to_object import get_class, rebuild_object, reduce_group
from ubo_app.rpc.object_to_message import build_message
from ubo_app.store.main import store
from ubo_app.store.operations import UboAction, UboEvent
from ubo_app.store.main import UboAction, UboEvent, store

if TYPE_CHECKING:
from collections.abc import AsyncIterator
Expand Down
4 changes: 2 additions & 2 deletions ubo_app/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async def shutdown(self: WorkerThread) -> None:
from ubo_app.constants import MAIN_LOOP_GRACE_PERIOD
from ubo_app.logging import logger

logger.info('Shutting down worker thread')
logger.info('Stopping worker thread')

while True:
tasks = [
Expand Down Expand Up @@ -93,7 +93,7 @@ async def shutdown(self: WorkerThread) -> None:
)
await asyncio.sleep(0.1)

logger.info('Stopping event loop', extra={'thread_': self})
logger.debug('Stopping event loop', extra={'thread_': self})
self.loop.stop()
self.is_finished.set()

Expand Down
5 changes: 3 additions & 2 deletions ubo_app/services/010-voice/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
)
from ubo_app.utils import secrets
from ubo_app.utils.async_ import create_task, to_thread
from ubo_app.utils.input import ubo_input
from ubo_app.utils.persistent_store import register_persistent_store
from ubo_app.utils.qrcode import qrcode_input

if TYPE_CHECKING:
from collections.abc import Callable, Sequence
Expand Down Expand Up @@ -73,10 +73,11 @@ def input_access_key() -> None:
async def act() -> None:
try:
access_key = (
await qrcode_input(
await ubo_input(
'.*',
prompt='Convert the Picovoice access key to a QR code and '
'scan it.',
title='Picovoice Access Key',
)
)[0]
secrets.write_secret(key=PICOVOICE_ACCESS_KEY, value=access_key)
Expand Down
Loading

0 comments on commit e4fa394

Please sign in to comment.