Skip to content

Commit

Permalink
Added Dashboard Configuration Capability
Browse files Browse the repository at this point in the history
Added dashboard configuration capability defined in the ./dashboard folder with a json file defining configuration for each.  This will get built on boot and loaded dynamically.

Default dashboard now has the option to set the maximum temperature to display for both the primary and the food gauges.  This can be configured by clicking on the gear icon.
  • Loading branch information
nebhead committed Jun 9, 2024
1 parent 11afb94 commit 7a79d03
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 53 deletions.
22 changes: 22 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@ def dash():
page_theme=settings['globals']['page_theme'],
grill_name=settings['globals']['grill_name'])

@app.route('/dashconfig', methods=['POST','GET'])
def dash_config():
global settings
current = settings['dashboard']['current']
dash_data = settings['dashboard']['dashboards'].get(current, {})
meta_data_filename = dash_data.get('metadata', None)
dash_metadata = read_generic_json(f'./dashboard/{meta_data_filename}')

if request.method == 'GET':
render_string = "{% from '_macro_dash_default.html' import render_config_card %}{{ render_config_card(dash_metadata, dash_data) }}"
return render_template_string(render_string, dash_metadata=dash_metadata, dash_data=dash_data)
elif request.method == 'POST':
dash_config_request = request.form
for key, value in dash_config_request.items():
if 'dashConfig_' in key:
dash_data['config'][key.replace('dashConfig_','')] = value
settings['dashboard']['dashboards'][current]['config'] = dash_data['config']
write_settings(settings)
return redirect('/dash')

return 'Bad Request'

@app.route('/hopperlevel')
def hopper_level():
pelletdb = read_pellet_db()
Expand Down
54 changes: 31 additions & 23 deletions common/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,29 +272,7 @@ def default_settings():
'auto_power_off' : False # Power off the system after shutdown (False = disabled)
}

settings['dashboard'] = {
'current' : 'Default',
'dashboards' : {
'Default' : {
'name' : 'Default',
'friendly_name' : 'Default Dashboard',
'html_name' : 'dash_default.html',
'custom' : {
'hidden_cards' : []
},
'config' : {}
},
'Basic' : {
'name' : 'Basic',
'friendly_name' : 'Basic Dashboard',
'html_name' : 'dash_basic.html',
'custom' : {
'hidden_cards' : []
},
'config' : {}
}
}
}
settings['dashboard'] = _default_dashboard()

settings['notify_services'] = default_notify_services()

Expand All @@ -312,6 +290,36 @@ def default_settings():

return settings

def _default_dashboard():
'''
Generate default dashboard settings by getting metadata from each json file in the /dashboard folder
'''
dash_data = {
'current' : 'Default',
'dashboards' : {}
}
# Define the folder path
folder_path = './dashboard'

# Loop through files in the folder
for filename in os.listdir(folder_path):
# Check if the file is a JSON file
if filename.endswith('.json'):
dash_metadata = read_generic_json(os.path.join(folder_path, filename))
dash_data['dashboards'][dash_metadata['name']] = {
'name' : dash_metadata['name'],
'friendly_name' : dash_metadata['friendly_name'],
'html_name' : dash_metadata['html_name'],
'metadata' : filename,
'custom' : dash_metadata['custom'],
'config' : {}
}
for item in dash_metadata['config']:
dash_data['dashboards'][dash_metadata['name']]['config'][item['name']] = item['default']

return dash_data


