forked from gcgarner/IOTstack
-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2024-07-21 Adds ESPHome - master branch - PR 1 of 2
Adds ESPHome container, as requested by #754. Includes documentation. Fixes: #754 Signed-off-by: Phill Kelley <[email protected]>
- Loading branch information
1 parent
b1d67ed
commit a890000
Showing
15 changed files
with
532 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Assumptions: | ||
# | ||
# 1. The ESPhome container is running with the container-name "esphome". | ||
# | ||
# 2. The service definition for the ESPhome container includes: | ||
# | ||
# device_cgroup_rules: | ||
# - 'c 188:* rw' | ||
# | ||
# This clause permits the container to access any device with a major | ||
# number 188, which captures most USB-to-serial adapters that are | ||
# found on ESP32 dev boards or equivalent off-board adapters such as | ||
# those made by Future Technology Devices International (FTDI) and | ||
# Silicon Laboratories Incorporated. The major number 188 also shows | ||
# up in the UDEV rules below. | ||
# | ||
# 3. The ESP device to be managed is mounted and/or unmounted WHILE the | ||
# container is running. In other words, all bets are off if the host | ||
# system reboots or the container starts while the USB device is | ||
# connected. You will likely need to unplug/replug the device to | ||
# get the container back in sync. | ||
# | ||
# The rules do NOT check if the container is running and do NOT check | ||
# for errors. All that will happen is errors in the system log. | ||
# | ||
# Removing ESPhome from your stack does NOT remove this rules file. It | ||
# does not matter whether you accomplish removal by editing your compose | ||
# file or via the IOTstack menu, this rule will be left in place and it | ||
# will generate an error every time it fires in response to insertion | ||
# or removal of a matching USB device. | ||
# | ||
# It is perfectly safe to remove this rules file yourself: | ||
# | ||
# sudo rm /etc/udev/rules.d/88-tty-iotstack-esphome.rules | ||
# | ||
# That's all you have to do. UDEV is dynamic and, despite what you read | ||
# on the web, does NOT have to be restarted or reloaded. | ||
|
||
# Upon insertion of a matching USB device, mount the same device inside the container | ||
ACTION=="add", \ | ||
SUBSYSTEM=="tty", ENV{MAJOR}=="188", \ | ||
RUN+="/usr/bin/docker exec esphome mknod %E{DEVNAME} c %M %m" | ||
|
||
# Upon removal of a matching USB device, remove the same device inside the container | ||
ACTION=="remove", \ | ||
SUBSYSTEM=="tty", ENV{MAJOR}=="188", \ | ||
RUN+="/usr/bin/docker exec esphome rm -f %E{DEVNAME}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
#!/usr/bin/python3 | ||
# -*- coding: utf-8 -*- | ||
|
||
issues = {} # Returned issues dict | ||
buildHooks = {} # Options, and others hooks | ||
haltOnErrors = True | ||
|
||
import os | ||
import sys | ||
|
||
global templatesDirectory | ||
global currentServiceName # Name of the current service | ||
global generateRandomString | ||
|
||
from deps.consts import templatesDirectory | ||
from deps.common_functions import generateRandomString | ||
|
||
|
||
# Main wrapper function. Required to make local vars work correctly | ||
def main(): | ||
|
||
global toRun # Switch for which function to run when executed | ||
global buildHooks # Where to place the options menu result | ||
global issues # Returned issues dict | ||
global haltOnErrors # Turn on to allow erroring | ||
|
||
# runtime vars | ||
portConflicts = [] | ||
|
||
# This lets the menu know whether to put " >> Options " or not | ||
# This function is REQUIRED. | ||
def checkForOptionsHook(): | ||
try: | ||
buildHooks["options"] = callable(runOptionsMenu) | ||
except: | ||
buildHooks["options"] = False | ||
return buildHooks | ||
return buildHooks | ||
|
||
# This function is REQUIRED. | ||
def checkForPreBuildHook(): | ||
try: | ||
buildHooks["preBuildHook"] = callable(preBuild) | ||
except: | ||
buildHooks["preBuildHook"] = False | ||
return buildHooks | ||
return buildHooks | ||
|
||
# This function is REQUIRED. | ||
def checkForPostBuildHook(): | ||
try: | ||
buildHooks["postBuildHook"] = callable(postBuild) | ||
except: | ||
buildHooks["postBuildHook"] = False | ||
return buildHooks | ||
return buildHooks | ||
|
||
# This function is REQUIRED. | ||
def checkForRunChecksHook(): | ||
try: | ||
buildHooks["runChecksHook"] = callable(runChecks) | ||
except: | ||
buildHooks["runChecksHook"] = False | ||
return buildHooks | ||
return buildHooks | ||
|
||
# This service will not check anything unless this is set | ||
# This function is optional, and will run each time the menu is rendered | ||
def runChecks(): | ||
checkForIssues() | ||
return [] | ||
|
||
# This function is optional, and will run after the docker-compose.yml file is written to disk. | ||
def postBuild(): | ||
return True | ||
|
||
# This function is optional, and will run just before the build docker-compose.yml code. | ||
def preBuild(): | ||
return True | ||
|
||
# ##################################### | ||
# Supporting functions below | ||
# ##################################### | ||
|
||
def doCustomSetup() : | ||
|
||
import os | ||
import re | ||
import subprocess | ||
from os.path import exists | ||
|
||
def copyUdevRulesFile(templates,rules) : | ||
|
||
# the expected location of the rules file in the template is the absolute path ... | ||
SOURCE_PATH = templates + '/' + currentServiceName + '/' + rules | ||
|
||
# the rules file should be installed at the following absolute path... | ||
TARGET_PATH = '/etc/udev/rules.d' + '/' + rules | ||
|
||
# does the target already exist? | ||
if not exists(TARGET_PATH) : | ||
|
||
# no! does the source path exist? | ||
if exists(SOURCE_PATH) : | ||
|
||
# yes! we should copy the source to the target | ||
subprocess.call(['sudo', 'cp', SOURCE_PATH, TARGET_PATH]) | ||
|
||
# sudo cp sets root ownership but not necessarily correct mode | ||
subprocess.call(['sudo', 'chmod', '644', TARGET_PATH]) | ||
|
||
def setEnvironment (path, key, value) : | ||
|
||
# assume the variable should be written | ||
shouldWrite = True | ||
|
||
# does the target file already exist? | ||
if exists(path) : | ||
|
||
# yes! open the file so we can search it | ||
env_file = open(path, 'r+') | ||
|
||
# prepare to read by lines | ||
env_data = env_file.readlines() | ||
|
||
# we are searching for... | ||
expression = '^' + key + '=' | ||
|
||
# search by line | ||
for line in env_data: | ||
if re.search(expression, line) : | ||
shouldWrite = False | ||
break | ||
|
||
else : | ||
|
||
# no! create the file | ||
env_file = open(path, 'w') | ||
|
||
# should the variable be written? | ||
if shouldWrite : | ||
print(key + '=' + value, file=env_file) | ||
|
||
# done with the environment file | ||
env_file.close() | ||
|
||
copyUdevRulesFile( | ||
os.path.realpath(templatesDirectory), | ||
'88-tty-iotstack-' + currentServiceName + '.rules' | ||
) | ||
|
||
# the environment file is located at ... | ||
DOT_ENV_PATH = os.path.realpath('.') + '/.env' | ||
|
||
# check/set environment variables | ||
setEnvironment(DOT_ENV_PATH,'ESPHOME_USERNAME',currentServiceName) | ||
setEnvironment(DOT_ENV_PATH,'ESPHOME_PASSWORD',generateRandomString()) | ||
|
||
|
||
def checkForIssues(): | ||
doCustomSetup() # done here because is called least-frequently | ||
return True | ||
|
||
if haltOnErrors: | ||
eval(toRun)() | ||
else: | ||
try: | ||
eval(toRun)() | ||
except: | ||
pass | ||
|
||
if currentServiceName == 'esphome': | ||
main() | ||
else: | ||
print("Error. '{}' Tried to run 'plex' config".format(currentServiceName)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
esphome: | ||
container_name: esphome | ||
image: esphome/esphome | ||
restart: unless-stopped | ||
environment: | ||
- TZ=${TZ:-Etc/UTC} | ||
- USERNAME=${ESPHOME_USERNAME:-esphome} | ||
- PASSWORD=${ESPHOME_PASSWORD:?eg echo ESPHOME_PASSWORD=ChangeMe >>~/IOTstack/.env} | ||
network_mode: host | ||
x-ports: | ||
- "6052:6052" | ||
volumes: | ||
- ./volumes/esphome/config:/config | ||
device_cgroup_rules: | ||
- 'c 188:* rw' |
Oops, something went wrong.