This is my first try at an extension. This is one file to place in user (/usr/share/weewx/user), with info in the header for the skin.conf and Standard/index.html.tmpl changes to place the data into a report. Please do give feedback! Thanks, Rich
-- You received this message because you are subscribed to the Google Groups "weewx-user" group. To unsubscribe from this group and stop receiving emails from it, send an email to weewx-user+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
# Copyright 2018 Rich Altmaier richa...@yahoo.com # # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # """ This search list extension calculates tags, one is a time interval, and one is a duration, of the most recent rainstorm: 'last_rain_storm': finds the last rain interval, allowing stats to be calculated over that interval. We search back to find the storm end, which might be still on-going, and search still further back to find the rain start (after a dry period) 'storm_duration': duration in days ******************************************************************************* Usage: 1) copy this file to the user directory 2) add the option search_list_extensions in the /etc/weewx/skins/Standard/skin.conf configuration file, adding the name of this extension. When you're done, it will look like this: [CheetahGenerator] search_list_extensions = user.storm.RainStorm 3) in the html template /etc/weewx/skins/Standard/index.html.tmpl in the Current Conditions section, perhaps after Inside Temperature, add this table row: <tr> <td class="stats_label">last storm total rain, <br />ending $last_rain_storm.end <br />of duration $storm_duration days</td> <td class="stats_data">$last_rain_storm.rain.sum</td> </tr> You can also use other tags such as $last_rain_storm.outTemp.max for the max temperature, or $last_rain_storm.rain.sum for the total rainfall in the last storm ******************************************************************************* """ import datetime import time from weewx.cheetahgenerator import SearchList from weewx.tags import TimespanBinder from weeutil.weeutil import TimeSpan class RainStorm(SearchList): def __init__(self, generator): SearchList.__init__(self, generator) def get_extension_list(self, timespan, db_lookup): """Returns a search list extension with one addition. Parameters: timespan: An instance of weeutil.weeutil.TimeSpan. This will hold the start and stop times of the domain of valid times. db_lookup: This is a function that, given a data binding as its only parameter, will return a database manager object. """ # first find end of most recent storm. # may be still ongoing (e.g. rain in most recent record). # parameterized with: storm_dry_period = 24 # number of hours of no rain to delimit # storm end. storm_in_past = 20 # number of days to look back for a storm storm_now = int(time.time() ) # right now this second # can choose other epoch value for testing! # set now to a testing time, to look at various past rain records #StructTime = time.strptime('2017-02-05 16:00:00-PST', '%Y-%m-%d %H:%M:%S-%Z') # 0.80in, from 2017-02-01 to 2017-02-04 #StructTime = time.strptime('2017-01-05 16:00:00-PST', '%Y-%m-%d %H:%M:%S-%Z') # 1.58 in, from 2017-01-01 to 2017-01-05 #storm_now = int(time.mktime(StructTime)) wx_db_mgr = db_lookup() c = wx_db_mgr.connection.cursor() #caching method: # temporary table RainStormcache, # fields: entryEpoch, startE, endE all are Integer # just one record # when entryEpoch+N > now, then the entry is recent enough CacheValid = False NowEpoch = int(time.time() ) c.execute("PRAGMA table_info(RainStormcache)") CacheInfo = c.fetchone() if CacheInfo is not None: c.execute("select count(*) from RainStormcache") #print "number of rows in storm cache ", int(c.fetchone()[0]) c.execute("SELECT * from RainStormcache") CacheData = c.fetchone() #print "CacheData ", CacheData CacheEpoch = CacheData[0] StartTepoch = CacheData[1] storm_end_epoch = CacheData[2] if CacheEpoch+30 > NowEpoch: # if aged cache is still ahead of now... CacheValid = True #print "Cache valid ", CacheValid, " NowEpoch ", NowEpoch, " start ", StartTepoch, " end ", storm_end_epoch #end cache fetch. CacheValid tells us if data found if CacheValid == False: c.execute("drop table IF EXISTS RainWeek") c.execute(""" create TEMPORARY table RainWeek as select dateTime, rain, datetime(dateTime, 'unixepoch', 'localtime') as dateString, dateTime as startEpoch, dateTime as endEpoch from archive where dateTime >= :now - (24 * 3600 * :past ) AND dateTime <= :now ORDER BY dateTime """, {"now": storm_now, "past": storm_in_past}) wx_db_mgr.connection.commit() c.execute("select count(*) from temp.RainWeek") print "number of rows in storm viewing table is ", int(c.fetchone()[0]) c.execute(""" -- find end of first wet period ending at or before storm_now -- or storm_now if has been wet within storm_dry_period hours of now. select CASE WHEN ((select MAX(dateTime) as RainEndT from temp.RainWeek where rain <> 0 ) + (:dry * 3600)) > :now THEN :now -- storm is still on-going ELSE -- find most recent last wet time (select MAX(dateTime) from temp.RainWeek where rain <> 0 OR rowid = (select rowid from (select MAX(dateTime) as LastWetT, rowid from temp.RainWeek where rain <> 0 ) ) + 1 ) --above is record after latest wet interval, or last wet interval --note: DB dateTime stamp is of beginning of interval END as RainEndTime """, {"now": storm_now, "past": storm_in_past, "dry": storm_dry_period}) EndT = c.fetchone() if EndT[0] is None: StormFound = False print "No storm in past", storm_in_past, " days" storm_end_epoch = storm_now + storm_in_past*24*3600 # next query will also get null else: StormFound = True #print "most recent wet record ", EndT storm_end_epoch = int(EndT[0]) print "Storm end epoch ", storm_end_epoch, ", local ", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(storm_end_epoch)) if StormFound: c.execute(""" -- find dry period preceeding storm_end_epoch. -- look at storm_dry_period hours of records preceeding each record to find -- a preceeding sequence of 0 rain. select MAX(dateTime) as RainStartT from (select f1.rowid AS ROWID, sum(f2.rain) as RunningRain, f1.dateTime FROM temp.RainWeek f1 INNER JOIN temp.RainWeek f2 ON f1.dateTime > f2.dateTime AND f2.dateTime >= (f1.dateTime - ( :dry *3600)) GROUP BY ROWID HAVING RunningRain = 0 and f1.dateTime < :ending -- Rain end time ) """, {"ending": storm_end_epoch, "dry": storm_dry_period}) row = c.fetchone() if row[0] is not None: StartTepoch = int(row[0]) print "Storm start time epoch ", StartTepoch, ", local ", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(StartTepoch)) else: StartTepoch = storm_now + storm_in_past*24*3600 print "No dry start found in search interval" else: # no rain :-( StartTepoch = storm_now -1 storm_end_epoch = storm_now #now stuff data into the cache c.execute("drop table IF EXISTS RainStormcache") c.execute("""create TEMPORARY table RainStormcache ( CacheEpoch integer not null, CacheStartE integer not null, CacheEndE integer not null) """) c.execute("""INSERT INTO RainStormcache VALUES ( :stamp , :start , :end ) """, {"stamp": int(time.time()), "start": StartTepoch, "end": storm_end_epoch}) #end of if not CacheValid #have storm delimiting times, form TimespanBinder # Form a TimespanBinder object, using the time span we just # calculated: LastStorm = TimespanBinder(TimeSpan(StartTepoch, storm_end_epoch), db_lookup, formatter=self.generator.formatter, converter=self.generator.converter, skin_dict=self.generator.skin_dict) StormDurationDays = ( storm_end_epoch - StartTepoch +(3600*24)) / (3600*24) # Now create a small dictionary search_list_extension = { 'last_rain_storm' : LastStorm, 'storm_duration' : StormDurationDays} # Finally, return our extension as a list: return [search_list_extension]