From 16f3ec817877f94c18fc013c6e8a6bc4642ff7b3 Mon Sep 17 00:00:00 2001 From: Randall Hand Date: Sun, 4 Oct 2020 10:25:35 -0400 Subject: [PATCH 1/7] Convert STDOUT from Popen's to UTF-8 By default, Python3's STDOUT's that come back from Popen commands are byte-arrays, not strings. So attempting to do string operations (like 'split' or 'strip') fail. They must be converted to UTF-8 strings first. This was causing the enclosure plugin to return false debug messages about 'disabling SUDO' when attempting to read my DHT22 sensor. --- octoprint_enclosure/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index b324484..bf0e77e 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -864,7 +864,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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()) + return self.to_float(stdout.decode("utf-8").strip()) except Exception as ex: self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") self.log_error(ex) @@ -883,7 +883,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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("|") + temp, hum = stdout.decode("utf-8").split("|") return (self.to_float(temp.strip()), self.to_float(hum.strip())) except Exception as ex: self._logger.info( @@ -904,7 +904,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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("|") + temp, hum = stdout.decode("utf-8").split("|") return (self.to_float(temp.strip()), self.to_float(hum.strip())) except Exception as ex: self._logger.info( @@ -925,7 +925,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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("|") + temp, hum = stdout.decode("utf-8").split("|") return (self.to_float(temp.strip()), self.to_float(hum.strip())) except Exception as ex: self._logger.info( @@ -946,7 +946,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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("|") + temp, hum = stdout.decode("utf-8").split("|") return (self.to_float(temp.strip()), self.to_float(hum.strip())) except Exception as ex: self._logger.info( @@ -990,7 +990,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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()) + return self.to_float(stdout.decode("utf-8").strip()) except Exception as ex: self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") self.log_error(ex) @@ -1006,7 +1006,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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()) + return self.to_float(stdout.decode("utf-8").strip()) except Exception as ex: self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.") self.log_error(ex) From 2237920a8eb6c4bfb21dfa539d587a55690d849b Mon Sep 17 00:00:00 2001 From: RFBomb Date: Sat, 24 Oct 2020 09:40:13 -0400 Subject: [PATCH 2/7] Update __init__.py Added IO status to the /inputs and /outputs API calls --- octoprint_enclosure/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index d1681a8..f613d86 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -151,7 +151,9 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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)) + pin = self.to_int(rpi_output['gpio_pin']) + val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) + inputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, Status=val)) return Response(json.dumps(inputs), mimetype='application/json') @@ -215,7 +217,9 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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)) + pin = self.to_int(rpi_output['gpio_pin']) + val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) + outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, status=val)) return Response(json.dumps(outputs), mimetype='application/json') From 223d61ff3318506a0f64712f3027aded380a0567 Mon Sep 17 00:00:00 2001 From: RFBomb Date: Sat, 24 Oct 2020 18:51:25 -0400 Subject: [PATCH 3/7] Expanded API for better debugging of inputs - Added 3 support routines at top of the script to normalize / functionize reading the GPIO Pin States for both inputs and outputs. - The '/inputs' and '/outputs' API Calls will now include the current pin state (Human Readable), Label, GPIO Pin Number, and Index in their JSON outputs. These changes also resolve the api returning both 'false' and '0' as the pin states would sometimes show up as from the "val = if-else "statement. --- octoprint_enclosure/__init__.py | 3790 ++++++++++++++++--------------- 1 file changed, 1926 insertions(+), 1864 deletions(-) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index f613d86..d2f1c78 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -23,1864 +23,1926 @@ import json import copy +#Function that returns Boolean output state of the GPIO inputs / outputs +def PinState_Boolean(pin, ActiveLow) : + try: + state = GPIO.input(pin) + if ActiveLow and not state: return True + if not ActiveLow and state: return True + return False + except: + return "ERROR: Unable to read pin" + +#Function that returns human-readable output state of the GPIO inputs / outputs +def PinState_Human(pin, ActiveLow): + PinState = PinState_Boolean(pin, ActiveLow) + if PinState == True : + return " ON " + elif PinState == False: + return " OFF " + else: + return PinState + +#Translates the Pull-Up/Pull-Down GPIO resistor setting to ActiveLow/ActiveHigh boolean +def CheckInputActiveLow(Input_Pull_Resistor): + #input_pull_up + #input_pull_down + if Input_Pull_Resistor == "input_pull_up": + return True + else: + return False + 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, '') - 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'] - pin = self.to_int(rpi_output['gpio_pin']) - val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) - inputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, Status=val)) - 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'] - pin = self.to_int(rpi_output['gpio_pin']) - val = GPIO.input(pin) if not rpi_output['active_low'] else (not GPIO.input(pin)) - outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, status=val)) - 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'], sensor['temp_sensor_i2cbus']) - 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 temperature_alarm['controlled_io_set_value'] == '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, i2cbus): - 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) + " " + str(i2cbus) - 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()) - self._logger.info("Sending GCODE command: %s", line.strip()) - 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()) - self._logger.info("Sending GCODE command: %s", line.strip()) - 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 + 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, '') + 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"]) + + #Scan all configured inputs and outputs and return the pin value + @octoprint.plugin.BlueprintPlugin.route("/ReadPin/", methods=["GET"]) + def ReadSinglePin(self, identifier): + Resp = [] + MatchFound = False + for rpi_input in self.rpi_inputs: + if identifier == self.to_int(rpi_input['gpio_pin']): + MatchFound = True + ConfiguredAs = "Input" + ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) + pin = self.to_int(rpi_input['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + label = rpi_input['label'] + Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['gpio_pin']): + MatchFound = True + ConfiguredAs = "Output" + ActiveLow = CheckInputActiveLow(rpi_output['active_low']) + pin = self.to_int(rpi_output['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + label = rpi_output['label'] + Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + if MatchFound == False: + pin = int(identifier) + ConfiguredAs = "Unknown" + ActiveLow = "Unknown" + try: + val = GPIO.input(pin) + except: + val = "GPIO pin not initialized." + Resp.append(dict(Configured_As=ConfiguredAs, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + return Response(json.dumps(Resp), mimetype='application/json') + + # ~~ 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'] + ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) + pin = self.to_int(rpi_input['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + inputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) + 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'] + pin = self.to_int(rpi_output['gpio_pin']) + ActiveLow = rpi_output['active_low'] + val = PinState_Human(pin,ActiveLow) + outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) + 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'] = PinState_Boolean(pin, rpi_output['active_low'] ) + 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']) + ActiveLow = rpi_output['active_low'] + val = PinState_Boolean(pin, ActiveLow) + val2 = PinState_Human(pin, ActiveLow) + index = self.to_int(rpi_output['index_id']) + gpio_status.append(dict(index_id=index, status=val, State=val2)) + 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'], sensor['temp_sensor_i2cbus']) + 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 temperature_alarm['controlled_io_set_value'] == '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, i2cbus): + 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) + " " + str(i2cbus) + 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()) + self._logger.info("Sending GCODE command: %s", line.strip()) + 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()) + self._logger.info("Sending GCODE command: %s", line.strip()) + 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" @@ -1888,11 +1950,11 @@ __plugin_pythoncompat__ = ">=2.7,<4" def __plugin_load__(): - global __plugin_implementation__ - __plugin_implementation__ = EnclosurePlugin() + 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 - } + 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 + } From 546d7535adfb5fdd52c81d72e3ecbe68554141c9 Mon Sep 17 00:00:00 2001 From: RFBomb Date: Sat, 24 Oct 2020 18:54:21 -0400 Subject: [PATCH 4/7] Converted from Tab Spacing to 4xSpacebar Spacing to match original --- octoprint_enclosure/__init__.py | 3836 +++++++++++++++---------------- 1 file changed, 1918 insertions(+), 1918 deletions(-) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index d2f1c78..ca3b983 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -25,1924 +25,1924 @@ import copy #Function that returns Boolean output state of the GPIO inputs / outputs def PinState_Boolean(pin, ActiveLow) : - try: - state = GPIO.input(pin) - if ActiveLow and not state: return True - if not ActiveLow and state: return True - return False - except: - return "ERROR: Unable to read pin" + try: + state = GPIO.input(pin) + if ActiveLow and not state: return True + if not ActiveLow and state: return True + return False + except: + return "ERROR: Unable to read pin" #Function that returns human-readable output state of the GPIO inputs / outputs def PinState_Human(pin, ActiveLow): - PinState = PinState_Boolean(pin, ActiveLow) - if PinState == True : - return " ON " - elif PinState == False: - return " OFF " - else: - return PinState + PinState = PinState_Boolean(pin, ActiveLow) + if PinState == True : + return " ON " + elif PinState == False: + return " OFF " + else: + return PinState #Translates the Pull-Up/Pull-Down GPIO resistor setting to ActiveLow/ActiveHigh boolean def CheckInputActiveLow(Input_Pull_Resistor): - #input_pull_up - #input_pull_down - if Input_Pull_Resistor == "input_pull_up": - return True - else: - return False - + #input_pull_up + #input_pull_down + if Input_Pull_Resistor == "input_pull_up": + return True + else: + return False + 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, '') - 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"]) - - #Scan all configured inputs and outputs and return the pin value - @octoprint.plugin.BlueprintPlugin.route("/ReadPin/", methods=["GET"]) - def ReadSinglePin(self, identifier): - Resp = [] - MatchFound = False - for rpi_input in self.rpi_inputs: - if identifier == self.to_int(rpi_input['gpio_pin']): - MatchFound = True - ConfiguredAs = "Input" - ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) - pin = self.to_int(rpi_input['gpio_pin']) - val = PinState_Human(pin,ActiveLow) - label = rpi_input['label'] - Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) - for rpi_output in self.rpi_outputs: - if identifier == self.to_int(rpi_output['gpio_pin']): - MatchFound = True - ConfiguredAs = "Output" - ActiveLow = CheckInputActiveLow(rpi_output['active_low']) - pin = self.to_int(rpi_output['gpio_pin']) - val = PinState_Human(pin,ActiveLow) - label = rpi_output['label'] - Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) - if MatchFound == False: - pin = int(identifier) - ConfiguredAs = "Unknown" - ActiveLow = "Unknown" - try: - val = GPIO.input(pin) - except: - val = "GPIO pin not initialized." - Resp.append(dict(Configured_As=ConfiguredAs, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) - return Response(json.dumps(Resp), mimetype='application/json') - - # ~~ 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'] - ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) - pin = self.to_int(rpi_input['gpio_pin']) - val = PinState_Human(pin,ActiveLow) - inputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) - 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'] - pin = self.to_int(rpi_output['gpio_pin']) - ActiveLow = rpi_output['active_low'] - val = PinState_Human(pin,ActiveLow) - outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) - 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'] = PinState_Boolean(pin, rpi_output['active_low'] ) - 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']) - ActiveLow = rpi_output['active_low'] - val = PinState_Boolean(pin, ActiveLow) - val2 = PinState_Human(pin, ActiveLow) - index = self.to_int(rpi_output['index_id']) - gpio_status.append(dict(index_id=index, status=val, State=val2)) - 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'], sensor['temp_sensor_i2cbus']) - 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 temperature_alarm['controlled_io_set_value'] == '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, i2cbus): - 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) + " " + str(i2cbus) - 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()) - self._logger.info("Sending GCODE command: %s", line.strip()) - 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()) - self._logger.info("Sending GCODE command: %s", line.strip()) - 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 + 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, '') + 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"]) + + #Scan all configured inputs and outputs and return the pin value + @octoprint.plugin.BlueprintPlugin.route("/ReadPin/", methods=["GET"]) + def ReadSinglePin(self, identifier): + Resp = [] + MatchFound = False + for rpi_input in self.rpi_inputs: + if identifier == self.to_int(rpi_input['gpio_pin']): + MatchFound = True + ConfiguredAs = "Input" + ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) + pin = self.to_int(rpi_input['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + label = rpi_input['label'] + Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + for rpi_output in self.rpi_outputs: + if identifier == self.to_int(rpi_output['gpio_pin']): + MatchFound = True + ConfiguredAs = "Output" + ActiveLow = CheckInputActiveLow(rpi_output['active_low']) + pin = self.to_int(rpi_output['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + label = rpi_output['label'] + Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + if MatchFound == False: + pin = int(identifier) + ConfiguredAs = "Unknown" + ActiveLow = "Unknown" + try: + val = GPIO.input(pin) + except: + val = "GPIO pin not initialized." + Resp.append(dict(Configured_As=ConfiguredAs, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) + return Response(json.dumps(Resp), mimetype='application/json') + + # ~~ 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'] + ActiveLow = CheckInputActiveLow(rpi_input['input_pull_resistor']) + pin = self.to_int(rpi_input['gpio_pin']) + val = PinState_Human(pin,ActiveLow) + inputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) + 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'] + pin = self.to_int(rpi_output['gpio_pin']) + ActiveLow = rpi_output['active_low'] + val = PinState_Human(pin,ActiveLow) + outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) + 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'] = PinState_Boolean(pin, rpi_output['active_low'] ) + 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']) + ActiveLow = rpi_output['active_low'] + val = PinState_Boolean(pin, ActiveLow) + val2 = PinState_Human(pin, ActiveLow) + index = self.to_int(rpi_output['index_id']) + gpio_status.append(dict(index_id=index, status=val, State=val2)) + 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'], sensor['temp_sensor_i2cbus']) + 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 temperature_alarm['controlled_io_set_value'] == '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, i2cbus): + 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) + " " + str(i2cbus) + 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()) + self._logger.info("Sending GCODE command: %s", line.strip()) + 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()) + self._logger.info("Sending GCODE command: %s", line.strip()) + 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" @@ -1950,11 +1950,11 @@ __plugin_pythoncompat__ = ">=2.7,<4" def __plugin_load__(): - global __plugin_implementation__ - __plugin_implementation__ = EnclosurePlugin() + 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 - } + 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 + } From 64eecefe76b68e44a8bfc1fba195abf93062c8be Mon Sep 17 00:00:00 2001 From: Roberto Bonacina Date: Tue, 1 Dec 2020 21:24:08 +0100 Subject: [PATCH 5/7] Fix some typos in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e75eba..e7e67fd 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Check pictures on thingiverse: http://www.thingiverse.com/thing:2245493 Install the plugin using the Plugin Manager bundled with OctoPrint, you can search for the Enclosure plugin or just use the url: https://github.com/vitormhenrique/OctoPrint-Enclosure/archive/master.zip. -To control the encosure temperature or get temperature trigged events, you need to install and configure a temperature sensor. This plugin can support DHT11, DHT22, AM2302, DS18B20, SI7021, BME280 and TMP102 temperature sensors. +To control the enclosure temperature or get temperature triggered events, you need to install and configure a temperature sensor. This plugin can support DHT11, DHT22, AM2302, DS18B20, SI7021, BME280 and TMP102 temperature sensors. * For the DHT11, DHT22 and AM2302 follow this steps: @@ -170,7 +170,7 @@ Outputs can be set to the following types: * Regular GPIO * PWM GPIO -* Neopixel Control via Microcontroler +* Neopixel Control via Microcontroller * Neopixel Control directly from raspberry pi * Temperature and Humidity Control * Temperature Alarm From c9103bcdf8c29bab9621ec62a68b99ed668c5485 Mon Sep 17 00:00:00 2001 From: raoulh Date: Fri, 5 Feb 2021 17:44:31 +0100 Subject: [PATCH 6/7] Add i2c gpio support (#1) Implement support for i2c gpio devices (GPIO outputs and temp/humidity inputs) --- octoprint_enclosure/__init__.py | 238 +++++++++++++++--- octoprint_enclosure/static/js/enclosure.js | 14 +- .../templates/enclosure_settings.jinja2 | 98 +++++++- setup.py | 2 +- 4 files changed, 315 insertions(+), 37 deletions(-) diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index 473321c..8116e81 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -21,6 +21,8 @@ import inspect import threading import json import copy +from smbus2 import SMBus +import struct #Function that returns Boolean output state of the GPIO inputs / outputs @@ -148,7 +150,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP self.print_complete = False def get_settings_version(self): - return 6 + return 8 def on_settings_migrate(self, target, current=None): self._logger.warn("######### current settings version %s target settings version %s #########", current, target) @@ -156,15 +158,39 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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 #########") + if current >= 4 and target == 8: + self._logger.warn("######### migrating settings to v8 #########") 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'] = "" + if 'gpio_i2c_enabled' not in rpi_output: + rpi_output['gpio_i2c_enabled'] = False + if 'gpio_i2c_bus' not in rpi_output: + rpi_output['gpio_i2c_bus'] = 1 + if 'gpio_i2c_address' not in rpi_output: + rpi_output['gpio_i2c_address'] = 1 + if 'gpio_i2c_register' not in rpi_output: + rpi_output['gpio_i2c_register'] = 1 + if 'gpio_i2c_data_on' not in rpi_output: + rpi_output['gpio_i2c_data_on'] = 1 + if 'gpio_i2c_data_off' not in rpi_output: + rpi_output['gpio_i2c_data_off'] = 0 + if 'gpio_i2c_register_status' not in rpi_output: + rpi_output['gpio_i2c_register_status'] = 1 self._settings.set(["rpi_outputs"], old_outputs) + + old_inputs = self._settings.get(["rpi_inputs"]) + for rpi_input in old_inputs: + if 'temp_i2c_bus' not in rpi_input: + rpi_input['temp_i2c_bus'] = 1 + if 'temp_i2c_address' not in rpi_input: + rpi_input['temp_i2c_address'] = 1 + if 'temp_i2c_register' not in rpi_input: + rpi_input['temp_i2c_register'] = 1 + self._settings.set(["rpi_inputs"], old_inputs) else: self._logger.warn("######### settings not compatible #########") self._settings.set(["rpi_outputs"], []) @@ -191,7 +217,11 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP ConfiguredAs = "Output" ActiveLow = CheckInputActiveLow(rpi_output['active_low']) pin = self.to_int(rpi_output['gpio_pin']) - val = PinState_Human(pin,ActiveLow) + if rpi_output['gpio_i2c_enabled']: + b = self.gpio_i2c_input(rpi_output, ActiveLow) + val = " ON " if b else " OFF " + else: + val = PinState_Human(pin,ActiveLow) label = rpi_output['label'] Resp.append(dict(Configured_As=ConfiguredAs, label=label, GPIO_Pin=pin, Active_Low=ActiveLow, State=val)) if MatchFound == False: @@ -279,7 +309,11 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP label = rpi_output['label'] pin = self.to_int(rpi_output['gpio_pin']) ActiveLow = rpi_output['active_low'] - val = PinState_Human(pin,ActiveLow) + if rpi_output['gpio_i2c_enabled']: + b = self.gpio_i2c_input(rpi_output, ActiveLow) + val = " ON " if b else " OFF " + else: + val = PinState_Human(pin,ActiveLow) outputs.append(dict(index_id=index, label=label, GPIO_Pin=pin, State=val)) return Response(json.dumps(outputs), mimetype='application/json') @@ -290,7 +324,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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'] = PinState_Boolean(pin, rpi_output['active_low'] ) + if rpi_output['gpio_i2c_enabled']: + out['current_value'] = self.gpio_i2c_input(rpi_output, rpi_output['active_low']) + else: + out['current_value'] = PinState_Boolean(pin, rpi_output['active_low'] ) return Response(json.dumps(out), mimetype='application/json') return make_response('', 404) @@ -313,7 +350,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if rpi_output['gpio_i2c_enabled']: + self.gpio_i2c_write(rpi_output, val) + else: + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) return make_response('', 204) @@ -520,7 +560,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP if rpi_output['output_type'] == 'regular': pin = self.to_int(rpi_output['gpio_pin']) ActiveLow = rpi_output['active_low'] - val = PinState_Boolean(pin, ActiveLow) + if rpi_output['gpio_i2c_enabled']: + val = self.gpio_i2c_input(rpi_output, rpi_output['active_low']) + else: + val = PinState_Boolean(pin, ActiveLow) val2 = PinState_Human(pin, ActiveLow) index = self.to_int(rpi_output['index_id']) gpio_status.append(dict(index_id=index, status=val, State=val2)) @@ -533,7 +576,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if rpi_output['gpio_i2c_enabled']: + self.gpio_i2c_write(rpi_output, val) + else: + self.write_gpio(self.to_int(rpi_output['gpio_pin']), val) return jsonify(success=True) @octoprint.plugin.BlueprintPlugin.route("/sendShellCommand", methods=["GET"]) @@ -642,7 +688,62 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP # DEPREACTION END + # GPIO over i2c + def gpio_i2c_input(self, output, active_low=None): + state = False + try: + i2cbus = self.to_int(output['gpio_i2c_bus']) + i2caddr = self.to_int(output['gpio_i2c_address']) + i2creg = self.to_int(output['gpio_i2c_register_status']) + data_on = self.to_int(output['gpio_i2c_data_on']) + + with SMBus(i2cbus) as bus: + data = bus.read_i2c_block_data(i2caddr, i2creg, 1) + if data[0] == data_on: + state = True + + self._logger.debug("gpio_i2c_input(i2cbus=%s, i2caddr=%s, i2creg=%s, data_on=%s) data == %s", + i2cbus, i2caddr, i2creg, data_on, data) + + if active_low is None and state: return state + + except Exception as ex: + self.log_error(ex) + + if active_low and not state: return True + if not active_low and state: return True + return False + + def gpio_i2c_write(self, output, state, queue_id=None): + try: + i2cbus = self.to_int(output['gpio_i2c_bus']) + i2caddr = self.to_int(output['gpio_i2c_address']) + i2creg = self.to_int(output['gpio_i2c_register']) + data_on = self.to_int(output['gpio_i2c_data_on']) + data_off = self.to_int(output['gpio_i2c_data_off']) + + with SMBus(i2cbus) as bus: + data = [] + if state: + data.append(data_on) + else: + data.append(data_off) + + bus.write_i2c_block_data(i2caddr, i2creg, data) + + if queue_id is not None: + self._logger.debug("Running scheduled queue id %s", queue_id) + self._logger.debug("Writing on GPIO (i2c): %s/%s value %s", output['gpio_i2c_address'], output['gpio_i2c_register'], state) + 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 i2c address {2}, reg {3}. Arguments:\n{4!r}" + message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, output['gpio_i2c_address'], output['gpio_i2c_register'], ex.args) + self._logger.warn(message) + pass def send_neopixel_command(self, led_pin, led_count, led_brightness, red, green, blue, address, neopixel_dirrect, @@ -720,10 +821,13 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP index_id = self.to_int(output['index_id']) if output['output_type'] == 'regular': - if first_run: - current_value = False + if output['gpio_i2c_enabled']: + current_value = self.gpio_i2c_input(output) else: - current_value = (not GPIO.input(gpio_pin)) if output['active_low'] else GPIO.input(gpio_pin) + 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']) @@ -731,12 +835,18 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP time_delay = self.to_int(output['toggle_timer_on']) if not self.print_complete: - self.write_gpio(gpio_pin, not current_value) + if output['gpio_i2c_enabled']: + self.gpio_i2c_write(output, not current_value) + else: + 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) + if output['gpio_i2c_enabled']: + self.gpio_i2c_write(output, off_value) + else: + self.write_gpio(gpio_pin, off_value) self.update_ui_outputs() return @@ -810,11 +920,17 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP shutdown = output['auto_shutdown'] if output['output_type'] == 'regular': - val = GPIO.input(pin) if not output['active_low'] else (not GPIO.input(pin)) + if output['gpio_i2c_enabled']: + val = self.gpio_i2c_input(output) + else: + 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)) + if output['gpio_i2c_enabled']: + val = self.gpio_i2c_input(output) + else: + 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': @@ -876,6 +992,12 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP elif sensor['temp_sensor_type'] == "mcp9808": temp = self.read_mcp_temp(sensor['temp_sensor_address']) hum = 0 + elif sensor['temp_sensor_type'] == "temp_raw_i2c": + temp = self.read_raw_i2c_temp(sensor) + hum = 0 + elif sensor['temp_sensor_type'] == "hum_raw_i2c": + temp = 0 + hum = self.read_raw_i2c_temp(sensor) else: self._logger.info("temp_sensor_type no match") temp = None @@ -901,8 +1023,12 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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 temperature_alarm['controlled_io_set_value'] == 'low' else GPIO.HIGH - self.write_gpio(self.to_int(rpi_controlled_output['gpio_pin']), val) + if rpi_controlled_output['gpio_i2c_enabled']: + val = False if temperature_alarm['controlled_io_set_value'] == 'low' else True + self.gpio_i2c_write(rpi_controlled_output, val) + else: + val = GPIO.LOW if temperature_alarm['controlled_io_set_value'] == '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[ @@ -920,6 +1046,28 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP return return_value, return_value + def read_raw_i2c_temp(self, sensor): + try: + i2cbus = self.to_int(sensor['temp_i2c_bus']) + i2caddr = self.to_int(sensor['temp_i2c_address']) + i2creg = self.to_int(sensor['temp_i2c_register']) + + with SMBus(i2cbus) as bus: + data = bus.read_i2c_block_data(i2caddr, i2creg, 4) + val = struct.unpack('f', bytearray(data)) + fval = val[0] + + self._logger.debug("read_raw_i2c_temp(i2cbus=%s, i2caddr=%s, i2creg=%s) data == %s (%s)", + i2cbus, i2caddr, i2creg, data, fval) + + return str(fval) + + except Exception as ex: + template = "An exception of type {0} occurred on {1} when reading on i2c address {2}, reg {3}. Arguments:\n{4!r}" + message = template.format(type(ex).__name__, inspect.currentframe().f_code.co_name, i2caddr, i2creg, ex.args) + self._logger.warn(message) + return str(-1) + def read_mcp_temp(self, address): try: script = os.path.dirname(os.path.realpath(__file__)) + "/mcp9808.py" @@ -1173,7 +1321,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if temp_hum_control['gpio_i2c_enabled']: + self.gpio_i2c_write(temp_hum_control, val) + else: + 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: @@ -1184,7 +1335,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if temp_hum_control['gpio_i2c_enabled']: + self.gpio_i2c_write(temp_hum_control, val) + else: + 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 @@ -1202,8 +1356,9 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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', + lambda item: (item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ + 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct') and + item['gpio_i2c_enabled'] == False, self.rpi_outputs)) inputs = list(filter(lambda item: item['input_type'] == 'gpio', self.rpi_inputs)) gpios = outputs + inputs @@ -1227,8 +1382,9 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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', + lambda item: (item['output_type'] == 'regular' or item['output_type'] == 'pwm' or item[ + 'output_type'] == 'temp_hum_control' or item['output_type'] == 'neopixel_direct') and + item['gpio_i2c_enabled'] == False, self.rpi_outputs)): gpio_pin = self.to_int(gpio_out['gpio_pin']) if gpio_pin not in self.rpi_outputs_not_changed: @@ -1260,7 +1416,8 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP try: for gpio_out in list( - filter(lambda item: item['output_type'] == 'regular' or item['output_type'] == 'temp_hum_control', + filter(lambda item: (item['output_type'] == 'regular' or item['output_type'] == 'temp_hum_control') and + item['gpio_i2c_enabled'] == False, self.rpi_outputs)): initial_value = GPIO.HIGH if gpio_out['active_low'] else GPIO.LOW pin = self.to_int(gpio_out['gpio_pin']) @@ -1387,8 +1544,12 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if rpi_output['gpio_i2c_enabled']: + val = False if rpi_input['controlled_io_set_value'] == 'low' else True + self.gpio_i2c_write(rpi_output, val) + 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) except Exception as ex: self.log_error(ex) pass @@ -1420,7 +1581,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if rpi_output['gpio_i2c_enabled']: + self.gpio_i2c_write(rpi_output, val) + else: + 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( @@ -1685,7 +1849,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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['gpio_i2c_enabled']: + self.gpio_i2c_write(rpi_output, value) + else: + 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']: @@ -1788,8 +1955,12 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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]) + if rpi_output['gpio_i2c_enabled']: + thread = threading.Timer(delay_seconds, self.gpio_i2c_write, + args=[rpi_output, value, queue_id]) + else: + 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)) @@ -1904,7 +2075,10 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP 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) + if output['gpio_i2c_enabled']: + self.gpio_i2c_write(output, value) + else: + 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': diff --git a/octoprint_enclosure/static/js/enclosure.js b/octoprint_enclosure/static/js/enclosure.js index e9c4847..048b45e 100644 --- a/octoprint_enclosure/static/js/enclosure.js +++ b/octoprint_enclosure/static/js/enclosure.js @@ -421,7 +421,14 @@ $(function () { ledstrip_gpio_dat: ko.observable(""), microcontroller_address: ko.observable(0), gcode: ko.observable(""), - show_on_navbar: ko.observable(false) + show_on_navbar: ko.observable(false), + gpio_i2c_enabled: ko.observable(false), + gpio_i2c_bus: ko.observable(1), + gpio_i2c_address: ko.observable(1), + gpio_i2c_register: ko.observable(1), + gpio_i2c_data_on: ko.observable(1), + gpio_i2c_data_off: ko.observable(0), + gpio_i2c_register_status: ko.observable(1) }); }; @@ -456,7 +463,10 @@ $(function () { temp_sensor_navbar: ko.observable(true), filament_sensor_timeout: ko.observable(120), filament_sensor_enabled: ko.observable(true), - temp_sensor_i2cbus: ko.observable(1) + temp_sensor_i2cbus: ko.observable(1), + temp_i2c_bus: ko.observable(1), + temp_i2c_address: ko.observable(1), + temp_i2c_register: ko.observable(1) }); }; diff --git a/octoprint_enclosure/templates/enclosure_settings.jinja2 b/octoprint_enclosure/templates/enclosure_settings.jinja2 index a30ca69..01ddaed 100644 --- a/octoprint_enclosure/templates/enclosure_settings.jinja2 +++ b/octoprint_enclosure/templates/enclosure_settings.jinja2 @@ -91,7 +91,7 @@ - +
@@ -263,6 +263,69 @@
+ +
+
+ + Active low means that the GPIO will turn on when receive a low signal (ground) from Raspberry PI +
+
+ + + +
+ +
+ + This value should remain 1 unless you've used dtoverlay=i2c-gpio or you are using another bus. + +
+
+ +
+ +
+ + I2C address in HEX value, you can find it by running + i2cdetect -y 1 on your Raspberry Pi +
+
+ +
+ +
+ + I2C register in HEX value +
+
+ +
+ +
+ + I2C data to write in HEX value when ON +
+
+ +
+ +
+ + I2C data to write in HEX value when OFF +
+
+ +
+ +
+ + I2C register for reading gpio state in HEX value +
+
+ +
@@ -540,6 +603,8 @@ + + Attention You need to install and configure the necessary libraries for the temperature sensor, check @@ -547,6 +612,35 @@ github page
+ + +
+ +
+ + This value should remain 1 unless you've used dtoverlay=i2c-gpio or you are using another bus. + +
+
+ +
+ +
+ + I2C address in HEX value, you can find it by running + i2cdetect -y 1 on your Raspberry Pi +
+
+ +
+ +
+ + I2C register in HEX value +
+
+ +
@@ -614,7 +708,7 @@
- +
diff --git a/setup.py b/setup.py index 3dd24a9..bcf8339 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ plugin_url = "https://github.com/vitormhenrique/OctoPrint-Enclosure" plugin_license = "AGPLv3" # Any additional requirements besides OctoPrint should be listed here -plugin_requires = ["RPi.GPIO>=0.6.5","requests>=2.7"] +plugin_requires = ["RPi.GPIO>=0.6.5","requests>=2.7","smbus2>=0.3.0"] additional_setup_parameters = {} From 330d70e531b7dc26a04468b7eff2d95d1b3ac6a2 Mon Sep 17 00:00:00 2001 From: Vitor de Miranda Henrique Date: Thu, 18 Mar 2021 21:52:01 -0500 Subject: [PATCH 7/7] Update README.md --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e7e67fd..0224ec7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# QUICK NOTE: I suffered an injury last Sunday (06/20/20) and broke both bones on my left forearm, it will be impossible to work on on this plugin in the near future, I'm struggling to make it work with a full time job + masters in computers science. This plugin is not abandoned but it is on vacation mode. + + Find the plugin useful? Buy me a coffee [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/VitorHenrique/2) @@ -32,7 +35,7 @@ Check pictures on thingiverse: http://www.thingiverse.com/thing:2245493 Install the plugin using the Plugin Manager bundled with OctoPrint, you can search for the Enclosure plugin or just use the url: https://github.com/vitormhenrique/OctoPrint-Enclosure/archive/master.zip. -To control the enclosure temperature or get temperature triggered events, you need to install and configure a temperature sensor. This plugin can support DHT11, DHT22, AM2302, DS18B20, SI7021, BME280 and TMP102 temperature sensors. +To control the encosure temperature or get temperature trigged events, you need to install and configure a temperature sensor. This plugin can support DHT11, DHT22, AM2302, DS18B20, SI7021, BME280 and TMP102 temperature sensors. * For the DHT11, DHT22 and AM2302 follow this steps: @@ -170,7 +173,7 @@ Outputs can be set to the following types: * Regular GPIO * PWM GPIO -* Neopixel Control via Microcontroller +* Neopixel Control via Microcontroler * Neopixel Control directly from raspberry pi * Temperature and Humidity Control * Temperature Alarm @@ -191,7 +194,7 @@ Temperature Sensors will be used to input temperature and humidity data, they ca GPIO inputs will trigger events for the plugin, this feature can be used to add buttons to the enclosure and cause pressing those buttons to act on the printer or other pre-configured outputs. -After selecting GPIO for the input type, and selecting output control on the action type, the button will be able to turn on / off or toggle linked regular outputs, basically being able to control your lights / fan using mechanical buttons instead of the octoprint interface. You can also use buttons to send g-code commands. +After selecting GPIO for the input type, and selecting output control on the action type, the button will be able to turn on / off or toggle linked regular_gpio outputs, basically being able to control your lights / fan using mechanical buttons instead of the octoprint interface. You can also use buttons to send g-code commands. Selecting print control on the action type will trigger printer actions when the configured GPIO receives a signal. The actions can be Resume and Pause a print job or Change Filament. You can use the "change filament" action and set up the input GPIO according to your filament sensor, for example, if your filament sensor connects to ground when detects the end of the filament, you should choose PULL UP resistors and detect the event on the falling edge. You can also add mechanical buttons to pause, resume and change filaments near your printer for convenience.