Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add homeassistant-supervisor container to improve Home Assistant Voice Assistant stack onboarding on Jetson devices #505

Draft
wants to merge 38 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f4ef0e8
feat(piper-tts): add piper-phonemize test
ms1design May 8, 2024
8e54b28
feat(homeassistant-supervisor): migrate ciso8601 to separate package …
ms1design May 8, 2024
61a8cee
feat(homeassistant-supervisor): migrate psutil-home-assistant to sepa…
ms1design May 8, 2024
d7fdae0
feat(homeassistant-supervisor): add eudev dependency
ms1design May 8, 2024
4bc0acc
feat(homeassistant-base): proper versioning for bashio, tempio & S6 i…
ms1design May 8, 2024
f99e8a8
feat(homeassistant-core): migration cleanup
ms1design May 8, 2024
d061ea7
feat(homeassistant-core): refactor the build.sh script
ms1design May 8, 2024
c5777ba
feat(homeassistant-core): improve the versioning in config.py
ms1design May 8, 2024
157257f
feat(wyoming-assist-microphone): improve the versioning in config.py
ms1design May 8, 2024
22c38ce
feat(wyoming-openwakeword): improve the versioning in config.py
ms1design May 8, 2024
85219c4
feat(wyoming-piper): improve the versioning in config.py
ms1design May 8, 2024
4a30d02
feat(wyoming-whisper): improve the versioning in config.py
ms1design May 8, 2024
6b4b0f6
feat(homeassistant-supervisor): introduce homeassistant-supervisor co…
ms1design May 8, 2024
13f37be
feat(jetson-containers): add new versioning methods to utils
ms1design May 8, 2024
70f009b
feat(homeassistant-supervised): [WIP] experimental container
ms1design May 8, 2024
162c9ab
Merge remote-tracking branch 'origin/dev' into feature/homeassistant-…
ms1design May 15, 2024
17c3c1a
feat(go-lang): add new go-lang container
ms1design May 15, 2024
0179781
test(eudev): add test
ms1design May 15, 2024
15e067a
test(homeassistant-core): fixed test declaration
ms1design May 15, 2024
0fc1978
feat(homeassistant-os-agent): add homeassistant-os-agent container
ms1design May 15, 2024
d8e8590
feat(homeassistant-supervised): experiments
ms1design May 15, 2024
4d48cd1
fix(homeassistant-base): move init to containers
ms1design May 15, 2024
da404f8
fix(wyoming-assist-microphone): add required image labels for HA Supe…
ms1design May 15, 2024
7b8e425
fix(wyoming-openwakeword): add required image labels for HA Supervisor
ms1design May 15, 2024
eb4589e
fix(wyoming-piper): add required image labels for HA Supervisor
ms1design May 15, 2024
9cc4b24
fix(wyoming-whisper): add required image labels for HA Supervisor
ms1design May 15, 2024
8a7d655
fix(homeassistant): move init to containers
ms1design May 15, 2024
7699136
fix(homeassistant-supervisor): next iteration
ms1design May 15, 2024
7e0c205
feat(go-lang): add versioning in config.py
ms1design May 16, 2024
f60114a
fix(eudev): fix test.sh
ms1design May 16, 2024
d33c103
feat(homeassistant-os-agent): WIP
ms1design May 16, 2024
9f4da6b
feat(homeassistant-supervised): WIP
ms1design May 16, 2024
0cd64b8
fix(wyoming-assist-microphone): fix tests
ms1design May 16, 2024
51ac223
feat(wyoming-assist-microphone): enabled wyoming dicovery
ms1design May 16, 2024
33236b6
feat(wyoming-assist-microphone): sort out docker image labels (WIP)
ms1design May 16, 2024
db876a6
feat(wyoming-containers): fix tests
ms1design May 16, 2024
a98b247
feat(homeassistant-supervisor): next iteration
ms1design May 16, 2024
d23919b
feat(homeassistant-supervisor): add install & uninstall script for ho…
ms1design May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jetson_containers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .packages import *
from .container import *
from .l4t_version import *
from .utils import github_latest_commit
from .utils import handle_json_request, handle_text_request, github_latest_commit, github_latest_tag, get_json_value_from_url
141 changes: 121 additions & 20 deletions jetson_containers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
import os
import grp
import sys
import json
import pprint
import urllib.request
from urllib.request import urlopen, Request
import requests


def check_dependencies(install=True):
Expand Down Expand Up @@ -132,28 +130,131 @@ def sudo_prefix(group='docker'):
return "sudo "
else:
return ""


