From 83cc2dae9d7aa3d1c491ec39ef468e32e6830376 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Wed, 5 Apr 2023 08:55:17 +0200 Subject: [PATCH 01/69] fix Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef77fc8..7fb6955 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Maximum power your solar cells can deliver. #### Cells efficiency [%] Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. -#### Cell area [m\u00b2] +#### Cell area [m2] Size of the active area your solar panel. #### Diffuse radiation efficiency [%] From e9cb25471e40112316fdea9d93b5d83409f08e61 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 6 Apr 2023 10:12:17 +0200 Subject: [PATCH 02/69] add shading --- README.md | 5 ++ .../woheller69/weather/SolarPowerPlant.java | 27 ++++-- .../activities/ManageLocationsActivity.java | 78 +++++++++++++++++- .../weather/database/CityToWatch.java | 2 +- .../weather/ui/Help/InputFilterMinMax.java | 32 +++++++ .../ui/RecycleList/CityWeatherAdapter.java | 11 ++- .../ui/RecycleList/CourseOfDayAdapter.java | 10 ++- .../RecyclerOverviewListAdapter.java | 4 +- .../open_meteo/OMDataExtractor.java | 2 +- .../main/res/layout/dialog_edit_location.xml | 17 +++- .../res/layout/list_item_course_of_day.xml | 11 ++- app/src/main/res/values-de/strings.xml | 8 +- app/src/main/res/values/strings.xml | 6 +- .../en-US/images/phoneScreenshots/04.png | Bin 0 -> 55783 bytes 14 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java create mode 100644 fastlane/metadata/android/en-US/images/phoneScreenshots/04.png diff --git a/README.md b/README.md index 7fb6955..477610e 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,11 @@ Maximum power of your inverter. If it is lower than the maximum power of your pa #### Inverter efficiency [%] Efficiency of your inverter. +#### Shading +In this section you can define the shading on your solar panels. +For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. +For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. + ## License This app is licensed under the GPLv3. diff --git a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java index c90dd57..d4a0d55 100644 --- a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java +++ b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java @@ -19,8 +19,10 @@ public class SolarPowerPlant { double inverterEfficiency; double azimuthAngle; double tiltAngle; + private int[] shadingElevation; + private int[] shadingOpacity; - public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle) { + public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) { this.latitude = latitude; this.longitude = longitude; this.cellsMaxPower = cellsMaxPower; @@ -31,6 +33,8 @@ public class SolarPowerPlant { this.inverterEfficiency = inverterEfficiency / 100; this.azimuthAngle = azimuthAngle; this.tiltAngle = tiltAngle; + this.shadingElevation = shadingElevation; + this.shadingOpacity = shadingOpacity; } @@ -51,14 +55,21 @@ public class SolarPowerPlant { Double[] normalPanel = {Math.sin(azimuthAngle / 180 * Math.PI) * Math.cos((90 - tiltAngle) / 180 * Math.PI), Math.cos(azimuthAngle / 180 * Math.PI) * Math.cos((90 - tiltAngle) / 180 * Math.PI), Math.sin((90 - tiltAngle) / 180 * Math.PI)}; double efficiency = 0; //calculate scalar product of sunDirection and normalPanel vectors - for (int j = 0; j < directionSun.length; j++) { - efficiency += directionSun[j] * normalPanel[j]; + if(solarPowerNormal>0) { //only needed if normal radiation is available + for (int j = 0; j < directionSun.length; j++) { + efficiency += directionSun[j] * normalPanel[j]; + } + + efficiency = Math.max(0, efficiency); //scalar product is negative if sun points to back of module. set 0 in this case + + if (efficiency > 0) { + //Calculate shading in 10 degree ranges, total 36 ranges + int shadingIndex = ((((int) Math.round((solarAzimuth + 5) / 10)) - 1) % 36 + 36) % 36; + if (shadingElevation[shadingIndex] > solarElevation) { + efficiency *= (double) (100 - shadingOpacity[shadingIndex])/100; + } + } } - - efficiency = Math.max(0,efficiency); //scalar product is negative if sun points to back of module. set 0 in this case - - //TODO solarPowerDiffuse ignored so far - double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea; double acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit); diff --git a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java index 118d5b7..333df47 100644 --- a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java @@ -10,11 +10,16 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.ItemTouchHelper; import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; import org.woheller69.weather.R; @@ -22,6 +27,7 @@ import org.woheller69.weather.database.City; import org.woheller69.weather.database.CityToWatch; import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.dialogs.AddLocationDialogOmGeocodingAPI; +import org.woheller69.weather.ui.Help.InputFilterMinMax; import org.woheller69.weather.ui.RecycleList.RecyclerItemClickListener; import org.woheller69.weather.ui.RecycleList.RecyclerOverviewListAdapter; import org.woheller69.weather.ui.RecycleList.SimpleItemTouchHelperCallback; @@ -111,11 +117,66 @@ public class ManageLocationsActivity extends NavigationActivity { } private void editCityToWatch(CityToWatch city) { + int[] shadingElevation = city.getShadingElevation(); + int[] shadingOpacity = city.getShadingOpacity(); + TextView[] textViews = new TextView[shadingElevation.length]; + EditText[] elevationViews = new EditText[shadingElevation.length]; + EditText[] opacityViews = new EditText[shadingElevation.length]; + AlertDialog.Builder alert = new AlertDialog.Builder(context); LayoutInflater inflater = getLayoutInflater(); View dialogView = inflater.inflate(R.layout.dialog_edit_location, null); + ViewGroup shading = (ViewGroup) dialogView.findViewById(R.id.edit_Location_shading); + + LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); + p.weight = 1; + + LinearLayout header = new LinearLayout(this); + TextView headerRange = new TextView(this); + TextView headerMinElevation = new TextView(this); + TextView headerOpacity = new TextView(this); + headerRange.setText(R.string.edit_location_shading_azimuth_heading); + headerRange.setPadding(5,0,5,0); + headerMinElevation.setText(R.string.edit_location_shading_solar_elevation_heading); + headerMinElevation.setPadding(5,0,5,0); + headerOpacity.setText(R.string.edit_location_shading_opacity_heading); + headerOpacity.setPadding(5,0,5,0); + headerRange.setLayoutParams(p); + headerMinElevation.setLayoutParams(p); + headerOpacity.setLayoutParams(p); + header.addView(headerRange); + header.addView(headerMinElevation); + header.addView(headerOpacity); + shading.addView(header); + + LinearLayout[] container = new LinearLayout[shadingElevation.length]; + for (int i = 0; i < shadingElevation.length; i++) { + container[i] = new LinearLayout(this); + container[i].setOrientation(LinearLayout.HORIZONTAL); + textViews[i] = new TextView(this); + opacityViews[i] = new EditText(this); + elevationViews[i] = new EditText(this); + textViews[i].setText("[" + (i*10) +","+ (i*10+10)+"]"); + textViews[i].setTextSize(18); + elevationViews[i].setText(Integer.toString(shadingElevation[i])); + elevationViews[i].setInputType(InputType.TYPE_CLASS_NUMBER); + elevationViews[i].setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)}); + elevationViews[i].setTextSize(18); + opacityViews[i].setText(Integer.toString(shadingOpacity[i])); + opacityViews[i].setInputType(InputType.TYPE_CLASS_NUMBER); + opacityViews[i].setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); + opacityViews[i].setTextSize(18); + textViews[i].setLayoutParams(p); + elevationViews[i].setLayoutParams(p); + opacityViews[i].setLayoutParams(p); + container[i].addView(textViews[i]); + container[i].addView(elevationViews[i]); + container[i].addView(opacityViews[i]); + shading.addView(container[i]); + } + alert.setTitle(getString(R.string.edit_location_title)); alert.setView(dialogView); EditText editLatitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lat); @@ -132,15 +193,22 @@ public class ManageLocationsActivity extends NavigationActivity { editCity.setText(city.getCityName()); editLatitude.setText(Float.toString(city.getLatitude())); + editLatitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-90, 90)}); editLongitude.setText(Float.toString(city.getLongitude())); + editLongitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-180, 180)}); editAzimuth.setText(Float.toString(city.getAzimuthAngle())); + editAzimuth.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 360)}); editTilt.setText(Float.toString(city.getTiltAngle())); + editTilt.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)}); editCellsMaxPower.setText(Float.toString(city.getCellsMaxPower())); editCellsArea.setText(Float.toString(city.getCellsArea())); editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency())); + editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency())); + editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit())); editInverterEfficiency.setText(Float.toString(city.getInverterEfficiency())); + editInverterEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editTilt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @@ -156,6 +224,11 @@ public class ManageLocationsActivity extends NavigationActivity { }); alert.setPositiveButton(getString(R.string.dialog_edit_change_button), (dialog, whichButton) -> { + + for (int i = 0; i < shadingElevation.length ; i++) { + shadingElevation[i]= Integer.parseInt(elevationViews[i].getText().toString().isEmpty() ? "0" : elevationViews[i].getText().toString()); + shadingOpacity[i]= Integer.parseInt(opacityViews[i].getText().toString().isEmpty() ? "0" : opacityViews[i].getText().toString()); + } adapter.updateCity(city, String.valueOf(editCity.getText()), Float.parseFloat(editLatitude.getText().toString().isEmpty() ? "0" : editLatitude.getText().toString()), Float.parseFloat(editLongitude.getText().toString().isEmpty() ? "0" : editLongitude.getText().toString()), @@ -166,7 +239,9 @@ public class ManageLocationsActivity extends NavigationActivity { Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()), Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()), Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()), - Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()) + Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()), + shadingElevation, + shadingOpacity ); }); alert.setNegativeButton(getString(R.string.dialog_add_close_button), (dialog, whichButton) -> { @@ -208,4 +283,5 @@ public class ManageLocationsActivity extends NavigationActivity { selectedCity.getCityName() ); } + } diff --git a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java index f8742cf..081e2b1 100644 --- a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java +++ b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java @@ -24,7 +24,7 @@ public class CityToWatch { private float tiltAngle; private int rank; private int[] shadingElevation = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; public CityToWatch() { } diff --git a/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java new file mode 100644 index 0000000..029afa8 --- /dev/null +++ b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java @@ -0,0 +1,32 @@ +package org.woheller69.weather.ui.Help; + +import android.text.InputFilter; +import android.text.Spanned; + +public class InputFilterMinMax implements InputFilter { + + private int min, max; + + public InputFilterMinMax(int min, int max) { + this.min = min; + this.max = max; + } + + + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { + String oldString = dest.toString(); + String insertString = source.toString(); + String newString = new StringBuilder(oldString).insert(dstart,insertString).toString(); + float input = Float.parseFloat(newString); + if (isInRange(min, max, input)) + return null; + else + return ""; + } + + private boolean isInRange(int a, int b, float c) { + return b > a ? c >= a && c <= b : c >= b && c <= a; + } + +} diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java index 91c42b4..03b16eb 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java @@ -3,8 +3,11 @@ package org.woheller69.weather.ui.RecycleList; import android.content.Context; import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; +import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + +import android.content.SharedPreferences; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -67,12 +70,16 @@ public class CityWeatherAdapter extends RecyclerView.Adapter hourlyForecasts) { - + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); courseDayList = new ArrayList<>(); for (HourlyForecast f : hourlyForecasts) { - if (f.getForecastTime() >= System.currentTimeMillis()) { + if (sp.getBoolean("pref_debug",false)) { courseDayList.add(f); + } else { + if (f.getForecastTime() >= System.currentTimeMillis()) { + courseDayList.add(f); + } } } notifyDataSetChanged(); diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java index 7acbd77..269cd0d 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java @@ -98,15 +98,22 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_course_of_day.xml b/app/src/main/res/layout/list_item_course_of_day.xml index 734d29e..2a84360 100644 --- a/app/src/main/res/layout/list_item_course_of_day.xml +++ b/app/src/main/res/layout/list_item_course_of_day.xml @@ -48,7 +48,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="3dp" - android:text="xy W" + android:text="xy Wh" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/colorPrimaryDark" /> + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e985cc7..436376d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -96,7 +96,7 @@ OK Nein Vielleicht später - Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub oder spendieren Sie dem Entwickler einen Kaffee über PayPal. + Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal. Luftdruck anzeigen Nur manuell aktualisieren 15 min @@ -116,7 +116,7 @@ Anzahl der prognostizierten Tage Breitengrad [°] Längengrad [°] - Azimuth [°] + Azimut [°] Neigung [°] Maximalleistung Zelle [W] Wirkungsgrad Zelle [%] @@ -126,5 +126,9 @@ Effizienz Wechselrichter [%] Wh Ort bearbeiten + Azimut Bereich [°] + Min. Elevation der Sonne [°] + Abschattung unterhalb dieser Elevation [%] + Abschattung diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 60d0e1f..4c6961f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,7 +104,7 @@ OK No Maybe later - Do you like this app? Please give a star on GitHub or buy the developer a coffee via PayPal. + Do you like this app? Please give a star on GitHub and buy the developer a coffee via PayPal. Show air pressure Update Location Manual update only @@ -128,4 +128,8 @@ Inverter efficiency [%] Wh Edit location + Azimuth range [°] + Min. solar elevation [°] + Shading below this elevation [%] + Shading diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png new file mode 100644 index 0000000000000000000000000000000000000000..7c82f6a8d8aa1945e6a0441a9838217b26cb5d8e GIT binary patch literal 55783 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 I;pLnE0r>?fI{*Lx literal 0 HcmV?d00001 From be0aa9162183c585ddf1dd37c5b73644ea164630 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 6 Apr 2023 14:18:52 +0200 Subject: [PATCH 03/69] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 477610e..0819b1b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This app takes direct and diffuse radiation data from Open-Meteo.com, calculates of the sun and projects the radiation on your solar panel. It shows the estimated energy production for the next hours and up to 16 days. - + ## Parameters From 4a81cd5c1f5ad3d2c38a87298e8cee8d6ad0e4df Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 6 Apr 2023 14:20:35 +0200 Subject: [PATCH 04/69] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0819b1b..98c1bc9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This app takes direct and diffuse radiation data from Open-Meteo.com, calculates of the sun and projects the radiation on your solar panel. It shows the estimated energy production for the next hours and up to 16 days. - + ## Parameters From 36a3e02c5282b6ecdcc5bff84c52898b22831e31 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Sat, 8 Apr 2023 17:46:35 +0200 Subject: [PATCH 05/69] add help activity --- README.md | 1 + app/src/main/AndroidManifest.xml | 9 ++++ app/src/main/assets/help/help-de.html | 50 +++++++++++++++++++ app/src/main/assets/help/help-en.html | 50 +++++++++++++++++++ .../weather/activities/HelpActivity.java | 44 ++++++++++++++++ .../activities/NavigationActivity.java | 7 ++- app/src/main/res/drawable/ic_help_24px.xml | 9 ++++ app/src/main/res/layout/activity_help.xml | 37 ++++++++++++++ .../main/res/menu/activity_main_drawer.xml | 4 ++ app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 11 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 app/src/main/assets/help/help-de.html create mode 100644 app/src/main/assets/help/help-en.html create mode 100644 app/src/main/java/org/woheller69/weather/activities/HelpActivity.java create mode 100644 app/src/main/res/drawable/ic_help_24px.xml create mode 100644 app/src/main/res/layout/activity_help.xml diff --git a/README.md b/README.md index 98c1bc9..2945ccb 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ solXpect forecasts the output of your solar power plant This app takes direct and diffuse radiation data from Open-Meteo.com, calculates the position of the sun and projects the radiation on your solar panel. It shows the estimated energy production for the next hours and up to 16 days. +The hourly values are calculated for the preceding hour. So if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a300af7..a1dac11 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,6 +46,15 @@ android:name="android.support.PARENT_ACTIVITY" android:value="org.woheller69.weather.activities.ForecastCityActivity" /> + + + + +

Übersicht

+ +Diese App nimmt direkte und diffuse Strahlungsdaten von Open-Meteo.com, berechnet die Position +der Sonne und projiziert die Strahlung auf Ihr Solarpanel. +Es zeigt die geschätzte Energieproduktion für die nächsten Stunden und bis zu 16 Tage an. +Die Stundenwerte werden für die vorangegangene Stunde berechnet. +Wenn also um 11:00 Uhr 150 Wh angezeigt werden, bedeutet dies, dass Sie zwischen 10:00 und 11:00 Uhr mit 150 Wh rechnen können. + +

Parameter

+ +

Breitengrad [°]

+Der Breitengrad gibt die Nord-Süd-Position Ihres Solarkraftwerks an. Er reicht von –90° am Südpol bis 90° am Nordpol. + +

Längengrad [°]

+Der Längengrad gibt die Ost-West-Position Ihres Solarkraftwerks an. Der Nullmeridian definiert 0° Länge. Positive Längen liegen östlich des Nullmeridians, negative westlich. + +

Azimut [°]

+Azimut ist die horizontale Richtung Ihres Solarkraftwerks. 0° entspricht Norden, 90° entspricht Osten, 180° entspricht Süden, 270° entspricht Westen. + +

Neigung [°]

+Neigung ist die vertikale Richtung Ihres Solarkraftwerks. 0° bedeutet, dass es zum Himmel zeigt, 90° bedeutet, dass es vertikal ausgerichtet ist und zum Horizont zeigt. + +

Zellen max. Leistung [W]

+Maximale Leistung, die Ihre Solarzellen liefern können. + +

Zelleneffizienz [%]

+Energieanteil in Form von Sonnenlicht, der von der Solarzelle in Strom umgewandelt werden kann. + +

Zellenfläche [m2]

+Größe der aktiven Fläche Ihres Solarpanels. + +

Diffuse Strahlungseffizienz [%]

+Wirkungsgrad Ihres Solarkraftwerks bei diffuser Strahlung. Bei der Ausrichtung nach oben sollte er etwa 100 % betragen, bei der Ausrichtung zum Horizont etwa 50 %. +Dies ist auch abhängig von Reflexionen etc. + +

Wechselrichterleistung [W]

+Maximale Leistung Ihres Wechselrichters. Wenn sie niedriger als die maximale Leistung Ihrer Panels ist, wird die Ausgangsleistung Ihres Systems durch diesen Parameter begrenzt. + +

Wirkungsgrad des Wechselrichters [%]

+Wirkungsgrad Ihres Wechselrichters. + +

Abschattung

+In diesem Abschnitt können Sie die Abschattung Ihrer Solarmodule definieren. +Für jeden Azimutwinkelbereich können Sie die minimale Elevation der Sonne angeben, die erforderlich ist, damit die Sonne auf die Solarmodule trifft. +Für Elevationen unter diesem Wert können Sie den Prozentsatz der Abschattung festlegen. Beispielsweise reduziert ein Gebäude die Strahlung um 100 %, ein Baum vielleicht nur um 60 %. + + + \ No newline at end of file diff --git a/app/src/main/assets/help/help-en.html b/app/src/main/assets/help/help-en.html new file mode 100644 index 0000000..a7437a4 --- /dev/null +++ b/app/src/main/assets/help/help-en.html @@ -0,0 +1,50 @@ + + +

Overview

+ +This app takes direct and diffuse radiation data from Open-Meteo.com, calculates the position +of the sun and projects the radiation on your solar panel. +It shows the estimated energy production for the next hours and up to 16 days. +The hourly values are calculated for the preceding hour. +So if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00. + +

Parameters

+ +

Latitude [°]

+Latitude specifies the north–south position of your solar power plant. It ranges from –90° at the south pole to 90° at the north pole. + +

Longitude [°]

+Longitude specifies the east–west position of your solar power plant. The prime meridian defines 0° longitude. Positive longitudes are east of the prime meridian, negative ones are west. + +

Azimuth [°]

+Azimuth is the horizontal direction of your solar power plant. 0° equals North, 90° equals East, 180° equals South, 270° equals West. + +

Tilt [°]

+Tilt is the vertical direction of your solar power plant. 0° means it points up towards the the sky, 90° means it has a vertical orientation and points towards the horizon. + +

Cells max power [W]

+Maximum power your solar cells can deliver. + +

Cells efficiency [%]

+Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. + +

Cell area [m2]

+Size of the active area your solar panel. + +

Diffuse radiation efficiency [%]

+Efficiency of your solar power plant for diffuse radiation. When pointing up it should be around 100%, when pointing to the horizon it may be around 50%. +Also depends on reflections etc. + +

Inverter power [W]

+Maximum power of your inverter. If it is lower than the maximum power of your panels the output power of your system will be limited by this parameter. + +

Inverter efficiency [%]

+Efficiency of your inverter. + +

Shading

+In this section you can define the shading on your solar panels. +For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. +For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. + + + diff --git a/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java b/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java new file mode 100644 index 0000000..08639f0 --- /dev/null +++ b/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java @@ -0,0 +1,44 @@ +package org.woheller69.weather.activities; + +import android.content.res.AssetManager; +import android.os.Bundle; +import android.webkit.WebView; +import org.woheller69.weather.R; + +import androidx.appcompat.app.ActionBar; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class HelpActivity extends NavigationActivity{ + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_help); + WebView view = findViewById(R.id.help); + + String language = getResources().getConfiguration().getLocales().get(0).getLanguage(); + + String filename = "help-"+language+".html"; + + AssetManager am = getAssets(); + try { + List mapList = Arrays.asList(am.list("help")); + + if (!mapList.contains(filename)) { + filename = "help-en.html"; + } + } catch ( IOException ex){ + ex.printStackTrace(); + } + + view.loadUrl("file:///android_asset/help/"+ filename); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + } +} diff --git a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java index 6d2e3f7..70193bf 100644 --- a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java @@ -174,10 +174,13 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio startActivity(intent); }else if (itemId==R.id.nav_about) { intent = new Intent(this, AboutActivity.class); - createBackStack(intent); + startActivity(intent); }else if(itemId==R.id.nav_settings) { intent = new Intent(this, SettingsActivity.class); - createBackStack(intent); + startActivity(intent); + }else if(itemId==R.id.nav_help) { + intent = new Intent(this, HelpActivity.class); + startActivity(intent); }else if (itemId==R.id.star_on_github){ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.GITHUB_URL))); diff --git a/app/src/main/res/drawable/ic_help_24px.xml b/app/src/main/res/drawable/ic_help_24px.xml new file mode 100644 index 0000000..956415a --- /dev/null +++ b/app/src/main/res/drawable/ic_help_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_help.xml b/app/src/main/res/layout/activity_help.xml new file mode 100644 index 0000000..0a90b44 --- /dev/null +++ b/app/src/main/res/layout/activity_help.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 9b81cdb..d8289ca 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -21,6 +21,10 @@ android:id="@+id/nav_settings" android:icon="@drawable/ic_settings_24px" android:title="@string/activity_settings" /> + Min. Elevation der Sonne [°] Abschattung unterhalb dieser Elevation [%] Abschattung + Anleitung diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c6961f..34818f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,4 +132,5 @@ Min. solar elevation [°] Shading below this elevation [%] Shading + Instructions From c45ab41ce5dcef632aa0f45b44ca6e603b666405 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Mon, 10 Apr 2023 09:47:07 +0200 Subject: [PATCH 06/69] remove unused stuff --- .../weather/activities/NavigationActivity.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java index 70193bf..7efa3e1 100644 --- a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java @@ -144,18 +144,6 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio } } - /** - * Enables back navigation for activities that are launched from the NavBar. See - * {@code AndroidManifest.xml} to find out the parent activity names for each activity. - * - * @param intent - */ - private void createBackStack(Intent intent) { - TaskStackBuilder builder = TaskStackBuilder.create(this); - builder.addNextIntentWithParentStack(intent); - builder.startActivities(); - } - private void callDrawerItem(final int itemId) { Intent intent; From fc7844f6f66f334d6836f995c7225ea21969727a Mon Sep 17 00:00:00 2001 From: woheller69 Date: Mon, 10 Apr 2023 21:39:56 +0200 Subject: [PATCH 07/69] highlight drawer item for help activity --- .../java/org/woheller69/weather/activities/HelpActivity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java b/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java index 08639f0..90cef72 100644 --- a/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/HelpActivity.java @@ -41,4 +41,9 @@ public class HelpActivity extends NavigationActivity{ actionBar.setDisplayHomeAsUpEnabled(true); } } + + @Override + protected int getNavigationDrawerID() { + return R.id.nav_help; + } } From 555c17bdd11f018eb1b8faebe6611b6ddf469a60 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Tue, 11 Apr 2023 21:39:33 +0200 Subject: [PATCH 08/69] add button to show current sun position --- .../activities/ForecastCityActivity.java | 28 +++++++++++++++++++ .../main/res/drawable/ic_solar_power_24px.xml | 9 ++++++ .../main/res/drawable/ic_wb_sunny_24px.xml | 2 +- .../main/res/menu/activity_forecast_city.xml | 10 +++---- .../main/res/menu/activity_main_drawer.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 7 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable/ic_solar_power_24px.xml diff --git a/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java b/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java index 10082f4..6ac27bb 100644 --- a/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import androidx.appcompat.app.AlertDialog; import androidx.preference.PreferenceManager; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; @@ -19,17 +20,24 @@ import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.TextView; +import net.e175.klaus.solarpositioning.AzimuthZenithAngle; +import net.e175.klaus.solarpositioning.DeltaT; +import net.e175.klaus.solarpositioning.Grena3; import org.woheller69.weather.R; import org.woheller69.weather.database.GeneralData; import org.woheller69.weather.database.HourlyForecast; import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.WeekForecast; +import org.woheller69.weather.ui.Help.StringFormatUtils; import org.woheller69.weather.ui.updater.IUpdateableCityUI; import org.woheller69.weather.ui.updater.ViewUpdater; import org.woheller69.weather.ui.viewPager.WeatherPagerAdapter; import java.lang.reflect.Field; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.List; public class ForecastCityActivity extends NavigationActivity implements IUpdateableCityUI { @@ -179,6 +187,26 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea WeatherPagerAdapter.refreshSingleData(getApplicationContext(),true, pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem())); ForecastCityActivity.startRefreshAnimation(); } + } else if (id==R.id.menu_sun_position){ + Long time = System.currentTimeMillis()/1000; + Instant i = Instant.ofEpochSecond(time); //currentTimeMillis is in GMT + ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT")); + AzimuthZenithAngle position = Grena3.calculateSolarPosition( + dateTime, + db.getCityToWatch(cityId).getLatitude(), + db.getCityToWatch(cityId).getLongitude(), + DeltaT.estimate(dateTime.toLocalDate())); // delta T (s) + + String solarAzimuth = StringFormatUtils.formatDecimal((float) position.getAzimuth(),""); + String solarElevation = StringFormatUtils.formatDecimal((float) (90 - position.getZenithAngle()),""); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.action_sun_position); + + alertDialogBuilder.setMessage(getString(R.string.edit_location_hint_azimuth)+": "+solarAzimuth + " \n" + getString(R.string.action_sun_elevation) + ": " + solarElevation); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), (dialog, which) -> { }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); } return super.onOptionsItemSelected(item); diff --git a/app/src/main/res/drawable/ic_solar_power_24px.xml b/app/src/main/res/drawable/ic_solar_power_24px.xml new file mode 100644 index 0000000..f328add --- /dev/null +++ b/app/src/main/res/drawable/ic_solar_power_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_wb_sunny_24px.xml b/app/src/main/res/drawable/ic_wb_sunny_24px.xml index 4f7c019..36167d1 100644 --- a/app/src/main/res/drawable/ic_wb_sunny_24px.xml +++ b/app/src/main/res/drawable/ic_wb_sunny_24px.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/app/src/main/res/menu/activity_forecast_city.xml b/app/src/main/res/menu/activity_forecast_city.xml index 0cc6a61..1403670 100644 --- a/app/src/main/res/menu/activity_forecast_city.xml +++ b/app/src/main/res/menu/activity_forecast_city.xml @@ -2,14 +2,12 @@
+ android:title="@string/action_sun_position" /> Abschattung unterhalb dieser Elevation [%] Abschattung Anleitung + Sonnenposition + Elevation [°] diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34818f7..15f1ffc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -133,4 +133,6 @@ Shading below this elevation [%] Shading Instructions + Sun position + Elevation [°] From ab477f2cabe57e02458251e808eda805a24d3878 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Wed, 12 Apr 2023 17:37:21 +0200 Subject: [PATCH 09/69] update V1.1 --- README.md | 6 +++++- app/build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/11.txt | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/11.txt diff --git a/README.md b/README.md index 2945ccb..1e9e53e 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,12 @@ Maximum power your solar cells can deliver. #### Cells efficiency [%] Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. +#### Temperature coefficient [%/K] +Dependence of the cell power on temperature (usually in the range of -0.4%/K). +Cell temperature is estimated from ambient temperature and total irradiance. + #### Cell area [m2] -Size of the active area your solar panel. +Size of your solar panel. #### Diffuse radiation efficiency [%] Efficiency of your solar power plant for diffuse radiation. When pointing up it should be around 100%, when pointing to the horizon it may be around 50%. diff --git a/app/build.gradle b/app/build.gradle index ff0787a..eca612e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "org.woheller69.solxpect" minSdkVersion 26 targetSdkVersion 31 - versionCode 10 - versionName "1.0" + versionCode 11 + versionName "1.1" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\"" diff --git a/fastlane/metadata/android/en-US/changelogs/11.txt b/fastlane/metadata/android/en-US/changelogs/11.txt new file mode 100644 index 0000000..4a7aed0 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/11.txt @@ -0,0 +1,3 @@ +Shading taken into account +Display current sun position +Help \ No newline at end of file From a1f14681fd4a85915072aad938fc0c1943346ce5 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 13 Apr 2023 08:52:54 +0200 Subject: [PATCH 10/69] Estimate panel temperature from ambient temperature and radiation Additional parameter: temperature coefficient Adapt efficiency according to these values Bugfix input filter Prepare V1.2 --- app/build.gradle | 4 +- app/src/main/assets/help/help-de.html | 5 ++- app/src/main/assets/help/help-en.html | 5 ++- .../woheller69/weather/SolarPowerPlant.java | 21 ++++++++-- .../activities/ManageLocationsActivity.java | 4 ++ .../weather/database/CityToWatch.java | 26 ++++++++---- .../weather/database/SQLiteHelper.java | 22 ++++++++-- .../weather/ui/Help/InputFilterMinMax.java | 35 +++++++++++----- .../ui/RecycleList/ItemViewHolder.java | 2 + .../RecyclerOverviewListAdapter.java | 4 +- .../open_meteo/OMDataExtractor.java | 7 +++- .../weather_api/open_meteo/OMHttpRequest.java | 2 +- app/src/main/res/drawable/ic_sunny_24px.xml | 9 +++++ .../main/res/drawable/ic_wb_sunny_24px.xml | 9 ----- app/src/main/res/layout/card_day.xml | 1 - app/src/main/res/layout/card_details.xml | 5 --- .../main/res/layout/dialog_edit_location.xml | 10 +++++ .../main/res/layout/list_item_city_list.xml | 8 ++++ .../main/res/menu/activity_forecast_city.xml | 2 +- app/src/main/res/values-de/strings.xml | 40 +------------------ app/src/main/res/values/arrays.xml | 18 --------- app/src/main/res/values/strings.xml | 39 +----------------- .../metadata/android/en-US/changelogs/12.txt | 6 +++ 23 files changed, 139 insertions(+), 145 deletions(-) create mode 100644 app/src/main/res/drawable/ic_sunny_24px.xml delete mode 100644 app/src/main/res/drawable/ic_wb_sunny_24px.xml create mode 100644 fastlane/metadata/android/en-US/changelogs/12.txt diff --git a/app/build.gradle b/app/build.gradle index eca612e..5a0a807 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "org.woheller69.solxpect" minSdkVersion 26 targetSdkVersion 31 - versionCode 11 - versionName "1.1" + versionCode 12 + versionName "1.2" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\"" diff --git a/app/src/main/assets/help/help-de.html b/app/src/main/assets/help/help-de.html index fd26d8e..d615019 100644 --- a/app/src/main/assets/help/help-de.html +++ b/app/src/main/assets/help/help-de.html @@ -28,8 +28,11 @@ Maximale Leistung, die Ihre Solarzellen liefern können.

Zelleneffizienz [%]

Energieanteil in Form von Sonnenlicht, der von der Solarzelle in Strom umgewandelt werden kann. +

Temperaturkoeffizient [%/K]

+Abhängigkeit der Leistung der Solarmodule von der Temperatur (normalerweise im Bereich von -0.4%/K). +

Zellenfläche [m2]

-Größe der aktiven Fläche Ihres Solarpanels. +Fläche Ihres Solarpanels.

Diffuse Strahlungseffizienz [%]

Wirkungsgrad Ihres Solarkraftwerks bei diffuser Strahlung. Bei der Ausrichtung nach oben sollte er etwa 100 % betragen, bei der Ausrichtung zum Horizont etwa 50 %. diff --git a/app/src/main/assets/help/help-en.html b/app/src/main/assets/help/help-en.html index a7437a4..9c54d80 100644 --- a/app/src/main/assets/help/help-en.html +++ b/app/src/main/assets/help/help-en.html @@ -28,8 +28,11 @@ Maximum power your solar cells can deliver.

Cells efficiency [%]

Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. +

Temperature coefficient [%/K]

+Dependence of the cell power on temperature (usually in the range of -0.4%/K). +

Cell area [m2]

-Size of the active area your solar panel. +Size of your solar panel.

Diffuse radiation efficiency [%]

Efficiency of your solar power plant for diffuse radiation. When pointing up it should be around 100%, when pointing to the horizon it may be around 50%. diff --git a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java index d4a0d55..d893bc1 100644 --- a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java +++ b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java @@ -14,6 +14,7 @@ public class SolarPowerPlant { double cellsMaxPower; double cellsArea; double cellsEfficiency; + double cellsTempCoeff; double diffuseEfficiency; double inverterPowerLimit; double inverterEfficiency; @@ -22,7 +23,7 @@ public class SolarPowerPlant { private int[] shadingElevation; private int[] shadingOpacity; - public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) { + public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double cellsTempCoeff, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) { this.latitude = latitude; this.longitude = longitude; this.cellsMaxPower = cellsMaxPower; @@ -35,10 +36,11 @@ public class SolarPowerPlant { this.tiltAngle = tiltAngle; this.shadingElevation = shadingElevation; this.shadingOpacity = shadingOpacity; + this.cellsTempCoeff = cellsTempCoeff / 100; } - public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds) { + public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds, float ambientTemperature) { Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT")); @@ -70,10 +72,23 @@ public class SolarPowerPlant { } } } - double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea; + + float totalRadiationOnCell = (float) (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency); //flat plate equivalent of the solar irradiance + float cellTemperature = calcCellTemperature(ambientTemperature,totalRadiationOnCell); + + double dcPower = totalRadiationOnCell * cellsEfficiency * (1+(cellTemperature - 25)*cellsTempCoeff) * cellsArea; double acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit); return (float) acPower; } + + public static float calcCellTemperature(float ambientTemperature, float totalIrradiance){ + //models from here: https://www.scielo.br/j/babt/a/FBq5Pmm4gSFqsfh3V8MxfGN/ Photovoltaic Cell Temperature Estimation for a Grid-Connect Photovoltaic Systems in Curitiba + //float cellTemperature = 30.006f + 0.0175f*(totalIrradiance-300f)+1.14f*(ambientTemperature-25f); //Lasnier and Ang Lasnier, F.; Ang, T. G. Photovoltaic engineering handbook, 1st ed.; IOP Publishing LTD: Lasnier, France, 1990; pp. 258. + //float cellTemperature = ambientTemperature + 0.028f*totalIrradiance-1f; //Schott Schott, T. Operation temperatures of PV modules. Photovoltaic solar energy conference 1985, pp. 392-396. + float cellTemperature = ambientTemperature + 0.0342f*totalIrradiance; //Ross model: https://www.researchgate.net/publication/275438802_Thermal_effects_of_the_extended_holographic_regions_for_holographic_planar_concentrator + //assuming "not so well cooled" : 0.0342 + return cellTemperature; + } } diff --git a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java index 333df47..8bfaa41 100644 --- a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java @@ -187,6 +187,7 @@ public class ManageLocationsActivity extends NavigationActivity { EditText editCellsMaxPower = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Max_Power); EditText editCellsArea = (EditText) dialogView.findViewById(R.id.EditLocation_Cells_Area); EditText editCellsEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Efficiency); + EditText editCellsTempCoeff = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Temp_Coeff); EditText editDiffuseEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Diffuse_Efficiency); EditText editInverterPowerLimit = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Power_Limit); EditText editInverterEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Efficiency); @@ -204,6 +205,8 @@ public class ManageLocationsActivity extends NavigationActivity { editCellsArea.setText(Float.toString(city.getCellsArea())); editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency())); editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); + editCellsTempCoeff.setText(Float.toString(city.getCellsTempCoeff())); + editCellsTempCoeff.setFilters(new InputFilter[]{ new InputFilterMinMax(-100, 100)}); editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency())); editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit())); @@ -237,6 +240,7 @@ public class ManageLocationsActivity extends NavigationActivity { Float.parseFloat(editCellsMaxPower.getText().toString().isEmpty() ? "0" : editCellsMaxPower.getText().toString()), Float.parseFloat(editCellsArea.getText().toString().isEmpty() ? "0" : editCellsArea.getText().toString()), Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()), + Float.parseFloat(editCellsTempCoeff.getText().toString().isEmpty() ? "0" : editCellsTempCoeff.getText().toString()), Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()), Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()), Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()), diff --git a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java index 081e2b1..bc68368 100644 --- a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java +++ b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java @@ -17,6 +17,7 @@ public class CityToWatch { private float cellsMaxPower; private float cellsArea; private float cellsEfficiency; + private float cellsTempCoeff; private float diffuseEfficiency; private float inverterPowerLimit; private float inverterEfficiency; @@ -36,14 +37,15 @@ public class CityToWatch { this.id = id; this.cityId = cityId; this.cityName = cityName; - this.cellsMaxPower=650; - this.cellsArea=3.18f; - this.cellsEfficiency=19.3f; - this.diffuseEfficiency=40; - this.inverterPowerLimit =600; - this.inverterEfficiency =95; - this.azimuthAngle=170; - this.tiltAngle =90; + this.cellsMaxPower = 650; + this.cellsArea = 3.18f; + this.cellsEfficiency = 19.3f; + this.cellsTempCoeff = -0.4f; + this.diffuseEfficiency = 40; + this.inverterPowerLimit = 600; + this.inverterEfficiency = 95; + this.azimuthAngle = 170; + this.tiltAngle = 90; } @@ -182,4 +184,12 @@ public class CityToWatch { public String getShadingOpacityString() { return Arrays.toString(shadingOpacity).replaceAll("\\[|\\]|\\s", ""); } + + public float getCellsTempCoeff() { + return cellsTempCoeff; + } + + public void setCellsTempCoeff(float cellsTempCoeff) { + this.cellsTempCoeff = cellsTempCoeff; + } } \ No newline at end of file diff --git a/app/src/main/java/org/woheller69/weather/database/SQLiteHelper.java b/app/src/main/java/org/woheller69/weather/database/SQLiteHelper.java index 863f3a8..29a4f83 100644 --- a/app/src/main/java/org/woheller69/weather/database/SQLiteHelper.java +++ b/app/src/main/java/org/woheller69/weather/database/SQLiteHelper.java @@ -20,7 +20,7 @@ import static androidx.core.app.JobIntentService.enqueueWork; */ public class SQLiteHelper extends SQLiteOpenHelper { - private static final int DATABASE_VERSION = 1; + private static final int DATABASE_VERSION = 2; private Context context; private List allCities = new ArrayList<>(); @@ -53,6 +53,7 @@ public class SQLiteHelper extends SQLiteOpenHelper { private static final String CITIES_TO_WATCH_TILT_ANGLE = "tilt_angle"; private static final String CITIES_TO_WATCH_SHADING_ELEVATION = "shading_elevation"; private static final String CITIES_TO_WATCH_SHADING_OPACITY = "shading_opacity"; + private static final String CITIES_TO_WATCH_CELLS_TEMP_COEFF = "cells_temp_coeff"; //Names of columns in TABLE_FORECAST private static final String FORECAST_ID = "forecast_id"; @@ -135,7 +136,8 @@ public class SQLiteHelper extends SQLiteOpenHelper { CITIES_TO_WATCH_AZIMUTH_ANGLE + " REAL NOT NULL," + CITIES_TO_WATCH_TILT_ANGLE + " REAL NOT NULL," + CITIES_TO_WATCH_SHADING_ELEVATION + " VARCHAR(255) NOT NULL," + - CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL)"; + CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL," + + CITIES_TO_WATCH_CELLS_TEMP_COEFF + " REAL NOT NULL)"; public static SQLiteHelper getInstance(Context context) { if (instance == null && context != null) { @@ -160,6 +162,12 @@ public class SQLiteHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + switch(oldVersion) { + case 1: + db.execSQL("ALTER TABLE "+TABLE_CITIES_TO_WATCH+" ADD COLUMN "+CITIES_TO_WATCH_CELLS_TEMP_COEFF+" REAL DEFAULT 0"); + // we want both updates, so no break statement here... + } + } @@ -185,6 +193,7 @@ public class SQLiteHelper extends SQLiteOpenHelper { values.put(CITIES_TO_WATCH_TILT_ANGLE,city.getTiltAngle()); values.put(CITIES_TO_WATCH_SHADING_ELEVATION,city.getShadingElevationString()); values.put(CITIES_TO_WATCH_SHADING_OPACITY,city.getShadingOpacityString()); + values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,city.getCellsTempCoeff()); long id=database.insert(TABLE_CITIES_TO_WATCH, null, values); @@ -218,6 +227,7 @@ public class SQLiteHelper extends SQLiteOpenHelper { ", " + CITIES_TO_WATCH_TILT_ANGLE + ", " + CITIES_TO_WATCH_SHADING_ELEVATION + ", " + CITIES_TO_WATCH_SHADING_OPACITY + + ", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF + ", " + CITIES_TO_WATCH_COLUMN_RANK + " FROM " + TABLE_CITIES_TO_WATCH + " WHERE " + CITIES_TO_WATCH_CITY_ID + " = ?", arguments); @@ -240,7 +250,8 @@ public class SQLiteHelper extends SQLiteOpenHelper { cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12))); cityToWatch.setShadingElevation(cursor.getString(13)); cityToWatch.setShadingOpacity(cursor.getString(14)); - cityToWatch.setRank(Integer.parseInt(cursor.getString(15))); + cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15))); + cityToWatch.setRank(Integer.parseInt(cursor.getString(16))); cursor.close(); } @@ -271,6 +282,7 @@ public class SQLiteHelper extends SQLiteOpenHelper { ", " + CITIES_TO_WATCH_TILT_ANGLE + ", " + CITIES_TO_WATCH_SHADING_ELEVATION + ", " + CITIES_TO_WATCH_SHADING_OPACITY + + ", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF + ", " + CITIES_TO_WATCH_COLUMN_RANK + " FROM " + TABLE_CITIES_TO_WATCH , new String[]{}); @@ -295,7 +307,8 @@ public class SQLiteHelper extends SQLiteOpenHelper { cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12))); cityToWatch.setShadingElevation(cursor.getString(13)); cityToWatch.setShadingOpacity(cursor.getString(14)); - cityToWatch.setRank(Integer.parseInt(cursor.getString(15))); + cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15))); + cityToWatch.setRank(Integer.parseInt(cursor.getString(16))); cityToWatchList.add(cityToWatch); } while (cursor.moveToNext()); @@ -325,6 +338,7 @@ public class SQLiteHelper extends SQLiteOpenHelper { values.put(CITIES_TO_WATCH_TILT_ANGLE,cityToWatch.getTiltAngle()); values.put(CITIES_TO_WATCH_SHADING_ELEVATION,cityToWatch.getShadingElevationString()); values.put(CITIES_TO_WATCH_SHADING_OPACITY,cityToWatch.getShadingOpacityString()); + values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,cityToWatch.getCellsTempCoeff()); database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?", new String[]{String.valueOf(cityToWatch.getId())}); diff --git a/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java index 029afa8..b371e34 100644 --- a/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java +++ b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java @@ -5,9 +5,9 @@ import android.text.Spanned; public class InputFilterMinMax implements InputFilter { - private int min, max; + private float min, max; - public InputFilterMinMax(int min, int max) { + public InputFilterMinMax(float min, float max) { this.min = min; this.max = max; } @@ -15,17 +15,30 @@ public class InputFilterMinMax implements InputFilter { @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - String oldString = dest.toString(); - String insertString = source.toString(); - String newString = new StringBuilder(oldString).insert(dstart,insertString).toString(); - float input = Float.parseFloat(newString); - if (isInRange(min, max, input)) - return null; - else - return ""; + try { + String oldString = dest.toString(); + String insertString = source.toString(); + String newString = oldString.substring(0, dstart) + oldString.substring(dend); + newString = newString.substring(0, dstart) + insertString + newString.substring(dstart); + float input = Float.parseFloat(newString); + + if (isInRange(min, max, input)) { + return null; + } else { + if (source.equals("") && dest.toString().length() != 1) { + //backspace was clicked, do not accept that change, unless user is deleting the last char + return dest.subSequence(dstart, dend); + } else { + return ""; + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + return ""; } - private boolean isInRange(int a, int b, float c) { + private boolean isInRange(float a, float b, float c) { return b > a ? c >= a && c <= b : c >= b && c <= a; } diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/ItemViewHolder.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/ItemViewHolder.java index c92586d..52b5a61 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/ItemViewHolder.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/ItemViewHolder.java @@ -23,6 +23,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder { public TextView cellsMaxPower; public TextView cellsArea; public TextView cellsEfficiency; + public TextView cellsTempCoeff; public TextView diffuseEfficiency; public TextView inverterPowerLimit; public TextView inverterEfficiency; @@ -41,6 +42,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder { this.cellsMaxPower = (TextView) itemView.findViewById(R.id.city_cells_max_power); this.cellsArea = (TextView) itemView.findViewById(R.id.city_cells_area); this.cellsEfficiency = (TextView) itemView.findViewById(R.id.city_cells_efficiency); + this.cellsTempCoeff = (TextView) itemView.findViewById(R.id.city_cells_temp_coeff); this.diffuseEfficiency = (TextView) itemView.findViewById(R.id.city_diffuse_efficiency); this.inverterPowerLimit = (TextView) itemView.findViewById(R.id.city_inverter_power_limit); this.inverterEfficiency = (TextView) itemView.findViewById(R.id.city_inverter_efficiency); diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/RecyclerOverviewListAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/RecyclerOverviewListAdapter.java index 0c5e032..e7bfd60 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/RecyclerOverviewListAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/RecyclerOverviewListAdapter.java @@ -63,6 +63,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter + + diff --git a/app/src/main/res/drawable/ic_wb_sunny_24px.xml b/app/src/main/res/drawable/ic_wb_sunny_24px.xml deleted file mode 100644 index 36167d1..0000000 --- a/app/src/main/res/drawable/ic_wb_sunny_24px.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/card_day.xml b/app/src/main/res/layout/card_day.xml index 9da02db..c78fd19 100644 --- a/app/src/main/res/layout/card_day.xml +++ b/app/src/main/res/layout/card_day.xml @@ -17,7 +17,6 @@ android:layout_height="wrap_content" android:layout_margin="10dp" android:gravity="center" - android:text="@string/card_day_heading" android:textAllCaps="true" android:textColor="@color/colorPrimaryDark" android:textStyle="bold" /> diff --git a/app/src/main/res/layout/card_details.xml b/app/src/main/res/layout/card_details.xml index e633bf5..e6dc147 100644 --- a/app/src/main/res/layout/card_details.xml +++ b/app/src/main/res/layout/card_details.xml @@ -20,7 +20,6 @@ android:textAllCaps="true" android:textColor="@color/colorPrimaryDark" android:textStyle="bold" - android:text="@string/card_details_heading" android:id="@+id/card_details_title"/> @@ -79,7 +77,6 @@ android:layout_column="0" android:layout_marginBottom="5dp" android:layout_row="2" - android:text="@string/card_details_humidity" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/colorPrimaryDark" /> @@ -101,7 +98,6 @@ android:layout_column="0" android:layout_marginBottom="5dp" android:layout_row="3" - android:text="@string/card_details_pressure" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/colorPrimaryDark" /> @@ -123,7 +119,6 @@ android:layout_column="0" android:layout_marginBottom="5dp" android:layout_row="4" - android:text="@string/card_details_wind_speed" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/colorPrimaryDark" /> diff --git a/app/src/main/res/layout/dialog_edit_location.xml b/app/src/main/res/layout/dialog_edit_location.xml index 77b7807..8dab3a0 100644 --- a/app/src/main/res/layout/dialog_edit_location.xml +++ b/app/src/main/res/layout/dialog_edit_location.xml @@ -91,6 +91,16 @@ android:layout_height="wrap_content" android:inputType="numberDecimal" android:hint="@string/edit_location_hint_cells_efficiency"/> + + + Einstellungen Aktualisieren Einstellungen - Jetzt - Luftfeuchte: - Luftdruck: - Wind: - Tagesverlauf Woche Die erhaltenen Wetterdaten entsprachen nicht dem erwarteten Format. Das Gerät ist nicht mit dem Internet verbunden! @@ -30,20 +25,10 @@ Ort, der hinzugefügt werden soll: Es wurde kein Ort gefunden, der der Eingabe entspricht. Es wird empfohlen, einen Eintrag aus dem Dropdown-Menu zu wählen. Navigationsleiste schließen - Maßeinheiten - Temperaturen - Entfernungen - Celsius - Fahrenheit - Kilometer - Meilen - Die Einheit, die zur Anzeige von Entfernungen verwendet wird. - Die Einheit, die zur Anzeige von Temperaturen verwendet wird. Setzen Sie das Interval der automatischen Updates Intervalle Update Intervall Anzeigeoptionen - Navigationsleiste öffnen Fr. Mo. @@ -62,7 +47,6 @@ Halten und ziehen Sie, um die Orte zu sortieren. Wischen Sie zum Löschen Die Wetterinformationen stammen von Open-Meteo.com - Montag Dienstag Mittwoch @@ -71,34 +55,17 @@ Samstag Sonntag Diagramm - % rh kWh - km - hPa - Bft - mm/h - km/h - mph - ☔ 60 min: - keine Daten Ändern Name Suche - Temperatur mit einer Dezimalstelle anzeigen Android 10+ Dark Mode - Position - Erlaubt die Nutzung der aktuellen GPS Position im ersten TAB und im Widget für das aktuelle Wetter. Das Update der Position wird automatisch vom Widget ausgelöst. - GPS Nutzung erlauben - Keine Position verfügbar 24-Stunden-Format Systemeinstellung überschreiben - Beaufort-Skala benutzen OK Nein Vielleicht später Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal. - Luftdruck anzeigen - Nur manuell aktualisieren 15 min 30 min 1 h @@ -106,13 +73,7 @@ 6 h 12 h 24 h - Bitte erlauben Sie auch die GPS Nutzung im Hintergrund für die Verwendung im Widget - Bitte GPS einschalten Open-Meteo - Verbessern der Wochensymbole durch Analyse der stündlichen Vorhersagen zwischen Sonnenaufgang und Sonnenuntergang (experimentell). - Wochensymbole - Niederschlag - Schneemenge anstelle der entsprechenden Wassermenge verwenden. Anzahl der prognostizierten Tage Breitengrad [°] Längengrad [°] @@ -133,5 +94,6 @@ Anleitung Sonnenposition Elevation [°] + Temperaturkoeffizient [%/K] diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 881b7ed..05639fd 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,24 +1,6 @@ - - @string/settings_celsius - @string/settings_fahrenheit - - - 1 - 2 - - - - @string/settings_kilometers - @string/settings_miles - - - 1 - 2 - - @string/settings_interval_quarter @string/settings_interval_half diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 15f1ffc..3d31510 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,12 +13,7 @@ solXpect forecasts the output of your solar power plant Github The sourcecode of this app is available at GitHub. For further explanations have a look at the About Page. - Now - Humidity: - Pressure: - Wind: Week - Course of the day Error fetching weather data Please try to update! Privacy Info @@ -30,15 +25,6 @@
Github-Repo Open-Meteo \n(Attribution 4.0 International CC BY 4.0) Settings - Units - Set the unit to use for displaying temperatures - Temperatures - Celsius - Fahrenheit - Set the unit to use for displaying distances - Distances - Kilometers - Miles Mo. Tu. We. @@ -79,42 +65,18 @@ Saturday Sunday Chart - % rh kWh - km - hPa - Bft - mm/h - km/h - mph - ☔ 60 min: - no data Modify Name Search - Show temperature with one decimal Android 10+ Dark Mode - Position - Allow usage of current GPS position in first TAB and in current weather widget. Position update is triggered automatically by widget. - Allow GPS usage - No position available 24-hour format Override system setting - Use Beaufort scale OK No Maybe later Do you like this app? Please give a star on GitHub and buy the developer a coffee via PayPal. - Show air pressure - Update Location - Manual update only - Please also allow GPS usage in the background for use in the widget - Please turn on GPS Open-Meteo - Improve week icons by analyzing hourly forecasts between sunrise and sunset (experimental). - Week Icons - Precipitation - Use snow amount instead of the equivalent amount of water. Number of forecast days Latitude [°] Longitude [°] @@ -135,4 +97,5 @@ Instructions Sun position Elevation [°] + Temperature coefficient [%/K] diff --git a/fastlane/metadata/android/en-US/changelogs/12.txt b/fastlane/metadata/android/en-US/changelogs/12.txt new file mode 100644 index 0000000..fea5a13 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/12.txt @@ -0,0 +1,6 @@ +Estimate panel temperature from ambient temperature and radiation +Additional parameter: temperature coefficient +Adapt efficiency according to these values +Bugfix input filter + +!!! Please enter temperature coeffient of your panels !!! \ No newline at end of file From f1a79e41e184f032028e2ffd5b37c93260eff344 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 13 Apr 2023 17:11:29 +0200 Subject: [PATCH 11/69] change some stuff from float to double --- .../java/org/woheller69/weather/SolarPowerPlant.java | 10 +++++----- .../org/woheller69/weather/database/CityToWatch.java | 12 ++++++------ .../weather_api/open_meteo/OMDataExtractor.java | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java index d893bc1..b6a6bca 100644 --- a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java +++ b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java @@ -40,7 +40,7 @@ public class SolarPowerPlant { } - public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds, float ambientTemperature) { + public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds, double ambientTemperature) { Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT")); @@ -73,8 +73,8 @@ public class SolarPowerPlant { } } - float totalRadiationOnCell = (float) (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency); //flat plate equivalent of the solar irradiance - float cellTemperature = calcCellTemperature(ambientTemperature,totalRadiationOnCell); + double totalRadiationOnCell = solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency; //flat plate equivalent of the solar irradiance + double cellTemperature = calcCellTemperature(ambientTemperature,totalRadiationOnCell); double dcPower = totalRadiationOnCell * cellsEfficiency * (1+(cellTemperature - 25)*cellsTempCoeff) * cellsArea; @@ -83,11 +83,11 @@ public class SolarPowerPlant { return (float) acPower; } - public static float calcCellTemperature(float ambientTemperature, float totalIrradiance){ + public static double calcCellTemperature(double ambientTemperature, double totalIrradiance){ //models from here: https://www.scielo.br/j/babt/a/FBq5Pmm4gSFqsfh3V8MxfGN/ Photovoltaic Cell Temperature Estimation for a Grid-Connect Photovoltaic Systems in Curitiba //float cellTemperature = 30.006f + 0.0175f*(totalIrradiance-300f)+1.14f*(ambientTemperature-25f); //Lasnier and Ang Lasnier, F.; Ang, T. G. Photovoltaic engineering handbook, 1st ed.; IOP Publishing LTD: Lasnier, France, 1990; pp. 258. //float cellTemperature = ambientTemperature + 0.028f*totalIrradiance-1f; //Schott Schott, T. Operation temperatures of PV modules. Photovoltaic solar energy conference 1985, pp. 392-396. - float cellTemperature = ambientTemperature + 0.0342f*totalIrradiance; //Ross model: https://www.researchgate.net/publication/275438802_Thermal_effects_of_the_extended_holographic_regions_for_holographic_planar_concentrator + double cellTemperature = ambientTemperature + 0.0342f*totalIrradiance; //Ross model: https://www.researchgate.net/publication/275438802_Thermal_effects_of_the_extended_holographic_regions_for_holographic_planar_concentrator //assuming "not so well cooled" : 0.0342 return cellTemperature; } diff --git a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java index bc68368..aef9b23 100644 --- a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java +++ b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java @@ -37,15 +37,15 @@ public class CityToWatch { this.id = id; this.cityId = cityId; this.cityName = cityName; - this.cellsMaxPower = 650; + this.cellsMaxPower = 650.0f; this.cellsArea = 3.18f; this.cellsEfficiency = 19.3f; this.cellsTempCoeff = -0.4f; - this.diffuseEfficiency = 40; - this.inverterPowerLimit = 600; - this.inverterEfficiency = 95; - this.azimuthAngle = 170; - this.tiltAngle = 90; + this.diffuseEfficiency = 40.0f; + this.inverterPowerLimit = 600.0f; + this.inverterEfficiency = 95.0f; + this.azimuthAngle = 170.0f; + this.tiltAngle = 90.0f; } diff --git a/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMDataExtractor.java b/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMDataExtractor.java index e04856b..712f1b5 100644 --- a/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMDataExtractor.java +++ b/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMDataExtractor.java @@ -78,16 +78,16 @@ public class OMDataExtractor implements IDataExtractor { IApiToDatabaseConversion conversion = new OMToDatabaseConversion(); - float ambientTemperature = 25; + double ambientTemperature = 25.0; for (int i = 0; i < timeArray.length(); i++) { HourlyForecast hourlyForecast = new HourlyForecast(); hourlyForecast.setTimestamp(System.currentTimeMillis() / 1000); if (timeArray!=null && !timeArray.isNull(i)) hourlyForecast.setForecastTime(timeArray.getLong(i)*1000L); - if (tempArray != null && !tempArray.isNull(i)) ambientTemperature = (float) tempArray.getDouble(i); + if (tempArray != null && !tempArray.isNull(i)) ambientTemperature = tempArray.getDouble(i); if (weathercodeArray!=null && !weathercodeArray.isNull(i)) hourlyForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i))); if (directRadiationArray!=null && !directRadiationArray.isNull(i)) hourlyForecast.setDirectRadiationNormal((float) directRadiationArray.getDouble(i)); if (diffuseRadiationArray!=null && !diffuseRadiationArray.isNull(i)) hourlyForecast.setDiffuseRadiation((float) diffuseRadiationArray.getDouble(i)); - hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(),hourlyForecast.getDiffuseRadiation(), timeArray.getLong(i)-1800 , ambientTemperature)); //use solar position 1/2h earlier for calculation of average power in preceding hour + hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(), hourlyForecast.getDiffuseRadiation(), timeArray.getLong(i)-1800, ambientTemperature)); //use solar position 1/2h earlier for calculation of average power in preceding hour hourlyForecasts.add(hourlyForecast); } return hourlyForecasts; From a3734b9d36af0bc621db4c2c3a5bb88286417e54 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Sun, 16 Apr 2023 20:04:35 +0200 Subject: [PATCH 12/69] remove unnecessary API calls --- .../weather/activities/ForecastCityActivity.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java b/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java index 6ac27bb..5b3ea20 100644 --- a/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/ForecastCityActivity.java @@ -83,18 +83,6 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea if (pagerAdapter.getItemCount()>0) { //only if at least one city is watched //if pagerAdapter has item with current cityId go there, otherwise use cityId from current item if (pagerAdapter.getPosForCityID(cityId)==-1) cityId=pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem()); - GeneralData generalData = db.getGeneralDataByCityId(cityId); - - long timestamp = generalData.getTimestamp(); - long systemTime = System.currentTimeMillis() / 1000; - SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - long updateInterval = (long) (Float.parseFloat(prefManager.getString("pref_updateInterval", "2")) * 60 * 60); - - if (timestamp + updateInterval - systemTime <= 0) { - WeatherPagerAdapter.refreshSingleData(getApplicationContext(), true, cityId); //only update current tab at start - ForecastCityActivity.startRefreshAnimation(); - - } if (viewPager2.getCurrentItem()!=pagerAdapter.getPosForCityID(cityId)) viewPager2.setCurrentItem(pagerAdapter.getPosForCityID(cityId),false); } } From 3314f624da60302b6bf92f6f26674c6732c95652 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Sun, 16 Apr 2023 20:05:11 +0200 Subject: [PATCH 13/69] fix y-axis labels --- .../ui/RecycleList/CityWeatherAdapter.java | 45 ++++++++++++------- app/src/main/res/layout/card_chart.xml | 5 ++- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java index 03b16eb..f6b3638 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java @@ -26,7 +26,6 @@ import org.woheller69.weather.database.HourlyForecast; import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.WeekForecast; import org.woheller69.weather.ui.Help.StringFormatUtils; -import org.woheller69.weather.ui.UiResourceProvider; import java.util.ArrayList; import java.util.Calendar; @@ -181,13 +180,13 @@ public class CityWeatherAdapter extends RecyclerView.Adapter8) dayString=dayString.substring(0,1); //use first character only if more than 8 days to avoid overlapping text - precipitationDataset.addBar(dayString, precip); - if (precip>pmax) pmax=precip; + energyDataset.addBar(dayString, energyDay); + if (energyDay>energyMax) energyMax=energyDay; } - ArrayList precipitation = new ArrayList<>(); - precipitation.add(precipitationDataset); + //Calculate step size. Target: 4 <= steps < 10, but step size must integer >= 1 + int stepSize = 1; + int numSteps; - precipitationDataset.setColor(ContextCompat.getColor(context,R.color.yellow)); - precipitationDataset.setAlpha(0.8f); // make precipitation bars transparent + do { + numSteps = (int) (energyMax / stepSize); + if (numSteps > 10) stepSize *=10; + else if (numSteps < 4) stepSize /=2; + } while (numSteps > 10 || numSteps < 4 && stepSize>0); - holder.barChartView.addData(precipitation); + if (stepSize<1) stepSize=1; //Step size must be integer, min 1 + + ArrayList energyData = new ArrayList<>(); + energyData.add(energyDataset); + + energyDataset.setColor(ContextCompat.getColor(context,R.color.yellow)); + energyDataset.setAlpha(0.8f); // make energyData bars transparent + + holder.barChartView.addData(energyData); holder.barChartView.setBarSpacing(10); + holder.barChartView.setStep(stepSize); holder.barChartView.setXAxis(false); holder.barChartView.setYAxis(false); - holder.barChartView.setYLabels(AxisController.LabelPosition.INSIDE); //no labels for precipitation + holder.barChartView.setYLabels(AxisController.LabelPosition.OUTSIDE); holder.barChartView.setLabelsColor(ContextCompat.getColor(context,R.color.colorPrimaryDark)); //transparent color, make labels invisible holder.barChartView.setAxisColor(ContextCompat.getColor(context,R.color.colorPrimaryDark)); holder.barChartView.setFontSize((int) Tools.fromDpToPx(17)); - holder.barChartView.setBorderSpacing(Tools.fromDpToPx(30)); holder.barChartView.show(); - holder.precipitationunit.setText(" " + context.getResources().getString(R.string.units_kWh)+" "); + holder.energyUnit.setText(" " + context.getResources().getString(R.string.units_kWh)+" "); } //No update for error needed } diff --git a/app/src/main/res/layout/card_chart.xml b/app/src/main/res/layout/card_chart.xml index db8c59e..cddc241 100644 --- a/app/src/main/res/layout/card_chart.xml +++ b/app/src/main/res/layout/card_chart.xml @@ -27,7 +27,7 @@ android:textStyle="bold" /> From b6838569ab7864226a1ba02863deede69a0f2df0 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Sun, 16 Apr 2023 20:55:01 +0200 Subject: [PATCH 14/69] limit number of y-axis labels --- .../weather/ui/RecycleList/CityWeatherAdapter.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java index f6b3638..2b7f680 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java @@ -361,15 +361,16 @@ public class CityWeatherAdapter extends RecyclerView.AdapterenergyMax) energyMax=energyDay; } - //Calculate step size. Target: 4 <= steps < 10, but step size must integer >= 1 + //Calculate step size. Target: 4 <= steps <= 7, but step size must integer >= 1 int stepSize = 1; int numSteps; - do { - numSteps = (int) (energyMax / stepSize); - if (numSteps > 10) stepSize *=10; - else if (numSteps < 4) stepSize /=2; - } while (numSteps > 10 || numSteps < 4 && stepSize>0); + do { + numSteps = (int) (energyMax / stepSize); + if (numSteps > 10) stepSize *=10; + else if (numSteps > 7) stepSize *=2; + else if (numSteps < 4) stepSize /=2; + } while (numSteps > 7 || numSteps < 4 && stepSize>0); if (stepSize<1) stepSize=1; //Step size must be integer, min 1 From fb19d501236f23f1586a6a9acb45f191fe7f5ecb Mon Sep 17 00:00:00 2001 From: woheller69 Date: Mon, 17 Apr 2023 08:32:38 +0200 Subject: [PATCH 15/69] improve documentation and strings --- README.md | 6 ++++-- app/src/main/assets/help/help-de.html | 11 ++++++----- app/src/main/assets/help/help-en.html | 6 ++++-- .../weather/ui/RecycleList/CityWeatherAdapter.java | 4 ++-- app/src/main/res/values-de/strings.xml | 10 +++++----- app/src/main/res/values/strings.xml | 2 +- fastlane/metadata/android/en-US/changelogs/12.txt | 1 + 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1e9e53e..f5210d2 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ Azimuth is the horizontal direction of your solar power plant. 0° equals North, #### Tilt [°] Tilt is the vertical direction of your solar power plant. 0° means it points up towards the the sky, 90° means it has a vertical orientation and points towards the horizon. -#### Cells max power [W] -Maximum power your solar cells can deliver. +#### Cells peak power [W] +Peak power your solar cells can deliver. #### Cells efficiency [%] Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. @@ -54,6 +54,8 @@ Efficiency of your inverter. In this section you can define the shading on your solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. +You can use the button with sun icon to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above +buildings or trees. ## License diff --git a/app/src/main/assets/help/help-de.html b/app/src/main/assets/help/help-de.html index d615019..10442d3 100644 --- a/app/src/main/assets/help/help-de.html +++ b/app/src/main/assets/help/help-de.html @@ -22,32 +22,33 @@ Azimut ist die horizontale Richtung Ihres Solarkraftwerks. 0° entspricht Norden

Neigung [°]

Neigung ist die vertikale Richtung Ihres Solarkraftwerks. 0° bedeutet, dass es zum Himmel zeigt, 90° bedeutet, dass es vertikal ausgerichtet ist und zum Horizont zeigt. -

Zellen max. Leistung [W]

+

Peakleistung der Zellen [W]

Maximale Leistung, die Ihre Solarzellen liefern können. -

Zelleneffizienz [%]

+

Wirkungsgrad der Zellen [%]

Energieanteil in Form von Sonnenlicht, der von der Solarzelle in Strom umgewandelt werden kann.

Temperaturkoeffizient [%/K]

Abhängigkeit der Leistung der Solarmodule von der Temperatur (normalerweise im Bereich von -0.4%/K). -

Zellenfläche [m2]

+

Fläche der Zellen [m2]

Fläche Ihres Solarpanels. -

Diffuse Strahlungseffizienz [%]

+

Effizienz diffuse Strahlung [%]

Wirkungsgrad Ihres Solarkraftwerks bei diffuser Strahlung. Bei der Ausrichtung nach oben sollte er etwa 100 % betragen, bei der Ausrichtung zum Horizont etwa 50 %. Dies ist auch abhängig von Reflexionen etc.

Wechselrichterleistung [W]

Maximale Leistung Ihres Wechselrichters. Wenn sie niedriger als die maximale Leistung Ihrer Panels ist, wird die Ausgangsleistung Ihres Systems durch diesen Parameter begrenzt. -

Wirkungsgrad des Wechselrichters [%]

+

Wechselrichtereffizienz [%]

Wirkungsgrad Ihres Wechselrichters.

Abschattung

In diesem Abschnitt können Sie die Abschattung Ihrer Solarmodule definieren. Für jeden Azimutwinkelbereich können Sie die minimale Elevation der Sonne angeben, die erforderlich ist, damit die Sonne auf die Solarmodule trifft. Für Elevationen unter diesem Wert können Sie den Prozentsatz der Abschattung festlegen. Beispielsweise reduziert ein Gebäude die Strahlung um 100 %, ein Baum vielleicht nur um 60 %. +Über die Schaltfläche mit dem Sonnensymbol erhalten Sie Informationen über den aktuellen Azimut und die aktuelle Elevation der Sonne. Damit können Sie herauszufinden, ab welcher Elevation die Sonne oberhalb der Gebäude oder Bäume steht. \ No newline at end of file diff --git a/app/src/main/assets/help/help-en.html b/app/src/main/assets/help/help-en.html index 9c54d80..1de0afa 100644 --- a/app/src/main/assets/help/help-en.html +++ b/app/src/main/assets/help/help-en.html @@ -22,8 +22,8 @@ Azimuth is the horizontal direction of your solar power plant. 0° equals North,

Tilt [°]

Tilt is the vertical direction of your solar power plant. 0° means it points up towards the the sky, 90° means it has a vertical orientation and points towards the horizon. -

Cells max power [W]

-Maximum power your solar cells can deliver. +

Cells peak power [W]

+Peak power your solar cells can deliver.

Cells efficiency [%]

Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. @@ -48,6 +48,8 @@ Efficiency of your inverter. In this section you can define the shading on your solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. +You can use the button with sun icon to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above +buildings or trees. diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java index 2b7f680..60a0e29 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java @@ -368,9 +368,9 @@ public class CityWeatherAdapter extends RecyclerView.Adapter 10) stepSize *=10; - else if (numSteps > 7) stepSize *=2; + else if (numSteps >= 8) stepSize *=2; else if (numSteps < 4) stepSize /=2; - } while (numSteps > 7 || numSteps < 4 && stepSize>0); + } while (numSteps >= 8 || numSteps < 4 && stepSize>0); if (stepSize<1) stepSize=1; //Step size must be integer, min 1 diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c3ee5c9..48ce1c4 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -79,12 +79,12 @@ Längengrad [°] Azimut [°] Neigung [°] - Maximalleistung Zelle [W] - Wirkungsgrad Zelle [%] - Fläche Zelle [m\u00b2] + Peakleistung der Zellen [W] + Wirkungsgrad der Zellen [%] + Fläche der Zellen [m\u00b2] Effizienz diffuse Strahlung [%] - Leistung Wechselrichter [W] - Effizienz Wechselrichter [%] + Wechselrichterleistung [W] + Wechselrichtereffizienz [%] Wh Ort bearbeiten Azimut Bereich [°] diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d31510..80c5f3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,7 +82,7 @@ Longitude [°] Azimuth [°] Tilt [°] - Cells max power [W] + Cells peak power [W] Cells efficiency [%] Cell area [m\u00b2] Diffuse radiation efficiency [%] diff --git a/fastlane/metadata/android/en-US/changelogs/12.txt b/fastlane/metadata/android/en-US/changelogs/12.txt index fea5a13..fa68002 100644 --- a/fastlane/metadata/android/en-US/changelogs/12.txt +++ b/fastlane/metadata/android/en-US/changelogs/12.txt @@ -2,5 +2,6 @@ Estimate panel temperature from ambient temperature and radiation Additional parameter: temperature coefficient Adapt efficiency according to these values Bugfix input filter +Bugfix y-axis labels in chart !!! Please enter temperature coeffient of your panels !!! \ No newline at end of file From 67027dc8e6abe9392a6b48a2dc8a64017b82c18c Mon Sep 17 00:00:00 2001 From: woheller69 Date: Mon, 17 Apr 2023 08:35:58 +0200 Subject: [PATCH 16/69] improve documentation and strings --- README.md | 2 +- app/src/main/assets/help/help-de.html | 2 +- app/src/main/assets/help/help-en.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f5210d2..5b6c4de 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Efficiency of your inverter. In this section you can define the shading on your solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. -You can use the button with sun icon to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above +You can use the button with sun icon (main window) to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above buildings or trees. ## License diff --git a/app/src/main/assets/help/help-de.html b/app/src/main/assets/help/help-de.html index 10442d3..932d8da 100644 --- a/app/src/main/assets/help/help-de.html +++ b/app/src/main/assets/help/help-de.html @@ -48,7 +48,7 @@ Wirkungsgrad Ihres Wechselrichters. In diesem Abschnitt können Sie die Abschattung Ihrer Solarmodule definieren. Für jeden Azimutwinkelbereich können Sie die minimale Elevation der Sonne angeben, die erforderlich ist, damit die Sonne auf die Solarmodule trifft. Für Elevationen unter diesem Wert können Sie den Prozentsatz der Abschattung festlegen. Beispielsweise reduziert ein Gebäude die Strahlung um 100 %, ein Baum vielleicht nur um 60 %. -Über die Schaltfläche mit dem Sonnensymbol erhalten Sie Informationen über den aktuellen Azimut und die aktuelle Elevation der Sonne. Damit können Sie herauszufinden, ab welcher Elevation die Sonne oberhalb der Gebäude oder Bäume steht. +Über die Schaltfläche mit dem Sonnensymbol (Hauptfenster) erhalten Sie Informationen über den aktuellen Azimut und die aktuelle Elevation der Sonne. Damit können Sie herauszufinden, ab welcher Elevation die Sonne oberhalb der Gebäude oder Bäume steht. \ No newline at end of file diff --git a/app/src/main/assets/help/help-en.html b/app/src/main/assets/help/help-en.html index 1de0afa..cd3bdca 100644 --- a/app/src/main/assets/help/help-en.html +++ b/app/src/main/assets/help/help-en.html @@ -48,7 +48,7 @@ Efficiency of your inverter. In this section you can define the shading on your solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. -You can use the button with sun icon to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above +You can use the button with sun icon (main window) to get information about the current azimuth and elevation of the sun to find out at what elevation the sun gets above buildings or trees. From 7a2edcf8146ca26630661b1aaa72cbdd5091ece9 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Mon, 17 Apr 2023 10:53:33 +0200 Subject: [PATCH 17/69] add backup/restore --- README.md | 1 + app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 13 ++ .../java/org/woheller69/weather/Backup.java | 69 +++++++++++ .../activities/BackupRestoreActivity.java | 115 ++++++++++++++++++ .../activities/NavigationActivity.java | 3 + .../ic_settings_backup_restore_24px.xml | 9 ++ .../drawable/ic_warning_amber_black_24dp.xml | 15 +++ .../res/layout/activity_backuprestore.xml | 52 ++++++++ .../main/res/menu/activity_main_drawer.xml | 4 + app/src/main/res/values-de/strings.xml | 9 +- app/src/main/res/values/strings.xml | 8 +- 12 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/woheller69/weather/Backup.java create mode 100644 app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java create mode 100644 app/src/main/res/drawable/ic_settings_backup_restore_24px.xml create mode 100644 app/src/main/res/drawable/ic_warning_amber_black_24dp.xml create mode 100644 app/src/main/res/layout/activity_backuprestore.xml diff --git a/README.md b/README.md index 5b6c4de..3f99ef6 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ The app uses: - AutoSuggestTextViewAPICall (https://github.com/Truiton/AutoSuggestTextViewAPICall) which is licensed under Apache License Version 2.0 - Map data from OpenStreetMap, licensed under the Open Data Commons Open Database License (ODbL) by the OpenStreetMap Foundation (OSMF) (https://www.openstreetmap.org/copyright) - Solar positioning library (https://github.com/klausbrunner/solarpositioning) which is licensed under MIT License +- Zip4j (https://github.com/srikanth-lingala/zip4j) which is licensed under Apache License Version 2.0 ## Contributing If you find a bug, please open an issue in the Github repository, assuming one does not already exist. diff --git a/app/build.gradle b/app/build.gradle index 5a0a807..7f4c7f6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,6 +38,7 @@ dependencies { implementation 'com.android.volley:volley:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.cardview:cardview:1.0.0' + implementation 'net.lingala.zip4j:zip4j:2.9.1' implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" //needed due to duplicate class error implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" //needed due to duplicate class error } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a1dac11..5425ae1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,9 +4,12 @@ + + + + + + { + dialog.cancel(); + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); + }); + builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel()); + AlertDialog dialog = builder.create(); + dialog.show(); + } + + public static void zipExtract(Context context, File targetDir, Uri zipFile) { + ZipEntry zipEntry; + int readLen; + byte[] readBuffer = new byte[4096]; + try { + InputStream src = context.getContentResolver().openInputStream(zipFile); + try { + try (ZipInputStream zipInputStream = new ZipInputStream(src)) { + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + File extractedFile = new File(targetDir ,zipEntry.getName()); + try (OutputStream outputStream = new FileOutputStream(extractedFile)) { + while ((readLen = zipInputStream.read(readBuffer)) != -1) { + outputStream.write(readBuffer, 0, readLen); + } + } + } + } + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java b/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java new file mode 100644 index 0000000..ebcfeaa --- /dev/null +++ b/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java @@ -0,0 +1,115 @@ +package org.woheller69.weather.activities; + +import static android.os.Environment.DIRECTORY_DOCUMENTS; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.view.Gravity; +import android.view.View; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; + +import org.woheller69.weather.Backup; +import org.woheller69.weather.R; + +import java.io.File; +import java.util.Objects; + + +public class BackupRestoreActivity extends NavigationActivity{ + ActivityResultLauncher mRestore; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_backuprestore); + + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + mRestore = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + File intData = new File(Environment.getDataDirectory() + "//databases//" + this.getPackageName()); + if (result.getData()!=null && result.getData().getData()!=null) Backup.zipExtract(this, intData, result.getData().getData()); + }); + } + + @Override + protected int getNavigationDrawerID() { + return R.id.nav_backuprestore; + } + + public void performBackup(View view) { + File extStorage; + File intData; + intData = new File(Environment.getDataDirectory()+"//data//" + this.getPackageName() + "//databases//"); + extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS); + String filesBackup = getResources().getString(R.string.app_name)+".zip"; + final File dbBackup = new File(extStorage, filesBackup); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getResources().getString(R.string.backup_database) +" -> " + dbBackup.toString()); + builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> { + if (!Backup.checkPermissionStorage(this)) { + Backup.requestPermission(this); + } else { + if (dbBackup.exists()){ + if (!dbBackup.delete()){ + Toast.makeText(this,getResources().getString(R.string.toast_delete), Toast.LENGTH_LONG).show(); + } + } + try { + new ZipFile(dbBackup).addFolder(intData); + } catch (ZipException e) { + e.printStackTrace(); + } + } + }); + builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel()); + AlertDialog dialog = builder.create(); + dialog.show(); + Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM); + + } + + public void performRestore(View view) { + File extStorage; + File intData; + intData = new File(Environment.getDataDirectory() + "//data//" + this.getPackageName()); + extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS); + String filesBackup = getResources().getString(R.string.app_name)+".zip"; + final File zipFileBackup = new File(extStorage, filesBackup); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getResources().getString(R.string.restore_database)); + builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> { + if (!Backup.checkPermissionStorage(this)) { + Backup.requestPermission(this); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("application/zip"); + mRestore.launch(intent); + } else { + Backup.zipExtract(this, intData, Uri.fromFile(zipFileBackup)); + } + } + }); + builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel()); + AlertDialog dialog = builder.create(); + dialog.show(); + Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM); + } +} diff --git a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java index 7efa3e1..d9a159e 100644 --- a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java @@ -169,6 +169,9 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio }else if(itemId==R.id.nav_help) { intent = new Intent(this, HelpActivity.class); startActivity(intent); + }else if(itemId==R.id.nav_backuprestore) { + intent = new Intent(this, BackupRestoreActivity.class); + startActivity(intent); }else if (itemId==R.id.star_on_github){ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.GITHUB_URL))); diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml b/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml new file mode 100644 index 0000000..edc6466 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml b/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml new file mode 100644 index 0000000..dfc11fb --- /dev/null +++ b/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/layout/activity_backuprestore.xml b/app/src/main/res/layout/activity_backuprestore.xml new file mode 100644 index 0000000..c0122dc --- /dev/null +++ b/app/src/main/res/layout/activity_backuprestore.xml @@ -0,0 +1,52 @@ + + + + + + + + +
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000..0826c1d --- /dev/null +++ b/app/src/main/res/values-tr/strings.xml @@ -0,0 +1,110 @@ + + solXpect + Hakkında + Ayarlar + Tahmin + Konumu yönet + Yenile + Navigasyon bölümünü aç + Navigasyon bölümünü kapat + Sıradaki + Tamam + Hoşgeldin! + solXpect güneş enerjisi santralinizin üretimini tahmin eder + Bu uygulamanın kaynak kodu GitHub\'da mevcuttur. Daha fazla açıklama için Hakkında Sayfası\'na bir göz atın. + Hafta + Hava durumu verisini getirirken hata oluştu + Lütfen güncellemeyi deneyin! + Gizlilik Bilgisi + Hakkında + Sürüm + Lisans + Bu uygulama, SECUSO araştırma grubu tarafından geliştirilen Privacy Friendly Weather\'dan türetilmiştir. Kaynak kodu GPLv3 altında lisanslanmıştır. Uygulama, Apache Lisansı Sürüm 2.0 altında lisanslanan Google Material Design Icons, 2 maddelik BSD Lisansı altında lisanslanan Leaflet kütüphanesi, Apache Lisansı Sürüm 2.0 altında lisanslanan AutoSuggestTextViewAPICall, MIT Lisansı altında lisanslanan Solarpositioning (net.e175.klaus:solarpositioning), Zip4j (https://github. com/srikanth-lingala/zip4j) Apache Lisansı Sürüm 2.0 altında, CompassView (https://github.com/kix2902/CompassView) Apache Lisansı Sürüm 2.0 altında ve WilliamChart kütüphanesi (com.db.chart) Apache Lisansı Sürüm 2.0 altında lisanslanmıştır. + Daha fazla bilgi için: + Ayarlar + Pzt. + Sal. + Çrş. + Perş. + Cum. + Cts. + Paz. + Eklemek için konum girin: + Girdiğiniz bilgilerle eşleşen bir konum bulunamadı. listedeki öğelerden birini seçebilirsiniz. + Ekle + Alınan hava durumu verileri iyi biçimlendirilmemiştir. + Cihazınız internete bağlı değil + Tahmini güncellerken bir hata oluştu, tekrar deneyin! + Seçili bir konum yok. Bir konum seçmek için \"Konumları yönet\" bölümüne gidin. + 15 dk + 30 dk + 1 sa + 2 sa + 6 sa + 12 sa + 24 sa + Aralıklar + Aralığı güncelle + Otomatik güncellemelerin aralığını ayarla + solXpect hava durumu verilerini almak için yalnızca \"İnternet\" iznini kullanır. Ayrıca herhangi bir izleme mekanizması veya reklam içermez. + Hava durumu bilgileri nereden geliyor? + Hava durumu bilgilerinin alındığı yer + Sıralamak için basılı tutun ve sürükleyin. + Silmek için kaydırın + Ekran seçenekleri + Hava durumu bilgileri Open-Meteo.com\'dan çekilir + Pazartesi + Salı + Çarşamba + Perşembe + Cuma + Cumartesi + Pazar + Grafik + kWsa + Kaydet + İsim + Ara + Android 10+ Koyu Mod + 24 saat formatı + Sistem ayarını geçersiz kıl + TAMAM + Hayır + Belki sonra + Bu uygulamayı beğendiniz mi? Lütfen GitHub\'da yıldız verin ve geliştiriciye PayPal ile bir kahve ısmarlayın. + Open-Meteo + Tahmin edilen gün sayısı + Enlem [°] + Boylam [°] + Azimut [°] + Eğim [°] + Hücrelerin tepe gücü [W] + Hücre verimliliği [%] + Hücre bölgesi [m\u00b2] + Dağınık radyasyon verimliliği [%] + İnvertör gücü [W] + İnvertör verimliliği [%] + Wh + Konumu düzenle + Azimut menzili [°] + Min. güneş yüksekliği [°] + Bu yüksekliğin altında gölgelendirme [%] + Gölgelendirme + Yönergeler + Güncel güneş konumu + Yükseklik [°] + Sıcaklık katsayısı [%/K] + Yedekle/Geri Yükle + Yedekleme veritabanı + Geri Yükleme veritabanı + İzİn gerekli + %s harici depolama alanına erişmesi gerekiyor. Lütfen izni kabul edin ve tekrar deneyin. + Lütfen dosyayı silin ve tekrar deneyin + Belgeler klasöründen Verileri Geri Yükleyin. Eğer gerekirse \'Belgeler\' klasörünü açın ve solXpect.zip dosyasını seçin! + Toplamı göster + Aynı enlem ve boylamdaki modüllerin değerlerini özetleyin. + Klon + \u0020\u0020%s KALDIRILDI + GERİ AL + Albedo [0..1] + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 80b1676..105cb2e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,4 +110,5 @@ REMOVED:\u0020\u0020%s UNDO Albedo [0..1] + GitHub From 62321e0e4fc3f24c067db37351cba7d9ff518c71 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Sun, 10 Sep 2023 22:24:26 +0200 Subject: [PATCH 51/69] Close dialog in case of recreation --- .../weather/dialogs/AddLocationDialogOmGeocodingAPI.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java b/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java index 179cd0d..b51dd69 100644 --- a/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java +++ b/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java @@ -24,6 +24,7 @@ import android.widget.AutoCompleteTextView; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.os.ConfigurationCompat; import androidx.fragment.app.DialogFragment; @@ -72,6 +73,11 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { } } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) dismiss(); + } @NonNull @SuppressLint("SetJavaScriptEnabled") From 5b4331388202f28a00dfbc6fcb32041dc0a2eb9a Mon Sep 17 00:00:00 2001 From: woheller69 Date: Fri, 15 Sep 2023 07:40:47 +0200 Subject: [PATCH 52/69] Update V2.1 --- app/build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/21.txt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/21.txt diff --git a/app/build.gradle b/app/build.gradle index d57a84a..fb0e7c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "org.woheller69.solxpect" minSdkVersion 26 targetSdkVersion 33 - versionCode 20 - versionName "2.0" + versionCode 21 + versionName "2.1" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\"" diff --git a/fastlane/metadata/android/en-US/changelogs/21.txt b/fastlane/metadata/android/en-US/changelogs/21.txt new file mode 100644 index 0000000..343192f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/21.txt @@ -0,0 +1,2 @@ +Add Turkish translation +Improved calculation for shading \ No newline at end of file From 836376d7ddfdf01ec27f582d1260bd145a7da4e2 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Thu, 21 Sep 2023 21:30:12 +0200 Subject: [PATCH 53/69] Add option to run own servers for APIs --- app/build.gradle | 2 ++ app/src/main/assets/map.html | 2 +- .../AddLocationDialogOmGeocodingAPI.java | 32 +++++++++++-------- .../weather_api/open_meteo/OMHttpRequest.java | 5 ++- app/src/main/res/values-de/strings.xml | 4 +-- app/src/main/res/values-tr/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 3 ++ app/src/main/res/xml/pref_general.xml | 18 +++++++++++ 8 files changed, 49 insertions(+), 19 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fb0e7c5..c0f1f0c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,6 +12,8 @@ android { versionName "2.1" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" + buildConfigField "String", "TILES_URL","\"https://tile.openstreetmap.org/\"" + buildConfigField "String", "GEOCODING_URL","\"https://geocoding-api.open-meteo.com/\"" buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\"" } diff --git a/app/src/main/assets/map.html b/app/src/main/assets/map.html index 37d7ee1..e694b29 100644 --- a/app/src/main/assets/map.html +++ b/app/src/main/assets/map.html @@ -34,7 +34,7 @@ var map = L.map('mapid', { }).setView([getUrlParameter('lat'), getUrlParameter('lon')], 10); //zoom factor 10 - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + L.tileLayer(getUrlParameter('tiles')+'{z}/{x}/{y}.png', { attribution: 'Map data ©OpenStreetMap contributors' }).addTo(map); diff --git a/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java b/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java index b51dd69..0b29c8c 100644 --- a/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java +++ b/app/src/main/java/org/woheller69/weather/dialogs/AddLocationDialogOmGeocodingAPI.java @@ -6,6 +6,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; @@ -27,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.os.ConfigurationCompat; import androidx.fragment.app.DialogFragment; +import androidx.preference.PreferenceManager; import com.android.volley.Response; import com.android.volley.VolleyError; @@ -62,13 +64,14 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { private static final long AUTO_COMPLETE_DELAY = 300; private Handler handler; private AutoSuggestAdapter autoSuggestAdapter; - String url="https://geocoding-api.open-meteo.com/v1/search?name="; - String lang="default"; + String urlSuffix="v1/search?name="; + String url=""; + String lang="en"; @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - if (context instanceof Activity){ + if (context instanceof Activity){ this.activity=(Activity) context; } } @@ -83,20 +86,22 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { @SuppressLint("SetJavaScriptEnabled") @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity); + url = sp.getString("pref_OMGEO_URL",BuildConfig.GEOCODING_URL); + url = url + urlSuffix; Locale locale = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(0); - lang=locale.getLanguage(); + if (locale != null) lang=locale.getLanguage(); - LayoutInflater inflater = getActivity().getLayoutInflater(); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + LayoutInflater inflater = activity.getLayoutInflater(); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); View view = inflater.inflate(R.layout.dialog_add_location, null); rootView = view; builder.setView(view); - builder.setTitle(getActivity().getString(R.string.dialog_add_label)); + builder.setTitle(activity.getString(R.string.dialog_add_label)); - this.database = SQLiteHelper.getInstance(getActivity()); + this.database = SQLiteHelper.getInstance(activity); final WebView webview= rootView.findViewById(R.id.webViewAddLocation); @@ -121,10 +126,11 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { int position, long id) { selectedCity=autoSuggestAdapter.getObject(position); //Hide keyboard to have more space - final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(rootView.getWindowToken(), 0); //Show city on map - webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude()); + String osmTiles = sp.getString("pref_OsmTiles_URL", BuildConfig.TILES_URL); + webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude() + "&tiles=" + osmTiles); } }); @@ -165,7 +171,7 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { } }); - builder.setPositiveButton(getActivity().getString(R.string.dialog_add_add_button), new DialogInterface.OnClickListener() { + builder.setPositiveButton(activity.getString(R.string.dialog_add_add_button), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -173,7 +179,7 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment { } }); - builder.setNegativeButton(getActivity().getString(android.R.string.cancel), null); + builder.setNegativeButton(activity.getString(android.R.string.cancel), null); return builder.create(); diff --git a/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMHttpRequest.java b/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMHttpRequest.java index c1d4678..c72ba4b 100644 --- a/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMHttpRequest.java +++ b/app/src/main/java/org/woheller69/weather/weather_api/open_meteo/OMHttpRequest.java @@ -19,13 +19,12 @@ import java.util.List; public class OMHttpRequest { protected String getUrlForQueryingOMweatherAPI(Context context, float lat, float lon) { - AppPreferencesManager prefManager = - new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(context)); + SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(context); return String.format( "%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=temperature_2m,diffuse_radiation,direct_normal_irradiance,shortwave_radiation,weathercode&daily=weathercode,sunrise,sunset,&timeformat=unixtime&timezone=auto", - BuildConfig.BASE_URL, + sharedPreferences.getString("pref_OM_URL", BuildConfig.BASE_URL), lat, lon, sharedPreferences.getInt("pref_number_days",7) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a3e43e4..97aedb6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -108,6 +108,6 @@ ENTFERNT:\u0020\u0020%s WIEDERHERSTELLEN Albedo [0..1] - - + Server URLs + Nur ändern, wenn Sie Ihre eigenen Server betreiben wollen diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 0826c1d..8b7db3a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -107,4 +107,6 @@ \u0020\u0020%s KALDIRILDI GERİ AL Albedo [0..1] + Sunucu URL\'leri + Yalnızca kendi sunucularınızı barındırmak istiyorsanız değiştirin diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 105cb2e..39fe573 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,4 +111,7 @@ UNDO Albedo [0..1] GitHub + Server URLs + Only change if you want to host your own servers + diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index f9839c8..84bfc28 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -51,4 +51,22 @@ android:title="@string/settings_update_interval" /> + + + + + + From 117adab1c6ff22a0514dc1034cb2798d2cc866f0 Mon Sep 17 00:00:00 2001 From: woheller69 Date: Fri, 22 Sep 2023 08:49:11 +0200 Subject: [PATCH 54/69] Update for V2.2 Remove unused stuff --- app/build.gradle | 4 ++-- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3758 -> 0 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2353 -> 0 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 5485 -> 0 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 8730 -> 0 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 11989 -> 0 bytes app/src/main/res/values-v14/dimens.xml | 9 --------- app/src/main/res/values-v21/styles.xml | 14 -------------- app/src/main/res/values-w820dp/dimens.xml | 6 ------ app/src/main/res/values/styles.xml | 1 + .../metadata/android/en-US/changelogs/22.txt | 1 + 11 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 app/src/main/res/values-v14/dimens.xml delete mode 100644 app/src/main/res/values-v21/styles.xml delete mode 100644 app/src/main/res/values-w820dp/dimens.xml create mode 100644 fastlane/metadata/android/en-US/changelogs/22.txt diff --git a/app/build.gradle b/app/build.gradle index c0f1f0c..9fa1b7c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "org.woheller69.solxpect" minSdkVersion 26 targetSdkVersion 33 - versionCode 21 - versionName "2.1" + versionCode 22 + versionName "2.2" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" buildConfigField "String", "TILES_URL","\"https://tile.openstreetmap.org/\"" diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index f27d8b5ea271f24541dfee76350ab681592d1d80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3758 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 Y|7_o>@V6zd#sB~S07*qoM6N<$f?CcTPXGV_ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 55d66e87825f6b13fe36162c3f70fcb57da6cf1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2353 zcmV-13C{M3P)fMv0m)yCLt2FGSGOTb`*z*u4v!ZJIU#el>n z31V!L$xPbmpH3TRnrWw#>2$jM?@YQ(J85FO?Q}YsR`d0qXA3_)3Gkvu{*g2D&4YX2 zyZ8I=DYkPrhZEbDa-Me?cA+Ie>tJQv!AZ&M+Jry7RjqjVyd)6!# z(dz1|pF2Qv69;G9oSN;19~cZf-dkRtURzoD>z$RAFWd>@@SfQ`O`P}~otmxlj@dlB zw!DlN31)*~j#2x@h5~HCa?4=Y%LIHz58bg?zGSf8Hk;i7nwvNfm*u&+zv(p^FFsd0 zqll;L_~2kL6L6m%qCtc!@w{MT;#yZ1ZY?c2^f`l#!}9$61C>e@#^-8hw#M%6Zs~1{ z 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 4cdec6d507c31559946c62f8f22cbd171aeda8e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8730 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 z 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 5755fdc7779f0ca38e88651bbb0cdd7eaf327d8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11989 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 - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml deleted file mode 100644 index 960fbc2..0000000 --- a/app/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml deleted file mode 100644 index 63fc816..0000000 --- a/app/src/main/res/values-w820dp/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - 64dp - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 1f9dd67..60f3c9c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -10,6 +10,7 @@