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)
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
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)
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
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()
def headers(self, url, params=None, method=urlfetch.GET, other_headers = None): oauth_headers = {'oauth_nonce': nonce(), 'oauth_callback': self.callback_url, 'oauth_signature_method': "HMAC-SHA1", 'oauth_timestamp': str(clock.timestamp(clock.now())), 'oauth_consumer_key': self.consumer_key, 'oauth_version': '1.0'} if other_headers: oauth_headers.update(other_headers) if self.access_token: oauth_headers['oauth_token'] = self.access_token oauth_headers['oauth_signature'] = self.sign(url, method, params, oauth_headers) oauth_headers['realm'] = '' return {'Authorization': 'OAuth ' + ', '.join('%s="%s"' % (key, _encode(value)) for key, value in oauth_headers.items())}
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 else: raise ValueError('HTTP status %d' % response.status_code) File.logger().info('%s - Today %.1fmm, tomorrow %.1fmm', clock.timestamp(), rain_today_mm, rain_tomorrow_mm) print('Today %.1fmm, tomorrow %.1fmm' % (rain_today_mm, rain_tomorrow_mm)) return round(rain_today_mm), round(rain_tomorrow_mm)