def github_latest_commit(repo, branch='main'):


def handle_text_request(url) -> str | None:
"""
Handles a request to fetch text data from the given URL.

Args:
url (str): The URL from which to fetch text data.

Returns:
str or None: The fetched text data, stripped of leading and trailing whitespace,
or None if an error occurs.
"""
Returns the SHA of the latest commit to the given github user/repo/branch.
try:
response = requests.get(url)
if response.status_code == 200:
return response.text.strip()
else:
print("Failed to fetch version information. Status code:", response.status_code)
return None
except Exception as e:
print("An error occurred:", e)
return None


def handle_json_request(url, headers=None):
"""
Handles a JSON request from the given URL with optional headers and returns the parsed JSON data.

Args:
url (str): The URL from which to fetch the JSON data.
headers (dict, optional): Headers to include in the request. Defaults to None.

Returns:
dict or None: The parsed JSON data as a dictionary, or None if an error occurs.
"""
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
print(f"HTTP error occurred: {e}")
return None
except requests.RequestException as e:
print(f"Error occurred: {e}")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None


def github_api(url):
"""
Sends a request to the GitHub API using the specified URL, including authorization headers if available.

Args:
url (str): The GitHub API URL endpoint relative to the base URL.

Returns:
dict or None: The parsed JSON response data as a dictionary, or None if an error occurs.
"""
url = f"https://api.github.com/repos/{repo}/commits/{branch}"
github_token = os.environ.get('GITHUB_TOKEN')
headers = {'Authorization': f'token {github_token}'} if github_token else None
request_url = f'https://api.github.com/{url}'

return handle_json_request(request_url, headers)

if github_token:
log_debug(f"-- GITHUB_TOKEN={github_token}")
headers = {'Authorization': 'token %s' % github_token}
request = Request(url, headers=headers)
else:
request = Request(url)

response = urlopen(request)
data = response.read()
encoding = response.info().get_content_charset('utf-8')
msg = json.loads(data.decode(encoding))

def github_latest_commit(repo, branch='main'):
"""
Retrieves the latest commit SHA from the specified branch of a GitHub repository.

Args:
repo (str): The full name of the GitHub repository in the format 'owner/repo'.
branch (str, optional): The branch name. Defaults to 'main'.

Returns:
str or None: The SHA (hash) of the latest commit, or None if no commit is found.
"""
commit_info = github_api(f"repos/{repo}/commits/{branch}")
return commit_info.get('sha') if commit_info else None


def github_latest_tag(repo):
"""
Retrieves the latest tag name from the specified GitHub repository.

Args:
repo (str): The full name of the GitHub repository in the format 'owner/repo'.

Returns:
str or None: The name of the latest tag, or None if no tags are found.
"""
tags = github_api(f"repos/{repo}/tags")
return tags[0].get('name') if tags else None

return msg['sha']

def get_json_value_from_url(url, notation=None):
"""
Retrieves JSON data from the given URL and returns either the whole data or a specified nested value using a dot notation string.

Args:
url (str): The URL from which to fetch the JSON data.
notation (str, optional): A dot notation string specifying the nested property to retrieve.
If None or an empty string is provided, the entire JSON data is returned.

Returns:
str or dict: The value of the specified nested property or the whole data if `notation` is None.
Returns None if the specified property does not exist.
"""
data = handle_json_request(url)

if notation and data is not None:
keys = notation.split('.') if '.' in notation else [notation]
current = data

try:
for key in keys:
current = current[key]
return str(current).strip()
except KeyError as e:
print(f'ERROR: Failed to get the value for {notation}: {e}')
return None

return data


