Beispiel #1
0
    def __init__(self, qtui, settings):
        super().__init__(qtui, settings)
        self._default_min_max_text = self._ui.forecast_d1_day_temps_value.text(
        )

        # set default day night icons - sunny clear
        day_icon = get_asset_file("weather_icons", "day-800")
        # night-clear
        night_icon = get_asset_file("weather_icons", "night-800")

        self._ui.forecast_background.setPixmap(
            QtGui.QPixmap(
                str(config.assets_path / "gui_bgrs" /
                    settings.get(FORECAST_BG))))

        common.draw_svg(self._ui.forecast_d1_day_icon, day_icon)
        common.draw_svg(self._ui.forecast_d2_day_icon, day_icon)
        common.draw_svg(self._ui.forecast_d3_day_icon, day_icon)

        common.draw_svg(self._ui.forecast_d1_night_icon, night_icon)
        common.draw_svg(self._ui.forecast_d2_night_icon, night_icon)
        common.draw_svg(self._ui.forecast_d3_night_icon, night_icon)

        self._init_dummy_values()

        # connect daily detail view
        self._ui.forecast_d1_icon.clicked.connect(lambda: self.show_detail(1))
        self._ui.forecast_d2_icon.clicked.connect(lambda: self.show_detail(2))
        self._ui.forecast_d3_icon.clicked.connect(lambda: self.show_detail(3))

        # call once at begin
        self.init_with_cyclic_update()
    def _get_condition_icon(ident, is_day) -> Path:
        if is_day:
            ident = "day-" + str(ident)
        else:
            ident = "night-" + str(ident)

        file_path = get_asset_file("weather_icons", ident)

        # normalize to lower case
        if not file_path:
            file_path = get_asset_file("gui_base", "dummy.png")
        return file_path
Beispiel #3
0
    def _init_dummy_values(self):
        dummy_icon = str(get_asset_file("gui_base", "dummy-pic"))
        self._ui.forecast_d1_icon.setPixmap(QtGui.QPixmap(dummy_icon))
        self._ui.forecast_d2_icon.setPixmap(QtGui.QPixmap(dummy_icon))
        self._ui.forecast_d3_icon.setPixmap(QtGui.QPixmap(dummy_icon))

        # set day temps to NA
        self._ui.forecast_d1_day_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))

        self._ui.forecast_d2_day_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))

        self._ui.forecast_d3_day_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))

        # set night temps to NA
        self._ui.forecast_d1_night_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))

        self._ui.forecast_d2_night_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))

        self._ui.forecast_d3_night_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))
Beispiel #4
0
def parse_event_file(events_file_path: Path) -> List[Event]:
    """ Parse the json config file, validate and convert to object structure """
    events: List[Event] = []
    Logger().info(f"EventHandler: Loading file '{events_file_path}'...")

    if not events_file_path.is_file():
        Logger().warning(
            f"EventHandler: Config file '{events_file_path}' does not exist.")
        return []
    with open(events_file_path, encoding="utf-8") as config_file:
        try:
            events_config = json.load(config_file)
            with open(get_asset_file("base", "events_schema")) as schema_file:
                json_schema = json.load(schema_file)
                jsonschema.validate(instance=events_config, schema=json_schema)
        except BaseException as error:
            Logger().error(f"EventHandler: Config file:\n{str(error)}")
            return []

    for event_data in events_config.get("events", []):
        event = Event(event_data.get("name", ""))
        event.triggers = event_data.get("triggers", "")
        event.actions = event_data.get("actions", "")
        event.recurrence = event_data.get("recurrence", "")
        event.date = event_data.get("date", "")
        event.last_triggered = event_data.get("last_triggered", "")
        events.append(event)
    return events
Beispiel #5
0
 def _change_background(self, file_id: str):
     """ Slot for change background signal """
     background = get_asset_file("gui_base", file_id)
     if background and background.is_file():
         while not self.ready:
             time.sleep(1)
         if not self._ui:
             return
         self._ui.interior_background.setPixmap(
             QtGui.QPixmap(str(background)))
def get_temperature_icon(temp_value: Optional[float]) -> Path:
    """
    Return the path of the image resource for the appropriate temperature input.
    t < 0: empty
    t < 10: low
    t < 22: half
    t < 30 high
    t > 30: full
    """
    assets_subfolder = "weather_icons"
    # set dummy as default
    icon_path = get_asset_file(assets_subfolder, "thermometer_empty")
    # return dummy for invalid value
    if not temp_value:
        return icon_path

    # set up ranges for the 5 icons
    if temp_value <= 0:
        icon_path = get_asset_file(assets_subfolder, "thermometer_empty")
    elif temp_value < 10:
        icon_path = get_asset_file(
            assets_subfolder, "thermometer_almost_empty")
    elif temp_value < 22:
        icon_path = get_asset_file(assets_subfolder, "thermometer_half")
    elif temp_value < 30:
        icon_path = get_asset_file(
            assets_subfolder, "thermometer_almost_full")
    else:
        icon_path = get_asset_file(assets_subfolder, "thermometer_full")
    return icon_path
def get_font(font_name) -> QtGui.QFont:
    # set up font
    font_file = get_asset_file("font", "franzo")
    font_id = QtGui.QFontDatabase.addApplicationFont(str(font_file))
    if not config.qt_app:
        return QtGui.QFont()
    font = config.qt_app.font()
    if font_id != -1:
        font_db = QtGui.QFontDatabase()
        font_styles = font_db.styles(font_name)
        font_families = QtGui.QFontDatabase.applicationFontFamilies(font_id)
        if font_families:
            font = font_db.font(font_families[0], font_styles[0], 13)
        else:
            logger.warning("Can't apply selected font file.")
    return font
Beispiel #8
0
    def show_info_screen(self):
        """ Shows the user help overlay (opaque)."""
        if not self._ui:
            return
        self._ui.overlay_background.setPixmap(
            QtGui.QPixmap(str(get_asset_file("gui_base", "info_overlay"))))
        self._ui.overlay_background.raise_()
        self._ui.overlay_background.show()

        self._ui.ol_sensors_label.raise_()
        self._ui.ol_sensors_label.show()
        self._ui.ol_wh_today_label.raise_()
        self._ui.ol_wh_today_label.show()
        self._ui.ol_wh_forecast_label.raise_()
        self._ui.ol_wh_forecast_label.show()
        self._ui.ol_title_label.raise_()
        self._ui.ol_title_label.show()
    def get_tts_string(self, key: str, lang="en") -> str:
        if lang in LANGS_MAP:
            lang = LANGS_MAP.get(lang)
        dict_file = get_asset_file("base", "tts_dict")
        # read filetoc.json
        with open(str(dict_file), encoding='utf-8') as f:
            tts_dict = json.load(f)

        # get filetype and filelist
        lang_dict = tts_dict.get(lang)
        if not lang_dict:
            self._logger.error(f"TTS: Cannot find language string for {lang}")
            return ""

        value = lang_dict.get(key)
        if not value:
            Logger().error("Cannot find resource id %s in catalog", key)
        return value
def qt_app_setup(settings) -> QtWidgets.QApplication:
    """
    Set up all Qt application specific attributes, which can't be changed later on
    Returns qt_app object.
    """
    # apply Qt attributes (only at init possible)
    QtWidgets.QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QtWidgets.QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

    # set up global Qt Application instance
    qt_app = QtWidgets.QApplication([])
    # set icon
    icon_path = get_asset_file("gui_base", "icon")
    qt_app.setWindowIcon(QtGui.QIcon(str(icon_path)))

    # install translator
    common.set_ui_language(qt_app, settings)

    return qt_app
    def __init__(self, main_ui, settings):
        super().__init__(main_ui, settings)
        self._online_info_date_time = None  # date when last online update occured

        # save default text to restore formatting
        self._default_temp_text = self._ui.exterior_temp_value.text()

        day_icon = get_asset_file("weather_icons", "day-800")
        common.draw_svg(self._ui.exterior_forecast_icon, day_icon, scale=3)

        self._ui.exterior_forecast_icon.clicked.connect(self.show_detail)

        # set day temps to NA
        self._default_min_max_text = self._ui.exterior_forecast_temps_value.text(
        )
        self._ui.exterior_forecast_temps_value.setText(
            common.format_temp_text_minmax(self._default_min_max_text, None,
                                           None))
        # call once at begin
        self.init_with_cyclic_update()
    def __init__(self, background=True):
        """
        background: set the image as the background.
        The layout changes if background ist set.
        Without: the spinner plays in the middle. A screenshot is taken when the splashcreen
        initializes and set as background, so changing elements in the gui are not seen by the user.
        With: background image is scaled and spinner plays in the bottom middle.
        """
        self._is_background_set = background
        self._label = None
        self._spinner = None

        if background:
            pixmap = QtGui.QPixmap(str(get_asset_file("gui_base", "loading")))
            pixmap = pixmap.scaled(800, 480, transformMode=Qt.SmoothTransformation)
        else:
            if not config.qt_app:
                return # can not really happen...
            screen = config.qt_app.primaryScreen()
            pixmap = screen.grabWindow(voidptr(0))

        QtWidgets.QSplashScreen.__init__(self, pixmap)
        if config.DEBUG_LEVEL > 0: # unlock gui when debugging
            self.setWindowFlags(Qt.FramelessWindowHint)
Beispiel #13
0
def write_events_file(events_file_path: Path, events: List[Event]):
    """ Write out the json dict from model """
    events_data = []
    for event in events:
        event_data = {
            "name": event.name,
            "recurrence": event.recurrence,
            "date": event.date,
            "triggers": event.triggers,
            "actions": event.actions,
            "last_triggered": event.last_triggered
        }
        events_data.append(event_data)

    # get last version
    with open(get_asset_file("base", "events_schema")) as schema_file:
        json_schema = json.load(schema_file)
    version = json_schema.get("properties").get("version").get("enum")[-1]
    events_config = {"version": version, "events": events_data}
    try:
        with open(events_file_path, "w") as config_file:
            json.dump(events_config, config_file, indent=4)
    except Exception:
        Logger().error("EventHandler: Cannot open events.json file")
    def _cyclic_update(self):
        self._logger.debug("ExteriorGui: update")
        if not self._comps.weather_info:
            return
        forecast = self._comps.weather_info.get_5_day_forecast()
        current_weather = self._comps.weather_info.get_current_weather()

        if not current_weather or not forecast:  # handling for no connection - throw away value after an hour
            if not self._online_info_date_time:  # never got any value
                online_temp_value = None
            else:  # look, if last value is older than 5 minutes
                current_date_time = datetime.datetime.now()
                time_delta = current_date_time - self._online_info_date_time
                if time_delta.seconds > 300:  # 5 minutes
                    online_temp_value = None
                else:
                    return  # forecast still valid, don't change displayed value
        else:
            self._online_info_date_time = current_weather.fetch_time
            online_temp_value = current_weather.temp  # for later use
            online_weather_desc = current_weather.description
            self._logger.debug("ExteriorGui: Current weather condition is %s",
                               online_weather_desc)

            # set weather description specific background image
            online_weather_category = current_weather.main.lower()
            cloudiness = current_weather.clouds

            if cloudiness > 65 and online_weather_category == "clouds":
                online_weather_category = "heavy_clouds"

            if current_weather.is_daytime():
                bg_name = "bg_day_" + online_weather_category
            else:
                bg_name = "bg_night_" + online_weather_category

            self._ui.exterior_background.set_image(
                str(get_asset_file("weather_bgrs", bg_name)))

            # set todays forecast
            if not forecast[0]:
                return
            common.draw_svg(self._ui.exterior_forecast_icon,
                            forecast[0].icon,
                            scale=3)

            if not current_weather.is_daytime():
                temp_min = forecast[0].temp_night_min
                temp_max = forecast[0].temp_night_max
                if temp_min == -float("inf") or temp_max == float("inf"):
                    temp_min = forecast[0].temp_min
                    temp_max = forecast[0].temp_max
            else:
                temp_min = forecast[0].temp_min
                temp_max = forecast[0].temp_max
                if temp_min == -float("inf") or temp_max == float("inf"):
                    temp_min = forecast[0].temp_night_min
                    temp_max = forecast[0].temp_night_max

            self._ui.exterior_forecast_temps_value.setText(
                common.format_temp_text_minmax(self._default_min_max_text,
                                               temp_min, temp_max))

        # if sensor is not available switch to online data
        temp_value = online_temp_value
        if self._comps.remote_temp_sensor and self._comps.remote_temp_sensor.is_active:
            temp_value = self._comps.remote_temp_sensor.get_temperature()

        # format and set values to temperature display
        temp_val_text = common.format_float_temp_text(self._default_temp_text,
                                                      temp_value)
        self._ui.exterior_temp_value.setText(temp_val_text)

        # set temperature icon
        common.draw_svg(self._ui.exterior_temp_icon,
                        common.get_temperature_icon(temp_value))
    def __init__(self, main_ui: "WeatherMainUi",
                 comp_ctrl: ComponentController, settings: Settings):
        super().__init__()

        self._main_ui = main_ui  # reference to main ui instance
        self._comp_ctrl = comp_ctrl
        self._comps = comp_ctrl.components
        self._settings = settings
        self._previous_scaling = self._settings.get(FONT_SCALING)
        self._runtime_system = RuntimeSystem()

        # create qt base objects
        self.setWindowFlags(
            Qt.WindowType(Qt.CustomizeWindowHint | Qt.FramelessWindowHint))

        ui_type = uic.loadUiType(config.base_path / "ui" / "qt" / "options.ui")
        self._ui = ui_type[0]()  # 0th element is always the UI class
        self._ui.setupUi(self)

        self.setGeometry(main_ui.geometry())

        # start fader - variable must be held otherwise gc will claim it
        fader_widget = FaderWidget(main_ui, self)  # pylint: disable=unused-variable

        # set version label
        self._ui.version_label.setText(WAQD_VERSION)

        # set up fix background image
        self._ui.background_label.setPixmap(
            QtGui.QPixmap(str(get_asset_file("gui_base", "background-full"))))

        # display current options
        self.display_options()

        # connect buttons on main tab
        self._ui.ok_button.clicked.connect(self.apply_options)
        self._ui.cancel_button.clicked.connect(self.close_ui)
        self._ui.shutdown_button.clicked.connect(self.call_shutdown)
        self._ui.restart_button.clicked.connect(self.call_restart)
        self._ui.connect_wlan_button.clicked.connect(self.connect_wlan)

        self._ui.lang_cbox.currentTextChanged.connect(
            self._update_language_cbox)
        self._ui.forecast_background_cbox.currentTextChanged.connect(
            self._update_preview_forecast)
        self._ui.interior_background_cbox.currentTextChanged.connect(
            self._update_preview_interior)
        self._ui.font_cbox.currentFontChanged.connect(self._update_font)

        # connect elements on energy saver tab
        self._ui.night_mode_begin_slider.valueChanged.connect(
            self._update_night_mode_slider)
        self._ui.night_mode_begin_slider.sliderMoved.connect(
            self._update_night_mode_slider)

        self._ui.night_mode_end_slider.valueChanged.connect(
            self._update_night_mode_slider)
        self._ui.night_mode_end_slider.sliderMoved.connect(
            self._update_night_mode_slider)

        self._ui.brightness_slider.valueChanged.connect(
            self._update_brightness_slider)
        self._ui.brightness_slider.sliderMoved.connect(
            self._update_brightness_slider)

        self._ui.motion_sensor_enable_toggle.toggled.connect(
            self._update_motion_sensor_enabled)

        # set starting tab to first tab
        self._ui.options_tabs.setCurrentIndex(0)

        # set to normal brightness
        self._comps.display.set_brightness(self._settings.get_int(BRIGHTNESS))

        common.scale_gui_elements(
            self,
            self._settings.get_float(FONT_SCALING) * self.EXTRA_SCALING)

        # initialize splash screen for the closing of the UI and make a screenshot
        self._splash_screen = SplashScreen(background=False)
        # minimal wait to show the button feedback
        time.sleep(0.3)
        self.show()