#!/bin/sh
# Monitors state of RE system powering this server.

# Writes simple HTML file to stdout with the stats.

# Directory to set/clear system energy-level flags in.
FLAGDIR=/run
# Last data: not guaranteed valid.
# Format:
#   2014/07/13T23:00:01Z AL 30 B1 12807 B2 13160 P 3337 BV 12588 ST OK ...
#   2016/06/05T16:30:06Z AL -1 B1 13135 B2 -1 P -1 BV -1 ST OK D e
#   2016/08/21T16:20:06Z AL 1175 B1 13315 B2 -1 P 16538 BV 13200 ST OK D r A1P 15567
#   2016/09/26T06:43:51Z AL 63 B1 12473 B2 -1 P 16278 BV 12309 ST OK D e A1P 801 B1T 17
LASTDATA=${FLAGDIR}/LASTDATA.flag
LASTSAMPLEDATA="`cat $LASTDATA`"
# Logging directory.
LOGDIR=/var/log/powermng

# Compute UTC YYYYMMDD date to match data logging.
UTCDATE="`date -u +%Y%m%d`"
# Yesterday in same format as UTCDATE (if easy to compute), else blank.
YESTERDAY="`date -u --date yesterday +%Y%m%d`"
# What should be today's log file.
LOGFILE=${LOGDIR}/${UTCDATE}.log
# Yesterday's log file, if it exists, else blank.
YLOGFILE=""
POSSIBLEYLF=${LOGDIR}/${YESTERDAY}.log
if [ -e ${POSSIBLEYLF} ]; then YLOGFILE=${POSSIBLEYLF}; fi

# Battery state-of-charge (SOC) full / floating.
BATTFULL=${FLAGDIR}/EXTERNAL_BATTERY_FULL.flag
BATTNOTFULL=${FLAGDIR}/EXTERNAL_BATTERY_NOTFULL.flag
# Battery state-of-charge (SOC) very high and charging / floating.
BATTNOTVHIGH=${FLAGDIR}/EXTERNAL_BATTERY_NOTVHIGH.flag
BATTVHIGH=${FLAGDIR}/EXTERNAL_BATTERY_VHIGH.flag
# Battery state-of-charge (SOC) (not) high.
BATTNOTHIGH=${FLAGDIR}/EXTERNAL_BATTERY_NOTHIGH.flag
BATTHIGH=${FLAGDIR}/EXTERNAL_BATTERY_HIGH.flag
# Battery state-of-charge (SOC) warning flag.
BATTLOW=${FLAGDIR}/EXTERNAL_BATTERY_LOW.flag
# Battery state-of-charge (SOC) warning flag.
BATTVLOW=${FLAGDIR}/EXTERNAL_BATTERY_VLOW.flag
# Battery dumping flag.
#BATTDUMPING=${FLAGDIR}/DUMPING.flag

# Nominal *usable* battery capacity (down to 50% SoC).
# Initial capacity as installed 2021-09 12V@220Ah = ~1320Wh.
BATTUCWH=1320

# Forecast tomorrow sunny flag.
FORECASTGOOD=${FLAGDIR}/FORECAST_PV_GEN_GOOD.flag

# UNUSED true if energy is going unused.
UNUSED=false
if [ 32000 -le "`echo ${LASTSAMPLEDATA} | awk '{print $23}'`" ]; then
    UNUSED=true
fi

# Grid-coupled storage log directory.
# Log files name of form: /var/log/Enphase/20211102.log
ELOGDIR=/var/log/Enphase/
ELOGFILE=${ELOGDIR}/${UTCDATE}.log
# Log records of form:
# 20211102T12:18Z consumption.readingTime 1635855482 consumption.net.wNow 21.574 consumption.total.wNow 689.288 production.wNow 667.715 storage.percentFull 32 storage.wNow -812 storage.readingTime 1635855290 storage.whNow 1562


# State-of-Charge percent crude estimate for the LA battery and whole system.
# Take first sample, which should be just after midnight and under light load,
# and assume 12.2V is 50% SoC (empty) and 12.7V is 100% SoC.
SOCPC="`awk < ${LOGFILE} '{print int(($5 - 12200)/5); exit;}'`"
# This applies temperature compensation (-30mV/C) to the thresholds.
#SOCPC="`awk < ${LOGFILE} '{print int(($5 - 12200 - 30*($19-20))/5); exit;}'`"

# State-of-Charge text for the LA battery and whole system.
SOC="UNKNOWN"
# Default is to not auto-refresh; with reasonable battery SoC it is enabled.
# Data samples only taken once every 10m by default.
if [ -e $BATTVLOW ]; then
    # Take shortest code path for VLOW.
    # No page auto-refresh.
    SOC="<strong><span style="color:red">VERY LOW</span></strong> (since `date -ur $BATTVLOW`)"
elif [ -e $BATTFULL ]; then
    SOC="<strong><span style="color:green">FULL</span></strong> (since `date -ur $BATTFULL`)"
    # If full then we can auto-refresh the page lots!
    OTHERMETA='<meta http-equiv=refresh content=300>'
elif [ -e $BATTVHIGH ]; then
    SOC="<strong><span style="color:green">VERY HIGH</span></strong> (since `date -ur $BATTVHIGH`, not FULL since `date -ur $BATTNOTFULL`)"
    # If (very) high then we can auto-refresh the page lots!
    OTHERMETA='<meta http-equiv=refresh content=450>'
elif [ -e $BATTHIGH ]; then
    SOC="<strong><span style="color:green">HIGH</span></strong> (since `date -ur $BATTHIGH`, not VHIGH since `date -ur $BATTNOTVHIGH`, not FULL since `date -ur $BATTNOTFULL`)"
    # If (very) high then we can auto-refresh the page lots!
    OTHERMETA='<meta http-equiv=refresh content=600>'
elif [ -e $BATTLOW ]; then
    # No page auto-refresh.
    SOC="<strong>LOW</strong> (since `date -ur $BATTLOW`)"
elif [ -e $BATTNOTHIGH ]; then
    SOC="<strong>OK</strong> (not VHIGH since `date -ur $BATTNOTVHIGH`, not FULL since `date -ur $BATTNOTFULL`)"
    # If OK then we can auto-refresh the page at a reasonable rate.
    OTHERMETA='<meta http-equiv=refresh content=1200>'
fi

# Generate HTML header in simplified form...
# DHD20160620: now 'html' ie HTML5; was <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
# Title of this page.
TITLE="Off-Grid Power For This Server"
TITLEFLAG=""
if [ "true" = "$UNUSED" ]; then
    TITLEFLAG="!! "
fi
# Series title text.
SERIESTITLE="Earth Notes"
# Canonical URL.
CANONURL=https://www.earth.org.uk/_off-grid-stats.html
# Home link to the index page.
cat << EOHEADER
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1">$OTHERMETA
<title>$TITLEFLAG$TITLE - $SERIESTITLE</title>
<meta property=og:title content="$TITLEFLAG$TITLE - $SERIESTITLE">
<meta property=og:description name=description content="Live and historical off-grid solar PV generation and use charts for 16WW.">
<meta name=twitter:card content=summary_large_image><meta name=twitter:site content=@EarthOrgUK><meta property=og:image content=https://www.earth.org.uk/out/hourly/battV.png>
<link href=$CANONURL rel=canonical>
<style>@media print{.noprint{display:none!important}}</style>
</head>
<body>
<div itemscope itemtype=http://schema.org/WebPage>
<h1 itemprop="headline name" style="font-family:sans-serif;margin:0;padding:0"><span style="color:green">$SERIESTITLE</span>: $TITLE</h1>
<p>(See <a href="_live-grid-tie-stats.html">grid-tie PV stats/graphs</a> and the <a href="off-grid-stats-historical.html">historical data</a>.)</p>
<p itemprop=description>This page shows how this server has been running 'off-grid' on solar PV, when known.</p>
EOHEADER

# Awk code for approx load Wh sum for the day, assuming 10m samples.
LWHSUMAWK='{sum += $9/6000} END {print int(sum)}'
# Awk code for approx primary-array Wh sum for the day, assuming 10m samples.
PWHSUMAWK='{sum += $17/6000} END {print int(sum)}'

echo "<p>Flags/status:</p><ul>"
echo "<li>Last sample: <code>${LASTSAMPLEDATA}</code></li>"
#echo "<li>The last sample time was "`echo ${LASTSAMPLEDATA} | awk '{print $1}' `".</li>"
#echo "<li>The last sampled (LA, Lead-Acid gel) battery voltage was "`echo ${LASTSAMPLEDATA} | awk '{print $5/1000}' `"V.</li>"
#echo "<li>The last sampled second (Li, LiFePO4) battery voltage was "`echo ${LASTSAMPLEDATA} | awk '{print $7/1000}' `"V.</li>"
if [ "true" = "$UNUSED" ]; then
    # Voc ~37.7V, Vmpp ~30.6V.
    echo "<li>Primary array over Vmp (~32V): <strong>energy going unused!</strong></li>"
fi
echo "<li>Whole-system state-of-charge: ${SOC}, ~${SOCPC}% effective capacity overnight.</li>"
#echo "<li>Net charge via primary controller today `/usr/local/bin/ssmppt15l-modbus -c`mAh.</li>"
echo "<li>Approx Wh primary-array generation so far today (UTC): `awk < ${LOGFILE} "$PWHSUMAWK"`</li>"
echo "<li>Approx Wh load so far today (UTC): `awk < ${LOGFILE} "$LWHSUMAWK"`</li>"
#echo "<li>Samples so far today (UTC): `wc -l < ${LOGFILE}`</li>"
if [ ! -z "${YLOGFILE}" -a -e "${YLOGFILE}" ]; then
    echo "<li>Approx Wh primary-array generation yesterday (UTC): `awk < ${YLOGFILE} "$PWHSUMAWK"`</li>"
    echo "<li>Approx Wh load yesterday (UTC): `awk < ${YLOGFILE} "$LWHSUMAWK"`</li>"
#    echo "<li>Samples yesterday (UTC): `wc -l < ${YLOGFILE}`</li>"
fi
#if [ -e $BATTDUMPING ]; then
    #echo "<li>Battery dumping (moving load off-grid) since `date -ur $BATTDUMPING`.</li>"
#fi
#echo "<li>Dump status flag: '`echo ${LASTSAMPLEDATA} | awk '{print $15}'`'.</li>"
#echo "<li>Load power: `echo ${LASTSAMPLEDATA} | awk '{print $9/1000}'`W.</li>"
#echo "<li>System load `uptime | awk '{print $NF}'`.</li>"
if [ -e $FORECASTGOOD ]; then
    echo "<li>Forecast PV generation tomorrow: good.</li>"
fi
if [ -e _gridCarbonIntensityGB.red.flag ]; then
    echo "<li>Grid carbon intensity is high.</li>"
fi
if [ -e /var/log/Enphase/EXPORT.flag ]; then
    echo "<li>Grid-tie system is exporting to grid.</li>"
fi
# Usable SoC.
USABLESOC=`echo $LASTSAMPLEDATA | awk '"UC"==$20{print $21}'`
USABLEWH=`expr \( $BATTUCWH \* $USABLESOC \) / 100`
if [ "" != "$USABLESOC" ]; then
    # If actually full then maybe we aren't working this hard enough!
    echo "<li><meter min=0 max=100 low=60 high=99 optimum=99 value=$USABLESOC title=$USABLESOC%>$USABLESOC%</meter> usable off-grid charge (${USABLESOC}%/${USABLEWH}Wh).</li>"
fi
# Grid-coupled storage.
# Be prepared to use last record from today, which limits staleness.
# 20211102T12:18Z consumption.readingTime 1635855482 consumption.net.wNow 21.574 consumption.total.wNow 689.288 production.wNow 667.715 storage.percentFull 32 storage.wNow -812 storage.readingTime 1635855290 storage.whNow 1562
if [ -s "$ELOGFILE" ]; then
    ELOGLAST=`tail -1 < $ELOGFILE`
    ELOGLASTWh=`echo $ELOGLAST | awk '"storage.whNow" == $16 {print $17}'`
    ELOGLASTPC=`echo $ELOGLAST | awk '"storage.percentFull" == $10 {print $11}'`
    if [ "" != "$ELOGLASTWh" -a "" != "$ELOGLASTPC" ]; then
        echo "<li><meter min=0 max=100 value=$ELOGLASTPC title=$ELOGLASTPC%>$ELOGLASTPC%</meter> grid-coupled charge (${ELOGLASTPC}%/${ELOGLASTWh}Wh).</li>"
    fi
fi
#if [ "" != "$ELOGLASTWh" -a "" != "$USABLEWH" ]; then
#    TOTALEWh="`expr $ELOGLASTWh + $USABLEWH`"
#    echo "<li>Total electrical storage: ${TOTALEWh}Wh.</li>"
#fi
echo "</ul>"

# Insert graph of battery voltage (if present).
if [ -f out/hourly/battV.png ]; then
cat << EOGRAPH
<p style="text-align:center"><img src="//www.earth.org.uk/out/hourly/battV.png" width="640" height="480" style="max-width:100%;height:auto" title="graph of battery voltage at controller and RPi, power in and out" alt="graph of battery voltage at controller and RPi, power in and out" decoding="async" /></p>
EOGRAPH
else
    echo "<p>No live graph available...</p>"
fi

# Insert calendar-month graph of battery voltage (if present).
if [ -f out/daily/battV-cm.png ]; then
cat << EOGRAPH2
<p style="text-align:center"><img src="//www.earth.org.uk/out/daily/battV-cm.png" width="640" height="240" style="max-width:100%;height:auto" title="graph of monthly battery voltage and power out" alt="graph of monthly battery voltage and power out" loading="lazy" /></p>
EOGRAPH2
else
    echo "<p>No calendar-month graph available...</p>"
fi

# Insert all-time link/thumbnail (if present).
if [ -f out/monthly/EOall-tn.png ]; then
    ALLDAT=out/daily/PVEOalldailykWh+smoothed.dat
    LASTSMOOTHED=""
    if [ -f ${ALLDAT} ]; then
        LASTSMOOTHED=" (to `tail -1 ${ALLDAT}`)"
    fi
cat << EOGRAPH3
<p style="text-align:center">Daily <a href="${ALLDAT}">off-grid main array PV generation since 2016-08 ${LASTSMOOTHED}</a>: <a href="//www.earth.org.uk/out/monthly/EOall.png"><img src="//www.earth.org.uk/out/monthly/EOall-tn.png" alt="Daily PV generation 2016/08" title="Daily PV generation since 2016/08" width="80" height="60" style="vertical-align:middle" loading="lazy" /></a></p>
EOGRAPH3
fi

# Wrap up the HTML page.
cat << FOOTER
<footer class=noprint>
Also:
<ul>
<li><a href="//www.earth.org.uk/out/daily/B1T.png">Recent battery temperature vs outside</a>.</li>
<li><a href="note-on-data.html#LASoC">SoC chart</a>.</li>
<li><a href="note-on-data.html">16WW data</a>.</li>
</ul>
</footer>
<footer><p><small>
This page was automatically generated on ${UTCDATE} (<span itemprop=dateModified>`date -u +%Y-%m-%dT%H:%M:%SZ`</span>).<br />
First published <span itemprop=datePublished>2007-08-06</span>.<br />
Copyright &copy; <a href="http://d.hd.org/"><span itemprop=author>Damon Hart-Davis</span></a> <span itemprop=copyrightYear>2007</span> to $(cat .work/copyrightYearLatest.txt). [<a href="/">home</a>]<br />
</small></p></footer>
</div></body></html>
FOOTER

exit 0