def log_debug(*args, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion packages/audio/piper-tts/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# name: piper-tts
# group: audio
# depends: [onnxruntime]
# test: test.py
# test: [test_piper_phonemize.sh, test.py]
#---
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
Expand Down
12 changes: 12 additions & 0 deletions packages/audio/piper-tts/test_piper_phonemize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

set -ex

echo "Testing piper-phonemize..."

${PIPER_PHONEMIZE_DIR}/piper_phonemize -l en-us --espeak-data ${ESPEAK_NG_DATA_DIR} <<EOF
This is a test.
This is another test!
EOF

echo "piper-phonemize OK"
32 changes: 32 additions & 0 deletions packages/build/go-lang/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#---
# name: go-lang
# group: build
# config: config.py
# depends: [build-essential]
# notes: installs go lang
# test: test.sh
#---

ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG GO_VERSION \
GO_INSTALL_DIR=/usr/local

# Install golang
RUN wget --quiet --show-progress --progress=bar:force:noscroll \
https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz \
-O /tmp/go${GO_VERSION}.linux-arm64.tar.gz \
&& rm -rf ${GO_INSTALL_DIR}/go \
&& tar -C ${GO_INSTALL_DIR} -xzf /tmp/go${GO_VERSION}.linux-arm64.tar.gz \
&& rm /tmp/go${GO_VERSION}.linux-arm64.tar.gz

# set Go env variables
ENV PATH="$PATH:${GO_INSTALL_DIR}/go/bin" \
GOOS=linux \
GOARCH=arm64

# Test golang version
RUN set -ex \
&& go version \
&& go env
16 changes: 16 additions & 0 deletions packages/build/go-lang/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
def create_package(version, default=False) -> list:
pkg = package.copy()

pkg['name'] = f'go-lang:{version}'
pkg['build_args'] = {
'GO_VERSION': version,
}

if default:
pkg['alias'] = 'go-lang'

return pkg

package = [
create_package('1.19', default=True),
]
7 changes: 7 additions & 0 deletions packages/build/go-lang/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}
13 changes: 13 additions & 0 deletions packages/build/go-lang/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

set -e

echo "testing go-lang..."

go version

go env

go run /${GOPATH}/test/test.go

echo "go-lang OK"
20 changes: 20 additions & 0 deletions packages/smart-home/dependencies/ciso8601/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#---
# name: ciso8601
# group: smart-home
# config: config.py
# requires: '>=34.1.0'
# depends: [build-essential, python:3.12]
# test: test.py
# notes: The `ciso8601` wheel that's build is saved in `/opt/wheels`
#---
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG FORCE_BUILD=off \
CISO8601_VERSION

WORKDIR /

COPY *.diff *.sh /tmp/ciso8601/

RUN /tmp/ciso8601/install.sh || /tmp/ciso8601/build.sh
12 changes: 12 additions & 0 deletions packages/smart-home/dependencies/ciso8601/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# ciso8601
set -xo pipefail

git clone --branch=${CISO8601_VERSION} https://github.com/closeio/ciso8601 /opt/ciso8601
git -C /opt/ciso8601 apply /tmp/ciso8601/patch.diff
git -C /opt/ciso8601 diff

pip3 wheel --wheel-dir=/opt/wheels --no-deps --verbose /opt/ciso8601
rm -rf /opt/ciso8601

twine upload --verbose /opt/wheels/ciso8601*.whl || echo "failed to upload wheel to ${TWINE_REPOSITORY_URL}"
25 changes: 25 additions & 0 deletions packages/smart-home/dependencies/ciso8601/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from jetson_containers import github_latest_tag


def create_package(version, default=False):
pkg = package.copy()
wanted_version = github_latest_tag('closeio/ciso8601') if version == 'latest' else version

pkg['name'] = f'ciso8601:{version}'
pkg['build_args'] = {
'CISO8601_VERSION': wanted_version,
}

builder = pkg.copy()
builder['name'] = f'ciso8601:{version}-builder'
builder['build_args'] = {**pkg['build_args'], **{'FORCE_BUILD': 'on'}}

if default:
pkg['alias'] = 'ciso8601'
builder['alias'] = 'ciso8601:builder'

return pkg, builder

package = [
create_package('latest', default=True),
]
16 changes: 16 additions & 0 deletions packages/smart-home/dependencies/ciso8601/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# ciso8601

set -euxo pipefail

if [ "$FORCE_BUILD" == "on" ]; then
echo "Forcing build of ciso8601 ${CISO8601_VERSION}..."
exit 1
fi

echo "Installing ciso8601 ${CISO8601_VERSION}..."

pip3 install --no-cache-dir --verbose ciso8601==${CISO8601_VERSION}

pip3 show ciso8601
python3 -c 'import ciso8601; print(ciso8601.__version__);'
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import ciso8601


print("testing ciso8601...")

import ciso8601

print(ciso8601.__version__)

# Parse an ISO 8601 formatted date string
Expand Down
18 changes: 18 additions & 0 deletions packages/smart-home/dependencies/eudev/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#---
# name: eudev
# group: smart-home
# config: config.py
# test: test.sh
# requires: '>=34.1.0'
# depends: [build-essential]
#---
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG EUDEV_VERSION

WORKDIR /

COPY *.sh /tmp/eudev/

RUN /tmp/eudev/build.sh
Loading