Skip to content

Commit

Permalink
Merge pull request #36 from Snuffy2/Move-config-to-python
Browse files Browse the repository at this point in the history
Move config into Python
  • Loading branch information
snicker committed Nov 23, 2023
2 parents 8b6fcab + e6376b8 commit 0c5c5cf
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 257 deletions.
11 changes: 2 additions & 9 deletions Dockerfile
Expand Up @@ -3,19 +3,12 @@ FROM python:3.12-slim-bookworm

LABEL org.opencontainers.image.source=https://github.com/snicker/juicepassproxy

ENV MQTT_HOST="127.0.0.1"
ENV MQTT_PORT=1883
ENV MQTT_DISCOVERY_PREFIX="homeassistant"
ENV DEVICE_NAME="JuiceBox"
ENV DEBUG=false
ENV UPDATE_UDPC=false

RUN pip install --upgrade pip
RUN apt-get update && apt-get install -y gnupg curl
RUN echo "deb https://ppa.launchpadcontent.net/rmescandon/yq/ubuntu jammy main" > /etc/apt/sources.list.d/yq.list
RUN echo "deb-src https://ppa.launchpadcontent.net/rmescandon/yq/ubuntu jammy main" >> /etc/apt/sources.list.d/yq.list
RUN curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x9a2d61f6bb03ced7522b8e7d6657dbe0cc86bb64' | gpg --dearmor | tee /etc/apt/trusted.gpg.d/yq.gpg
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y git dnsutils net-tools telnet expect yq
RUN apt-get install -y git curl
RUN git clone https://github.com/snicker/juicepassproxy.git /juicepassproxy
RUN pip install --no-cache-dir -r /juicepassproxy/requirements.txt
RUN chmod +x /juicepassproxy/*.sh /juicepassproxy/*.expect
Expand Down
54 changes: 33 additions & 21 deletions README.MD
Expand Up @@ -30,7 +30,7 @@ _Hopefully we won't need this if EnelX fixes their API!_
B. Set these Docker Environment Variables in your Docker Compose file:</br>
```
UPDATE_UDPC=true
JUICEBOX_HOST=<IP address of your JuiceBox
JUICEBOX_HOST=<IP address of your JuiceBox>
JPP_HOST=<IP address of the machine that the JuicePass Proxy Docker Container is running on>
```

Expand Down Expand Up @@ -83,29 +83,27 @@ services:
Variable | Required | Description & Default |
-- | -- | --
**JUICEBOX_HOST** | **Recommended**</br></br>**Required if Update UDPC is True.** | If defined, it will attempt to get the EnelX Server and Port using Telnet. If unsuccessful, it will default to the EnelX Server and Port below.
**UPDATE_UDPC** | No | Default: false. If true, will continually update the JuiceBox via telnet to point to JuicePass Proxy.
**JPP_HOST** | **Required if Update UDPC is True** | This is the IP or Hostname of the machine where JuicePass Proxy is running (**not** the IP of the Docker Container)
**SRC** | No | If not defined, it will attempt to get the Local Docker IP. If unsuccessful, it will default to 127.0.0.1.
**DST** | No | If not defined, it will attempt to get the IP of the EnelX Server. If unsuccessful, it will default to 54.161.185.130. If manually defined, you should only use the IP address of the EnelX Server and not the fully qualified domain name to avoid DNS lookup loops.
**JUICEBOX_ID** | No | If not defined, it will attempt to get the JuiceBox ID using Telnet.
**ENELX_SERVER** | No | juicenet-udp-prod3-usa.enelx.com
**ENELX_PORT** | No | 8047
**MQTT_HOST** | No | 127.0.0.1
**MQTT_PORT** | No | 1883
**MQTT_USER** | No |
**MQTT_PASS** | No |
**MQTT_DISCOVERY_PREFIX** | No | homeassistant
**DEVICE_NAME** | No | JuiceBox
**UPDATE_UDPC** | No | Default: false. If true, will continually update the JuiceBox via telnet to point to JuicePass Proxy.
**JPP_HOST** | **Required if Update UDPC is True** | This is the IP or Hostname of the machine where JuicePass Proxy is running (**not** the IP of the Docker Container)
**DEBUG** | No | false


<details>
<summary><h2>Manual Installation</h2></summary>

1. Clone this repository
2. Use Python 3.10+ (I recommend setting up a virtual environment)
2. Use Python 3.10-3.12 (I recommend setting up a virtual environment)
3. Install requirements `pip install -r requirements.txt`
4. Launch by executing `python juicepassproxy.py --dst <enelx IP:port> --host <mqtthost>` (params documented below)
4. Launch by executing `python3 juicepassproxy.py --juicebox_host <IP of the JuiceBox> --mqtt_host <mqtt_host>` (params documented below)
5. Nothing happens!
6. Configure your DNS server running on your network (like Pi-hole or your router) to route all DNS requests from EnelX to the machine running this proxy. For me this was `juicenet-udp-prod3-usa.enelx.com`. See below for instructions to determine that.
7. Alternatively to #6, you can enable `update_udpc` on the command line and set `juicebox_host` and the application will force publish the IP in the `src` argument to the Juicebox and avoid the need to set DNS rules on your router or DNS server. **NOTE: if you need to publish a different IP than the one in the `src` argument, you can make use of the `--juicepass_proxy_host` arg.**
Expand All @@ -115,25 +113,39 @@ Variable | Required | Description & Default |
```
options:
-h, --help show this help message and exit
-s SRC, --src SRC Source IP and port, (default: 127.0.0.1:8047)
-d DST, --dst DST Destination IP and port of EnelX Server.
-s SRC, --src SRC Source IP (and optional port). If not defined, will
obtain it automatically. (Ex. 127.0.0.1:8047)
-d DST, --dst DST Destination IP (and optional port) of EnelX Server. If
not defined, --juicebox_host required and then will
obtain it automatically. (Ex. 127.0.0.1:8047)
--debug
-u USER, --user USER MQTT username
-P PASSWORD, --password PASSWORD
MQTT password
-H HOST, --host HOST MQTT hostname to connect to (default: 127.0.0.1)
-p PORT, --port PORT MQTT port (default: 1883)
-D DISCOVERY_PREFIX, --discovery-prefix DISCOVERY_PREFIX
Home Assistant MQTT topic prefix (default: homeassistant)
--name DEVICE_NAME Home Assistant Device Name (default: Juicebox)
-u MQTT_USER, --mqtt_user MQTT_USER
MQTT Username
-P MQTT_PASSWORD, --mqtt_password MQTT_PASSWORD
MQTT Password
-H MQTT_HOST, --mqtt_host MQTT_HOST
MQTT Hostname to connect to (default: 127.0.0.1)
-p MQTT_PORT, --mqtt_port MQTT_PORT
MQTT Port (default: 1883)
-D MQTT_DISCOVERY_PREFIX, --mqtt_discovery_prefix MQTT_DISCOVERY_PREFIX
Home Assistant MQTT topic prefix (default:
homeassistant)
--name DEVICE_NAME Home Assistant Device Name (default: JuiceBox)
--juicebox_id JUICEBOX_ID
JuiceBox ID
JuiceBox ID. If not defined, will obtain it
automatically.
--update_udpc Update UDPC on the JuiceBox. Requires --juicebox_host
--juicebox_host JUICEBOX_HOST
host or IP address of the Juicebox. required for --update_udpc
Host or IP address of the JuiceBox. Required for
--update_udpc or if --dst not defined.
--juicepass_proxy_host JUICEPASS_PROXY_HOST
EXTERNAL host or IP address of the machine running Juicepass Proxy. Optional: only necessary when using --update_udpc and it will be inferred from the address in --src if
omitted.
EXTERNAL host or IP address of the machine running
JuicePass Proxy. Optional: only necessary when using
--update_udpc and it will be inferred from the address
in --src if omitted.
--config_loc CONFIG_LOC
The location to store the config file (default:
/~/.juicepassproxy)
```

_For **DST**, you should only use the IP address of the EnelX Server and **not** the fully qualified domain name (FQDN) to avoid DNS lookup loops._
Expand Down
11 changes: 11 additions & 0 deletions const.py
@@ -0,0 +1,11 @@
CONF_YAML = "juicepassproxy.yaml"

# Defaults
DEFAULT_ENELX_SERVER = "juicenet-udp-prod3-usa.enelx.com"
DEFAULT_ENELX_PORT = "8047"
DEFAULT_SRC = "127.0.0.1"
DEFAULT_DST = "54.161.147.91"
DEFAULT_MQTT_HOST = "127.0.0.1"
DEFAULT_MQTT_PORT = "1883"
DEFAULT_MQTT_DISCOVERY_PREFIX = "homeassistant"
DEFAULT_DEVICE_NAME = "JuiceBox"
219 changes: 38 additions & 181 deletions docker_entrypoint.sh
@@ -1,215 +1,72 @@
#!/bin/bash

CONFIG_FILE="/config/juicepassproxy.yaml"
JUICEPASSPROXY="/juicepassproxy/juicepassproxy.py"
TELNET_GET_SERVER="/juicepassproxy/telnet_get_server.expect"
TELNET_GET_JUICEBOX_ID="/juicepassproxy/telnet_get_juicebox_id.expect"

ENELX_PORT_DEFAULT="8047"
ENELX_SERVER_DEFAULT="juicenet-udp-prod3-usa.enelx.com"
SRC_DEFAULT="127.0.0.1"
DST_DEFAULT="54.161.147.91"

RED='\033[1;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

function logger() {
if [ "${1^^}" != "DEBUG" ] || ($DEBUG && [ "${1^^}" = "DEBUG" ]); then
if [ "${1^^}" = "ERROR" ]; then
printf "%-15s ${RED}%-10s %s${NC}\n" "$(date +'%Y-%m-%d %H:%M:%S')" "${1^^}" "${2}"
elif [ "${1^^}" = "WARNING" ]; then
printf "%-15s ${YELLOW}%-10s %s${NC}\n" "$(date +'%Y-%m-%d %H:%M:%S')" "${1^^}" "${2}"
else
printf "%-15s %-10s %s\n" "$(date +'%Y-%m-%d %H:%M:%S')" "${1^^}" "${2}"
fi
printf "%-15s %-10s %s\n" "$(date +'%Y-%m-%d %H:%M:%S,%3N')" "${1^^}" "${2}"
fi
}

function valid_ip() {
local ip=$1
local stat=1

if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}

echo "--------------------------------"
logger info "Starting JuicePass Proxy"
logger INFO "Starting JuicePass Proxy"
echo ""
logger INFO "DEBUG: ${DEBUG}"

if test -f ${CONFIG_FILE}; then
logger DEBUG "Importing Config File"
JUICEBOX_ID_CONFIG="$(yq e '.JUICEBOX_ID' ${CONFIG_FILE})"
if [[ "${JUICEBOX_ID_CONFIG}" = "null" ]]; then
unset JUICEBOX_ID_CONFIG
fi
ENELX_SERVER_CONFIG="$(yq e '.ENELX_SERVER' ${CONFIG_FILE})"
if [[ "${ENELX_SERVER_CONFIG}" = "null" ]]; then
unset ENELX_SERVER_CONFIG
fi
ENELX_PORT_CONFIG="$(yq e '.ENELX_PORT' ${CONFIG_FILE})"
if [[ "${ENELX_PORT_CONFIG}" = "null" ]]; then
unset ENELX_PORT_CONFIG
fi
SRC_CONFIG="$(yq e '.SRC' ${CONFIG_FILE})"
if [[ "${SRC_CONFIG}" = "null" ]]; then
unset SRC_CONFIG
fi
DST_CONFIG="$(yq e '.DST' ${CONFIG_FILE})"
if [[ "${DST_CONFIG}" = "null" ]]; then
unset DST_CONFIG
fi
else
logger DEBUG "No Config File Found."
JPP_STRING="python3 ${JUICEPASSPROXY}"
logger INFO "Docker Environment Variables:"
if [[ ! -z "${DEVICE_NAME}" ]]; then
logger INFO "DEVICE_NAME: ${DEVICE_NAME}"
JPP_STRING+=" --name ${DEVICE_NAME}"
fi

if [[ ! -z "${JUICEBOX_HOST}" ]]; then
if [[ -z "${JUICEBOX_ID}" ]]; then
JUICEBOX_ID=$(${TELNET_GET_JUICEBOX_ID} ${JUICEBOX_HOST} | sed -n 8p)
if [[ ! -z "${JUICEBOX_ID}" ]]; then
logger DEBUG "Sucessfully obtained JuiceBox ID."
JUICEBOX_ID=${JUICEBOX_ID%?}
elif [[ ! -z "${JUICEBOX_ID_CONFIG}" ]]; then
logger WARNING "Cannot get JuiceBox ID. Using config."
JUICEBOX_ID=${JUICEBOX_ID_CONFIG}
else
echo -e "\n${RED}******************************************************************************${NC}"
logger ERROR "Cannot get JuiceBox ID from Telnet. If a JuiceBox ID is later set or is obtained via Telnet, it will likely create a new JuiceBox Device with new Entities in Home Assistant."
echo -e "${RED}******************************************************************************${NC}\n"
unset JUICEBOX_ID
fi
fi

TELNET_SERVER_STRING=$(${TELNET_GET_SERVER} ${JUICEBOX_HOST} | grep "UDPC" | head -1)
if [[ ! -z "${TELNET_SERVER_STRING}" ]]; then
logger DEBUG "Sucessfully obtained EnelX Server and Port."
#logger debug "TELNET_SERVER_STRING: ${TELNET_SERVER_STRING}"
ENELX_SERVER=$(echo ${TELNET_SERVER_STRING} | sed -E 's/(# 2 UDPC[ ]+)(.*)(:.*)/\2/g')
ENELX_PORT=$(echo ${TELNET_SERVER_STRING} | sed -E 's/(.*:)(.*)([ ]+.*)/\2/g')
if valid_ip ${ENELX_SERVER}; then
if [[ ! -z "${ENELX_SERVER_CONFIG}" ]] && ! valid_ip ${ENELX_SERVER_CONFIG}; then
logger WARNING "EnelX Server is already an IP. Using config."
ENELX_SERVER=${ENELX_SERVER_CONFIG}
else
logger ERROR "EnelX Server is already an IP. Not set in config. Using default."
ENELX_SERVER=${ENELX_SERVER_DEFAULT}
fi
fi
else
if [[ ! -z "${ENELX_SERVER_CONFIG}" ]]; then
logger WARNING "Cannot get EnelX Server from Telnet. Using config."
ENELX_SERVER=${ENELX_SERVER_CONFIG}
else
logger ERROR "Cannot get EnelX Server from Telnet. Not set in config. Using default."
ENELX_SERVER=${ENELX_SERVER_DEFAULT}
fi
if [[ ! -z "${ENELX_PORT_CONFIG}" ]]; then
logger WARNING "Cannot get EnelX Port from Telnet. Using config."
ENELX_PORT=${ENELX_PORT_CONFIG}
else
logger ERROR "Cannot get EnelX Port from Telnet. Not set in config. Using default."
ENELX_PORT=${ENELX_PORT_DEFAULT}
fi
fi
else
logger DEBUG "JuiceBox Local IP not defined."
fi

if [[ -z "${SRC}" ]]; then
SRC=$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')
if valid_ip ${SRC}; then
logger DEBUG "Sucessfully obtained Docker Local IP."
elif [[ ! -z "${SRC_CONFIG}" ]]; then
logger WARNING "Cannot get Docker Local IP. Using config."
SRC=${SRC_CONFIG}
else
logger ERROR "Cannot get Docker Local IP. Not set in config. Using default."
SRC=${SRC_DEFAULT}
fi
fi

if [[ -z "${DST}" ]]; then
DST=$(dig +short @1.1.1.1 ${ENELX_SERVER} | awk '{ getline ; print $1 ; exit }')
if valid_ip ${DST}; then
logger DEBUG "Sucessfully obtained EnelX Server IP."
elif [[ ! -z "${DST_CONFIG}" ]]; then
logger WARNING "Cannot get EnelX Server IP. Using config."
DST=${DST_CONFIG}
else
logger ERROR "Cannot get EnelX Server IP. Not set in config. Using default."
DST=${DST_DEFAULT}
fi
logger INFO "JUICEBOX_HOST: ${JUICEBOX_HOST}"
JPP_STRING+=" --juicebox_host ${JUICEBOX_HOST}"
fi

JPP_STRING="python3 ${JUICEPASSPROXY} --src ${SRC}:${ENELX_PORT} --dst ${DST}:${ENELX_PORT} --host ${MQTT_HOST} --port ${MQTT_PORT} --discovery-prefix ${MQTT_DISCOVERY_PREFIX} --name ${DEVICE_NAME}"
echo ""
logger INFO "DEVICE_NAME: ${DEVICE_NAME}"
logger INFO "JUICEBOX_HOST: ${JUICEBOX_HOST}"
if [[ ! -z "${JUICEBOX_ID}" ]]; then
logger INFO "JUICEBOX_ID: ${JUICEBOX_ID}"
JPP_STRING+=" --juicebox_id ${JUICEBOX_ID}"
fi

logger INFO "SRC: ${SRC}"
logger INFO "DST: ${DST}"
logger INFO "ENELX_SERVER: ${ENELX_SERVER}"
logger INFO "ENELX_PORT: ${ENELX_PORT}"
logger INFO "MQTT_HOST: ${MQTT_HOST}"
logger INFO "MQTT_PORT: ${MQTT_PORT}"
if [[ ! -z "${SRC}" ]]; then
logger INFO "SRC: ${SRC}"
JPP_STRING+=" --src ${SRC}"
fi
if [[ ! -z "${DST}" ]]; then
logger INFO "DST: ${DST}"
JPP_STRING+=" --dst ${DST}"
fi
if [[ ! -z "${MQTT_HOST}" ]]; then
logger INFO "MQTT_HOST: ${MQTT_HOST}"
JPP_STRING+=" --mqtt_host ${MQTT_HOST}"
fi
if [[ ! -z "${MQTT_PORT}" ]]; then
logger INFO "MQTT_PORT: ${MQTT_PORT}"
JPP_STRING+=" --mqtt_port ${MQTT_PORT}"
fi
if [[ ! -z "${MQTT_USER}" ]]; then
logger INFO "MQTT_USER: ${MQTT_USER}"
JPP_STRING+=" --user ${MQTT_USER}"
JPP_STRING+=" --mqtt_user ${MQTT_USER}"
fi
if [[ ! -z "${MQTT_PASS}" ]]; then
logger INFO "MQTT_PASS: $(echo ${MQTT_PASS} | sed -E 's/./*/g')"
JPP_STRING+=" --password ${MQTT_PASS}"
JPP_STRING+=" --mqtt_password ${MQTT_PASS}"
fi

