From 3bc81440f48683fbff4812e4521f969c68ac4cde Mon Sep 17 00:00:00 2001 From: vitormhenrique Date: Mon, 19 Oct 2020 21:48:04 +0100 Subject: [PATCH] Cleaning up old logic and files --- octoprint_enclosure/__init__.py | 1883 +--------------- octoprint_enclosure/{ => old_stuff}/AM2320.py | 0 octoprint_enclosure/{ => old_stuff}/BME280.py | 0 octoprint_enclosure/{ => old_stuff}/SI7021.py | 0 octoprint_enclosure/old_stuff/__init__.py | 1894 +++++++++++++++++ .../{ => old_stuff}/getDHTTemp.py | 0 .../{ => old_stuff}/ledstrip.py | 0 .../{ => old_stuff}/max31855.py | 0 .../{ => old_stuff}/mcp9808.py | 0 .../{ => old_stuff}/neopixel_direct.py | 0 .../{ => old_stuff}/neopixel_indirect.py | 0 .../static/css/bootstrap-colorpicker.css | 230 ++ .../old_stuff/static/css/enclosure.css | 74 + .../alpha-horizontal.png | Bin 0 -> 557 bytes .../img/bootstrap-colorpicker/alpha.png | Bin 0 -> 488 bytes .../bootstrap-colorpicker/hue-horizontal.png | Bin 0 -> 478 bytes .../static/img/bootstrap-colorpicker/hue.png | Bin 0 -> 504 bytes .../img/bootstrap-colorpicker/saturation.png | Bin 0 -> 4143 bytes .../static/js/bootstrap-colorpicker.min.js | 5 + .../old_stuff/static/js/enclosure.js | 709 ++++++ .../templates/enclosure_navbar.jinja2 | 0 .../templates/enclosure_navbar_input.jinja2 | 0 .../templates/enclosure_settings.jinja2 | 0 .../templates/enclosure_tab.jinja2 | 0 octoprint_enclosure/{ => old_stuff}/tmp102.py | 0 octoprint_enclosure/static/js/enclosure.js | 709 ------ 26 files changed, 2931 insertions(+), 2573 deletions(-) rename octoprint_enclosure/{ => old_stuff}/AM2320.py (100%) rename octoprint_enclosure/{ => old_stuff}/BME280.py (100%) rename octoprint_enclosure/{ => old_stuff}/SI7021.py (100%) create mode 100644 octoprint_enclosure/old_stuff/__init__.py rename octoprint_enclosure/{ => old_stuff}/getDHTTemp.py (100%) rename octoprint_enclosure/{ => old_stuff}/ledstrip.py (100%) rename octoprint_enclosure/{ => old_stuff}/max31855.py (100%) rename octoprint_enclosure/{ => old_stuff}/mcp9808.py (100%) rename octoprint_enclosure/{ => old_stuff}/neopixel_direct.py (100%) rename octoprint_enclosure/{ => old_stuff}/neopixel_indirect.py (100%) create mode 100644 octoprint_enclosure/old_stuff/static/css/bootstrap-colorpicker.css create mode 100644 octoprint_enclosure/old_stuff/static/css/enclosure.css create mode 100644 octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha-horizontal.png create mode 100644 octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha.png create mode 100644 octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/hue-horizontal.png create mode 100644 octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/hue.png create mode 100644 octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/saturation.png create mode 100644 octoprint_enclosure/old_stuff/static/js/bootstrap-colorpicker.min.js create mode 100644 octoprint_enclosure/old_stuff/static/js/enclosure.js rename octoprint_enclosure/{ => old_stuff}/templates/enclosure_navbar.jinja2 (100%) rename octoprint_enclosure/{ => old_stuff}/templates/enclosure_navbar_input.jinja2 (100%) rename octoprint_enclosure/{ => old_stuff}/templates/enclosure_settings.jinja2 (100%) rename octoprint_enclosure/{ => old_stuff}/templates/enclosure_tab.jinja2 (100%) rename octoprint_enclosure/{ => old_stuff}/tmp102.py (100%) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index d66a07b..0d64784 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -1,1882 +1,37 @@ # coding=utf-8 from __future__ import absolute_import -from octoprint.events import eventManager, Events -from octoprint.util import RepeatedTimer -from subprocess import Popen, PIPE -from .ledstrip import LEDStrip + import octoprint.plugin -import RPi.GPIO as GPIO -from flask import jsonify, request, make_response, Response -from octoprint.server.util.flask import restricted_access -from werkzeug.exceptions import BadRequest -import time -import sys -import glob -import os -from datetime import datetime -from datetime import timedelta import octoprint.util -import requests -import inspect -import threading -import json -import copy + class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.BlueprintPlugin, octoprint.plugin.EventHandlerPlugin): - rpi_outputs = [] - rpi_inputs = [] - waiting_temperature = [] - rpi_outputs_not_changed = [] - notifications = [] - pwm_instances = [] - event_queue = [] - temp_hum_control_status = [] - temperature_sensor_data = [] - last_filament_end_detected = [] - print_complete = False - development_mode = False - dummy_value = 30.0 - dummy_delta = 0.5 - - def start_timer(self): - """ - Function to start timer that checks enclosure temperature - """ - - self._check_temp_timer = RepeatedTimer(10, self.check_enclosure_temp, None, None, True) - self._check_temp_timer.start() - - @staticmethod - def to_float(value): - """Converts value to flow - - Arguments: - value {any} -- value to be - - Returns: - float -- value converted - """ - - try: - val = float(value) - return val - except: - return 0 - - @staticmethod - def to_int(value): - try: - val = int(value) - return val - except: - return 0 - - @staticmethod - def is_hour(value): - try: - datetime.strptime(value, '%H:%M') - return True - except: - return False - - @staticmethod - def create_date(value): - temp_string = datetime.now().strftime('%m/%d/%Y') + " " + value - return datetime.strptime(temp_string, '%m/%d/%Y %H:%M') - - @staticmethod - def constrain(n, minn, maxn): - return max(min(maxn, n), minn) - - @staticmethod - def get_gcode_value(command_string, gcode): - semicolon = command_string.find(';') - if not semicolon == -1: - command_string = command_string[:semicolon] - - for command in command_string.split(' '): - index = command.upper().find(gcode.upper()) - if not index == -1: - return command.replace(gcode, '').upper() - return -1 - - # ~~ StartupPlugin mixin - def on_after_startup(self): - self.pwm_instances = [] - self.event_queue = [] - self.rpi_outputs_not_changed = [] - self.rpi_outputs = self._settings.get(["rpi_outputs"]) - self.rpi_inputs = self._settings.get(["rpi_inputs"]) - self.notifications = self._settings.get(["notifications"]) - self.generate_temp_hum_control_status() - self.setup_gpio() - self.configure_gpio() - self.update_ui() - self.start_outpus_with_server() - self.handle_initial_gpio_control() - self.start_timer() - self.print_complete = False - - def get_settings_version(self): - return 6 - - def on_settings_migrate(self, target, current=None): - self._logger.warn("######### current settings version %s target settings version %s #########", current, target) - self._logger.info("######### Current settings #########") - self._logger.info("rpi_outputs: %s", self.rpi_outputs) - self._logger.info("rpi_inputs: %s", self.rpi_inputs) - self._logger.info("######### End Current Settings #########") - if current >= 4 and target == 6: - self._logger.warn("######### migrating settings to v6 #########") - old_outputs = self._settings.get(["rpi_outputs"]) - for rpi_output in old_outputs: - if 'shutdown_on_failed' not in rpi_output: - rpi_output['shutdown_on_failed'] = False - if 'shell_script' not in rpi_output: - rpi_output['shell_script'] = "" - self._settings.set(["rpi_outputs"], old_outputs) - else: - self._logger.warn("######### settings not compatible #########") - self._settings.set(["rpi_outputs"], []) - self._settings.set(["rpi_inputs"], []) - self.rpi_inputs = self._settings.get(["rpi_inputs"]) - - # ~~ Blueprintplugin mixin - @octoprint.plugin.BlueprintPlugin.route("/inputs", methods=["GET"]) - def get_inputs(self): - inputs = [] - for rpi_input in self.rpi_inputs: - index = self.to_int(rpi_input['index_id']) - label = rpi_input['label'] - inputs.append(dict(index_id=index, label=label)) - return Response(json.dumps(inputs), mimetype='application/json') - - - @octoprint.plugin.BlueprintPlugin.route("/inputs/", methods=["GET"]) - def get_input_status(self, identifier): - for rpi_input in self.rpi_inputs: - if identifier == self.to_int(rpi_input['index_id']): - return Response(json.dumps(rpi_input), mimetype='application/json') - return make_response('', 404) - - - @octoprint.plugin.BlueprintPlugin.route("/temperature/", methods=["PATCH"]) - @restricted_access - def set_enclosure_temp_humidity(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'temperature' not in data: - return make_response("missing temperature attribute", 406) - - set_value = data["temperature"] - - for temp_hum_control in [item for item in self.rpi_outputs if item['index_id'] == identifier]: - temp_hum_control['temp_ctr_set_value'] = set_value - - self.handle_temp_hum_control() - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/filament/", methods=["PATCH"]) - @restricted_access - def set_filament_sensor(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'status' not in data: - return make_response("missing status attribute", 406) - - value = data["status"] - - for sensor in self.rpi_inputs: - if identifier == self.to_int(sensor['index_id']): - sensor['filament_sensor_enabled'] = value - self._logger.info("Setting filament sensor for input %s to : %s", str(identifier), value) - self._settings.set(["rpi_inputs"], self.rpi_inputs) - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/outputs", methods=["GET"]) - def get_outputs(self): - outputs = [] - for rpi_output in self.rpi_outputs: - if rpi_output['output_type'] == 'regular': - index = self.to_int(rpi_output['index_id']) - label = rpi_output['label'] - outputs.append(dict(index_id=index, label=label)) - return Response(json.dumps(outputs), mimetype='application/json') - - - @octoprint.plugin.BlueprintPlugin.route("/outputs/", methods=["GET"]) - def get_output_status(self, identifier): - for rpi_output in self.rpi_outputs: - if identifier == self.to_int(rpi_output['index_id']): - out = copy.deepcopy(rpi_output) - pin = self.to_int(rpi_output['gpio_pin']) - out['current_value'] = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) - return Response(json.dumps(out), mimetype='application/json') - return make_response('', 404) - - - @octoprint.plugin.BlueprintPlugin.route("/outputs/", methods=["PATCH"]) - @restricted_access - def set_io(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'status' not in data: - return make_response("missing status attribute", 406) - - value = data["status"] - - for rpi_output in self.rpi_outputs: - if identifier == self.to_int(rpi_output['index_id']): - val = (not value) if rpi_output['active_low'] else value - self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/outputs//auto-startup", methods=["PATCH"]) - @restricted_access - def set_auto_startup(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'status' not in data: - return make_response("missing status attribute", 406) - - value = data["status"] - - if not value: - suffix = 'auto_startup' - queue_id = '{0!s}_{1!s}'.format(str(identifier), suffix) - self.stop_queue_item(queue_id) - for output in self.rpi_outputs: - if identifier == self.to_int(output['index_id']): - output['auto_startup'] = value - self._logger.info("Setting auto startup for output %s to : %s", str(identifier), value) - self._settings.set(["rpi_outputs"], self.rpi_outputs) - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/outputs//auto-shutdown", methods=["PATCH"]) - @restricted_access - def set_auto_shutdown(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'status' not in data: - return make_response("missing status attribute", 406) - - value = data["status"] - - if not value: - suffix = 'auto_shutdown' - queue_id = '{0!s}_{1!s}'.format(str(identifier), suffix) - self.stop_queue_item(queue_id) - - for output in self.rpi_outputs: - if identifier == self.to_int(output['index_id']): - output['auto_shutdown'] = value - self._logger.info("Setting auto shutdown for output %s to : %s", str(identifier), value) - self._settings.set(["rpi_outputs"], self.rpi_outputs) - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/pwm/", methods=["PATCH"]) - @restricted_access - def set_pwm(self, identifier): - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'duty_cycle' not in data: - return make_response("missing duty_cycle attribute", 406) - - set_value = self.to_int(data['duty_cycle']) - for rpi_output in [item for item in self.rpi_outputs if item['index_id'] == identifier]: - rpi_output['duty_cycle'] = set_value - rpi_output['new_duty_cycle'] = "" - gpio = self.to_int(rpi_output['gpio_pin']) - self.write_pwm(gpio, set_value) - return make_response('', 204) - - @octoprint.plugin.BlueprintPlugin.route("/rgb-led/", methods=["PATCH"]) - @restricted_access - def set_ledstrip_color(self, identifier): - """ set_ledstrip_color method get request from octoprint and send the command to Open-Smart RGB LED Strip""" - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'rgb' not in data: - return make_response("missing rgb attribute", 406) - rgb = data['rgb'] - - for rpi_output in self.rpi_outputs: - if identifier == self.to_int(rpi_output['index_id']): - self.ledstrip_set_rgb(rpi_output, rgb) - - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/neopixel/", methods=["PATCH"]) - @restricted_access - def set_neopixel(self, identifier): - """ set_neopixel method get request from octoprint and send the command to arduino or neopixel""" - if "application/json" not in request.headers["Content-Type"]: - return make_response("expected json", 400) - try: - data = request.json - except BadRequest: - return make_response("malformed request", 400) - - if 'red' not in data: - return make_response("missing red attribute", 406) - if 'green' not in data: - return make_response("missing green attribute", 406) - if 'blue' not in data: - return make_response("missing blue attribute", 406) - - red = data['red'] - green = data['green'] - blue = data['blue'] - - for rpi_output in self.rpi_outputs: - if identifier == self.to_int(rpi_output['index_id']): - led_count = rpi_output['neopixel_count'] - led_brightness = rpi_output['neopixel_brightness'] - address = rpi_output['microcontroller_address'] - - neopixel_dirrect = rpi_output['output_type'] == 'neopixel_direct' - - self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, green, - blue, address, neopixel_dirrect, identifier) - - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/clear-gpio", methods=["POST"]) - @restricted_access - def clear_gpio_mode(self): - GPIO.cleanup() - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/update", methods=["POST"]) - @restricted_access - def update_ui_requested(self): - self.update_ui() - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/shell/", methods=["POST"]) - @restricted_access - def send_shell_command(self, identifier): - rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == identifier].pop() - - command = rpi_output['shell_script'] - self.shell_command(command) - return make_response('', 204) - - - @octoprint.plugin.BlueprintPlugin.route("/gcode/", methods=["POST"]) - @restricted_access - def requested_gcode_command(self, identifier): - rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == identifier].pop() - self.send_gcode_command(rpi_output['gcode']) - return make_response('', 204) - - - - - - """ - DEPRECATION - This API will be deprecated in a future version - """ - - # ~~ Blueprintplugin mixin - @octoprint.plugin.BlueprintPlugin.route("/setEnclosureTempHum", methods=["GET"]) - def set_enclosure_temp_humidity_old(self): - set_value = self.to_float(request.values["set_temperature"]) - index_id = self.to_int(request.values["index_id"]) - - for temp_hum_control in [item for item in self.rpi_outputs if item['index_id'] == index_id]: - temp_hum_control['temp_ctr_set_value'] = set_value - - self.handle_temp_hum_control() - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/clearGPIOMode", methods=["GET"]) - def clear_gpio_mode_old(self): - GPIO.cleanup() - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/updateUI", methods=["GET"]) - def update_ui_requested_old(self): - self.update_ui() - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/getOutputStatus", methods=["GET"]) - def get_output_status_old(self): - gpio_status = [] - for rpi_output in self.rpi_outputs: - if rpi_output['output_type'] == 'regular': - pin = self.to_int(rpi_output['gpio_pin']) - val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) - index = self.to_int(rpi_output['index_id']) - gpio_status.append(dict(index_id=index, status=val)) - return Response(json.dumps(gpio_status), mimetype='application/json') - - @octoprint.plugin.BlueprintPlugin.route("/setIO", methods=["GET"]) - def set_io_old(self): - index = request.values["index_id"] - value = True if request.values["status"] == 'true' else False - for rpi_output in self.rpi_outputs: - if self.to_int(index) == self.to_int(rpi_output['index_id']): - val = (not value) if rpi_output['active_low'] else value - self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/sendShellCommand", methods=["GET"]) - def send_shell_command_old(self): - output_index = self.to_int(request.values["index_id"]) - - rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == output_index].pop() - - command = rpi_output['shell_script'] - self.shell_command(command) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setAutoStartUp", methods=["GET"]) - def set_auto_startup_old(self): - index = request.values["index_id"] - value = True if request.values["status"] == 'true' else False - - if not value: - suffix = 'auto_startup' - queue_id = '{0!s}_{1!s}'.format(index, suffix) - self.stop_queue_item(queue_id) - for output in self.rpi_outputs: - if self.to_int(index) == self.to_int(output['index_id']): - output['auto_startup'] = value - self._logger.info("Setting auto startup for output %s to : %s", index, value) - self._settings.set(["rpi_outputs"], self.rpi_outputs) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setAutoShutdown", methods=["GET"]) - def set_auto_shutdown_old(self): - index = request.values["index_id"] - value = True if request.values["status"] == 'true' else False - - if not value: - suffix = 'auto_shutdown' - queue_id = '{0!s}_{1!s}'.format(index, suffix) - self.stop_queue_item(queue_id) - - for output in self.rpi_outputs: - if self.to_int(index) == self.to_int(output['index_id']): - output['auto_shutdown'] = value - self._logger.info("Setting auto shutdown for output %s to : %s", index, value) - self._settings.set(["rpi_outputs"], self.rpi_outputs) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setFilamentSensor", methods=["GET"]) - def set_filament_sensor_old(self): - index = request.values["index_id"] - value = True if request.values["status"] == 'true' else False - for sensor in self.rpi_inputs: - if self.to_int(index) == self.to_int(sensor['index_id']): - sensor['filament_sensor_enabled'] = value - self._logger.info("Setting filament sensor for input %s to : %s", index, value) - self._settings.set(["rpi_inputs"], self.rpi_inputs) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setPWM", methods=["GET"]) - def set_pwm_old(self): - set_value = self.to_int(request.values["new_duty_cycle"]) - index_id = self.to_int(request.values["index_id"]) - for rpi_output in [item for item in self.rpi_outputs if item['index_id'] == index_id]: - rpi_output['duty_cycle'] = set_value - rpi_output['new_duty_cycle'] = "" - gpio = self.to_int(rpi_output['gpio_pin']) - self.write_pwm(gpio, set_value) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/sendGcodeCommand", methods=["GET"]) - def requested_gcode_command_old(self): - gpio_index = self.to_int(request.values["index_id"]) - rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == gpio_index].pop() - self.send_gcode_command(rpi_output['gcode']) - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setNeopixel", methods=["GET"]) - def set_neopixel_old(self): - """ set_neopixel method get request from octoprint and send the command to arduino or neopixel""" - gpio_index = self.to_int(request.values["index_id"]) - red = request.values["red"] - green = request.values["green"] - blue = request.values["blue"] - for rpi_output in self.rpi_outputs: - if gpio_index == self.to_int(rpi_output['index_id']): - led_count = rpi_output['neopixel_count'] - led_brightness = rpi_output['neopixel_brightness'] - address = rpi_output['microcontroller_address'] - - neopixel_dirrect = rpi_output['output_type'] == 'neopixel_direct' - - self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, green, - blue, address, neopixel_dirrect, gpio_index) - - return jsonify(success=True) - - @octoprint.plugin.BlueprintPlugin.route("/setLedstripColor", methods=["GET"]) - def set_ledstrip_color_old(self): - """ set_ledstrip_color method get request from octoprint and send the command to Open-Smart RGB LED Strip""" - gpio_index = self.to_int(request.values["index_id"]) - rgb = request.values["rgb"] - for rpi_output in self.rpi_outputs: - if gpio_index == self.to_int(rpi_output['index_id']): - self.ledstrip_set_rgb(rpi_output, rgb) - - return jsonify(success=True) - - # DEPREACTION END - - - - - - def send_neopixel_command(self, led_pin, led_count, led_brightness, red, green, blue, address, neopixel_dirrect, - index_id, queue_id=None): - """Send neopixel command - - Arguments: - led_pin {int} -- GPIO number - ledCount {int} -- number of LEDS - ledBrightness {int} -- brightness from 0 to 255 - red {int} -- red value from 0 to 255 - green {int} -- green value from 0 to 255 - blue {int} -- blue value from 0 to 255 - address {int} -- i2c address from microcontroller - """ - - try: - - for rpi_output in self.rpi_outputs: - if self.to_int(index_id) == self.to_int(rpi_output['index_id']): - rpi_output['neopixel_color'] = 'rgb({0!s},{1!s},{2!s})'.format(red, green, blue) - - if address == '': - address = 0 - - if neopixel_dirrect: - script = os.path.dirname(os.path.realpath(__file__)) + "/neopixel_direct.py " - else: - script = os.path.dirname(os.path.realpath(__file__)) + "/neopixel_indirect.py " - - if self._settings.get(["use_sudo"]): - sudo_str = "sudo " - else: - sudo_str = "" - - cmd = sudo_str + "python " + script + str(led_pin) + " " + str(led_count) + " " + str( - led_brightness) + " " + str(red) + " " + str(green) + " " + str(blue) + " " - - if neopixel_dirrect: - dma = self._settings.get(["neopixel_dma"]) or 10 - cmd = cmd + str(dma) - else: - cmd = cmd + str(address) - - if queue_id is not None: - self._logger.debug("running scheduled queue id %s", queue_id) - self._logger.debug("Sending neopixel cmd: %s", cmd) - Popen(cmd, shell=True) - if queue_id is not None: - self.stop_queue_item(queue_id) - except Exception as ex: - self.log_error(ex) - - def check_enclosure_temp(self): - try: - sensor_data = [] - for sensor in list(filter(lambda item: item['input_type'] == 'temperature_sensor', self.rpi_inputs)): - temp, hum = self.get_sensor_data(sensor) - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Sensor %s Temperature: %s humidity %s", sensor['label'], temp, hum) - if temp is not None and hum is not None: - sensor["temp_sensor_temp"] = temp - sensor["temp_sensor_humidity"] = hum - sensor_data.append(dict(index_id=sensor['index_id'], temperature=temp, humidity=hum)) - self.temperature_sensor_data = sensor_data - self.handle_temp_hum_control() - self.handle_temperature_events() - self.handle_pwm_linked_temperature() - self.update_ui() - except Exception as ex: - self.log_error(ex) - - def toggle_output(self, output_index, first_run=False): - for output in [item for item in self.rpi_outputs if item['index_id'] == output_index]: - gpio_pin = self.to_int(output['gpio_pin']) - index_id = self.to_int(output['index_id']) - - if output['output_type'] == 'regular': - if first_run: - current_value = False - else: - current_value = (not GPIO.input(gpio_pin)) if output['active_low'] else GPIO.input(gpio_pin) - - if current_value: - time_delay = self.to_int(output['toggle_timer_off']) - else: - time_delay = self.to_int(output['toggle_timer_on']) - - if not self.print_complete: - self.write_gpio(gpio_pin, not current_value) - thread = threading.Timer(time_delay, self.toggle_output, args=[index_id]) - thread.start() - else: - off_value = True if output['active_low'] else False - self.write_gpio(gpio_pin, off_value) - self.update_ui_outputs() - return - - if output['output_type'] == 'pwm': - for pwm in self.pwm_instances: - if gpio_pin in pwm: - if first_run: - current_pwm_value = 0 - else: - if 'duty_cycle' in pwm: - current_pwm_value = pwm['duty_cycle'] - current_pwm_value = self.to_int(current_pwm_value) - else: - current_pwm_value = 0 - - if not current_pwm_value == 0: - time_delay = self.to_int(output['toggle_timer_off']) - write_value = 0 - else: - time_delay = self.to_int(output['toggle_timer_on']) - write_value = self.to_int(output['default_duty_cycle']) - - if not self.print_complete: - self.write_pwm(gpio_pin, write_value) - thread = threading.Timer(time_delay, self.toggle_output, args=[index_id]) - thread.start() - else: - self.write_pwm(self.to_int(output['gpio_pin']), 0) - self.update_ui_outputs() - return - - def update_ui(self): - self.update_ui_outputs() - self.update_ui_current_temperature() - self.update_ui_set_temperature() - self.update_ui_inputs() - - def update_ui_current_temperature(self): - self._plugin_manager.send_plugin_message(self._identifier, dict(sensor_data=self.temperature_sensor_data)) - - def update_ui_set_temperature(self): - result = [] - for temp_crt_output in list(filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): - set_temperature = self.to_float(temp_crt_output['temp_ctr_set_value']) - result.append(dict(index_id=temp_crt_output['index_id'], set_temperature=set_temperature)) - result.append(set_temperature) - self._plugin_manager.send_plugin_message(self._identifier, dict(set_temperature=result)) - - def stop_queue_item(self, queue_id): - old_list = self.event_queue - self._logger.debug("Stopping queue id %s...", queue_id) - for task in self.event_queue: - self._logger.debug("Queue id found...") - if task['queue_id'] == queue_id: - task['thread'].cancel() - self.event_queue.remove(task) - self._logger.debug("Queue id stopped and removed from list...") - self._logger.debug("Old queue list: %s", old_list) - self._logger.debug("New queue list: %s", self.event_queue) - - def update_ui_outputs(self): - try: - regular_status = [] - pwm_status = [] - neopixel_status = [] - temp_control_status = [] - for output in self.rpi_outputs: - index = self.to_int(output['index_id']) - pin = self.to_int(output['gpio_pin']) - startup = output['auto_startup'] - shutdown = output['auto_shutdown'] - - if output['output_type'] == 'regular': - val = GPIO.input(pin) if not output['active_low'] else (not GPIO.input(pin)) - regular_status.append( - dict(index_id=index, status=val, auto_startup=startup, auto_shutdown=shutdown)) - if output['output_type'] == 'temp_hum_control': - val = GPIO.input(pin) if not output['active_low'] else (not GPIO.input(pin)) - temp_control_status.append( - dict(index_id=index, status=val, auto_startup=startup, auto_shutdown=shutdown)) - if output['output_type'] == 'neopixel_indirect' or output['output_type'] == 'neopixel_direct': - val = output['neopixel_color'] - neopixel_status.append( - dict(index_id=index, color=val, auto_startup=startup, auto_shutdown=shutdown)) - if output['output_type'] == 'pwm': - for pwm in self.pwm_instances: - if pin in pwm: - if 'duty_cycle' in pwm: - pwm_val = pwm['duty_cycle'] - val = self.to_int(pwm_val) - else: - val = 0 - pwm_status.append( - dict(index_id=index, pwm_value=val, auto_startup=startup, auto_shutdown=shutdown)) - self._plugin_manager.send_plugin_message(self._identifier, - dict(rpi_output_regular=regular_status, rpi_output_pwm=pwm_status, - rpi_output_neopixel=neopixel_status, - rpi_output_temp_hum_ctrl=temp_control_status)) - except Exception as ex: - self.log_error(ex) - - def update_ui_inputs(self): - try: - sensor_status = [] - for sensor in self.rpi_inputs: - if sensor['input_type'] == 'gpio' and sensor['action_type'] == 'printer_control' and sensor[ - 'printer_action'] == 'filament': - index = self.to_int(sensor['index_id']) - value = sensor['filament_sensor_enabled'] - sensor_status.append(dict(index_id=index, filament_sensor_enabled=value)) - self._plugin_manager.send_plugin_message(self._identifier, dict(filament_sensor_status=sensor_status)) - except Exception as ex: - self.log_error(ex) - - def get_sensor_data(self, sensor): - try: - if self.development_mode: - temp, hum = self.read_dummy_temp() - else: - if sensor['temp_sensor_type'] in ["11", "22", "2302"]: - temp, hum = self.read_dht_temp(sensor['temp_sensor_type'], sensor['gpio_pin']) - elif sensor['temp_sensor_type'] == "18b20": - temp = self.read_18b20_temp(sensor['ds18b20_serial']) - hum = 0 - elif sensor['temp_sensor_type'] == "bme280": - temp, hum = self.read_bme280_temp(sensor['temp_sensor_address']) - elif sensor['temp_sensor_type'] == "am2320": - temp, hum = self.read_am2320_temp() # sensor has fixed address - elif sensor['temp_sensor_type'] == "si7021": - temp, hum = self.read_si7021_temp(sensor['temp_sensor_address']) - elif sensor['temp_sensor_type'] == "tmp102": - temp = self.read_tmp102_temp(sensor['temp_sensor_address']) - hum = 0 - elif sensor['temp_sensor_type'] == "max31855": - temp = self.read_max31855_temp(sensor['temp_sensor_address']) - hum = 0 - elif sensor['temp_sensor_type'] == "mcp9808": - temp = self.read_mcp_temp(sensor['temp_sensor_address']) - hum = 0 - else: - self._logger.info("temp_sensor_type no match") - temp = None - hum = None - if temp != -1 and hum != -1: - temp = round(self.to_float(temp), 1) if not sensor['use_fahrenheit'] else round( - self.to_float(temp) * 1.8 + 32, 1) - hum = round(self.to_float(hum), 1) - return temp, hum - return None, None - except Exception as ex: - self.log_error(ex) - - def handle_temperature_events(self): - for temperature_alarm in [item for item in self.rpi_outputs if item['output_type'] == 'temperature_alarm']: - set_temperature = self.to_float(temperature_alarm['alarm_set_temp']) - if int(set_temperature) is 0: - continue - linked_data = [item for item in self.temperature_sensor_data if - item['index_id'] == temperature_alarm['linked_temp_sensor']].pop() - sensor_temperature = self.to_float(linked_data['temperature']) - if set_temperature < sensor_temperature: - for rpi_controlled_output in self.rpi_outputs: - if self.to_int(temperature_alarm['controlled_io']) == self.to_int( - rpi_controlled_output['index_id']): - val = GPIO.LOW if rpi_controlled_output['active_low'] else GPIO.HIGH - self.write_gpio(self.to_int(rpi_controlled_output['gpio_pin']), val) - for notification in self.notifications: - if notification['temperatureAction']: - msg = ("Temperature action: enclosure temperature exceed " + temperature_alarm[ - 'alarm_set_temp']) - self.send_notification(msg) - - def read_dummy_temp(self): - current_value = self.dummy_value - if current_value > 40 or current_value < 30: - self.dummy_delta = - self.dummy_delta - - return_value = current_value + self.dummy_delta - - self.dummy_value = return_value - - return return_value, return_value - - def read_mcp_temp(self, address): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/mcp9808.py" - args = ["python", script, str(address)] - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature MCP9808 cmd: %s", " ".join(args)) - proc = Popen(args, stdout=PIPE) - stdout, _ = proc.communicate() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("MCP9808 result: %s", stdout) - return self.to_float(stdout.strip()) - except Exception as ex: - self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") - self.log_error(ex) - return 0 - - def read_dht_temp(self, sensor, pin): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/getDHTTemp.py " - if self._settings.get(["use_sudo"]): - sudo_str = "sudo " - else: - sudo_str = "" - cmd = sudo_str + "python " + script + str(sensor) + " " + str(pin) - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature dht cmd: %s", cmd) - stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Dht result: %s", stdout) - temp, hum = stdout.split("|") - return (self.to_float(temp.strip()), self.to_float(hum.strip())) - except Exception as ex: - self._logger.info( - "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") - self.log_error(ex) - return (0, 0) - - def read_bme280_temp(self, address): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/BME280.py " - if self._settings.get(["use_sudo"]): - sudo_str = "sudo " - else: - sudo_str = "" - cmd = sudo_str + "python " + script + str(address) - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature BME280 cmd: %s", cmd) - stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("BME280 result: %s", stdout) - temp, hum = stdout.split("|") - return (self.to_float(temp.strip()), self.to_float(hum.strip())) - except Exception as ex: - self._logger.info( - "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") - self.log_error(ex) - return (0, 0) - - def read_am2320_temp(self): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/AM2320.py " - if self._settings.get(["use_sudo"]): - sudo_str = "sudo " - else: - sudo_str = "" - cmd = sudo_str + "python " + script # sensor has fixed address 0x5C - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature AM2320 cmd: %s", cmd) - stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("AM2320 result: %s", stdout) - temp, hum = stdout.split("|") - return (self.to_float(temp.strip()), self.to_float(hum.strip())) - except Exception as ex: - self._logger.info( - "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") - self.log_error(ex) - return (0, 0) - - def read_si7021_temp(self, address): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/SI7021.py " - if self._settings.get(["use_sudo"]): - sudo_str = "sudo " - else: - sudo_str = "" - cmd = sudo_str + "python " + script + str(address) - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature SI7021 cmd: %s", cmd) - stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("SI7021 result: %s", stdout) - temp, hum = stdout.split("|") - return (self.to_float(temp.strip()), self.to_float(hum.strip())) - except Exception as ex: - self._logger.info( - "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") - self.log_error(ex) - return (0, 0) - - def read_18b20_temp(self, serial_number): - os.system('modprobe w1-gpio') - os.system('modprobe w1-therm') - - lines = self.read_raw_18b20_temp(serial_number) - while lines[0].strip()[-3:] != 'YES': - time.sleep(0.2) - lines = self.read_raw_18b20_temp(serial_number) - equals_pos = lines[1].find('t=') - if equals_pos != -1: - temp_string = lines[1][equals_pos + 2:] - temp_c = float(temp_string) / 1000. - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("DS18B20 result: %s", temp_c) - return '{0:0.1f}'.format(temp_c) - return 0 - - def read_raw_18b20_temp(self, serial_number): - base_dir = '/sys/bus/w1/devices/' - device_folder = glob.glob(base_dir + str(serial_number) + '*')[0] - device_file = device_folder + '/w1_slave' - device_file_result = open(device_file, 'r') - lines = device_file_result.readlines() - device_file_result.close() - return lines - - def read_tmp102_temp(self, address): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/tmp102.py" - args = ["python", script, str(address)] - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature TMP102 cmd: %s", " ".join(args)) - proc = Popen(args, stdout=PIPE) - stdout, _ = proc.communicate() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("TMP102 result: %s", stdout) - return self.to_float(stdout.strip()) - except Exception as ex: - self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") - self.log_error(ex) - return 0 - - def read_max31855_temp(self, address): - try: - script = os.path.dirname(os.path.realpath(__file__)) + "/max31855.py" - args = ["python", script, str(address)] - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Temperature MAX31855 cmd: %s", " ".join(args)) - proc = Popen(args, stdout=PIPE) - stdout, _ = proc.communicate() - if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("MAX31855 result: %s", stdout) - return self.to_float(stdout.strip()) - except Exception as ex: - self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") - self.log_error(ex) - return 0 - - def handle_pwm_linked_temperature(self): - try: - for pwm_output in list(filter(lambda item: item['output_type'] == 'pwm' and item['pwm_temperature_linked'], - self.rpi_outputs)): - gpio_pin = self.to_int(pwm_output['gpio_pin']) - if self._printer.is_printing(): - index_id = self.to_int(pwm_output['index_id']) - linked_id = self.to_int(pwm_output['linked_temp_sensor']) - linked_data = self.get_linked_temp_sensor_data(linked_id) - current_temp = self.to_float(linked_data['temperature']) - - duty_a = self.to_float(pwm_output['duty_a']) - duty_b = self.to_float(pwm_output['duty_b']) - temp_a = self.to_float(pwm_output['temperature_a']) - temp_b = self.to_float(pwm_output['temperature_b']) - - try: - calculated_duty = ((current_temp - temp_a) * (duty_b - duty_a) / (temp_b - temp_a)) + duty_a - - if current_temp < temp_a: - calculated_duty = 0 - except: - calculated_duty = 0 - - self._logger.debug("Calculated duty for PWM %s is %s", index_id, calculated_duty) - elif self.print_complete: - calculated_duty = self.to_int(pwm_output['duty_cycle']) - else: - calculated_duty = 0 - - self.write_pwm(gpio_pin, self.constrain(calculated_duty, 0, 100)) - - except Exception as ex: - self.log_error(ex) - - def get_linked_temp_sensor_data(self, linked_id): - try: - linked_data = [data for data in self.temperature_sensor_data if data['index_id'] == linked_id].pop() - return linked_data - except: - self._logger.warn("No linked temperature sensor found for %s", linked_id) - return None - - def handle_temp_hum_control(self): - try: - for temp_hum_control in list( - filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): - - set_temperature = self.to_float(temp_hum_control['temp_ctr_set_value']) - temp_deadband = self.to_float(temp_hum_control['temp_ctr_deadband']) - max_temp = self.to_float(temp_hum_control['temp_ctr_max_temp']) - - linked_id = temp_hum_control['linked_temp_sensor'] - - previous_status = list(filter(lambda item: item['index_id'] == temp_hum_control['index_id'], - self.temp_hum_control_status)).pop()['status'] - - if set_temperature == 0: - current_status = False - else: - linked_data = self.get_linked_temp_sensor_data(linked_id) - - control_type = str(temp_hum_control['temp_ctr_type']) - - if control_type == 'dehumidifier': - current_value = self.to_float(linked_data['humidity']) - temp_deadband = 0 - else: - current_value = self.to_float(linked_data['temperature']) - - if control_type == 'cooler' or control_type == 'dehumidifier': - if current_value <= set_temperature and current_value >= (set_temperature - temp_deadband): - current_status = previous_status - elif current_value < set_temperature: - current_status = False - else: - current_status = True - else: - if current_value <= set_temperature and current_value >= (set_temperature - temp_deadband): - current_status = previous_status - elif current_value > set_temperature: - current_status = False - else: - current_status = True - - if control_type == 'heater' and max_temp > 0.0 and max_temp < current_value: - self._logger.debug("Maximum temperature reached for temperature control %s", - temp_hum_control['index_id']) - temp_hum_control['temp_ctr_set_value'] = 0 - current_status = False - - if current_status != previous_status: - if current_status: - self._logger.info("Turning gpio to control temperature on.") - val = False if temp_hum_control['active_low'] else True - self.write_gpio(self.to_int(temp_hum_control['gpio_pin']), val) - else: - index_id = temp_hum_control['index_id'] - if index_id in self.waiting_temperature: - self.waiting_temperature.remove(index_id) - - if not self.waiting_temperature and self._printer.is_paused(): - self._printer.resume_print() - - self._logger.info("Turning gpio to control temperature off.") - val = True if temp_hum_control['active_low'] else False - self.write_gpio(self.to_int(temp_hum_control['gpio_pin']), val) - for control_status in self.temp_hum_control_status: - if control_status['index_id'] == temp_hum_control['index_id']: - control_status['status'] = current_status - except Exception as ex: - self.log_error(ex) - - def log_error(self, ex): - template = "An exception of type {0} occurred on {1}. Arguments:\n{2!r}" - message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, ex.args) - self._logger.warn(message) - - def setup_gpio(self): - try: - current_mode = GPIO.getmode() - set_mode = GPIO.BOARD if self._settings.get(["use_board_pin_number"]) else GPIO.BCM - if current_mode is None: - outputs = list(filter( - lambda item: item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ - 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct', - self.rpi_outputs)) - inputs = list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)) - gpios = outputs + inputs - if gpios: - GPIO.setmode(set_mode) - tempstr = "BOARD" if set_mode == GPIO.BOARD else "BCM" - self._logger.info("Setting GPIO mode to %s", tempstr) - elif current_mode != set_mode: - GPIO.setmode(current_mode) - tempstr = "BOARD" if current_mode == GPIO.BOARD else "BCM" - self._settings.set(["use_board_pin_number"], True if current_mode == GPIO.BOARD else False) - warn_msg = "GPIO mode was configured before, GPIO mode will be forced to use: " + tempstr + " as pin numbers. Please update GPIO accordingly!" - self._logger.info(warn_msg) - self._plugin_manager.send_plugin_message(self._identifier, - dict(is_msg=True, msg=warn_msg, msg_type="error")) - GPIO.setwarnings(False) - except Exception as ex: - self.log_error(ex) - - def clear_gpio(self): - try: - - for gpio_out in list(filter( - lambda item: item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ - 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct', - self.rpi_outputs)): - gpio_pin = self.to_int(gpio_out['gpio_pin']) - if gpio_pin not in self.rpi_outputs_not_changed: - GPIO.cleanup(gpio_pin) - - for gpio_in in list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)): - try: - GPIO.remove_event_detect(self.to_int(gpio_in['gpio_pin'])) - except: - pass - GPIO.cleanup(self.to_int(gpio_in['gpio_pin'])) - except Exception as ex: - self.log_error(ex) - - def clear_channel(self, channel): - try: - GPIO.cleanup(self.to_int(channel)) - self._logger.debug("Clearing channel %s", channel) - except Exception as ex: - self.log_error(ex) - - def generate_temp_hum_control_status(self): - status = [] - for temp_hum_control in list(filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): - status.append(dict(index_id=temp_hum_control['index_id'], status=False)) - self.temp_hum_control_status = status - - def configure_gpio(self): - try: - - for gpio_out in list( - filter(lambda item: item['output_type'] == 'regular' or item['output_type'] == 'temp_hum_control', - self.rpi_outputs)): - initial_value = GPIO.HIGH if gpio_out['active_low'] else GPIO.LOW - pin = self.to_int(gpio_out['gpio_pin']) - if pin not in self.rpi_outputs_not_changed: - self._logger.info("Setting GPIO pin %s as OUTPUT with initial value: %s", pin, initial_value) - GPIO.setup(pin, GPIO.OUT, initial=initial_value) - for gpio_out_pwm in list(filter(lambda item: item['output_type'] == 'pwm', self.rpi_outputs)): - pin = self.to_int(gpio_out_pwm['gpio_pin']) - self._logger.info("Setting GPIO pin %s as PWM", pin) - for pwm in (pwm_dict for pwm_dict in self.pwm_instances if pin in pwm_dict): - self.pwm_instances.remove(pwm) - self.clear_channel(pin) - GPIO.setup(pin, GPIO.OUT) - pwm_instance = GPIO.PWM(pin, self.to_int(gpio_out_pwm['pwm_frequency'])) - self._logger.info("starting PWM on pin %s", pin) - pwm_instance.start(0) - self.pwm_instances.append({pin: pwm_instance}) - for gpio_out_neopixel in list( - filter(lambda item: item['output_type'] == 'neopixel_direct', self.rpi_outputs)): - pin = self.to_int(gpio_out_neopixel['gpio_pin']) - self.clear_channel(pin) - - for rpi_input in list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)): - gpio_pin = self.to_int(rpi_input['gpio_pin']) - pull_resistor = GPIO.PUD_UP if rpi_input['input_pull_resistor'] == 'input_pull_up' else GPIO.PUD_DOWN - GPIO.setup(gpio_pin, GPIO.IN, pull_resistor) - edge = GPIO.RISING if rpi_input['edge'] == 'rise' else GPIO.FALLING - - inputs_same_gpio = list( - [r_inp for r_inp in self.rpi_inputs if self.to_int(r_inp['gpio_pin']) == gpio_pin]) - - if len(inputs_same_gpio) > 1: - GPIO.remove_event_detect(gpio_pin) - for other_input in inputs_same_gpio: - if other_input['edge'] is not edge: - edge = GPIO.BOTH - - if rpi_input['action_type'] == 'output_control': - self._logger.info("Adding GPIO event detect on pin %s with edge: %s", gpio_pin, edge) - GPIO.add_event_detect(gpio_pin, edge, callback=self.handle_gpio_control, bouncetime=200) - if (rpi_input['action_type'] == 'printer_control' and rpi_input['printer_action'] != 'filament'): - GPIO.add_event_detect(gpio_pin, edge, callback=self.handle_printer_action, bouncetime=200) - self._logger.info("Adding PRINTER CONTROL event detect on pin %s with edge: %s", gpio_pin, edge) - except Exception as ex: - self.log_error(ex) - - def handle_filamment_detection(self, channel): - try: - for filament_sensor in list(filter( - lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ - 'printer_action'] == 'filament' and self.to_int(item['gpio_pin']) == self.to_int(channel), - self.rpi_inputs)): - if ((filament_sensor['edge'] == 'fall') ^ (GPIO.input(self.to_int(filament_sensor['gpio_pin']))) and - filament_sensor['filament_sensor_enabled']): - last_detected_time = list(filter(lambda item: item['index_id'] == filament_sensor['index_id'], - self.last_filament_end_detected)).pop()['time'] - time_now = time.time() - time_difference = self.to_int(time_now - last_detected_time) - time_out_value = self.to_int(filament_sensor['filament_sensor_timeout']) - if time_difference > time_out_value: - self._logger.info("Detected end of filament.") - for item in self.last_filament_end_detected: - if item['index_id'] == filament_sensor['index_id']: - item['time'] = time_now - for line in self._settings.get(["filament_sensor_gcode"]).split('\n'): - if line: - self._printer.commands(line.strip().upper()) - self._logger.info("Sending GCODE command: %s", line.strip().upper()) - time.sleep(0.2) - for notification in self.notifications: - if notification['filamentChange']: - msg = "Filament change action caused by sensor: " + str(filament_sensor['label']) - self.send_notification(msg) - else: - self._logger.info("Prevented end of filament detection, filament sensor timeout not elapsed.") - except Exception as ex: - self.log_error(ex) - - def start_filament_detection(self): - self.stop_filament_detection() - try: - for filament_sensor in list(filter( - lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ - 'printer_action'] == 'filament', self.rpi_inputs)): - edge = GPIO.RISING if filament_sensor['edge'] == 'rise' else GPIO.FALLING - if GPIO.input(self.to_int(filament_sensor['gpio_pin'])) == (edge == GPIO.RISING): - self._printer.pause_print() - self._logger.info("Started printing with no filament.") - else: - self.last_filament_end_detected.append(dict(index_id=filament_sensor['index_id'], time=0)) - self._logger.info("Adding GPIO event detect on pin %s with edge: %s", filament_sensor['gpio_pin'], - edge) - GPIO.add_event_detect(self.to_int(filament_sensor['gpio_pin']), edge, - callback=self.handle_filamment_detection, bouncetime=200) - except Exception as ex: - self.log_error(ex) - - def stop_filament_detection(self): - try: - self.last_filament_end_detected = [] - for filament_sensor in list(filter( - lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ - 'printer_action'] == 'filament', self.rpi_inputs)): - GPIO.remove_event_detect(self.to_int(filament_sensor['gpio_pin'])) - except Exception as ex: - self.log_error(ex) - - def cancel_all_events_on_queue(self): - for task in self.event_queue: - try: - task['thread'].cancel() - except: - self._logger.warn("Failed to stop task %s.", task) - pass - - def handle_initial_gpio_control(self): - try: - for rpi_input in list( - filter(lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'output_control', - self.rpi_inputs)): - gpio_pin = self.to_int(rpi_input['gpio_pin']) - controlled_io = self.to_int(rpi_input['controlled_io']) - if (rpi_input['edge'] == 'fall') ^ GPIO.input(gpio_pin): - rpi_output = [r_out for r_out in self.rpi_outputs if - self.to_int(r_out['index_id']) == controlled_io].pop() - if rpi_output['output_type'] == 'regular': - val = GPIO.LOW if rpi_input['controlled_io_set_value'] == 'low' else GPIO.HIGH - self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) - except Exception as ex: - self.log_error(ex) - pass - - def shell_command(self, command): - try: - stdout = (Popen(command, shell=True, stdout=PIPE).stdout).read() - self._plugin_manager.send_plugin_message(self._identifier, - dict(is_msg=True, msg=stdout, msg_type="success")) - except Exception as ex: - self.log_error(ex) - self._plugin_manager.send_plugin_message(self._identifier, - dict(is_msg=True, msg="Could not execute shell script", msg_type="error")) - - def handle_gpio_control(self, channel): - - try: - self._logger.debug("GPIO event triggered on channel %s", channel) - for rpi_input in list( - filter(lambda item: self.to_int(item['gpio_pin']) == self.to_int(channel), self.rpi_inputs)): - gpio_pin = self.to_int(rpi_input['gpio_pin']) - controlled_io = self.to_int(rpi_input['controlled_io']) - if (rpi_input['edge'] == 'fall') ^ GPIO.input(gpio_pin): - rpi_output = [r_out for r_out in self.rpi_outputs if - self.to_int(r_out['index_id']) == controlled_io].pop() - if rpi_output['output_type'] == 'regular': - if rpi_input['controlled_io_set_value'] == 'toggle': - val = GPIO.LOW if GPIO.input( - self.to_int(rpi_output['gpio_pin'])) == GPIO.HIGH else GPIO.HIGH - else: - val = GPIO.LOW if rpi_input['controlled_io_set_value'] == 'low' else GPIO.HIGH - self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) - for notification in self.notifications: - if notification['gpioAction']: - msg = "GPIO control action caused by input " + str( - rpi_input['label']) + ". Setting GPIO" + str( - rpi_input['controlled_io']) + " to: " + str(rpi_input['controlled_io_set_value']) - self.send_notification(msg) - if rpi_output['output_type'] == 'gcode_output': - self.send_gcode_command(rpi_output['gcode']) - for notification in self.notifications: - if notification['gpioAction']: - msg = "GPIO control action caused by input " + str( - rpi_input['label']) + ". Sending GCODE command" - self.send_notification(msg) - if rpi_output['output_type'] == 'shell_output': - command = rpi_output['shell_script'] - self.shell_command(command) - except Exception as ex: - self.log_error(ex) - pass - - def send_gcode_command(self, command): - for line in command.split('\n'): - if line: - self._printer.commands(line.strip().upper()) - self._logger.info("Sending GCODE command: %s", line.strip().upper()) - time.sleep(0.2) - - def handle_printer_action(self, channel): - try: - for rpi_input in self.rpi_inputs: - if (channel == self.to_int(rpi_input['gpio_pin']) and rpi_input[ - 'action_type'] == 'printer_control' and ( - (rpi_input['edge'] == 'fall') ^ GPIO.input(self.to_int(rpi_input['gpio_pin'])))): - if rpi_input['printer_action'] == 'resume': - self._logger.info("Printer action resume.") - self._printer.resume_print() - elif rpi_input['printer_action'] == 'pause': - self._logger.info("Printer action pause.") - self._printer.pause_print() - elif rpi_input['printer_action'] == 'cancel': - self._logger.info("Printer action cancel.") - self._printer.cancel_print() - elif rpi_input['printer_action'] == 'toggle': - self._logger.info("Printer action toggle.") - if self._printer.is_operational(): - self._printer.toggle_pause_print() - else: - self._printer.connect() - elif rpi_input['printer_action'] == 'start': - self._logger.info("Printer action start.") - self._printer.start_print() - elif rpi_input['printer_action'] == 'toggle_job': - self._logger.info("Printer action toggle_job.") - if self._printer.is_operational(): - if self._printer.is_printing(): - self._printer.cancel_print() - elif self._printer.is_ready(): - self._printer.start_print() - else: - self._printer.connect() - elif rpi_input['printer_action'] == 'stop_temp_hum_control': - self._logger.info("Printer action stopping temperature control.") - for rpi_output in self.rpi_outputs: - if rpi_output['auto_shutdown'] and rpi_output['output_type'] == 'temp_hum_control': - rpi_output['temp_ctr_set_value'] = 0 - self.handle_temp_hum_control() - for notification in self.notifications: - if notification['printer_action']: - msg = "Printer action: " + rpi_input['printer_action'] + " caused by input: " + str( - rpi_input['label']) - self.send_notification(msg) - except Exception as ex: - self.log_error(ex) - pass - - def write_gpio(self, gpio, value, queue_id=None): - try: - GPIO.output(gpio, value) - if queue_id is not None: - self._logger.debug("Running scheduled queue id %s", queue_id) - self._logger.debug("Writing on GPIO: %s value %s", gpio, value) - self.update_ui() - if queue_id is not None: - self.stop_queue_item(queue_id) - except Exception as ex: - template = "An exception of type {0} occurred on {1} when writing on pin {2}. Arguments:\n{3!r}" - message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, gpio, ex.args) - self._logger.warn(message) - pass - - def write_pwm(self, gpio, pwm_value, queue_id=None): - try: - if queue_id is not None: - self._logger.debug("running scheduled queue id %s", queue_id) - for pwm in self.pwm_instances: - if gpio in pwm: - pwm_object = pwm[gpio] - old_pwm_value = pwm['duty_cycle'] if 'duty_cycle' in pwm else -1 - if not self.to_int(old_pwm_value) == self.to_int(pwm_value): - pwm['duty_cycle'] = pwm_value - pwm_object.start(pwm_value) #should be changed back to pwm_object.ChangeDutyCycle() but this - # was causing errors. - self._logger.debug("Writing PWM on gpio: %s value %s", gpio, pwm_value) - self.update_ui() - if queue_id is not None: - self.stop_queue_item(queue_id) - break - except Exception as ex: - self.log_error(ex) - pass - - def get_output_list(self): - result = [] - for rpi_output in self.rpi_outputs: - if rpi_output['output_type'] == 'regular': - result.append(self.to_int(rpi_output['gpio_pin'])) - return result - - def send_notification(self, message): - try: - provider = self._settings.get(["notification_provider"]) - if provider == 'ifttt': - event = self._settings.get(["notification_event_name"]) - api_key = self._settings.get(["notification_api_key"]) - self._logger.debug("Sending notification to: %s with msg: %s with key: %s", provider, message, - api_key) - try: - res = self.ifttt_notification(message, event, api_key) - except requests.exceptions.ConnectionError: - self._logger.info("Error: Could not connect to IFTTT") - except requests.exceptions.HTTPError: - self._logger.info("Error: Received invalid response") - except requests.exceptions.Timeout: - self._logger.info("Error: Request timed out") - except requests.exceptions.TooManyRedirects: - self._logger.info("Error: Too many redirects") - except requests.exceptions.RequestException as reqe: - self._logger.info("Error: {e}".format(e=reqe)) - if res.status_code != requests.codes['ok']: - try: - j = res.json() - except ValueError: - self._logger.info('Error: Could not parse server response. Event not sent') - for err in j['errors']: - self._logger.info('Error: {}'.format(err['message'])) - except Exception as ex: - self.log_error(ex) - pass - - def ifttt_notification(self, message, event, api_key): - url = "https://maker.ifttt.com/trigger/{e}/with/key/{k}/".format(e=event, k=api_key) - payload = {'value1': message} - return requests.post(url, data=payload) - - # ~~ EventPlugin mixin - def on_event(self, event, payload): - if event == Events.CONNECTED: - self.update_ui() - - if event == Events.CLIENT_OPENED: - self.update_ui() - - if event == Events.PRINT_RESUMED: - self.start_filament_detection() - - if event == Events.PRINT_STARTED: - self.print_complete = False - self.cancel_all_events_on_queue() - self.event_queue = [] - self.start_filament_detection() - for rpi_output in self.rpi_outputs: - if rpi_output['auto_startup']: - delay_seconds = self.get_startup_delay_from_output(rpi_output) - self.schedule_auto_startup_outputs(rpi_output, delay_seconds) - if rpi_output['toggle_timer']: - if rpi_output['output_type'] == 'regular' or rpi_output['output_type'] == 'pwm': - self.toggle_output(rpi_output['index_id'], True) - if self.is_hour(rpi_output['shutdown_time']): - shutdown_delay_seconds = self.get_shutdown_delay_from_output(rpi_output) - self.schedule_auto_shutdown_outputs(rpi_output, shutdown_delay_seconds) - self.run_tasks() - self.update_ui() - - elif event == Events.PRINT_DONE: - self.stop_filament_detection() - self.print_complete = True - for rpi_output in self.rpi_outputs: - shutdown_time = rpi_output['shutdown_time'] - if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: - rpi_output['duty_cycle'] = rpi_output['default_duty_cycle'] - if rpi_output['auto_shutdown'] and not self.is_hour(shutdown_time): - delay_seconds = self.to_float(shutdown_time) - self.schedule_auto_shutdown_outputs(rpi_output, delay_seconds) - self.run_tasks() - self.update_ui() - - elif event in (Events.PRINT_CANCELLED, Events.PRINT_FAILED): - self.stop_filament_detection() - self.cancel_all_events_on_queue() - self.event_queue = [] - for rpi_output in self.rpi_outputs: - if rpi_output['shutdown_on_failed']: - shutdown_time = rpi_output['shutdown_time'] - if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: - rpi_output['duty_cycle'] = rpi_output['default_duty_cycle'] - if rpi_output['auto_shutdown'] and not self.is_hour(shutdown_time): - delay_seconds = self.to_float(shutdown_time) - self.schedule_auto_shutdown_outputs(rpi_output, delay_seconds) - if rpi_output['output_type'] == 'temp_hum_control': - rpi_output['temp_ctr_set_value'] = 0 - self.run_tasks() - - if event == Events.PRINT_DONE: - for notification in self.notifications: - if notification['printFinish']: - file_name = os.path.basename(payload["file"]) - elapsed_time_in_seconds = payload["time"] - elapsed_time = octoprint.util.get_formatted_timedelta(timedelta(seconds=elapsed_time_in_seconds)) - msg = "Print job finished: " + file_name + "finished printing in " + file_name, elapsed_time - self.send_notification(msg) - - def run_tasks(self): - for task in self.event_queue: - if not task['thread'].is_alive(): - task['thread'].start() - - def schedule_auto_shutdown_outputs(self, rpi_output, shutdown_delay_seconds): - sufix = 'auto_shutdown' - if rpi_output['output_type'] == 'regular': - value = True if rpi_output['active_low'] else False - self.add_regular_output_to_queue(shutdown_delay_seconds, rpi_output, value, sufix) - if rpi_output['output_type'] == 'ledstrip': - self.ledstrip_set_rgb(rpi_output) - if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: - value = 0 - self.add_pwm_output_to_queue(shutdown_delay_seconds, rpi_output, value, sufix) - if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: - self.schedule_pwm_duty_on_queue(shutdown_delay_seconds, rpi_output, 0, sufix) - if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): - self.add_neopixel_output_to_queue(rpi_output, shutdown_delay_seconds, 0, 0, 0, sufix) - if rpi_output['output_type'] == 'temp_hum_control': - value = 0 - self.add_temperature_output_temperature_queue(shutdown_delay_seconds, rpi_output, value, sufix) - self._logger.debug("Events scheduled to run %s", self.event_queue) - - def ledstrip_set_rgb(self, rpi_output, rgb=None): - clk = rpi_output["ledstrip_gpio_clk"] - data = rpi_output["ledstrip_gpio_dat"] - if clk is not None and data is not None: - ledstrip = LEDStrip(self.to_int(clk), self.to_int(data)) - if rgb is None: - red, green, blue = self.get_color_from_rgb(rpi_output['default_ledstrip_color']) - else: - red, green, blue = self.get_color_from_rgb(rgb) - - self._logger.info("LEDSTRIP set rgb color: %s, %s, %s", red, green, blue) - ledstrip.setcolourrgb(self.to_int(red), self.to_int(green), self.to_int(blue)) - - def start_outpus_with_server(self): - for rpi_output in self.rpi_outputs: - if rpi_output['startup_with_server']: - gpio = self.to_int(rpi_output['gpio_pin']) - if rpi_output['output_type'] == 'regular': - value = False if rpi_output['active_low'] else True - self.write_gpio(gpio, value) - if rpi_output['output_type'] == 'ledstrip': - self.ledstrip_set_rgb(rpi_output) - if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: - value = self.to_int(rpi_output['default_duty_cycle']) - self.write_pwm(gpio, value) - if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): - red, green, blue = self.get_color_from_rgb(rpi_output['default_neopixel_color']) - led_count = rpi_output['neopixel_count'] - led_brightness = rpi_output['neopixel_brightness'] - address = rpi_output['microcontroller_address'] - index_id = self.to_int(rpi_output['index_id']) - neopixel_direct = rpi_output['output_type'] == 'neopixel_direct' - self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, - green, blue, address, neopixel_direct, index_id) - if rpi_output['output_type'] == 'temp_hum_control': - rpi_output['temp_ctr_set_value'] = rpi_output['temp_ctr_default_value'] - - def schedule_auto_startup_outputs(self, rpi_output, delay_seconds): - sufix = 'auto_startup' - if rpi_output['output_type'] == 'regular': - value = False if rpi_output['active_low'] else True - self.add_regular_output_to_queue(delay_seconds, rpi_output, value, sufix) - if rpi_output['output_type'] == 'ledstrip': - self.ledstrip_set_rgb(rpi_output) - if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: - value = self.to_int(rpi_output['default_duty_cycle']) - self.add_pwm_output_to_queue(delay_seconds, rpi_output, value, sufix) - if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): - red, green, blue = self.get_color_from_rgb(rpi_output['default_neopixel_color']) - self.add_neopixel_output_to_queue(rpi_output, delay_seconds, red, green, blue, sufix) - if rpi_output['output_type'] == 'temp_hum_control': - value = rpi_output['temp_ctr_default_value'] - self.add_temperature_output_temperature_queue(delay_seconds, rpi_output, value, sufix) - self._logger.debug("Events scheduled to run %s", self.event_queue) - - def get_color_from_rgb(self, stringColor): - stringColor = stringColor.replace('rgb(', '') - red = stringColor[:stringColor.index(',')] - stringColor = stringColor[stringColor.index(',') + 1:] - green = stringColor[:stringColor.index(',')] - stringColor = stringColor[stringColor.index(',') + 1:] - blue = stringColor[:stringColor.index(')')] - return red, green, blue - - def get_shutdown_delay_from_output(self, rpi_output): - shutdown_time = rpi_output['shutdown_time'] - - shut_down_date_time = self.create_date(shutdown_time) - - if shut_down_date_time < datetime.now(): - shut_down_date_time = shut_down_date_time + timedelta(days=1) - - delay_seconds = (shut_down_date_time - datetime.now()).total_seconds() - - return delay_seconds - - def add_neopixel_output_to_queue(self, rpi_output, delay_seconds, red, green, blue, sufix): - gpio_pin = rpi_output['gpio_pin'] - ledCount = rpi_output['neopixel_count'] - ledBrightness = rpi_output['neopixel_brightness'] - address = rpi_output['microcontroller_address'] - neopixel_direct = rpi_output['output_type'] == 'neopixel_direct' - index_id = self.to_int(rpi_output['index_id']) - - queue_id = '{0!s}_{1!s}'.format(index_id, sufix) - - self._logger.debug("Scheduling neopixel output id %s for on %s delay_seconds", queue_id, delay_seconds) - - thread = threading.Timer(delay_seconds, self.send_neopixel_command, - args=[gpio_pin, ledCount, ledBrightness, red, green, blue, address, neopixel_direct, - index_id, queue_id]) - - self.event_queue.append(dict(queue_id=queue_id, thread=thread)) - - def add_pwm_output_to_queue(self, delay_seconds, rpi_output, value, sufix): - queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) - - self._logger.debug("Scheduling pwm output id %s for on %s delay_seconds", queue_id, delay_seconds) - - thread = threading.Timer(delay_seconds, self.write_pwm, - args=[self.to_int(rpi_output['gpio_pin']), value, queue_id]) - - self.event_queue.append(dict(queue_id=queue_id, thread=thread)) - - def schedule_pwm_duty_on_queue(self, delay_seconds, rpi_output, value, sufix): - queue_id = '{0!s}_{1!s}_{2!s}'.format(rpi_output['index_id'], "pwm_linked_temp", sufix) - thread = threading.Timer(delay_seconds, self.set_pwm_duty_cycle, args=[rpi_output, value, queue_id]) - - self._logger.debug("Scheduling pwm linked temp output id %s on %s delay_seconds", queue_id, delay_seconds) - - self.event_queue.append(dict(queue_id=queue_id, thread=thread)) - - def set_pwm_duty_cycle(self, rpi_output, value, queue_id): - rpi_output['duty_cycle'] = value - if queue_id is not None: - self.stop_queue_item(queue_id) - - def add_regular_output_to_queue(self, delay_seconds, rpi_output, value, sufix): - queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) - - self._logger.debug("Scheduling regular output id %s on %s delay_seconds", queue_id, delay_seconds) - - thread = threading.Timer(delay_seconds, self.write_gpio, - args=[self.to_int(rpi_output['gpio_pin']), value, queue_id]) - - self.event_queue.append(dict(queue_id=queue_id, thread=thread)) - - def add_temperature_output_temperature_queue(self, delay_seconds, rpi_output, value, sufix): - queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) - self._logger.debug("Scheduling temperature control id %s on %s delay_seconds", queue_id, delay_seconds) - - thread = threading.Timer(delay_seconds, self.write_temperature_to_output, - args=[self.to_int(rpi_output['index_id']), value, queue_id]) - - self.event_queue.append(dict(queue_id=queue_id, thread=thread)) - - def write_temperature_to_output(self, rpi_output_index, value, queue_id=None): - try: - rpi_output = [r_out for r_out in self.rpi_outputs if - self.to_int(r_out['index_id']) == rpi_output_index].pop() - - if rpi_output['output_type'] == 'temp_hum_control': - rpi_output['temp_ctr_set_value'] = value - - if queue_id is not None: - self._logger.debug("running scheduled queue id %s", queue_id) - self._logger.debug("Setting temperature to output index: %s value %s", rpi_output['index_id'], value) - - self.update_ui() - if queue_id is not None: - self.stop_queue_item(queue_id) - - except Exception as ex: - template = "An exception of type {0} occurred on {1}. Arguments:\n{3!r}" - message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, ex.args) - self._logger.warn(message) - pass - - def get_startup_delay_from_output(self, rpi_output): - start_up_time = rpi_output['startup_time'] - if self.is_hour(start_up_time): - start_up_date_time = self.create_date(start_up_time) - if start_up_date_time < datetime.now(): - delay_seconds = 0.0 - else: - delay_seconds = (start_up_date_time - datetime.now()).total_seconds() - else: - delay_seconds = self.to_float(rpi_output['startup_time']) - return delay_seconds - - # ~~ SettingsPlugin mixin - def on_settings_save(self, data): - outputsBeforeSave = self.get_output_list() - octoprint.plugin.SettingsPlugin.on_settings_save(self, data) - self.rpi_outputs = self._settings.get(["rpi_outputs"]) - self.rpi_inputs = self._settings.get(["rpi_inputs"]) - self.notifications = self._settings.get(["notifications"]) - outputsAfterSave = self.get_output_list() - - commonPins = list(set(outputsBeforeSave) & set(outputsAfterSave)) - - for pin in (pin for pin in outputsBeforeSave if pin not in commonPins): - self.clear_channel(pin) - - self.rpi_outputs_not_changed = commonPins - self.clear_gpio() - - self._logger.debug("rpi_outputs: %s", self.rpi_outputs) - self._logger.debug("rpi_inputs: %s", self.rpi_inputs) - self.setup_gpio() - self.configure_gpio() - self.generate_temp_hum_control_status() - - def get_settings_defaults(self): - return dict(rpi_outputs=[], rpi_inputs=[], - filament_sensor_gcode="G91 ;Set Relative Mode \n" + "G1 E-5.000000 F500 ;Retract 5mm\n" + "G1 Z15 F300 ;move Z up 15mm\n" + "G90 ;Set Absolute Mode\n " + "G1 X20 Y20 F9000 ;Move to hold position\n" + "G91 ;Set Relative Mode\n" + "G1 E-40 F500 ;Retract 40mm\n" + "M0 ;Idle Hold\n" + "G90 ;Set Absolute Mode\n" + "G1 F5000 ;Set speed limits\n" + "G28 X0 Y0 ;Home X Y\n" + "M82 ;Set extruder to Absolute Mode\n" + "G92 E0 ;Set Extruder to 0", - use_sudo=True, neopixel_dma=10, debug=False, gcode_control=False, debug_temperature_log=False, - use_board_pin_number=False, notification_provider="disabled", notification_api_key="", - notification_event_name="printer_event", notifications=[{ - 'printFinish' : True, - 'filamentChange' : True, - 'printer_action' : True, - 'temperatureAction': True, 'gpioAction': True - }]) + # ~~ TemplatePlugin - def get_template_configs(self): - return [dict(type="settings", custom_bindings=True), dict(type="tab", custom_bindings=True), - dict(type="navbar", custom_bindings=True, suffix="_1", classes=["dropdown"]), - dict(type="navbar", custom_bindings=True, template="enclosure_navbar_input.jinja2", suffix="_2", - classes=["dropdown"])] + # def get_template_configs(self): + # return [dict(type="settings", custom_bindings=True), dict(type="tab", custom_bindings=True), + # dict(type="navbar", custom_bindings=True, suffix="_1", classes=["dropdown"]), + # dict(type="navbar", custom_bindings=True, template="enclosure_navbar_input.jinja2", suffix="_2", + # classes=["dropdown"])] # ~~ AssetPlugin mixin - def get_assets(self): - return dict(js=["js/enclosure.js", "js/bootstrap-colorpicker.min.js"], - css=["css/bootstrap-colorpicker.css", "css/enclosure.css"]) + # def get_assets(self): + # return dict(js=["js/enclosure.js", "js/bootstrap-colorpicker.min.js"], + # css=["css/bootstrap-colorpicker.css", "css/enclosure.css"]) # ~~ Softwareupdate hook - def get_update_information(self): - return dict(enclosure=dict(displayName="Enclosure Plugin", displayVersion=self._plugin_version, - # version check: github repository - type="github_release", user="vitormhenrique", repo="OctoPrint-Enclosure", current=self._plugin_version, - # update method: pip - pip="https://github.com/vitormhenrique/OctoPrint-Enclosure/archive/{target_version}.zip")) + # def get_update_information(self): + # return dict(enclosure=dict(displayName="Enclosure Plugin", displayVersion=self._plugin_version, + # # version check: github repository + # type="github_release", user="vitormhenrique", repo="OctoPrint-Enclosure", current=self._plugin_version, + # # update method: pip + # pip="https://github.com/vitormhenrique/OctoPrint-Enclosure/archive/{target_version}.zip")) - def hook_gcode_queuing(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs): - if self._settings.get(["gcode_control"]) is False: - return - - if cmd.strip().startswith("ENC"): - self._logger.debug("Gcode queuing: %s", cmd) - index_id = self.to_int(self.get_gcode_value(cmd, 'O')) - for output in [item for item in self.rpi_outputs if item['index_id'] == index_id]: - if output['output_type'] == 'regular': - set_value = self.to_int(self.get_gcode_value(cmd, 'S')) - set_value = self.constrain(set_value, 0, 1) - value = True if set_value == 1 else False - value = (not value) if output['active_low'] else value - self.write_gpio(self.to_int(output['gpio_pin']), value) - comm_instance._log("Setting REGULAR output %s to value %s" % (index_id, value)) - return - if output['output_type'] == 'pwm': - set_value = self.to_int(self.get_gcode_value(cmd, 'S')) - set_value = self.constrain(set_value, 0, 100) - output['duty_cycle'] = set_value - self.write_pwm(self.to_int(output['gpio_pin']), set_value) - comm_instance._log("Setting PWM output %s to value %s" % (index_id, set_value)) - return - if output['output_type'] == 'neopixel_indirect' or output['output_type'] == 'neopixel_direct': - red = self.get_gcode_value(cmd, 'R') - green = self.get_gcode_value(cmd, 'G') - blue = self.get_gcode_value(cmd, 'B') - - led_count = output['neopixel_count'] - led_brightness = output['neopixel_brightness'] - address = output['microcontroller_address'] - - index_id = self.to_int(output['index_id']) - - neopixel_direct = output['output_type'] == 'neopixel_direct' - - self.send_neopixel_command(self.to_int(output['gpio_pin']), led_count, led_brightness, red, green, - blue, address, neopixel_direct, index_id) - comm_instance._log( - "Setting NEOPIXEL output %s to red: %s green: %s blue: %s" % (index_id, red, green, blue)) - return - if output['output_type'] == 'temp_hum_control': - set_value = self.to_float(self.get_gcode_value(cmd, 'S')) - should_wait = self.to_int(self.get_gcode_value(cmd, 'W')) - if should_wait == 1 and self._printer.is_printing(): - self._printer.pause_print() - self.waiting_temperature.append(index_id) - output['temp_ctr_set_value'] = set_value - self.update_ui_set_temperature() - self.handle_temp_hum_control() - comm_instance._log("Setting TEMP/HUM control output %s to value %s" % (index_id, set_value)) - return + __plugin_name__ = "Enclosure Plugin" @@ -1889,6 +44,6 @@ def __plugin_load__(): global __plugin_hooks__ __plugin_hooks__ = { - "octoprint.comm.protocol.gcode.queuing" : __plugin_implementation__.hook_gcode_queuing, + # "octoprint.comm.protocol.gcode.queuing" : __plugin_implementation__.hook_gcode_queuing, "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information } \ No newline at end of file diff --git a/octoprint_enclosure/AM2320.py b/octoprint_enclosure/old_stuff/AM2320.py similarity index 100% rename from octoprint_enclosure/AM2320.py rename to octoprint_enclosure/old_stuff/AM2320.py diff --git a/octoprint_enclosure/BME280.py b/octoprint_enclosure/old_stuff/BME280.py similarity index 100% rename from octoprint_enclosure/BME280.py rename to octoprint_enclosure/old_stuff/BME280.py diff --git a/octoprint_enclosure/SI7021.py b/octoprint_enclosure/old_stuff/SI7021.py similarity index 100% rename from octoprint_enclosure/SI7021.py rename to octoprint_enclosure/old_stuff/SI7021.py diff --git a/octoprint_enclosure/old_stuff/__init__.py b/octoprint_enclosure/old_stuff/__init__.py new file mode 100644 index 0000000..d66a07b --- /dev/null +++ b/octoprint_enclosure/old_stuff/__init__.py @@ -0,0 +1,1894 @@ +# coding=utf-8 +from __future__ import absolute_import +from octoprint.events import eventManager, Events +from octoprint.util import RepeatedTimer +from subprocess import Popen, PIPE +from .ledstrip import LEDStrip +import octoprint.plugin +import RPi.GPIO as GPIO +from flask import jsonify, request, make_response, Response +from octoprint.server.util.flask import restricted_access +from werkzeug.exceptions import BadRequest +import time +import sys +import glob +import os +from datetime import datetime +from datetime import timedelta +import octoprint.util +import requests +import inspect +import threading +import json +import copy + + +class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin, + octoprint.plugin.AssetPlugin, octoprint.plugin.BlueprintPlugin, + octoprint.plugin.EventHandlerPlugin): + rpi_outputs = [] + rpi_inputs = [] + waiting_temperature = [] + rpi_outputs_not_changed = [] + notifications = [] + pwm_instances = [] + event_queue = [] + temp_hum_control_status = [] + temperature_sensor_data = [] + last_filament_end_detected = [] + print_complete = False + development_mode = False + dummy_value = 30.0 + dummy_delta = 0.5 + + def start_timer(self): + """ + Function to start timer that checks enclosure temperature + """ + + self._check_temp_timer = RepeatedTimer(10, self.check_enclosure_temp, None, None, True) + self._check_temp_timer.start() + + @staticmethod + def to_float(value): + """Converts value to flow + + Arguments: + value {any} -- value to be + + Returns: + float -- value converted + """ + + try: + val = float(value) + return val + except: + return 0 + + @staticmethod + def to_int(value): + try: + val = int(value) + return val + except: + return 0 + + @staticmethod + def is_hour(value): + try: + datetime.strptime(value, '%H:%M') + return True + except: + return False + + @staticmethod + def create_date(value): + temp_string = datetime.now().strftime('%m/%d/%Y') + " " + value + return datetime.strptime(temp_string, '%m/%d/%Y %H:%M') + + @staticmethod + def constrain(n, minn, maxn): + return max(min(maxn, n), minn) + + @staticmethod + def get_gcode_value(command_string, gcode): + semicolon = command_string.find(';') + if not semicolon == -1: + command_string = command_string[:semicolon] + + for command in command_string.split(' '): + index = command.upper().find(gcode.upper()) + if not index == -1: + return command.replace(gcode, '').upper() + return -1 + + # ~~ StartupPlugin mixin + def on_after_startup(self): + self.pwm_instances = [] + self.event_queue = [] + self.rpi_outputs_not_changed = [] + self.rpi_outputs = self._settings.get(["rpi_outputs"]) + self.rpi_inputs = self._settings.get(["rpi_inputs"]) + self.notifications = self._settings.get(["notifications"]) + self.generate_temp_hum_control_status() + self.setup_gpio() + self.configure_gpio() + self.update_ui() + self.start_outpus_with_server() + self.handle_initial_gpio_control() + self.start_timer() + self.print_complete = False + + def get_settings_version(self): + return 6 + + def on_settings_migrate(self, target, current=None): + self._logger.warn("######### current settings version %s target settings version %s #########", current, target) + self._logger.info("######### Current settings #########") + self._logger.info("rpi_outputs: %s", self.rpi_outputs) + self._logger.info("rpi_inputs: %s", self.rpi_inputs) + self._logger.info("######### End Current Settings #########") + if current >= 4 and target == 6: + self._logger.warn("######### migrating settings to v6 #########") + old_outputs = self._settings.get(["rpi_outputs"]) + for rpi_output in old_outputs: + if 'shutdown_on_failed' not in rpi_output: + rpi_output['shutdown_on_failed'] = False + if 'shell_script' not in rpi_output: + rpi_output['shell_script'] = "" + self._settings.set(["rpi_outputs"], old_outputs) + else: + self._logger.warn("######### settings not compatible #########") + self._settings.set(["rpi_outputs"], []) + self._settings.set(["rpi_inputs"], []) + self.rpi_inputs = self._settings.get(["rpi_inputs"]) + + # ~~ Blueprintplugin mixin + @octoprint.plugin.BlueprintPlugin.route("/inputs", methods=["GET"]) + def get_inputs(self): + inputs = [] + for rpi_input in self.rpi_inputs: + index = self.to_int(rpi_input['index_id']) + label = rpi_input['label'] + inputs.append(dict(index_id=index, label=label)) + return Response(json.dumps(inputs), mimetype='application/json') + + + @octoprint.plugin.BlueprintPlugin.route("/inputs/", methods=["GET"]) + def get_input_status(self, identifier): + for rpi_input in self.rpi_inputs: + if identifier == self.to_int(rpi_input['index_id']): + return Response(json.dumps(rpi_input), mimetype='application/json') + return make_response('', 404) + + + @octoprint.plugin.BlueprintPlugin.route("/temperature/", methods=["PATCH"]) + @restricted_access + def set_enclosure_temp_humidity(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'temperature' not in data: + return make_response("missing temperature attribute", 406) + + set_value = data["temperature"] + + for temp_hum_control in [item for item in self.rpi_outputs if item['index_id'] == identifier]: + temp_hum_control['temp_ctr_set_value'] = set_value + + self.handle_temp_hum_control() + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/filament/", methods=["PATCH"]) + @restricted_access + def set_filament_sensor(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'status' not in data: + return make_response("missing status attribute", 406) + + value = data["status"] + + for sensor in self.rpi_inputs: + if identifier == self.to_int(sensor['index_id']): + sensor['filament_sensor_enabled'] = value + self._logger.info("Setting filament sensor for input %s to : %s", str(identifier), value) + self._settings.set(["rpi_inputs"], self.rpi_inputs) + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/outputs", methods=["GET"]) + def get_outputs(self): + outputs = [] + for rpi_output in self.rpi_outputs: + if rpi_output['output_type'] == 'regular': + index = self.to_int(rpi_output['index_id']) + label = rpi_output['label'] + outputs.append(dict(index_id=index, label=label)) + return Response(json.dumps(outputs), mimetype='application/json') + + + @octoprint.plugin.BlueprintPlugin.route("/outputs/", methods=["GET"]) + def get_output_status(self, identifier): + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['index_id']): + out = copy.deepcopy(rpi_output) + pin = self.to_int(rpi_output['gpio_pin']) + out['current_value'] = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) + return Response(json.dumps(out), mimetype='application/json') + return make_response('', 404) + + + @octoprint.plugin.BlueprintPlugin.route("/outputs/", methods=["PATCH"]) + @restricted_access + def set_io(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'status' not in data: + return make_response("missing status attribute", 406) + + value = data["status"] + + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['index_id']): + val = (not value) if rpi_output['active_low'] else value + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/outputs//auto-startup", methods=["PATCH"]) + @restricted_access + def set_auto_startup(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'status' not in data: + return make_response("missing status attribute", 406) + + value = data["status"] + + if not value: + suffix = 'auto_startup' + queue_id = '{0!s}_{1!s}'.format(str(identifier), suffix) + self.stop_queue_item(queue_id) + for output in self.rpi_outputs: + if identifier == self.to_int(output['index_id']): + output['auto_startup'] = value + self._logger.info("Setting auto startup for output %s to : %s", str(identifier), value) + self._settings.set(["rpi_outputs"], self.rpi_outputs) + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/outputs//auto-shutdown", methods=["PATCH"]) + @restricted_access + def set_auto_shutdown(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'status' not in data: + return make_response("missing status attribute", 406) + + value = data["status"] + + if not value: + suffix = 'auto_shutdown' + queue_id = '{0!s}_{1!s}'.format(str(identifier), suffix) + self.stop_queue_item(queue_id) + + for output in self.rpi_outputs: + if identifier == self.to_int(output['index_id']): + output['auto_shutdown'] = value + self._logger.info("Setting auto shutdown for output %s to : %s", str(identifier), value) + self._settings.set(["rpi_outputs"], self.rpi_outputs) + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/pwm/", methods=["PATCH"]) + @restricted_access + def set_pwm(self, identifier): + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'duty_cycle' not in data: + return make_response("missing duty_cycle attribute", 406) + + set_value = self.to_int(data['duty_cycle']) + for rpi_output in [item for item in self.rpi_outputs if item['index_id'] == identifier]: + rpi_output['duty_cycle'] = set_value + rpi_output['new_duty_cycle'] = "" + gpio = self.to_int(rpi_output['gpio_pin']) + self.write_pwm(gpio, set_value) + return make_response('', 204) + + @octoprint.plugin.BlueprintPlugin.route("/rgb-led/", methods=["PATCH"]) + @restricted_access + def set_ledstrip_color(self, identifier): + """ set_ledstrip_color method get request from octoprint and send the command to Open-Smart RGB LED Strip""" + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'rgb' not in data: + return make_response("missing rgb attribute", 406) + rgb = data['rgb'] + + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['index_id']): + self.ledstrip_set_rgb(rpi_output, rgb) + + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/neopixel/", methods=["PATCH"]) + @restricted_access + def set_neopixel(self, identifier): + """ set_neopixel method get request from octoprint and send the command to arduino or neopixel""" + if "application/json" not in request.headers["Content-Type"]: + return make_response("expected json", 400) + try: + data = request.json + except BadRequest: + return make_response("malformed request", 400) + + if 'red' not in data: + return make_response("missing red attribute", 406) + if 'green' not in data: + return make_response("missing green attribute", 406) + if 'blue' not in data: + return make_response("missing blue attribute", 406) + + red = data['red'] + green = data['green'] + blue = data['blue'] + + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['index_id']): + led_count = rpi_output['neopixel_count'] + led_brightness = rpi_output['neopixel_brightness'] + address = rpi_output['microcontroller_address'] + + neopixel_dirrect = rpi_output['output_type'] == 'neopixel_direct' + + self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, green, + blue, address, neopixel_dirrect, identifier) + + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/clear-gpio", methods=["POST"]) + @restricted_access + def clear_gpio_mode(self): + GPIO.cleanup() + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/update", methods=["POST"]) + @restricted_access + def update_ui_requested(self): + self.update_ui() + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/shell/", methods=["POST"]) + @restricted_access + def send_shell_command(self, identifier): + rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == identifier].pop() + + command = rpi_output['shell_script'] + self.shell_command(command) + return make_response('', 204) + + + @octoprint.plugin.BlueprintPlugin.route("/gcode/", methods=["POST"]) + @restricted_access + def requested_gcode_command(self, identifier): + rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == identifier].pop() + self.send_gcode_command(rpi_output['gcode']) + return make_response('', 204) + + + + + + """ + DEPRECATION + This API will be deprecated in a future version + """ + + # ~~ Blueprintplugin mixin + @octoprint.plugin.BlueprintPlugin.route("/setEnclosureTempHum", methods=["GET"]) + def set_enclosure_temp_humidity_old(self): + set_value = self.to_float(request.values["set_temperature"]) + index_id = self.to_int(request.values["index_id"]) + + for temp_hum_control in [item for item in self.rpi_outputs if item['index_id'] == index_id]: + temp_hum_control['temp_ctr_set_value'] = set_value + + self.handle_temp_hum_control() + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/clearGPIOMode", methods=["GET"]) + def clear_gpio_mode_old(self): + GPIO.cleanup() + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/updateUI", methods=["GET"]) + def update_ui_requested_old(self): + self.update_ui() + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/getOutputStatus", methods=["GET"]) + def get_output_status_old(self): + gpio_status = [] + for rpi_output in self.rpi_outputs: + if rpi_output['output_type'] == 'regular': + pin = self.to_int(rpi_output['gpio_pin']) + val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) + index = self.to_int(rpi_output['index_id']) + gpio_status.append(dict(index_id=index, status=val)) + return Response(json.dumps(gpio_status), mimetype='application/json') + + @octoprint.plugin.BlueprintPlugin.route("/setIO", methods=["GET"]) + def set_io_old(self): + index = request.values["index_id"] + value = True if request.values["status"] == 'true' else False + for rpi_output in self.rpi_outputs: + if self.to_int(index) == self.to_int(rpi_output['index_id']): + val = (not value) if rpi_output['active_low'] else value + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/sendShellCommand", methods=["GET"]) + def send_shell_command_old(self): + output_index = self.to_int(request.values["index_id"]) + + rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == output_index].pop() + + command = rpi_output['shell_script'] + self.shell_command(command) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setAutoStartUp", methods=["GET"]) + def set_auto_startup_old(self): + index = request.values["index_id"] + value = True if request.values["status"] == 'true' else False + + if not value: + suffix = 'auto_startup' + queue_id = '{0!s}_{1!s}'.format(index, suffix) + self.stop_queue_item(queue_id) + for output in self.rpi_outputs: + if self.to_int(index) == self.to_int(output['index_id']): + output['auto_startup'] = value + self._logger.info("Setting auto startup for output %s to : %s", index, value) + self._settings.set(["rpi_outputs"], self.rpi_outputs) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setAutoShutdown", methods=["GET"]) + def set_auto_shutdown_old(self): + index = request.values["index_id"] + value = True if request.values["status"] == 'true' else False + + if not value: + suffix = 'auto_shutdown' + queue_id = '{0!s}_{1!s}'.format(index, suffix) + self.stop_queue_item(queue_id) + + for output in self.rpi_outputs: + if self.to_int(index) == self.to_int(output['index_id']): + output['auto_shutdown'] = value + self._logger.info("Setting auto shutdown for output %s to : %s", index, value) + self._settings.set(["rpi_outputs"], self.rpi_outputs) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setFilamentSensor", methods=["GET"]) + def set_filament_sensor_old(self): + index = request.values["index_id"] + value = True if request.values["status"] == 'true' else False + for sensor in self.rpi_inputs: + if self.to_int(index) == self.to_int(sensor['index_id']): + sensor['filament_sensor_enabled'] = value + self._logger.info("Setting filament sensor for input %s to : %s", index, value) + self._settings.set(["rpi_inputs"], self.rpi_inputs) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setPWM", methods=["GET"]) + def set_pwm_old(self): + set_value = self.to_int(request.values["new_duty_cycle"]) + index_id = self.to_int(request.values["index_id"]) + for rpi_output in [item for item in self.rpi_outputs if item['index_id'] == index_id]: + rpi_output['duty_cycle'] = set_value + rpi_output['new_duty_cycle'] = "" + gpio = self.to_int(rpi_output['gpio_pin']) + self.write_pwm(gpio, set_value) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/sendGcodeCommand", methods=["GET"]) + def requested_gcode_command_old(self): + gpio_index = self.to_int(request.values["index_id"]) + rpi_output = [r_out for r_out in self.rpi_outputs if self.to_int(r_out['index_id']) == gpio_index].pop() + self.send_gcode_command(rpi_output['gcode']) + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setNeopixel", methods=["GET"]) + def set_neopixel_old(self): + """ set_neopixel method get request from octoprint and send the command to arduino or neopixel""" + gpio_index = self.to_int(request.values["index_id"]) + red = request.values["red"] + green = request.values["green"] + blue = request.values["blue"] + for rpi_output in self.rpi_outputs: + if gpio_index == self.to_int(rpi_output['index_id']): + led_count = rpi_output['neopixel_count'] + led_brightness = rpi_output['neopixel_brightness'] + address = rpi_output['microcontroller_address'] + + neopixel_dirrect = rpi_output['output_type'] == 'neopixel_direct' + + self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, green, + blue, address, neopixel_dirrect, gpio_index) + + return jsonify(success=True) + + @octoprint.plugin.BlueprintPlugin.route("/setLedstripColor", methods=["GET"]) + def set_ledstrip_color_old(self): + """ set_ledstrip_color method get request from octoprint and send the command to Open-Smart RGB LED Strip""" + gpio_index = self.to_int(request.values["index_id"]) + rgb = request.values["rgb"] + for rpi_output in self.rpi_outputs: + if gpio_index == self.to_int(rpi_output['index_id']): + self.ledstrip_set_rgb(rpi_output, rgb) + + return jsonify(success=True) + + # DEPREACTION END + + + + + + def send_neopixel_command(self, led_pin, led_count, led_brightness, red, green, blue, address, neopixel_dirrect, + index_id, queue_id=None): + """Send neopixel command + + Arguments: + led_pin {int} -- GPIO number + ledCount {int} -- number of LEDS + ledBrightness {int} -- brightness from 0 to 255 + red {int} -- red value from 0 to 255 + green {int} -- green value from 0 to 255 + blue {int} -- blue value from 0 to 255 + address {int} -- i2c address from microcontroller + """ + + try: + + for rpi_output in self.rpi_outputs: + if self.to_int(index_id) == self.to_int(rpi_output['index_id']): + rpi_output['neopixel_color'] = 'rgb({0!s},{1!s},{2!s})'.format(red, green, blue) + + if address == '': + address = 0 + + if neopixel_dirrect: + script = os.path.dirname(os.path.realpath(__file__)) + "/neopixel_direct.py " + else: + script = os.path.dirname(os.path.realpath(__file__)) + "/neopixel_indirect.py " + + if self._settings.get(["use_sudo"]): + sudo_str = "sudo " + else: + sudo_str = "" + + cmd = sudo_str + "python " + script + str(led_pin) + " " + str(led_count) + " " + str( + led_brightness) + " " + str(red) + " " + str(green) + " " + str(blue) + " " + + if neopixel_dirrect: + dma = self._settings.get(["neopixel_dma"]) or 10 + cmd = cmd + str(dma) + else: + cmd = cmd + str(address) + + if queue_id is not None: + self._logger.debug("running scheduled queue id %s", queue_id) + self._logger.debug("Sending neopixel cmd: %s", cmd) + Popen(cmd, shell=True) + if queue_id is not None: + self.stop_queue_item(queue_id) + except Exception as ex: + self.log_error(ex) + + def check_enclosure_temp(self): + try: + sensor_data = [] + for sensor in list(filter(lambda item: item['input_type'] == 'temperature_sensor', self.rpi_inputs)): + temp, hum = self.get_sensor_data(sensor) + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Sensor %s Temperature: %s humidity %s", sensor['label'], temp, hum) + if temp is not None and hum is not None: + sensor["temp_sensor_temp"] = temp + sensor["temp_sensor_humidity"] = hum + sensor_data.append(dict(index_id=sensor['index_id'], temperature=temp, humidity=hum)) + self.temperature_sensor_data = sensor_data + self.handle_temp_hum_control() + self.handle_temperature_events() + self.handle_pwm_linked_temperature() + self.update_ui() + except Exception as ex: + self.log_error(ex) + + def toggle_output(self, output_index, first_run=False): + for output in [item for item in self.rpi_outputs if item['index_id'] == output_index]: + gpio_pin = self.to_int(output['gpio_pin']) + index_id = self.to_int(output['index_id']) + + if output['output_type'] == 'regular': + if first_run: + current_value = False + else: + current_value = (not GPIO.input(gpio_pin)) if output['active_low'] else GPIO.input(gpio_pin) + + if current_value: + time_delay = self.to_int(output['toggle_timer_off']) + else: + time_delay = self.to_int(output['toggle_timer_on']) + + if not self.print_complete: + self.write_gpio(gpio_pin, not current_value) + thread = threading.Timer(time_delay, self.toggle_output, args=[index_id]) + thread.start() + else: + off_value = True if output['active_low'] else False + self.write_gpio(gpio_pin, off_value) + self.update_ui_outputs() + return + + if output['output_type'] == 'pwm': + for pwm in self.pwm_instances: + if gpio_pin in pwm: + if first_run: + current_pwm_value = 0 + else: + if 'duty_cycle' in pwm: + current_pwm_value = pwm['duty_cycle'] + current_pwm_value = self.to_int(current_pwm_value) + else: + current_pwm_value = 0 + + if not current_pwm_value == 0: + time_delay = self.to_int(output['toggle_timer_off']) + write_value = 0 + else: + time_delay = self.to_int(output['toggle_timer_on']) + write_value = self.to_int(output['default_duty_cycle']) + + if not self.print_complete: + self.write_pwm(gpio_pin, write_value) + thread = threading.Timer(time_delay, self.toggle_output, args=[index_id]) + thread.start() + else: + self.write_pwm(self.to_int(output['gpio_pin']), 0) + self.update_ui_outputs() + return + + def update_ui(self): + self.update_ui_outputs() + self.update_ui_current_temperature() + self.update_ui_set_temperature() + self.update_ui_inputs() + + def update_ui_current_temperature(self): + self._plugin_manager.send_plugin_message(self._identifier, dict(sensor_data=self.temperature_sensor_data)) + + def update_ui_set_temperature(self): + result = [] + for temp_crt_output in list(filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): + set_temperature = self.to_float(temp_crt_output['temp_ctr_set_value']) + result.append(dict(index_id=temp_crt_output['index_id'], set_temperature=set_temperature)) + result.append(set_temperature) + self._plugin_manager.send_plugin_message(self._identifier, dict(set_temperature=result)) + + def stop_queue_item(self, queue_id): + old_list = self.event_queue + self._logger.debug("Stopping queue id %s...", queue_id) + for task in self.event_queue: + self._logger.debug("Queue id found...") + if task['queue_id'] == queue_id: + task['thread'].cancel() + self.event_queue.remove(task) + self._logger.debug("Queue id stopped and removed from list...") + self._logger.debug("Old queue list: %s", old_list) + self._logger.debug("New queue list: %s", self.event_queue) + + def update_ui_outputs(self): + try: + regular_status = [] + pwm_status = [] + neopixel_status = [] + temp_control_status = [] + for output in self.rpi_outputs: + index = self.to_int(output['index_id']) + pin = self.to_int(output['gpio_pin']) + startup = output['auto_startup'] + shutdown = output['auto_shutdown'] + + if output['output_type'] == 'regular': + val = GPIO.input(pin) if not output['active_low'] else (not GPIO.input(pin)) + regular_status.append( + dict(index_id=index, status=val, auto_startup=startup, auto_shutdown=shutdown)) + if output['output_type'] == 'temp_hum_control': + val = GPIO.input(pin) if not output['active_low'] else (not GPIO.input(pin)) + temp_control_status.append( + dict(index_id=index, status=val, auto_startup=startup, auto_shutdown=shutdown)) + if output['output_type'] == 'neopixel_indirect' or output['output_type'] == 'neopixel_direct': + val = output['neopixel_color'] + neopixel_status.append( + dict(index_id=index, color=val, auto_startup=startup, auto_shutdown=shutdown)) + if output['output_type'] == 'pwm': + for pwm in self.pwm_instances: + if pin in pwm: + if 'duty_cycle' in pwm: + pwm_val = pwm['duty_cycle'] + val = self.to_int(pwm_val) + else: + val = 0 + pwm_status.append( + dict(index_id=index, pwm_value=val, auto_startup=startup, auto_shutdown=shutdown)) + self._plugin_manager.send_plugin_message(self._identifier, + dict(rpi_output_regular=regular_status, rpi_output_pwm=pwm_status, + rpi_output_neopixel=neopixel_status, + rpi_output_temp_hum_ctrl=temp_control_status)) + except Exception as ex: + self.log_error(ex) + + def update_ui_inputs(self): + try: + sensor_status = [] + for sensor in self.rpi_inputs: + if sensor['input_type'] == 'gpio' and sensor['action_type'] == 'printer_control' and sensor[ + 'printer_action'] == 'filament': + index = self.to_int(sensor['index_id']) + value = sensor['filament_sensor_enabled'] + sensor_status.append(dict(index_id=index, filament_sensor_enabled=value)) + self._plugin_manager.send_plugin_message(self._identifier, dict(filament_sensor_status=sensor_status)) + except Exception as ex: + self.log_error(ex) + + def get_sensor_data(self, sensor): + try: + if self.development_mode: + temp, hum = self.read_dummy_temp() + else: + if sensor['temp_sensor_type'] in ["11", "22", "2302"]: + temp, hum = self.read_dht_temp(sensor['temp_sensor_type'], sensor['gpio_pin']) + elif sensor['temp_sensor_type'] == "18b20": + temp = self.read_18b20_temp(sensor['ds18b20_serial']) + hum = 0 + elif sensor['temp_sensor_type'] == "bme280": + temp, hum = self.read_bme280_temp(sensor['temp_sensor_address']) + elif sensor['temp_sensor_type'] == "am2320": + temp, hum = self.read_am2320_temp() # sensor has fixed address + elif sensor['temp_sensor_type'] == "si7021": + temp, hum = self.read_si7021_temp(sensor['temp_sensor_address']) + elif sensor['temp_sensor_type'] == "tmp102": + temp = self.read_tmp102_temp(sensor['temp_sensor_address']) + hum = 0 + elif sensor['temp_sensor_type'] == "max31855": + temp = self.read_max31855_temp(sensor['temp_sensor_address']) + hum = 0 + elif sensor['temp_sensor_type'] == "mcp9808": + temp = self.read_mcp_temp(sensor['temp_sensor_address']) + hum = 0 + else: + self._logger.info("temp_sensor_type no match") + temp = None + hum = None + if temp != -1 and hum != -1: + temp = round(self.to_float(temp), 1) if not sensor['use_fahrenheit'] else round( + self.to_float(temp) * 1.8 + 32, 1) + hum = round(self.to_float(hum), 1) + return temp, hum + return None, None + except Exception as ex: + self.log_error(ex) + + def handle_temperature_events(self): + for temperature_alarm in [item for item in self.rpi_outputs if item['output_type'] == 'temperature_alarm']: + set_temperature = self.to_float(temperature_alarm['alarm_set_temp']) + if int(set_temperature) is 0: + continue + linked_data = [item for item in self.temperature_sensor_data if + item['index_id'] == temperature_alarm['linked_temp_sensor']].pop() + sensor_temperature = self.to_float(linked_data['temperature']) + if set_temperature < sensor_temperature: + for rpi_controlled_output in self.rpi_outputs: + if self.to_int(temperature_alarm['controlled_io']) == self.to_int( + rpi_controlled_output['index_id']): + val = GPIO.LOW if rpi_controlled_output['active_low'] else GPIO.HIGH + self.write_gpio(self.to_int(rpi_controlled_output['gpio_pin']), val) + for notification in self.notifications: + if notification['temperatureAction']: + msg = ("Temperature action: enclosure temperature exceed " + temperature_alarm[ + 'alarm_set_temp']) + self.send_notification(msg) + + def read_dummy_temp(self): + current_value = self.dummy_value + if current_value > 40 or current_value < 30: + self.dummy_delta = - self.dummy_delta + + return_value = current_value + self.dummy_delta + + self.dummy_value = return_value + + return return_value, return_value + + def read_mcp_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/mcp9808.py" + args = ["python", script, str(address)] + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature MCP9808 cmd: %s", " ".join(args)) + proc = Popen(args, stdout=PIPE) + stdout, _ = proc.communicate() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("MCP9808 result: %s", stdout) + return self.to_float(stdout.strip()) + except Exception as ex: + self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") + self.log_error(ex) + return 0 + + def read_dht_temp(self, sensor, pin): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/getDHTTemp.py " + if self._settings.get(["use_sudo"]): + sudo_str = "sudo " + else: + sudo_str = "" + cmd = sudo_str + "python " + script + str(sensor) + " " + str(pin) + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature dht cmd: %s", cmd) + stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Dht result: %s", stdout) + temp, hum = stdout.split("|") + return (self.to_float(temp.strip()), self.to_float(hum.strip())) + except Exception as ex: + self._logger.info( + "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") + self.log_error(ex) + return (0, 0) + + def read_bme280_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/BME280.py " + if self._settings.get(["use_sudo"]): + sudo_str = "sudo " + else: + sudo_str = "" + cmd = sudo_str + "python " + script + str(address) + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature BME280 cmd: %s", cmd) + stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("BME280 result: %s", stdout) + temp, hum = stdout.split("|") + return (self.to_float(temp.strip()), self.to_float(hum.strip())) + except Exception as ex: + self._logger.info( + "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") + self.log_error(ex) + return (0, 0) + + def read_am2320_temp(self): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/AM2320.py " + if self._settings.get(["use_sudo"]): + sudo_str = "sudo " + else: + sudo_str = "" + cmd = sudo_str + "python " + script # sensor has fixed address 0x5C + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature AM2320 cmd: %s", cmd) + stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("AM2320 result: %s", stdout) + temp, hum = stdout.split("|") + return (self.to_float(temp.strip()), self.to_float(hum.strip())) + except Exception as ex: + self._logger.info( + "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") + self.log_error(ex) + return (0, 0) + + def read_si7021_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/SI7021.py " + if self._settings.get(["use_sudo"]): + sudo_str = "sudo " + else: + sudo_str = "" + cmd = sudo_str + "python " + script + str(address) + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature SI7021 cmd: %s", cmd) + stdout = (Popen(cmd, shell=True, stdout=PIPE).stdout).read() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("SI7021 result: %s", stdout) + temp, hum = stdout.split("|") + return (self.to_float(temp.strip()), self.to_float(hum.strip())) + except Exception as ex: + self._logger.info( + "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") + self.log_error(ex) + return (0, 0) + + def read_18b20_temp(self, serial_number): + os.system('modprobe w1-gpio') + os.system('modprobe w1-therm') + + lines = self.read_raw_18b20_temp(serial_number) + while lines[0].strip()[-3:] != 'YES': + time.sleep(0.2) + lines = self.read_raw_18b20_temp(serial_number) + equals_pos = lines[1].find('t=') + if equals_pos != -1: + temp_string = lines[1][equals_pos + 2:] + temp_c = float(temp_string) / 1000. + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("DS18B20 result: %s", temp_c) + return '{0:0.1f}'.format(temp_c) + return 0 + + def read_raw_18b20_temp(self, serial_number): + base_dir = '/sys/bus/w1/devices/' + device_folder = glob.glob(base_dir + str(serial_number) + '*')[0] + device_file = device_folder + '/w1_slave' + device_file_result = open(device_file, 'r') + lines = device_file_result.readlines() + device_file_result.close() + return lines + + def read_tmp102_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/tmp102.py" + args = ["python", script, str(address)] + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature TMP102 cmd: %s", " ".join(args)) + proc = Popen(args, stdout=PIPE) + stdout, _ = proc.communicate() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("TMP102 result: %s", stdout) + return self.to_float(stdout.strip()) + except Exception as ex: + self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") + self.log_error(ex) + return 0 + + def read_max31855_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/max31855.py" + args = ["python", script, str(address)] + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature MAX31855 cmd: %s", " ".join(args)) + proc = Popen(args, stdout=PIPE) + stdout, _ = proc.communicate() + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("MAX31855 result: %s", stdout) + return self.to_float(stdout.strip()) + except Exception as ex: + self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") + self.log_error(ex) + return 0 + + def handle_pwm_linked_temperature(self): + try: + for pwm_output in list(filter(lambda item: item['output_type'] == 'pwm' and item['pwm_temperature_linked'], + self.rpi_outputs)): + gpio_pin = self.to_int(pwm_output['gpio_pin']) + if self._printer.is_printing(): + index_id = self.to_int(pwm_output['index_id']) + linked_id = self.to_int(pwm_output['linked_temp_sensor']) + linked_data = self.get_linked_temp_sensor_data(linked_id) + current_temp = self.to_float(linked_data['temperature']) + + duty_a = self.to_float(pwm_output['duty_a']) + duty_b = self.to_float(pwm_output['duty_b']) + temp_a = self.to_float(pwm_output['temperature_a']) + temp_b = self.to_float(pwm_output['temperature_b']) + + try: + calculated_duty = ((current_temp - temp_a) * (duty_b - duty_a) / (temp_b - temp_a)) + duty_a + + if current_temp < temp_a: + calculated_duty = 0 + except: + calculated_duty = 0 + + self._logger.debug("Calculated duty for PWM %s is %s", index_id, calculated_duty) + elif self.print_complete: + calculated_duty = self.to_int(pwm_output['duty_cycle']) + else: + calculated_duty = 0 + + self.write_pwm(gpio_pin, self.constrain(calculated_duty, 0, 100)) + + except Exception as ex: + self.log_error(ex) + + def get_linked_temp_sensor_data(self, linked_id): + try: + linked_data = [data for data in self.temperature_sensor_data if data['index_id'] == linked_id].pop() + return linked_data + except: + self._logger.warn("No linked temperature sensor found for %s", linked_id) + return None + + def handle_temp_hum_control(self): + try: + for temp_hum_control in list( + filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): + + set_temperature = self.to_float(temp_hum_control['temp_ctr_set_value']) + temp_deadband = self.to_float(temp_hum_control['temp_ctr_deadband']) + max_temp = self.to_float(temp_hum_control['temp_ctr_max_temp']) + + linked_id = temp_hum_control['linked_temp_sensor'] + + previous_status = list(filter(lambda item: item['index_id'] == temp_hum_control['index_id'], + self.temp_hum_control_status)).pop()['status'] + + if set_temperature == 0: + current_status = False + else: + linked_data = self.get_linked_temp_sensor_data(linked_id) + + control_type = str(temp_hum_control['temp_ctr_type']) + + if control_type == 'dehumidifier': + current_value = self.to_float(linked_data['humidity']) + temp_deadband = 0 + else: + current_value = self.to_float(linked_data['temperature']) + + if control_type == 'cooler' or control_type == 'dehumidifier': + if current_value <= set_temperature and current_value >= (set_temperature - temp_deadband): + current_status = previous_status + elif current_value < set_temperature: + current_status = False + else: + current_status = True + else: + if current_value <= set_temperature and current_value >= (set_temperature - temp_deadband): + current_status = previous_status + elif current_value > set_temperature: + current_status = False + else: + current_status = True + + if control_type == 'heater' and max_temp > 0.0 and max_temp < current_value: + self._logger.debug("Maximum temperature reached for temperature control %s", + temp_hum_control['index_id']) + temp_hum_control['temp_ctr_set_value'] = 0 + current_status = False + + if current_status != previous_status: + if current_status: + self._logger.info("Turning gpio to control temperature on.") + val = False if temp_hum_control['active_low'] else True + self.write_gpio(self.to_int(temp_hum_control['gpio_pin']), val) + else: + index_id = temp_hum_control['index_id'] + if index_id in self.waiting_temperature: + self.waiting_temperature.remove(index_id) + + if not self.waiting_temperature and self._printer.is_paused(): + self._printer.resume_print() + + self._logger.info("Turning gpio to control temperature off.") + val = True if temp_hum_control['active_low'] else False + self.write_gpio(self.to_int(temp_hum_control['gpio_pin']), val) + for control_status in self.temp_hum_control_status: + if control_status['index_id'] == temp_hum_control['index_id']: + control_status['status'] = current_status + except Exception as ex: + self.log_error(ex) + + def log_error(self, ex): + template = "An exception of type {0} occurred on {1}. Arguments:\n{2!r}" + message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, ex.args) + self._logger.warn(message) + + def setup_gpio(self): + try: + current_mode = GPIO.getmode() + set_mode = GPIO.BOARD if self._settings.get(["use_board_pin_number"]) else GPIO.BCM + if current_mode is None: + outputs = list(filter( + lambda item: item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ + 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct', + self.rpi_outputs)) + inputs = list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)) + gpios = outputs + inputs + if gpios: + GPIO.setmode(set_mode) + tempstr = "BOARD" if set_mode == GPIO.BOARD else "BCM" + self._logger.info("Setting GPIO mode to %s", tempstr) + elif current_mode != set_mode: + GPIO.setmode(current_mode) + tempstr = "BOARD" if current_mode == GPIO.BOARD else "BCM" + self._settings.set(["use_board_pin_number"], True if current_mode == GPIO.BOARD else False) + warn_msg = "GPIO mode was configured before, GPIO mode will be forced to use: " + tempstr + " as pin numbers. Please update GPIO accordingly!" + self._logger.info(warn_msg) + self._plugin_manager.send_plugin_message(self._identifier, + dict(is_msg=True, msg=warn_msg, msg_type="error")) + GPIO.setwarnings(False) + except Exception as ex: + self.log_error(ex) + + def clear_gpio(self): + try: + + for gpio_out in list(filter( + lambda item: item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ + 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct', + self.rpi_outputs)): + gpio_pin = self.to_int(gpio_out['gpio_pin']) + if gpio_pin not in self.rpi_outputs_not_changed: + GPIO.cleanup(gpio_pin) + + for gpio_in in list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)): + try: + GPIO.remove_event_detect(self.to_int(gpio_in['gpio_pin'])) + except: + pass + GPIO.cleanup(self.to_int(gpio_in['gpio_pin'])) + except Exception as ex: + self.log_error(ex) + + def clear_channel(self, channel): + try: + GPIO.cleanup(self.to_int(channel)) + self._logger.debug("Clearing channel %s", channel) + except Exception as ex: + self.log_error(ex) + + def generate_temp_hum_control_status(self): + status = [] + for temp_hum_control in list(filter(lambda item: item['output_type'] == 'temp_hum_control', self.rpi_outputs)): + status.append(dict(index_id=temp_hum_control['index_id'], status=False)) + self.temp_hum_control_status = status + + def configure_gpio(self): + try: + + for gpio_out in list( + filter(lambda item: item['output_type'] == 'regular' or item['output_type'] == 'temp_hum_control', + self.rpi_outputs)): + initial_value = GPIO.HIGH if gpio_out['active_low'] else GPIO.LOW + pin = self.to_int(gpio_out['gpio_pin']) + if pin not in self.rpi_outputs_not_changed: + self._logger.info("Setting GPIO pin %s as OUTPUT with initial value: %s", pin, initial_value) + GPIO.setup(pin, GPIO.OUT, initial=initial_value) + for gpio_out_pwm in list(filter(lambda item: item['output_type'] == 'pwm', self.rpi_outputs)): + pin = self.to_int(gpio_out_pwm['gpio_pin']) + self._logger.info("Setting GPIO pin %s as PWM", pin) + for pwm in (pwm_dict for pwm_dict in self.pwm_instances if pin in pwm_dict): + self.pwm_instances.remove(pwm) + self.clear_channel(pin) + GPIO.setup(pin, GPIO.OUT) + pwm_instance = GPIO.PWM(pin, self.to_int(gpio_out_pwm['pwm_frequency'])) + self._logger.info("starting PWM on pin %s", pin) + pwm_instance.start(0) + self.pwm_instances.append({pin: pwm_instance}) + for gpio_out_neopixel in list( + filter(lambda item: item['output_type'] == 'neopixel_direct', self.rpi_outputs)): + pin = self.to_int(gpio_out_neopixel['gpio_pin']) + self.clear_channel(pin) + + for rpi_input in list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)): + gpio_pin = self.to_int(rpi_input['gpio_pin']) + pull_resistor = GPIO.PUD_UP if rpi_input['input_pull_resistor'] == 'input_pull_up' else GPIO.PUD_DOWN + GPIO.setup(gpio_pin, GPIO.IN, pull_resistor) + edge = GPIO.RISING if rpi_input['edge'] == 'rise' else GPIO.FALLING + + inputs_same_gpio = list( + [r_inp for r_inp in self.rpi_inputs if self.to_int(r_inp['gpio_pin']) == gpio_pin]) + + if len(inputs_same_gpio) > 1: + GPIO.remove_event_detect(gpio_pin) + for other_input in inputs_same_gpio: + if other_input['edge'] is not edge: + edge = GPIO.BOTH + + if rpi_input['action_type'] == 'output_control': + self._logger.info("Adding GPIO event detect on pin %s with edge: %s", gpio_pin, edge) + GPIO.add_event_detect(gpio_pin, edge, callback=self.handle_gpio_control, bouncetime=200) + if (rpi_input['action_type'] == 'printer_control' and rpi_input['printer_action'] != 'filament'): + GPIO.add_event_detect(gpio_pin, edge, callback=self.handle_printer_action, bouncetime=200) + self._logger.info("Adding PRINTER CONTROL event detect on pin %s with edge: %s", gpio_pin, edge) + except Exception as ex: + self.log_error(ex) + + def handle_filamment_detection(self, channel): + try: + for filament_sensor in list(filter( + lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ + 'printer_action'] == 'filament' and self.to_int(item['gpio_pin']) == self.to_int(channel), + self.rpi_inputs)): + if ((filament_sensor['edge'] == 'fall') ^ (GPIO.input(self.to_int(filament_sensor['gpio_pin']))) and + filament_sensor['filament_sensor_enabled']): + last_detected_time = list(filter(lambda item: item['index_id'] == filament_sensor['index_id'], + self.last_filament_end_detected)).pop()['time'] + time_now = time.time() + time_difference = self.to_int(time_now - last_detected_time) + time_out_value = self.to_int(filament_sensor['filament_sensor_timeout']) + if time_difference > time_out_value: + self._logger.info("Detected end of filament.") + for item in self.last_filament_end_detected: + if item['index_id'] == filament_sensor['index_id']: + item['time'] = time_now + for line in self._settings.get(["filament_sensor_gcode"]).split('\n'): + if line: + self._printer.commands(line.strip().upper()) + self._logger.info("Sending GCODE command: %s", line.strip().upper()) + time.sleep(0.2) + for notification in self.notifications: + if notification['filamentChange']: + msg = "Filament change action caused by sensor: " + str(filament_sensor['label']) + self.send_notification(msg) + else: + self._logger.info("Prevented end of filament detection, filament sensor timeout not elapsed.") + except Exception as ex: + self.log_error(ex) + + def start_filament_detection(self): + self.stop_filament_detection() + try: + for filament_sensor in list(filter( + lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ + 'printer_action'] == 'filament', self.rpi_inputs)): + edge = GPIO.RISING if filament_sensor['edge'] == 'rise' else GPIO.FALLING + if GPIO.input(self.to_int(filament_sensor['gpio_pin'])) == (edge == GPIO.RISING): + self._printer.pause_print() + self._logger.info("Started printing with no filament.") + else: + self.last_filament_end_detected.append(dict(index_id=filament_sensor['index_id'], time=0)) + self._logger.info("Adding GPIO event detect on pin %s with edge: %s", filament_sensor['gpio_pin'], + edge) + GPIO.add_event_detect(self.to_int(filament_sensor['gpio_pin']), edge, + callback=self.handle_filamment_detection, bouncetime=200) + except Exception as ex: + self.log_error(ex) + + def stop_filament_detection(self): + try: + self.last_filament_end_detected = [] + for filament_sensor in list(filter( + lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'printer_control' and item[ + 'printer_action'] == 'filament', self.rpi_inputs)): + GPIO.remove_event_detect(self.to_int(filament_sensor['gpio_pin'])) + except Exception as ex: + self.log_error(ex) + + def cancel_all_events_on_queue(self): + for task in self.event_queue: + try: + task['thread'].cancel() + except: + self._logger.warn("Failed to stop task %s.", task) + pass + + def handle_initial_gpio_control(self): + try: + for rpi_input in list( + filter(lambda item: item['input_type'] == 'gpio' and item['action_type'] == 'output_control', + self.rpi_inputs)): + gpio_pin = self.to_int(rpi_input['gpio_pin']) + controlled_io = self.to_int(rpi_input['controlled_io']) + if (rpi_input['edge'] == 'fall') ^ GPIO.input(gpio_pin): + rpi_output = [r_out for r_out in self.rpi_outputs if + self.to_int(r_out['index_id']) == controlled_io].pop() + if rpi_output['output_type'] == 'regular': + val = GPIO.LOW if rpi_input['controlled_io_set_value'] == 'low' else GPIO.HIGH + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) + except Exception as ex: + self.log_error(ex) + pass + + def shell_command(self, command): + try: + stdout = (Popen(command, shell=True, stdout=PIPE).stdout).read() + self._plugin_manager.send_plugin_message(self._identifier, + dict(is_msg=True, msg=stdout, msg_type="success")) + except Exception as ex: + self.log_error(ex) + self._plugin_manager.send_plugin_message(self._identifier, + dict(is_msg=True, msg="Could not execute shell script", msg_type="error")) + + def handle_gpio_control(self, channel): + + try: + self._logger.debug("GPIO event triggered on channel %s", channel) + for rpi_input in list( + filter(lambda item: self.to_int(item['gpio_pin']) == self.to_int(channel), self.rpi_inputs)): + gpio_pin = self.to_int(rpi_input['gpio_pin']) + controlled_io = self.to_int(rpi_input['controlled_io']) + if (rpi_input['edge'] == 'fall') ^ GPIO.input(gpio_pin): + rpi_output = [r_out for r_out in self.rpi_outputs if + self.to_int(r_out['index_id']) == controlled_io].pop() + if rpi_output['output_type'] == 'regular': + if rpi_input['controlled_io_set_value'] == 'toggle': + val = GPIO.LOW if GPIO.input( + self.to_int(rpi_output['gpio_pin'])) == GPIO.HIGH else GPIO.HIGH + else: + val = GPIO.LOW if rpi_input['controlled_io_set_value'] == 'low' else GPIO.HIGH + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) + for notification in self.notifications: + if notification['gpioAction']: + msg = "GPIO control action caused by input " + str( + rpi_input['label']) + ". Setting GPIO" + str( + rpi_input['controlled_io']) + " to: " + str(rpi_input['controlled_io_set_value']) + self.send_notification(msg) + if rpi_output['output_type'] == 'gcode_output': + self.send_gcode_command(rpi_output['gcode']) + for notification in self.notifications: + if notification['gpioAction']: + msg = "GPIO control action caused by input " + str( + rpi_input['label']) + ". Sending GCODE command" + self.send_notification(msg) + if rpi_output['output_type'] == 'shell_output': + command = rpi_output['shell_script'] + self.shell_command(command) + except Exception as ex: + self.log_error(ex) + pass + + def send_gcode_command(self, command): + for line in command.split('\n'): + if line: + self._printer.commands(line.strip().upper()) + self._logger.info("Sending GCODE command: %s", line.strip().upper()) + time.sleep(0.2) + + def handle_printer_action(self, channel): + try: + for rpi_input in self.rpi_inputs: + if (channel == self.to_int(rpi_input['gpio_pin']) and rpi_input[ + 'action_type'] == 'printer_control' and ( + (rpi_input['edge'] == 'fall') ^ GPIO.input(self.to_int(rpi_input['gpio_pin'])))): + if rpi_input['printer_action'] == 'resume': + self._logger.info("Printer action resume.") + self._printer.resume_print() + elif rpi_input['printer_action'] == 'pause': + self._logger.info("Printer action pause.") + self._printer.pause_print() + elif rpi_input['printer_action'] == 'cancel': + self._logger.info("Printer action cancel.") + self._printer.cancel_print() + elif rpi_input['printer_action'] == 'toggle': + self._logger.info("Printer action toggle.") + if self._printer.is_operational(): + self._printer.toggle_pause_print() + else: + self._printer.connect() + elif rpi_input['printer_action'] == 'start': + self._logger.info("Printer action start.") + self._printer.start_print() + elif rpi_input['printer_action'] == 'toggle_job': + self._logger.info("Printer action toggle_job.") + if self._printer.is_operational(): + if self._printer.is_printing(): + self._printer.cancel_print() + elif self._printer.is_ready(): + self._printer.start_print() + else: + self._printer.connect() + elif rpi_input['printer_action'] == 'stop_temp_hum_control': + self._logger.info("Printer action stopping temperature control.") + for rpi_output in self.rpi_outputs: + if rpi_output['auto_shutdown'] and rpi_output['output_type'] == 'temp_hum_control': + rpi_output['temp_ctr_set_value'] = 0 + self.handle_temp_hum_control() + for notification in self.notifications: + if notification['printer_action']: + msg = "Printer action: " + rpi_input['printer_action'] + " caused by input: " + str( + rpi_input['label']) + self.send_notification(msg) + except Exception as ex: + self.log_error(ex) + pass + + def write_gpio(self, gpio, value, queue_id=None): + try: + GPIO.output(gpio, value) + if queue_id is not None: + self._logger.debug("Running scheduled queue id %s", queue_id) + self._logger.debug("Writing on GPIO: %s value %s", gpio, value) + self.update_ui() + if queue_id is not None: + self.stop_queue_item(queue_id) + except Exception as ex: + template = "An exception of type {0} occurred on {1} when writing on pin {2}. Arguments:\n{3!r}" + message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, gpio, ex.args) + self._logger.warn(message) + pass + + def write_pwm(self, gpio, pwm_value, queue_id=None): + try: + if queue_id is not None: + self._logger.debug("running scheduled queue id %s", queue_id) + for pwm in self.pwm_instances: + if gpio in pwm: + pwm_object = pwm[gpio] + old_pwm_value = pwm['duty_cycle'] if 'duty_cycle' in pwm else -1 + if not self.to_int(old_pwm_value) == self.to_int(pwm_value): + pwm['duty_cycle'] = pwm_value + pwm_object.start(pwm_value) #should be changed back to pwm_object.ChangeDutyCycle() but this + # was causing errors. + self._logger.debug("Writing PWM on gpio: %s value %s", gpio, pwm_value) + self.update_ui() + if queue_id is not None: + self.stop_queue_item(queue_id) + break + except Exception as ex: + self.log_error(ex) + pass + + def get_output_list(self): + result = [] + for rpi_output in self.rpi_outputs: + if rpi_output['output_type'] == 'regular': + result.append(self.to_int(rpi_output['gpio_pin'])) + return result + + def send_notification(self, message): + try: + provider = self._settings.get(["notification_provider"]) + if provider == 'ifttt': + event = self._settings.get(["notification_event_name"]) + api_key = self._settings.get(["notification_api_key"]) + self._logger.debug("Sending notification to: %s with msg: %s with key: %s", provider, message, + api_key) + try: + res = self.ifttt_notification(message, event, api_key) + except requests.exceptions.ConnectionError: + self._logger.info("Error: Could not connect to IFTTT") + except requests.exceptions.HTTPError: + self._logger.info("Error: Received invalid response") + except requests.exceptions.Timeout: + self._logger.info("Error: Request timed out") + except requests.exceptions.TooManyRedirects: + self._logger.info("Error: Too many redirects") + except requests.exceptions.RequestException as reqe: + self._logger.info("Error: {e}".format(e=reqe)) + if res.status_code != requests.codes['ok']: + try: + j = res.json() + except ValueError: + self._logger.info('Error: Could not parse server response. Event not sent') + for err in j['errors']: + self._logger.info('Error: {}'.format(err['message'])) + except Exception as ex: + self.log_error(ex) + pass + + def ifttt_notification(self, message, event, api_key): + url = "https://maker.ifttt.com/trigger/{e}/with/key/{k}/".format(e=event, k=api_key) + payload = {'value1': message} + return requests.post(url, data=payload) + + # ~~ EventPlugin mixin + def on_event(self, event, payload): + if event == Events.CONNECTED: + self.update_ui() + + if event == Events.CLIENT_OPENED: + self.update_ui() + + if event == Events.PRINT_RESUMED: + self.start_filament_detection() + + if event == Events.PRINT_STARTED: + self.print_complete = False + self.cancel_all_events_on_queue() + self.event_queue = [] + self.start_filament_detection() + for rpi_output in self.rpi_outputs: + if rpi_output['auto_startup']: + delay_seconds = self.get_startup_delay_from_output(rpi_output) + self.schedule_auto_startup_outputs(rpi_output, delay_seconds) + if rpi_output['toggle_timer']: + if rpi_output['output_type'] == 'regular' or rpi_output['output_type'] == 'pwm': + self.toggle_output(rpi_output['index_id'], True) + if self.is_hour(rpi_output['shutdown_time']): + shutdown_delay_seconds = self.get_shutdown_delay_from_output(rpi_output) + self.schedule_auto_shutdown_outputs(rpi_output, shutdown_delay_seconds) + self.run_tasks() + self.update_ui() + + elif event == Events.PRINT_DONE: + self.stop_filament_detection() + self.print_complete = True + for rpi_output in self.rpi_outputs: + shutdown_time = rpi_output['shutdown_time'] + if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: + rpi_output['duty_cycle'] = rpi_output['default_duty_cycle'] + if rpi_output['auto_shutdown'] and not self.is_hour(shutdown_time): + delay_seconds = self.to_float(shutdown_time) + self.schedule_auto_shutdown_outputs(rpi_output, delay_seconds) + self.run_tasks() + self.update_ui() + + elif event in (Events.PRINT_CANCELLED, Events.PRINT_FAILED): + self.stop_filament_detection() + self.cancel_all_events_on_queue() + self.event_queue = [] + for rpi_output in self.rpi_outputs: + if rpi_output['shutdown_on_failed']: + shutdown_time = rpi_output['shutdown_time'] + if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: + rpi_output['duty_cycle'] = rpi_output['default_duty_cycle'] + if rpi_output['auto_shutdown'] and not self.is_hour(shutdown_time): + delay_seconds = self.to_float(shutdown_time) + self.schedule_auto_shutdown_outputs(rpi_output, delay_seconds) + if rpi_output['output_type'] == 'temp_hum_control': + rpi_output['temp_ctr_set_value'] = 0 + self.run_tasks() + + if event == Events.PRINT_DONE: + for notification in self.notifications: + if notification['printFinish']: + file_name = os.path.basename(payload["file"]) + elapsed_time_in_seconds = payload["time"] + elapsed_time = octoprint.util.get_formatted_timedelta(timedelta(seconds=elapsed_time_in_seconds)) + msg = "Print job finished: " + file_name + "finished printing in " + file_name, elapsed_time + self.send_notification(msg) + + def run_tasks(self): + for task in self.event_queue: + if not task['thread'].is_alive(): + task['thread'].start() + + def schedule_auto_shutdown_outputs(self, rpi_output, shutdown_delay_seconds): + sufix = 'auto_shutdown' + if rpi_output['output_type'] == 'regular': + value = True if rpi_output['active_low'] else False + self.add_regular_output_to_queue(shutdown_delay_seconds, rpi_output, value, sufix) + if rpi_output['output_type'] == 'ledstrip': + self.ledstrip_set_rgb(rpi_output) + if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: + value = 0 + self.add_pwm_output_to_queue(shutdown_delay_seconds, rpi_output, value, sufix) + if rpi_output['output_type'] == 'pwm' and rpi_output['pwm_temperature_linked']: + self.schedule_pwm_duty_on_queue(shutdown_delay_seconds, rpi_output, 0, sufix) + if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): + self.add_neopixel_output_to_queue(rpi_output, shutdown_delay_seconds, 0, 0, 0, sufix) + if rpi_output['output_type'] == 'temp_hum_control': + value = 0 + self.add_temperature_output_temperature_queue(shutdown_delay_seconds, rpi_output, value, sufix) + self._logger.debug("Events scheduled to run %s", self.event_queue) + + def ledstrip_set_rgb(self, rpi_output, rgb=None): + clk = rpi_output["ledstrip_gpio_clk"] + data = rpi_output["ledstrip_gpio_dat"] + if clk is not None and data is not None: + ledstrip = LEDStrip(self.to_int(clk), self.to_int(data)) + if rgb is None: + red, green, blue = self.get_color_from_rgb(rpi_output['default_ledstrip_color']) + else: + red, green, blue = self.get_color_from_rgb(rgb) + + self._logger.info("LEDSTRIP set rgb color: %s, %s, %s", red, green, blue) + ledstrip.setcolourrgb(self.to_int(red), self.to_int(green), self.to_int(blue)) + + def start_outpus_with_server(self): + for rpi_output in self.rpi_outputs: + if rpi_output['startup_with_server']: + gpio = self.to_int(rpi_output['gpio_pin']) + if rpi_output['output_type'] == 'regular': + value = False if rpi_output['active_low'] else True + self.write_gpio(gpio, value) + if rpi_output['output_type'] == 'ledstrip': + self.ledstrip_set_rgb(rpi_output) + if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: + value = self.to_int(rpi_output['default_duty_cycle']) + self.write_pwm(gpio, value) + if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): + red, green, blue = self.get_color_from_rgb(rpi_output['default_neopixel_color']) + led_count = rpi_output['neopixel_count'] + led_brightness = rpi_output['neopixel_brightness'] + address = rpi_output['microcontroller_address'] + index_id = self.to_int(rpi_output['index_id']) + neopixel_direct = rpi_output['output_type'] == 'neopixel_direct' + self.send_neopixel_command(self.to_int(rpi_output['gpio_pin']), led_count, led_brightness, red, + green, blue, address, neopixel_direct, index_id) + if rpi_output['output_type'] == 'temp_hum_control': + rpi_output['temp_ctr_set_value'] = rpi_output['temp_ctr_default_value'] + + def schedule_auto_startup_outputs(self, rpi_output, delay_seconds): + sufix = 'auto_startup' + if rpi_output['output_type'] == 'regular': + value = False if rpi_output['active_low'] else True + self.add_regular_output_to_queue(delay_seconds, rpi_output, value, sufix) + if rpi_output['output_type'] == 'ledstrip': + self.ledstrip_set_rgb(rpi_output) + if rpi_output['output_type'] == 'pwm' and not rpi_output['pwm_temperature_linked']: + value = self.to_int(rpi_output['default_duty_cycle']) + self.add_pwm_output_to_queue(delay_seconds, rpi_output, value, sufix) + if (rpi_output['output_type'] == 'neopixel_indirect' or rpi_output['output_type'] == 'neopixel_direct'): + red, green, blue = self.get_color_from_rgb(rpi_output['default_neopixel_color']) + self.add_neopixel_output_to_queue(rpi_output, delay_seconds, red, green, blue, sufix) + if rpi_output['output_type'] == 'temp_hum_control': + value = rpi_output['temp_ctr_default_value'] + self.add_temperature_output_temperature_queue(delay_seconds, rpi_output, value, sufix) + self._logger.debug("Events scheduled to run %s", self.event_queue) + + def get_color_from_rgb(self, stringColor): + stringColor = stringColor.replace('rgb(', '') + red = stringColor[:stringColor.index(',')] + stringColor = stringColor[stringColor.index(',') + 1:] + green = stringColor[:stringColor.index(',')] + stringColor = stringColor[stringColor.index(',') + 1:] + blue = stringColor[:stringColor.index(')')] + return red, green, blue + + def get_shutdown_delay_from_output(self, rpi_output): + shutdown_time = rpi_output['shutdown_time'] + + shut_down_date_time = self.create_date(shutdown_time) + + if shut_down_date_time < datetime.now(): + shut_down_date_time = shut_down_date_time + timedelta(days=1) + + delay_seconds = (shut_down_date_time - datetime.now()).total_seconds() + + return delay_seconds + + def add_neopixel_output_to_queue(self, rpi_output, delay_seconds, red, green, blue, sufix): + gpio_pin = rpi_output['gpio_pin'] + ledCount = rpi_output['neopixel_count'] + ledBrightness = rpi_output['neopixel_brightness'] + address = rpi_output['microcontroller_address'] + neopixel_direct = rpi_output['output_type'] == 'neopixel_direct' + index_id = self.to_int(rpi_output['index_id']) + + queue_id = '{0!s}_{1!s}'.format(index_id, sufix) + + self._logger.debug("Scheduling neopixel output id %s for on %s delay_seconds", queue_id, delay_seconds) + + thread = threading.Timer(delay_seconds, self.send_neopixel_command, + args=[gpio_pin, ledCount, ledBrightness, red, green, blue, address, neopixel_direct, + index_id, queue_id]) + + self.event_queue.append(dict(queue_id=queue_id, thread=thread)) + + def add_pwm_output_to_queue(self, delay_seconds, rpi_output, value, sufix): + queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) + + self._logger.debug("Scheduling pwm output id %s for on %s delay_seconds", queue_id, delay_seconds) + + thread = threading.Timer(delay_seconds, self.write_pwm, + args=[self.to_int(rpi_output['gpio_pin']), value, queue_id]) + + self.event_queue.append(dict(queue_id=queue_id, thread=thread)) + + def schedule_pwm_duty_on_queue(self, delay_seconds, rpi_output, value, sufix): + queue_id = '{0!s}_{1!s}_{2!s}'.format(rpi_output['index_id'], "pwm_linked_temp", sufix) + thread = threading.Timer(delay_seconds, self.set_pwm_duty_cycle, args=[rpi_output, value, queue_id]) + + self._logger.debug("Scheduling pwm linked temp output id %s on %s delay_seconds", queue_id, delay_seconds) + + self.event_queue.append(dict(queue_id=queue_id, thread=thread)) + + def set_pwm_duty_cycle(self, rpi_output, value, queue_id): + rpi_output['duty_cycle'] = value + if queue_id is not None: + self.stop_queue_item(queue_id) + + def add_regular_output_to_queue(self, delay_seconds, rpi_output, value, sufix): + queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) + + self._logger.debug("Scheduling regular output id %s on %s delay_seconds", queue_id, delay_seconds) + + thread = threading.Timer(delay_seconds, self.write_gpio, + args=[self.to_int(rpi_output['gpio_pin']), value, queue_id]) + + self.event_queue.append(dict(queue_id=queue_id, thread=thread)) + + def add_temperature_output_temperature_queue(self, delay_seconds, rpi_output, value, sufix): + queue_id = '{0!s}_{1!s}'.format(rpi_output['index_id'], sufix) + self._logger.debug("Scheduling temperature control id %s on %s delay_seconds", queue_id, delay_seconds) + + thread = threading.Timer(delay_seconds, self.write_temperature_to_output, + args=[self.to_int(rpi_output['index_id']), value, queue_id]) + + self.event_queue.append(dict(queue_id=queue_id, thread=thread)) + + def write_temperature_to_output(self, rpi_output_index, value, queue_id=None): + try: + rpi_output = [r_out for r_out in self.rpi_outputs if + self.to_int(r_out['index_id']) == rpi_output_index].pop() + + if rpi_output['output_type'] == 'temp_hum_control': + rpi_output['temp_ctr_set_value'] = value + + if queue_id is not None: + self._logger.debug("running scheduled queue id %s", queue_id) + self._logger.debug("Setting temperature to output index: %s value %s", rpi_output['index_id'], value) + + self.update_ui() + if queue_id is not None: + self.stop_queue_item(queue_id) + + except Exception as ex: + template = "An exception of type {0} occurred on {1}. Arguments:\n{3!r}" + message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, ex.args) + self._logger.warn(message) + pass + + def get_startup_delay_from_output(self, rpi_output): + start_up_time = rpi_output['startup_time'] + if self.is_hour(start_up_time): + start_up_date_time = self.create_date(start_up_time) + if start_up_date_time < datetime.now(): + delay_seconds = 0.0 + else: + delay_seconds = (start_up_date_time - datetime.now()).total_seconds() + else: + delay_seconds = self.to_float(rpi_output['startup_time']) + return delay_seconds + + # ~~ SettingsPlugin mixin + def on_settings_save(self, data): + outputsBeforeSave = self.get_output_list() + octoprint.plugin.SettingsPlugin.on_settings_save(self, data) + self.rpi_outputs = self._settings.get(["rpi_outputs"]) + self.rpi_inputs = self._settings.get(["rpi_inputs"]) + self.notifications = self._settings.get(["notifications"]) + outputsAfterSave = self.get_output_list() + + commonPins = list(set(outputsBeforeSave) & set(outputsAfterSave)) + + for pin in (pin for pin in outputsBeforeSave if pin not in commonPins): + self.clear_channel(pin) + + self.rpi_outputs_not_changed = commonPins + self.clear_gpio() + + self._logger.debug("rpi_outputs: %s", self.rpi_outputs) + self._logger.debug("rpi_inputs: %s", self.rpi_inputs) + self.setup_gpio() + self.configure_gpio() + self.generate_temp_hum_control_status() + + def get_settings_defaults(self): + return dict(rpi_outputs=[], rpi_inputs=[], + filament_sensor_gcode="G91 ;Set Relative Mode \n" + "G1 E-5.000000 F500 ;Retract 5mm\n" + "G1 Z15 F300 ;move Z up 15mm\n" + "G90 ;Set Absolute Mode\n " + "G1 X20 Y20 F9000 ;Move to hold position\n" + "G91 ;Set Relative Mode\n" + "G1 E-40 F500 ;Retract 40mm\n" + "M0 ;Idle Hold\n" + "G90 ;Set Absolute Mode\n" + "G1 F5000 ;Set speed limits\n" + "G28 X0 Y0 ;Home X Y\n" + "M82 ;Set extruder to Absolute Mode\n" + "G92 E0 ;Set Extruder to 0", + use_sudo=True, neopixel_dma=10, debug=False, gcode_control=False, debug_temperature_log=False, + use_board_pin_number=False, notification_provider="disabled", notification_api_key="", + notification_event_name="printer_event", notifications=[{ + 'printFinish' : True, + 'filamentChange' : True, + 'printer_action' : True, + 'temperatureAction': True, 'gpioAction': True + }]) + + # ~~ TemplatePlugin + def get_template_configs(self): + return [dict(type="settings", custom_bindings=True), dict(type="tab", custom_bindings=True), + dict(type="navbar", custom_bindings=True, suffix="_1", classes=["dropdown"]), + dict(type="navbar", custom_bindings=True, template="enclosure_navbar_input.jinja2", suffix="_2", + classes=["dropdown"])] + + # ~~ AssetPlugin mixin + def get_assets(self): + return dict(js=["js/enclosure.js", "js/bootstrap-colorpicker.min.js"], + css=["css/bootstrap-colorpicker.css", "css/enclosure.css"]) + + # ~~ Softwareupdate hook + def get_update_information(self): + return dict(enclosure=dict(displayName="Enclosure Plugin", displayVersion=self._plugin_version, + # version check: github repository + type="github_release", user="vitormhenrique", repo="OctoPrint-Enclosure", current=self._plugin_version, + # update method: pip + pip="https://github.com/vitormhenrique/OctoPrint-Enclosure/archive/{target_version}.zip")) + + def hook_gcode_queuing(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs): + if self._settings.get(["gcode_control"]) is False: + return + + if cmd.strip().startswith("ENC"): + self._logger.debug("Gcode queuing: %s", cmd) + index_id = self.to_int(self.get_gcode_value(cmd, 'O')) + for output in [item for item in self.rpi_outputs if item['index_id'] == index_id]: + if output['output_type'] == 'regular': + set_value = self.to_int(self.get_gcode_value(cmd, 'S')) + set_value = self.constrain(set_value, 0, 1) + value = True if set_value == 1 else False + value = (not value) if output['active_low'] else value + self.write_gpio(self.to_int(output['gpio_pin']), value) + comm_instance._log("Setting REGULAR output %s to value %s" % (index_id, value)) + return + if output['output_type'] == 'pwm': + set_value = self.to_int(self.get_gcode_value(cmd, 'S')) + set_value = self.constrain(set_value, 0, 100) + output['duty_cycle'] = set_value + self.write_pwm(self.to_int(output['gpio_pin']), set_value) + comm_instance._log("Setting PWM output %s to value %s" % (index_id, set_value)) + return + if output['output_type'] == 'neopixel_indirect' or output['output_type'] == 'neopixel_direct': + red = self.get_gcode_value(cmd, 'R') + green = self.get_gcode_value(cmd, 'G') + blue = self.get_gcode_value(cmd, 'B') + + led_count = output['neopixel_count'] + led_brightness = output['neopixel_brightness'] + address = output['microcontroller_address'] + + index_id = self.to_int(output['index_id']) + + neopixel_direct = output['output_type'] == 'neopixel_direct' + + self.send_neopixel_command(self.to_int(output['gpio_pin']), led_count, led_brightness, red, green, + blue, address, neopixel_direct, index_id) + comm_instance._log( + "Setting NEOPIXEL output %s to red: %s green: %s blue: %s" % (index_id, red, green, blue)) + return + if output['output_type'] == 'temp_hum_control': + set_value = self.to_float(self.get_gcode_value(cmd, 'S')) + should_wait = self.to_int(self.get_gcode_value(cmd, 'W')) + if should_wait == 1 and self._printer.is_printing(): + self._printer.pause_print() + self.waiting_temperature.append(index_id) + output['temp_ctr_set_value'] = set_value + self.update_ui_set_temperature() + self.handle_temp_hum_control() + comm_instance._log("Setting TEMP/HUM control output %s to value %s" % (index_id, set_value)) + return + + +__plugin_name__ = "Enclosure Plugin" +__plugin_pythoncompat__ = ">=3,<4" + + +def __plugin_load__(): + global __plugin_implementation__ + __plugin_implementation__ = EnclosurePlugin() + + global __plugin_hooks__ + __plugin_hooks__ = { + "octoprint.comm.protocol.gcode.queuing" : __plugin_implementation__.hook_gcode_queuing, + "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information + } \ No newline at end of file diff --git a/octoprint_enclosure/getDHTTemp.py b/octoprint_enclosure/old_stuff/getDHTTemp.py similarity index 100% rename from octoprint_enclosure/getDHTTemp.py rename to octoprint_enclosure/old_stuff/getDHTTemp.py diff --git a/octoprint_enclosure/ledstrip.py b/octoprint_enclosure/old_stuff/ledstrip.py similarity index 100% rename from octoprint_enclosure/ledstrip.py rename to octoprint_enclosure/old_stuff/ledstrip.py diff --git a/octoprint_enclosure/max31855.py b/octoprint_enclosure/old_stuff/max31855.py similarity index 100% rename from octoprint_enclosure/max31855.py rename to octoprint_enclosure/old_stuff/max31855.py diff --git a/octoprint_enclosure/mcp9808.py b/octoprint_enclosure/old_stuff/mcp9808.py similarity index 100% rename from octoprint_enclosure/mcp9808.py rename to octoprint_enclosure/old_stuff/mcp9808.py diff --git a/octoprint_enclosure/neopixel_direct.py b/octoprint_enclosure/old_stuff/neopixel_direct.py similarity index 100% rename from octoprint_enclosure/neopixel_direct.py rename to octoprint_enclosure/old_stuff/neopixel_direct.py diff --git a/octoprint_enclosure/neopixel_indirect.py b/octoprint_enclosure/old_stuff/neopixel_indirect.py similarity index 100% rename from octoprint_enclosure/neopixel_indirect.py rename to octoprint_enclosure/old_stuff/neopixel_indirect.py diff --git a/octoprint_enclosure/old_stuff/static/css/bootstrap-colorpicker.css b/octoprint_enclosure/old_stuff/static/css/bootstrap-colorpicker.css new file mode 100644 index 0000000..26cb0de --- /dev/null +++ b/octoprint_enclosure/old_stuff/static/css/bootstrap-colorpicker.css @@ -0,0 +1,230 @@ +/*! + * Bootstrap Colorpicker v2.5.1 + * https://itsjavi.com/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */ +.colorpicker-saturation { + width: 100px; + height: 100px; + background-image: url("../img/bootstrap-colorpicker/saturation.png"); + cursor: crosshair; + float: left; +} +.colorpicker-saturation i { + display: block; + height: 5px; + width: 5px; + border: 1px solid #000; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + position: absolute; + top: 0; + left: 0; + margin: -4px 0 0 -4px; +} +.colorpicker-saturation i b { + display: block; + height: 5px; + width: 5px; + border: 1px solid #fff; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.colorpicker-hue, +.colorpicker-alpha { + width: 15px; + height: 100px; + float: left; + cursor: row-resize; + margin-left: 4px; + margin-bottom: 4px; +} +.colorpicker-hue i, +.colorpicker-alpha i { + display: block; + height: 1px; + background: #000; + border-top: 1px solid #fff; + position: absolute; + top: 0; + left: 0; + width: 100%; + margin-top: -1px; +} +.colorpicker-hue { + background-image: url("../img/bootstrap-colorpicker/hue.png"); +} +.colorpicker-alpha { + background-image: url("../img/bootstrap-colorpicker/alpha.png"); + display: none; +} +.colorpicker-saturation, +.colorpicker-hue, +.colorpicker-alpha { + background-size: contain; +} +.colorpicker { + padding: 4px; + min-width: 130px; + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + z-index: 2500; +} +.colorpicker:before, +.colorpicker:after { + display: table; + content: ""; + line-height: 0; +} +.colorpicker:after { + clear: both; +} +.colorpicker:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; +} +.colorpicker:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; +} +.colorpicker div { + position: relative; +} +.colorpicker.colorpicker-with-alpha { + min-width: 140px; +} +.colorpicker.colorpicker-with-alpha .colorpicker-alpha { + display: block; +} +.colorpicker-color { + height: 10px; + margin-top: 5px; + clear: both; + background-image: url("../img/bootstrap-colorpicker/alpha.png"); + background-position: 0 100%; +} +.colorpicker-color div { + height: 10px; +} +.colorpicker-selectors { + display: none; + height: 10px; + margin-top: 5px; + clear: both; +} +.colorpicker-selectors i { + cursor: pointer; + float: left; + height: 10px; + width: 10px; +} +.colorpicker-selectors i + i { + margin-left: 3px; +} +.colorpicker-element .input-group-addon i, +.colorpicker-element .add-on i { + display: inline-block; + cursor: pointer; + height: 16px; + vertical-align: text-top; + width: 16px; +} +.colorpicker.colorpicker-inline { + position: relative; + display: inline-block; + float: none; + z-index: auto; +} +.colorpicker.colorpicker-horizontal { + width: 110px; + min-width: 110px; + height: auto; +} +.colorpicker.colorpicker-horizontal .colorpicker-saturation { + margin-bottom: 4px; +} +.colorpicker.colorpicker-horizontal .colorpicker-color { + width: 100px; +} +.colorpicker.colorpicker-horizontal .colorpicker-hue, +.colorpicker.colorpicker-horizontal .colorpicker-alpha { + width: 100px; + height: 15px; + float: left; + cursor: col-resize; + margin-left: 0px; + margin-bottom: 4px; +} +.colorpicker.colorpicker-horizontal .colorpicker-hue i, +.colorpicker.colorpicker-horizontal .colorpicker-alpha i { + display: block; + height: 15px; + background: #ffffff; + position: absolute; + top: 0; + left: 0; + width: 1px; + border: none; + margin-top: 0px; +} +.colorpicker.colorpicker-horizontal .colorpicker-hue { + background-image: url("../img/bootstrap-colorpicker/hue-horizontal.png"); +} +.colorpicker.colorpicker-horizontal .colorpicker-alpha { + background-image: url("../img/bootstrap-colorpicker/alpha-horizontal.png"); +} +.colorpicker-right:before { + left: auto; + right: 6px; +} +.colorpicker-right:after { + left: auto; + right: 7px; +} +.colorpicker-no-arrow:before { + border-right: 0; + border-left: 0; +} +.colorpicker-no-arrow:after { + border-right: 0; + border-left: 0; +} +.colorpicker.colorpicker-visible, +.colorpicker-alpha.colorpicker-visible, +.colorpicker-saturation.colorpicker-visible, +.colorpicker-hue.colorpicker-visible, +.colorpicker-selectors.colorpicker-visible { + display: block; +} +.colorpicker.colorpicker-hidden, +.colorpicker-alpha.colorpicker-hidden, +.colorpicker-saturation.colorpicker-hidden, +.colorpicker-hue.colorpicker-hidden, +.colorpicker-selectors.colorpicker-hidden { + display: none; +} +.colorpicker-inline.colorpicker-visible { + display: inline-block; +} +/*# sourceMappingURL=bootstrap-colorpicker.css.map */ \ No newline at end of file diff --git a/octoprint_enclosure/old_stuff/static/css/enclosure.css b/octoprint_enclosure/old_stuff/static/css/enclosure.css new file mode 100644 index 0000000..0909952 --- /dev/null +++ b/octoprint_enclosure/old_stuff/static/css/enclosure.css @@ -0,0 +1,74 @@ +#enclosure-table { + table-layout: fixed; + width: 100%; + margin-top: 20px; +} + +#enclosure-table th, +#enclosure-table td { + vertical-align: middle; + text-align: center; +} + + +#enclosure-table th.temperature_sensor, +#enclosure-table td.temperature_sensor { + width: 15%; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +#enclosure-table th.temperature_actual, +#enclosure-table td.temperature_actual { + width: 20%; +} + +#enclosure-table th.humidity_actual, +#enclosure-table td.humidity_actual { + width: 15%; +} + +#enclosure-table th.temp_hum_control, +#enclosure-table td.temp_hum_control { + width: 15%; +} + +#enclosure-table th.temperature_target, +#enclosure-table td.temperature_target { + width: 35%; + overflow: visible; +} + +.glyphs-on { + color: blue; + text-shadow: 4px 4px 15px blue; +} + +.glyphs-off { + color: red; + text-shadow: 4px 4px 15px red; +} + +.enclosure-link{ + text-decoration: none; +} + +.enclosure-link:hover{ + text-decoration: none; +} + +.navbar-enclosure{ + text-shadow: 0 1px 0 #ccc; + color: #333; + float: none; + padding: 10px 15px 10px; + text-decoration: none; +} + +.ledstrip-div, .neopixel-div{ + width: 25%; +} + + diff --git a/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha-horizontal.png b/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha-horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..f83188951a8e4fab4ba847c8c89f3105354a9816 GIT binary patch literal 557 zcmV+|0@D47P)Nkl5JbD$mJ%t8>^MX+pA8q_5}cAta0DO|R@{IU5|M(GxNxgy+GBd8l;w&2Ec?5< zVo(HvFZxdJAnNTau8ZPXtKOAPtn2rp+IrX0nbY#4bkaNYzIIZLJrooctxYd|?rp8? z)cO+ap+cSA7pbEAUi(YitNzQ6mD^}>0x7Ez^ll73R_Xc-*~Q+Zpz#R`Ko|>@qj2QF zAcKncK;vstA0eL^`!E>xIk#_;8u-NduvaQXn$g$>7z4ttYmjqc8pS->gW&WGdI5U9QmYbj;6~_suM0VjVBk)9?FjGKLNzR9mK$u}% zJ?3)?fRmicTx%xVMC69^f7R;rWA8uZm)yve{K1EP8^1~E?L`XT8&R{cit@G7SGmNI zD-;bNVa)Z2Mil~Fb~9WBo8Kj@u3_#HiLFA<1-{qWmO2-=?0YdKVz6%S z2*9Wz8-&7K@4=W?VKkD82M*@cB4wmZ=tias{62*I43WQYJZEcwS<&N#Nal+GF`i11 zY=e+9d7#-KG!R}kc+3AQ$^6t@932p}V@@!QyyuhBvt_KD{nGq0=-+8wbOe^=^onsM vr1hwgCt@ro;xv+DFr+*}$b1+=kSNMOdLipIvOQ8u00000NkvXXu0mjf9#jU9 literal 0 HcmV?d00001 diff --git a/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha.png b/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..2e53a30e73175009326fc030f00862d682ddfc45 GIT binary patch literal 488 zcmVP)B00056Nkl} zZ#=A8HwTy*Ji~Hui*DN)Xc=D2SbWU)JF^vFq?ipSE1)dQB?%2~zL;*2(|O#(oBR;O z#J!!4+zDc0I&77>T+tDIE@oI;6%kXWiptfQV#*c>2foJxnWs^#66pqri5SJi0v*Xc z_vjIT9?6}|hzFF05)=1g9#80;7y9mior*2jP95+vQ|{sWGtx)s=39zr^u6;HX^F`p zkx2{{y^wicti6Khm3Wyed%Eg2H=fbPc7D3VBcDWdj{r#qvrgUX$)#2lTEc%5bs}9E zZ`+UiVVyX}VI5jr+$k@C{pP8ABQF)*q|@k8ll1fgWY+Q?y|Zg0V9OmodqfH%3xo%V zF_0b~gfCME=jtfJ($v-Y^!<}BBQx2`+78RhRbxt$n5&{MunD^|({9#^#Hp{!+@o)R z!Ci&=u08T*r)OJ-Py8D;`eSXCiKA=ket6eR*LHXng}b_5r%ttFu8MwPi~vTbhPJwk eS?^O73H$>F-M_zn@}r;t0000ShR^6anYhsD}*2sixx#_ z;;N9)z(r=E(k3ElBO!xKg9K3|ZCX@f)afnmy=Qme_QYdH!ujj-fpFu|`s2Y~2olQ6 z2|AMd=Wf7d@urrB#gesBBwHjZbrP3WX_M>HCxbE~lQJV2S(N2p_yyDW7AJ5R`>_W* z@De7l32U$%Be)tvE2K~&QXw@GlZ2$CLwcoOp2~#0leB!5-{KN}!+D&+S2%(L*oR%% zhDnTLEkEmxDdue*zK#wJq_##5Q`F$=0GT~`-7ysSAOhUwhSHMLP5MN9R=S!a}6CtN3K7ctc(wrm-Pb|Ljm}_kZeK)QSKA literal 0 HcmV?d00001 diff --git a/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/hue.png b/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/hue.png new file mode 100644 index 0000000000000000000000000000000000000000..6f5ec2e502ae3e3e03f4725b0e868e8762d089ab GIT binary patch literal 504 zcmWlVPe_vi0L6c6Mj^~HS41FE*i8vzMTa!3jau0Ftck^RiETv4%*eh1etNgh#Z9kO+%DF(^hwQhXE(;=A}Olt({N8qK0N zXbe3;F%&_a$d3$k2-TpSsMI2>O_Yl&aZnr=K5;=@6TM_+u^<&@c2A zeL`>1OY{`=qnoG;wWCIK1notY$WDGs$^MZILnglaZfCu>$G?91_ND(cr{ke#6QRt+ zxJqh^itgTVIcMJOt{i?I4CJy~?T;TddFSW$ImQ!Le#{N~;{Hg%w%rj(^){Y+e`3H9 zTzq)u@>2EbL?Hd3spCs1l?Z0~Tf=MR2fDMV=&AFcTa(?n#hCBv@7kW#Y`U-c;y&>!CvSwpt@}Bttx69*vb-3-;Ki8A$y8r+H literal 0 HcmV?d00001 diff --git a/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/saturation.png b/octoprint_enclosure/old_stuff/static/img/bootstrap-colorpicker/saturation.png new file mode 100644 index 0000000000000000000000000000000000000000..170841cba2fe51c604a6ed63d9fa119dc4f7a73a GIT binary patch literal 4143 zcmV+~5YX?5P)+#sK+(JQRJ~A(_5>{!ukUR)i7jcSrf7+9fQH~lRI)qc5p5fP$Ioc!An4H= z%NP;FPmORvKp_F;eH0~_u{d=%(&6=qp`vB+dUNkoR;dv6{jpm2FGdQS-0 zkO&rONv#g|hQn2{-!^;ciBCQB9slSPO+jRcXo>jL0JAkU5cK~*ks!JHB>=hUsIVu z@+1V0SNEtlMF>cW{vad@AXRu0nPW;db+}bjtJqPs>v?)C4#Qs8F&IA$Z-(P{g$;9Ny5>td)kU%{Fb^%WKTZ z15Z?_+}qS)!L0;i3^lzm3d>ZbJ{N-Ss$( z+x^uK8s%jhxBjwPq&t&90$kHE*DBE2Gn&Bxm;@wjkEuv@;;MU=AP%L9IB%;0F*7Bx zFlJ&sPP+ASMApy)4Ldd+YH%wXs&;hgIwO(eY-P;5X5N*?dR_5%&*)Ci7t2Ex@MASb zTLRgQ*2{b@w{t$)VHLX_2DjJY%{8(v@uG;98mW#$SrJIU2$1H!pa;>ZLMTbYf<2Fu zB(cc%27Jbh7;p0#+T{I7qm8_yq7FTdN)?+`IzbAlNa{h4MvLg{AmMN&K=Ve>J;y2t ztYM~*)Mmv{CuV}aPDKcLMkkA$1kMA7<|bF13GPTg~!z4!Bb zX1+c1@An;Ro$XxRz08_fv-V*=`#ZCq2j|1_J`PtnEWQoEcSXy)w9bztz|-v+PkNCjeJz*q(RKdoL%ctZ zz+l0|ND#{c!n}hRSQb7FC~yD+kpbKNKFlEu3XV`Hx%<69Y*zy@9x((nUxy@$T=m)4 zM--niy+iQaL645-de886?YIk>a#rJ`8xmNATa~Wu*VY&bUipD zJH(RF^MoD7O)}q?C_zk*#5h4juRiXFKI`4!-+#wlkv|6rRSV{L>1;Cp9CM%zJv`719nfKvfcm6XKwS zfdC;w>}Ugz!~NBii8%?7KfVkCbSUtCQ6cXnXGsBZ0E_}rDMUDmMBs649VD^XRu-)w zN7}u)4CD_xh!q5ns0a}V3j%3VMcI%5ha2t9%AA}zI<}SZO2)FOa>#)ro9jBBALs8o zNWEN3xe--^#8-UXk9; z-q65QJ?CRu>tGx5R80Decmxd+EUhIO$$6^QQU`Dp+)eJgL7+gckMp+#!P-oWP|>ai za@ebQNmC?pZ!I>OmMD3W@In~N|DZ7)bc89;%UBGF*uut<-GXiKqzyyrPVGO3l(vHt4Z;*4ZH@(PYp!$9P@F)Xz3GY1L^=4L3CQY%ma0T z=d={_yupP91Wi_u%oWVdVncvPv{-&0evr%jiGx7VK!BzOU#)DV0_DJ?-zCWTB?Sd8 z{A33Kw0nMg*+IH0sZ7{?%{$1CJBV8qSQ!Fo0hT^hWVb-5eBvaTZwrWaAn9|0v$Y1l zvN%j=GDIXanet|SkRV+PLTM>eP|cT0yWuQ3;X$ZIX~f8LS3@+z!I$6$w%UC>m zn-QdP2}ETAVjz^TYrHxTOL ziqJ=nDnx-)Li0YE5^e{fgAy!Y3ABMgW#EKC8+Lt9|1-Zr5Ecr>teF@~Yh^`|CP;xL zN}2%>hWR=x#tJbOW$Zj4;i-;Mi^T=<xt#ti9DjI?jq=1;`f-MB(=Y@zK^f z>RAPPOC`VpSw>Yc7F&M!7e|03@D9~gB9d0?y{E6{7aWA`!CT$tT9v2@F8#2qOW%*=cG@A)|c*)>p%cj-a@ zR#ld^;;gXPk!p~71)|rNqX7A=gK*Thzxx&m-;?`km4=Q#&+ zhrxrsnx7#EA;zWz_p#(cyaI(sgvGl+3YM*a_1jJYBpqQ1^MF_L(**JK%o&NA##tyK z8Ukl(n~cnHj94t)3Al9OAPC~D3F+RPdrcSv`ILkFuXZy~jUooZ@JxFaM1&$#1QA38 zK?DIr5CIWH5D^6N-F*bTs(k_z_x3XRI{UK$Yp-lJlT0!*UuLqUK#9^W=K?4wKnXp8 zLKI6C#GoN^$x<#a)=+T9Xac$LuYmM8w7iUCMos~zv6L1uirhE^XdxGX0I*(K7yd;+ zfFMf*2vi3yOcCce_$6)ax!q(eox7skBJuuva)MIgR9j zPqp(qfowLA_cKU}kVy3dV5R{vsjLJlN~RUGhz>{a2FwidPChOc6uDLdXH~b^_Vyoq(#fZHzS#?hu5Uxdftsz_1;doDi>&8d>WgZzhnP?8*7e zo`T^4b_GNafN=;JO2(Xg(DJ!VdO%)JATz)+WV0L$VqC?r(!fm^c&oItRufxH9+-|6I5f{To zL8QgJxFkE*)qqG0s;8BGzp3Y&VvS+`DdxM@=IY}A5|_<9_QpzVp&2%>l~M8%B$0cM zzgDfbAWPqPWJms-jP#_@QCg;{N@BV{j$+*8E+Wz*JUpQ38S!U;^n>7qKM*sP>JhPP z8Uvy_BexSBNPCbcJCMX`NZAtvJajrh6nX(% zF|6E(70sq@{qY{8m3FE9W^?1z(7+Mu5y<$UBECbg%nJe0aG7C=PzuR~(i9+fQcNf#jJes2Q7UNuir%@CMnz6%a)W)%XY z!GVnXoWC2k^}7=Y%2M6PPIAPIn6ib|g#szf2v|NX$&i`~kRN^r5cD|9vN%9q-=N^g z^sC0lCILBJfT;XG8sr1{0Oa-zf=cLf0ubnAks4uiRikPIq#5K^0}+&i%vAHM*?~eZ z8pusR`n-crHxOm*2yMJ$>`W;FWa}pZBAJwZ2qWz{ICIA1ZDU_qcVgfD_`*(cA`And t>uB}5*8g_@W@q+?3O)eH;oGro{{qbcPck5Ql{)|c002ovPDHLkV1nX9d?Nq= literal 0 HcmV?d00001 diff --git a/octoprint_enclosure/old_stuff/static/js/bootstrap-colorpicker.min.js b/octoprint_enclosure/old_stuff/static/js/bootstrap-colorpicker.min.js new file mode 100644 index 0000000..7cd35b8 --- /dev/null +++ b/octoprint_enclosure/old_stuff/static/js/bootstrap-colorpicker.min.js @@ -0,0 +1,5 @@ +/*! + * Bootstrap Colorpicker v2.5.1 + * https://itsjavi.com/bootstrap-colorpicker/ + */ +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):jQuery&&!jQuery.fn.colorpicker&&b(jQuery)}(this,function(a){"use strict";var b=function(c,d,e,f,g){this.fallbackValue=e?e&&"undefined"!=typeof e.h?e:this.value={h:0,s:0,b:0,a:1}:null,this.fallbackFormat=f?f:"rgba",this.hexNumberSignPrefix=g===!0,this.value=this.fallbackValue,this.origFormat=null,this.predefinedColors=d?d:{},this.colors=a.extend({},b.webColors,this.predefinedColors),c&&("undefined"!=typeof c.h?this.value=c:this.setColor(String(c))),this.value||(this.value={h:0,s:0,b:0,a:1})};b.webColors={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32",transparent:"transparent"},b.prototype={constructor:b,colors:{},predefinedColors:{},getValue:function(){return this.value},setValue:function(a){this.value=a},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:""===a?0:"undefined"!=typeof a.toLowerCase?(a.match(/^\./)&&(a="0"+a),Math.ceil(100*parseFloat(a))/100):1},isTransparent:function(a){return!(!a||!("string"==typeof a||a instanceof String))&&(a=a.toLowerCase().trim(),"transparent"===a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/))},rgbaIsTransparent:function(a){return 0===a.r&&0===a.g&&0===a.b&&0===a.a},setColor:function(a){if(a=a.toLowerCase().trim()){if(this.isTransparent(a))return this.value={h:0,s:0,b:0,a:0},!0;var b=this.parse(a);b?(this.value=this.value={h:b.h,s:b.s,b:b.b,a:b.a},this.origFormat||(this.origFormat=b.format)):this.fallbackValue&&(this.value=this.fallbackValue)}return!1},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=Math.round(parseInt(100*(1-a),10)/100*100)/100},toRGB:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d}},toHex:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a);var e=this.toRGB(a,b,c,d);if(this.rgbaIsTransparent(e))return"transparent";var f=(this.hexNumberSignPrefix?"#":"")+((1<<24)+(parseInt(e.r)<<16)+(parseInt(e.g)<<8)+parseInt(e.b)).toString(16).slice(1);return f},toHSL:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a);var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&f<=1?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e,f=0===arguments.length?this.toHex():this.toHex(a,b,c,d),g="alias"===this.origFormat?f:this.toString(this.origFormat,!1);for(var h in this.colors)if(e=this.colors[h].toLowerCase().trim(),e===f||e===g)return h;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return c<0?c+=1:c>1&&(c-=1),6*c<1?a+(b-a)*c*6:2*c<1?b:3*c<2?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){b<0&&(b=0);var e;e=c<=.5?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},parse:function(b){if(0===arguments.length)return!1;var c,d,e=this,f=!1,g="undefined"!=typeof this.colors[b];return g&&(b=this.colors[b].toLowerCase().trim()),a.each(this.stringParsers,function(a,h){var i=h.re.exec(b);return c=i&&h.parse.apply(e,[i]),!c||(f={},d=g?"alias":h.format?h.format:e.getValidFallbackFormat(),f=d.match(/hsla?/)?e.RGBtoHSB.apply(e,e.HSLtoRGB.apply(e,c)):e.RGBtoHSB.apply(e,c),f instanceof Object&&(f.format=d),!1)}),f},getValidFallbackFormat:function(){var a=["rgba","rgb","hex","hsla","hsl"];return this.origFormat&&a.indexOf(this.origFormat)!==-1?this.origFormat:this.fallbackFormat&&a.indexOf(this.fallbackFormat)!==-1?this.fallbackFormat:"rgba"},toString:function(a,c){a=a||this.origFormat||this.fallbackFormat,c=c||!1;var d=!1;switch(a){case"rgb":return d=this.toRGB(),this.rgbaIsTransparent(d)?"transparent":"rgb("+d.r+","+d.g+","+d.b+")";case"rgba":return d=this.toRGB(),"rgba("+d.r+","+d.g+","+d.b+","+d.a+")";case"hsl":return d=this.toHSL(),"hsl("+Math.round(360*d.h)+","+Math.round(100*d.s)+"%,"+Math.round(100*d.l)+"%)";case"hsla":return d=this.toHSL(),"hsla("+Math.round(360*d.h)+","+Math.round(100*d.s)+"%,"+Math.round(100*d.l)+"%,"+d.a+")";case"hex":return this.toHex();case"alias":return d=this.toAlias(),d===!1?this.toString(this.getValidFallbackFormat()):c&&!(d in b.webColors)&&d in this.predefinedColors?this.predefinedColors[d]:d;default:return d}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]&&this.colors[a.toLowerCase()]}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",fallbackColor:!1,fallbackFormat:"hex",hexNumberSignPrefix:!0,sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'