Compare commits
110 Commits
9417e711a9
...
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 |
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/
|
venv/
|
||||||
tmp/
|
tmp/
|
||||||
Unsorted/
|
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",
|
"label": "Build arch-toolbox",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "./infrastructure/graduated/distoolbox/arch-build.sh",
|
"command": "./active/software_distoolbox/arch-build.sh",
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
|
|||||||
23
.vscode/vscode.md
vendored
23
.vscode/vscode.md
vendored
@@ -7,7 +7,8 @@
|
|||||||
- [Fonts](#fonts)
|
- [Fonts](#fonts)
|
||||||
- [Navigation](#navigation)
|
- [Navigation](#navigation)
|
||||||
- [Extensions](#extensions)
|
- [Extensions](#extensions)
|
||||||
- [Continue](#continue)
|
- [Continue](#continue)
|
||||||
|
- [Pylance Type Checking](#pylance-type-checking)
|
||||||
|
|
||||||
## Debugpy Snippet
|
## Debugpy Snippet
|
||||||
|
|
||||||
@@ -117,18 +118,10 @@ To install that list of extensions run:
|
|||||||
cat vscode_extensions.txt | xargs -L 1 code --install-extension
|
cat vscode_extensions.txt | xargs -L 1 code --install-extension
|
||||||
```
|
```
|
||||||
|
|
||||||
## Continue
|
### Continue
|
||||||
|
|
||||||
```json
|
Continue -> Settings -> Help -> Quickstart
|
||||||
{
|
|
||||||
"models": [
|
### Pylance Type Checking
|
||||||
{
|
|
||||||
"title": "qwen2.5-coder:32b",
|
Settings -> `python.analysis.typeChecking`
|
||||||
"provider": "ollama",
|
|
||||||
"apiBase": "https://ollama.example.com",
|
|
||||||
"apiKey": "...",
|
|
||||||
"model": "qwen2.5-coder:32b"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
...
|
|
||||||
```
|
|
||||||
308
README.md
308
README.md
@@ -1,81 +1,272 @@
|
|||||||
# Homelab
|
# 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
|
## Table of Contents
|
||||||
|
|
||||||
- [Homelab](#homelab)
|
- [Homelab](#homelab)
|
||||||
- [Table of Contents](#table-of-contents)
|
- [Table of Contents](#table-of-contents)
|
||||||
- [Fun Facts](#fun-facts)
|
- [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)
|
- [Project Lifecycle](#project-lifecycle)
|
||||||
- [Supported Projects](#supported-projects)
|
- [Project Types](#project-types)
|
||||||
- [Graduation Requirements](#graduation-requirements)
|
- [Active Project Requirements](#active-project-requirements)
|
||||||
- [Retirement Requirements](#retirement-requirements)
|
- [Retirement Requirements](#retirement-requirements)
|
||||||
|
- [Project Structure](#project-structure)
|
||||||
|
- [Creating a Project](#creating-a-project)
|
||||||
- [Order of Operations](#order-of-operations)
|
- [Order of Operations](#order-of-operations)
|
||||||
|
|
||||||
## Fun Facts
|
## Fun Facts
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
|
||||||
On linux, <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>, then, while holding
|
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
|
<kbd>ctrl</kbd>+<kbd>shift</kbd>, typing <kbd>b</kbd>+<kbd>0</kbd> will type a
|
||||||
can enter any unicode symbol this way.
|
° (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
|
## Project Lifecycle
|
||||||
|
|
||||||
Projects will fall into one of the three following categories:
|
Projects will either be `active` or `retired`.
|
||||||
|
|
||||||
1. Incubating
|
Active projects are being actively developed. They are in-use, stable, and
|
||||||
2. Graduated
|
production ready. Active projects should meet and track the [active project
|
||||||
3. Retired
|
requirements](#active-project-requirements)
|
||||||
|
|
||||||
Incubating projects are experimental or prototypal. They're being actively developed and aren't
|
Retired projects are no longer in use or recommended. They are kept for
|
||||||
ready for production deployment. These projects may appear and disappear without warning and are not
|
reference. Retired projects must meet the [retirement
|
||||||
stable. There is no minimum requirement for a project to be in incubation.
|
requirements](#retirement-requirements)
|
||||||
|
|
||||||
Graduated projects are in-use, stable, and production ready. They met the [graduation
|
You'll notice that most of the active projects have scripts or examples that
|
||||||
requirements](#graduation-requirements) and are actively maintained.
|
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
|
## Project Types
|
||||||
must meet the [retirement requirements](#retirement-requirements)
|
|
||||||
|
|
||||||
## 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
|
Note, some projects will be named with just the prefix. These are projects for
|
||||||
- infrastructure
|
configuring the underlying technology. The `podman` project, for example, will
|
||||||
- cloud
|
tell you how to configure and install podman so it works correctly.
|
||||||
- systemd
|
|
||||||
- podman
|
|
||||||
- docker
|
|
||||||
- kubernetes
|
|
||||||
|
|
||||||
Hardware will contain projects that relate to specific machines or equipment. 3D printers, Raspberry
|
`device_` will prefix projects that relate to specific machines or equipment.
|
||||||
Pis, and other IOT devices qualify as specialized hardware that needs documentation and
|
3D printers, Raspberry Pis, and other IOT devices qualify as specialized
|
||||||
configuration. This is not limited to computer equipment. The furnace is an important part of the
|
hardware that needs documentation and configuration. This is not limited to
|
||||||
home lab. the Air Conditioner is integral to the homelab's function. These projects will also be documented.
|
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
|
`os_` will contain projects that set up operating systems. These include best
|
||||||
types. For example, infrastructure will contain "how to set up a linux box with docker" or "how to
|
practices, backups, updates, default software, etc.
|
||||||
set up a k3s cluster for kubernetes".
|
|
||||||
|
|
||||||
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
|
`software_` projects record configuration for common software agnostic to
|
||||||
other linux hardware.
|
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.
|
## Active Project Requirements
|
||||||
|
|
||||||
## Graduation Requirements
|
|
||||||
|
|
||||||
- [ ] Installation is documented
|
- [ ] Installation is documented
|
||||||
- [ ] Installation configuration examples are provided
|
- [ ] 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, a replacement has been identified and documented
|
||||||
- [ ] If applicable, backup data locations are 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
|
## Order of Operations
|
||||||
|
|
||||||
1. Install cloud projects. These usually have no dependencies and typically provide critical services
|
1. Configure cloud providers. These usually have no dependencies and typically
|
||||||
to other projects (DNS, email notifications, etc.)
|
provide critical services to other projects (DNS, email notifications, etc.)
|
||||||
2. Install infrastructure projects. Usually these only have dependencies on cloud services.
|
2. Install infrastructure projects. Usually these only have dependencies on
|
||||||
3. Install systemd services. These are usually low-level programs that require a dedicated machine
|
cloud services.
|
||||||
and perform semi-critical functions (ipv4 proxy, ddns, etc.).
|
3. Install systemd, kubernetes, docker, podman, and other services.
|
||||||
4. Install kubernetes, docker, podman, and other services.
|
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
# AWS Credentials
|
# AWS Credentials
|
||||||
|
|
||||||
Note: this requires the AWS CLI. See [AWS CLI](/cloud/graduated/aws_cli/aws_cli.md)
|
Note: this requires the AWS CLI. See [AWS CLI](/active/aws_cli/aws_cli.md)
|
||||||
|
|
||||||
## Credential Generation
|
- [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
|
```bash
|
||||||
export AWS_USERNAME=
|
export AWS_USERNAME=
|
||||||
aws iam create-user --user-name $AWS_USERNAME
|
aws iam create-user --user-name $AWS_USERNAME
|
||||||
|
|
||||||
# Allow updating reeseapps
|
# 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
|
# 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)
|
||||||
|
|
||||||
# Allow sending emails
|
|
||||||
aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn $(cat cloud/graduated/aws_iam/secrets/ses-send-email-policy-arn)
|
|
||||||
|
|
||||||
# Create credentials (run aws configure on the machine that needs these to input them manually)
|
# 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 iam create-access-key --user-name $AWS_USERNAME
|
||||||
@@ -25,7 +27,7 @@ aws iam create-access-key --user-name $AWS_USERNAME
|
|||||||
|
|
||||||
Example Policy:
|
Example Policy:
|
||||||
|
|
||||||
cloud/graduated/aws_iam/secrets/policies/route53_reeselink.json
|
active/aws_iam/secrets/route53_reeselink.json
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -57,11 +59,21 @@ cloud/graduated/aws_iam/secrets/policies/route53_reeselink.json
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Allow updating route53 records for reeselink.com
|
# 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
|
# 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
|
||||||
|
|
||||||
# Allow sending emails
|
|
||||||
aws iam create-policy --policy-name send-email --policy-document file://cloud/graduated/aws_iam/secrets/ses_allow_send_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
|
## 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
|
```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
|
## Reeseapps Addresses
|
||||||
|
|
||||||
```bash
|
```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>
|
<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
|
### Connecting via LAN mode
|
||||||
|
|
||||||
- Make sure you open 1990 and 2021 (tcp + udp) in your firewall application.
|
- 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.
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
|
helm repo update
|
||||||
|
|
||||||
kubectl create namespace bitwarden
|
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/
|
# Installation ID: https://bitwarden.com/host/
|
||||||
# Optional argument for Have I Been Pwned: --from-literal=globalSettings__hibpApiKey="REPLACE" \
|
# 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=globalSettings__yubico__key=./secrets/bitwarden/yubico_secret \
|
||||||
--from-file=SA_PASSWORD=./secrets/bitwarden/sa_password
|
--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
|
||||||
```
|
```
|
||||||
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:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
create: true
|
create: true
|
||||||
storageClass: zfs-iscsi-enc0
|
|
||||||
claimName: data-gitea-staging-0
|
claimName: data-gitea-staging-0
|
||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
|
|
||||||
global:
|
|
||||||
storageClass: zfs-iscsi-enc1
|
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
enabled: true
|
enabled: true
|
||||||
image:
|
image:
|
||||||
@@ -36,7 +32,6 @@ postgresql:
|
|||||||
primary:
|
primary:
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
storageClass: zfs-iscsi-enc1
|
|
||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# Gitea
|
# Gitea
|
||||||
|
|
||||||
- [Gitea](#gitea)
|
- [Gitea](#gitea)
|
||||||
|
- [Demo](#demo)
|
||||||
- [Staging](#staging)
|
- [Staging](#staging)
|
||||||
- [Install](#install)
|
- [Install](#install)
|
||||||
- [Backup and Restore](#backup-and-restore)
|
- [Backup and Restore](#backup-and-restore)
|
||||||
@@ -14,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
|
we need to pay attention to the MetalLB annotation. This has been set in the values.yaml
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade --install \
|
||||||
|
gitea \
|
||||||
|
gitea-charts/gitea \
|
||||||
|
--values active/kubernetes_gitea/gitea-demo-values.yaml \
|
||||||
|
--namespace gitea \
|
||||||
|
--create-namespace
|
||||||
|
```
|
||||||
|
|
||||||
## Staging
|
## Staging
|
||||||
|
|
||||||
There is a `gitea-staging.yaml` file with staging values. This should be installed in
|
There is a `gitea-staging.yaml` file with staging values. This should be installed in
|
||||||
@@ -40,7 +52,7 @@ helm repo update
|
|||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
gitea \
|
gitea \
|
||||||
gitea-charts/gitea \
|
gitea-charts/gitea \
|
||||||
--values kubernetes/graduated/gitea/gitea-values.yaml \
|
--values active/kubernetes_gitea/gitea-values.yaml \
|
||||||
--namespace gitea \
|
--namespace gitea \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -8,7 +8,7 @@ helm repo update
|
|||||||
helm upgrade --install my-grafana grafana/grafana \
|
helm upgrade --install my-grafana grafana/grafana \
|
||||||
--namespace monitoring \
|
--namespace monitoring \
|
||||||
--create-namespace \
|
--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
|
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 \
|
kube-prometheus-stack \
|
||||||
prometheus-community/kube-prometheus-stack \
|
prometheus-community/kube-prometheus-stack \
|
||||||
--namespace kube-system \
|
--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
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
iperf3 \
|
iperf3 \
|
||||||
./kubernetes/graduated/iperf3 \
|
./active/kubernetes_iperf3/iperf3 \
|
||||||
--namespace iperf3 \
|
--namespace iperf3 \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
0
yq → active/kubernetes_iperf3/values.yaml
Normal file → Executable file
0
yq → active/kubernetes_iperf3/values.yaml
Normal file → Executable file
@@ -5,7 +5,7 @@
|
|||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
jellyfin \
|
jellyfin \
|
||||||
./kubernetes/graduated/jellyfin \
|
./active/kubernetes_jellyfin \
|
||||||
--namespace jellyfin \
|
--namespace jellyfin \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -10,15 +10,14 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download the updated template from github
|
# Download the updated template from github
|
||||||
kubectl kustomize "github.com/rancher/local-path-provisioner/deploy?ref=v0.0.31" > kubernetes/graduated/local-path-provisioner/local-path-storage.yaml
|
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)
|
# Apply customizations (ssd/hdd storage, read write many support)
|
||||||
kubectl kustomize kubernetes/graduated/local-path-provisioner | kubectl apply -f -
|
kubectl kustomize active/kubernetes_local-path-provisioner | kubectl apply -f -
|
||||||
|
```
|
||||||
# Create test pod
|
|
||||||
kubectl apply -f systemd/graduated/k3s/tests/local-storage-test.yaml
|
Mark the class as default
|
||||||
kubectl get pod -n default
|
|
||||||
# Exec in and test - storage will be mounted at /storage
|
```bash
|
||||||
kubectl exec -it -n default <local-storage-test> -- bash
|
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
|
||||||
kubectl delete -f systemd/graduated/k3s/tests/local-storage-test.yaml
|
|
||||||
```
|
```
|
||||||
@@ -176,7 +176,7 @@ spec:
|
|||||||
fieldPath: metadata.namespace
|
fieldPath: metadata.namespace
|
||||||
- name: CONFIG_MOUNT_PATH
|
- name: CONFIG_MOUNT_PATH
|
||||||
value: /etc/config/
|
value: /etc/config/
|
||||||
image: rancher/local-path-provisioner:v0.0.31
|
image: rancher/local-path-provisioner:v0.0.32
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
name: local-path-provisioner
|
name: local-path-provisioner
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -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
|
with a new port. The server-downloader is called "minecraft_get_server" and is available on
|
||||||
[Github](https://github.com/ducoterra/minecraft_get_server).
|
[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
|
## Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
testcraft \
|
testcraft \
|
||||||
./kubernetes/graduated/minecraft \
|
./active/kubernetes_minecraft \
|
||||||
--namespace minecraft \
|
--namespace minecraft \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -20,7 +29,7 @@ helm upgrade --install \
|
|||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
nimcraft \
|
nimcraft \
|
||||||
./kubernetes/graduated/minecraft \
|
./active/kubernetes_minecraft \
|
||||||
--namespace minecraft \
|
--namespace minecraft \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -30,7 +39,7 @@ helm upgrade --install \
|
|||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
courtniecraft \
|
courtniecraft \
|
||||||
./kubernetes/graduated/minecraft \
|
./active/kubernetes_minecraft \
|
||||||
--namespace minecraft \
|
--namespace minecraft \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -40,7 +49,7 @@ helm upgrade --install \
|
|||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
camcraft1 \
|
camcraft1 \
|
||||||
./kubernetes/graduated/minecraft \
|
./active/kubernetes_minecraft \
|
||||||
--namespace minecraft \
|
--namespace minecraft \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
@@ -56,10 +56,10 @@ spec:
|
|||||||
value: "1"
|
value: "1"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: {{ div .Values.max_ram 2 }}Gi
|
memory: "{{ div .Values.max_ram 2 }}Gi"
|
||||||
cpu: 1m
|
cpu: 1m
|
||||||
limits:
|
limits:
|
||||||
memory: {{ add 1 .Values.max_ram }}Gi
|
memory: "{{ add 1 .Values.max_ram }}Gi"
|
||||||
cpu: {{ .Values.max_cpu | quote }}
|
cpu: {{ .Values.max_cpu | quote }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
@@ -5,7 +5,6 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
spec:
|
spec:
|
||||||
storageClassName: ssd
|
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteOnce
|
- ReadWriteOnce
|
||||||
resources:
|
resources:
|
||||||
@@ -2,11 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}
|
name: {{ .Release.Name }}
|
||||||
annotations:
|
|
||||||
metallb.universe.tf/address-pool: "external"
|
|
||||||
external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.reeseapps.com
|
|
||||||
spec:
|
spec:
|
||||||
ipFamilies: ["IPv6"]
|
|
||||||
externalTrafficPolicy: Cluster
|
externalTrafficPolicy: Cluster
|
||||||
selector:
|
selector:
|
||||||
app: {{ .Release.Name }}
|
app: {{ .Release.Name }}
|
||||||
@@ -7,7 +7,7 @@ Snapdrop is a file sharing app that allows airdrop-like functionality over the w
|
|||||||
```bash
|
```bash
|
||||||
helm upgrade --install \
|
helm upgrade --install \
|
||||||
snapdrop \
|
snapdrop \
|
||||||
./kubernetes/graduated/snapdrop \
|
./active/kubernetes_snapdrop \
|
||||||
--namespace snapdrop \
|
--namespace snapdrop \
|
||||||
--create-namespace
|
--create-namespace
|
||||||
```
|
```
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user