Compare commits
No commits in common. "master" and "2020.05.24.1" have entirely different histories.
master
...
2020.05.24
311 changed files with 358 additions and 3267 deletions
|
|
@ -1,4 +0,0 @@
|
|||
.git
|
||||
**/*.hash
|
||||
**/src
|
||||
public/
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -1,5 +1,2 @@
|
|||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.xcf filter=lfs diff=lfs merge=lfs -text
|
||||
*.webp filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
|
|||
9
.gitmodules
vendored
9
.gitmodules
vendored
|
|
@ -1,6 +1,3 @@
|
|||
[submodule "themes/hugo-coder"]
|
||||
path = themes/hugo-coder
|
||||
url = git@github.com:W-Floyd/hugo-coder-iconify.git
|
||||
[submodule "assets/MaterialDesign-SVG"]
|
||||
path = assets/MaterialDesign-SVG
|
||||
url = git@github.com:Templarian/MaterialDesign-SVG.git
|
||||
[submodule "themes/KeepIt"]
|
||||
path = themes/KeepIt
|
||||
url = https://github.com/Fastbyte01/KeepIt.git
|
||||
|
|
|
|||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"Gluster"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
FROM hugomods/hugo:exts as hugo
|
||||
COPY . /src
|
||||
RUN hugo --minify
|
||||
|
||||
FROM nginx:alpine-slim
|
||||
COPY --from=hugo /src/public /usr/share/nginx/html
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
145
config.toml
145
config.toml
|
|
@ -2,89 +2,84 @@ baseURL = "//notmy.space/"
|
|||
languageCode = "en"
|
||||
defaultContentLanguage = "en"
|
||||
title = "William Floyd"
|
||||
theme = "github.com/W-Floyd/hugo-coder-iconify"
|
||||
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = 'content'
|
||||
target = 'content'
|
||||
excludeFiles = "**/media/src"
|
||||
[[module.imports]]
|
||||
path = 'github.com/hugomods/icons/vendors/mdi'
|
||||
|
||||
paginate = 20
|
||||
|
||||
pygmentsStyle = "bw"
|
||||
pygmentsCodeFences = true
|
||||
pygmentsCodeFencesGuessSyntax = true
|
||||
theme = "KeepIt"
|
||||
|
||||
paginate = 12
|
||||
enableEmoji = true
|
||||
googleAnalytics = "UA-63647911-2"
|
||||
enableRobotsTXT = true
|
||||
canonifyURLs = true
|
||||
|
||||
[permalinks]
|
||||
posts = "/:year/:month/:filename/"
|
||||
[sitemap]
|
||||
changefreq = "daily"
|
||||
filename = "sitemap.xml"
|
||||
priority = 0.5
|
||||
|
||||
[blackfriday]
|
||||
hrefTargetBlank = true
|
||||
nofollowLinks = true
|
||||
noreferrerLinks = true
|
||||
|
||||
[Permalinks]
|
||||
posts = "/:year/:filename/"
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
name = "Blog"
|
||||
url = "/posts/"
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
name = "Projects"
|
||||
url = "/projects/"
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
name = "Categories"
|
||||
url = "/categories/"
|
||||
weight = 3
|
||||
|
||||
[[menu.main]]
|
||||
name = "About"
|
||||
url = "/about"
|
||||
weight = 4
|
||||
|
||||
[params]
|
||||
author = "William Floyd"
|
||||
description = "William Floyd's personal website"
|
||||
gravatar = "william.png2000@gmail.com"
|
||||
dateFormat = "January 2, 2006"
|
||||
subtitle = "This is not MySpace, it's my space"
|
||||
since = 2018
|
||||
colorScheme = "auto"
|
||||
author = "William Floyd"
|
||||
home_mode = ""
|
||||
|
||||
# If you want to implement a Content-Security-Policy, add this section
|
||||
scriptsrc = [
|
||||
"'self'",
|
||||
"'unsafe-inline'",
|
||||
"https://cdn.jsdelivr.net/"
|
||||
]
|
||||
prefetchsrc = ["'self'"]
|
||||
[params.gravatar]
|
||||
email = "william.png2000@gmail.com"
|
||||
|
||||
[taxonomies]
|
||||
category = "categories"
|
||||
series = "series"
|
||||
tag = "tags"
|
||||
author = "authors"
|
||||
[params.social]
|
||||
GitHub = "W-Floyd"
|
||||
Linkedin = "william-floyd-906674190"
|
||||
Email = "william.png2000@gmail.com"
|
||||
Steam = "W-Floyd"
|
||||
|
||||
[[params.social]]
|
||||
name = "Github"
|
||||
icon = "github"
|
||||
weight = 1
|
||||
url = "https://github.com/W-Floyd/"
|
||||
|
||||
[[params.social]]
|
||||
name = "LinkedIn"
|
||||
icon = "linkedin"
|
||||
weight = 2
|
||||
url = "https://www.linkedin.com/in/william-floyd/"
|
||||
[params.share]
|
||||
|
||||
[[params.social]]
|
||||
name = "Email"
|
||||
icon = "email"
|
||||
weight = 3
|
||||
url = "mailto:contact@notmy.space"
|
||||
|
||||
[[params.social]]
|
||||
name = "RSS"
|
||||
icon = "rss"
|
||||
weight = 4
|
||||
url = "https://notmy.space/posts/index.xml"
|
||||
rel = "alternate"
|
||||
type = "application/rss+xml"
|
||||
|
||||
[languages.en]
|
||||
languageName = ":uk:"
|
||||
|
||||
[[languages.en.menu.main]]
|
||||
name = "About"
|
||||
weight = 1
|
||||
url = "about/"
|
||||
|
||||
[[languages.en.menu.main]]
|
||||
name = "Resume"
|
||||
weight = 2
|
||||
url = "https://github.com/W-Floyd/misc-job/releases/download/release/William_Floyd.pdf"
|
||||
|
||||
[[languages.en.menu.main]]
|
||||
name = "Posts"
|
||||
weight = 3
|
||||
url = "posts/"
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = false
|
||||
[privacy.googleAnalytics]
|
||||
anonymizeIP = true
|
||||
disable = false
|
||||
respectDoNotTrack = true
|
||||
useSessionStorage = true
|
||||
[privacy.instagram]
|
||||
disable = false
|
||||
simple = true
|
||||
[privacy.twitter]
|
||||
disable = false
|
||||
enableDNT = true
|
||||
simple = true
|
||||
[privacy.vimeo]
|
||||
disable = false
|
||||
simple = true
|
||||
[privacy.youtube]
|
||||
disable = false
|
||||
privacyEnhanced = true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "About"
|
||||
date: "2023-12-15"
|
||||
date: "2018-06-19"
|
||||
---
|
||||
|
||||
I am William Floyd, this is my blog.
|
||||
|
||||
I currently work as an Associate Project Manager at Engenious Design.
|
||||
I am currently a Mechanical Engineering student at LeTourneau.
|
||||
|
|
|
|||
BIN
content/posts/clickbait/media/20190919_143530.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20190919_143530.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20190919_213947.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20190919_213947.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20190919_224711.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20190919_224711.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191003_000323.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191003_000323.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191004_160107.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191004_160107.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191010_152148.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191010_152148.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191025_194859.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191025_194859.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191102_144426.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191102_144426.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191110_014623.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191110_014623.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191110_072012.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191110_072012.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191115_090125.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191115_090125.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191118_032416.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191118_032416.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/20191120_224129.webp
(Stored with Git LFS)
BIN
content/posts/clickbait/media/20191120_224129.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/clickbait/media/src/20190919_143530.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20190919_143530.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
0fefa0a42b5ddc8f15e48e77cbadfa2fb8f25db06fc16e8e1afbc17f4e572505
|
||||
BIN
content/posts/clickbait/media/src/20190919_213947.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20190919_213947.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
524f410f6f8d7febe879205b516ff2c2de9c2b2613bb42da8a3103e196763f73
|
||||
BIN
content/posts/clickbait/media/src/20190919_224711.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20190919_224711.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
d0da1a5cca00a3d31b31769b9dc8f3c2ef5e69055d901fa8e6575ffa10d88f4a
|
||||
BIN
content/posts/clickbait/media/src/20191003_000323.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191003_000323.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
2ad738c50c7640a8bdcdcf5d5feeb8118bbd90343596bcdf50ce00c37e631544
|
||||
BIN
content/posts/clickbait/media/src/20191004_160107.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191004_160107.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
db26f30ae8b9406206394d6f08146d9a6c664da2f65ea30f7c0f493a2925c464
|
||||
BIN
content/posts/clickbait/media/src/20191010_152148.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191010_152148.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
55c2322f7bba7c7d29eb3e28ce2269662f4b2016daa9b240fb236e6821b692c0
|
||||
BIN
content/posts/clickbait/media/src/20191025_194859.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191025_194859.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
5051917955383bd82ec91d5b9082c3f1305782417ae6065e1cd1b80a220514f8
|
||||
BIN
content/posts/clickbait/media/src/20191102_144426.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191102_144426.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
3c91e3d30fb5b38e543b11f1a21982954078f8dd5d182805f5182a59c6469041
|
||||
BIN
content/posts/clickbait/media/src/20191110_014623.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191110_014623.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
d5e6e4baba1a9bc8858defe107721f069edf82e1c518326367a3d31f6ee8cedf
|
||||
BIN
content/posts/clickbait/media/src/20191110_072012.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191110_072012.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
6c9c220b1d695d76e5e3a93832fe14c49b3765ac93e4d971ebe9a981d33f3ce0
|
||||
BIN
content/posts/clickbait/media/src/20191115_090125.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191115_090125.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
24f6a28a746ea3d7213ee2ed2c40b22b756624331b24b486fd353b4abea576db
|
||||
BIN
content/posts/clickbait/media/src/20191118_032416.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191118_032416.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
e67916e0e2ea755a40fe7859b7e46fdfc1698c24d5da7a4898b7785dbe8d1754
|
||||
BIN
content/posts/clickbait/media/src/20191120_224129.jpg
(Stored with Git LFS)
BIN
content/posts/clickbait/media/src/20191120_224129.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
ef9c7801402a53ffc20bad84a5e5fb3587bbb2f2738d8d5df295d9e681070897
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
---
|
||||
title: "DIY 'Smart' Coffee Maker"
|
||||
date: "2020-07-05"
|
||||
author: "William Floyd"
|
||||
featured_image: "media/20200702_120123.webp"
|
||||
categories: [
|
||||
"Hardware",
|
||||
"Electronics",
|
||||
"Hacking"
|
||||
]
|
||||
tags: [
|
||||
"Hobby",
|
||||
"Home Assistant",
|
||||
"Home Automation",
|
||||
"ESPHome",
|
||||
"Coffee",
|
||||
"IOT"
|
||||
]
|
||||
---
|
||||
|
||||
I have recently (last couple months) been getting into home automation, specifically using Home Assistant.
|
||||
I also wanted to get a coffee maker that used K-Cups.
|
||||
Thus the goal was born - to get a coffee maker working with Home Assistant, such that I could automate it to fit my morning routine.
|
||||
|
||||
I first searched for new coffee makers, looking to see what the easiest ones would be to hack into and automate.
|
||||
I got so far as adding a sleek new brand name model to my Amazon cart, but thankfully reason and frugality prevailed!
|
||||
Instead, I opted to swing by the local Goodwill the next day, to see what secondhand options might be available.
|
||||
As luck would have it, I found a no-frills, single button coffee maker for the princely sum of $4, and I was on my merry way.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
Later that evening, opening the black box up (thankfully I had the correct triangular screwdriver head), I found very easily hackable innards.
|
||||
|
||||

