diff --git a/octoprint_smart_filament_sensor/__init__.py b/octoprint_smart_filament_sensor/__init__.py index c99632a..763cc5d 100644 --- a/octoprint_smart_filament_sensor/__init__.py +++ b/octoprint_smart_filament_sensor/__init__.py @@ -1,7 +1,7 @@ # coding=utf-8 from __future__ import absolute_import import octoprint.plugin -from octoprint.events import Events +from octoprint.events import Events, eventManager import RPi.GPIO as GPIO from time import sleep from datetime import datetime @@ -10,12 +10,20 @@ from octoprint_smart_filament_sensor.filament_motion_sensor_timeout_detection import FilamentMotionSensorTimeoutDetection from octoprint_smart_filament_sensor.data import SmartFilamentSensorDetectionData +PLUGIN_KEY_PREFIX = "SmartFilamentSensor_" + +EVENT_KEY_MOVEMENT = "Movement" +EVENT_KEY_FILAMENT_CHANGE = "FilamentChange" +EVENT_KEY_PAUSE_IGNORED = "PauseIgnored" +EVENT_KEY_REMAIN_RATIO = "RemainRatio" + class SmartFilamentSensor(octoprint.plugin.StartupPlugin, octoprint.plugin.EventHandlerPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.SimpleApiPlugin): + _last_event_publish_data = dict() def initialize(self): self._logger.info("Running RPi.GPIO version '{0}'".format(GPIO.VERSION)) @@ -25,6 +33,8 @@ def initialize(self): self.print_started = False self.last_movement_time = None + self._last_event_publish_data = dict() + self._last_event_publish_time = None self.lastE = -1 self.currentE = -1 self.START_DISTANCE_OFFSET = 7 @@ -52,11 +62,29 @@ def motion_sensor_enabled(self): def pause_command(self): return self._settings.get(["pause_command"]) + @property + def skip_pause_while_moving(self): + return self._settings.get_boolean(["skip_pause_while_moving"]) + #Distance detection @property def motion_sensor_detection_distance(self): return int(self._settings.get(["motion_sensor_detection_distance"])) +#Event Publishing + + @property + def enable_event_publishing(self): + return self._settings.get_boolean(["enable_event_publishing"]) + + @property + def enable_publish_remaining_ratio(self): + return self._settings.get_boolean(["enable_publish_remaining_ratio"]) + + @property + def event_publish_interval_remaining_ratio(self): + return int(self._settings.get(["event_publish_interval_remaining_ratio"])) + #Timeout detection @property def motion_sensor_max_not_moving(self): @@ -97,6 +125,7 @@ def _setup_sensor(self): if self.motion_sensor_enabled == False: self._logger.info("Motion sensor is deactivated") + self._sendDataToClient(EVENT_KEY_MOVEMENT, dict(movement_detected=False), skip_check=True) self._data.filament_moving = False self.motion_sensor_thread = None @@ -117,6 +146,10 @@ def get_settings_defaults(self): motion_sensor_enabled = True, #Sensor detection is enabled by default motion_sensor_pin=-1, # Default is no pin detection_method = 0, # 0 = timeout detection, 1 = distance detection + enable_event_publishing = True, + enable_publish_remaining_ratio = True, + event_publish_interval_remaining_ratio = 5, + skip_pause_while_moving = False, # Distance detection motion_sensor_detection_distance = 15, # Recommended detection distance from Marlin would be 7 @@ -187,6 +220,10 @@ def motion_sensor_start(self): self._logger.info("Motion sensor started: Timeout detection") self.send_code = False + if self.skip_pause_while_moving: + self._sendDataToClient(EVENT_KEY_PAUSE_IGNORED, dict(pause_ignored=False)) + self._sendDataToClient(EVENT_KEY_FILAMENT_CHANGE, dict(printer_change_filament=False), skip_check=True) + self._sendDataToClient(EVENT_KEY_MOVEMENT, dict(movement_detected=True), skip_check=True) self._data.filament_moving = True # Stop the motion_sensor thread @@ -202,15 +239,18 @@ def printer_change_filament (self): # Check if stop signal was already sent if(not self.send_code): self._logger.error("Motion sensor detected no movement") + self._sendDataToClient(EVENT_KEY_MOVEMENT, dict(movement_detected=False), skip_check=True) self._logger.info("Pause command: " + self.pause_command) self._printer.commands(self.pause_command) self.send_code = True + self._sendDataToClient(EVENT_KEY_FILAMENT_CHANGE, dict(printer_change_filament=True), skip_check=True) self._data.filament_moving = False self.lastE = -1 # Set to -1 so it ignores the first test then continues # Reset the distance, if the remaining distance is smaller than the new value def reset_distance (self, pPin): self._logger.debug("Motion sensor detected movement") + self._sendDataToClient(EVENT_KEY_MOVEMENT, dict(movement_detected=True), skip_check=True) self.send_code = False self.last_movement_time = datetime.now() if(self._data.remaining_distance < self.motion_sensor_detection_distance): @@ -232,6 +272,13 @@ def reset_remainin_distance(self): def calc_distance(self, pE): if (self.detection_method == 1): + if self.enable_publish_remaining_ratio: + try: + remain_ratio = min((int((self._data.remaining_distance / self.motion_sensor_detection_distance) * 1e4) / 1e4), 1.0) + self._sendDataToClient(EVENT_KEY_REMAIN_RATIO, dict(remaining_ratio=remain_ratio)) + except Exception as e: + self._logger.error(f"Error in publish ratio: {e}") + # First check if need continue after last move if(self._data.remaining_distance > 0): @@ -267,6 +314,10 @@ def calc_distance(self, pE): ) self._data.remaining_distance = (self._data.remaining_distance - deltaDistance) + elif self._data.filament_moving and self.skip_pause_while_moving: + self._sendDataToClient(EVENT_KEY_PAUSE_IGNORED, dict(pause_ignored=True)) + self._logger.debug("Ignored pause command due to filament moving") + self.reset_distance(None) else: # Only pause the print if its been over 5 seconds since the last movement. Stops pausing when the CPU gets hung up. if (datetime.now() - self.last_movement_time).total_seconds() > 10: @@ -274,6 +325,29 @@ def calc_distance(self, pE): else: self._logger.debug("Ignored pause command due to 5 second rule") + def _checkShouldPublish(self, event_id, dataDict, skip_check=False): + if skip_check: + return True + if self._last_event_publish_data != dataDict: + if event_id != EVENT_KEY_REMAIN_RATIO: + return True + if not self._last_event_publish_time: + return True + last_publish_diff = (datetime.now() - self._last_event_publish_time).total_seconds() + if last_publish_diff > self.event_publish_interval_remaining_ratio: + return True + return False + + def _sendDataToClient(self, event_id, dataDict, skip_check=False): + if self.enable_event_publishing: + if self._checkShouldPublish(event_id, dataDict, skip_check): + eventKey = PLUGIN_KEY_PREFIX + event_id + eventManager().fire(eventKey, dataDict) + self._last_event_publish_data = dataDict + self._last_event_publish_time = datetime.now() + else: + self._logger.debug("Ignoring send data, duplicated dict:" + str(dataDict)) + def updateToUi(self): self._plugin_manager.send_plugin_message(self._identifier, self._data.toJSON()) diff --git a/octoprint_smart_filament_sensor/templates/smartfilamentsensor_settings.jinja2 b/octoprint_smart_filament_sensor/templates/smartfilamentsensor_settings.jinja2 index ab252cf..14567dd 100644 --- a/octoprint_smart_filament_sensor/templates/smartfilamentsensor_settings.jinja2 +++ b/octoprint_smart_filament_sensor/templates/smartfilamentsensor_settings.jinja2 @@ -81,6 +81,45 @@ GCode commands that are sent to the printer are interpreted. Don't choose this value too small, because it could make the detection too sensitive. + +