Esempio n. 1
0
def read_forecast():
    """Read the weather forecast."""
    watcher.feed()

    start_regex = ure.compile(r'timeseries')
    continue_regex = ure.compile(r'\},\{')
    hour_regex = ure.compile(r'next_1_hours')
    precip_regex = ure.compile(r'precipitation_amount\":(\d+\.\d)')
    date_regex = ure.compile(r'time\":\"(\d+-\d+-\d+)T')
    # Large chunk size more performant as less regex's run. Must be less than
    # characters in one timeseries element in the response.
    chunkSize = 1000
    windowSize = chunkSize * 2

    rain_today_mm, rain_tomorrow_mm = (0.0, 0.0)
    window = memoryview(bytearray(windowSize))
    emptyChunk = bytes(chunkSize)
    periodFound, hourFound, precipFound, dateFound = False, False, False, False

    File.logger().info('%s - Req to: %s', clock.timestamp(), _FORECAST_URL)
    with requests.get(_FORECAST_URL, headers=secrets.HEADER) as response:
        File.logger().info('%s - HTTP status: %d', clock.timestamp(),
                           response.status_code)
        if response.status_code == 200 or response.status_code == 203:
            for chunk in response.iter_content(chunkSize):
                # Populate response window
                window[0:chunkSize] = window[chunkSize:windowSize]
                if len(chunk) == chunkSize:
                    window[chunkSize:windowSize] = chunk
                else:
                    # last chunk is short
                    window[chunkSize:windowSize] = emptyChunk
                    window[chunkSize:chunkSize + len(chunk)] = chunk
                # print(window)
                windowBytes = bytes(window)  # regex requires bytes
                # Gather precipitation data
                if continue_regex.search(windowBytes) or\
                   start_regex.search(windowBytes):
                    periodFound = True
                if periodFound and hour_regex.search(windowBytes):
                    hourFound = True
                if periodFound and not dateFound:
                    if (dateGroup := date_regex.search(windowBytes)):
                        dateFound = True
                        date = dateGroup.group(1).decode()
                if hourFound and not precipFound:
                    if (precipGroup := precip_regex.search(windowBytes)):
                        precipFound = True
                        mm = float(precipGroup.group(1))
                if dateFound and precipFound:
                    periodFound, hourFound = False, False
                    dateFound, precipFound = False, False
                    # print(date, mm)
                    if clock.greater_than_tommorow(date):
                        break
                    elif clock.equal_to_today(date):
                        rain_today_mm += mm
                    else:
                        rain_tomorrow_mm += mm
Esempio n. 2
0
def send(rain_data, battery_volts):
    """Send weather and system information to Thingspeak."""
    watcher.feed()
    data_tuple = rain_data.get_data()
    url = _URL.format(key=secrets.THINGSPEAK_API_KEY,
                      *data_tuple,
                      volts=battery_volts,
                      status=int(not rain_data.rainfall_occurring()))
    File.logger().info('%s - Req to: %s', clock.timestamp(), url)
    with requests.get(url) as response:
        File.logger().info('%s - HTTP status: %d', clock.timestamp(),
                           response.status_code)
        if response.status_code != 200:
            raise ValueError("HTTP status %d" % response.status_code)
Esempio n. 3
0
def read_rainfall():
    """Read todays rainfall."""
    watcher.feed()
    rain_last_hour_mm, rain_today_mm = (0.0, 0.0)
    File.logger().info('%s - Req to: %s', clock.timestamp(), _RAIN_URL)
    with requests.get(_RAIN_URL) as response:
        File.logger().info('%s - HTTP status: %d', clock.timestamp(),
                           response.status_code)
        if response.status_code == 200:
            first_line = True
            for line in response.iter_lines():
                if first_line:
                    first_line = False
                    continue
                gc.collect()
                text = line.decode('utf-8', 'ignore').strip()
                values = text.split(',')
                if len(values) == 3:
                    day = int(values[1].split('/')[0])
                    if day == clock.day_of_month():
                        mm = float(values[2])
                        rain_today_mm += mm
                        rain_last_hour_mm = mm
        else:
            raise ValueError("HTTP status %d" % response.status_code)

    File.logger().info('%s - Last hour %.1fmm, today %.1fmm',
                       clock.timestamp(), rain_last_hour_mm, rain_today_mm)
    print('Last hour %.1fmm, today %.1fmm' %
          (rain_last_hour_mm, rain_today_mm))
    return round(rain_last_hour_mm), round(rain_today_mm)
