1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
Enphase Monitor for dummy-ups
=============================
The 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 its "port" file
(see the linkman:dummy-ups[8] man page).
NOTE: Location where we write the data file for `dummy-ups` to process and
publish should not be in a temporary file system, as we may inherit some
manually added data points in the file that we want to retain across reboots.
On systems with wear-prone storage (flash/SSD), you can fiddle with a
"manually-made" file that would be copied into a tmpfs mount point, and
have the script (and `dummy-ups` driver) pick up *that* tmpfs location
with initially inherited data points.
The `enphase-monitor` script 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 configuration 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)
- GPLv2 licensed
- minimal requirements: `bash`, `jq`, `base64` and `curl`
- includes a sample instantiated 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 <devel@shambarger.net>
Documentation
-------------
The script is self-documented, but the following is the script's leading
comments block 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="$NUT_STATEPATH" (e.g. "/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 (NUT_CONFPATH, e.g. /etc/ups)
NUT_LOCALSTATE - default for STATE_DIR (NUT_STATEPATH, e.g. /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> (e.g. /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, e.g.
$ 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/enphase-monitor@.service ---
[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.
----
|