-
-
Notifications
You must be signed in to change notification settings - Fork 366
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
scripts/external_apis: add example external protocol script
New scripts directory for external_apis, including one integrating the Enphase IQ Gateway web-API with NUT. Signed-off-by: Scott Shambarger <[email protected]>
- Loading branch information
Showing
10 changed files
with
1,244 additions
and
2 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
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
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
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
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
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,33 @@ | ||
# Network UPS Tools: scripts/external_apis | ||
|
||
EXTRA_DIST = \ | ||
README.adoc \ | ||
enphase/README.adoc \ | ||
enphase/enphase-monitor \ | ||
enphase/[email protected] | ||
|
||
SPELLCHECK_SRC = README.adoc | ||
|
||
# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. | ||
# We also have to export some variables that may be tainted by relative | ||
# paths when parsing the other makefile (e.g. MKDIR_P that may be defined | ||
# via expanded $(top_builddir)/install-sh): | ||
#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) | ||
# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ | ||
|
||
# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here | ||
# by a wildcard target in case the make implementation can put the two together. | ||
*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) | ||
|
||
.sample.sample-spellchecked: | ||
+$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ | ||
|
||
.in.in-spellchecked: | ||
+$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ | ||
|
||
spellcheck spellcheck-interactive spellcheck-sortdict: | ||
+$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ | ||
|
||
CLEANFILES = *-spellchecked enphase/*-spellchecked | ||
|
||
MAINTAINERCLEANFILES = Makefile.in .dirstamp |
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,12 @@ | ||
NUT external API integration scripts | ||
==================================== | ||
|
||
These directories hold scripts that integrate NUT with external APIs | ||
not yet natively supported. These may include Rest APIs, Web-based JSON, | ||
or any other protocol that doesn't have a supported subdriver. They | ||
a useful both for adding the integration to an existing install, or | ||
as a starting point for creating new integrations. | ||
|
||
- enphase: web-API based integration with Enphase's locally hosted IQ Gateway. | ||
Supports web-based login and token management, and maps JSON data | ||
to files consumed by the dummy-ups driver. |
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,192 @@ | ||
Enphase Monitor for dummy-ups | ||
============================= | ||
|
||
Enphase Monitor is a bash-script that queries the local IQ Gateway's | ||
API and makes "Grid On/Off" and Battery State-Of-Charge status | ||
available to NUT's dummy-ups driver by updating it's "port" file | ||
(see the dummy-ups(8) man page) | ||
|
||
enphase-monitor supports the following: | ||
|
||
- auto-login to enlighten.enphaseenergy.com to generate and auto-renew | ||
tokens for local Gateway API access (tokens are cached until expiration) | ||
- retains any (non-generated) values in the "port" file | ||
- gracefully handles split-phase or 3-phase input/output values | ||
- calculates derived values such as battery voltage, runtime and "ups.load" | ||
- handles no-comms with temporary rename of the port file (indicates | ||
"stale data") | ||
- dedicated config-file (for login, API query timing etc.) | ||
- enforces access permissions on files containing secrets | ||
- is fully self-documented (leading comment in the script included below) | ||
- GPLv3 licensed | ||
- minimal requirements: bash, jq, base64 and curl | ||
- includes systemd service for use with dummy-ups | ||
- includes a "TEST" mode that loops through various states and | ||
randomly expires the token | ||
|
||
Developed by Scott Shambarger <[email protected]> | ||
|
||
Documentation | ||
------------- | ||
The script is self-documented, but the following is the script's leading | ||
comments with installation instructions and configuration reference: | ||
---- | ||
Usage: enphase-monitor [ <options> ] -c <config> | <ups> | ||
|
||
-c <config> - use named config-file (or set $CONFIG_FILE) | ||
<ups> - use config-file /etc/ups/enphase-<ups>.conf (or set $UPS) | ||
|
||
<options> may include: | ||
-d - increase debug to stderr (2+ exposes secrets!) | ||
-h - show help and exit | ||
-s - perform one network check and exit | ||
-v - verbose output | ||
-x - set 'nocomms' and exit" | ||
|
||
<config> must contain: | ||
|
||
USERNAME=<enphase login> | ||
PASSWORD=<enphase password> | ||
SERIAL=<envoy serial#> | ||
PORT_FILE=<portfile from ups.conf, see below> | ||
|
||
and optionally (defaults shown): | ||
|
||
DISABLE_METERS= any value to disable power reporting | ||
ENVOY_HOST="envoy.local" ip/hostname of IQ Gateway on local network | ||
STATE_DIR="/var/lib/ups" writable directory for portfile/tokens | ||
POLLFREQ=60 seconds between API queries, min 5 | ||
POLLFREQALERT=20 seconds between API queries when on battery, min 5 | ||
TOKEN_FILE="enphase-<ups>.token" path defaults to STATE_DIR | ||
LOADKWH=768 max load/1kWh capacity, used for ups.load calculation | ||
0 disables calc (default based on IQ 5P rate 3.84kVA/5kWh) | ||
LOGIN_TIMEOUT=10 timeout (secs) for login/token gen, min 5 | ||
API_TIMEOUT=5 timeout (secs) for local ENVOY_HOST api access, min 2 | ||
|
||
Add section to /etc/ups/ups.conf for your <ups> name (replace <XXX>) | ||
|
||
[<ups>] | ||
driver = dummy-ups | ||
port = <STATE_DIR>/<portfile> this should be an absolute path! | ||
mode = dummy-once or name <portfile> with `.dev` extension | ||
desc = "Enphase IQ Gateway" | ||
|
||
<portfile> MUST EXIST before running the monitor (to ensure it's running | ||
on the correct machine). The following entries are optional but | ||
used if specified (defaults shown); other non-generated entries are retained. | ||
|
||
battery.charge.low: 20 | ||
battery.voltage.high: 86.4 | ||
battery.voltage.nominal: 76.8 | ||
battery.voltage.low: 68.5 | ||
device.mfr: Enphase Energy | ||
device.model: IQ Gateway | ||
|
||
The monitor uses the enphase <login> + <serial#> to retrieve a long-term | ||
token and saves it in STATE_DIR (token renewal is handled automatically) | ||
|
||
The monitor then queries the ENVOY_HOST (local IQ Gateway) API at POLLFREQ | ||
intervals to retrieve the envoy state, and updates <portfile>. | ||
Using values retrieved from the API and settings above, the monitor | ||
calculates the values ups.load, battery.voltage and battery.runtime | ||
|
||
enphase-monitor needs to have write access to <portfile>, so usually | ||
upsd hosting <ups> should be on local host, but shared filesystems may | ||
allow upsd to be remote. | ||
|
||
NOTE: if connections to ENVOY_HOST fail, <portfile> is renamed | ||
<portfile>-nocomms to trigger dummy-ups to show stale data. | ||
Either filename may exist on startup. | ||
|
||
Environment (optional): | ||
CONFIG_FILE - override default <config> | ||
UPS - set a default <ups> | ||
NUT_SYSCONFIG - default <config> directory (/etc/ups) | ||
NUT_LOCALSTATE - default for STATE_DIR (/var/lib/ups) | ||
|
||
=== INSTALL === | ||
|
||
Install required support programs: bash, base64, jq, and curl | ||
|
||
Create an entry in /etc/ups/ups.conf (as above) | ||
|
||
Copy enphase-monitor to some <INSTALL-DIR> (eg /usr/local/libexec) | ||
|
||
Create a <config> file with required variables. If only <ups> | ||
used to start the monitor, is looks for `/etc/ups/enphase-<ups>.conf` | ||
Ensure <config> can be read by monitor and is not world readable! | ||
|
||
Choose a NUT writable directory for STATE_DIR (default /var/lib/ups), | ||
and create an empty <portfile> there: | ||
|
||
$ touch <STATE_DIR>/<portfile> | ||
$ chown <nut-user>:<nut-group> <STATE_DIR>/<portfile> | ||
|
||
If using SELinux, ensure NUT's dummy-ups has access to the <portfile> | ||
(even in /var/lib/ups!) by adding a label, eg. | ||
|
||
$ semanage fcontext -a -t nut_conf_t <STATE_DIR>/<portfile> | ||
$ restoreconf -F <STATE_DIR>/<portfile> | ||
|
||
Create a systemd template file (replace <XXX> items) | ||
|
||
--- /etc/systemd/system/[email protected] --- | ||
[Unit] | ||
Description=Enphase API monitor for NUT dummy-ups %I | ||
PartOf=nut-driver.target | ||
Before=nut-driver@%i.service | ||
|
||
[Service] | ||
SyslogIdentifier=%N | ||
User=<NUT-USER> | ||
ExecStartPre=<INSTALL-PATH>/enphase-monitor -s %I | ||
ExecStart=<INSTALL-PATH>/enphase-monitor %I | ||
Type=exec | ||
Restart=always | ||
RestartSec=30 | ||
|
||
[Install] | ||
WantedBy=nut-driver@%i.service | ||
--- end of file --- | ||
|
||
Enable the instance for <ups> | ||
|
||
$ systemctl daemon-reload | ||
$ systemctl enable nut-driver@<ups> | ||
$ systemctl enable enphase-monitor@<ups> | ||
|
||
Restart NUT :) | ||
|
||
=== TEST MODE === | ||
|
||
If using the distributed `test.conf`, copy `test-ref.dev` to `test.dev` | ||
and then run: | ||
|
||
$ ./enphase-monitor -c test.conf | ||
|
||
`test.conf` sets "UPS=test" and "STATE_DIR=." and PORT_FILE="test.dev" | ||
(so token/portfiles are located in the current directory) | ||
It also sets "DEBUG=1" to show debug output (optional), and POLLFREQ | ||
to a few secs. | ||
|
||
"TEST" mode will loop (and randomly expire the token): | ||
|
||
online -> nocomms -> online -> onbatt -> lowbatt <- <repeat> | ||
|
||
A "TEST" mode <config> should set: | ||
|
||
TEST=1 <- required for "TEST" mode | ||
TEST_SESS=<json> use {"session_id":"some-value"} | ||
TEST_TOKEN=<web-token> JWT token, should have valid expires! | ||
TEST_RELAY=<json> ivp/ensemble/relay {"mains_oper_state":"@RELAY_STATE@"} | ||
TEST_LIVE=<json> ivp/livedata/status, {"soc":"@BATT_SOC@"} | ||
TEST_REPORTS=<json> ivp/meters/reports | ||
TEST_SECCTRL=<json> ivp/ensemble/secctrl, {"soc_recovery_exit":10} | ||
TEST_INFO=<xml> info.xml | ||
|
||
Output from real HTTP requests can be used (use "-d -d" to see output) | ||
for each of those APIs. Any empty TEST_XXXX value simulates a | ||
failed API query. | ||
---- |
Oops, something went wrong.