Compare commits
144 Commits
e1d2f013d4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
380d8f8e48
|
|||
|
07a297f818
|
|||
|
d7224b038b
|
|||
|
fc62219db7
|
|||
|
2ebd97c345
|
|||
|
270e86bfd0
|
|||
|
7305e3a35b
|
|||
|
aabbd8286f
|
|||
|
37f7d442a1
|
|||
|
3ff805fa39
|
|||
|
1ae62e70ed
|
|||
|
91f4687c07
|
|||
|
dc2df62d04
|
|||
|
b75aac76c2
|
|||
|
5161dced6e
|
|||
|
d9ed144578
|
|||
|
5516f9530b
|
|||
|
621be95870
|
|||
|
b526901546
|
|||
|
b328081b59
|
|||
|
113b859927
|
|||
|
57ff005186
|
|||
|
7ccedb9768
|
|||
|
ef527abef4
|
|||
|
75f4aaebf1
|
|||
|
1396e09227
|
|||
|
cbe8c4a369
|
|||
|
2f88c75655
|
|||
|
0f4b73720c
|
|||
|
b97f41eb70
|
|||
|
6df02e8dff
|
|||
|
57ae6b7e72
|
|||
|
e3ba1759c4
|
|||
|
af70d1d396
|
|||
|
5b474c7190
|
|||
|
d94cd01008
|
|||
|
afb27c512c
|
|||
|
a500c8a572
|
|||
|
c5748d81da
|
|||
|
b38390029f
|
|||
|
b116ea73ec
|
|||
|
920aeef7f3
|
|||
|
9038962f29
|
|||
|
3fed164193
|
|||
|
487e03c0bd
|
|||
|
cf0a7373d4
|
|||
|
e0adee5362
|
|||
|
8f3e624925
|
|||
| e1e551c5cc | |||
| 23d3949421 | |||
| 714dd32ff6 | |||
| 8035fa38dc | |||
| b91cc1adc3 | |||
| 4fe56de990 | |||
| 9ef631b266 | |||
| 8c39f749c7 | |||
| 1361c726d9 | |||
| 1879158b6c | |||
| 7b9968762a | |||
| 250ffeb266 | |||
| de6c1941c5 | |||
| 9bc09a4b98 | |||
| 79377b3653 | |||
| d44bca3f2b | |||
| 660735f0ae | |||
| 6dfd30e175 | |||
| 0e5250d84d | |||
| 556149c583 | |||
| 72e13f53aa | |||
| e9c68abeb9 | |||
| 69e8e89e72 | |||
| 85e74541c2 | |||
| cb66fb6195 | |||
| 8d98cd06fa | |||
| a85627b3b2 | |||
| f046e6edc2 | |||
| a32f055ede | |||
| 0c6509cc17 | |||
| 82b60c086c | |||
| 999869cab6 | |||
| 548cdc8b87 | |||
| 4832b283bb | |||
| 9e83048248 | |||
| f2d684fa7c | |||
| 7980bfb381 | |||
| 20690c48e5 | |||
| ca582333f1 | |||
| dae4063f25 | |||
| 5184c84d50 | |||
| 3f3a03ee05 | |||
| 22c1d635c6 | |||
| 5512c266eb | |||
| de8b827cfb | |||
| 7b93f740ec | |||
| b3e4a45996 | |||
| ab2b033c54 | |||
|
|
c2fa408c1e | ||
|
|
a469444811 | ||
| ed2088d0dc | |||
| 7099e72d6f | |||
| d44773389e | |||
| 03e959c215 | |||
| b45bcd802e | |||
| 38b81fda9a | |||
| 960e91f911 | |||
| 4723ffb13d | |||
| ef9104c796 | |||
| 6e393d90ee | |||
| 9acff25d43 | |||
| 3752f9da61 | |||
| 9417e711a9 | |||
| 9a3382862d | |||
| 237a906b68 | |||
| 85de8a54d9 | |||
| e30db947b0 | |||
| 3865e64b19 | |||
| 7a2589d01f | |||
| e57bb30ccb | |||
| 2fbdde303a | |||
| 7479bcd493 | |||
| 34fad5846f | |||
| ecf66a5fb2 | |||
| 74d1f7364a | |||
| b9386f32b6 | |||
| fb5ac88686 | |||
| 2a421392b1 | |||
| b3fb0f1dd8 | |||
| 90db511077 | |||
| c70d992221 | |||
| 559febeaea | |||
| 10599fae23 | |||
| 5844fa224f | |||
| ae0e17c66f | |||
| 3c7c1ce60a | |||
| d53d95df1a | |||
| d7bb612677 | |||
| 495d2bb159 | |||
| 3c1e5540af | |||
| 00b6ea4058 | |||
| c1c82f8f04 | |||
| f27e9548c8 | |||
| 7472dce736 | |||
| 3a8dc44bbd | |||
| f9d50950ab |
30
.gitea/workflows/caddy.yaml
Normal file
30
.gitea/workflows/caddy.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Podman DDNS Image
|
||||
run-name: Build and Push the Custom Caddy Image with Route53 DNS Certbot
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- active/podman_caddy/**
|
||||
- .gitea/workflows/caddy.yaml
|
||||
schedule:
|
||||
- cron: '@daily'
|
||||
jobs:
|
||||
build-and-push-ddns:
|
||||
runs-on: ubuntu-latest
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.reeseapps.com
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Build and push Docker image
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: ${{ gitea.workspace }}/active/podman_caddy
|
||||
file: ${{ gitea.workspace }}/active/podman_caddy/Containerfile
|
||||
push: true
|
||||
tags: "gitea.reeseapps.com/services/caddy:latest,gitea.reeseapps.com/services/caddy:${{gitea.sha}}"
|
||||
no-cache: true
|
||||
30
.gitea/workflows/ddns.yaml
Normal file
30
.gitea/workflows/ddns.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Podman DDNS Image
|
||||
run-name: Build and Push the Podman DDNS Image
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- active/podman_ddns/**
|
||||
- .gitea/workflows/ddns.yaml
|
||||
schedule:
|
||||
- cron: '@daily'
|
||||
jobs:
|
||||
build-and-push-ddns:
|
||||
runs-on: ubuntu-latest
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.reeseapps.com
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Build and push Docker image
|
||||
uses: https://github.com/docker/build-push-action@v5
|
||||
with:
|
||||
context: ${{ gitea.workspace }}/active/podman_ddns
|
||||
file: ${{ gitea.workspace }}/active/podman_ddns/Containerfile
|
||||
push: true
|
||||
tags: "gitea.reeseapps.com/services/ddns:latest,gitea.reeseapps.com/services/ddns:${{gitea.sha}}"
|
||||
no-cache: true
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,4 +2,12 @@ secrets/
|
||||
venv/
|
||||
tmp/
|
||||
Unsorted/
|
||||
volumes/
|
||||
volumes/
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
.venv/
|
||||
.mypy_cache
|
||||
TODO.md
|
||||
eicar.com
|
||||
*.pp
|
||||
*.mod
|
||||
17
.vscode/code_oss_extensions.txt
vendored
Normal file
17
.vscode/code_oss_extensions.txt
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
charliermarsh.ruff
|
||||
eamodio.gitlens
|
||||
franneck94.vscode-python-config
|
||||
franneck94.vscode-python-dev-extension-pack
|
||||
ms-pyright.pyright
|
||||
ms-python.debugpy
|
||||
ms-python.mypy-type-checker
|
||||
ms-python.python
|
||||
ms-python.vscode-python-envs
|
||||
njpwerner.autodocstring
|
||||
njqdev.vscode-python-typehint
|
||||
redhat.vscode-yaml
|
||||
stkb.rewrap
|
||||
streetsidesoftware.code-spell-checker
|
||||
tamasfe.even-better-toml
|
||||
vue.volar
|
||||
yzhang.markdown-all-in-one
|
||||
86
.vscode/settings.json
vendored
Normal file
86
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"[css]": {
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[django-html]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.quickSuggestions": {
|
||||
"comments": true,
|
||||
"other": true,
|
||||
"strings": true
|
||||
},
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[dockercompose]": {
|
||||
"breadcrumbs.showConstants": true,
|
||||
"editor.quickSuggestions": {
|
||||
"comments": false,
|
||||
"other": true,
|
||||
"strings": true
|
||||
},
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[helm]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2,
|
||||
"rewrap.autoWrap.enabled": true,
|
||||
"rewrap.wholeComment": true,
|
||||
"rewrap.wrappingColumn": 73
|
||||
},
|
||||
"[html]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.maxTokenizationLineLength": 2500,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "yzhang.markdown-all-in-one",
|
||||
"editor.quickSuggestions": {
|
||||
"comments": "off",
|
||||
"other": "off",
|
||||
"strings": "off"
|
||||
},
|
||||
"editor.tabSize": 4,
|
||||
"editor.wordWrap": "off"
|
||||
},
|
||||
"[python]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "always"
|
||||
},
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true
|
||||
},
|
||||
"[shellscript]": {
|
||||
"editor.tabSize": 2,
|
||||
"files.eol": "\n"
|
||||
},
|
||||
"[terraform]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.maxTokenizationLineLength": 2500,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2,
|
||||
"gitlens.codeLens.scopes": [
|
||||
"document"
|
||||
]
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"cSpell.userWords": [
|
||||
"Kubernetes",
|
||||
"clamd",
|
||||
"rtype"
|
||||
],
|
||||
}
|
||||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@@ -6,7 +6,7 @@
|
||||
{
|
||||
"label": "Build arch-toolbox",
|
||||
"type": "shell",
|
||||
"command": "./infrastructure/graduated/distoolbox/arch-build.sh",
|
||||
"command": "./active/software_distoolbox/arch-build.sh",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
|
||||
58
.vscode/vscode.md
vendored
58
.vscode/vscode.md
vendored
@@ -1,12 +1,32 @@
|
||||
# VSCODE Configuration
|
||||
|
||||
- [VSCODE Configuration](#vscode-configuration)
|
||||
- [Debugpy Snippet](#debugpy-snippet)
|
||||
- [Fix Paste (if ctrl+v not working)](#fix-paste-if-ctrlv-not-working)
|
||||
- [Shell](#shell)
|
||||
- [Fonts](#fonts)
|
||||
- [Navigation](#navigation)
|
||||
- [Extensions](#extensions)
|
||||
- [Continue](#continue)
|
||||
- [Continue](#continue)
|
||||
- [Pylance Type Checking](#pylance-type-checking)
|
||||
|
||||
## Debugpy Snippet
|
||||
|
||||
Use this to add debugging to any project
|
||||
|
||||
```python
|
||||
# Listens on 5678
|
||||
# Only runs if ACTIVATE_DEBUGPY=true is set
|
||||
if os.getenv("ACTIVATE_DEBUGPY", "").lower() == "true":
|
||||
try:
|
||||
import debugpy
|
||||
debugpy.listen(("0.0.0.0", 5678))
|
||||
print("Waiting for client on 5678...")
|
||||
debugpy.wait_for_client()
|
||||
print("Client connected")
|
||||
except Exception as e:
|
||||
print("DEBUG ERROR: " + str(e))
|
||||
```
|
||||
|
||||
## Fix Paste (if ctrl+v not working)
|
||||
|
||||
@@ -21,11 +41,23 @@ Add to user keyboard settings (json)
|
||||
|
||||
## Shell
|
||||
|
||||
Edit settings.json
|
||||
I would recommend using tmux as your default shell. VSCode automatically creates new
|
||||
sessions when adding or splitting windows. The only small problem is closing out of
|
||||
vscode and reopening it won't reattach the previous sessions. You'll need to run
|
||||
`tmux switch-client -t #` to get back to each session you lost.
|
||||
|
||||
```json
|
||||
"terminal.integrated.profiles.osx": {
|
||||
//...existing profiles...
|
||||
"tmux-shell": {
|
||||
"path": "tmux",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
{
|
||||
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||
"terminal.integrated.defaultProfile.linux": "tmux",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -86,18 +118,10 @@ To install that list of extensions run:
|
||||
cat vscode_extensions.txt | xargs -L 1 code --install-extension
|
||||
```
|
||||
|
||||
## Continue
|
||||
### Continue
|
||||
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"title": "qwen2.5-coder:32b",
|
||||
"provider": "ollama",
|
||||
"apiBase": "https://ollama.example.com",
|
||||
"apiKey": "...",
|
||||
"model": "qwen2.5-coder:32b"
|
||||
}
|
||||
],
|
||||
...
|
||||
```
|
||||
Continue -> Settings -> Help -> Quickstart
|
||||
|
||||
### Pylance Type Checking
|
||||
|
||||
Settings -> `python.analysis.typeChecking`
|
||||
308
README.md
308
README.md
@@ -1,81 +1,272 @@
|
||||
# Homelab
|
||||
|
||||
A project to store homelab stuff.
|
||||
Welcome to my homelab!
|
||||
|
||||
Just here for the Arch distoolbox?
|
||||
This repo is an in-flux collection of my personal notes, docs, and tutorials of
|
||||
things I find interesting and self-host.
|
||||
|
||||
[Arch Distoolbox](infrastructure/graduated/distoolbox/distoolbox.md)
|
||||
Take a look around!
|
||||
|
||||

|
||||
- "Active" projects (/active) are in use today and generally fall into these
|
||||
categories:
|
||||
- `aws_` is for aws notes
|
||||
- `device_` is for hardware
|
||||
- `kubernetes_` is for helm charts or other kubernetes hosted software
|
||||
- `os_` is for operating system setup guides and notes
|
||||
- `podman_` is for containerized projects
|
||||
- `software_` is for cli tools, projects without a specific way to host them,
|
||||
or other misfits
|
||||
|
||||
All active projects will have a markdown file named after the project. This is
|
||||
for quick access via shortcuts like `ctrl + p` in vscode. For example, I want
|
||||
to check my notes for `virsh` so I would type `ctrl + p` "virsh" to open
|
||||
"virsh.md".
|
||||
|
||||
"Retired" projects (/retired) is a graveyard of things I didn't want to delete.
|
||||
|
||||
"Template" projects (/templates) are quick templates for creating new active
|
||||
projects with sane defaults.
|
||||
|
||||
I keep my GPG and SSH keys in `keys` if you want to add those to your keyring
|
||||
or give me access to your servers.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Homelab](#homelab)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Fun Facts](#fun-facts)
|
||||
- [Keyboard Shortcuts](#keyboard-shortcuts)
|
||||
- [inputrc](#inputrc)
|
||||
- ["find ." shortcuts](#find--shortcuts)
|
||||
- [tmux](#tmux)
|
||||
- [bash](#bash)
|
||||
- [SSH Setup](#ssh-setup)
|
||||
- [Git GPG Commit Signing](#git-gpg-commit-signing)
|
||||
- [Important Dates and Times](#important-dates-and-times)
|
||||
- [Project Lifecycle](#project-lifecycle)
|
||||
- [Supported Projects](#supported-projects)
|
||||
- [Graduation Requirements](#graduation-requirements)
|
||||
- [Project Types](#project-types)
|
||||
- [Active Project Requirements](#active-project-requirements)
|
||||
- [Retirement Requirements](#retirement-requirements)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Creating a Project](#creating-a-project)
|
||||
- [Order of Operations](#order-of-operations)
|
||||
|
||||
## Fun Facts
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
On linux, <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>, then, while holding
|
||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>, typing <kbd>b</kbd>+<kbd>0</kbd> will type a ° (degree) symbol. Also you
|
||||
can enter any unicode symbol this way.
|
||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>, typing <kbd>b</kbd>+<kbd>0</kbd> will type a
|
||||
° (degree) symbol. Also you can enter any unicode symbol this way.
|
||||
|
||||
In vim: `esc + o` will take you to the end of a file and insert a new line.
|
||||
|
||||
### inputrc
|
||||
|
||||
Add this to your `~/.inputrc` to allow ctrl + backspace to delete whole words.
|
||||
|
||||
```bash
|
||||
"\C-h": backward-kill-word
|
||||
```
|
||||
|
||||
### "find ." shortcuts
|
||||
|
||||
```bash
|
||||
# Change file mode for a bunch of directories
|
||||
find . -type d -exec chmod 755 {} \;
|
||||
```
|
||||
|
||||
### tmux
|
||||
|
||||
- Vertical: ctrl + b + "
|
||||
- Horizontal: ctrl + b + %
|
||||
- Event Horizontal Distribution: ctrl + b + alt + 1
|
||||
- Even Vertical Distribution: ctrl + b + alt + 2
|
||||
- Swap pane order: ctrl + b + : -> swap-pane -t 0
|
||||
|
||||
### bash
|
||||
|
||||
<https://tecadmin.net/bash-special-variables/>
|
||||
|
||||
Here are some handy references for default bash variables
|
||||
|
||||
```text
|
||||
$0 – The name of the script being executed.
|
||||
$1-$9 – The first nine command-line arguments.
|
||||
$# – The number of command-line arguments.
|
||||
$* – All command-line arguments as a single string.
|
||||
$@ – All command-line arguments as an array.
|
||||
$? – The exit status of the last executed command.
|
||||
$$ – The process ID of the current shell.
|
||||
$! – The process ID of the last background command.
|
||||
$- – Shows the current shell options or flags.
|
||||
```
|
||||
|
||||
And here are the meanings of the shell options
|
||||
|
||||
```text
|
||||
h – Remember the location of commands as they are looked up
|
||||
i – Interactive shell
|
||||
m – Job control is enabled
|
||||
B – Brace expansion is enabled
|
||||
H – History substitution is enabled
|
||||
```
|
||||
|
||||
So to check if you are in an interactive shell:
|
||||
|
||||
```bash
|
||||
[ $- == *i* ]] && Some command here
|
||||
```
|
||||
|
||||
## SSH Setup
|
||||
|
||||
Generate a key (password protect it!)
|
||||
|
||||
```bash
|
||||
# Pick one of the below key types
|
||||
# ed25519
|
||||
ssh-keygen -C ssh@ducoterra.net -t ed25519
|
||||
# rsa 4096
|
||||
ssh-keygen -C ssh@ducoterra.net -t rsa -b 4096
|
||||
|
||||
# Inspect a key
|
||||
ssh-keygen -l -f ~/.ssh/id_rsa
|
||||
|
||||
# Change the password
|
||||
ssh-keygen -p -f ~/.ssh/id_rsa
|
||||
```
|
||||
|
||||
In your ~/.ssh/config, add the following line to set the default key
|
||||
|
||||
```conf
|
||||
IdentityFile ~/.foo/identity
|
||||
```
|
||||
|
||||
Then add a host to your local computer
|
||||
|
||||
```bash
|
||||
Host <hostname>
|
||||
Hostname <host.something.com or IP address>
|
||||
User <remote user>
|
||||
Port <remote port>
|
||||
```
|
||||
|
||||
And copy the key to a remote computer
|
||||
|
||||
```bash
|
||||
# Copy the generated key to the server using password auth. Assumes password auth enabled.
|
||||
ssh-copy-id -f -i ~/.ssh/id_ed25519 ${REMOTE_USER}@${REMOTE_HOST}
|
||||
|
||||
# Log into the server with your key
|
||||
ssh -i ${KEY_NAME} ${REMOTE_HOST}
|
||||
# Copy authorized_keys to root
|
||||
sudo mkdir -p /root/.ssh
|
||||
sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
|
||||
exit
|
||||
|
||||
# login and disable password auth
|
||||
ssh ${REMOTE_HOST}
|
||||
mkdir -p /etc/ssh/sshd_config.d
|
||||
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/01-prohibit-password.conf
|
||||
systemctl restart sshd
|
||||
|
||||
# OPTIONAL: Disable sudo password
|
||||
echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/01-nopasswd-wheel
|
||||
|
||||
exit
|
||||
|
||||
# Test if you can SSH with a password
|
||||
ssh -o PubkeyAuthentication=no ducoterra@${SSH_HOST}.reeselink.com
|
||||
|
||||
# Test that you can log into the server with ssh config
|
||||
ssh $SSH_HOST
|
||||
```
|
||||
|
||||
## Git GPG Commit Signing
|
||||
|
||||
1. Use `gpg --list-key 'git@ducoterra.net'` to find your key
|
||||
2. Use `git config --global user.signingkey 0A46826A...` to set the signing key
|
||||
3. Use `gpg --export -a 'git@ducoterra.net'` to export the key to copy into Gitea/Github/Gitlab
|
||||
|
||||
Now you can sign commits with `git commit -S`.
|
||||
|
||||
Alternatively, you can sign every commit by default with `git config --global commit.gpgsign true`.
|
||||
|
||||
You can verify a commit with `git verify-commit e1e551c`. If the commit is
|
||||
signed you'll see an output. If not, nothing will show.
|
||||
|
||||
## Important Dates and Times
|
||||
|
||||
| Time | Day | Description |
|
||||
| ----- | -------- | ---------------------------------- |
|
||||
| 00:00 | All | Automated builds |
|
||||
| 00:00 | All | NAS Snapshots |
|
||||
| 02:00 | All | Backups |
|
||||
| 04:00 | All | Bare Metal Server Security Updates |
|
||||
| 05:00 | All | VM Server Security Updates |
|
||||
| 05:00 | All | Unifi Protect Firmware Updates |
|
||||
| 06:00 | All | Unifi Network Firmware Updates |
|
||||
| 06:00 | Saturday | Truenas Disk Scrub |
|
||||
|
||||
## Project Lifecycle
|
||||
|
||||
Projects will fall into one of the three following categories:
|
||||
Projects will either be `active` or `retired`.
|
||||
|
||||
1. Incubating
|
||||
2. Graduated
|
||||
3. Retired
|
||||
Active projects are being actively developed. They are in-use, stable, and
|
||||
production ready. Active projects should meet and track the [active project
|
||||
requirements](#active-project-requirements)
|
||||
|
||||
Incubating projects are experimental or prototypal. They're being actively developed and aren't
|
||||
ready for production deployment. These projects may appear and disappear without warning and are not
|
||||
stable. There is no minimum requirement for a project to be in incubation.
|
||||
Retired projects are no longer in use or recommended. They are kept for
|
||||
reference. Retired projects must meet the [retirement
|
||||
requirements](#retirement-requirements)
|
||||
|
||||
Graduated projects are in-use, stable, and production ready. They met the [graduation
|
||||
requirements](#graduation-requirements) and are actively maintained.
|
||||
You'll notice that most of the active projects have scripts or examples that
|
||||
use the `active` path as part of their install process. When moved outside the
|
||||
`active` directory their scripts and examples break. This is intentional. If
|
||||
you want a retired project to work again, bring it back to the active
|
||||
directory.
|
||||
|
||||
Retired projects are no longer in use or recommended. They are kept for reference. Retired projects
|
||||
must meet the [retirement requirements](#retirement-requirements)
|
||||
## Project Types
|
||||
|
||||
## Supported Projects
|
||||
All projects will be prefixed with one of the following categories:
|
||||
|
||||
All projects will fall into one of the following categories:
|
||||
- `device_`
|
||||
- `os_`
|
||||
- `software_`
|
||||
- `podman_`
|
||||
- `docker_`
|
||||
- `kubernetes_`
|
||||
|
||||
- hardware
|
||||
- infrastructure
|
||||
- cloud
|
||||
- systemd
|
||||
- podman
|
||||
- docker
|
||||
- kubernetes
|
||||
Note, some projects will be named with just the prefix. These are projects for
|
||||
configuring the underlying technology. The `podman` project, for example, will
|
||||
tell you how to configure and install podman so it works correctly.
|
||||
|
||||
Hardware will contain projects that relate to specific machines or equipment. 3D printers, Raspberry
|
||||
Pis, and other IOT devices qualify as specialized hardware that needs documentation and
|
||||
configuration. This is not limited to computer equipment. The furnace is an important part of the
|
||||
home lab. the Air Conditioner is integral to the homelab's function. These projects will also be documented.
|
||||
`device_` will prefix projects that relate to specific machines or equipment.
|
||||
3D printers, Raspberry Pis, and other IOT devices qualify as specialized
|
||||
hardware that needs documentation and configuration. This is not limited to
|
||||
computer equipment. The furnace is an important part of the homelab. the Air
|
||||
Conditioner is integral to the homelab's function. These projects will also be
|
||||
documented.
|
||||
|
||||
Infrastructure will contain projects that set up the environments for the remaining listed project
|
||||
types. For example, infrastructure will contain "how to set up a linux box with docker" or "how to
|
||||
set up a k3s cluster for kubernetes".
|
||||
`os_` will contain projects that set up operating systems. These include best
|
||||
practices, backups, updates, default software, etc.
|
||||
|
||||
Cloud projects are for specific cloud providers.
|
||||
`cloud_` projects are for specific cloud providers. This will contain
|
||||
documentation and errata for things like AWS IAM, Route53, etc. Note these will
|
||||
be prefixed with the cloud's name, not the word "cloud". So AWS services will
|
||||
be prefixed with `aws_` and azure would be `azure_`. This should make them more
|
||||
searchable.
|
||||
|
||||
Systemd projects are designed to be installed with ansible and run via systemd on a linux VM or
|
||||
other linux hardware.
|
||||
`software_` projects record configuration for common software agnostic to
|
||||
operating system or linux flavor.
|
||||
|
||||
Podman projects are either designed to be run as quadlets or as podman containers outright.
|
||||
`podman_` projects are either designed to be run as quadlets or as podman
|
||||
containers outright.
|
||||
|
||||
Docker projects are either docker-compose or some form of docker run command.
|
||||
`kubernetes_` projects are helm, kustomize, kubectl, or some other kubernetes
|
||||
compliant deployment.
|
||||
|
||||
Kubernetes projects are helm, kustomize, kubectl, or some other kubernetes compliant deployment.
|
||||
|
||||
## Graduation Requirements
|
||||
## Active Project Requirements
|
||||
|
||||
- [ ] Installation is documented
|
||||
- [ ] Installation configuration examples are provided
|
||||
@@ -91,11 +282,30 @@ Kubernetes projects are helm, kustomize, kubectl, or some other kubernetes compl
|
||||
- [ ] If applicable, a replacement has been identified and documented
|
||||
- [ ] If applicable, backup data locations are documented
|
||||
|
||||
## Project Structure
|
||||
|
||||
All projects will have, at minimum.
|
||||
|
||||
1. A README named `project-name.md`
|
||||
2. A directory called `secrets` which will be gitignored.
|
||||
|
||||
## Creating a Project
|
||||
|
||||
Assuming your project name is `my-project` and it runs on `podman`
|
||||
|
||||
1. Create a new directory called `podman_my-project` under the `active`
|
||||
directory
|
||||
2. Copy the readme template: `cp project_readme_template.md
|
||||
active/podman_my-project/my-project.md`
|
||||
3. Populate `my-project.md` as you work through the install process
|
||||
4. Create a directory called `secrets` in `podman_my-project`. This will be
|
||||
automatically gitignored. Put all secrets here.
|
||||
5. Push the changes when you have a working product
|
||||
|
||||
## Order of Operations
|
||||
|
||||
1. Install cloud projects. These usually have no dependencies and typically provide critical services
|
||||
to other projects (DNS, email notifications, etc.)
|
||||
2. Install infrastructure projects. Usually these only have dependencies on cloud services.
|
||||
3. Install systemd services. These are usually low-level programs that require a dedicated machine
|
||||
and perform semi-critical functions (ipv4 proxy, ddns, etc.).
|
||||
4. Install kubernetes, docker, podman, and other services.
|
||||
1. Configure cloud providers. These usually have no dependencies and typically
|
||||
provide critical services to other projects (DNS, email notifications, etc.)
|
||||
2. Install infrastructure projects. Usually these only have dependencies on
|
||||
cloud services.
|
||||
3. Install systemd, kubernetes, docker, podman, and other services.
|
||||
|
||||
12
active/aws_cli/aws_cli.md
Normal file
12
active/aws_cli/aws_cli.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# AWS CLI
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# Run as root
|
||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
|
||||
unzip awscliv2.zip && \
|
||||
./aws/install && \
|
||||
rm -f ./awscliv2.zip && \
|
||||
rm -rf ./aws
|
||||
```
|
||||
@@ -1,24 +1,33 @@
|
||||
# AWS Credentials
|
||||
|
||||
## Credential Generation
|
||||
Note: this requires the AWS CLI. See [AWS CLI](/active/aws_cli/aws_cli.md)
|
||||
|
||||
- [AWS Credentials](#aws-credentials)
|
||||
- [Route53 Credential Generation](#route53-credential-generation)
|
||||
- [AWS Certbot Route53 Policies](#aws-certbot-route53-policies)
|
||||
- [Email Credentials](#email-credentials)
|
||||
|
||||
## Route53 Credential Generation
|
||||
|
||||
```bash
|
||||
export AWS_USERNAME=
|
||||
aws iam create-user --user-name $AWS_USERNAME
|
||||
aws iam create-access-key --user-name $AWS_USERNAME
|
||||
|
||||
# Allow updating reeseapps
|
||||
aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn $(cat cloud/graduated/aws_iam/secrets/update-reeseapps-iam-policy-arn)
|
||||
aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn $(cat active/aws_iam/secrets/update-reeseapps-iam-policy-arn)
|
||||
|
||||
# Allow updating reeselink
|
||||
aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn $(cat cloud/graduated/aws_iam/secrets/update-reeselink-iam-policy-arn)
|
||||
aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn $(cat active/aws_iam/secrets/update-reeselink-iam-policy-arn)
|
||||
|
||||
# Create credentials (run aws configure on the machine that needs these to input them manually)
|
||||
aws iam create-access-key --user-name $AWS_USERNAME
|
||||
```
|
||||
|
||||
## AWS Certbot Route53 Policies
|
||||
|
||||
Example Policy:
|
||||
|
||||
cloud/graduated/aws_iam/secrets/policies/route53_reeselink.json
|
||||
active/aws_iam/secrets/route53_reeselink.json
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -50,8 +59,21 @@ cloud/graduated/aws_iam/secrets/policies/route53_reeselink.json
|
||||
|
||||
```bash
|
||||
# Allow updating route53 records for reeselink.com
|
||||
aws iam create-policy --policy-name update-reeselink --policy-document file://cloud/graduated/aws_iam/secrets/route53_reeselink_policy.json
|
||||
aws iam create-policy --policy-name update-reeselink --policy-document file://active/aws_iam/secrets/route53_reeselink_policy.json
|
||||
|
||||
# Allow updating route53 records for reeseapps.com
|
||||
aws iam create-policy --policy-name update-reeseapps --policy-document file://cloud/graduated/aws_iam/secrets/route53_reeseapps_policy.json
|
||||
aws iam create-policy --policy-name update-reeseapps --policy-document file://active/aws_iam/secrets/route53_reeseapps_policy.json
|
||||
```
|
||||
|
||||
## Email Credentials
|
||||
|
||||
<https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html>
|
||||
|
||||
You can technically do this through the CLI, see above link.
|
||||
|
||||
1. Log into the AWS console
|
||||
2. Navigate to SES
|
||||
3. Click "SMTP Settings"
|
||||
4. Click "Create SMTP Credentials"
|
||||
5. Name it "ses-smtp-user.something"
|
||||
6. Copy the username and password
|
||||
@@ -17,14 +17,14 @@ convenience.
|
||||
|
||||
## Reeselink Addresses
|
||||
|
||||
See `example-record-file.json` for example contents of `file://cloud/graduated/aws_route53/secrets/aws/reeselink.json`.
|
||||
See `example-record-file.json` for example contents of `file://active/aws_route53/secrets/aws/reeselink.json`.
|
||||
|
||||
```bash
|
||||
aws route53 change-resource-record-sets --hosted-zone-id $(cat cloud/graduated/aws_route53/secrets/reeselink-zoneid) --change-batch file://cloud/graduated/aws_route53/secrets/reeselink.json
|
||||
aws route53 change-resource-record-sets --hosted-zone-id $(cat active/aws_route53/secrets/reeselink-zoneid) --change-batch file://active/aws_route53/secrets/reeselink.json
|
||||
```
|
||||
|
||||
## Reeseapps Addresses
|
||||
|
||||
```bash
|
||||
aws route53 change-resource-record-sets --hosted-zone-id $(cat cloud/graduated/aws_route53/secrets/reeseapps-zoneid) --change-batch file://cloud/graduated/aws_route53/secrets/reeseapps.json
|
||||
aws route53 change-resource-record-sets --hosted-zone-id $(cat active/aws_route53/secrets/reeseapps-zoneid) --change-batch file://active/aws_route53/secrets/reeseapps.json
|
||||
```
|
||||
3
active/aws_ses/aws_ses.md
Normal file
3
active/aws_ses/aws_ses.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# AWS SES
|
||||
|
||||
AWS Simple Email Service
|
||||
14
active/device_3dserver/3dserver.md
Normal file
14
active/device_3dserver/3dserver.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 3D Server Hardware
|
||||
|
||||
## Motherboard
|
||||
|
||||
B650 GAMING X AX rev 1.5
|
||||
|
||||
<https://www.gigabyte.com/Motherboard/B650-GAMING-X-AX-rev-15/support#dl>
|
||||
|
||||
- Enable PBO
|
||||
- Enable XMP
|
||||
- Enable SVM
|
||||
- Enable PCIe x4x4x4x4 bifurcation
|
||||
- Enable Power always back on
|
||||
- Fans to full speed
|
||||
@@ -8,6 +8,16 @@ If you want to set up WiFi without logging in follow this:
|
||||
|
||||
<https://wiki.bambulab.com/en/p1/manual/p1-sd-card-network-configuration-guide>
|
||||
|
||||
1. Create a new file on the printer's SD card named `user_wifi.cfg`
|
||||
2. Add the following contents to `user_wifi.cfg`:
|
||||
|
||||
```text
|
||||
ssid:FruitTest08
|
||||
password:wikitest12
|
||||
```
|
||||
|
||||
3. Put the SD card back in the printer. It should connect automatically.
|
||||
|
||||
### Connecting via LAN mode
|
||||
|
||||
- Make sure you open 1990 and 2021 (tcp + udp) in your firewall application.
|
||||
11
active/device_epson_et_2800/epson_et_2800.md
Normal file
11
active/device_epson_et_2800/epson_et_2800.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Epson ET 2800
|
||||
|
||||
## Printer Setup
|
||||
|
||||
1. Download and install the drivers at <https://support.epson.net/linux/Printer/LSB_distribution_pages/en/escpr.php>
|
||||
2. Settings -> Printers -> Add
|
||||
3. Select LPD/LPR Host or Printer
|
||||
4. Enter the address: `lpd://<ip_address>`
|
||||
5. Select Epson, then select Epson ET-2800 Series
|
||||
6. Save
|
||||
7. Print
|
||||
5
active/device_esphome/.gitignore
vendored
Normal file
5
active/device_esphome/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Gitignore settings for ESPHome
|
||||
# This is an example and may include too much for your use-case.
|
||||
# You can modify this file to suit your needs.
|
||||
/.esphome/
|
||||
/secrets.yaml
|
||||
370
active/device_esphome/default-atom-echo.yaml
Normal file
370
active/device_esphome/default-atom-echo.yaml
Normal file
@@ -0,0 +1,370 @@
|
||||
substitutions:
|
||||
name: m5stack-atom-echo
|
||||
friendly_name: M5Stack Atom Echo
|
||||
|
||||
esphome:
|
||||
name: ${name}
|
||||
name_add_mac_suffix: true
|
||||
friendly_name: ${friendly_name}
|
||||
min_version: 2025.5.0
|
||||
|
||||
esp32:
|
||||
board: m5stack-atom
|
||||
cpu_frequency: 240MHz
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
logger:
|
||||
api:
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
id: ota_esphome
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
captive_portal:
|
||||
|
||||
button:
|
||||
- platform: factory_reset
|
||||
id: factory_reset_btn
|
||||
name: Factory reset
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_audio_bus
|
||||
i2s_lrclk_pin: GPIO33
|
||||
i2s_bclk_pin: GPIO19
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: echo_microphone
|
||||
i2s_din_pin: GPIO23
|
||||
adc_type: external
|
||||
pdm: true
|
||||
sample_rate: 16000
|
||||
correct_dc_offset: true
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: echo_speaker
|
||||
i2s_dout_pin: GPIO22
|
||||
dac_type: external
|
||||
bits_per_sample: 16bit
|
||||
sample_rate: 16000
|
||||
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||
buffer_duration: 60ms
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
name: None
|
||||
id: echo_media_player
|
||||
announcement_pipeline:
|
||||
speaker: echo_speaker
|
||||
format: WAV
|
||||
codec_support_enabled: false
|
||||
buffer_size: 6000
|
||||
volume_min: 0.4
|
||||
files:
|
||||
- id: timer_finished_wave_file
|
||||
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||
on_announcement:
|
||||
- if:
|
||||
condition:
|
||||
- microphone.is_capturing:
|
||||
then:
|
||||
- script.execute: stop_wake_word
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_idle:
|
||||
- script.execute: start_wake_word
|
||||
- script.execute: reset_led
|
||||
|
||||
voice_assistant:
|
||||
id: va
|
||||
micro_wake_word:
|
||||
microphone:
|
||||
microphone: echo_microphone
|
||||
channels: 0
|
||||
gain_factor: 4
|
||||
media_player: echo_media_player
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Slow Pulse"
|
||||
on_stt_vad_end:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Fast Pulse"
|
||||
on_tts_start:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_end:
|
||||
# Handle the "nevermind" case where there is no announcement
|
||||
- wait_until:
|
||||
condition:
|
||||
- media_player.is_announcing:
|
||||
timeout: 0.5s
|
||||
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- wait_until:
|
||||
- and:
|
||||
- not:
|
||||
voice_assistant.is_running:
|
||||
- not:
|
||||
speaker.is_playing:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- script.execute: reset_led
|
||||
on_error:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
- delay: 2s
|
||||
- script.execute: reset_led
|
||||
on_client_connected:
|
||||
- delay: 2s # Give the api server time to settle
|
||||
- script.execute: start_wake_word
|
||||
on_client_disconnected:
|
||||
- script.execute: stop_wake_word
|
||||
on_timer_finished:
|
||||
- script.execute: stop_wake_word
|
||||
- wait_until:
|
||||
not:
|
||||
microphone.is_capturing:
|
||||
- switch.turn_on: timer_ringing
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: "Fast Pulse"
|
||||
- wait_until:
|
||||
- switch.is_off: timer_ringing
|
||||
- light.turn_off: led
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
binary_sensor:
|
||||
# button does the following:
|
||||
# short click - stop a timer
|
||||
# if no timer then restart either microwakeword or voice assistant continuous
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: GPIO39
|
||||
inverted: true
|
||||
name: Button
|
||||
disabled_by_default: true
|
||||
entity_category: diagnostic
|
||||
id: echo_button
|
||||
on_multi_click:
|
||||
- timing:
|
||||
- ON for at least 50ms
|
||||
- OFF for at least 50ms
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: timer_ringing
|
||||
then:
|
||||
- switch.turn_off: timer_ringing
|
||||
else:
|
||||
- script.execute: start_wake_word
|
||||
- timing:
|
||||
- ON for at least 10s
|
||||
then:
|
||||
- button.press: factory_reset_btn
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led
|
||||
name: None
|
||||
disabled_by_default: true
|
||||
entity_category: config
|
||||
pin: GPIO27
|
||||
default_transition_length: 0s
|
||||
chipset: SK6812
|
||||
num_leds: 1
|
||||
rgb_order: grb
|
||||
effects:
|
||||
- pulse:
|
||||
name: "Slow Pulse"
|
||||
transition_length: 250ms
|
||||
update_interval: 250ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
- pulse:
|
||||
name: "Fast Pulse"
|
||||
transition_length: 100ms
|
||||
update_interval: 100ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
|
||||
script:
|
||||
- id: reset_led
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 89%
|
||||
blue: 71%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- light.turn_off: led
|
||||
- id: start_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- id: stop_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: Use listen light
|
||||
id: use_listen_light
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- script.execute: reset_led
|
||||
on_turn_off:
|
||||
- script.execute: reset_led
|
||||
- platform: template
|
||||
id: timer_ringing
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
on_turn_off:
|
||||
# Turn off the repeat mode and disable the pause between playlist items
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||
# Stop playing the alarm
|
||||
- media_player.stop:
|
||||
announcement: true
|
||||
on_turn_on:
|
||||
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||
- media_player.speaker.play_on_device_media_file:
|
||||
media_file: timer_finished_wave_file
|
||||
announcement: true
|
||||
- delay: 15min
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
select:
|
||||
- platform: template
|
||||
entity_category: config
|
||||
name: Wake word engine location
|
||||
id: wake_word_engine_location
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
options:
|
||||
- In Home Assistant
|
||||
- On device
|
||||
initial_option: On device
|
||||
on_value:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "In Home Assistant";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
- delay: 500ms
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- delay: 500ms
|
||||
- micro_wake_word.start:
|
||||
|
||||
micro_wake_word:
|
||||
on_wake_word_detected:
|
||||
- voice_assistant.start:
|
||||
wake_word: !lambda return wake_word;
|
||||
vad:
|
||||
models:
|
||||
- model: okay_nabu
|
||||
- model: hey_mycroft
|
||||
- model: hey_jarvis
|
||||
249
active/device_esphome/esphome.md
Normal file
249
active/device_esphome/esphome.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# ESP32
|
||||
|
||||
- [ESP32](#esp32)
|
||||
- [Install](#install)
|
||||
- [Devices](#devices)
|
||||
- [Lilygo tdongle](#lilygo-tdongle)
|
||||
- [Local Flashing](#local-flashing)
|
||||
- [Adding a New Device](#adding-a-new-device)
|
||||
- [Controlling Home Assistant](#controlling-home-assistant)
|
||||
- [Configuration Sections](#configuration-sections)
|
||||
- [esphome](#esphome)
|
||||
- [esp32](#esp32-1)
|
||||
- [logger](#logger)
|
||||
- [api](#api)
|
||||
- [wifi](#wifi)
|
||||
- [ota](#ota)
|
||||
- [captive portal](#captive-portal)
|
||||
- [button](#button)
|
||||
- [i2s audio](#i2s-audio)
|
||||
- [microphone](#microphone)
|
||||
- [speaker](#speaker)
|
||||
- [media player](#media-player)
|
||||
- [voice assistant](#voice-assistant)
|
||||
- [micro wake word](#micro-wake-word)
|
||||
- [light](#light)
|
||||
- [binary sensor](#binary-sensor)
|
||||
- [lambda](#lambda)
|
||||
- [Display](#display)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# Check that you have python 3.11 installed
|
||||
uv python list --only-installed
|
||||
|
||||
# Create the venv (python 3.11 is recommended in the docs)
|
||||
uv venv --python 3.11
|
||||
|
||||
# Install esphome
|
||||
uv pip install esphome wheel pip
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
## Devices
|
||||
|
||||
### Lilygo tdongle
|
||||
|
||||
Display: 80 X 160
|
||||
|
||||
## Local Flashing
|
||||
|
||||
Make sure your permissions are set correctly
|
||||
|
||||
```bash
|
||||
sudo usermod -a -G dialout ducoterra
|
||||
```
|
||||
|
||||
Then "run" your config file
|
||||
|
||||
```bash
|
||||
cd active/device_esp32
|
||||
uv venv
|
||||
uv pip install esphome
|
||||
source .venv/bin/activate
|
||||
|
||||
esphome run m5stack-atom-echo.yaml
|
||||
```
|
||||
|
||||
## Adding a New Device
|
||||
|
||||
1. Create a new yaml configuration file called "my-device-device-type.yaml"
|
||||
|
||||
## Controlling Home Assistant
|
||||
|
||||
<https://esphome.io/components/api/#api-actions>
|
||||
|
||||
## Configuration Sections
|
||||
|
||||
<https://esphome.io/components/>
|
||||
|
||||
### esphome
|
||||
|
||||
### esp32
|
||||
|
||||
<https://esphome.io/components/esp32/#configuration-variables>
|
||||
|
||||
### logger
|
||||
|
||||
<https://esphome.io/components/logger/>
|
||||
|
||||
### api
|
||||
|
||||
<https://esphome.io/components/api/>
|
||||
|
||||
### wifi
|
||||
|
||||
<https://esphome.io/components/wifi/>
|
||||
|
||||
### ota
|
||||
|
||||
<https://esphome.io/components/ota/>
|
||||
|
||||
<https://esphome.io/components/ota/esphome/>
|
||||
|
||||
### captive portal
|
||||
|
||||
<https://esphome.io/components/captive_portal/>
|
||||
|
||||
### button
|
||||
|
||||
<https://esphome.io/components/button/>
|
||||
|
||||
### i2s audio
|
||||
|
||||
<https://esphome.io/components/i2s_audio/>
|
||||
|
||||
### microphone
|
||||
|
||||
<https://esphome.io/components/microphone/>
|
||||
|
||||
<https://esphome.io/components/microphone/i2s_audio/>
|
||||
|
||||
### speaker
|
||||
|
||||
<https://esphome.io/components/speaker/i2s_audio/>
|
||||
|
||||
### media player
|
||||
|
||||
<https://esphome.io/components/media_player/speaker/>
|
||||
|
||||
Sometimes you'll need to convert media files to supported encoders.
|
||||
|
||||
```bash
|
||||
ffmpeg -i input.flac output.wav
|
||||
```
|
||||
|
||||
To play media on other devices from home assistant, put the
|
||||
|
||||
```yaml
|
||||
action: media_player.play_media
|
||||
target:
|
||||
entity_id: media_player.kitchen_google_home
|
||||
data:
|
||||
media_content_type: "audio/wav"
|
||||
media_content_id: "media-source://media_source/local/wake_word_triggered.wav"
|
||||
```
|
||||
|
||||
### voice assistant
|
||||
|
||||
<https://esphome.io/components/voice_assistant/>
|
||||
|
||||
In Home Assistant's configuration.yaml, add the following to listen to
|
||||
audio recordings of your voice request:
|
||||
|
||||
```bash
|
||||
assist_pipeline:
|
||||
debug_recording_dir: /share/assist_pipeline
|
||||
```
|
||||
|
||||
### micro wake word
|
||||
|
||||
<https://esphome.io/components/micro_wake_word/>
|
||||
|
||||
### light
|
||||
|
||||
<https://esphome.io/components/light/#light-effects>
|
||||
|
||||
### binary sensor
|
||||
|
||||
<https://esphome.io/components/binary_sensor/>
|
||||
|
||||
### lambda
|
||||
|
||||
<https://esphome.io/automations/templates/#config-lambda>
|
||||
|
||||
> id(...) is a helper function that makes ESPHome fetch an object with the
|
||||
> supplied ID (which you defined somewhere else, like top_end_stop ) and lets
|
||||
> you call any of ESPHome’s many APIs directly. For example, here we’re
|
||||
> retrieving the current state of the end stop using .state and using it to
|
||||
> construct our cover state.
|
||||
|
||||
### Display
|
||||
|
||||
Display pages
|
||||
|
||||
```yaml
|
||||
display:
|
||||
- platform: st7735
|
||||
spi_id: spi_lcd
|
||||
model: "INITR_MINI160X80"
|
||||
reset_pin: GPIO1
|
||||
cs_pin: GPIO4
|
||||
dc_pin: GPIO2
|
||||
rotation: 270
|
||||
device_width: 82
|
||||
device_height: 161
|
||||
col_start: 0
|
||||
row_start: 0
|
||||
eight_bit_color: true
|
||||
invert_colors: true
|
||||
use_bgr: true
|
||||
auto_clear_enabled: true
|
||||
id: my_display
|
||||
pages:
|
||||
- id: page1
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Connecting to");
|
||||
it.print(0, 30, id(font_roboto), "Home Assistant...");
|
||||
- id: page2
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Configuring");
|
||||
it.print(0, 30, id(font_roboto), "sensors...");
|
||||
- id: page3
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Loading");
|
||||
it.print(0, 30, id(font_roboto), "important");
|
||||
it.print(0, 50, id(font_roboto), "update...");
|
||||
- id: page4
|
||||
lambda: |-
|
||||
it.image(0, 0, id(my_image), COLOR_OFF, COLOR_ON);
|
||||
```
|
||||
|
||||
Switch pages
|
||||
|
||||
```yaml
|
||||
interval:
|
||||
- interval: 5s
|
||||
then:
|
||||
- display.page.show_next: my_display
|
||||
- component.update: my_display
|
||||
```
|
||||
|
||||
Show an image
|
||||
|
||||
```yaml
|
||||
image:
|
||||
- file: "test_tdongle_image.png"
|
||||
type: RGB
|
||||
id: my_image
|
||||
```
|
||||
|
||||
Specify a font
|
||||
|
||||
```yaml
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_roboto
|
||||
size: 20
|
||||
```
|
||||
386
active/device_esphome/great-room-atom-echo.yaml
Normal file
386
active/device_esphome/great-room-atom-echo.yaml
Normal file
@@ -0,0 +1,386 @@
|
||||
esphome:
|
||||
name: great-room-atom-echo
|
||||
friendly_name: Great Room Atom Echo
|
||||
|
||||
esp32:
|
||||
board: m5stack-atom
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
level: debug
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: !secret great_room_atom_echo_key
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: .reeselink.com
|
||||
fast_connect: true
|
||||
enable_btm: true
|
||||
on_disconnect:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 0%
|
||||
red: 100%
|
||||
green: 0%
|
||||
effect: "Slow Pulse"
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "Great-Room-Atom-Echo"
|
||||
password: !secret hotspot_password
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
button:
|
||||
- platform: factory_reset
|
||||
id: factory_reset_btn
|
||||
name: Factory reset
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_audio_bus
|
||||
i2s_lrclk_pin: GPIO33
|
||||
i2s_bclk_pin: GPIO19
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: echo_microphone
|
||||
i2s_din_pin: GPIO23
|
||||
adc_type: external
|
||||
pdm: true
|
||||
sample_rate: 16000
|
||||
correct_dc_offset: true
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: echo_speaker
|
||||
i2s_dout_pin: GPIO22
|
||||
dac_type: external
|
||||
bits_per_sample: 16bit
|
||||
sample_rate: 16000
|
||||
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||
buffer_duration: 60ms
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
name: None
|
||||
id: echo_media_player
|
||||
announcement_pipeline:
|
||||
speaker: echo_speaker
|
||||
format: WAV
|
||||
codec_support_enabled: false
|
||||
buffer_size: 6000
|
||||
volume_min: 1
|
||||
volume_max: 1
|
||||
volume_initial: 1
|
||||
files:
|
||||
- id: timer_finished_wave_file
|
||||
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||
on_announcement:
|
||||
- if:
|
||||
condition:
|
||||
- microphone.is_capturing:
|
||||
then:
|
||||
- script.execute: stop_wake_word
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_idle:
|
||||
- script.execute: start_wake_word
|
||||
- script.execute: reset_led
|
||||
|
||||
voice_assistant:
|
||||
id: va
|
||||
micro_wake_word:
|
||||
microphone:
|
||||
microphone: echo_microphone
|
||||
channels: 0
|
||||
gain_factor: 64
|
||||
media_player: echo_media_player
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Slow Pulse"
|
||||
on_stt_vad_end:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Fast Pulse"
|
||||
on_tts_start:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_end:
|
||||
# Handle the "nevermind" case where there is no announcement
|
||||
- wait_until:
|
||||
condition:
|
||||
- media_player.is_announcing:
|
||||
timeout: 0.5s
|
||||
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- wait_until:
|
||||
- and:
|
||||
- not:
|
||||
voice_assistant.is_running:
|
||||
- not:
|
||||
speaker.is_playing:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- script.execute: reset_led
|
||||
on_error:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
- delay: 2s
|
||||
- script.execute: reset_led
|
||||
on_client_connected:
|
||||
- delay: 2s # Give the api server time to settle
|
||||
- script.execute: start_wake_word
|
||||
on_client_disconnected:
|
||||
- script.execute: stop_wake_word
|
||||
on_timer_finished:
|
||||
- script.execute: stop_wake_word
|
||||
- wait_until:
|
||||
not:
|
||||
microphone.is_capturing:
|
||||
- switch.turn_on: timer_ringing
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: "Fast Pulse"
|
||||
- wait_until:
|
||||
- switch.is_off: timer_ringing
|
||||
- light.turn_off: led
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
binary_sensor:
|
||||
# button does the following:
|
||||
# short click - stop a timer
|
||||
# if no timer then restart either microwakeword or voice assistant continuous
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: GPIO39
|
||||
inverted: true
|
||||
name: Button
|
||||
disabled_by_default: true
|
||||
entity_category: diagnostic
|
||||
id: echo_button
|
||||
on_multi_click:
|
||||
- timing:
|
||||
- ON for at least 50ms
|
||||
- OFF for at least 50ms
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: timer_ringing
|
||||
then:
|
||||
- switch.turn_off: timer_ringing
|
||||
else:
|
||||
- script.execute: start_wake_word
|
||||
- timing:
|
||||
- ON for at least 10s
|
||||
then:
|
||||
- button.press: factory_reset_btn
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led
|
||||
name: None
|
||||
disabled_by_default: true
|
||||
entity_category: config
|
||||
pin: GPIO27
|
||||
default_transition_length: 0s
|
||||
chipset: SK6812
|
||||
num_leds: 1
|
||||
rgb_order: grb
|
||||
effects:
|
||||
- pulse:
|
||||
name: "Slow Pulse"
|
||||
transition_length: 250ms
|
||||
update_interval: 250ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
- pulse:
|
||||
name: "Fast Pulse"
|
||||
transition_length: 100ms
|
||||
update_interval: 100ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
|
||||
script:
|
||||
- id: reset_led
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 89%
|
||||
blue: 71%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- light.turn_off: led
|
||||
- id: start_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- id: stop_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: Use listen light
|
||||
id: use_listen_light
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- script.execute: reset_led
|
||||
on_turn_off:
|
||||
- script.execute: reset_led
|
||||
- platform: template
|
||||
id: timer_ringing
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
on_turn_off:
|
||||
# Turn off the repeat mode and disable the pause between playlist items
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||
# Stop playing the alarm
|
||||
- media_player.stop:
|
||||
announcement: true
|
||||
on_turn_on:
|
||||
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||
- media_player.speaker.play_on_device_media_file:
|
||||
media_file: timer_finished_wave_file
|
||||
announcement: true
|
||||
- delay: 15min
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
select:
|
||||
- platform: template
|
||||
entity_category: config
|
||||
name: Wake word engine location
|
||||
id: wake_word_engine_location
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
options:
|
||||
- In Home Assistant
|
||||
- On device
|
||||
initial_option: On device
|
||||
on_value:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "In Home Assistant";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
- delay: 500ms
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- delay: 500ms
|
||||
- micro_wake_word.start:
|
||||
|
||||
micro_wake_word:
|
||||
on_wake_word_detected:
|
||||
- voice_assistant.start:
|
||||
wake_word: !lambda return wake_word;
|
||||
vad:
|
||||
models:
|
||||
- model: okay_nabu
|
||||
- model: hey_mycroft
|
||||
- model: hey_jarvis
|
||||
118
active/device_esphome/lilygo-tdongle.yaml
Normal file
118
active/device_esphome/lilygo-tdongle.yaml
Normal file
@@ -0,0 +1,118 @@
|
||||
esphome:
|
||||
name: tdongle
|
||||
friendly_name: tdongle
|
||||
|
||||
esp32:
|
||||
board: esp32-s3-devkitc-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
flash_size: 16MB
|
||||
|
||||
logger:
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: !secret lilygo_tdongle_key
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: .reeselink.com
|
||||
fast_connect: true
|
||||
enable_btm: true
|
||||
id: wifithing
|
||||
# on_connect:
|
||||
# - component.update: my_online_image
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: GPIO0
|
||||
name: Button
|
||||
|
||||
spi:
|
||||
- id: spi_led
|
||||
clk_pin: GPIO39
|
||||
mosi_pin: GPIO40
|
||||
- id: spi_lcd
|
||||
clk_pin: GPIO5
|
||||
mosi_pin: GPIO3
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
frequency: 2000
|
||||
pin: GPIO38
|
||||
inverted: True
|
||||
id: backlight_output
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
output: backlight_output
|
||||
name: "LCD Backlight"
|
||||
id: lcd_backlight
|
||||
restore_mode: ALWAYS_ON
|
||||
# RGB Led, APA102 on GPIO39/GPIO40
|
||||
- platform: spi_led_strip
|
||||
spi_id: spi_led
|
||||
num_leds: 1
|
||||
name: "FastLED SPI Light"
|
||||
data_rate: 1MHz # Adjust as needed, APA102 supports up to 20MHz, 1MHz is a safe starting point
|
||||
|
||||
display:
|
||||
- platform: st7735
|
||||
spi_id: spi_lcd
|
||||
model: "INITR_MINI160X80"
|
||||
reset_pin: GPIO1
|
||||
cs_pin: GPIO4
|
||||
dc_pin: GPIO2
|
||||
rotation: 270
|
||||
device_width: 82
|
||||
device_height: 161
|
||||
col_start: 0
|
||||
row_start: 0
|
||||
eight_bit_color: true
|
||||
invert_colors: true
|
||||
use_bgr: true
|
||||
auto_clear_enabled: true
|
||||
id: my_display
|
||||
pages:
|
||||
- id: page1
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Connecting to");
|
||||
it.print(0, 30, id(font_roboto), "Home Assistant...");
|
||||
- id: page2
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Configuring");
|
||||
it.print(0, 30, id(font_roboto), "sensors...");
|
||||
- id: page3
|
||||
lambda: |-
|
||||
it.print(0, 10, id(font_roboto), "Loading");
|
||||
it.print(0, 30, id(font_roboto), "important");
|
||||
it.print(0, 50, id(font_roboto), "update...");
|
||||
- id: page4
|
||||
lambda: |-
|
||||
it.image(0, 0, id(my_image), COLOR_OFF, COLOR_ON);
|
||||
|
||||
image:
|
||||
- file: "test_tdongle_image.png"
|
||||
type: RGB
|
||||
id: my_image
|
||||
|
||||
http_request:
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_roboto
|
||||
size: 20
|
||||
|
||||
interval:
|
||||
- interval: 5s
|
||||
then:
|
||||
- display.page.show_next: my_display
|
||||
- component.update: my_display
|
||||
387
active/device_esphome/loft-atom-echo.yaml
Normal file
387
active/device_esphome/loft-atom-echo.yaml
Normal file
@@ -0,0 +1,387 @@
|
||||
esphome:
|
||||
name: loft-atom-echo
|
||||
friendly_name: Loft Atom Echo
|
||||
|
||||
esp32:
|
||||
board: m5stack-atom
|
||||
cpu_frequency: 240MHz
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
level: debug
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: !secret loft_atom_echo_key
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: .reeselink.com
|
||||
fast_connect: true
|
||||
enable_btm: true
|
||||
on_disconnect:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 0%
|
||||
red: 100%
|
||||
green: 0%
|
||||
effect: "Slow Pulse"
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "Loft-Atom-Echo"
|
||||
password: !secret hotspot_password
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
button:
|
||||
- platform: factory_reset
|
||||
id: factory_reset_btn
|
||||
name: Factory reset
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_audio_bus
|
||||
i2s_lrclk_pin: GPIO33
|
||||
i2s_bclk_pin: GPIO19
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: echo_microphone
|
||||
i2s_din_pin: GPIO23
|
||||
adc_type: external
|
||||
pdm: true
|
||||
sample_rate: 16000
|
||||
correct_dc_offset: true
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: echo_speaker
|
||||
i2s_dout_pin: GPIO22
|
||||
dac_type: external
|
||||
bits_per_sample: 16bit
|
||||
sample_rate: 16000
|
||||
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||
buffer_duration: 60ms
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
name: None
|
||||
id: echo_media_player
|
||||
announcement_pipeline:
|
||||
speaker: echo_speaker
|
||||
format: WAV
|
||||
codec_support_enabled: false
|
||||
buffer_size: 6000
|
||||
volume_min: 1
|
||||
volume_max: 1
|
||||
volume_initial: 1
|
||||
files:
|
||||
- id: timer_finished_wave_file
|
||||
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||
on_announcement:
|
||||
- if:
|
||||
condition:
|
||||
- microphone.is_capturing:
|
||||
then:
|
||||
- script.execute: stop_wake_word
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_idle:
|
||||
- script.execute: start_wake_word
|
||||
- script.execute: reset_led
|
||||
|
||||
voice_assistant:
|
||||
id: va
|
||||
micro_wake_word:
|
||||
microphone:
|
||||
microphone: echo_microphone
|
||||
channels: 0
|
||||
gain_factor: 64
|
||||
media_player: echo_media_player
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Slow Pulse"
|
||||
on_stt_vad_end:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
effect: "Fast Pulse"
|
||||
on_tts_start:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
blue: 100%
|
||||
red: 0%
|
||||
green: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
on_end:
|
||||
# Handle the "nevermind" case where there is no announcement
|
||||
- wait_until:
|
||||
condition:
|
||||
- media_player.is_announcing:
|
||||
timeout: 0.5s
|
||||
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- wait_until:
|
||||
- and:
|
||||
- not:
|
||||
voice_assistant.is_running:
|
||||
- not:
|
||||
speaker.is_playing:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- script.execute: reset_led
|
||||
on_error:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: none
|
||||
- delay: 2s
|
||||
- script.execute: reset_led
|
||||
on_client_connected:
|
||||
- delay: 2s # Give the api server time to settle
|
||||
- script.execute: start_wake_word
|
||||
on_client_disconnected:
|
||||
- script.execute: stop_wake_word
|
||||
on_timer_finished:
|
||||
- script.execute: stop_wake_word
|
||||
- wait_until:
|
||||
not:
|
||||
microphone.is_capturing:
|
||||
- switch.turn_on: timer_ringing
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
brightness: 100%
|
||||
effect: "Fast Pulse"
|
||||
- wait_until:
|
||||
- switch.is_off: timer_ringing
|
||||
- light.turn_off: led
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
binary_sensor:
|
||||
# button does the following:
|
||||
# short click - stop a timer
|
||||
# if no timer then restart either microwakeword or voice assistant continuous
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: GPIO39
|
||||
inverted: true
|
||||
name: Button
|
||||
disabled_by_default: true
|
||||
entity_category: diagnostic
|
||||
id: echo_button
|
||||
on_multi_click:
|
||||
- timing:
|
||||
- ON for at least 50ms
|
||||
- OFF for at least 50ms
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: timer_ringing
|
||||
then:
|
||||
- switch.turn_off: timer_ringing
|
||||
else:
|
||||
- script.execute: start_wake_word
|
||||
- timing:
|
||||
- ON for at least 10s
|
||||
then:
|
||||
- button.press: factory_reset_btn
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led
|
||||
name: None
|
||||
disabled_by_default: true
|
||||
entity_category: config
|
||||
pin: GPIO27
|
||||
default_transition_length: 0s
|
||||
chipset: SK6812
|
||||
num_leds: 1
|
||||
rgb_order: grb
|
||||
effects:
|
||||
- pulse:
|
||||
name: "Slow Pulse"
|
||||
transition_length: 250ms
|
||||
update_interval: 250ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
- pulse:
|
||||
name: "Fast Pulse"
|
||||
transition_length: 100ms
|
||||
update_interval: 100ms
|
||||
min_brightness: 50%
|
||||
max_brightness: 100%
|
||||
|
||||
script:
|
||||
- id: reset_led
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 100%
|
||||
green: 89%
|
||||
blue: 71%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||
- switch.is_on: use_listen_light
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: led
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
brightness: 60%
|
||||
effect: none
|
||||
else:
|
||||
- light.turn_off: led
|
||||
- id: start_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- micro_wake_word.start:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- not:
|
||||
- voice_assistant.is_running:
|
||||
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- id: stop_wake_word
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(wake_word_engine_location).state == "On device";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: Use listen light
|
||||
id: use_listen_light
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- script.execute: reset_led
|
||||
on_turn_off:
|
||||
- script.execute: reset_led
|
||||
- platform: template
|
||||
id: timer_ringing
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
on_turn_off:
|
||||
# Turn off the repeat mode and disable the pause between playlist items
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||
# Stop playing the alarm
|
||||
- media_player.stop:
|
||||
announcement: true
|
||||
on_turn_on:
|
||||
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||
- lambda: |-
|
||||
id(echo_media_player)
|
||||
->make_call()
|
||||
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||
.set_announcement(true)
|
||||
.perform();
|
||||
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||
- media_player.speaker.play_on_device_media_file:
|
||||
media_file: timer_finished_wave_file
|
||||
announcement: true
|
||||
- delay: 15min
|
||||
- switch.turn_off: timer_ringing
|
||||
|
||||
select:
|
||||
- platform: template
|
||||
entity_category: config
|
||||
name: Wake word engine location
|
||||
id: wake_word_engine_location
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
options:
|
||||
- In Home Assistant
|
||||
- On device
|
||||
initial_option: On device
|
||||
on_value:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "In Home Assistant";
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
- delay: 500ms
|
||||
- lambda: id(va).set_use_wake_word(true);
|
||||
- voice_assistant.start_continuous:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x == "On device";
|
||||
then:
|
||||
- lambda: id(va).set_use_wake_word(false);
|
||||
- voice_assistant.stop:
|
||||
- delay: 500ms
|
||||
- micro_wake_word.start:
|
||||
|
||||
micro_wake_word:
|
||||
on_wake_word_detected:
|
||||
- voice_assistant.start:
|
||||
wake_word: !lambda return wake_word;
|
||||
vad:
|
||||
models:
|
||||
- model: okay_nabu
|
||||
- model: hey_mycroft
|
||||
- model: hey_jarvis
|
||||
BIN
active/device_esphome/test_tdongle_image.png
Normal file
BIN
active/device_esphome/test_tdongle_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
active/device_esphome/test_tdongle_image.xcf
Normal file
BIN
active/device_esphome/test_tdongle_image.xcf
Normal file
Binary file not shown.
BIN
active/device_esphome/wake_word_triggered.flac
Normal file
BIN
active/device_esphome/wake_word_triggered.flac
Normal file
Binary file not shown.
BIN
active/device_esphome/wake_word_triggered.wav
Normal file
BIN
active/device_esphome/wake_word_triggered.wav
Normal file
Binary file not shown.
240
active/device_framework_16/dgpu_wake_from_sleep_post.md
Normal file
240
active/device_framework_16/dgpu_wake_from_sleep_post.md
Normal file
@@ -0,0 +1,240 @@
|
||||
## Laptop Specs
|
||||
|
||||
Operating System: Fedora Linux 41
|
||||
KDE Plasma Version: 6.3.4
|
||||
KDE Frameworks Version: 6.12.0
|
||||
Qt Version: 6.8.2
|
||||
Kernel Version: 6.13.9-200.fc41.x86_64 (64-bit)
|
||||
Graphics Platform: Wayland
|
||||
Processors: 16 × AMD Ryzen 9 7940HS w/ Radeon 780M Graphics
|
||||
Memory: 98.9 GB of RAM
|
||||
Graphics Processor: AMD Radeon 780M
|
||||
Manufacturer: Framework
|
||||
Product Name: Laptop 16 (AMD Ryzen 7040 Series)
|
||||
System Version: AJ
|
||||
|
||||
## The Problem
|
||||
|
||||
Hey everyone, I've had no luck searching for this issue online. I might be looking in the wrong places so please point me to existing posts if there's already a topic in flight on this, or another, forum.
|
||||
|
||||
My Framework 16's dGPU disconnects after waking from sleep. It won't show up in monitoring software (Mission Center from Flatpak) or MangoHud in games. It's also not being detected by steam games or ollama - both will only detect and use the internal gpu. I'm pretty sure it shows up in `lscpi` (shown below in dGPU disconnected state).
|
||||
|
||||
No bios settings have made a difference. This happens both on-battery and charging with the framework charger and a 240 watt Delta charger. Power profiles don't make a difference either.
|
||||
|
||||
I have secure boot on. I don't use hibernate. I have a couple custom udev rules that prevent usb devices from waking the laptop in sleep (shown below).
|
||||
|
||||
Looking for anything to try, thanks for the help!
|
||||
|
||||
## Details
|
||||
|
||||
### lspci
|
||||
|
||||
```bash
|
||||
00:00.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Root Complex
|
||||
00:00.2 IOMMU: Advanced Micro Devices, Inc. [AMD] Phoenix IOMMU
|
||||
00:01.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
|
||||
00:01.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
|
||||
00:02.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
|
||||
00:02.2 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
|
||||
00:02.4 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
|
||||
00:03.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
|
||||
00:03.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Family 19h USB4/Thunderbolt PCIe tunnel
|
||||
00:04.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
|
||||
00:04.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Family 19h USB4/Thunderbolt PCIe tunnel
|
||||
00:08.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
|
||||
00:08.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
|
||||
00:08.2 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
|
||||
00:08.3 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
|
||||
00:14.0 SMBus: Advanced Micro Devices, Inc. [AMD] FCH SMBus Controller (rev 71)
|
||||
00:14.3 ISA bridge: Advanced Micro Devices, Inc. [AMD] FCH LPC Bridge (rev 51)
|
||||
00:18.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 0
|
||||
00:18.1 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 1
|
||||
00:18.2 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 2
|
||||
00:18.3 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 3
|
||||
00:18.4 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 4
|
||||
00:18.5 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 5
|
||||
00:18.6 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 6
|
||||
00:18.7 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 7
|
||||
01:00.0 PCI bridge: Advanced Micro Devices, Inc. [AMD/ATI] Navi 10 XL Upstream Port of PCI Express Switch (rev 12)
|
||||
02:00.0 PCI bridge: Advanced Micro Devices, Inc. [AMD/ATI] Navi 10 XL Downstream Port of PCI Express Switch (rev 12)
|
||||
03:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Navi 33 [Radeon RX 7600/7600 XT/7600M XT/7600S/7700S / PRO W7600] (rev c1)
|
||||
03:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Navi 31 HDMI/DP Audio
|
||||
04:00.0 Network controller: Intel Corporation Wi-Fi 6E(802.11ax) AX210/AX1675* 2x2 [Typhoon Peak] (rev 1a)
|
||||
05:00.0 Non-Volatile memory controller: Seagate Technology PLC E18 PCIe SSD (rev 01)
|
||||
c4:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Phoenix1 (rev c1)
|
||||
c4:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Rembrandt Radeon High Definition Audio Controller
|
||||
c4:00.2 Encryption controller: Advanced Micro Devices, Inc. [AMD] Phoenix CCP/PSP 3.0 Device
|
||||
c4:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15b9
|
||||
c4:00.4 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15ba
|
||||
c4:00.5 Multimedia controller: Advanced Micro Devices, Inc. [AMD] ACP/ACP3X/ACP6x Audio Coprocessor (rev 63)
|
||||
c4:00.6 Audio device: Advanced Micro Devices, Inc. [AMD] Family 17h/19h/1ah HD Audio Controller
|
||||
c5:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Function
|
||||
c5:00.1 Signal processing controller: Advanced Micro Devices, Inc. [AMD] AMD IPU Device
|
||||
c6:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Function
|
||||
c6:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15c0
|
||||
c6:00.4 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15c1
|
||||
c6:00.5 USB controller: Advanced Micro Devices, Inc. [AMD] Pink Sardine USB4/Thunderbolt NHI controller #1
|
||||
c6:00.6 USB controller: Advanced Micro Devices, Inc. [AMD] Pink Sardine USB4/Thunderbolt NHI controller #2
|
||||
```
|
||||
|
||||
### UDev Rules
|
||||
|
||||
```conf
|
||||
ACTION=="add", SUBSYSTEM=="acpi", DRIVERS=="button", ATTRS{hid}=="PNP0C0D", ATTR{power/wakeup}="disabled"
|
||||
ACTION=="add", SUBSYSTEM=="serio", DRIVERS=="atkbd", ATTR{power/wakeup}="disabled"
|
||||
ACTION=="add", SUBSYSTEM=="i2c", DRIVERS=="i2c_hid_acpi", ATTRS{name}=="PIXA3854:00", ATTR{power/wakeup}="disabled"
|
||||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTR{power/wakeup}="disabled"
|
||||
```
|
||||
|
||||
### Dmesg
|
||||
|
||||
```bash
|
||||
[27962.377892] CPU: 11 UID: 0 PID: 187288 Comm: kworker/11:6 Tainted: G W 6.13.9-200.fc41.x86_64 #1
|
||||
[27962.377896] Tainted: [W]=WARN
|
||||
[27962.377898] Hardware name: Framework Laptop 16 (AMD Ryzen 7040 Series)/FRANMZCP09, BIOS 03.05 11/13/2024
|
||||
[27962.377901] Workqueue: pm pm_runtime_work
|
||||
[27962.377906] RIP: 0010:amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27962.378233] Code: c0 74 33 48 8b 4e 10 48 83 39 00 74 29 89 d1 48 8d 04 88 8b 08 85 c9 74 11 f0 ff 08 74 07 31 c0 e9 0a a3 c6 fa e9 1a fd ff ff <0f> 0b b8 ea ff ff ff e9 f9 a2 c6 fa b8 ea ff ff ff e9 ef a2 c6 fa
|
||||
[27962.378237] RSP: 0018:ffffb598a9667c98 EFLAGS: 00010246
|
||||
[27962.378241] RAX: ffff9a9fe45a0ea8 RBX: ffff9a9fdb880000 RCX: 0000000000000000
|
||||
[27962.378244] RDX: 0000000000000000 RSI: ffff9a9fdb8a5560 RDI: ffff9a9fdb880000
|
||||
[27962.378246] RBP: ffff9a9fdb8c55d0 R08: 0000000000000000 R09: 0000000001195b5a
|
||||
[27962.378249] R10: ffffb598a9667c48 R11: 0000000000000000 R12: 0000000000000006
|
||||
[27962.378251] R13: ffff9a9fdb880000 R14: 0000000000000001 R15: ffff9aa5dcb2b040
|
||||
[27962.378254] FS: 0000000000000000(0000) GS:ffff9ab67ff80000(0000) knlGS:0000000000000000
|
||||
[27962.378257] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
|
||||
[27962.378260] CR2: 00001f4402539014 CR3: 000000161c82c000 CR4: 0000000000f50ef0
|
||||
[27962.378263] PKRU: 55555554
|
||||
[27962.378265] Call Trace:
|
||||
[27962.378268] <TASK>
|
||||
[27962.378270] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27962.378274] ? show_trace_log_lvl+0x255/0x2f0
|
||||
[27962.378280] ? show_trace_log_lvl+0x255/0x2f0
|
||||
[27962.378289] ? gfx_v11_0_hw_fini+0x41/0xf0 [amdgpu]
|
||||
[27962.378597] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27962.378906] ? __warn.cold+0x93/0xfa
|
||||
[27962.378912] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27962.379216] ? report_bug+0xff/0x140
|
||||
[27962.379222] ? handle_bug+0x58/0x90
|
||||
[27962.379226] ? exc_invalid_op+0x17/0x70
|
||||
[27962.379230] ? asm_exc_invalid_op+0x1a/0x20
|
||||
[27962.379239] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27962.379549] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27962.379553] gfx_v11_0_hw_fini+0x41/0xf0 [amdgpu]
|
||||
[27962.379879] gfx_v11_0_suspend+0xe/0x20 [amdgpu]
|
||||
[27962.380210] amdgpu_ip_block_suspend+0x24/0x40 [amdgpu]
|
||||
[27962.380523] amdgpu_device_ip_suspend_phase2+0x125/0x340 [amdgpu]
|
||||
[27962.380830] amdgpu_device_suspend+0xcf/0x170 [amdgpu]
|
||||
[27962.381154] amdgpu_pmops_runtime_suspend+0xb9/0x1a0 [amdgpu]
|
||||
[27962.381488] pci_pm_runtime_suspend+0x67/0x1a0
|
||||
[27962.381494] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27962.381499] __rpm_callback+0x41/0x170
|
||||
[27962.381503] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27962.381508] rpm_callback+0x55/0x60
|
||||
[27962.381512] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27962.381516] rpm_suspend+0xe6/0x5f0
|
||||
[27962.381520] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27962.381523] ? finish_task_switch.isra.0+0x99/0x2c0
|
||||
[27962.381531] pm_runtime_work+0x98/0xb0
|
||||
[27962.381535] process_one_work+0x176/0x330
|
||||
[27962.381541] worker_thread+0x252/0x390
|
||||
[27962.381547] ? __pfx_worker_thread+0x10/0x10
|
||||
[27962.381551] kthread+0xcf/0x100
|
||||
[27962.381557] ? __pfx_kthread+0x10/0x10
|
||||
[27962.381562] ret_from_fork+0x31/0x50
|
||||
[27962.381568] ? __pfx_kthread+0x10/0x10
|
||||
[27962.381573] ret_from_fork_asm+0x1a/0x30
|
||||
[27962.381583] </TASK>
|
||||
[27962.381587] ---[ end trace 0000000000000000 ]---
|
||||
[27965.093916] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27965.093923] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27967.860541] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27967.860554] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27970.792292] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27970.792306] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27973.717776] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27973.717790] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27976.642661] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27976.642670] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27979.503724] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27979.503737] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27982.355092] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27982.355102] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27985.020353] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27985.020365] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27987.689301] amdgpu 0000:03:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE
|
||||
[27987.689308] [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue
|
||||
[27987.916382] [drm:gfx_v11_0_hw_fini [amdgpu]] *ERROR* failed to halt cp gfx
|
||||
[27987.916872] ------------[ cut here ]------------
|
||||
[27987.916873] WARNING: CPU: 11 PID: 187288 at drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:631 amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27987.917063] Modules linked in: ib_core uinput overlay rfcomm snd_seq_dummy snd_hrtimer nft_reject_ipv6 nft_masq nft_reject_ipv4 act_csum cls_u32 sch_htb nf_nat_tftp nf_conntrack_tftp bridge stp llc nf_conntrack_netbios_ns nf_conntrack_broadcast nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 ip_set nf_tables qrtr uhid bnep snd_hda_codec_realtek snd_hda_codec_generic snd_hda_scodec_component snd_hda_codec_hdmi sunrpc binfmt_misc vfat fat snd_sof_amd_acp70 snd_sof_amd_acp63 snd_sof_amd_vangogh snd_sof_amd_rembrandt snd_sof_amd_renoir snd_sof_amd_acp snd_sof_pci iwlmvm snd_sof_xtensa_dsp snd_sof cdc_mbim cdc_wdm cdc_ncm cdc_ether usbnet snd_sof_utils mac80211 snd_pci_ps snd_soc_acpi_amd_match snd_amd_sdw_acpi soundwire_amd soundwire_generic_allocation soundwire_bus snd_soc_sdca amd_atl intel_rapl_msr intel_rapl_common snd_soc_core libarc4 snd_hda_intel edac_mce_amd snd_intel_dspcfg snd_compress
|
||||
[27987.917127] cros_usbpd_charger leds_cros_ec gpio_cros_ec cros_charge_control led_class_multicolor cros_ec_chardev ac97_bus cros_ec_hwmon cros_ec_sysfs cros_usbpd_logger cros_usbpd_notify snd_intel_sdw_acpi snd_pcm_dmaengine snd_hda_codec kvm_amd hid_sensor_als snd_rpl_pci_acp6x snd_acp_pci hid_sensor_trigger cros_ec_dev snd_hda_core hid_sensor_iio_common snd_acp_legacy_common kvm btusb iwlwifi industrialio_triggered_buffer snd_pci_acp6x btrtl btintel snd_hwdep kfifo_buf btbcm snd_seq btmtk cros_ec_lpcs spd5118 cros_ec snd_seq_device industrialio rapl cfg80211 wmi_bmof bluetooth snd_pcm snd_pci_acp5x pcspkr snd_rn_pci_acp3x snd_timer r8152 snd_acp_config thunderbolt mii k10temp snd_soc_acpi i2c_piix4 snd rfkill i2c_smbus snd_pci_acp3x amd_pmf soundcore amdtee joydev amd_sfh tee platform_profile amd_pmc loop nfnetlink zram lz4hc_compress lz4_compress dm_crypt hid_logitech_hidpp hid_logitech_dj typec_displayport amdgpu amdxcp drm_exec gpu_sched drm_panel_backlight_quirks drm_buddy drm_ttm_helper ttm nvme i2c_algo_bit
|
||||
[27987.917196] drm_suballoc_helper crct10dif_pclmul crc32_pclmul drm_display_helper crc32c_intel nvme_core polyval_clmulni polyval_generic ghash_clmulni_intel video hid_multitouch sha512_ssse3 ucsi_acpi hid_sensor_hub typec_ucsi sha256_ssse3 sha1_ssse3 cec sp5100_tco typec nvme_auth wmi i2c_hid_acpi i2c_hid fuse i2c_dev
|
||||
[27987.917222] CPU: 11 UID: 0 PID: 187288 Comm: kworker/11:6 Tainted: G W 6.13.9-200.fc41.x86_64 #1
|
||||
[27987.917226] Tainted: [W]=WARN
|
||||
[27987.917227] Hardware name: Framework Laptop 16 (AMD Ryzen 7040 Series)/FRANMZCP09, BIOS 03.05 11/13/2024
|
||||
[27987.917229] Workqueue: pm pm_runtime_work
|
||||
[27987.917235] RIP: 0010:amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27987.917403] Code: c0 74 33 48 8b 4e 10 48 83 39 00 74 29 89 d1 48 8d 04 88 8b 08 85 c9 74 11 f0 ff 08 74 07 31 c0 e9 0a a3 c6 fa e9 1a fd ff ff <0f> 0b b8 ea ff ff ff e9 f9 a2 c6 fa b8 ea ff ff ff e9 ef a2 c6 fa
|
||||
[27987.917405] RSP: 0018:ffffb598a9667c68 EFLAGS: 00010246
|
||||
[27987.917408] RAX: ffff9a9fe45a0508 RBX: ffff9a9fc7c27000 RCX: 0000000000000000
|
||||
[27987.917410] RDX: 0000000000000000 RSI: ffff9a9fc7c27008 RDI: ffff9a9fdb880000
|
||||
[27987.917411] RBP: ffff9a9fdb880000 R08: ffffffffbc873d28 R09: ffff9a9fc04020e8
|
||||
[27987.917412] R10: ffffffffffffffff R11: 0000000000000000 R12: 0000000000000004
|
||||
[27987.917413] R13: ffff9a9fdb880000 R14: 0000000000000001 R15: ffff9aa5dcb2b040
|
||||
[27987.917415] FS: 0000000000000000(0000) GS:ffff9ab67ff80000(0000) knlGS:0000000000000000
|
||||
[27987.917416] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
|
||||
[27987.917418] CR2: 00001f4402539014 CR3: 000000161c82c000 CR4: 0000000000f50ef0
|
||||
[27987.917419] PKRU: 55555554
|
||||
[27987.917420] Call Trace:
|
||||
[27987.917423] <TASK>
|
||||
[27987.917425] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27987.917429] ? show_trace_log_lvl+0x255/0x2f0
|
||||
[27987.917435] ? show_trace_log_lvl+0x255/0x2f0
|
||||
[27987.917439] ? smu_smc_hw_cleanup+0x68/0xa0 [amdgpu]
|
||||
[27987.917633] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27987.917785] ? __warn.cold+0x93/0xfa
|
||||
[27987.917789] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27987.917930] ? report_bug+0xff/0x140
|
||||
[27987.917934] ? handle_bug+0x58/0x90
|
||||
[27987.917937] ? exc_invalid_op+0x17/0x70
|
||||
[27987.917939] ? asm_exc_invalid_op+0x1a/0x20
|
||||
[27987.917945] ? amdgpu_irq_put+0x46/0x70 [amdgpu]
|
||||
[27987.918084] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27987.918087] smu_smc_hw_cleanup+0x68/0xa0 [amdgpu]
|
||||
[27987.918231] smu_suspend+0x77/0xe0 [amdgpu]
|
||||
[27987.918344] amdgpu_ip_block_suspend+0x24/0x40 [amdgpu]
|
||||
[27987.918461] amdgpu_device_ip_suspend_phase2+0x125/0x340 [amdgpu]
|
||||
[27987.918568] amdgpu_device_suspend+0xcf/0x170 [amdgpu]
|
||||
[27987.918674] amdgpu_pmops_runtime_suspend+0xb9/0x1a0 [amdgpu]
|
||||
[27987.918778] pci_pm_runtime_suspend+0x67/0x1a0
|
||||
[27987.918782] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27987.918784] __rpm_callback+0x41/0x170
|
||||
[27987.918787] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27987.918790] rpm_callback+0x55/0x60
|
||||
[27987.918792] ? __pfx_pci_pm_runtime_suspend+0x10/0x10
|
||||
[27987.918794] rpm_suspend+0xe6/0x5f0
|
||||
[27987.918795] ? srso_alias_return_thunk+0x5/0xfbef5
|
||||
[27987.918797] ? finish_task_switch.isra.0+0x99/0x2c0
|
||||
[27987.918802] pm_runtime_work+0x98/0xb0
|
||||
[27987.918805] process_one_work+0x176/0x330
|
||||
[27987.918808] worker_thread+0x252/0x390
|
||||
[27987.918811] ? __pfx_worker_thread+0x10/0x10
|
||||
[27987.918813] kthread+0xcf/0x100
|
||||
[27987.918816] ? __pfx_kthread+0x10/0x10
|
||||
[27987.918818] ret_from_fork+0x31/0x50
|
||||
[27987.918821] ? __pfx_kthread+0x10/0x10
|
||||
[27987.918823] ret_from_fork_asm+0x1a/0x30
|
||||
[27987.918828] </TASK>
|
||||
[27987.918829] ---[ end trace 0000000000000000 ]---
|
||||
[27987.918833] amdgpu 0000:03:00.0: amdgpu: Fail to disable thermal alert!
|
||||
[27987.918836] amdgpu 0000:03:00.0: amdgpu: suspend of IP block <smu> failed -22
|
||||
[27987.918851] amdgpu 0000:03:00.0: amdgpu: SMU: response:0xFFFFFFFF for index:46 param:0x00000000 message:PrepareMp1ForUnload?
|
||||
[27987.918853] amdgpu 0000:03:00.0: amdgpu: [PrepareMp1] Failed!
|
||||
[27987.918855] [drm:amdgpu_device_ip_suspend_phase2 [amdgpu]] *ERROR* SMC failed to set mp1 state 2, -121
|
||||
[28270.864511] amdgpu 0000:03:00.0: amdgpu: PSP is resuming...
|
||||
[28271.140289] amdgpu 0000:03:00.0: amdgpu: PSP create ring failed!
|
||||
[28271.140299] amdgpu 0000:03:00.0: amdgpu: PSP resume failed
|
||||
[28271.140303] amdgpu 0000:03:00.0: amdgpu: resume of IP block <psp> failed -62
|
||||
[28271.140309] amdgpu 0000:03:00.0: amdgpu: amdgpu_device_ip_resume failed (-62).
|
||||
```
|
||||
23
active/device_framework_16/framework_16.md
Normal file
23
active/device_framework_16/framework_16.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Framework Laptop 16
|
||||
|
||||
## Keyboard VIA
|
||||
|
||||
Access keyboard configuration at <https://keyboard.frame.work/>
|
||||
|
||||
You might need to add the qmk udev rules for the browser to access your keyboard. Follow below:
|
||||
|
||||
```bash
|
||||
sudo curl -o /etc/udev/rules.d/50-qmk.rules https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
## Beta Bios Updates
|
||||
|
||||
```bash
|
||||
# With charger attached
|
||||
sudo fwupdmgr enable-remote lvfs-testing
|
||||
sudo fwupdmgr refresh --force
|
||||
sudo fwupdmgr get-updates
|
||||
sudo fwupdmgr update
|
||||
```
|
||||
BIN
active/device_framework_16/icc_profile.icm
Normal file
BIN
active/device_framework_16/icc_profile.icm
Normal file
Binary file not shown.
710
active/device_home_assistant/home_assistant.md
Normal file
710
active/device_home_assistant/home_assistant.md
Normal file
@@ -0,0 +1,710 @@
|
||||
# Home Assistant
|
||||
|
||||
- [Home Assistant](#home-assistant)
|
||||
- [Certificates](#certificates)
|
||||
- [Setup and Configuration](#setup-and-configuration)
|
||||
- [Schlage Door Lock](#schlage-door-lock)
|
||||
- [Philips Hue Lights](#philips-hue-lights)
|
||||
- [Shelly](#shelly)
|
||||
- [Barometer](#barometer)
|
||||
- [Relative Humidity Calculator](#relative-humidity-calculator)
|
||||
- [Font Colors](#font-colors)
|
||||
- [Light Indicator for Voice Assistant](#light-indicator-for-voice-assistant)
|
||||
- [Blank Button (Spacer)](#blank-button-spacer)
|
||||
- [Roku Remote](#roku-remote)
|
||||
- [Flair Vent Battery](#flair-vent-battery)
|
||||
- [Voice](#voice)
|
||||
- [Changing the Voice of TTS](#changing-the-voice-of-tts)
|
||||
- [Custom Sentences](#custom-sentences)
|
||||
- [Overriding Default Sentences](#overriding-default-sentences)
|
||||
- [Notifications](#notifications)
|
||||
- [Unifi Cameras](#unifi-cameras)
|
||||
- [Multiple Entity Triggers with Custom Names](#multiple-entity-triggers-with-custom-names)
|
||||
- [Philips Hue Switches](#philips-hue-switches)
|
||||
- [Datetimes](#datetimes)
|
||||
- [LG TV Switch](#lg-tv-switch)
|
||||
|
||||
## Certificates
|
||||
|
||||
Note, self signed certs won't work on the hass android app.
|
||||
|
||||
```bash
|
||||
# Generate the key/cert
|
||||
# Note, 36159 days == 99 years
|
||||
openssl req \
|
||||
-sha256 \
|
||||
-addext "subjectAltName = IP:10.2.0.230" \
|
||||
-newkey rsa:4096 \
|
||||
-nodes \
|
||||
-keyout privkey.pem \
|
||||
-x509 \
|
||||
-days 36159 \
|
||||
-out fullchain.pem
|
||||
|
||||
http:
|
||||
server_port: 8123
|
||||
ssl_certificate: /ssl/fullchain.pem
|
||||
ssl_key: /ssl/privkey.pem
|
||||
```
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### Schlage Door Lock
|
||||
|
||||
1. Install Z-wave
|
||||
2. Install z-wave JS module
|
||||
3. Add device -> How do you want to add your device -> Legacy Secure
|
||||
4. Disconnect and Reconnect the battery on the lock
|
||||
5. Press and hold the zwave button until the light turns solid red, release and it should flash red
|
||||
1. (OR) Enter programming pin on lock -> 0 (this may take a few attempts, don't click the pair button)
|
||||
|
||||
If the lock ever disconnects you can safely delete it from home assistant and re-interview. It will
|
||||
set back up with the correct entity IDs and automations/dashboards will work just fine.
|
||||
|
||||
### Philips Hue Lights
|
||||
|
||||
1. I configure all philips hue lights through zigbee directly connected to HA
|
||||
|
||||
hue lights support color_temp in mireds, here are some mired-kelvin conversions:
|
||||
|
||||
| Kelvin | Mired |
|
||||
| ------ | ----- |
|
||||
| 6000 | 167 |
|
||||
| 4000 | 250 |
|
||||
| 2600 | 385 |
|
||||
|
||||
### Shelly
|
||||
|
||||
1. Outbound Websocket `wss://homeassistant.reeseapps.com/api/shelly/ws`
|
||||
|
||||
Shelly devices can act as "passive" or "active" bluetooth scanners. Both of these configurations
|
||||
allow home assistant to proxy bluetooth connections through shelly devices, significantly extending
|
||||
the range of your home assistant's bluetooth capabilities. Active scanning uses more power but
|
||||
is quicker to pick up and transmit device information. Note that "gateway mode" is not required,
|
||||
just enable bluetooth and rpc or select "active" from the configuration menu for the shelly
|
||||
device.
|
||||
|
||||
#### Barometer
|
||||
|
||||
<https://www.thoughtco.com/how-to-read-a-barometer-3444043>
|
||||
|
||||
A barometric reading over 30.20 inHg is generally considered high, and high pressure is associated with clear skies and calm weather.
|
||||
|
||||
If the reading is over 30.20 inHg (102268.9 Pa or 1022.689 mb):
|
||||
|
||||
- Rising or steady pressure means continued fair weather.
|
||||
- Slowly falling pressure means fair weather.
|
||||
- Rapidly falling pressure means cloudy and warmer conditions.
|
||||
|
||||
A barometric reading in the range of 29.80 and 30.20 inHg can be considered normal, and normal pressure is associated with steady weather.
|
||||
|
||||
If the reading falls between 29.80 and 30.20 inHg (100914.4–102268.9 Pa or 1022.689–1009.144 mb):
|
||||
|
||||
- Rising or steady pressure means present conditions will continue.
|
||||
- Slowly falling pressure means little change in the weather.
|
||||
- Rapidly falling pressure means that rain is likely, or snow if it is cold enough.
|
||||
|
||||
A barometric reading below 29.80 inHg is generally considered low, and low pressure is associated with warm air and rainstorms.
|
||||
|
||||
If the reading is under 29.80 inHg (100914.4 Pa or 1009.144 mb):
|
||||
|
||||
- Rising or steady pressure indicates clearing and cooler weather.
|
||||
- Slowly falling pressure indicates rain.
|
||||
- Rapidly falling pressure indicates a storm is coming.
|
||||
|
||||
A basic automation would look like
|
||||
|
||||
```yaml
|
||||
It's {{ int(states("sensor.grouse_temp")) }} degrees and {{ states("weather.grouse_weather") }}. The relative humidity is {{ states("sensor.grouse_humidity") }}%. I'm seeing {{ int(states("sensor.grouse_wind_speed")) }}mph wind with gusts up to {{ int(states("sensor.grouse_wind_gust")) }}mph.
|
||||
|
||||
{% set pressure = float(states("sensor.grouse_rel_pressure")) %}
|
||||
The barometer reads {{ pressure }}inHg.
|
||||
{% if pressure > 30.20 %}
|
||||
Fair weather is expected
|
||||
{% elif pressure > 29.80 %}
|
||||
Rain is possible
|
||||
{% else %}
|
||||
Rain is coming.
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
#### Relative Humidity Calculator
|
||||
|
||||
<https://www.wikihow.com/Calculate-Humidity>
|
||||
|
||||
You can calculate the relative humidity of the outdoor air if warmed to indoor temperatures like so:
|
||||
|
||||
```jinja
|
||||
{% set dew_point = state_attr("weather.forecast_home", "dew_point") %}
|
||||
{% set air_temp_f = state_attr("climate.ecobee_thermostat", "current_temperature") %}
|
||||
{% set air_temp = (5/9)*(air_temp_f-32) %}
|
||||
{% set sat_vap_press = 6.11 * 10**((7.5*air_temp) / (237.3+air_temp)) %}
|
||||
{% set act_vap_press = 6.11 * 10**((7.5*dew_point) / (237.3+dew_point)) %}
|
||||
{% set rel_hum = 100*(act_vap_press / sat_vap_press) %}
|
||||
|
||||
{{ dew_point }}
|
||||
{{ air_temp }}
|
||||
{{ sat_vap_press }}
|
||||
{{ act_vap_press }}
|
||||
{{ rel_hum }}
|
||||
```
|
||||
|
||||
### Font Colors
|
||||
|
||||
```html
|
||||
<font color = {{ "green" if state_attr("climate.ecobee_thermostat", "current_humidity") > low_humidity and state_attr("climate.ecobee_thermostat", "current_humidity") < high_humidity else "red" }}>
|
||||
HVAC Humidity: {{ state_attr("climate.ecobee_thermostat", "current_humidity") }}%
|
||||
</font>
|
||||
```
|
||||
|
||||
### Light Indicator for Voice Assistant
|
||||
|
||||
```yaml
|
||||
alias: Flash Lights on Bedroom Voice Assistant Start
|
||||
description: ""
|
||||
triggers:
|
||||
- type: turned_on
|
||||
device_id: d50fa1ae499e88bf37225c7e82ed189b
|
||||
entity_id: 7ab2896ca3a55efd2e0ee9bba91fdf68
|
||||
domain: binary_sensor
|
||||
metadata:
|
||||
secondary: false
|
||||
trigger: device
|
||||
conditions: []
|
||||
actions:
|
||||
- action: scene.create
|
||||
metadata: {}
|
||||
data:
|
||||
scene_id: bedroombeforescene
|
||||
snapshot_entities:
|
||||
- light.main_bedroom_lamps
|
||||
- action: light.turn_on
|
||||
metadata: {}
|
||||
data:
|
||||
transition: 0.25
|
||||
brightness_step_pct: 5
|
||||
target:
|
||||
entity_id: light.main_bedroom_lamps
|
||||
- delay:
|
||||
hours: 0
|
||||
minutes: 0
|
||||
seconds: 0
|
||||
milliseconds: 250
|
||||
- action: scene.turn_on
|
||||
data:
|
||||
entity_id: scene.bedroombeforescene
|
||||
transition: 0.25
|
||||
mode: single
|
||||
```
|
||||
|
||||
### Blank Button (Spacer)
|
||||
|
||||
```yaml
|
||||
- type: button
|
||||
tap_action:
|
||||
action: none
|
||||
show_state: false
|
||||
show_name: false
|
||||
show_icon: false
|
||||
hold_action:
|
||||
action: none
|
||||
```
|
||||
|
||||
### Roku Remote
|
||||
|
||||
```yaml
|
||||
type: vertical-stack
|
||||
cards:
|
||||
- type: entity
|
||||
entity: select.left_living_room_application
|
||||
- square: true
|
||||
type: grid
|
||||
cards:
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: power
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:power
|
||||
name: power
|
||||
show_state: false
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: volume_down
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:volume-minus
|
||||
name: volume down / hold mute
|
||||
show_state: false
|
||||
hold_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: volume_mute
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: volume_up
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:volume-plus
|
||||
name: volume up / hold mute
|
||||
show_state: false
|
||||
"hold_action:":
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: volume_mute
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: back
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:undo
|
||||
name: back
|
||||
show_state: false
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: up
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:arrow-up-bold
|
||||
name: up
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: home
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:home
|
||||
name: home
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: left
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:arrow-left-bold
|
||||
name: left
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: select
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:select-all
|
||||
name: select
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: right
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:arrow-right-bold
|
||||
name: right
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: play
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:play-pause
|
||||
name: play/pause
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: down
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:arrow-down-bold
|
||||
name: down
|
||||
hold_action:
|
||||
action: none
|
||||
- show_name: true
|
||||
show_icon: true
|
||||
type: button
|
||||
tap_action:
|
||||
action: perform-action
|
||||
perform_action: remote.send_command
|
||||
target:
|
||||
entity_id: remote.left_living_room
|
||||
data:
|
||||
command: info
|
||||
entity: remote.left_living_room
|
||||
icon: mdi:wrench
|
||||
name: settings
|
||||
hold_action:
|
||||
action: none
|
||||
title: Left Living Room TV
|
||||
```
|
||||
|
||||
### Flair Vent Battery
|
||||
|
||||
Flair vents report low battery at 2.4v. 3v is nominal/full.
|
||||
|
||||
```yaml
|
||||
{% set volt_min=2.4 %}
|
||||
{% set volt_max=3.0 %}
|
||||
{% set volt_diff_max=0.6 %}
|
||||
|
||||
{{ (min(float(states("sensor.main_bedroom_29bf_voltage")) - volt_min, volt_diff_max) / volt_diff_max) * 100 }}
|
||||
```
|
||||
|
||||
## Voice
|
||||
|
||||
### Changing the Voice of TTS
|
||||
|
||||
Select a media player -> play TTS -> select voice -> copy voice ID.
|
||||
|
||||
```yaml
|
||||
options:
|
||||
voice: DavisNeural||chat
|
||||
```
|
||||
|
||||
### Custom Sentences
|
||||
|
||||
<https://developers.home-assistant.io/docs/voice/intent-recognition/template-sentence-syntax/#sentence-templates-syntax>
|
||||
|
||||
### Overriding Default Sentences
|
||||
|
||||
1. Identify if your sentence conflicts with [Home Assistant's default
|
||||
sentences](https://github.com/OHF-Voice/intents/tree/main/sentences/en)
|
||||
2. Create a new file at `/config/custom_sentences/en/overrides.yaml`
|
||||
3. As an example, to override the `HassGetWeather` sentence:
|
||||
1. Copy the contents of `weather_HassGetWeather.yaml` into `overrides.yaml`
|
||||
2. Rename `HassGetWeather` to `HassGetWeather_Custom`
|
||||
3. Delete the required context `weather`
|
||||
4. Now in `configuration.yaml`, under a section called `intent_script`, add the following
|
||||
|
||||
```yaml
|
||||
HassGetWeather_Custom:
|
||||
speech:
|
||||
text: >-
|
||||
It's {{ int(states("sensor.backyard_weather_station_temp")) }} degrees
|
||||
with {{ states("sensor.backyard_weather_station_humidity") }}% humidity.
|
||||
I'm seeing {{ int(states("sensor.backyard_weather_station_wind_speed"))
|
||||
}}mph wind. It's rained {{
|
||||
int(states("sensor.backyard_weather_station_hourly_rain_rate")) }} inches
|
||||
in the last hour.
|
||||
```
|
||||
|
||||
5. Restart Home Assistant
|
||||
6. Navigate to Settings -> Voice Assistants -> Click the 3 dots next to
|
||||
your voice assistant -> Debug -> Click the icon in the top right -> Run
|
||||
text pipeline -> "What's the weather"
|
||||
|
||||
## Notifications
|
||||
|
||||
Notification Information:
|
||||
|
||||
<https://www.home-assistant.io/docs/automation/templating/>
|
||||
|
||||
```yaml
|
||||
Triggered by {{ trigger.entity_id }}, Date: {{ now().strftime('%Y-%m-%d') }}, Time: {{ now().strftime('%H:%M') }}
|
||||
```
|
||||
|
||||
## Unifi Cameras
|
||||
|
||||
Create image/video previews of events with the following automation:
|
||||
|
||||
```yaml
|
||||
alias: Vehicle Driveway Notification
|
||||
description: Sends a notification with video upon motion detection.
|
||||
triggers:
|
||||
- entity_id:
|
||||
- binary_sensor.driveway_camera_vehicle_detected
|
||||
trigger: state
|
||||
from: "on"
|
||||
to: "off"
|
||||
actions:
|
||||
- data:
|
||||
message: Vehicle detected on Driveway Camera
|
||||
data:
|
||||
image: >-
|
||||
/api/unifiprotect/thumbnail/{{ config_entry_id(trigger.entity_id)
|
||||
}}/{{ trigger.from_state.attributes.event_id }}
|
||||
video: >-
|
||||
/api/unifiprotect/video/{{ config_entry_id(trigger.entity_id) }}/{{
|
||||
trigger.from_state.attributes.event_id }}
|
||||
action: notify.notify
|
||||
mode: single
|
||||
max_exceeded: silent
|
||||
```
|
||||
|
||||
## Multiple Entity Triggers with Custom Names
|
||||
|
||||
You can set an "id" for a trigger that can be used as a human readable name.
|
||||
|
||||
```yaml
|
||||
alias: Notify when a Door Opened
|
||||
description: ""
|
||||
triggers:
|
||||
- trigger: state
|
||||
entity_id:
|
||||
- binary_sensor.my_front_door
|
||||
from: "off"
|
||||
to: "on"
|
||||
id: Front Door
|
||||
- trigger: state
|
||||
entity_id:
|
||||
- binary_sensor.my_back_door
|
||||
from: "off"
|
||||
to: "on"
|
||||
id: Back Door
|
||||
- trigger: state
|
||||
entity_id:
|
||||
- binary_sensor.super_secret_door
|
||||
from: "off"
|
||||
to: "on"
|
||||
id: Trap Door
|
||||
conditions: []
|
||||
actions:
|
||||
- action: notify.notify
|
||||
metadata: {}
|
||||
data:
|
||||
message: "{{ trigger.id }} Opened"
|
||||
mode: single
|
||||
```
|
||||
|
||||
## Philips Hue Switches
|
||||
|
||||
Philips Hue Switches don't expose entities, but rather trigger "zha_event" events.
|
||||
|
||||
To see events fired by these devices: Developer tools -> Events -> Listen to events `zha_event`
|
||||
|
||||
You can use this in automations like so:
|
||||
|
||||
```yaml
|
||||
alias: Some Switch
|
||||
description: ""
|
||||
triggers:
|
||||
- device_id: bb54b111ec77fb7d5356bb600789098f
|
||||
domain: zha
|
||||
type: remote_button_short_press
|
||||
subtype: turn_on
|
||||
trigger: device
|
||||
id: "on"
|
||||
- device_id: bb54b111ec77fb7d5356bb600789098f
|
||||
domain: zha
|
||||
type: remote_button_long_press
|
||||
subtype: turn_on
|
||||
trigger: device
|
||||
id: on-con
|
||||
conditions: []
|
||||
actions:
|
||||
- action: scene.turn_on
|
||||
metadata: {}
|
||||
data: {}
|
||||
target:
|
||||
entity_id: scene.some_scene
|
||||
mode: single
|
||||
```
|
||||
|
||||
## Datetimes
|
||||
|
||||
Stolen from Reddit
|
||||
|
||||
```yaml
|
||||
## Set placeholder templates for reference in this template
|
||||
## 'dt' substitutes 'now()'
|
||||
## eg. if currently 5 March 2024 at 09:08:07 (AM)
|
||||
eg_now = {% set eg_now = "2024-03-05 09:08:07.123456+00:00" %}{{ eg_now }}
|
||||
dt = {% set dt = eg_now | as_datetime %}{{ dt }}
|
||||
ts = {% set ts = eg_now | as_timestamp %}{{ ts }}
|
||||
|
||||
## Basic Time & Date Functions
|
||||
time_now: {{ now() }}
|
||||
time_local: {{ now() | as_local }}
|
||||
time_timestamp: {{ now() | as_timestamp }}
|
||||
|
||||
## Time Conversions
|
||||
seconds_per_min : {% set spm = 60 | int %}{{ spm }}
|
||||
seconds_per_hour: {% set sph = ( spm * 60 ) | int %}{{ sph }}
|
||||
seconds_per_day : {% set spd = 86400 | int %}{{ spd }}
|
||||
seconds_per_week: {% set spw = ( spd * 7 ) | int %}{{ spw }}
|
||||
minutes_per_day : {% set mpd = ( spd / 60 ) | int %}{{ mpd }}
|
||||
minutes_per_week: {% set mpw = ( mpd * 7 ) | int %}{{ mpw }}
|
||||
hours_per_week : {% set hpw = ( 24 * 7 ) | int %}{{ hpw }}
|
||||
|
||||
## Time Calculations
|
||||
## with DATETIME use timedelta:
|
||||
* CURRENT TIME : {{ dt }}
|
||||
+ 1 YEAR : {{ dt + timedelta(days=365) }}
|
||||
- 1 DAY (24H) : {{ dt - timedelta(days=1) }}
|
||||
+ 3 DAYS (72H) : {{ dt + timedelta(days=3) }}
|
||||
- 3 HOURS : {{ dt - timedelta(hours=3) }}
|
||||
+ 1 HR 26 MIN : {{ dt + timedelta(hours=1, minutes=26) }}
|
||||
+ 1D 2H 3M 4S : {{ dt + timedelta(days=1, hours=2, minutes=3, seconds=4) }}
|
||||
|
||||
## with TIMESTAMP use maths and then convert:
|
||||
## Referencing earlier calculations for ease
|
||||
* TIMESTAMP : {{ ts }}
|
||||
* CURRENT TIME : {{ ts | as_datetime }}
|
||||
+ 1 YEAR : {{ ( ts + (spd * 365) ) | as_datetime }}
|
||||
- 1 DAY (24H) : {{ ( ts - spd ) | as_datetime }}
|
||||
+ 3 DAYS (72H) : {{ ( ts + (spd * 3) ) | as_datetime }}
|
||||
- 3 HOURS : {{ ( ts - (sph * 3) ) | as_datetime }}
|
||||
+ 1 HR 26 MIN : {{ ( ts + sph + (spm * 26) ) | as_datetime }}
|
||||
+ 1D 2H 3M 4S : {{ ( ts + spd + (sph * 2) + (spm * 3) + 4 ) | as_datetime }}
|
||||
|
||||
## Adjusting Time & Date For Calculations
|
||||
Start Of Today: {% set start_today = dt.replace(hour=0, minute=0, second=0, microsecond=0) %}{{ start_today }}
|
||||
End Of Today : {% set start_tomorrow = start_today + timedelta(days=1) %}{{ start_tomorrow }}
|
||||
|
||||
## Use Relative Time For DATETIME in the PAST
|
||||
relative_time: {{ relative_time( start_today ) }} ago
|
||||
|
||||
## For time in the FUTURE you can use:
|
||||
{% set current_time = dt %}{% set future_time = as_local(dt) %}{% set time_distance = future_time - current_time %}
|
||||
relative_future: In {{ relative_time(current_time - time_distance) }}
|
||||
|
||||
## Use Time Templates combined with History Stats Sensor:
|
||||
sensor:
|
||||
- platform: history_stats
|
||||
name: Lamp ON today
|
||||
entity_id: light.my_lamp
|
||||
state: "on"
|
||||
```
|
||||
|
||||
Stolen from <https://www.fabriziomusacchio.com/blog/2021-08-15-strftime_Cheat_Sheet/>
|
||||
|
||||
| Format | Example | Description |
|
||||
| ------ | ------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||
| %c | Thu Jan 28 12:32:01 2014 | locale’s appropriate date and time representation |
|
||||
| %D | 23/05/12 | formats the date |
|
||||
| %F | 2002-01-30 | date in ISO 8601 format YYYY-MM-DD |
|
||||
| %x | 02/10/11 | locale’s appropriate date representation |
|
||||
| %X | 14:22:01 | locale’s appropriate time representation |
|
||||
| %r | 3:44:12 AM | 12-hour time |
|
||||
| %R | 15:21 | 24-hour time HH:MM |
|
||||
| %T | 15:21:59 | time in ISO 8601 format HH:MM:SS |
|
||||
| %A | Monday | full weekday name |
|
||||
| %a | Mon | abbreviated weekday name |
|
||||
| %w | 0-6 | day of the week with Sunday as 0 |
|
||||
| %d | 01-31 | day of the month (with a leading zero) |
|
||||
| %e | 1-31 | day of the month (without a leading zero) |
|
||||
| %B | April | full month name |
|
||||
| %b | Apr | abbreviated month name |
|
||||
| %m | 01-12 | month of the year (with a leading zero) |
|
||||
| %-m | 1-12 | month of the year (without a leading zero) |
|
||||
| %Y | 2003 | year |
|
||||
| %y | 00-99 | year without a century (last two digits, with a leading zero) |
|
||||
| %-y | 0-99 | year without a century (last two digits, without a leading zero) |
|
||||
| %H | 00-23 | hour of the day, 24-hour time (with a leading zero) |
|
||||
| %k | 0-23 | hour of the day, 24-hour time (without a leading zero) |
|
||||
| %I | 01-11 | hour of the day, 12-hour time (with a leading zero) |
|
||||
| %-I | 1-11 | hour of the day, 12-hour time (without a leading zero) |
|
||||
| %P | am, pm | am or pm designation |
|
||||
| %p | AM, PM | AM or PM designation |
|
||||
| %M | 00-59 | minute of the hour (with a leading zero) |
|
||||
| %-M | 0-59 | minute of the hour (without a leading zero) |
|
||||
| %S | 00-60 | second of the minute (with a leading zero) |
|
||||
| %-S | 0-60 | second of the minute (without a leading zero) |
|
||||
| %f | 000000-999999 | microsecond of the second (with a leading zero) |
|
||||
| %Z | UTC | timezone name or abbreviation |
|
||||
| %z | +0000 | UTC offset in the form +HHMM or -HHMM |
|
||||
| %s | | amount of seconds since 1970-01-01 00:00:00 UTC |
|
||||
| %% | | % sign |
|
||||
| %j | 001-366 | day of the year (with a leading zeroes) |
|
||||
| %U | 00-53 | week number with the first Sunday as the first day of week one |
|
||||
| %W | 00-53 | week number of the current year, starting with the first Monday as the first day of the first week |
|
||||
| %V | 01-53 | week number in ISO 8601 format |
|
||||
|
||||
## LG TV Switch
|
||||
|
||||
```yaml
|
||||
- platform: wake_on_lan
|
||||
mac: b4:b2:91:8e:ce:20
|
||||
name: loft_lg_tv_wol
|
||||
turn_off:
|
||||
service: media_player.turn_off
|
||||
target:
|
||||
device_id: "{{device_id('media_player.loft_lg_tv')}}"
|
||||
|
||||
- platform: wake_on_lan
|
||||
mac: 60:8d:26:2c:4d:45
|
||||
name: living_room_lg_tv_wol
|
||||
turn_off:
|
||||
service: media_player.turn_off
|
||||
target:
|
||||
device_id: "{{device_id('media_player.living_room_lg_tv')}}"
|
||||
```
|
||||
88
active/device_shelly/shelly.md
Normal file
88
active/device_shelly/shelly.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Shelly Devices
|
||||
|
||||
- [Shelly Devices](#shelly-devices)
|
||||
- [1PM Mini Gen4](#1pm-mini-gen4)
|
||||
- [Setup 1PM Mini Gen4](#setup-1pm-mini-gen4)
|
||||
- [Install 1PM Mini Gen4](#install-1pm-mini-gen4)
|
||||
- [Shelly Plug US](#shelly-plug-us)
|
||||
- [Shelly BLU Motion](#shelly-blu-motion)
|
||||
- [Shelly BLU Door/Window](#shelly-blu-doorwindow)
|
||||
- [Reset](#reset)
|
||||
- [Shelly Flood](#shelly-flood)
|
||||
|
||||
## 1PM Mini Gen4
|
||||
|
||||
### Setup 1PM Mini Gen4
|
||||
|
||||
1. Cut 1 white and 3 black pieces of 14 gauge wire to 3" long.
|
||||
2. Strip 1/4" from one side of each wire.
|
||||
3. Strip 1/2" from the other side of each wire.
|
||||
4. Connect the 1/4" side to the shelly. Tighten the screws until you can't turn them.
|
||||
5. Push line and neutral into a standard outlet. The wider receptacle is neutral.
|
||||
6. Press and hold the button for 10 seconds to factory reset. Light will flash on/off every 1/4 second.
|
||||
7. Press and hold the button for 5 seconds to turn on AP mode. Light will flash on/off every 1/2 second.
|
||||
8. Connect to shelly network.
|
||||
9. Navigate to <http://192.168.33.1>.
|
||||
10. Connect to wifi. The light should turn solid.
|
||||
11. Update firmware.
|
||||
12. Set a password for AP mode.
|
||||
13. Turn off the AP.
|
||||
14. In Unifi: Name the device, give it a fixed IP, set the icon.
|
||||
15. Navigate to the Shelly website via its IP address.
|
||||
16. Set a password for http access: Settings -> Authentication.
|
||||
17. Name the device: Settings -> Device name.
|
||||
18. Set Restore last known state of output/relay: Home -> Output -> Input/Output Settings.
|
||||
19. Enable Zigbee: Zigbee -> Enable.
|
||||
20. Connect Shelly to Home Assistant via Zigbee.
|
||||
21. Change switch type: Click on switch control -> Settings -> Show as.
|
||||
|
||||
### Install 1PM Mini Gen4
|
||||
|
||||
1. Cut 1 3" white wire for neutral bridge.
|
||||
2. Cut 2 3" black wires for line bridge and light switch input.
|
||||
3. Prepare 4 14 gauge wire connectors.
|
||||
|
||||
## Shelly Plug US
|
||||
|
||||
1. Connect to WiFi
|
||||
2. Set password for AP
|
||||
3. Disable AP
|
||||
4. Set password for device authentication
|
||||
5. Set Restore last known state of output/relay
|
||||
6. Set Device Name
|
||||
7. Enable Bluetooth Gateway
|
||||
8. Update Firmware
|
||||
|
||||
## Shelly BLU Motion
|
||||
|
||||
1. Download and install the Shelly Debug app
|
||||
2. Follow the instructions in the app to connect the device
|
||||
3. Update the firmware
|
||||
4. Enable encryption (generate a 6 digit code)
|
||||
5. "Read" from the device and copy the encryption key for home assistant
|
||||
|
||||
## Shelly BLU Door/Window
|
||||
|
||||
1. Download and install the Shelly Debug app
|
||||
2. Follow the instructions in the app to connect the device
|
||||
3. Update the firmware
|
||||
4. Create a new "login" in Bitwarden called "Shelly BLU DW " + name of device
|
||||
1. Password will be the encryption key
|
||||
2. Website should be the MAC address of the Shelly
|
||||
5. Generate a 6 digit code, send it to your phone then throw it away
|
||||
6. In the Shelly Debug app, enable encryption using the 6 digit code
|
||||
7. Copy the encryption and store in the password field
|
||||
8. Add to Home Assistant
|
||||
9. Unpair from Phone
|
||||
|
||||
### Reset
|
||||
|
||||
Resetting is super finnicky. You'll need to plug it in, press and hold the power button until the
|
||||
red light flashes quickly (not slowly, that's a reboot). You'll probably have to do it multiple
|
||||
times because they seem to reboot halfway through the reset process.
|
||||
|
||||
## Shelly Flood
|
||||
|
||||
1. In the web interface, ensure "CoIoT" is enabled and pointing to `<home assistant ip>:5683`.
|
||||
Allow 5683/udp from shelly flood to home assistant. If you don't do this Shelly Flood will
|
||||
not report its status correctly!
|
||||
0
kubernetes/graduated/iperf3/values.yaml → active/device_truenas/truenas.md
Executable file → Normal file
0
kubernetes/graduated/iperf3/values.yaml → active/device_truenas/truenas.md
Executable file → Normal file
99
active/device_yubikey/yubikey.md
Normal file
99
active/device_yubikey/yubikey.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Yubikey
|
||||
|
||||
- [Yubikey](#yubikey)
|
||||
- [Configuration](#configuration)
|
||||
- [Software](#software)
|
||||
- [GPG](#gpg)
|
||||
- [Saving GPG key to card](#saving-gpg-key-to-card)
|
||||
- [Using the GPG key on a Yubikey](#using-the-gpg-key-on-a-yubikey)
|
||||
- [Factory Reset](#factory-reset)
|
||||
|
||||
## Configuration
|
||||
|
||||
1. You will likely need the [udev
|
||||
rules](https://support.yubico.com/hc/en-us/articles/360013708900-Using-Your-YubiKey-with-Linux)
|
||||
to use the AppImage configuration tool on linux even if your udev version is above 244.
|
||||
|
||||
## Software
|
||||
|
||||
The [Yubikey Manager](https://www.yubico.com/support/download/yubikey-manager/) is deprecated.
|
||||
|
||||
Use the [Yubikey Authenticator](https://www.yubico.com/products/yubico-authenticator/) for GUI.
|
||||
|
||||
## GPG
|
||||
|
||||
### Saving GPG key to card
|
||||
|
||||
<https://support.yubico.com/hc/en-us/articles/360013790259-Using-Your-YubiKey-with-OpenPGP>
|
||||
|
||||
On Fedora you'll need to add the following polkit rules to access your smart card.
|
||||
|
||||
```bash
|
||||
export MY_USER=ducoterra
|
||||
echo <<EOF > /etc/polkit-1/rules.d/10-pcsc-custom.rules
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "org.debian.pcsc-lite.access_pcsc" &&
|
||||
subject.user == "${MY_USER}") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "org.debian.pcsc-lite.access_card" &&
|
||||
action.lookup("reader") == 'Yubico YubiKey OTP+FIDO+CCID 00 00' &&
|
||||
subject.user == "${MY_USER}") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
EOF
|
||||
```
|
||||
|
||||
Now you can add your key to your card.
|
||||
|
||||
```bash
|
||||
gpg --edit-key 1234ABC
|
||||
|
||||
# Save both the signature and authentication keys
|
||||
> keytocard
|
||||
|
||||
# Do not save or your key will be deleted locally
|
||||
> quit
|
||||
```
|
||||
|
||||
Check the keys on the yubikey with
|
||||
|
||||
```bash
|
||||
gpg --card-status
|
||||
```
|
||||
|
||||
Once your keys have been loaded, change the pin.
|
||||
|
||||
```bash
|
||||
gpg --change-pin
|
||||
```
|
||||
|
||||
### Using the GPG key on a Yubikey
|
||||
|
||||
<https://github.com/drduh/YubiKey-Guide?tab=readme-ov-file#notes>
|
||||
|
||||
```bash
|
||||
export GPG_EMAIL='myemail@example.com'
|
||||
|
||||
# Import the public key. Without this the key won't show up.
|
||||
gpg --auto-key-locate hkps://keys.openpgp.org --locate-keys ${GPG_EMAIL}
|
||||
|
||||
# Trust the key
|
||||
gpg --quick-set-ownertrust ${GPG_EMAIL} full
|
||||
|
||||
# Yubikey should now show up
|
||||
gpg --list-secret-keys
|
||||
```
|
||||
|
||||
### Factory Reset
|
||||
|
||||
```bash
|
||||
gpg --edit-card
|
||||
|
||||
> admin
|
||||
> factory-reset
|
||||
```
|
||||
13
active/kubernetes/kubernetes.md
Normal file
13
active/kubernetes/kubernetes.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Kubernetes
|
||||
|
||||
## CLI Tools
|
||||
|
||||
kubectl: <https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/>
|
||||
|
||||
helm: <https://helm.sh/docs/intro/install/>
|
||||
|
||||
## Install a Kubernetes Server
|
||||
|
||||
For k3s, see [k3s](/active/systemd_k3s/k3s.md)
|
||||
|
||||
For k0s, see [k0s](/active/systemd_k0s/k0s.md)
|
||||
@@ -12,7 +12,7 @@ helm repo add bitwarden https://charts.bitwarden.com/
|
||||
helm repo update
|
||||
|
||||
kubectl create namespace bitwarden
|
||||
helm show values bitwarden/self-host > kubernetes/incubating/bitwarden/values.yaml
|
||||
helm show values bitwarden/self-host > active/kubernetes_bitwarden/values.yaml
|
||||
|
||||
# Installation ID: https://bitwarden.com/host/
|
||||
# Optional argument for Have I Been Pwned: --from-literal=globalSettings__hibpApiKey="REPLACE" \
|
||||
@@ -25,5 +25,5 @@ kubectl create secret generic custom-secret -n bitwarden \
|
||||
--from-file=globalSettings__yubico__key=./secrets/bitwarden/yubico_secret \
|
||||
--from-file=SA_PASSWORD=./secrets/bitwarden/sa_password
|
||||
|
||||
helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values kubernetes/incubating/bitwarden/values.yaml
|
||||
helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values active/kubernetes_bitwarden/values.yaml
|
||||
```
|
||||
15
active/kubernetes_external-dns/values.yaml
Normal file
15
active/kubernetes_external-dns/values.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
provider:
|
||||
name: aws
|
||||
env:
|
||||
- name: AWS_SHARED_CREDENTIALS_FILE
|
||||
value: /etc/aws/credentials/externaldns-credentials
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: us-east-1 # change to region where EKS is installed
|
||||
extraVolumes:
|
||||
- name: aws-credentials
|
||||
secret:
|
||||
secretName: external-dns # In this example, the secret will have the data stored in a key named `my_credentials`
|
||||
extraVolumeMounts:
|
||||
- name: aws-credentials
|
||||
mountPath: /etc/aws/credentials
|
||||
readOnly: true
|
||||
11
active/kubernetes_gitea/gitea-demo-values.yaml
Normal file
11
active/kubernetes_gitea/gitea-demo-values.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
service:
|
||||
http:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Cluster
|
||||
annotations:
|
||||
metallb.io/allow-shared-ip: gitea
|
||||
ssh:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Cluster
|
||||
annotations:
|
||||
metallb.io/allow-shared-ip: gitea
|
||||
@@ -21,14 +21,10 @@ ingress:
|
||||
persistence:
|
||||
enabled: true
|
||||
create: true
|
||||
storageClass: zfs-iscsi-enc0
|
||||
claimName: data-gitea-staging-0
|
||||
annotations:
|
||||
"helm.sh/resource-policy": keep
|
||||
|
||||
global:
|
||||
storageClass: zfs-iscsi-enc1
|
||||
|
||||
postgresql:
|
||||
enabled: true
|
||||
image:
|
||||
@@ -36,7 +32,6 @@ postgresql:
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: zfs-iscsi-enc1
|
||||
annotations:
|
||||
"helm.sh/resource-policy": keep
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# Gitea
|
||||
|
||||
- [Gitea](#gitea)
|
||||
- [Demo](#demo)
|
||||
- [Staging](#staging)
|
||||
- [Install](#install)
|
||||
- [Backup and Restore](#backup-and-restore)
|
||||
- [Gitea Runners](#gitea-runners)
|
||||
- [Install](#install-1)
|
||||
- [Cache Cleanup](#cache-cleanup)
|
||||
|
||||
Gitea provides a helm chart [here](https://gitea.com/gitea/helm-chart/). We're not
|
||||
going to modify much, but we are going to solidify some of the default values in case
|
||||
@@ -12,6 +15,17 @@ they decide to change things. This is the first chart (besides ingress-nginx) wh
|
||||
we need to pay attention to the MetalLB annotation. This has been set in the values.yaml
|
||||
file.
|
||||
|
||||
## Demo
|
||||
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
gitea \
|
||||
gitea-charts/gitea \
|
||||
--values active/kubernetes_gitea/gitea-demo-values.yaml \
|
||||
--namespace gitea \
|
||||
--create-namespace
|
||||
```
|
||||
|
||||
## Staging
|
||||
|
||||
There is a `gitea-staging.yaml` file with staging values. This should be installed in
|
||||
@@ -38,7 +52,7 @@ helm repo update
|
||||
helm upgrade --install \
|
||||
gitea \
|
||||
gitea-charts/gitea \
|
||||
--values kubernetes/graduated/gitea/gitea-values.yaml \
|
||||
--values active/kubernetes_gitea/gitea-values.yaml \
|
||||
--namespace gitea \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -78,6 +92,8 @@ kubectl scale statefulset gitea --replicas 1
|
||||
|
||||
<https://docs.gitea.com/next/usage/actions/act-runner/#install-with-the-docker-image>
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
touch config.yaml
|
||||
|
||||
@@ -95,3 +111,30 @@ docker run \
|
||||
--name kube_runner \
|
||||
-d gitea/act_runner:latest
|
||||
```
|
||||
|
||||
### Cache Cleanup
|
||||
|
||||
Each org or project with a package registry will have its own cleanup rules. For example,
|
||||
services -> settings -> Packages -> Add Cleanup Rule will allow you to create a cleanup
|
||||
rule for packages stored under the "services" org. These cleanup rules should run automatically.
|
||||
|
||||
On the other hand, the docker builder cache will balloon out of control over time. The gitea
|
||||
docker runner is handled outside of Gitea's context, so you'll need to clean it up yourself.
|
||||
|
||||
```bash
|
||||
# Check used system resources
|
||||
docker system df
|
||||
```
|
||||
|
||||
You should run something like this on a schedule:
|
||||
|
||||
```bash
|
||||
# Prune the builder cache
|
||||
docker builder prune -a
|
||||
```
|
||||
|
||||
To run it every day at midnight: `crontab -e`
|
||||
|
||||
```bash
|
||||
0 0 * * * yes | docker builder prune -a
|
||||
```
|
||||
@@ -8,7 +8,7 @@ helm repo update
|
||||
helm upgrade --install my-grafana grafana/grafana \
|
||||
--namespace monitoring \
|
||||
--create-namespace \
|
||||
--values kubernetes/incubating/grafana/values.yaml
|
||||
--values active/kubernetes_grafana/values.yaml
|
||||
|
||||
kubectl get secret --namespace monitoring my-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
|
||||
```
|
||||
@@ -25,5 +25,5 @@ helm upgrade --install \
|
||||
kube-prometheus-stack \
|
||||
prometheus-community/kube-prometheus-stack \
|
||||
--namespace kube-system \
|
||||
--values kubernetes/incubating/grafana/helm-prom-stack-values.yaml
|
||||
--values active/kubernetes_grafana/helm-prom-stack-values.yaml
|
||||
```
|
||||
@@ -7,7 +7,7 @@ This creates a basic iperf3 server.
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
iperf3 \
|
||||
./kubernetes/graduated/iperf3 \
|
||||
./active/kubernetes_iperf3/iperf3 \
|
||||
--namespace iperf3 \
|
||||
--create-namespace
|
||||
```
|
||||
```
|
||||
0
systemd/graduated/ddns/vars.yaml → active/kubernetes_iperf3/values.yaml
Normal file → Executable file
0
systemd/graduated/ddns/vars.yaml → active/kubernetes_iperf3/values.yaml
Normal file → Executable file
@@ -5,7 +5,7 @@
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
jellyfin \
|
||||
./kubernetes/graduated/jellyfin \
|
||||
./active/kubernetes_jellyfin \
|
||||
--namespace jellyfin \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -59,16 +59,16 @@ spec:
|
||||
claimName: {{ .Release.Name }}-cache
|
||||
- name: movies
|
||||
nfs:
|
||||
server: driveripper.reeselink.com
|
||||
path: /mnt/enc0/media/Movies
|
||||
server: driveripper-lab.reeselink.com
|
||||
path: /mnt/enc0/smb/media/Movies
|
||||
readOnly: true
|
||||
- name: shows
|
||||
nfs:
|
||||
server: driveripper.reeselink.com
|
||||
path: /mnt/enc0/media/Shows
|
||||
server: driveripper-lab.reeselink.com
|
||||
path: /mnt/enc0/smb/media/Shows
|
||||
readOnly: true
|
||||
- name: videos
|
||||
nfs:
|
||||
server: driveripper.reeselink.com
|
||||
path: /mnt/enc0/media/Videos
|
||||
server: driveripper-lab.reeselink.com
|
||||
path: /mnt/enc0/smb/media/Videos
|
||||
readOnly: true
|
||||
@@ -0,0 +1,10 @@
|
||||
- op: replace # action
|
||||
path: /data/config.json # resource we want to change
|
||||
value: |-
|
||||
{
|
||||
"storageClassConfigs": {
|
||||
"local-path": {
|
||||
"sharedFileSystemPath": "/opt/local-path-provisioner"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# Creates local path storage and a storage class
|
||||
# Patched by ConfigMap-patch.yaml and namespace-patch.yaml
|
||||
resources:
|
||||
- local-path-storage.yaml
|
||||
- ssd-storage.yaml
|
||||
patches:
|
||||
- target:
|
||||
group: storage.k8s.io
|
||||
version: v1
|
||||
kind: StorageClass
|
||||
name: local-path
|
||||
path: StorageClass-hdd-patch.yaml
|
||||
- target:
|
||||
group: ""
|
||||
version: v1
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
# Local Path Provisioner Install
|
||||
|
||||
1. `mkdir /var/lib/rancher/k3s/storage`
|
||||
2. Edit fstab to mount your drive to `/var/lib/rancher/k3s/storage`
|
||||
3. `systemctl daemon-reload`
|
||||
4. `mount -a`
|
||||
|
||||
<https://github.com/rancher/local-path-provisioner/tree/master/deploy/chart/local-path-provisioner>
|
||||
|
||||
```bash
|
||||
# Download the updated template from github
|
||||
kubectl kustomize "github.com/rancher/local-path-provisioner/deploy?ref=v0.0.32" > active/kubernetes_local-path-provisioner/local-path-storage.yaml
|
||||
|
||||
# Apply customizations (ssd/hdd storage, read write many support)
|
||||
kubectl kustomize active/kubernetes_local-path-provisioner | kubectl apply -f -
|
||||
```
|
||||
|
||||
Mark the class as default
|
||||
|
||||
```bash
|
||||
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
|
||||
```
|
||||
@@ -176,7 +176,7 @@ spec:
|
||||
fieldPath: metadata.namespace
|
||||
- name: CONFIG_MOUNT_PATH
|
||||
value: /etc/config/
|
||||
image: rancher/local-path-provisioner:v0.0.28
|
||||
image: rancher/local-path-provisioner:v0.0.32
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: local-path-provisioner
|
||||
volumeMounts:
|
||||
19
active/kubernetes_metallb/addresspool.yaml
Normal file
19
active/kubernetes_metallb/addresspool.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: IPAddressPool
|
||||
metadata:
|
||||
name: unifi-pool
|
||||
namespace: kube-system
|
||||
spec:
|
||||
addresses:
|
||||
- 2603:6013:3140:105:10:5:0:10-2603:6013:3140:105:10:5:0:210
|
||||
- 10.5.0.10-10.5.0.210
|
||||
---
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: L2Advertisement
|
||||
metadata:
|
||||
name: l2advertisement
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ipAddressPools:
|
||||
- unifi-pool
|
||||
@@ -5,12 +5,21 @@ below installs nimcraft. For each installation you'll want to create your own va
|
||||
with a new port. The server-downloader is called "minecraft_get_server" and is available on
|
||||
[Github](https://github.com/ducoterra/minecraft_get_server).
|
||||
|
||||
After installing, you can run admin commands (like whitelisting players) by
|
||||
attaching to the container:
|
||||
|
||||
```bash
|
||||
kubectl attach -it <pod>
|
||||
|
||||
> /whitelist add ducoterra
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
testcraft \
|
||||
./kubernetes/graduated/minecraft \
|
||||
./active/kubernetes_minecraft \
|
||||
--namespace minecraft \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -20,7 +29,7 @@ helm upgrade --install \
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
nimcraft \
|
||||
./kubernetes/graduated/minecraft \
|
||||
./active/kubernetes_minecraft \
|
||||
--namespace minecraft \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -30,7 +39,7 @@ helm upgrade --install \
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
courtniecraft \
|
||||
./kubernetes/graduated/minecraft \
|
||||
./active/kubernetes_minecraft \
|
||||
--namespace minecraft \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -40,7 +49,7 @@ helm upgrade --install \
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
camcraft1 \
|
||||
./kubernetes/graduated/minecraft \
|
||||
./active/kubernetes_minecraft \
|
||||
--namespace minecraft \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -56,10 +56,10 @@ spec:
|
||||
value: "1"
|
||||
resources:
|
||||
requests:
|
||||
memory: {{ div .Values.max_ram 2 }}Gi
|
||||
memory: "{{ div .Values.max_ram 2 }}Gi"
|
||||
cpu: 1m
|
||||
limits:
|
||||
memory: {{ add 1 .Values.max_ram }}Gi
|
||||
memory: "{{ add 1 .Values.max_ram }}Gi"
|
||||
cpu: {{ .Values.max_cpu | quote }}
|
||||
volumes:
|
||||
- name: data
|
||||
@@ -5,7 +5,6 @@ metadata:
|
||||
annotations:
|
||||
"helm.sh/resource-policy": keep
|
||||
spec:
|
||||
storageClassName: ssd
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
@@ -2,11 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
annotations:
|
||||
metallb.universe.tf/address-pool: "external"
|
||||
external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.reeseapps.com
|
||||
spec:
|
||||
ipFamilies: ["IPv6"]
|
||||
externalTrafficPolicy: Cluster
|
||||
selector:
|
||||
app: {{ .Release.Name }}
|
||||
@@ -7,7 +7,7 @@ Snapdrop is a file sharing app that allows airdrop-like functionality over the w
|
||||
```bash
|
||||
helm upgrade --install \
|
||||
snapdrop \
|
||||
./kubernetes/graduated/snapdrop \
|
||||
./active/kubernetes_snapdrop \
|
||||
--namespace snapdrop \
|
||||
--create-namespace
|
||||
```
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
- name: snapdrop
|
||||
image: {{ .Values.snapdrop.image }}
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
@@ -1,3 +1,3 @@
|
||||
snapdrop:
|
||||
image: linuxserver/snapdrop:latest
|
||||
image: linuxserver/pairdrop:latest
|
||||
domain: snapdrop.reeseapps.com
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user