#include "Designer.h" #include "common.h" #include "common_functions.h" #include "Graph.h" Designer::Designer(Ecran * _ecran, Meteo * _meteo, Connect * _connect, Domoticz * _domoticz, Timer * _timer) { ecran = _ecran; meteo = _meteo; connect = _connect; domoticz = _domoticz; timer = _timer; } //######################################################################################### void Designer::displayWeather(int boucle) { // 7.5" e-paper display is 800x480 resolution if (debug) Serial.println("B - displayWeather boucle=" + String(boucle)); if (true || boucle == 1) { displayGeneralInfoSection(); // Top line of the display if (debug) Serial.println("1 - displayWeather"); displayWindSection(108, 146, meteo->conditions[0].Winddir, meteo->conditions[0].Windspeed, 81); if (debug) Serial.println("2 - displayWeather"); // displayMainWeatherSection(300, 100); // Centre section of display for Location, temperature, Weather report, current Wx Symbol and wind direction ecran->_display->drawLine(0, 38, SCREEN_WIDTH - 3, 38, GxEPD_BLACK); displayConditionsSection(303, 149, meteo->conditions[0].Icon,LargeIcon); displayTemperatureSection(389, 19, 137, 100); //displayForecastTextSection(397, 120, 409, 65); displayPressureSection(581, 19, meteo->conditions[0].Pressure, meteo->conditions[0].Trend, 140, 100); displayPrecipitationSection(712, 19, 136, 100); if (debug) Serial.println("3 - displayWeather"); displayForecastSection(0, 243,FORECAST_WIDTH, FORECAST_HEIGHT); // 3hr forecast boxes if (debug) Serial.println("4 - displayWeather"); // displayAstronomySection(0, 245); // Astronomy section Sun rise/set, Moon phase and Moon icon // if (debug) Serial.println("5 - displayWeather"); displayStatusSection(700, 10); // Wi-Fi signal strength and Battery voltage domoticz->drawTemps(389, 117, 412, 105); domoticz->drawGraphs(0, 375, 150, 72); //domoticz->loadLinky(); } // Partial mode else { ecran->_display->setPartialWindow(1,1,601,17); //ecran->_display->fillScreen(GxEPD_WHITE);; displayGeneralInfoSection(); // Top line of the display } if (debug) Serial.println("E - displayWeather"); } //######################################################################################### void Designer::displayGeneralInfoSection() { ecran->fonts.setFont(FONT_10); ecran->drawString(6, 2, "[Version: " + String(VERSION) + "] " + timer->Date_str + ' ' + timer->Time_str, LEFT); // Programme version ecran->drawString(SCREEN_WIDTH / 2, 3, meteo->City, CENTER); ecran->_display->drawLine(0, 18, SCREEN_WIDTH - 3, 18, GxEPD_BLACK); } //######################################################################################### void Designer::displayWindSection(int x, int y, float angle, float windspeed, int Cradius) { ecran->arrow(x, y, Cradius - 22, angle, 18, 33); // Show wind direction on outer circle of width and length ecran->fonts.setFont(FONT_08); ecran->drawString(x, y - Cradius - 41, TXT_WIND_SPEED_DIRECTION, CENTER, GxEPD_BLACK); int dxo, dyo, dxi, dyi; ecran->_display->drawLine(0, 18, 0, y + Cradius + 37, GxEPD_BLACK); ecran->_display->drawCircle(x, y, Cradius, GxEPD_BLACK); // Draw compass circle ecran->_display->drawCircle(x, y, Cradius + 1, GxEPD_BLACK); // Draw compass circle ecran->_display->drawCircle(x, y, Cradius * 0.7, GxEPD_BLACK); // Draw compass inner circle for (float a = 0; a < 360; a = a + 22.5) { dxo = Cradius * cos((a - 90) * PI / 180); dyo = Cradius * sin((a - 90) * PI / 180); if (a == 45) ecran->drawString(dxo + x + 12, dyo + y - 12, TXT_NE, CENTER); if (a == 135) ecran->drawString(dxo + x + 7, dyo + y + 6, TXT_SE, CENTER); if (a == 225) ecran->drawString(dxo + x - 18, dyo + y, TXT_SW, CENTER); if (a == 315) ecran->drawString(dxo + x - 18, dyo + y - 12, TXT_NW, CENTER); dxi = dxo * 0.9; dyi = dyo * 0.9; ecran->_display->drawLine(dxo + x, dyo + y, dxi + x, dyi + y, GxEPD_BLACK); dxo = dxo * 0.7; dyo = dyo * 0.7; dxi = dxo * 0.9; dyi = dyo * 0.9; ecran->_display->drawLine(dxo + x, dyo + y, dxi + x, dyi + y, GxEPD_BLACK); } ecran->drawString(x, y - Cradius - 12, TXT_N, CENTER); ecran->drawString(x, y + Cradius + 6, TXT_S, CENTER); ecran->drawString(x - Cradius - 12, y - 3, TXT_W, CENTER); ecran->drawString(x + Cradius + 10, y - 3, TXT_E, CENTER); ecran->drawString(x - 2, y - 43, WindDegToDirection(angle), CENTER); ecran->drawString(x + 6, y + 30, String(angle, 0) + "°", CENTER); ecran->fonts.setFont(FONT_18); ecran->drawString(x - 12, y - 3, String(windspeed, 1), CENTER,GxEPD_BLACK); ecran->fonts.setFont(FONT_08); ecran->drawString(x, y + 12, (Units == "M" ? "m/s" : "mph"), CENTER); } //######################################################################################### void Designer::displayTemperatureSection(int x, int y, int twidth, int tdepth) { ecran->_display->drawRect(x, y - 1, twidth, tdepth, GxEPD_BLACK); // temp outline ecran->fonts.setFont(FONT_08); ecran->drawString(x + 63, y + 5, String(TXT_TEMPERATURES), CENTER); ecran->fonts.setFont(FONT_10); ecran->drawString(x + 73, y + 82, String(meteo->conditions[0].High, 0) + "° | " + String(meteo->conditions[0].Low, 0) + "°", CENTER); // Show forecast high and Low ecran->fonts.setFont(FONT_24); ecran->drawString(x + 53, y + 53, String(meteo->conditions[0].Temperature, 1) + "°", CENTER); // Show current Temperature ecran->fonts.setFont(FONT_10); ecran->_display->drawInvertedBitmap(x, y + 66, thermometer32x32, 32, 32, GxEPD_BLACK); //ecran->drawString(x + 43, y + 53, Units == "M" ? "C" : "F", LEFT); } //######################################################################################### void Designer::displayPressureSection(int x, int y, float pressure, String slope, int pwidth, int pdepth) { ecran->_display->drawRect(x - 56, y - 1, pwidth, pdepth, GxEPD_BLACK); // pressure outline ecran->fonts.setFont(FONT_08); ecran->drawString(x + 8, y + 5, TXT_PRESSURE, CENTER); String slope_direction = TXT_PRESSURE_STEADY; int x_i = x + 40; if (slope == "+") { slope_direction = TXT_PRESSURE_RISING; ecran->_display->fillTriangle(x_i, y + 50, x_i + 10, y + 50, x_i + 5, y + 40, GxEPD_RED); ecran->_display->fillTriangle(x_i, y + 55, x_i + 10, y + 55, x_i + 5, y + 65, GxEPD_BLACK); } if (slope == "-") { slope_direction = TXT_PRESSURE_FALLING; ecran->_display->fillTriangle(x_i, y + 50, x_i + 10, y + 50, x_i + 5, y + 40, GxEPD_BLACK); ecran->_display->fillTriangle(x_i, y + 55, x_i + 10, y + 55, x_i + 5, y + 65, GxEPD_RED); } ecran->_display->drawRect(x + 40, y + 78, 41, 21, GxEPD_BLACK); ecran->fonts.setFont(FONT_24); if (Units == "I") ecran->drawString(x - 22, y + 55, String(pressure, 2), CENTER); // "Imperial" else ecran->drawString(x - 22, y + 55, String(pressure, 0), CENTER); // "Metric" ecran->fonts.setFont(FONT_08); ecran->drawString(x + 59, y + 83, (Units == "M" ? "hPa" : "in"), CENTER); //ecran->drawString(x - 3, y + 83, slope_direction, CENTER); } //######################################################################################### void Designer::displayPrecipitationSection(int x, int y, int pwidth, int pdepth) { ecran->_display->drawRect(x - 48, y - 1, pwidth, pdepth, GxEPD_BLACK); // precipitation outline ecran->fonts.setFont(FONT_08); ecran->drawString(x + 25, y + 5, TXT_PRECIPITATION_SOON, CENTER); ecran->fonts.setFont(u8g2_font_helvB12_tf); if (meteo->previsions[1].Rainfall >= 0.005) { // Ignore small amounts ecran->drawString(x - 25, y + 40, String(meteo->previsions[1].Rainfall, 2) + (Units == "M" ? "mm" : "in"), LEFT); // Only display rainfall total today if > 0 meteo->addraindrop(x + 58, y + 40, 7); } if (meteo->previsions[1].Snowfall >= 0.005) // Ignore small amounts ecran->drawString(x - 25, y + 71, String(meteo->previsions[1].Snowfall, 2) + (Units == "M" ? "mm" : "in") + " **", LEFT); // Only display snowfall total today if > 0 if (meteo->previsions[1].Pop >= 0.005) // Ignore small amounts ecran->drawString(x - 25, y + 81, String(meteo->previsions[1].Pop * 100, 0) + "%", LEFT); // Only display pop if > 0 ecran->_display->drawInvertedBitmap(x + 45, y + 65, humidity32x32, 32, 32, GxEPD_BLACK); ecran->drawString(x + 40, y + 81, String(meteo->conditions[0].Humidity, 0), CENTER); } //######################################################################################### void Designer::displayForecastWeather(int x, int y, int fwidth, int height, int index) { Forecast_record_type frt; ecran->fonts.setFont(FONT_10); frt = meteo->previsions[index]; ecran->_display->drawRect(x, y, fwidth, height, GxEPD_BLACK); ecran->_display->drawLine(x, y + 16, x + fwidth, y + 16, GxEPD_BLACK); displayConditionsSection(x + fwidth / 2, y + 43, frt.Icon, SmallIcon); ecran->drawString(x + fwidth / 2, y + 6, String(meteo->ConvertUnixTime(frt.Dt + meteo->conditions[0].Timezone).substring(0, 5)) , CENTER); ecran->drawString(x + 5, y + height - 10, String(frt.High, 0) + "°", LEFT); ecran->drawString(x + fwidth, y + height - 10, String(frt.Low, 0) + "°", RIGHT); } //######################################################################################### void Designer::displayForecastSection(int x, int y,int fwidth, int height) { if (debug) Serial.println("B - displayForecastSection"); int f = 8 - FORECAST_HORIZON; int to_show = 0; // displayForecastWeather(x, y, to_show); // to_show ++; do { displayForecastWeather(x + FORECAST_WIDTH * (to_show), y, fwidth, height, to_show); f++; to_show ++; } while (f <= 7); } //######################################################################################### void Designer::displayConditionsSection(int x, int y, String IconName, bool IconSize) { if (debug) Serial.println("B - displayConditionsSection Icon name: " + IconName); #if defined(ICONS_SET_ONE) bool inverted = true; IconName.replace('n', 'd'); #endif #if defined(ICONS_SET_TWO) bool inverted = false; #endif #if defined(ICONS_SET_THREE) bool inverted = true; #endif if (IconSize != LargeIcon) { int scale = 1; int xi = x - ICON_SIZE / 2; int yi = y - ICON_SIZE / 2; if (String(IconName) == "01d") ecran->drawBitmap(xi, yi, icon_64x64_01d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "02d") ecran->drawBitmap(xi, yi, icon_64x64_02d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "03d") ecran->drawBitmap(xi, yi, icon_64x64_03d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "04d") ecran->drawBitmap(xi, yi, icon_64x64_04d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "09d") ecran->drawBitmap(xi, yi, icon_64x64_09d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "10d") ecran->drawBitmap(xi, yi, icon_64x64_10d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "11d") ecran->drawBitmap(xi, yi, icon_64x64_11d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "13d") ecran->drawBitmap(xi, yi, icon_64x64_13d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "50d") ecran->drawBitmap(xi, yi, icon_64x64_50d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); #if defined(ICONS_SET_THREE) || defined(ICONS_SET_TWO) if (String(IconName) == "01n") ecran->drawBitmap(xi, yi, icon_64x64_01n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "02n") ecran->drawBitmap(xi, yi, icon_64x64_02n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "03n") ecran->drawBitmap(xi, yi, icon_64x64_03n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "04n") ecran->drawBitmap(xi, yi, icon_64x64_04n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "09n") ecran->drawBitmap(xi, yi, icon_64x64_09n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "10n") ecran->drawBitmap(xi, yi, icon_64x64_10n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "11n") ecran->drawBitmap(xi, yi, icon_64x64_11n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "13n") ecran->drawBitmap(xi, yi, icon_64x64_13n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "50n") ecran->drawBitmap(xi, yi, icon_64x64_50n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); #endif } if (SHOW_MOON_IN_FORECAST) { if (IconName.endsWith("n")) meteo->addMoon(x, y + 3, Small, IconSize); } if (IconSize ==LargeIcon) { float scale = 2; int xi = x - ICON_SIZE / 2 * scale; int yi = y - 20 - ICON_SIZE / 2 * scale; #if defined(CALCULATED) yi = y - 20; if (IconName == "01d" || IconName == "01n") meteo->Sunny(x, yi, IconSize, IconName); else if (IconName == "02d" || IconName == "02n") meteo->MostlySunny(x, yi, IconSize, IconName); else if (IconName == "03d" || IconName == "03n") meteo->Cloudy(x, yi, IconSize, IconName); else if (IconName == "04d" || IconName == "04n") meteo->MostlyCloudy(x, yi, IconSize, IconName); else if (IconName == "09d" || IconName == "09n") meteo->ChanceRain(x, yi, IconSize, IconName); else if (IconName == "10d" || IconName == "10n") meteo->Rain(x, yi, IconSize, IconName); else if (IconName == "11d" || IconName == "11n") meteo->Tstorms(x, yi, IconSize, IconName); else if (IconName == "13d" || IconName == "13n") meteo->Snow(x, yi, IconSize, IconName); else if (IconName == "50d") meteo->Haze(x, yi, IconSize, IconName); else if (IconName == "50n") meteo->Fog(x, yi, IconSize, IconName); else Nodata(x, yi, IconSize, IconName); #endif if (xi > 0) { if (String(IconName) == "01d") ecran->drawBitmap(xi, yi, icon_64x64_01d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "02d") ecran->drawBitmap(xi, yi, icon_64x64_02d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "03d") ecran->drawBitmap(xi, yi, icon_64x64_03d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "04d") ecran->drawBitmap(xi, yi, icon_64x64_04d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "09d") ecran->drawBitmap(xi, yi, icon_64x64_09d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "10d") ecran->drawBitmap(xi, yi, icon_64x64_10d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "11d") ecran->drawBitmap(xi, yi, icon_64x64_11d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "13d") ecran->drawBitmap(xi, yi, icon_64x64_13d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "50d") ecran->drawBitmap(xi, yi, icon_64x64_50d, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); #if defined(ICONS_SET_THREE) || defined(ICONS_SET_TWO) if (String(IconName) == "01n") ecran->drawBitmap(xi, yi, icon_64x64_01n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "02n") ecran->drawBitmap(xi, yi, icon_64x64_02n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "03n") ecran->drawBitmap(xi, yi, icon_64x64_03n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "04n") ecran->drawBitmap(xi, yi, icon_64x64_04n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "09n") ecran->drawBitmap(xi, yi, icon_64x64_09n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "10n") ecran->drawBitmap(xi, yi, icon_64x64_10n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "11n") ecran->drawBitmap(xi, yi, icon_64x64_11n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "13n") ecran->drawBitmap(xi, yi, icon_64x64_13n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); if (String(IconName) == "50n") ecran->drawBitmap(xi, yi, icon_64x64_50n, ICON_SIZE, ICON_SIZE, GxEPD_BLACK, scale, inverted); #endif } ecran->_display->drawRect(x - 86, y - 131, 173, 226, GxEPD_BLACK); ecran->fonts.setFont(FONT_08); ecran->drawString(x, y - 125, TXT_CONDITIONS, CENTER); ecran->fonts.setFont(FONT_14); ecran->fonts.setFont(FONT_10); // ecran->drawString(x + 35, y + 80, "RH", CENTER); if (meteo->conditions[0].Visibility > 0) meteo->Visibility(x - 20, y - 10, String(meteo->conditions[0].Visibility) + "M"); if (meteo->conditions[0].Cloudcover > 0) meteo->CloudCover(x - 20, y - 35, meteo->conditions[0].Cloudcover); ecran->fonts.setFont(FONT_10); meteo->drawTodayMoon(x + 90 - 2 * MOON_DIAMETER - 10, y - 120); int yp = 35; ecran->_display->drawInvertedBitmap(x - 85, y + yp, sunrise48x48, 48, 48, GxEPD_BLACK); ecran->_display->drawInvertedBitmap(x + 90 - 64, y + yp, sunset48x48, 48, 48, GxEPD_BLACK); String sunset = String(meteo->ConvertUnixTime(meteo->conditions[0].Sunset + meteo->conditions[0].Timezone)); String sunrise = String(meteo->ConvertUnixTime(meteo->conditions[0].Sunrise + meteo->conditions[0].Timezone)); ecran->fonts.setFont(FONT_14); ecran->drawString(x - 30, y + yp + 10, sunrise.substring(0, 2), CENTER); ecran->drawString(x - 30, y + yp + 35, sunrise.substring(3, 5), CENTER); ecran->drawString(x + 10, y + yp + 10, sunset.substring(0, 2), CENTER); ecran->drawString(x + 10, y + yp + 35, sunset.substring(3, 5), CENTER); } if (debug) Serial.println("E - displayConditionsSection Icon name: " + IconName); } //######################################################################################### void Designer::displayStatusSection(int x, int y) { // ecran->_display->drawRect(x - 35, y - 32, 145, 61, GxEPD_BLACK); // ecran->_display->drawLine(x - 35, y - 17, x - 35 + 145, y - 17, GxEPD_BLACK); // ecran->_display->drawLine(x - 35 + 146 / 2, y - 18, x - 35 + 146 / 2, y - 32, GxEPD_BLACK); ecran->fonts.setFont(FONT_08); // ecran->drawString(x, y - 29, TXT_WIFI, CENTER); // ecran->drawString(x + 68, y - 30, TXT_POWER, CENTER); connect->drawRSSI(x, y + 6); DrawBattery(x + 58, y + 6, 1); } //######################################################################################### String Designer::WindDegToDirection(float winddirection) { int dir = int((winddirection / 22.5) + 0.5); String Ord_direction[16] = {TXT_N, TXT_NNE, TXT_NE, TXT_ENE, TXT_E, TXT_ESE, TXT_SE, TXT_SSE, TXT_S, TXT_SSW, TXT_SW, TXT_WSW, TXT_W, TXT_WNW, TXT_NW, TXT_NNW}; return Ord_direction[(dir % 16)]; } //######################################################################################### void Designer::DrawBattery(int x, int y, float scale) { uint8_t percentage = 100; float voltage = analogRead(35) / 4096.0 * 7.46; if (voltage < 0.5) { voltage = 4.95; } if (voltage > 1 ) { // Only display if there is a valid reading if (debug) Serial.println("Voltage = " + String(voltage)); percentage = 2836.9625 * pow(voltage, 4) - 43987.4889 * pow(voltage, 3) + 255233.8134 * pow(voltage, 2) - 656689.7123 * voltage + 632041.7303; if (voltage >= 4.20) percentage = 100; if (voltage <= 3.50) percentage = 0; ecran->_display->drawRect(x + 15, y - 12, 19, 10, GxEPD_RED); ecran->_display->fillRect(x + 34, y - 10, 2, 5, GxEPD_RED); ecran->_display->fillRect(x + 17, y - 10, 15 * percentage / 100.0, 6, GxEPD_RED); ecran->drawString(x + 10, y - 11, String(percentage) + "%", RIGHT); //ecran->drawString(x + 13, y + 5, String(voltage, 2) + "v", CENTER); } } //######################################################################################### void Designer::Nodata(int x, int y, bool IconSize, String IconName) { if (IconSize ==LargeIcon) ecran->fonts.setFont(FONT_24); else ecran->fonts.setFont(FONT_10); ecran->drawString(x - 3, y - 10, "?", CENTER); ecran->fonts.setFont(FONT_08); }