def _default_controller_config():
controller_metadata = read_generic_json('./controller/controllers.json')
config = {}
Expand Down
47 changes: 46 additions & 1 deletion dashboard/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,50 @@
"custom" : {
"hidden_cards" : []
},
"config" : {}
"config" : [
{
"name": "max_primary_temp_F",
"friendly_name": "Max Primary Probe Display Temp F",
"description": "This is the maximum primary probe (grill probe) display temperature (F). The primary gauge will max out at this temperature.",
"hidden": false,
"type": "float",
"min": 0,
"max": 10000,
"step": 1,
"default": 600
},
{
"name": "max_food_temp_F",
"friendly_name": "Max Food Probe Display Temp F",
"description": "This is the maximum food probe display temperature (F). The food gauges will max out at this temperature.",
"hidden": false,
"type": "float",
"min": 0,
"max": 10000,
"step": 1,
"default": 300
},
{
"name": "max_primary_temp_C",
"friendly_name": "Max Primary Probe Display Temp C",
"description": "This is the maximum primary probe (grill probe) display temperature (C). The primary gauge will max out at this temperature.",
"hidden": false,
"type": "float",
"min": 0,
"max": 10000,
"step": 1,
"default": 300
},
{
"name": "max_food_temp_C",
"friendly_name": "Max Food Probe Display Temp C",
"description": "This is the maximum food probe display temperature (C). The food gauges will max out at this temperature.",
"hidden": false,
"type": "float",
"min": 0,
"max": 10000,
"step": 1,
"default": 150
}
]
}
40 changes: 28 additions & 12 deletions static/js/dash_default.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,33 @@ var probes = []; // List of probe keys
var primary = ''; // Primary key
var probeGauges = {}; // List of probe gauges
var probesReady = false; // Pre-initialized state
// Set max temperatures for units specified
if (units == 'F') {
var maxTempPrimary = 600;
var maxTempFood = 300;
} else {
var maxTempPrimary = 300;
var maxTempFood = 150;
}

var last_fan_status = null;
var last_auger_status = null;
var last_igniter_status = null;
var last_pmode_status = null;
var last_lid_open_status = false;
var display_mode = null;
var dashDataStruct = {};
if (typeof dashDataStruct == 'undefined') {
var dashDataStruct = {};
console.log('DEBUG: dashDataStruct undefined');
// Set max temperatures for units specified
if (units == 'F') {
var maxTempPrimary = 600;
var maxTempFood = 300;
} else {
var maxTempPrimary = 300;
var maxTempFood = 150;
};
} else {
if (units == 'F') {
var maxTempPrimary = dashDataStruct.config.max_primary_temp_F;
var maxTempFood = dashDataStruct.config.max_food_temp_F;
} else {
var maxTempPrimary = dashDataStruct.config.max_primary_temp_C;
var maxTempFood = dashDataStruct.config.max_food_temp_C;
};
};