|
||||
|
||||
The control board ([front](media/20200610_205847.webp), [back](media/20200610_205841.webp)) is very simple - two buttons in parallel, an LED, and a couple resistors.
|
||||
I'm not quite sure what the logic is upstream (it un/latches on each press), but the buttons short to ground, and this is something I can work with.
|
||||
The control board has wires for ground, signal, and 5V, in that order.
|
||||
|
||||
The ordeal of hacking into the thing, and my missteps trying to do so, is rather boring really, but it produced this:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
The brains I added was a Wemos D1 Mini (thanks, Aliexpress!), with a 3.3V<->5V converter between to sense when the coffee maker is on, and also turn on a mosfet to short the button to ground.
|
||||
I initially had issues with the coffee maker turning on whenever I plugger the D1 Mini in, but a pulldown resistor solved this.
|
||||
|
||||

|
||||
|
||||
I also opted for an external USB plug - this allows me to possibly reflash this without opening the enclosure, and also meant I did not need to splice into the mains line inside, which I was reluctant to do.
|
||||
|
||||
I initially flashed and programmed the coffee maker with Tasmota, but almost immediately opted to use ESPHome instead.
|
||||
I like Tasmota and ESPHome, I was just able to get more fine control of the setup using ESPHome in this instance, especially concerning the button logic.
|
||||
|
||||
The (important) code is as follows:
|
||||
|
||||
```yaml
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
device_class: power
|
||||
internal: true
|
||||
id: power_on
|
||||
pin: D2
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
pin: D1
|
||||
id: relay
|
||||
restore_mode: ALWAYS_OFF
|
||||
- platform: template
|
||||
name: "Coffee Maker"
|
||||
icon: "mdi:coffee-maker"
|
||||
lambda: |-
|
||||
if (id(power_on).state) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
turn_on_action:
|
||||
- switch.turn_on: relay
|
||||
- delay: 50ms
|
||||
- switch.turn_off: relay
|
||||
turn_off_action:
|
||||
- switch.turn_on: relay
|
||||
- delay: 50ms
|
||||
- switch.turn_off: relay
|
||||
```
|
||||
|
||||
The beauty of the way in which I can detect signals and simulate a button press is that ESPHome senses when I use the physical button, and updates accordingly.
|
||||
I can use the coffee maker absolutely as normal, the 'Smart' part of it is optional.
|
||||
|
||||
***
|
||||
|
||||
The rest of my setup using this coffee maker is somewhat outside the scope of this post, but it includes using Tasker on my phone to run a 'Wakeup' automation on Home Assistant, which will - along with turning on my bed-head lamp, and switching my monitor on before putting it to sleep - check if I turned on a 'Morning Coffee' user input.
|
||||
If I remembered to prep my coffee the night before, and turned on the switch, I will wake up to a hot cup of coffee (or, the sound of a coffee-in-progress).
|
||||
I can also command Alexa and Google Assistant to do my bidding and make me a coffee.
|
||||
At least someone does what I want...
|
||||
|
||||
Truly, we are living in the future.
|
||||
BIN
content/posts/coffee/media/20200610_204856.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200610_204856.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200610_205351.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200610_205351.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200610_205841.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200610_205841.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200610_205847.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200610_205847.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200702_120123.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200702_120123.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200702_120302.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200702_120302.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200702_120309.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200702_120309.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200702_120328.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200702_120328.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/20200702_120334.webp
(Stored with Git LFS)
BIN
content/posts/coffee/media/20200702_120334.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/coffee/media/src/20200610_204856.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200610_204856.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
372a99d5662d499adccdd71c8110b8d459612110babf4099bf4d5bdd7370e329
|
||||
BIN
content/posts/coffee/media/src/20200610_205351.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200610_205351.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
d48ea9d5b0cfd9291c110a1beb64be4d7f76f62644986b49a969bc10cce9a7ca
|
||||
BIN
content/posts/coffee/media/src/20200610_205841.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200610_205841.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
f53659511bd33ac8c51a8f1fae74d24551c270e8fc6870553ce4edc04add95f2
|
||||
BIN
content/posts/coffee/media/src/20200610_205847.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200610_205847.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
cf91c706dec4682dbddc5be255fe9e5793c61941c0284c3bb7dae9e0fb592e80
|
||||
BIN
content/posts/coffee/media/src/20200702_120123.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200702_120123.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
526b6f7d067f17a0b593482823cb88024a0ad1417f905e7b61f1077773acd7aa
|
||||
BIN
content/posts/coffee/media/src/20200702_120302.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200702_120302.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
c259c61a1fc9b3554f5e2c3ed4a0318deb92ea013b665a32534f90c4f5b6d897
|
||||
BIN
content/posts/coffee/media/src/20200702_120309.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200702_120309.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
14bfcaec0739089a4bbbf57c78ad3ca93286cf402c9a987fcb63a23bfffddbf5
|
||||
BIN
content/posts/coffee/media/src/20200702_120328.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200702_120328.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
4404536aaf5f70f51fad0e9298ecf1ead5b97e52a3557d482f89d23f7ccba1ce
|
||||
BIN
content/posts/coffee/media/src/20200702_120334.jpg
(Stored with Git LFS)
BIN
content/posts/coffee/media/src/20200702_120334.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
1ee1ad4b87c3a581e97879eb627e6c478972b86a83232fbd66173390de34fb80
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
---
|
||||
title: "Ghetto NAS Part 1"
|
||||
date: "2023-08-29"
|
||||
author: "William Floyd"
|
||||
#featured_image: "media/IMG_20220126_225541.webp"
|
||||
categories: [
|
||||
"Sys Admin",
|
||||
"Hardware",
|
||||
"Software"
|
||||
]
|
||||
tags: [
|
||||
"NAS",
|
||||
"3D Printing",
|
||||
"Gluster",
|
||||
"Homelab"
|
||||
]
|
||||
series: ["Ghetto NAS"]
|
||||
list: never
|
||||
draft: true
|
||||
---
|
||||
|
||||
This is an ongoing project to build a custom NAS on the most minimal budget possible.
|
||||
|
||||
# Use Case
|
||||
|
||||
Storing a large (30TB+) amount of infrequently accessed data that must still be immediately accessible (primarily Jellyfin, Nextcloud), with some level of safety.
|
||||
|
||||
Some details about my use case:
|
||||
* There will be no external network access except via a single local client mounting the drive and sharing via ZeroTier
|
||||
* There will be very few clients total
|
||||
* Most data is replaceable, though inconveniently so (media may be reacquired / restored from backups)
|
||||
* Neither latency nor throughput are very important
|
||||
|
||||
# Bill of Materials
|
||||
|
||||
| Quantity | Item | Per Unit Cost | Notes |
|
||||
|----------|------------------------------|---------------|-----------------------------------------------------------------------------------------------|
|
||||
| 3 | Dell Wyse 3030LT Thin Client | $11 | Ebay - Fairly common, though may run out eventually - other thin clients will no doubt appear |
|
||||
| 3 | HGST 10TB He10 510 | $80 | Amazon / Ebay - Very common, can pick these up any day |
|
||||
| 3 | ORICO 3.5in to USB enclosure | $25 | Amazon - Could use another, this is what I chose, does the job for me |
|
||||
| 5 | Ethernet Cables | $2.5 | Amazon - $12.50 / 5 pack - Or whatever you have lying around |
|
||||
| 1 | 8 Port Ethernet Switch | $13 | Amazon - Or whatever you have lying around |
|
||||
| 0.5kg | PLA | $20 | For the NAS enclosure |
|
||||
|
||||
# Rationale
|
||||
|
||||
In order of importance for my use case: Price > Redundancy > Performance
|
||||
|
||||
## Hardware
|
||||
|
||||
### Thin Client
|
||||
You simply cannot beat a whole working Linux box for $11.
|
||||
With 2GB RAM, 4GB eMMC, 1 GbE, 1 USB 3 port, and a bundled power adapter, it does the bare minimum I need.
|
||||
|
||||
### HDD
|
||||
Similarly, **used** enterprise drives deliver an amazing value.
|
||||
For less than $9/TB or just over $10/TB with the enclosure, these drives are the cheapest possible way to get storage right now.
|
||||
By using external enclosures we can also upgrade to larger drives in future, with minimal effort.
|
||||
No shucking required!
|
||||
|
||||
I buy ones that have a 5 year warranty (spoiler - it's worth having!).
|
||||
|
||||
### Networking
|
||||
1GbE is plenty enough for me, but if in future I need more speed, I can find a network switch with 10GbE uplink and scale horizontally a fair bit.
|
||||
For now, a cheap unmanaged GbE switch will do just fine.
|
||||
|
||||
### UPS
|
||||
Not 100% required, but the peace of mind in having the whole system on a UPS is worth it.
|
||||
|
||||
## Software
|
||||
|
||||
### Gluster
|
||||
|
||||
I am using Gluster to run my NAS cluster.
|
||||
This is in large part due to its very modest hardware requirements, especially memory.
|
||||
I can run my nodes with less than 50% memory utilization, and not fill my limited eMMC storage either.
|
||||
It is very easy to work with, and offers flexible redundancy configurations.
|
||||
|
||||
#### Configuration
|
||||
|
||||
I am using Gluster with a dispersed volume, using the native client on my main server to mount the volume.
|
||||
Dispersed lets me add clusters of bricks fairly easily, which suits my needs well.
|
||||
|
||||
### Netdata
|
||||
|
||||
This lets me know if/when drives get full, lets me know drive temperature from SMART data, and will email me if any hosts go offline.
|
||||
|
||||
# Experiences so far
|
||||
|
||||
I've been too busy to document the whole process, but I currently have a 2 x (2 + 1) array running (if I'd known I'd need 6 drives, I'd have done 1 x (4 + 2), but I didn't know at first).
|
||||
Capacity is 60TB raw, 40TB usable.
|
||||
|
||||
## HDD Failures
|
||||
|
||||
That 5 year warranty I mentioned?
|
||||
I've needed it twice so far - one drive died about 1 month in, and a second died 2 months in.
|
||||
To their credit, the vendor got me a return package label within one business day each time, and refunded me as soon as the return package arrived.
|
||||
For now, I continue to use these drives because the $/TB is so good, but in future I may upgrade to some larger drives in the same way to keep power costs down.
|
||||
|
||||
## Power Draw
|
||||
|
||||
6 x HDDs + 6 x Thin Clients + Network Switch + 12V Power Supply, draws about 40W at the wall under regular load (serving files).
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
---
|
||||
title: "Ghetto NAS Part 2"
|
||||
date: "2024-02-16"
|
||||
author: "William Floyd"
|
||||
#featured_image: "media/IMG_20220126_225541.webp"
|
||||
categories: [
|
||||
"Sys Admin",
|
||||
"Hardware",
|
||||
"Software"
|
||||
]
|
||||
tags: [
|
||||
"NAS",
|
||||
"3D Printing",
|
||||
"Gluster",
|
||||
"Homelab"
|
||||
]
|
||||
series: ["Ghetto NAS"]
|
||||
list: never
|
||||
draft: true
|
||||
---
|
||||
|
||||
I've been running the Gluster array from [part one](../ghetto-nas-part-01/) of this series for some months now, and am looking to improve my setup as I move to a new location and have new requirements.
|
||||
|
||||
# Existing Hardware
|
||||
|
||||
As a reminder/update, here is my existing hardware setup:
|
||||
|
||||
* Used HP Z440
|
||||
* CPU
|
||||
* Intel Xeon 1650-v4 (6 core, 12 thread, 3.6/4.0GHZ)
|
||||
* Memory
|
||||
* 128GB LRDDR4 @ 2133MT/s
|
||||
* Storage
|
||||
* 1TB NVME boot drive via PCIE adapter
|
||||
* 8TB shucked WD Easystore (bought new)
|
||||
* 14TB shucked WD Easystore (bought new)
|
||||
* GPU
|
||||
* Dell GTX 1080 (for gaming)
|
||||
* Intel Arc A380 (for transcoding)
|
||||
* 6 x Gluster Nodes
|
||||
* Dell Wyse 3030 LT Thin Client
|
||||
* CPU
|
||||
* Intel Celerton N2807 (2 core, 0.5/2.167GHz)
|
||||
* Memory
|
||||
* 2GB Memory
|
||||
* Storage
|
||||
* 4GB MMC boot drive
|
||||
* ORICO 3.5" SATA to USB 3.0 desktop adapter
|
||||
* 10TB HGST He10 (refurbished, 5 year warranty)
|
||||
* Generic 360W 12V power supply for Thin Clients and HDDs
|
||||
* Generic Gigabit ethernet switch for all thin clients and workstation
|
||||
|
||||
# Requirements
|
||||
|
||||
Given my experiences with my existing solution, my new setup must (continue) to be:
|
||||
* Able to support my existing 40TB usable space, scalable up to ~100TB
|
||||
* Easily maintainable
|
||||
* Performant
|
||||
* Mostly quiet
|
||||
* Cost effective
|
||||
* Initial cost
|
||||
* Cost over time (aiming for 5 year lifecycle)
|
||||
* Power efficient
|
||||
* Fewer Gluster nodes
|
||||
* Large disks > many disks
|
||||
* Reliable
|
||||
* ECC Memory
|
||||
* Redundant storage
|
||||
|
||||
This leaves me with the following requirements:
|
||||
* Must support a `n x (4 + 2)` disk arrangement (~67% usable space with 2 disks of redundancy, especially as I plan to use used drives)
|
||||
* Disks must be 10TB or larger
|
||||
* Disks must be cheap
|
||||
* Disks should have reasonable warranty
|
||||
|
||||
Additional observations/experience:
|
||||
* The 4GB storage on the Dell Wyse 3030 LT nodes is difficult to work in. If the storage fills, it can result in a node failing to come online after a restart
|
||||
* Network latency results in slow directory operations via Gluster
|
||||
* The workstation is already well capable of handling this many drives, it makes more sense to connect them directly to the drives as it is their only client
|
||||
|
||||
With this in mind, I want to move away from multiple storage nodes and consolidate into a more unified storage system
|
||||
|
||||
# Options
|
||||
|
||||
## NAS
|
||||
|
||||
### Prebuilt
|
||||
|
||||
Easiest option, but not my ideal as I want to learn, and know my system wholely.
|
||||
Hardware is too expensive, no expandability, so I'm not going to do it.
|
||||
Good more many people's cases though.
|
||||
|
||||
### Custom built
|
||||
|
||||
Solid option, but too expensive - I already have a workstation, I don't want another desktop holding all the drives and not doing anything useful otherwise. More of a sunk cost issue than a failure of this option, I just can't justify redundant hardware like this. Also, power draw would be increased as I'd be adding a system, not replacing.
|
||||
|
||||
If I were to do this, these are some of the options I've looked at:
|
||||
* Mini ITX motherboard
|
||||
* [All in one](https://www.aliexpress.us/item/3256806141617147.html) ([alternative](https://www.aliexpress.us/item/3256806353828287.html)) - $125-$160 depending on spec
|
||||
* 6 SATA ports, PCIE, 4x2.5GbE, NVME
|
||||
* Power efficient (<10W TDP)
|
||||
* No ECC, memory not included
|
||||
* No brand support
|
||||
* [Xeon Kit](https://www.aliexpress.us/item/3256805579918121.html) - ~$135
|
||||
* 6(?) SATA ports, PCIE, 2x2.5GbE, NVME(?)
|
||||
* Powerful, not power efficient (90W TDP)
|
||||
* ECC memory included
|
||||
* No brand support
|
||||
* Cooler not included
|
||||
* More of a replacement to my workstation
|
||||
* [3D printed case](https://modcase.com.au/products/nas)
|
||||
* NAS Case
|
||||
* [Silverstone DS308B](https://www.silverstonetek.com/en/product/info/server-nas/DS380/)
|
||||
* Too expensive ($200+)
|
||||
* [Generic 8 bay ITX enclosure](https://www.amazon.com/KCMconmey-Internal-Compatible-Backplane-Enclosure/dp/B0BXKSS8YY/)
|
||||
* Too expensive ($150)
|
||||
* No brand support
|
||||
* Leaves empty bays if expanding in 6 drive increments
|
||||
|
||||
Overall something I've strongly considered, mostly for space savings, but cost is keeping me away, as it's basically a whole new PC for each new node (unless I'm expanding somehow otherwise, which I could do via the workstation anyway).
|
||||
|
||||
## JBOD
|
||||
|
||||
Requires an external HBA/SATA expander from the workstation.
|
||||
|
||||
### Prebuilt (ex-Enterprise)
|
||||
|
||||
Strong option, moderately easy to set up.
|
||||
Concerns are:
|
||||
* Power draw
|
||||
* Noise
|
||||
* Need for rack mounting
|
||||
* More bays than I need
|
||||
|
||||
If I were to do this (and I may do some day), I would probably get an EMC KTN-STL3, a 15 bay chassis.
|
||||
|
||||
### Custom built (from scratch)
|
||||
|
||||
Too much work, don't want to *need* to design my own PCB for this.
|
||||
|
||||
### Custom built (using ex-Enterprise parts)
|
||||
|
||||
A few options,
|
||||
|
||||
https://www.supermicro.com/manuals/other/BPN-SAS3-815TQ.pdf
|
||||
|
||||
# Physical layout
|
||||
|
||||
I had begun modelling and came close to 3D printing an all in one cluster enclosure for 3 clients and 3 drives that would include a power distribution board, fan controller with temperature sensor, and panel mounted Ethernet ports.
|
||||
This was never finished, and as I look to
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
---
|
||||
title: "Ghetto NAS Part 1"
|
||||
date: "2023-08-29"
|
||||
author: "William Floyd"
|
||||
#featured_image: "media/IMG_20220126_225541.webp"
|
||||
categories: [
|
||||
"Sys Admin",
|
||||
"Hardware",
|
||||
"Software"
|
||||
]
|
||||
tags: [
|
||||
"NAS",
|
||||
"3D Printing",
|
||||
"Gluster"
|
||||
]
|
||||
series: ["Ghetto NAS"]
|
||||
---
|
||||
|
||||
This is an ongoing project to build a custom NAS on the most minimal budget possible.
|
||||
|
||||
# Use Case
|
||||
|
||||
Storing a large (30TB+) amount of infrequently accessed data that must still be immediately accessible (primarily Jellyfin), with some level of safety.
|
||||
|
||||
Some details about my use case:
|
||||
* There will be no external network access except via a single local client mounting the drive and sharing via ZeroTier
|
||||
* There will be very few clients total
|
||||
* Most data is replaceable, though inconveniently so (media may be reacquired / restored from backups)
|
||||
* Neither latency nor throughput are very important
|
||||
|
||||
# Bill of Materials
|
||||
|
||||
| Quantity | Item | Per Unit Cost | Notes |
|
||||
|----------|------------------------------|---------------|-----------------------------------------------------------------------------------------------|
|
||||
| 3 | Dell Wyse 3030LT Thin Client | $11 | Ebay - Fairly common, though may run out eventually - other thin clients will no doubt appear |
|
||||
| 3 | HGST 10TB He10 510 | $80 | Amazon / Ebay - Very common, can pick these up any day |
|
||||
| 3 | ORICO 3.5in to USB enclosure | $25 | Amazon - Could use another, this is what I chose, does the job for me |
|
||||
| 5 | Ethernet Cables | $2.5 | Amazon - $12.50 / 5 pack - Or whatever you have lying around |
|
||||
| 1 | 8 Port Ethernet Switch | $13 | Amazon - Or whatever you have lying around |
|
||||
| 0.5kg | PLA | $20 | For the NAS enclosure |
|
||||
|
||||
# Rationale
|
||||
|
||||
In order of importance for my use case: Price > Redundancy > Performance
|
||||
|
||||
## Hardware
|
||||
|
||||
### Thin Client
|
||||
You simply cannot beat a whole working Linux box for $11.
|
||||
With 2GB RAM, 4GB eMMC, 1 GbE, 1 USB 3 port, and a bundled power adapter, it does the bare minimum I need.
|
||||
|
||||
### HDD
|
||||
Similarly, **used** enterprise drives deliver an amazing value.
|
||||
For less than $9/TB or just over $10/TB with the enclosure, these drives are the cheapest possible way to get storage right now.
|
||||
By using external enclosures we can also upgrade to larger drives in future, with minimal effort.
|
||||
No shucking required!
|
||||
|
||||
I buy ones that have a 5 year warranty (spoiler - it's worth having!).
|
||||
|
||||
### Networking
|
||||
1GbE is plenty enough for me, but if in future I need more speed, I can find a network switch with 10GbE uplink and scale horizontally a fair bit.
|
||||
For now, a cheap unmanaged GbE switch will do just fine.
|
||||
|
||||
### UPS
|
||||
Not 100% required, but the peace of mind in having the whole system on a UPS is worth it.
|
||||
|
||||
## Software
|
||||
|
||||
### Gluster
|
||||
|
||||
I am using Gluster to run my NAS cluster.
|
||||
This is in large part due to its very modest hardware requirements, especially memory.
|
||||
I can run my nodes with less than 50% memory utilization, and not fill my limited eMMC storage either.
|
||||
It is very easy to work with, and offers flexible redundancy configurations.
|
||||
|
||||
#### Configuration
|
||||
|
||||
I am using Gluster with a dispersed volume, using the native client on my main server to mount the volume.
|
||||
Dispersed lets me add clusters of bricks fairly easily, which suits my needs well.
|
||||
|
||||
### Netdata
|
||||
|
||||
This lets me know if/when drives get full, lets me know drive temperature from SMART data, and will email me if any hosts go offline.
|
||||
|
||||
# Experiences so far
|
||||
|
||||
I've been too busy to document the whole process, but I currently have a 2 x (2 + 1) array running (if I'd known I'd need 6 drives, I'd have done 1 x (4 + 2), but I didn't know at first).
|
||||
Capacity is 60TB raw, 40TB usable.
|
||||
|
||||
## HDD Failures
|
||||
|
||||
That 5 year warranty I mentioned?
|
||||
I've needed it twice so far - one drive died about 1 month in, and a second died 2 months in.
|
||||
To their credit, the vendor got me a return package label within one business day each time, and refunded me as soon as the return package arrived.
|
||||
For now, I continue to use these drives because the $/TB is so good, but in future I may upgrade to some larger drives in the same way to keep power costs down.
|
||||
|
||||
## Power Draw
|
||||
|
||||
6 x HDDs + 6 x Thin Clients + Network Switch + 12V Power Supply, draws about 40W at the wall under regular load (serving files).
|
||||
|
||||
# Topology
|
||||
|
||||
{{<mermaid>}}
|
||||
%%{
|
||||
init: {
|
||||
'theme': 'base',
|
||||
'themeVariables': {
|
||||
'background': '#00000000',
|
||||
'primaryColor': '#00000000',
|
||||
'primaryTextColor': '#888888',
|
||||
'secondaryColor': '#00000000',
|
||||
'primaryBorderColor': '#888888',
|
||||
'secondaryBorderColor': '#888888',
|
||||
'secondaryTextColor': '#888888',
|
||||
'tertiaryColor': '#00000000',
|
||||
'tertiaryBorderColor': '#888888',
|
||||
'tertiaryTextColor': '#888888',
|
||||
'noteBkgColor': '#00000000',
|
||||
'noteTextColor': '#888888',
|
||||
'noteBorderColor': '#888888',
|
||||
'lineColor': '#888888',
|
||||
'textColor': '#888888',
|
||||
'mainBkg': '#00000000',
|
||||
'errorBkgColor': '#00000000',
|
||||
'errorTextColor': '#888888'
|
||||
}
|
||||
}
|
||||
}%%
|
||||
graph TB
|
||||
|
||||
subgraph internet["Internet"]
|
||||
me_away["Me when away from home"] & Friends & Family & Fiancé --- caddy
|
||||
subgraph vps["Cloud VPS"]
|
||||
caddy --- vps_zerotier["Zerotier"] & rss
|
||||
subgraph vps_docker["Docker"]
|
||||
caddy["Caddy"]
|
||||
rss["FreshRSS"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vps_zerotier ---- zerotier
|
||||
|
||||
subgraph home["Home Network"]
|
||||
|
||||
z440 ---- me_home["Me at home"]
|
||||
|
||||
subgraph z440["Server (HP Z440)"]
|
||||
|
||||
zerotier["Zerotier"] --- jellyfin & arr & ha_zerotier
|
||||
|
||||
subgraph docker[Docker]
|
||||
jellyfin["Jellyfin"]
|
||||
arr["*arr Applications"]
|
||||
end
|
||||
|
||||
subgraph vms["VMs"]
|
||||
subgraph ha["Home Assistant"]
|
||||
ha_zerotier["Zerotier"]
|
||||
end
|
||||
end
|
||||
|
||||
jellyfin & arr --- gluster["Gluster mount"]
|
||||
|
||||
jellyfin & arr --- disk_internal["Internal Disks"]
|
||||
|
||||
end
|
||||
|
||||
ha ---- smart_home_devices["Smart Home Devices"]
|
||||
|
||||
gluster --- switch["GbE Network Switch"] --- client1 & client2 & client3 & client4 & client5 & client6
|
||||
|
||||
client1[1.wyse] --"USB"--- disk1[Disk 1]
|
||||
client2[2.wyse] --"USB"--- disk2[Disk 2]
|
||||
client3[3.wyse] --"USB"--- disk3[Disk 3]
|
||||
client4[4.wyse] --"USB"--- disk4[Disk 4]
|
||||
client5[5.wyse] --"USB"--- disk5[Disk 5]
|
||||
client6[6.wyse] --"USB"--- disk6[Disk 6]
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
{{</mermaid>}}
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
---
|
||||
title: "Home Automation at School"
|
||||
date: "2021-04-21"
|
||||
author: "William Floyd"
|
||||
featured_image: "media/20200813_023018.webp"
|
||||
categories: [
|
||||
"Software"
|
||||
]
|
||||
tags: [
|
||||
"School",
|
||||
"Home Assistant",
|
||||
"Home Automation",
|
||||
"ESPHome",
|
||||
"IOT"
|
||||
]
|
||||
---
|
||||
|
||||
I love home automation - I've spent far longer writing automation routines and hacking together my own devices and programs than I have ever saved by doing so, and that's perfectly fine.
|
||||
One unique aspect of my setup, however, is that I cannot control the network I must use - in my Uni dorm, I am not allowed to run my own router, and so all IoT devices must connect to the school wireless network.
|
||||
There abound dozens of Google Home devices, Chromecasts, and so on, all accessible on the same network - but not from the wired connection that my server/desktop uses.
|
||||
|
||||
Here then is my solution: MQTT everything I can.
|
||||
From my lights, so light sensor, to coffee maker to desktop software, I bounce it through a MQTT server hosted on a VPS.
|
||||
I use Home Assistant, so automatic discovery is easy on most things, especially ESPHome.
|
||||
In fact, I disabled direct Home Assistant connectivity entirely on these devices, which works well enough for me to live with.
|
||||
|
||||
# But how to flash?
|
||||
|
||||
Given that my Home Assistant instance isn't even on the same network as the IoT devices, how do I update the firmware?
|
||||
Using my laptop, I can connect to the wireless network that they reside on, and using IP address sensors reported by these devices, flash them directly without needing local discovery.
|
||||
In fact, I can easily automate this for myself using a couple scripts and a minimal number of hard-coded values:
|
||||
|
||||
`hassio.sh`
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
export HASS_SERVER=https://<server_url>:443
|
||||
export HASS_TOKEN='<HA_Token>'
|
||||
|
||||
hass-cli ${@}
|
||||
|
||||
exit
|
||||
|
||||
```
|
||||
|
||||
`lamps.sh`
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
declare -A aa
|
||||
|
||||
aa["gosund_lb1_1.yaml"]="sensor.desk_lamp_ip_address"
|
||||
aa["gosund_lb1_2.yaml"]="sensor.bed_lamp_ip_address"
|
||||
aa["gosund_lb1_3.yaml"]="sensor.floor_lamp_girlfriend_ip_address"
|
||||
aa["gosund_lb1_4.yaml"]="sensor.desk_lamp_girlfriend_ip_address"
|
||||
aa["gosund_lb1_5.yaml"]="sensor.floor_lamp_ip_address"
|
||||
|
||||
__flash() {
|
||||
|
||||
__config="${1}"
|
||||
__entity_name="${aa[${__config}]}"
|
||||
echo "Getting ${__config} IP..."
|
||||
|
||||
__ip="$(
|
||||
./hassio.sh -o yaml state get \
|
||||
"${__entity_name}" |
|
||||
grep -E '^ *state' | sed -e 's/.* //'
|
||||
)"
|
||||
|
||||
echo "IP: ${__ip}"
|
||||
|
||||
if [ "${__ip}" == 'unavailable' ]; then
|
||||
echo 'Ignoring...'
|
||||
else
|
||||
echo "Flashing..."
|
||||
./esphome.sh "${__config}" run --upload-port="${__ip}"
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
if [ "${#}" -gt 0 ]; then
|
||||
until [ "${#}" == 0 ]; do
|
||||
__flash "${1}"
|
||||
shift
|
||||
done
|
||||
else
|
||||
for __config in ${!aa[@]}; do
|
||||
__flash "${__config}"
|
||||
done
|
||||
fi
|
||||
|
||||
exit
|
||||
```
|
||||
|
||||
this allows me to mostly painlessly flash my devices, though truth be told there is little need.
|
||||
|
||||
# Custom software
|
||||
|
||||
I developed for myself a tool in Golang to help tie more of my devices together.
|
||||
It is rather uncreatively/cryptically named `ha-mqtt-iot` - that is, "Home Assistant MQTT Internet of Things".
|
||||
I may rename this some day, but why bother.
|
||||
It is similar to IOTLink (which is Windows only), and HASS Workstation Service - they are great projects, but this one is mine, even if it is poorly written.
|
||||
|
||||
The gist of the software is that most (all?) device types supported by Home Assistant may be implemented using a selection of user defined commands.
|
||||
The most prominent examples in my case are in order to enable/disable dark mode on my desktop.
|
||||
I automate this according to ambient light in my room, to better match the aesthetic I want.
|
||||
Additionally, I can use it to turn my desktop monitor off, without resorting to using a relay outlet, and even change the color temperature of my system.
|
||||
The script I use for this looks like the following:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
__monitor_i2c='dev:/dev/i2c-3'
|
||||
__monitor_dpms='0xd6'
|
||||
__monitor_brightness='0x10'
|
||||
__monitor_standby='4'
|
||||
__monitor_off='5'
|
||||
__monitor_on='1'
|
||||
|
||||
__unknown() {
|
||||
echo "Unknown ${1}"
|
||||
}
|
||||
|
||||
f2i() {
|
||||
awk 'BEGIN{for (i=1; i<ARGC;i++)
|
||||
printf "%.0f\n", ARGV[i]}' "$@"
|
||||
}
|
||||
|
||||
com="${1}"
|
||||
arg="${2}"
|
||||
|
||||
case "${com}" in
|
||||
"command")
|
||||
case "${arg}" in
|
||||
"ON")
|
||||
xset dpms force on
|
||||
;;
|
||||
"OFF")
|
||||
(
|
||||
#xset dpms force off
|
||||
#sleep 0.5s
|
||||
#ddccontrol -r "${__monitor_dpms}" -w "${__monitor_standby}" "${__monitor_i2c}" -f
|
||||
#sleep 2s
|
||||
ddccontrol -r "${__monitor_dpms}" -w "${__monitor_off}" "${__monitor_i2c}" -f
|
||||
) &
|
||||
;;
|
||||
*)
|
||||
__unknown "${arg}"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"command-state")
|
||||
echo -n "$(xset q | grep 'Monitor is' | sed -e 's/.* //' | tr '[:lower:]' '[:upper:]')"
|
||||
;;
|
||||
"color-temp")
|
||||
v="$(f2i "$(bc -l <<<"1000000/${arg}")")"
|
||||
./scripts/run-in-user-session.sh gsettings set org.gnome.settings-daemon.plugins.color night-light-temperature "${v}"
|
||||
;;
|
||||
"color-temp-state")
|
||||
v="$(./scripts/run-in-user-session.sh gsettings get org.gnome.settings-daemon.plugins.color night-light-temperature)"
|
||||
echo -n "$(f2i "$(bc -l <<<"1000000/${v/* /}")")"
|
||||
;;
|
||||
"brightness")
|
||||
ddccontrol -r "${__monitor_brightness}" "${__monitor_i2c}" -w "${arg}"
|
||||
;;
|
||||
"brightness-state")
|
||||
echo -n "$(ddccontrol 2>/dev/null -r "${__monitor_brightness}" "${__monitor_i2c}" | tail -n 1 | grep -o '/[0-9]*/100' | sed -e 's|^/||' -e 's|/.*||')"
|
||||
;;
|
||||
|
||||
*)
|
||||
__unknown "root command ${com}"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit
|
||||
```
|
||||
|
||||
Note that the display doesn't respond to being turned back on, so this is somewhat incomplete, but it's good enough for my needs.
|
||||
The corresponding portion of the config for `ha-mqtt-iot` looks like the following:
|
||||
|
||||
```json
|
||||
"lights": [
|
||||
{
|
||||
"info": {
|
||||
"name": "Desktop Monitor",
|
||||
"id": "monitor"
|
||||
},
|
||||
"command": [
|
||||
"./scripts/monitor.sh",
|
||||
"command"
|
||||
],
|
||||
"command_state": [
|
||||
"./scripts/monitor.sh",
|
||||
"command-state"
|
||||
],
|
||||
"command_color_temp": [
|
||||
"./scripts/monitor.sh",
|
||||
"color-temp"
|
||||
],
|
||||
"command_color_temp_state": [
|
||||
"./scripts/monitor.sh",
|
||||
"color-temp-state"
|
||||
],
|
||||
"command_brightness": [
|
||||
"./scripts/monitor.sh",
|
||||
"brightness"
|
||||
],
|
||||
"command_brightness_state": [
|
||||
"./scripts/monitor.sh",
|
||||
"brightness-state"
|
||||
],
|
||||
"brightness_scale": 100,
|
||||
"update_interval": 5
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Pretty simple.
|
||||
This makes custom system sensors trivial.
|
||||
For example, to show my system IP, I use the following:
|
||||
|
||||
```json
|
||||
"sensors": [
|
||||
{
|
||||
"info": {
|
||||
"name": "IP Address Desktop Solus",
|
||||
"id": "ip-address-desktop-solus",
|
||||
"icon": "mdi:ip-network"
|
||||
},
|
||||
"command_state": [
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"ip -j address show eno1 | jq -r '.[0].addr_info[0].local'"
|
||||
],
|
||||
"update_interval": 10
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Some common use cases are built in as well.
|
||||
Currently, this includes laptop displays (as lights) and batteries (as sensors), as well as Crypto prices (though the CoinGecko Golang library).
|
||||
These are really easy to call.
|
||||
An exhaustive example is quite short:
|
||||
|
||||
```json
|
||||
"builtin": {
|
||||
"prefix": "Name Prefix ",
|
||||
"backlight": {
|
||||
"enable": true,
|
||||
"temperature": false,
|
||||
"range": {
|
||||
"minimum": 0.025,
|
||||
"maximum": 0.95
|
||||
}
|
||||
},
|
||||
"battery": {
|
||||
"enable": true
|
||||
},
|
||||
"crypto": [
|
||||
{
|
||||
"coin_name": "dogecoin",
|
||||
"currency_name": "usd",
|
||||
"update_interval": 1,
|
||||
"icon": "mdi:currency-usd"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This lets me tailor my setup to each machine I'm using, while still enjoying the benefits of Home Assistant MQTT Discovery.
|
||||
The primary limitation at present is the inability to signal to `ha-mqtt-iot` from another process - it can only poll for changes.
|
||||
This will be addressed one day, when it is important for my own needs.
|
||||
|
||||
# How to host?
|
||||
|
||||
But the question is now, how do I access my HomeAssistant instance if it's also hosted at school?
|
||||
I most certainly don't have a public IP, so in comes AutoSSH.
|
||||
I'm not sure which is the best one at this stage, but refer to [this](https://github.com/psallandre/hassio-addons-autossh) repo and check the various forks of the parent project.
|
||||
|
||||
I have configured on my VPS a docker image that accepts reverse SSH tunnelling, authorized only to the key of the HA addon.
|
||||
From my `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
homeassistant:
|
||||
image: "docker.io/panubo/sshd"
|
||||
container_name: homeassistant
|
||||
environment:
|
||||
- TCP_FORWARDING=true
|
||||
- GATEWAY_PORTS=true
|
||||
- SSH_ENABLE_ROOT=true
|
||||
- DISABLE_SFTP=true
|
||||
volumes:
|
||||
- "./hassio/authorized_keys:/root/.ssh/authorized_keys:ro"
|
||||
- ./docker-config/hassio/data/:/data
|
||||
- ./docker-config/hassio/keys/:/etc/ssh/keys
|
||||
ports:
|
||||
- "<MY_PORT>:22"
|
||||
restart: unless-stopped
|
||||
hostname: "homeassistant"
|
||||
```
|
||||
|
||||
This is then reverse proxied to using Caddy, to expose the website on a subdomain of a website.
|
||||
From my `Caddyfile`:
|
||||
|
||||
```dockerfile
|
||||
<MY_SUBDOMAIN>.{$MY_DOMAIN} {
|
||||
reverse_proxy homeassistant:8123
|
||||
encode gzip
|
||||
}
|
||||
```
|
||||
|
||||
Pretty simple, but not without some hiccups now and again - I occasionally have to restart the sshd docker on my VPS if something goes wrong with HomeAssistant.
|
||||
|
||||
# Bonus: Android tie in
|
||||
|
||||
I use Sleep As Android to track my sleeping patterns, and as my alarm clock.
|
||||
Using Tasker, I can run an action when I begin sleep tracking, which (using a HomeAssistant plugin for Tasker) can call a script on my HomeAssistant instance to turn off my lights (only if I'm home, of course).
|
||||
Similarly, it turns on my bedhead light when my alarm goes off in the morning, and could optionally make me coffee...
|
||||
BIN
content/posts/home-automation-at-school/media/20200813_023018.webp
(Stored with Git LFS)
BIN
content/posts/home-automation-at-school/media/20200813_023018.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/home-automation-at-school/media/src/20200813_023018.jpg
(Stored with Git LFS)
BIN
content/posts/home-automation-at-school/media/src/20200813_023018.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
70c3922ade2b73a7a949d1015e738013903342a5860c7dae3525ff8b436e424c
|
||||
BIN
content/posts/midiMixerV1/media/handwired/20190811_004928.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190811_004928.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/handwired/20190811_005806.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190811_005806.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/handwired/20190811_195900.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190811_195900.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/handwired/20190811_200447.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190811_200447.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/handwired/20190812_175132.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190812_175132.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/handwired/20190812_181606.webp
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/handwired/20190812_181606.webp
(Stored with Git LFS)
Binary file not shown.
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_004928.jpg
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_004928.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
80bebd4e51d1aaabde9777b00e87e415e73f4b5fe6a0acbf5f675fad2bab3ea9
|
||||
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_005806.jpg
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_005806.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
4e34713687f375c61df8d71d6e49adfb643f3f5df0ba8a919273e8b66b48e6f8
|
||||
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_195900.jpg
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_195900.jpg
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
b1f7de3b630c01de2c09bf0355706ea6801b429651ff73fa8f1d69d979106481
|
||||
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_200447.jpg
(Stored with Git LFS)
BIN
content/posts/midiMixerV1/media/src/handwired/20190811_200447.jpg
(Stored with Git LFS)
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue