diff --git a/__init__.py b/__init__.py index fc0f286c..e3cdcc92 100644 --- a/__init__.py +++ b/__init__.py @@ -29,6 +29,7 @@ from collections import defaultdict from datetime import datetime, timedelta from multi_key_dict import multi_key_dict +from time import sleep from typing import List, Optional from adapt.intent import IntentBuilder @@ -59,7 +60,16 @@ # Later weather: only the word "later" in the vocab file works all others # invoke datetime skill +MARK_II = "mycroft_mark_2" MINUTES = 60 # Minutes to seconds multiplier +CLEAR = 0 +PARTLY_CLOUDY = 1 +CLOUDY = 2 +LIGHT_RAIN = 3 +RAIN = 4 +THUNDERSTORM = 5 +SNOW = 6 +WINDY = 7 class WeatherSkill(MycroftSkill): @@ -68,19 +78,19 @@ def __init__(self): self.weather_api = OWMApi() self.weather_api_new = OpenWeatherMapApi() self.weather_config = WeatherConfig(self.config_core, self.settings) - + self.platform = self.config_core['enclosure']['platform'] # Build a dictionary to translate OWM weather-conditions # codes into the Mycroft weather icon codes # (see https://openweathermap.org/weather-conditions) - self.CODES = multi_key_dict() - self.CODES['01d', '01n'] = 0 # clear - self.CODES['02d', '02n', '03d', '03n'] = 1 # partly cloudy - self.CODES['04d', '04n'] = 2 # cloudy - self.CODES['09d', '09n'] = 3 # light rain - self.CODES['10d', '10n'] = 4 # raining - self.CODES['11d', '11n'] = 5 # stormy - self.CODES['13d', '13n'] = 6 # snowing - self.CODES['50d', '50n'] = 7 # windy/misty + self.image_codes = multi_key_dict() + self.image_codes['01d', '01n'] = CLEAR + self.image_codes['02d', '02n', '03d', '03n'] = PARTLY_CLOUDY + self.image_codes['04d', '04n'] = CLOUDY + self.image_codes['09d', '09n'] = LIGHT_RAIN + self.image_codes['10d', '10n'] = RAIN + self.image_codes['11d', '11n'] = THUNDERSTORM + self.image_codes['13d', '13n'] = SNOW + self.image_codes['50d', '50n'] = WINDY # Use Mycroft proxy if no private key provided self.settings["api_key"] = None @@ -378,30 +388,75 @@ def _report_current_weather(self, message): intent_data = self._get_intent_data(message) weather = self._get_weather(intent_data) if weather is not None: + self._display_current_conditions(weather) dialog = WeatherDialog( weather.current, self.weather_config, intent_data ) dialog.build_current_weather_dialog() - self._display_current_weather(weather) self._speak_weather(dialog) + if self.gui.connected and self.platform != MARK_II: + self._display_more_current_conditions(weather) dialog.build_high_low_temperature_dialog() self._speak_weather(dialog) - - def _display_current_weather(self, report): - image_code = self.CODES[report.current.condition.icon] + if self.gui.connected: + if self.platform == MARK_II: + self._display_more_current_conditions(weather) + sleep(5) + self._display_hourly_forecast(weather) + else: + four_day_forecast = weather.daily[1:5] + self._display_forecast(four_day_forecast) + + def _display_current_conditions(self, weather): + image_code = self.image_codes[weather.current.condition.icon] if self.gui.connected: - self.gui["current"] = report.current.temperature - self.gui["min"] = report.current.low_temperature - self.gui["max"] = report.current.high_temperature - self.gui["condition"] = report.current.condition.description - self.gui["icon"] = report.current.condition.icon - self.gui["weathercode"] = image_code - self.gui.show_pages(["weather.qml", "highlow.qml"]) + page_name = "current_1_generic.qml" + self.gui.clear() + self.gui["currentTemperature"] = weather.current.temperature + self.gui["weatherCode"] = image_code + if self.platform == MARK_II: + self.gui["highTemperature"] = weather.current.high_temperature + self.gui["lowTemperature"] = weather.current.low_temperature + page_name = page_name.replace("generic", "mark_ii") + self.gui.show_page(page_name) else: self.enclosure.deactivate_mouth_events() self.enclosure.weather_display( - image_code, report.current.temperature + image_code, weather.current.temperature + ) + + def _display_more_current_conditions(self, weather): + page_name = "current_2_generic.qml" + self.gui.clear() + if self.platform == MARK_II: + self.gui["windSpeed"] = weather.current.wind_speed + self.gui["humidity"] = weather.current.humidity + page_name = page_name.replace("generic", "mark_ii") + else: + self.gui["highTemperature"] = weather.current.high_temperature + self.gui["lowTemperature"] = weather.current.low_temperature + self.gui.show_page(page_name) + + def _display_hourly_forecast(self, weather): + hourly_forecast = defaultdict(list) + for hour_count, hourly in enumerate(weather.hourly): + if not hour_count: + continue + if hour_count > 4: + break + # TODO: make the timeframe aware of language/location settings + hourly_forecast['times'].append(hourly.date_time.strftime('%H:00')) + hourly_forecast['temperatures'].append(hourly.temperature) + hourly_forecast['weather_codes'].append( + self.image_codes.get(hourly.condition.icon) ) + hourly_forecast['precipitation'].append(hourly.chance_of_precipitation) + self.gui.clear() + self.gui['times'] = hourly_forecast['times'] + self.gui['temperatures'] = hourly_forecast['temperatures'] + self.gui['weatherCodes'] = hourly_forecast['weather_codes'] + self.gui['chancesOfPrecipitation'] = hourly_forecast['precipitation'] + self.gui.show_page('hourly_mark_ii.qml') def _report_one_hour_weather(self, message): intent_data = self._get_intent_data(message) @@ -418,7 +473,7 @@ def _report_multi_day_forecast(self, message, days): intent_data = WeatherIntent(message, self.lang) weather = self._get_weather(intent_data) if weather is not None: - forecast = weather.daily[:days] + forecast = weather.daily[1:days + 1] dialogs = self._build_forecast_dialogs(forecast, intent_data) self._display_forecast(forecast) for dialog in dialogs: @@ -457,19 +512,60 @@ def _build_forecast_dialogs(self, forecast, intent_data): def _display_forecast(self, forecast: List): """Builds forecast for the upcoming days for the Mark-2 display.""" + if self.platform == MARK_II: + self._display_forecast_mark_ii(forecast) + else: + self._display_forecast_generic(forecast) + + def _display_forecast_mark_ii(self, forecast): + page_one_name = "daily_mark_ii.qml" + display_data = defaultdict(list) + for day in forecast: + display_data['weatherCodes'].append( + self.image_codes[day.condition.icon] + ) + display_data['days'].append(day.date_time.strftime("%a")) + display_data['highTemperatures'].append(day.temperature.high) + display_data['lowTemperatures'].append(day.temperature.low) + self.gui.clear() + self.gui["numberOfDays"] = min([4, len(forecast)]) + self.gui['weatherCodes'] = display_data['weatherCodes'][:4] + self.gui['days'] = display_data["days"][:4] + self.gui["lowTemperatures"] = display_data["lowTemperatures"][:4] + self.gui["highTemperatures"] = display_data["highTemperatures"][:4] + self.gui.show_page(page_one_name) + if len(forecast) > 4: + sleep(20) + self.gui.clear() + self.gui["numberOfDays"] = min([4, len(forecast) - 4]) + self.gui['weatherCodes'] = display_data['weatherCodes'][4:] + self.gui['days'] = display_data["days"][4:] + self.gui["lowTemperatures"] = display_data["lowTemperatures"][4:] + self.gui["highTemperatures"] = display_data["highTemperatures"][4:] + self.gui.clear() + self.gui.show_page(page_one_name) + + def _display_forecast_generic(self, forecast): + page_one_name = "daily_1_generic.qml" + page_two_name = page_one_name.replace("1", "2") display_data = [] - for forecast_day in forecast: + for day_number, day in enumerate(forecast): + if day_number == 4: + break display_data.append( dict( - weathercode=self.CODES[forecast_day.condition.icon], - max=forecast_day.temperature.high, - min=forecast_day.temperature.low, - date=forecast_day.date_time.strftime('%a') + weatherCode=self.image_codes[day.condition.icon], + highTemperature=day.temperature.high, + lowTemperature=day.temperature.low, + date=day.date_time.strftime('%a') ) ) self.gui['forecast'] = dict( first=display_data[:2], second=display_data[2:] ) + self.gui.show_page(page_one_name) + sleep(5) + self.gui.show_page(page_two_name) def _report_temperature(self, message, temperature_type=None): intent_data = self._get_intent_data(message) @@ -527,7 +623,6 @@ def _get_intent_data(self, message): except ValueError: self.speak_dialog("cant.get.forecast") else: - print(self.voc_match(intent_data.utterance, "RelativeDay")) if self.voc_match(intent_data.utterance, "RelativeTime"): intent_data.timeframe = "hourly" elif self.voc_match(intent_data.utterance, "Later"): diff --git a/ui/ForecastDelegate.qml b/ui/DailyGenericDelegate.qml similarity index 92% rename from ui/ForecastDelegate.qml rename to ui/DailyGenericDelegate.qml index a4db1cec..9bc29864 100644 --- a/ui/ForecastDelegate.qml +++ b/ui/DailyGenericDelegate.qml @@ -6,7 +6,7 @@ import org.kde.kirigami 2.4 as Kirigami import Mycroft 1.0 as Mycroft import org.kde.lottie 1.0 -WeatherDelegate { +WeatherGenericDelegate { id: root property alias model: forecastRepeater.model @@ -25,7 +25,7 @@ WeatherDelegate { Layout.preferredHeight: proportionalGridUnit * 20 Layout.preferredWidth: Layout.preferredHeight - source: Qt.resolvedUrl(getWeatherImagery(modelData.weathercode)) + source: Qt.resolvedUrl(getWeatherImagery(modelData.weatherCode)) loops: Animation.Infinite fillMode: Image.PreserveAspectFit running: true @@ -43,7 +43,7 @@ WeatherDelegate { Layout.fillWidth: true Layout.preferredHeight: proportionalGridUnit * 20 rightPadding: -font.pixelSize * 0.1 - text: modelData.max + "°" + text: modelData.highTemperature + "°" } Mycroft.AutoFitLabel { @@ -51,7 +51,7 @@ WeatherDelegate { Layout.fillWidth: true Layout.preferredHeight: proportionalGridUnit * 20 rightPadding: -font.pixelSize * 0.1 - text: modelData.min + "°" + text: modelData.lowTemperature + "°" } } } diff --git a/ui/WeatherDelegate.qml b/ui/WeatherGenericDelegate.qml similarity index 100% rename from ui/WeatherDelegate.qml rename to ui/WeatherGenericDelegate.qml diff --git a/ui/WeatherMarkIIDelegate.qml b/ui/WeatherMarkIIDelegate.qml new file mode 100644 index 00000000..236309cb --- /dev/null +++ b/ui/WeatherMarkIIDelegate.qml @@ -0,0 +1,44 @@ +import QtQuick.Layouts 1.4 +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.kirigami 2.4 as Kirigami + +import Mycroft 1.0 as Mycroft + +Mycroft.Delegate { + id: root + bottomPadding: 32 + leftPadding: 32 + rightPadding: 32 + topPadding: 32 + + function getWeatherImagery(weathercode) { + switch(weathercode) { + case 0: + return "images/sunny.svg"; + break + case 1: + return "images/partly_cloudy.svg"; + break + case 2: + return "images/cloudy.svg"; + break + case 3: + return "images/rain.svg"; + break + case 4: + return "images/rain.svg"; + break + case 5: + return "images/storm.svg"; + break + case 6: + return "images/snow.svg"; + break + case 7: + return "images/cloudy.svg"; + break + } + } + +} diff --git a/ui/weather.qml b/ui/current_1_generic.qml similarity index 86% rename from ui/weather.qml rename to ui/current_1_generic.qml index 0bd579ab..bbe0bc69 100644 --- a/ui/weather.qml +++ b/ui/current_1_generic.qml @@ -6,7 +6,7 @@ import org.kde.kirigami 2.4 as Kirigami import Mycroft 1.0 as Mycroft import org.kde.lottie 1.0 -WeatherDelegate { +WeatherGenericDelegate { id: root spacing: proportionalGridUnit * 5 @@ -17,7 +17,7 @@ WeatherDelegate { Layout.preferredWidth: Math.min(root.contentWidth, proportionalGridUnit * 50) Layout.preferredHeight: Layout.preferredWidth - source: Qt.resolvedUrl(getWeatherImagery(sessionData.weathercode)) + source: Qt.resolvedUrl(getWeatherImagery(sessionData.weatherCode)) loops: Animation.Infinite fillMode: Image.PreserveAspectFit @@ -25,7 +25,7 @@ WeatherDelegate { // Debug: onSourceChanged: { - console.log(getWeatherImagery(sessionData.weathercode)); + console.log(getWeatherImagery(sessionData.weatherCode)); } onStatusChanged: { console.log(weatherAnimation.status, errorString); @@ -38,6 +38,6 @@ WeatherDelegate { Layout.fillWidth: true Layout.preferredHeight: proportionalGridUnit * 40 rightPadding: -font.pixelSize * 0.1 - text: sessionData.current + "°" + text: sessionData.currentTemperature + "°" } } diff --git a/ui/current_1_mark_ii.qml b/ui/current_1_mark_ii.qml new file mode 100644 index 00000000..497b50e1 --- /dev/null +++ b/ui/current_1_mark_ii.qml @@ -0,0 +1,150 @@ +/* +One of many screns that show when the user asks for the current weather. + +Shows an animation with current conditions, the current temperature, and +the high/low temperature for today. + +This code is specific to the Mark II device. It uses a grid of 32x32 pixel +squares for alignment of items. +*/ +import QtQuick.Layouts 1.4 +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.kirigami 2.4 as Kirigami + +import Mycroft 1.0 as Mycroft +import org.kde.lottie 1.0 + +WeatherMarkIIDelegate { + id: root + + Item { + // Bounding box for the content of the screen. + id: card + height: parent.height + width: parent.width + + Item { + // City/state if in same country as device. City/country if in a different country + id: weatherLocation + height: 64 + width: parent.width + + Label { + id: weatherLocationText + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 48 + text: "Kansas City, Missouri" + } + } + + GridLayout { + id: weather + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.top: weatherLocation.bottom + anchors.topMargin: 32 + columns: 2 + columnSpacing: 32 + + Item { + // First row in grid + id: currentConditions + height: 288 + width: 320 + + Image { + // Image depicting the current weather conditions (e.g. sunny, cloudy, etc.) + id: conditionsImage + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: -32 + fillMode: Image.PreserveAspectFit + height: 112 + source: Qt.resolvedUrl(getWeatherImagery(sessionData.weatherCode)) + } + + Label { + // Current temperature in the configured temperature unit. + id: currentTemperature + anchors.baseline: parent.bottom + anchors.baselineOffset: -16 + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 176 + font.weight: Font.Bold + text: sessionData.currentTemperature + "°" + } + } + + Column { + // Second column + id: highLowTemperature + height: 288 + width: 320 + spacing: 32 + + Item { + // High temperature for today + id: highTemperature + height: 128 + width: parent.width + + Image { + id: highTemperatureIcon + anchors.bottom: parent.bottom + anchors.bottomMargin: 32 + anchors.left: highTemperature.left + anchors.leftMargin: 32 + fillMode: Image.PreserveAspectFit + height: 64 + source: Qt.resolvedUrl("images/high_temperature.svg") + } + + Label { + id: highTemperatureValue + anchors.baseline: parent.bottom + anchors.baselineOffset: -32 + anchors.left: highTemperatureIcon.right + anchors.leftMargin: 32 + font.family: "Noto Sans Display" + font.pixelSize: 118 + font.weight: Font.Bold + text: sessionData.highTemperature + "°" + } + } + + Item { + // Low temperature for today + id: lowTemperature + height: 128 + width: parent.width + + Image { + id: lowTemperatureIcon + anchors.bottom: parent.bottom + anchors.bottomMargin: 32 + anchors.left: lowTemperature.left + anchors.leftMargin: 32 + fillMode: Image.PreserveAspectFit + height: 64 + source: Qt.resolvedUrl("images/low_temperature.svg") + } + + Label { + id: lowTemperatureValue + anchors.baseline: parent.bottom + anchors.baselineOffset: -32 + anchors.left: lowTemperatureIcon.right + anchors.leftMargin: 32 + font.family: "Noto Sans Display" + font.pixelSize: 118 + font.weight: Font.Light + text: sessionData.lowTemperature + "°" + } + } + } + } + } +} diff --git a/ui/highlow.qml b/ui/current_2_generic.qml similarity index 83% rename from ui/highlow.qml rename to ui/current_2_generic.qml index 60d638dc..9deff6b7 100644 --- a/ui/highlow.qml +++ b/ui/current_2_generic.qml @@ -10,22 +10,22 @@ WeatherDelegate { id: root Mycroft.AutoFitLabel { - id: maxTemp + id: highTemperature font.weight: Font.Bold Layout.fillWidth: true Layout.preferredHeight: proportionalGridUnit * 40 //The off-centering to balance the ° should be proportional as well, so we use the computed pixel size rightPadding: -font.pixelSize * 0.1 - text: sessionData.max + "°" + text: sessionData.highTemperature + "°" } Mycroft.AutoFitLabel { - id: minTemp + id: lowTemperature Layout.fillWidth: true Layout.preferredHeight: proportionalGridUnit * 40 rightPadding: -font.pixelSize * 0.1 font.weight: Font.Thin font.styleName: "Thin" - text: sessionData.min + "°" + text: sessionData.lowTemperature + "°" } } diff --git a/ui/current_2_mark_ii.qml b/ui/current_2_mark_ii.qml new file mode 100644 index 00000000..264ad53e --- /dev/null +++ b/ui/current_2_mark_ii.qml @@ -0,0 +1,107 @@ +import QtQuick.Layouts 1.4 +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.kirigami 2.4 as Kirigami + +import Mycroft 1.0 as Mycroft +import org.kde.lottie 1.0 + +WeatherMarkIIDelegate { + id: root + + Item { + // Bounding box for the content of the screen. + id: card + height: parent.height + width: parent.width + + GridLayout { + id: weather + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.top: parent.top + anchors.topMargin: 32 + columns: 2 + columnSpacing: 32 + rowSpacing: 0 + + Column { + id: wind + height: 352 + width: 320 + + Item { + id: windIconBox + height: 128 + width: parent.width + + Image { + id: windIcon + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + fillMode: Image.PreserveAspectFit + height: parent.height + source: Qt.resolvedUrl("images/wind.svg") + } + } + + Item { + id: windSpeedBox + anchors.top: windIconBox.bottom + anchors.topMargin: 32 + height: 160 + width: parent.width + + Label { + id: windSpeed + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.weight: Font.Bold + font.pixelSize: 176 + text: sessionData.windSpeed + } + } + } + + Column { + id: humidity + height: 352 + width: 320 + + Item { + id: humidityIconBox + height: 128 + width: parent.width + + Image { + id: humidityIcon + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + fillMode: Image.PreserveAspectFit + height: parent.height + source: Qt.resolvedUrl("images/humidity.svg") + } + } + + Item { + id: humidityValueBox + anchors.top: humidityIconBox.bottom + anchors.topMargin: 32 + height: 160 + width: parent.width + + Label { + id: humidityValue + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 180 + font.weight: Font.Bold + text: sessionData.humidity + } + } + } + } + } +} diff --git a/ui/forecast1.qml b/ui/daily_1_generic.qml similarity index 90% rename from ui/forecast1.qml rename to ui/daily_1_generic.qml index b8b321d4..956df1ab 100644 --- a/ui/forecast1.qml +++ b/ui/daily_1_generic.qml @@ -6,7 +6,7 @@ import org.kde.kirigami 2.4 as Kirigami import Mycroft 1.0 as Mycroft import org.kde.lottie 1.0 -ForecastDelegate { +DailyGenericDelegate { id: root model: sessionData.forecast.first } diff --git a/ui/forecast2.qml b/ui/daily_2_generic.qml similarity index 90% rename from ui/forecast2.qml rename to ui/daily_2_generic.qml index aad6da03..922b9de8 100644 --- a/ui/forecast2.qml +++ b/ui/daily_2_generic.qml @@ -6,7 +6,7 @@ import org.kde.kirigami 2.4 as Kirigami import Mycroft 1.0 as Mycroft import org.kde.lottie 1.0 -ForecastDelegate { +DailyGenericDelegate { id: root model: sessionData.forecast.second } diff --git a/ui/daily_mark_ii.qml b/ui/daily_mark_ii.qml new file mode 100644 index 00000000..84849f0d --- /dev/null +++ b/ui/daily_mark_ii.qml @@ -0,0 +1,114 @@ +//import QtQuick.Layouts 1.4 +//import QtQuick 2.4 +//import QtQuick.Controls 2.0 +//import org.kde.kirigami 2.4 as Kirigami +// +//import Mycroft 1.0 as Mycroft +//import org.kde.lottie 1.0 +// +//ForecastMarkIIDelegate { +// id: root +// model: sessionData.forecast.first +//} +import QtQuick.Layouts 1.4 +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.kirigami 2.4 as Kirigami + +import Mycroft 1.0 as Mycroft +import org.kde.lottie 1.0 + +WeatherMarkIIDelegate { + Item { + id: hourlyCard + height: parent.height + width: parent.width + + GridLayout { + id: currentWeather + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.top: parent.top + anchors.topMargin: 32 + columns: sessionData.numberOfDays + columnSpacing: 32 + rowSpacing: 0 + Layout.fillWidth: true + + Repeater { + id: conditionRepeater + model: sessionData.weatherCodes + anchors.top: parent.top + + Item { + height: 64 + width: 144 + + Image { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + height: 64 + fillMode: Image.PreserveAspectFit + source: Qt.resolvedUrl(getWeatherImagery(modelData)) + } + } + } + + Repeater { + id: timeRepeater + model: sessionData.days + + Item { + height: 64 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 59 + text: modelData + } + } + } + + Repeater { + id: highTemperatureRepeater + model: sessionData.highTemperatures + + Item { + height: 96 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 72 + font.weight: Font.Bold + text: modelData + "°" + } + } + } + + Repeater { + id: lowTemperatureRepeater + model: sessionData.lowTemperatures + + Item { + height: 96 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 59 + font.weight: Font.Thin + text: modelData + "°" + } + } + } + } + } +} diff --git a/ui/hourly_mark_ii.qml b/ui/hourly_mark_ii.qml new file mode 100644 index 00000000..f0209855 --- /dev/null +++ b/ui/hourly_mark_ii.qml @@ -0,0 +1,114 @@ +//import QtQuick.Layouts 1.4 +//import QtQuick 2.4 +//import QtQuick.Controls 2.0 +//import org.kde.kirigami 2.4 as Kirigami +// +//import Mycroft 1.0 as Mycroft +//import org.kde.lottie 1.0 +// +//ForecastMarkIIDelegate { +// id: root +// model: sessionData.forecast.first +//} +import QtQuick.Layouts 1.4 +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.kirigami 2.4 as Kirigami + +import Mycroft 1.0 as Mycroft +import org.kde.lottie 1.0 + +WeatherMarkIIDelegate { + Item { + id: hourlyCard + height: parent.height + width: parent.width + + GridLayout { + id: currentWeather + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.top: parent.top + anchors.topMargin: 32 + columns: 4 + columnSpacing: 32 + rowSpacing: 0 + Layout.fillWidth: true + + Repeater { + id: conditionRepeater + model: sessionData.weatherCodes + anchors.top: parent.top + + Item { + height: 64 + width: 144 + + Image { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + height: 64 + fillMode: Image.PreserveAspectFit + source: Qt.resolvedUrl(getWeatherImagery(modelData)) + } + } + } + + Repeater { + id: timeRepeater + model: sessionData.times + + Item { + height: 64 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 59 + text: modelData + } + } + } + + Repeater { + id: temperatureRepeater + model: sessionData.temperatures + + Item { + height: 96 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 72 + font.weight: Font.Bold + text: modelData + "°" + } + } + } + + Repeater { + id: precipitationRepeater + model: sessionData.chancesOfPrecipitation + + Item { + height: 96 + width: 144 + + Label { + anchors.baseline: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Sans Display" + font.pixelSize: 59 + font.weight: Font.Thin + text: modelData + "%" + } + } + } + } + } +} diff --git a/ui/images/cloudy.svg b/ui/images/cloudy.svg new file mode 100644 index 00000000..6ce35734 --- /dev/null +++ b/ui/images/cloudy.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/high_temperature.svg b/ui/images/high_temperature.svg new file mode 100644 index 00000000..f6ae4048 --- /dev/null +++ b/ui/images/high_temperature.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/humidity.svg b/ui/images/humidity.svg new file mode 100644 index 00000000..f6fba0dc --- /dev/null +++ b/ui/images/humidity.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ui/images/low_temperature.svg b/ui/images/low_temperature.svg new file mode 100644 index 00000000..d98838f2 --- /dev/null +++ b/ui/images/low_temperature.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/partly_cloudy.svg b/ui/images/partly_cloudy.svg new file mode 100644 index 00000000..361aa1f6 --- /dev/null +++ b/ui/images/partly_cloudy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/images/rain.svg b/ui/images/rain.svg new file mode 100644 index 00000000..d941e79c --- /dev/null +++ b/ui/images/rain.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ui/images/snow.svg b/ui/images/snow.svg new file mode 100644 index 00000000..584779b8 --- /dev/null +++ b/ui/images/snow.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/storm.svg b/ui/images/storm.svg new file mode 100644 index 00000000..4ca5bc3d --- /dev/null +++ b/ui/images/storm.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/sunny.svg b/ui/images/sunny.svg new file mode 100644 index 00000000..b8c3c205 --- /dev/null +++ b/ui/images/sunny.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/images/wind.svg b/ui/images/wind.svg new file mode 100644 index 00000000..44a66f2c --- /dev/null +++ b/ui/images/wind.svg @@ -0,0 +1,3 @@ + + +