Created
November 13, 2020 22:33
-
-
Save tommie/ea2d1160bf0d7f6cdb0abfb45d12e26f to your computer and use it in GitHub Desktop.
i3wttr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | |
import json | |
import logging | |
import signal | |
import socket | |
import sys | |
import threading | |
import time | |
import urllib.parse | |
import urllib.request | |
class WttrClient(object): | |
UPDATE_INTERVAL = 3600 | |
URL = 'https://wttr.in/' | |
LINE_FORMAT = '1' | |
def __init__(self): | |
self._weather_data = {} | |
self._running = True | |
self._lock = threading.Lock() | |
self._cond = threading.Condition(self._lock) | |
self._thread = threading.Thread(target=self._run, name='WeatherUpdater') | |
self._thread.start() | |
def __enter__(self): | |
return self | |
def __exit__(self, *args): | |
self.close() | |
def get_local_weather(self): | |
with self._lock: | |
return self._weather_data.get('local', None) | |
def close(self): | |
with self._lock: | |
self._running = False | |
self._cond.notify_all() | |
self._thread.join() | |
@property | |
def _is_running(self): | |
return self._running and self._thread.is_alive() | |
def _run(self): | |
try: | |
while self._is_running: | |
try: | |
logging.debug('Entering read loop...') | |
with urllib.request.urlopen(self.URL + '?' + urllib.parse.urlencode({'format': self.LINE_FORMAT})) as f: | |
line = f.read().decode('utf-8').strip() | |
with self._lock: | |
self._weather_data['local'] = line | |
delay = self.UPDATE_INTERVAL | |
except Exception as ex: | |
logging.warning('Request failed: %s', ex) | |
delay = self.UPDATE_INTERVAL * 0.1 | |
with self._lock: | |
while self._is_running: | |
if self._cond.wait(delay): | |
break | |
except BaseException as ex: | |
logging.exception('Receive thread failed: %s', ex) | |
raise | |
def get_weather(client): | |
line = client.get_local_weather() | |
if not line: return [] | |
return [{ | |
'full_text': client.get_local_weather(), | |
'name': 'wttr', | |
'markup': 'none', | |
'instance': 'local', | |
}] | |
if __name__ == '__main__': | |
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) | |
# websocket-client causes SIGPIPE. | |
signal.signal(signal.SIGPIPE, signal.SIG_IGN) | |
with WttrClient() as wttr: | |
# Skip the first line which contains the version header. | |
print(sys.stdin.readline().strip(), flush=True) | |
# The second line contains the start of the infinite array. | |
print(sys.stdin.readline().strip(), flush=True) | |
for line in sys.stdin: | |
line = line.strip() | |
prefix = '' | |
# ignore comma at start of lines | |
if line.startswith(','): | |
line, prefix = line[1:], ',' | |
j = json.loads(line) | |
try: | |
j = list(get_weather(wttr)) + j | |
except BaseException as ex: | |
logging.exception('Failed to generate content (ignored): %s', | |
ex) | |
print(prefix + json.dumps(j), flush=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment