class SelfUpdate(WeatherModule): """ Self update module This module detects git updates and updates itself. example config: { "module": "SelfUpdate", "config": { "check_interval": 86400 } } """ def __init__(self, fonts, location, language, units, config): self.check_interval = None if isinstance(config["check_interval"], int): self.check_interval = config["check_interval"] if self.check_interval is None: raise ValueError(__class__.__name__) self.timer_thread = RepeatedTimer(self.check_interval, self_update) self.timer_thread.start() def quit(self): if self.timer_thread: self.timer_thread.quit()
def __init__(self, fonts, location, language, units, config): self.check_interval = None if isinstance(config["check_interval"], int): self.check_interval = config["check_interval"] if self.check_interval is None: raise ValueError(__class__.__name__) self.timer_thread = RepeatedTimer(self.check_interval, self_update) self.timer_thread.start()
def __init__(self, fonts, location, language, units, config): super().__init__(fonts, location, language, units, config) if "prefectures" in config and "city" in config: self.prefectures = config["prefectures"] self.city = config["city"] elif self.location["address"]: self.city, self.prefectures = self.location["address"].split(",") if not self.prefectures or not self.city: raise ValueError(__class__.__name__) # start weather alerts thread self.timer_thread = RepeatedTimer(600, weather_alerts, [self.prefectures, self.city]) self.timer_thread.start()
def main(): """main program """ # initialize logger parser = argparse.ArgumentParser(description=__file__) parser.add_argument("--debug", "-d", action="store_const", const=True, default=False) parser.add_argument("--screenshot", "-s") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO, stream=sys.stdout, format="%(asctime)s %(levelname)s %(message)s") # initialize thread timer_thread = None # initialize modules modules = [] # initialize restart flag restart = False # initialize reboot flag reboot = False try: # load config file file = "/boot/WeatherPi.json" if not os.path.exists(file): file = "{}/config.json".format(sys.path[0]) with open(file, "r") as f: config = json.loads(f.read()) logging.info("%s loaded", file) # initialize locale, gettext language = config["locale"].split("_")[0] locale.setlocale(locale.LC_ALL, config["locale"]) trans = gettext.translation("messages", localedir="{}/locale".format(sys.path[0]), languages=[language], fallback=True) trans.install() # initialize address, latitude and longitude if "google_api_key" in config and config["google_api_key"]: results = geocode(config["google_api_key"], language, config["address"], config["latitude"], config["longitude"]) if results is not None: latitude, longitude, address = results config["latitude"] = latitude config["longitude"] = longitude config["address"] = address logging.info("location: %s,%s %s", latitude, longitude, address) # start weather forecast thread timer_thread = RepeatedTimer(300, weather_forecast, [ config["openweather_appid"], config["latitude"], config["longitude"], language, config["units"] ]) timer_thread.start() # initialize pygame if "DISPLAY_NO" in config: os.putenv("DISPLAY", config["DISPLAY_NO"]) if "SDL_FBDEV" in config: os.putenv("SDL_FBDEV", config["SDL_FBDEV"]) pygame.init() pygame.mouse.set_visible(False) if pygame.display.mode_ok(config["display"]): display = screen = pygame.display.set_mode(config["display"]) scale = None else: display = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) screen = pygame.Surface(config["display"]) display_w, display_h = display.get_size() screen_w, screen_h = screen.get_size() if display_w / screen_w * screen_h <= display_h: scale = (display_w, int(display_w / screen_w * screen_h)) else: scale = (int(display_h / screen_h * screen_w), display_h) DISPLAY_SLEEP = pygame.USEREVENT + 1 DISPLAY_WAKEUP = pygame.USEREVENT + 2 RESTART = pygame.USEREVENT + 3 REBOOT = pygame.USEREVENT + 4 logging.info("pygame initialized. display:%s screen:%s scale:%s", display.get_size(), screen.get_size(), scale) # load modules location = { "latitude": config["latitude"], "longitude": config["longitude"], "address": config["address"] } units = config["units"] fonts = config["fonts"] modules = [] for module in config["modules"]: name = module["module"] conf = module["config"] if name in globals(): logging.info("load built-in module: %s", name) mod = (globals()[name]) else: logging.info("load external module: %s", name) mod = getattr( importlib.import_module("modules.{}".format(name)), name) modules.append((mod)(fonts, location, language, units, conf)) logging.info("modules loaded") # main loop display_wakeup = True last_hash_value = None running = True while running: # weather data check weather = timer_thread.get_result() updated = False if weather: hash_value = timer_thread.get_hash_value() if last_hash_value != hash_value: logging.info("weather data updated") last_hash_value = hash_value updated = True # update screen for module in modules: module.draw(screen, weather, updated) # update display if display_wakeup: if scale: # fit to display display.blit(pygame.transform.scale(screen, scale), (0, 0)) pygame.display.flip() # event check for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == RESTART: running = False restart = True elif event.type == REBOOT: running = False reboot = True elif event.type == DISPLAY_SLEEP: if display_wakeup: display.fill(pygame.Color("black")) pygame.display.flip() display_wakeup = False elif event.type == DISPLAY_WAKEUP: if not display_wakeup: last_hash_value = None display_wakeup = True time.sleep(1) except Exception as e: logging.error(e, exc_info=True) finally: if args.screenshot: pygame.image.save(display, args.screenshot) if timer_thread: timer_thread.quit() for module in modules: module.quit() pygame.quit() if restart: logging.info("restarting..") os.execl(sys.executable, sys.executable, *sys.argv) if reboot: os.system('sudo reboot') sys.exit()
class JMAAlerts(WeatherModule): """ 気象庁 (Japan Meteorological Agency) alerts module example config: { "module": "JMAAlerts", "config": { "rect": [x, y, width, height], "prefectures": "東京都", "city": "中央区" } } 気象庁防災情報XMLフォーマット形式電文の公開(PULL型)で公開されているAtomフィードのうち、 "高頻度フィード/随時"のフィードに掲載された都道府県のデータフィードから、指定した市区町村の 注意報、警報、特別警報を取得し、表示する。 参考:http://xml.kishou.go.jp/xmlpull.html """ def __init__(self, fonts, location, language, units, config): super().__init__(fonts, location, language, units, config) if "prefectures" in config and "city" in config: self.prefectures = config["prefectures"] self.city = config["city"] elif self.location["address"]: self.city, self.prefectures = self.location["address"].split(",") if not self.prefectures or not self.city: raise ValueError(__class__.__name__) # start weather alerts thread self.timer_thread = RepeatedTimer(600, weather_alerts, [self.prefectures, self.city]) self.timer_thread.start() def quit(self): if self.timer_thread: self.timer_thread.quit() def draw(self, screen, weather, updated): if weather is None: message = "Waiting data..." else: result = self.timer_thread.get_result() if result: message = ",".join(list(map(_, result))) else: message = "" self.clear_surface() if message: logging.info("%s: %s", __class__.__name__, message) if "特別警報" in message: color = "violet" elif "警報" in message: color = "red" elif "注意報" in message: color = "yellow" else: color = "white" for size in ("large", "medium", "small"): w, h = self.text_size(message, size, bold=True) if w <= self.rect.width and h <= self.rect.height: break self.draw_text(message, (0, 0), size, color, bold=True, align="center") self.update_screen(screen)
def start_sensor_thread(self, interval, function, args=None, kwargs=None): """start sensor thread """ self.sensor_thread = RepeatedTimer(interval, function, args, kwargs) self.sensor_thread.start()
class TemperatureModule(WeatherModule): """ Temperature and humidity sensor module class """ def __init__(self, fonts, location, language, units, config): super().__init__(fonts, location, language, units, config) self.sensor_thread = None self.last_hash_value = None # histrical data self.window_size = 6 * 60 now = datetime.datetime.now() self.times = [ now - datetime.timedelta(minutes=x) for x in range(0, self.window_size) ] self.times.reverse() self.temperatures = [np.nan] * self.window_size self.humidities = [np.nan] * self.window_size # logging setup self.logfile = None if "logfile" in config: self.logfile = config["logfile"] if not os.path.isfile(self.logfile): with open(self.logfile, mode="w") as f: f.write("Date,Temperature,Humidity¥n") # glaph module setup self.graph_module = None if "graph_rect" in config: config["rect"] = config["graph_rect"] self.graph_module = TemperatureGraph(fonts, location, language, units, config) def start_sensor_thread(self, interval, function, args=None, kwargs=None): """start sensor thread """ self.sensor_thread = RepeatedTimer(interval, function, args, kwargs) self.sensor_thread.start() def get_sensor_value(self): """read last sensor value """ # No result yet result = self.sensor_thread.get_result() if result is None: logging.info("%s: No data from sensor", __class__.__name__) self.last_hash_value = None return (None, None, False) (celsius, humidity) = result # logging only once a minute dt = datetime.datetime.now() if dt.second == 0: self.times = self.times[1:] + [dt] if self.temperatures is not None: celsius = np.nan if celsius is None else float( celsius if self.units == "metric" else Utils.fahrenheit(celsius)) self.temperatures = self.temperatures[1:] + [celsius] else: celsius = None if self.humidities is not None: humidity = np.nan if humidity is None else float(humidity) self.humidities = self.humidities[1:] + [humidity] else: humidity = None if self.logfile: with open(self.logfile, mode="a") as f: f.write("{},{},{}\n".format(dt, celsius, humidity)) # Has the value changed hash_value = self.sensor_thread.get_hash_value() if self.last_hash_value == hash_value: return (celsius, humidity, False) self.last_hash_value = hash_value return (celsius, humidity, True) def draw_graph(self, screen, _weather, _updated): """draw temperature and humidity graph """ if self.graph_module: self.graph_module.draw_graph(screen, self.times, self.temperatures, self.humidities) def quit(self): if self.sensor_thread: self.sensor_thread.quit()