Skip to content

Commit

Permalink
chore: ETA now respects chosen ETA-Sources
Browse files Browse the repository at this point in the history
  • Loading branch information
Clon1998 committed Jul 16, 2024
1 parent b46be5f commit 563d15d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 56 deletions.
102 changes: 54 additions & 48 deletions mobileraker/data/dtos/moonraker/printer_snapshot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime, timedelta
from typing import Dict, Optional
from typing import Dict, List, Optional
import math
from dateutil import tz

Expand Down Expand Up @@ -47,6 +47,59 @@ def __eq__(self, other):
and self.filament_sensors == other.filament_sensors
)

def remaining_time_avg(self, sources: List[str]) -> Optional[int]:
"""
Calculate the average of remaining times from different criteria based on the provided sources.
Args:
sources (List[str]): List of sources to calculate the average from. Possible values are 'file', 'filament', 'slicer'.
Returns:
Optional[int]: Average remaining time in seconds, or None if calculation is not possible.
"""
remaining = 0
cnt = 0

r_file = self.remaining_time_by_file or 0
if 'file' in sources and r_file > 0:
remaining += r_file
cnt += 1

r_filament = self.remaining_time_by_filament or 0
if 'filament' in sources and r_filament > 0:
remaining += r_filament
cnt += 1

r_slicer = self.remaining_time_by_slicer or 0
if 'slicer' in sources and r_slicer > 0:
remaining += r_slicer
cnt += 1

if cnt == 0:
return None

return remaining // cnt

def remaining_time_formatted(self, sources: List[str]) -> Optional[str]:
sec = self.remaining_time_avg(sources)
if sec:
return str(timedelta(seconds=sec))[:-3] # remove the seconds part


def calc_eta(self, sources: List[str]) -> Optional[datetime]:
remaining = self.remaining_time_avg(sources)
if remaining:
now = datetime.now()
return now + timedelta(seconds=remaining)

def calc_eta_seconds_utc(self, sources: List[str]) -> Optional[int]:
eta = self.calc_eta(sources)
return int(eta.astimezone(tz.UTC).timestamp()) if eta else None

@property
def eta_available(self) -> bool:
return self.remaining_time_avg(['file', 'filament', 'slicer']) is not None

@property
def remaining_time_by_file(self) -> Optional[int]:
"""
Expand Down Expand Up @@ -96,37 +149,6 @@ def remaining_time_by_slicer(self) -> Optional[int]:
return None
return int((slicer_estimate - print_duration))

@property
def remaining_time_avg(self) -> Optional[int]:
"""
Calculate the average of remaining times from different criteria.
Returns:
Optional[int]: Average remaining time in seconds, or None if calculation is not possible.
"""
remaining = 0
cnt = 0

r_file = self.remaining_time_by_file or 0
if r_file > 0:
remaining += r_file
cnt += 1

r_filament = self.remaining_time_by_filament or 0
if r_filament > 0:
remaining += r_filament
cnt += 1

r_slicer = self.remaining_time_by_slicer or 0
if r_slicer > 0:
remaining += r_slicer
cnt += 1

if cnt == 0:
return None

return remaining // cnt

@property
def print_progress_by_fileposition_relative(self) -> Optional[float]:
"""
Expand Down Expand Up @@ -157,23 +179,7 @@ def print_progress_by_fileposition_relative(self) -> Optional[float]:

return self.virtual_sdcard.progress if self.virtual_sdcard else None

@property
def remaining_time_formatted(self) -> Optional[str]:
sec = self.remaining_time_avg
if sec:
return str(timedelta(seconds=sec))[:-3] # remove the seconds part

@property
def eta(self) -> Optional[datetime]:
remaining = self.remaining_time_avg
if remaining:
now = datetime.now()
return now + timedelta(seconds=remaining)

@property
def eta_seconds_utc(self) -> Optional[int]:
return int(self.eta.astimezone(
tz.UTC).timestamp()) if self.eta else None


@property
Expand Down
16 changes: 10 additions & 6 deletions mobileraker/mobileraker_companion.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def _fulfills_evaluation_threshold(self, snapshot: PrinterSnapshot) -> bool:
return True


if self._last_snapshot.eta is None and snapshot.eta is not None:
if not self._last_snapshot.eta_available and snapshot.eta_available:
self._logger.info('ETA is available. Evaluating!')
return True

Expand Down Expand Up @@ -459,13 +459,17 @@ def _live_activity_update(self, cfg: DeviceNotificationEntry, cur_snap: PrinterS
# Calculate the eta delta based on the estimated time of the current file or 15 minutes (whichever is higher)
eta_delta = max(15, cur_snap.eta_window) if cur_snap.eta_window is not None else 15

etaUpdate = self._last_snapshot is not None and \
self._last_snapshot.eta is not None and cur_snap.eta is not None and \
abs((self._last_snapshot.eta - cur_snap.eta).seconds) > eta_delta
last_remaining_time = self._last_snapshot.remaining_time_avg(cfg.settings.eta_sources) if self._last_snapshot is not None else None
cur_remaining_time = cur_snap.remaining_time_avg(cfg.settings.eta_sources)


eta_update = last_remaining_time is not None and cur_remaining_time is not None and \
abs(last_remaining_time - cur_remaining_time) > eta_delta

# The live activity can be updted more frequent. Max however in 5 percent steps or if there was a state change
if not normalized_progress_interval_reached(cfg.snap.progress_live_activity, cur_snap.progress, self.remote_config.increments) and cfg.snap.state == cur_snap.print_state and not etaUpdate:
if not normalized_progress_interval_reached(cfg.snap.progress_live_activity, cur_snap.progress, self.remote_config.increments) and cfg.snap.state == cur_snap.print_state and not eta_update:
return None


self._logger.info(
'LiveActivityUpdate passed'
Expand All @@ -474,7 +478,7 @@ def _live_activity_update(self, cfg: DeviceNotificationEntry, cur_snap: PrinterS
# Set the last apns message time to now
self._last_apns_message = time.monotonic_ns()

return LiveActivityContentDto(cfg.apns.liveActivity, cur_snap.progress, cur_snap.eta_seconds_utc, "update" if cur_snap.print_state in [
return LiveActivityContentDto(cfg.apns.liveActivity, cur_snap.progress, cur_snap.calc_eta_seconds_utc(cfg.settings.eta_sources), "update" if cur_snap.print_state in [
"printing", "paused"] else "end")

def _custom_notification(self, cfg: DeviceNotificationEntry, cur_snap: PrinterSnapshot, is_m117: bool) -> Optional[NotificationContentDto]:
Expand Down
7 changes: 5 additions & 2 deletions mobileraker/util/notification_placeholders.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@ def replace_placeholders(input: str, cfg: DeviceNotificationEntry, snap: Printer
Returns:
str: The input string with placeholders replaced by their corresponding values.
"""
eta = snap.eta
eta_source = cfg.settings.eta_sources

eta = snap.calc_eta(eta_source)
if eta is not None:
eta = eta.astimezone(companion_config.timezone)

progress = snap.print_progress_by_fileposition_relative if snap.print_state == 'printing' else None
remaining_time_avg = snap.remaining_time_avg(eta_source)

data = {
'printer_name': cfg.machine_name,
'progress': f'{progress:.0%}' if progress is not None else None,
'file': snap.filename if snap.filename is not None else 'UNKNOWN',
'eta': eta_formatted(eta, companion_config.eta_format),
'a_eta': adaptive_eta_formatted(eta, companion_config.eta_format),
'remaining_avg': snap.remaining_time_avg if snap.remaining_time_avg else '--:--',
'remaining_avg': remaining_time_avg if remaining_time_avg else '--:--',
'remaining_file': snap.remaining_time_by_file if snap.remaining_time_by_file else '--:--',
'remaining_filament': snap.remaining_time_by_filament if snap.remaining_time_by_filament else '--:--',
'remaining_slicer': snap.remaining_time_by_slicer if snap.remaining_time_by_slicer else '--:--',
Expand Down

0 comments on commit 563d15d

Please sign in to comment.