Esempio n. 4
0
def connect():
    """Connect to WiFi."""
    secs = WIFI_DELAY
    start = ticks_ms()

    sta_if = network.WLAN(network.STA_IF)
    sta_if.active(True)
    sta_if.connect(secrets.WIFI_SSID, secrets.WIFI_PASSPHRASE)

    while secs >= 0 and not sta_if.isconnected():
        sleep(CHECK_INTERVAL)
        secs -= CHECK_INTERVAL

    if sta_if.isconnected():
        File.logger().info('%s - Connected, address: %s in %d ms',
                           clock.timestamp(),
                           sta_if.ifconfig()[0], ticks_diff(ticks_ms(), start))
        return True
    else:
        sta_if.active(False)
        File.logger().error('%s - WiFi did not connect' % clock.timestamp())
        return False
Esempio n. 5
0
        self.rain_last_hour_mm, self.rain_today_mm = weather

    def set_from_forecast(self, forecast):
        """Set rain data from forecast."""
        self.rain_forecast_today_mm, self.rain_forecast_tomorrow_mm = forecast


def get_rain_data():
    """Get the rain data retrieved from the weather service."""
    data = RainData()
    data.set_from_weather(read_rainfall())
    data.set_from_forecast(read_forecast())
    return data


@retry(Exception, tries=6, delay=2, backoff=2, logger=File.logger())
def read_rainfall():
    """Read todays rainfall."""
    watcher.feed()
    rain_last_hour_mm, rain_today_mm = (0.0, 0.0)
    File.logger().info('%s - Req to: %s', clock.timestamp(), _RAIN_URL)
    with requests.get(_RAIN_URL) as response:
        File.logger().info('%s - HTTP status: %d', clock.timestamp(),
                           response.status_code)
        if response.status_code == 200:
            first_line = True
            for line in response.iter_lines():
                if first_line:
                    first_line = False
                    continue
                gc.collect()
Esempio n. 6
0
"""Thingspeak upload."""
from retrier import retry
import requests
from file_logger import File
import watcher
import secrets
import clock

_URL = ('https://api.thingspeak.com/update'
        '?api_key={key}&field1={}&field2={}&field3={}&field4={}'
        '&field5={volts}&field6={status}')


@retry(Exception, tries=5, delay=2, backoff=2.0, logger=File.logger())
def send(rain_data, battery_volts):
    """Send weather and system information to Thingspeak."""
    watcher.feed()
    data_tuple = rain_data.get_data()
    url = _URL.format(key=secrets.THINGSPEAK_API_KEY,
                      *data_tuple,
                      volts=battery_volts,
                      status=int(not rain_data.rainfall_occurring()))
    File.logger().info('%s - Req to: %s', clock.timestamp(), url)
    with requests.get(url) as response:
        File.logger().info('%s - HTTP status: %d', clock.timestamp(),
                           response.status_code)
        if response.status_code != 200:
            raise ValueError("HTTP status %d" % response.status_code)
Esempio n. 7
0
def run():
    """Main entry point to execute this program."""
    sleep_enabled = _sleep_enabled()
    try:
        File.logger().info('%s - Awake: %s', clock.timestamp(),
                           machine.wake_reason())
        rainfall = False
        next_wake = config.RTC_ALARM
        battery_volts = _battery_voltage()

        if not sleep_enabled:
            watcher.disable()

        if wifi.connect():
            _resetConnectCount()
            rain_data = weather.get_rain_data()
            rainfall = rain_data.rainfall_occurring()
            thingspeak.send(rain_data, battery_volts)

            if rainfall:
                File.logger().info('%s - System OFF', clock.timestamp())
                _system_off()
            else:
                File.logger().info('%s - System ON', clock.timestamp())
                _system_on()
        else:
            if _incrementConnectCount() > 5:
                # Give up trying to connect to WiFi
                _resetConnectCount()
            else:
                File.logger().info('%s - Set one minute sleep, attempts %d',
                                   clock.timestamp(), _getConnectCount())
                next_wake = config.SLEEP_ONE_MINUTE

    except Exception as ex:
        # Catch exceptions so that device goes back to sleep HTTP calls
        # fail with exceptions.
        File.logger().exc(ex, '%s - Error', clock.timestamp())
    finally:
        try:
            wifi.disconnect()
        except Exception as ex:
            File.logger().exc(ex, '%s - WIFI disconnect error',
                              clock.timestamp())

        if sleep_enabled:
            File.logger().info('%s - Sleeping...', clock.timestamp())
            File.close_log()
            _sleep_until(next_wake)
        else:
            File.close_log()