From a35abbe03024ac92e0176857b018aaadd3a441c0 Mon Sep 17 00:00:00 2001 From: Thorrak Date: Thu, 8 Apr 2021 23:31:45 -0400 Subject: [PATCH 01/17] Allow blanks for wifi_host_ip --- .../0003_allow_blank_wifi_host_ip.py | 18 ++++++++++++++++++ app/models.py | 2 +- docs/source/develop/changelog.rst | 11 +++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 app/migrations/0003_allow_blank_wifi_host_ip.py diff --git a/app/migrations/0003_allow_blank_wifi_host_ip.py b/app/migrations/0003_allow_blank_wifi_host_ip.py new file mode 100644 index 00000000..e55c31c9 --- /dev/null +++ b/app/migrations/0003_allow_blank_wifi_host_ip.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.13 on 2021-04-09 03:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0002_fermentationprofile_notes'), + ] + + operations = [ + migrations.AlterField( + model_name='brewpidevice', + name='wifi_host_ip', + field=models.CharField(blank=True, default='', help_text='Cached IP address in case of mDNS issues (only used if connection_type is wifi)', max_length=46), + ), + ] diff --git a/app/models.py b/app/models.py index 5931bf94..6a43ba35 100644 --- a/app/models.py +++ b/app/models.py @@ -518,7 +518,7 @@ class Meta: wifi_host = models.CharField(max_length=40, default='None', help_text="mDNS host name or IP address for WiFi connected hardware (only used if " + "connection_type is wifi)") - wifi_host_ip = models.CharField(max_length=46, default='', help_text="Cached IP address in case of mDNS issues (only used if connection_type is wifi)") + wifi_host_ip = models.CharField(max_length=46, blank=True, default='', help_text="Cached IP address in case of mDNS issues (only used if connection_type is wifi)") wifi_port = models.IntegerField(default=23, validators=[MinValueValidator(10,"Port must be 10 or higher"), MaxValueValidator(65535, "Port must be 65535 or lower")], help_text="The internet socket to use (only used if connection_type is wifi)") diff --git a/docs/source/develop/changelog.rst b/docs/source/develop/changelog.rst index 8b3354a0..32f06e83 100644 --- a/docs/source/develop/changelog.rst +++ b/docs/source/develop/changelog.rst @@ -6,6 +6,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) because it was the first relatively standard format to pop up when I googled "changelog formats". +[Unreleased] - Bugfixes +~~~~~~~~~~~~~~~~~~~~~~~ + +Fixed +----- + +- Properly allow blanks for BrewPiDevice.wifi_host_ip + + + + [2021-04-05] - Docker Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c31a8045109b28d94accefef80ccfc9eaa1725bc Mon Sep 17 00:00:00 2001 From: Thorrak Date: Sat, 10 Apr 2021 00:10:17 -0400 Subject: [PATCH 02/17] Refactor hostname resolution & IP caching (Fixes #623) --- brewpi-script/scriptlibs/BrewPiUtil.py | 23 +++++++++++------------ brewpi-script/scriptlibs/tcpSerial.py | 12 ++++++------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/brewpi-script/scriptlibs/BrewPiUtil.py b/brewpi-script/scriptlibs/BrewPiUtil.py index 0aadadee..2cb93386 100644 --- a/brewpi-script/scriptlibs/BrewPiUtil.py +++ b/brewpi-script/scriptlibs/BrewPiUtil.py @@ -188,28 +188,27 @@ def setupSerial(dbConfig: models.BrewPiDevice, baud_rate:int=57600, time_out=0.1 error = "" if dbConfig.wifi_port is None: - logMessage("Invalid WiFi configuration - Port '{}' cannot be converted to integer".format(config['wifiPort'])) + logMessage("Invalid WiFi configuration - Port '{}' cannot be converted to integer".format(dbConfig.wifi_port)) logMessage("Exiting.") exit(1) port = dbConfig.wifi_port - if dbConfig.wifi_host_ip is None or len(dbConfig.wifi_host_ip) < 7: - if dbConfig.wifi_host is None or len(dbConfig.wifi_host) <= 4: - logMessage("Invalid WiFi configuration - No wifi_host or wifi_host_ip set") - logMessage("Exiting.") - exit(1) - connect_to = dbConfig.wifi_host - else: - # the wifi_host_ip is set - use that as the host to connect to - connect_to = dbConfig.wifi_host_ip + ip_address = dbConfig.get_cached_ip() # Resolve wifi_host to an IP address or get the cached IP + + if ip_address is None: + logMessage("Invalid WiFi configuration - No wifi_host or wifi_host_ip set") + logMessage("Exiting.") + exit(1) - if dbConfig.wifi_host is None or len(dbConfig.wifi_host) <= 4: + if not dbConfig.wifi_host: # If we don't have a hostname at all, set it to None hostname = None else: hostname = dbConfig.wifi_host - ser = tcpSerial.TCPSerial(host=connect_to, port=port, hostname=hostname) + # The way TCPSerial is implemented, hostname is just a memo field. We always connect to the host (which + # in this case is a resolved IP address) + ser = tcpSerial.TCPSerial(host=ip_address, port=port, hostname=hostname) if ser: break diff --git a/brewpi-script/scriptlibs/tcpSerial.py b/brewpi-script/scriptlibs/tcpSerial.py index 4d4aa2f7..cec27d4f 100644 --- a/brewpi-script/scriptlibs/tcpSerial.py +++ b/brewpi-script/scriptlibs/tcpSerial.py @@ -25,13 +25,13 @@ def __init__(self, host=None, port=None, hostname=None): self.port=port self.retries=10 # max reconnect attempts to try when doing a read or write operation self.retryCount=0 # count of reconnect attempts performed + + if host is None: + logMessage(f"No IP address provided - cannot connect to BrewPi") # This -should- never get tripped + exit(1) + if hostname: - # TODO - Do something with the hostname - if host is None: - logMessage("Unable to resolve hostname {}. Exiting.".format(hostname)) - exit(1) - else: - logMessage("Connecting to BrewPi " + hostname + " (via " + host + ") on port " + str(port)) + logMessage("Connecting to BrewPi " + hostname + " (via " + host + ") on port " + str(port)) else: logMessage("Connecting to BrewPi " + host + " on port " + str(port)) self.open() From 7d12789604840441f06af883b57fe5c260eec525 Mon Sep 17 00:00:00 2001 From: Thorrak Date: Tue, 20 Apr 2021 10:56:35 -0400 Subject: [PATCH 03/17] Update Sentry DSN --- fermentrack_django/settings.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fermentrack_django/settings.py b/fermentrack_django/settings.py index 0e3b69e6..ce317b0f 100644 --- a/fermentrack_django/settings.py +++ b/fermentrack_django/settings.py @@ -299,12 +299,21 @@ } +SENTRY_DSN_MAP = { + 'http://99c0c3b2c3214cec950891d07ac6b4fb@sentry.optictheory.com:9000/6': 'http://10a2b38a37f4440f8d8c60769566c5a0@sentry.optictheory.com:9000/2', # Former "legacy" install DSN + 'http://b590686c4b614b4b9edad2fbe3d55002@sentry.optictheory.com:9000/7': 'http://48dbb30951254646a472d378672c2689@sentry.optictheory.com:9000/4', # Former "docker" install DSN +} + + if ENABLE_SENTRY: import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.logging import LoggingIntegration - SENTRY_DSN = env("SENTRY_DSN", default="http://99c0c3b2c3214cec950891d07ac6b4fb@sentry.optictheory.com:9000/6") + SENTRY_DSN = env("SENTRY_DSN", default="http://10a2b38a37f4440f8d8c60769566c5a0@sentry.optictheory.com:9000/2") + # Sentry is a fickle beast, and from time to time I may have to update the DSN. Since it is now set via ENV, + if SENTRY_DSN in SENTRY_DSN_MAP: + SENTRY_DSN = SENTRY_DSN_MAP[SENTRY_DSN] SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO) sentry_logging = LoggingIntegration( From 5306e055fda70e912bb60fde523679957f168e94 Mon Sep 17 00:00:00 2001 From: Thorrak Date: Tue, 20 Apr 2021 14:44:39 -0400 Subject: [PATCH 04/17] Update Sentry DSN --- fermentrack_django/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fermentrack_django/settings.py b/fermentrack_django/settings.py index ce317b0f..b6451674 100644 --- a/fermentrack_django/settings.py +++ b/fermentrack_django/settings.py @@ -300,8 +300,8 @@ SENTRY_DSN_MAP = { - 'http://99c0c3b2c3214cec950891d07ac6b4fb@sentry.optictheory.com:9000/6': 'http://10a2b38a37f4440f8d8c60769566c5a0@sentry.optictheory.com:9000/2', # Former "legacy" install DSN - 'http://b590686c4b614b4b9edad2fbe3d55002@sentry.optictheory.com:9000/7': 'http://48dbb30951254646a472d378672c2689@sentry.optictheory.com:9000/4', # Former "docker" install DSN + 'http://99c0c3b2c3214cec950891d07ac6b4fb@sentry.optictheory.com:9000/6': 'http://bfcd360610c44449aa8fc6b0a6bccc3b@sentry.optictheory.com:9000/2', # Former "legacy" install DSN + 'http://b590686c4b614b4b9edad2fbe3d55002@sentry.optictheory.com:9000/7': 'http://1157cb94d5ae42eab8d718f23228093e@sentry.optictheory.com:9000/3', # Former "docker" install DSN } @@ -310,7 +310,7 @@ from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.logging import LoggingIntegration - SENTRY_DSN = env("SENTRY_DSN", default="http://10a2b38a37f4440f8d8c60769566c5a0@sentry.optictheory.com:9000/2") + SENTRY_DSN = env("SENTRY_DSN", default="http://bfcd360610c44449aa8fc6b0a6bccc3b@sentry.optictheory.com:9000/2") # Sentry is a fickle beast, and from time to time I may have to update the DSN. Since it is now set via ENV, if SENTRY_DSN in SENTRY_DSN_MAP: SENTRY_DSN = SENTRY_DSN_MAP[SENTRY_DSN] From 3c76cb81c27bc5ef677b41aa9c8b898ab042369b Mon Sep 17 00:00:00 2001 From: Thorrak Date: Wed, 21 Apr 2021 10:56:26 -0400 Subject: [PATCH 05/17] Lock Django version --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index b6857cb3..341a9892 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,4 +1,4 @@ -Django>=3.0.13,<3.1 +Django==3.0.14 configobj # required by brewpi.py #pyzmq~=19.0.1 --no-binary=pyzmq # The wheel for pyzmq periodically has issues for Raspbian installs From fa515daf8e25cf8d5b19301aca220c7312498d08 Mon Sep 17 00:00:00 2001 From: Thorrak Date: Sat, 1 May 2021 08:39:13 -0400 Subject: [PATCH 06/17] Order devices in gitLCDs --- app/api/lcd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/lcd.py b/app/api/lcd.py index b8626d94..22b5f9f9 100644 --- a/app/api/lcd.py +++ b/app/api/lcd.py @@ -7,7 +7,7 @@ def getLCDs(req): ret = [] - all_devices = BrewPiDevice.objects.all() + all_devices = BrewPiDevice.objects.all().order_by('device_name') for dev in all_devices: ret.append({"device_name": dev.device_name, "lcd_data": dev.read_lcd(), 'device_url': reverse('device_dashboard', kwargs={'device_id': dev.id,}), From 8f51fb23591990a364628e880da7b8b0ce131b96 Mon Sep 17 00:00:00 2001 From: calandryll Date: Tue, 11 May 2021 19:47:14 -0400 Subject: [PATCH 07/17] Custom Theme addition --- app/static/css/nord.fermentrack.css | 176 ++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 app/static/css/nord.fermentrack.css diff --git a/app/static/css/nord.fermentrack.css b/app/static/css/nord.fermentrack.css new file mode 100644 index 00000000..7ace4dd8 --- /dev/null +++ b/app/static/css/nord.fermentrack.css @@ -0,0 +1,176 @@ +/* Nord Theme for Fermentrack */ +/* Default Colors from https://github.com/arcticicestudio/nord */ +:root { + --bgcolor: #2e3440; /* Background color of the page */ + --textcolor: #e5e9f0; /* Main text color */ + --linkcolor: #88c0d0; /* Link color */ + --linkhover: #8fbcbb; /* Link color when hovering */ + --navbgcolor: #3b4252; /* Navigation bar background color */ + --bgsensorbox: #d08770; /* Sensor box background color */ + --bgsgbox: #b48ead; /* Specific gravity box background color */ + --bgabvbox: #4c566a; /* ABV box background color */ + --bgtempbox: #5e81ac; /* Temperature box background color */ + --bgtempconbox: #81a1c1; /* Temperature control box background color */ + --graphlabels: #eceff4; /* Graph axis text color */ + --bgdatapt: #bf616a; /* Add data point button background color */ + --bgconbut: #88c0d0; /* Control logging button background color */ + --bgprior: #a3be8c; /* Load prior log button background color */ + --bgalert: #a3be8c; /* Pop-up alert background color */ + --borderalert: #a3be8c; /* Pop-up alert border color */ + --textalert: #e5e9f0; /* Pop-up alert text color */ + --bghovermenu: #88c0d0; /* Menu hover color */ + --legendrow: #4c566a; /* Legend row background color */ + --bordercolor: #d8dee9; /* Border color for legend row */ + --caret: #4c566a; /* Menu caret color */ + } + + +/* Overall colors */ +body{ + color: var(--textcolor); + background-color: var(--bgcolor); +} + +/* Link colors */ +a { + color: var(--linkcolor); +} + +a:hover, a:focus { + color: var(--linkhover); +} + +/* Background color of the navigation bar */ +.navbar-inverse { + background-color: var(--navbgcolor); +} + +/* Drop down menu from navigation bar */ +.navbar-inverse .navbar-nav>.open>.dropdown-menu { + background-color: var(--navbgcolor); +} + +.navbar-inverse .navbar-brand { + color: var(--textcolor); +} + +.navbar-inverse .navbar-nav>li>a { + color: var(--textcolor); +} + +.navbar-inverse .navbar-nav>li>a:hover, .navbar-inverse .navbar-nav>li>a:focus { + color: var(--linkhover); +} + +.navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:hover, .navbar-inverse .navbar-nav>.open>a:focus { + color: var(--textcolor); + background-color: var(--linkhover); +} + +.navbar-inverse .navbar-nav>.dropdown>a:hover .caret, .navbar-inverse .navbar-nav>.dropdown>a:focus .caret { + border-top-color: var(--linkhover); + border-bottom-color: var(--linkhover); +} + +.navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { + color: var(--linkhover); + background-color: transparent; +} + +.navbar-inverse .navbar-nav>.dropdown>a .caret { + border-top-color: var(--caret); + border-bottom-color: var(--caret); +} + +.navbar-inverse .navbar-nav>.open>.dropdown-menu>li>a:hover, +.navbar-inverse .navbar-nav>.open>.dropdown-menu>li>a:focus { + color: var(--textcolor); + background-color: var(--linkhover) +} + +/* Gravity Sensor Box */ +.bg-carrot { + background: var(--bgsensorbox)!important; +} + +/* SG Box */ +.bg-amethyst { + background: var(--bgsgbox)!important; +} + +/* ABV Box */ +.bg-wet-asphalt { + background: var(--bgabvbox)!important; +} + +/* Temperature Box */ +.bg-petermann { + background: var(--bgtempbox)!important; +} + +/* Temp Controller Box */ +.bg-concrete { + background: var(--bgtempconbox)!important; +} + +/* Graph colors */ +.dygraph-axis-label { + color: var(--graphlabels); +} + +/* Bottom Buttons */ +.btn-danger { + background-color: var(--bgdatapt); +} + +.btn-info { + background-color: var(--bgconbut); +} + +.btn-success { + background-color: var(--bgprior); +} + +/* Buttons (e.g. login, etc.) */ +.btn-primary:hover, .btn-primary.hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open>.dropdown-toggle.btn-primary { + color: var(--textcolor); + background-color: var(--bghovermenu); + border-color: var(--bghovermenu); +} + +.btn-primary { + color: var(--textcolor); + background-color: var(--bgprior); +} + +/* Alert pop-up */ +.alert-success { + color: var(--textcolor); + background-color: var(--bgalert); + border-color: var(--bgalert); +} + +.panel-red > .panel-heading { + border-color: var(--bgdatapt); + color: var(--textcolor); + background-color: var(--bgdatapt); +} + +.panel-red { + border-color: var(--bgdatapt); + background-color: var(--legendrow); +} + +.chart-legend-row { + background-color: var(--legendrow); + border-top: 1px solid #ddd; +} + +.form-control, .select2-search input[type=text] { + border: 2px solid var(--bordercolor); +} + +.panel-footer { + background-color: var(--legendrow); + border-top: 1px solid #ddd; +} \ No newline at end of file From 1e2a240e770bacfc7adb09d69526c2d2e189d073 Mon Sep 17 00:00:00 2001 From: calandryll Date: Tue, 11 May 2021 20:32:22 -0400 Subject: [PATCH 08/17] Custom theme addtions --- app/setup_forms.py | 6 ++++++ app/setup_views.py | 1 + app/templates/sitewide/flat_ui_template.html | 5 ++++- app/views.py | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/setup_forms.py b/app/setup_forms.py index 10b22850..942bb17d 100644 --- a/app/setup_forms.py +++ b/app/setup_forms.py @@ -55,6 +55,7 @@ class GuidedSetupConfigForm(forms.Form): (True, 'Yes'), (False, 'No') ] + theme_select = settings.CONSTANCE_ADDITIONAL_FIELDS['theme_select'][1]['choices'] update_options = settings.CONSTANCE_ADDITIONAL_FIELDS['git_update_type_select'][1]['choices'][1:] @@ -64,6 +65,10 @@ class GuidedSetupConfigForm(forms.Form): # database table hasn't been created yet (ie - at initial setup) brewery_name = forms.CharField() # initial=config.BREWERY_NAME + custom_theme = forms.ChoiceField( + choices=theme_select, + ) + date_time_format_display = forms.ChoiceField( # initial=config.DATE_TIME_FORMAT_DISPLAY choices=date_time_display_select_choices, ) @@ -109,6 +114,7 @@ def __init__(self, *args, **kwargs): self.fields['brewery_name'].initial = config.BREWERY_NAME self.fields['brewery_name'].help_text = config.BREWERY_NAME + self.fields['custom_theme'].initial = config.CUSTOM_THEME self.fields['date_time_format_display'].initial = config.DATE_TIME_FORMAT_DISPLAY self.fields['require_login_for_dashboard'].initial = config.REQUIRE_LOGIN_FOR_DASHBOARD self.fields['temperature_format'].initial = config.TEMPERATURE_FORMAT diff --git a/app/setup_views.py b/app/setup_views.py index caccb297..fbf7e453 100644 --- a/app/setup_views.py +++ b/app/setup_views.py @@ -76,6 +76,7 @@ def setup_config(request): if form.is_valid(): f = form.cleaned_data config.BREWERY_NAME = f['brewery_name'] + config.CUSTOM_THEME = f['custom_theme'] config.DATE_TIME_FORMAT_DISPLAY = f['date_time_format_display'] config.REQUIRE_LOGIN_FOR_DASHBOARD = f['require_login_for_dashboard'] config.TEMPERATURE_FORMAT = f['temperature_format'] diff --git a/app/templates/sitewide/flat_ui_template.html b/app/templates/sitewide/flat_ui_template.html index c70dc574..5786a890 100644 --- a/app/templates/sitewide/flat_ui_template.html +++ b/app/templates/sitewide/flat_ui_template.html @@ -17,7 +17,10 @@ {# TODO - Move dygraph out to only the pages that use it #} - + {% if config.CUSTOM_THEME == 'nord' %} + + {% endif %} +