// Credits to https://github.com/naikus for SVG-Gauge (https://github.com/naikus/svg-gauge) MIT License Copyright (c) 2016 Aniket Naik
var Gauge = window.Gauge;
Expand Down Expand Up @@ -69,10 +81,10 @@ function initProbeGauge(key) {
value: 0,
// Custom dial colors (Optional)
color: function(value) {
if(value <= maxTemp) {
if(value <= maxTemp * 0.90) {
return "#3498db"; // default color
}else {
return "#ef4655"; // if exceeds max value, RED
return "#ef4655"; // if is temperature is greater than 10% of max value, RED
};
}
});
Expand Down Expand Up @@ -534,8 +546,12 @@ function setPmode(pmode) {

// Show the Dashboard Settings Modal/Dialog when clicked
function dashSettings() {
dashLoadConfig();
$("#dashSettingsModal").modal('show');
//dashData();
};

function dashLoadConfig() {
$("#dash_config_card").load("/dashconfig");
};

// Get dashboard data structure
Expand Down
46 changes: 45 additions & 1 deletion templates/_macro_dash_default.html
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,48 @@ <h1>
<span class="badge badge-secondary">HH:MM:SS</span>
</div>
</div>
{% endmacro %}
{% endmacro %}

{% macro render_config_card(dash_metadata, dash_data) %}
{% from '_macro_generic_config.html' import config_input_float_int, config_input_list, config_input_string%}
<form method="POST" action="/dashconfig">
<div class="card">
<div class="card-header">
Default Dashboard Configuration Settings
</div>
<div class="card-body">
<!-- Table for Config -->
<table class="table table-sm table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Setting</th>
<th scope="col">Options</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
{% for item in dash_metadata['config'] %}
<tr {% if item['hidden'] %}hidden{% endif %}>
<td>{{ item['friendly_name'] }}</td>
<td>
{% if item['type'] == 'float' or item['type'] == 'int' %}
{{ config_input_float_int('dashConfigItem', 'dashConfig', item['name'], dash_data['config'][item['name']], item['min'], item['max'], item['step']) }}
{% elif item['type'] == 'list' %}
{{ config_input_list('dashConfigItem', 'dashConfig', item['name'], dash_data['config'][item['name']], item['list_values'], item['list_labels']) }}
{% elif item['type'] == 'string' %}
{{ config_input_string('dashConfigItem', 'dashConfig', item['name'], dash_data['config'][item['name']]) }}
{% endif %}
</td>
<td>{{ item.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
<button type="button" class="btn btn-secondary" onclick="dashLoadConfig();">Refresh</button>
<button type="submit" class="btn btn-danger">Save & Reload</button>
</div>
</div>
</form>
{% endmacro %}
27 changes: 27 additions & 0 deletions templates/_macro_generic_config.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% macro config_input_float_int(classname, id_prefix, label, default, min, max, step) %}
<input type="number" class="form-control {{ classname }}"
value="{{ default }}"
min="{{ min }}"
max="{{ max }}"
step="{{ step }}"
id="{{ id_prefix }}_{{ label }}"
name="{{ id_prefix }}_{{ label }}"/>
{% endmacro %}

{% macro config_input_list(classname, id_prefix, label, default, list_values, list_labels) %}
<select class="form-control {{ classname }}"
id="{{ id_prefix }}_{{ label }}"
name="{{ id_prefix }}_{{ label }}">
{% for item in list_values %}
<option value="{{ item }}"{% if default == item %} selected{% endif %}>{{ list_labels[loop.index0] }}</option>
{% endfor %}
</select>
{% endmacro %}

{% macro config_input_string(classname, id_prefix, label, default) %}
<input type="text" class="form-control {{ classname }}"
value="{{ default }}"
id="{{ id_prefix }}_{{ label }}"
name="{{ id_prefix }}_{{ label }}"/>
{% endmacro %}

32 changes: 16 additions & 16 deletions templates/dash_default.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ <h4 class="modal-title">Server Change Detected</h4>

<!-- Dashboard Settings Modal -->
<div class="modal" id="dashSettingsModal">
<div class="modal-dialog">
<div class="modal-dialog modal-xl">
<div class="modal-content">

<!-- Modal Header -->
Expand All @@ -101,21 +101,20 @@ <h4 class="modal-title">Dashboard Settings</h4>

<!-- Modal body -->
<div class="modal-body">
<div class="card">
<div class="card-header">
Dashboard Configuration Settings
</div>
<div class="card-body">
<!-- Standard Dashboard Configuration Settings -->
<div id="dash_config">
There doesn't seem to be any config settings for this dashboard. <br>
<!-- p style="font-family: 'Courier New', Courier, monospace;">
{{ dash_data }}
</p-->
<div id="dash_config_card">
<div class="card">
<div class="card-header">
Dashboard Configuration Settings
</div>
<div class="card-body">
There doesn't seem to be any config settings for this dashboard. <br>
</div>
<div class="card-footer">
<button type="button" class="btn btn-secondary" onclick="dashLoadConfig();">Refresh</button>
</div>
</div>

</div>

<br>
<!-- Custom Dashboard Configuration Settings -->
<div class="card">
Expand Down Expand Up @@ -173,8 +172,7 @@ <h4 class="modal-title">Dashboard Settings</h4>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Dismiss</button>
</div>

</div>
Expand All @@ -196,7 +194,9 @@ <h4 class="modal-title">Dashboard Settings</h4>

{% block scripts %}
<script>
var units = "{{ settings['globals']['units'] }}";
var units = "{{ settings['globals']['units'] }}";
var dashDataStruct = {{ dash_data|tojson }};
//console.log(dashDataStruct);
</script>
<script src="{{ url_for('static', filename='js/dash_default.js') }}"></script>
<script src="{{ url_for('static', filename='js/gauge.js') }}"></script>
Expand Down
3 changes: 3 additions & 0 deletions updater/post-update-message.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ <H1>
<li>
When the dashboard is loaded it will check if the control process is alive and flag an error if the control process does not respond. This may be helpful in debugging issues as they arise.
</li>
<li>
Default Dashboard gets the ability to set maximum temperature to display for the primary probe and food probes. This can be configured via the gear icon settings.
</li>
<li>
Behind the scenes improvements including:
<ul>
Expand Down

0 comments on commit 7a79d03

Please sign in to comment.