logger INFO "MQTT_DISCOVERY_PREFIX: ${MQTT_DISCOVERY_PREFIX}"

logger INFO "UPDATE_UDPC: ${UPDATE_UDPC}"
if $UPDATE_UDPC; then
if [[ ! -z "${MQTT_DISCOVERY_PREFIX}" ]]; then
logger INFO "MQTT_DISCOVERY_PREFIX: ${MQTT_DISCOVERY_PREFIX}"
JPP_STRING+=" --mqtt_discovery_prefix ${MQTT_DISCOVERY_PREFIX}"
fi
if [[ ! -z "${JPP_HOST}" ]]; then
logger INFO "JPP_HOST: ${JPP_HOST}"
JPP_STRING+=" --juicebox_host ${JUICEBOX_HOST}"
JPP_STRING+=" --juicepass_proxy_host ${JPP_HOST}"
fi
logger INFO "UPDATE_UDPC: ${UPDATE_UDPC}"
if $UPDATE_UDPC; then
JPP_STRING+=" --update_udpc"
fi

JPP_STRING+=" --config_loc /config"
logger INFO "DEBUG: ${DEBUG}"
if $DEBUG; then
JPP_STRING+=" --debug"
fi

touch ${CONFIG_FILE}
if [[ ! -z "${JUICEBOX_ID}" ]]; then
eval "yq e -i '.JUICEBOX_ID = \"${JUICEBOX_ID}\"' ${CONFIG_FILE}"
fi
if [[ ! -z "${ENELX_SERVER}" ]]; then
eval "yq e -i '.ENELX_SERVER = \"${ENELX_SERVER}\"' ${CONFIG_FILE}"
fi
if [[ ! -z "${ENELX_PORT}" ]]; then
eval "yq e -i '.ENELX_PORT = \"${ENELX_PORT}\"' ${CONFIG_FILE}"
fi
if [[ ! -z "${SRC}" ]]; then
eval "yq e -i '.SRC = \"${SRC}\"' ${CONFIG_FILE}"
fi
if [[ ! -z "${DST}" ]]; then
eval "yq e -i '.DST = \"${DST}\"' ${CONFIG_FILE}"
fi
if $DEBUG; then
echo -e "\n${CYAN}${CONFIG_FILE}:${NC}"
yq e /config/juicepassproxy.yaml
echo ""
fi
logger INFO "COMMAND: $(echo ${JPP_STRING} | sed -E 's/(.* --password )([\"]?[a-zA-Z0-9_\?\*\^\&\#\@\!]+[\"]?)/\1*****/g')"
logger DEBUG "COMMAND: $(echo ${JPP_STRING} | sed -E 's/(.* --mqtt_password )([\"]?[a-zA-Z0-9_\?\*\^\&\#\@\!]+[\"]?)/\1*****/g')"
eval ${JPP_STRING}

0 comments on commit 0c5c5cf

Please sign in to comment.