Skip to content

Commit

Permalink
Add Action: Measurement (Single, Last, with Timestamp)
Browse files Browse the repository at this point in the history
  • Loading branch information
kizniche committed Sep 9, 2024
1 parent 09246a7 commit 1a4ff20
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This release changes the install directory from ~/Mycodo to /opt/Mycodo. This ne

### Features

- Add Action: Measurement (Single, Last, with Timestamp)
- Add Input: DHT20
- Add Input: MAX31855 CircuitPython
- Add Output: GPIO On/Off using pinctrl (First Pi 5-compatible Output)
Expand Down
2 changes: 2 additions & 0 deletions mycodo/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@
CONDITIONAL_CONDITIONS = [
('measurement',
f"{T['measurement']['title']} ({T['single']['title']}, {T['last']['title']})"),
('measurement_and_ts',
f"{T['measurement']['title']} ({T['single']['title']}, {T['last']['title']}, with Timestamp)"),
('measurement_past_average',
f"{T['measurement']['title']} ({T['single']['title']}, {T['past']['title']}, {T['average']['title']})"),
('measurement_past_sum',
Expand Down
41 changes: 0 additions & 41 deletions mycodo/mycodo_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ def __init__(self):
state = 'disabled' if self.opt_out_statistics else 'enabled'
self.logger.debug(f"Anonymous statistics {state}")


def run(self):
self.load_actions()

Expand Down Expand Up @@ -201,17 +200,14 @@ def run(self):
# Wait for the client to receive the response before it disconnects
time.sleep(1)


@staticmethod
def get_condition_measurement(condition_id):
return get_condition_value(condition_id)


@staticmethod
def get_condition_measurement_dict(condition_id):
return get_condition_value_dict(condition_id)


@staticmethod
def determine_controller_type(unique_id):
db_tables = {
Expand All @@ -225,7 +221,6 @@ def determine_controller_type(unique_id):
if db_tables[each_type]:
return each_type


def controller_activate(self, cont_id):
"""
Activate currently-inactive controller
Expand Down Expand Up @@ -288,7 +283,6 @@ def controller_activate(self, cont_id):
self.logger.debug(message)
return 0, message


def controller_deactivate(self, cont_id):
"""
Deactivate currently-active controller
Expand Down Expand Up @@ -358,7 +352,6 @@ def controller_deactivate(self, cont_id):
self.logger.error(message)
return 1, message


def controller_restart(self, cont_id):
"""
Restart a currently-active controller
Expand All @@ -376,7 +369,6 @@ def controller_restart(self, cont_id):
else:
return 1, ", ".join([msg_deactivate, msg_activate])


def controller_is_active(self, cont_id):
"""
Checks if a controller is active
Expand Down Expand Up @@ -405,7 +397,6 @@ def controller_is_active(self, cont_id):
self.logger.exception(message)
return False


def check_daemon(self):
try:
for cond_id in self.controller['Conditional']:
Expand All @@ -432,7 +423,6 @@ def check_daemon(self):
self.logger.exception(message)
return f"Exception: {except_msg}"


def module_function(self, controller_type, unique_id, button_id, args_dict, thread=True, return_from_function=False):
"""
Call a module function
Expand Down Expand Up @@ -481,7 +471,6 @@ def module_function(self, controller_type, unique_id, button_id, args_dict, thre
self.logger.exception(message)
return 0, message


def input_force_measurements(self, input_id):
"""
Force Input measurements to be acquired
Expand All @@ -500,7 +489,6 @@ def input_force_measurements(self, input_id):
self.logger.exception(message)
return 1, message


def function_status(self, function_id):
if function_id in self.controller["Function"]:
try:
Expand All @@ -520,7 +508,6 @@ def function_status(self, function_id):
else:
return {'error': [f"Function ID not found. Is the Function activated?"]}


def lcd_reset(self, lcd_id):
"""
Resets an LCD
Expand All @@ -543,7 +530,6 @@ def lcd_reset(self, lcd_id):
message = f"Could not reset display: {except_msg}"
self.logger.exception(message)


def lcd_backlight(self, lcd_id, state):
"""
Turn on or off the LCD backlight
Expand Down Expand Up @@ -571,7 +557,6 @@ def lcd_backlight(self, lcd_id, state):
message = f"Cannot change display backlight: {except_msg}"
self.logger.exception(message)


def display_backlight_color(self, lcd_id, color):
"""
Set the LCD backlight color
Expand All @@ -596,7 +581,6 @@ def display_backlight_color(self, lcd_id, color):
message = f"Cannot change display color: {except_msg}"
self.logger.exception(message)


def lcd_flash(self, lcd_id, state):
"""
Begin or end a repeated flashing of an LCD
Expand All @@ -622,7 +606,6 @@ def lcd_flash(self, lcd_id, state):
self.logger.exception(message)
return 0, message


def pid_hold(self, pid_id):
try:
return self.controller['PID'][pid_id].pid_hold()
Expand All @@ -634,7 +617,6 @@ def pid_hold(self, pid_id):
message = f"Could not hold PID: {except_msg}"
self.logger.exception(message)


def pid_mod(self, pid_id):
try:
return self.controller['PID'][pid_id].pid_mod()
Expand All @@ -646,7 +628,6 @@ def pid_mod(self, pid_id):
message = f"Could not modify PID: {except_msg}"
self.logger.exception(message)


def pid_pause(self, pid_id):
try:
return self.controller['PID'][pid_id].pid_pause()
Expand All @@ -658,7 +639,6 @@ def pid_pause(self, pid_id):
message = f"Could not pause PID: {except_msg}"
self.logger.exception(message)


def pid_resume(self, pid_id):
try:
return self.controller['PID'][pid_id].pid_resume()
Expand All @@ -670,7 +650,6 @@ def pid_resume(self, pid_id):
message = f"Could not resume PID: {except_msg}"
self.logger.exception(message)


def pid_get(self, pid_id, setting):
try:
if pid_id not in self.controller['PID']:
Expand All @@ -695,7 +674,6 @@ def pid_get(self, pid_id, setting):
message = f"Could not get PID {setting}: {except_msg}"
self.logger.exception(message)


def pid_set(self, pid_id, setting, value):
try:
if setting == 'setpoint':
Expand All @@ -716,15 +694,13 @@ def pid_set(self, pid_id, setting, value):
message = f"Could not set PID {setting}: {except_msg}"
self.logger.exception(message)


def refresh_daemon_conditional_settings(self, unique_id):
try:
return self.controller['Conditional'][unique_id].refresh_settings()
except Exception as except_msg:
message = f"Could not refresh conditional settings: {except_msg}"
self.logger.exception(message)


def refresh_daemon_misc_settings(self):
try:
self.logger.debug("Refreshing misc settings")
Expand All @@ -738,14 +714,12 @@ def refresh_daemon_misc_settings(self):
except Exception:
self.logger.exception("Could not refresh misc settings")


def refresh_daemon_trigger_settings(self, unique_id):
try:
return self.controller['Trigger'][unique_id].refresh_settings()
except Exception:
self.logger.exception("Could not refresh trigger settings")


def output_off(self, output_id, output_channel=None, trigger_conditionals=True):
"""
Turn output off using default output controller
Expand All @@ -768,7 +742,6 @@ def output_off(self, output_id, output_channel=None, trigger_conditionals=True):
self.logger.exception(message)
return 1, message


def output_on(self,
output_id,
output_channel=None,
Expand Down Expand Up @@ -810,7 +783,6 @@ def output_on(self,
self.logger.exception(message)
return 1, message


def output_setup(self, action, output_id):
"""
Setup output in running output controller
Expand All @@ -829,7 +801,6 @@ def output_setup(self, action, output_id):
message = f"Could not set up output: {except_msg}"
self.logger.exception(message)


def output_state(self, output_id, output_channel):
"""
Return the output state, whether "on" or "off"
Expand All @@ -844,7 +815,6 @@ def output_state(self, output_id, output_channel):
except Exception:
self.logger.exception("Could not query output state")


def output_states_all(self):
"""
Return all output states, whether "on" or "off"
Expand All @@ -854,7 +824,6 @@ def output_states_all(self):
except Exception:
self.logger.exception(f"Could not query all output state")


def startup_stats(self):
"""Ensure existence of statistics file and save daemon startup time."""
# if statistics file doesn't exist, create it
Expand All @@ -863,11 +832,9 @@ def startup_stats(self):
recreate_stat_file()
add_update_csv(STATS_CSV, 'daemon_startup_seconds', self.startup_time)


def load_actions(self):
self.actions = parse_action_information()


def start_all_controllers(self):
"""
Start all activated controllers
Expand Down Expand Up @@ -932,7 +899,6 @@ def start_all_controllers(self):

time.sleep(0.5)


def stop_all_controllers(self):
"""Stop all running controllers."""
controller_running = {}
Expand Down Expand Up @@ -972,7 +938,6 @@ def stop_all_controllers(self):
except Exception as err:
self.logger.info(f"Widget controller had an issue stopping: {err}")


def trigger_action(self, action_id, value={}, debug=False):
try:
return trigger_action(
Expand All @@ -984,7 +949,6 @@ def trigger_action(self, action_id, value={}, debug=False):
message = f"Could not trigger Conditional Actions: {err}"
self.logger.exception(message)


def trigger_all_actions(self, function_id, message='', debug=False):
try:
return trigger_controller_actions(
Expand All @@ -994,7 +958,6 @@ def trigger_all_actions(self, function_id, message='', debug=False):
self.logger.exception(message)
return message


def terminate_daemon(self):
"""Instruct the daemon to shut down."""
self.thread_shutdown_timer = timeit.default_timer()
Expand All @@ -1004,7 +967,6 @@ def terminate_daemon(self):
time.sleep(0.1)
return 1


#
# Timed functions
#
Expand Down Expand Up @@ -1040,7 +1002,6 @@ def check_mycodo_upgrade_exists(self, now):
except Exception:
self.logger.exception("Mycodo Upgrade Check ERROR")


def check_all_timelapses(self, now):
with session_scope(MYCODO_DB_PATH) as new_session:
for each_camera in new_session.query(Camera).all():
Expand Down Expand Up @@ -1080,7 +1041,6 @@ def check_all_timelapses(self, now):
except Exception:
self.logger.exception("Could not execute timelapse")


def generate_usage_report(self):
"""Generate an Output usage report."""
try:
Expand All @@ -1101,7 +1061,6 @@ def generate_usage_report(self):
except:
self.logger.exception("Calculating next report time")


def send_stats(self):
"""Collect and send statistics."""
# Check if stats file exists, recreate if not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

{% if each_condition.condition_type == 'measurement' %}
{% include 'pages/function_options/conditional_options/measurement.html' %}
{% elif each_condition.condition_type == 'measurement_and_ts' %}
{% include 'pages/function_options/conditional_options/measurement_and_ts.html' %}
{% elif each_condition.condition_type == 'measurement_past_average' %}
{% include 'pages/function_options/conditional_options/measurement_past_average.html' %}
{% elif each_condition.condition_type == 'measurement_past_sum' %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<div class="col-12">
<strong>[{{_('Condition')}}] {{_('Measurement')}} ({{_('Single')}}, {{_('Last')}}, with Timestamp)</strong>: <strong>self.condition("{{each_condition.unique_id.split('-')[0]}}")</strong> {{_('returns a dictionary with the last timestamp and value found within the Max Age, otherwise returns a dictionary {"time": None, "value": None}.')}}
</div>
<div class="col-auto">
{{form_conditional_conditions.measurement.label(class_='control-label')}}
<div>
<select class="form-control form-tooltip form-dropdown" id="measurement" name="measurement" style="width: 100%;" title="" data-original-title="{{_('Select the measurement to use as the input')}}">
<option value="">{{dict_translation['select_one']['title']}}</option>
{% for each_input_form in choices_input if each_input_form['value'].split(',')[1] != 'edge' -%}
<option value="{{each_input_form['value']}}"{% if each_condition.measurement == each_input_form['value'] %} selected{% endif %}>{{each_input_form['item']}}</option>
{% endfor -%}
{% for each_output_form in choices_output -%}
<option value="{{each_output_form['value']}}"{% if each_condition.measurement == each_output_form['value'] %} selected{% endif %}>{{each_output_form['item']}}</option>
{% endfor -%}
{% for each_function_form in choices_function -%}
<option value="{{each_function_form['value']}}"{% if each_condition.measurement == each_function_form['value'] %} selected{% endif %}>{{each_function_form['item']}}</option>
{% endfor -%}
{% for each_pid_form in choices_pid -%}
<option value="{{each_pid_form['value']}}"{% if each_condition.measurement == each_pid_form['value'] %} selected{% endif %}>{{each_pid_form['item']}}</option>
{% endfor -%}
</select>
</div>
</div>
<div class="col-auto">
{{form_conditional_conditions.max_age.label(class_='control-label')}}
<div>
{{form_conditional_conditions.max_age(class_='form-control', value=each_condition.max_age, **{'title':_('Do not accept measurements older than this age')})}}
</div>
</div>
1 change: 1 addition & 0 deletions mycodo/mycodo_flask/utils/utils_conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def conditional_condition_mod(form):
Conditional.unique_id == cond_mod.conditional_id).first()

if cond_mod.condition_type in ['measurement',
'measurement_and_ts',
'measurement_past_average',
'measurement_past_sum',
'measurement_dict']:
Expand Down
Loading

0 comments on commit 1a4ff20

Please sign in to comment.