class BikeRoutine(): def __init__(self, gear, sensor_factory: Callable[[], BikeSensor]) -> None: self.initialise() self.gear = gear self.timer_thread = None self.timer_thread_factory = lambda: TimerThread(self) self.on_update: Callable[[dict], None] = None self.on_idle_cb: Callable[[bool], None] = None self.on_duration_update: Callable[[int], None] = None self.sensor_factory = sensor_factory self.sensor = None self.heart_rate_updated = None self.data_points = [] self.firebase = Firebase() def start_timer(self): self.stop_timer() self.timer_thread = self.timer_thread_factory() self.timer_thread.start() def stop_timer(self): if self.timer_thread: self.timer_thread.stop = True def initialise(self): self.paused = False self.start_time = -1 self.revolutions = 0 self.total_time = 0 self.duration = 0 self.heart_rate = 0 def on_idle(self, is_idle): if is_idle: self._pause() else: self.resume() if self.on_idle_cb: self.on_idle_cb(is_idle) def on_revolution(self, t: float): if not self.paused: if t == -1 or self.start_time == -1: if self.start_time == -1: self.start_timer() self.start_time = current_millis() else: self.revolutions += 1 if self.on_update: self.on_update(self._calculate_values_from_rev(t)) def _pause(self): self.firebase.push_heartrate_request(False) self.paused = True self.stop_timer() def force_pause(self): self._pause() if self.sensor != None: self.sensor.pause() def resume(self): self.firebase.push_heartrate_request(True) self.start_timer() self.paused = False if self.sensor != None: self.sensor.resume() def save_data_point(self, data_point): self.data_points.append(data_point) def _calculate_values_from_rev(self, time_in_seconds): self.total_time += time_in_seconds distance_in_metres = properties.bike.revolution_distance metres_per_seconds = distance_in_metres / time_in_seconds metres_per_seconds_history.append(metres_per_seconds) average_metres_per_seconds = mean(metres_per_seconds_history) total_average_speed = (self.revolutions * distance_in_metres) / self.total_time x = { "last_rev_time": time_in_seconds, "total_revs": self.revolutions, "distance": self.revolutions * distance_in_metres / 1000, "rpm": 60 / time_in_seconds, "current_speed": average_metres_per_seconds * 3.6, "total_average_speed": total_average_speed * 3.6, "gear": self.gear, "duration": self.duration, "time": current_millis() * 1000000 } if self.heart_rate_updated and self.heart_rate > 0: x['heartrate'] = self.heart_rate self.heart_rate = 0 print_debug(x) self.data_points.append(x) return x def on_bike_ride_started(self): self.start_time = current_millis() def publish_heartrate(self, heartrate): print_debug(f'Heartrate retrieved: {heartrate}') self.heart_rate = heartrate self.heart_rate_updated = current_millis() def stop(self): self.firebase.push_heartrate_request(False) data_points = self.data_points Thread( target=self.firebase.write_workout, args=(data_points, ), daemon=True, ).start() self.stop_timer() if self.sensor: self.sensor.stop() def start(self): self.initialise() if self.sensor: self.sensor.recording = False self.sensor = self.sensor_factory() self.sensor.on_revolution = self.on_revolution self.sensor.on_idle = self.on_idle self.firebase.push_heartrate_request(True) self.sensor.start()