[PR #218] [CLOSED] Consolidate heart rate and cycling power devices into unified WorkoutDevice #221

Closed
opened 2026-03-02 04:00:48 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/eerimoq/moblin/pull/218
Author: @Copilot
Created: 2/8/2026
Status: Closed

Base: mainHead: copilot/integrate-cycling-power-protocol


📝 Commits (10+)

  • 892f15c Initial plan
  • 5a75648 Add WorkoutDevice integration with both heart rate and cycling power protocols
  • ec28ef0 Add workout device UI, settings, and status display integration
  • 146f22f Add workout device to local overlays and widget text settings
  • 7ed992d Fix typo: averageIngoreZeros -> averageIgnoreZeros
  • d73d21f Remove heartRateDeviceStatus in favor of workoutDeviceStatus
  • 851b525 Remove cycling power and heart rate device states and UI components
  • 5bcef51 Delete old cycling power and heart rate device files and settings classes
  • 8b831a3 Encapsulate cycling power specific state in CyclingPowerState class
  • 8cfcc59 Split workout device into separate heart rate and cycling power files, and change icon to figure.walk.motion

📊 Changes

17 files changed (+565 additions, -513 deletions)

View changed files

Moblin/Integrations/WorkoutDevice/WorkoutDevice.swift (+194 -0)
Moblin/Integrations/WorkoutDevice/WorkoutDeviceCyclingPower.swift (+203 -0)
Moblin/Integrations/WorkoutDevice/WorkoutDeviceHeartRate.swift (+22 -0)
📝 Moblin/Various/Model/Model.swift (+9 -20)
Moblin/Various/Model/ModelCyclingPowerDevice.swift (+0 -75)
Moblin/Various/Model/ModelHeartRateDevice.swift (+0 -78)
Moblin/Various/Model/ModelWorkoutDevice.swift (+85 -0)
📝 Moblin/Various/Settings/Settings.swift (+12 -81)
Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDeviceScannerSettingsView.swift (+0 -46)
Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDeviceSettingsView.swift (+0 -92)
Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDevicesSettingsView.swift (+0 -38)
📝 Moblin/View/Settings/Display/LocalOverlays/LocalOverlaysSettingsView.swift (+2 -7)
📝 Moblin/View/Settings/SettingsView.swift (+2 -7)
📝 Moblin/View/Settings/WorkoutDevices/WorkoutDeviceScannerSettingsView.swift (+2 -2)
📝 Moblin/View/Settings/WorkoutDevices/WorkoutDeviceSettingsView.swift (+13 -15)
📝 Moblin/View/Settings/WorkoutDevices/WorkoutDevicesSettingsView.swift (+13 -13)
📝 Moblin/View/Stream/Overlay/StreamOverlayRightView.swift (+8 -39)

📄 Description

Summary

Merged separate HeartRateDevice and CyclingPowerDevice implementations into a single WorkoutDevice that handles both BLE protocols (heart rate service 180D, cycling power service 1818) simultaneously.

Changes

Core Implementation

  • WorkoutDevice scans for both service IDs and manages connections to devices supporting either/both protocols
  • Protocol-specific logic extracted to WorkoutDeviceHeartRate.swift (22 lines) and WorkoutDeviceCyclingPower.swift (203 lines)
  • CyclingPowerState class encapsulates power/cadence calculation with 3-sample rolling average and zero-filtering
  • Delegate pattern with callbacks: workoutDeviceHeartRate(), workoutDeviceCyclingPower(), workoutDeviceState()
protocol WorkoutDeviceDelegate: AnyObject {
    func workoutDeviceState(_ device: WorkoutDevice, state: WorkoutDeviceState)
    func workoutDeviceHeartRate(_ device: WorkoutDevice, heartRate: Int)
    func workoutDeviceCyclingPower(_ device: WorkoutDevice, power: Int, cadence: Int)
}

Settings Migration

  • Reused heartRateDevices coding key for workoutDevices property to maintain backward compatibility
  • Old settings files decode seamlessly without migration code
  • Removed redundant cyclingPowerDevices and workoutDevices coding keys

Cleanup

  • Removed 10 files (649 lines): separate settings views, model extensions, device-specific status components
  • Unified state: workoutDeviceStatus/workoutDeviceState replaces separate heart rate and cycling power state
  • UI consolidated to single "Workout devices" menu with figure.walk.motion icon

Data Flow

WorkoutDevice → WorkoutDeviceDelegate
  ├─ workoutDeviceHeartRate() → model.heartRates[deviceName]
  └─ workoutDeviceCyclingPower() → model.cyclingPower, model.cyclingCadence

Old integration files (CyclingPowerDevice.swift, HeartRateDevice.swift) remain but are unreferenced.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/eerimoq/moblin/pull/218 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 2/8/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `copilot/integrate-cycling-power-protocol` --- ### 📝 Commits (10+) - [`892f15c`](https://github.com/eerimoq/moblin/commit/892f15cdcad50e24698ecf55237ca6f5e799a107) Initial plan - [`5a75648`](https://github.com/eerimoq/moblin/commit/5a756484363db704b74c1471dfbc3bc56acd97b3) Add WorkoutDevice integration with both heart rate and cycling power protocols - [`ec28ef0`](https://github.com/eerimoq/moblin/commit/ec28ef07dfdf44966efe894a5da9c9bb7a558f70) Add workout device UI, settings, and status display integration - [`146f22f`](https://github.com/eerimoq/moblin/commit/146f22f7e141d69ad56773f72db603cc6b4a0763) Add workout device to local overlays and widget text settings - [`7ed992d`](https://github.com/eerimoq/moblin/commit/7ed992de2b1f7ccbf108bb9a04c29cca69ef162c) Fix typo: averageIngoreZeros -> averageIgnoreZeros - [`d73d21f`](https://github.com/eerimoq/moblin/commit/d73d21fce59f95f4ba39cbf8bdffa6889456c30d) Remove heartRateDeviceStatus in favor of workoutDeviceStatus - [`851b525`](https://github.com/eerimoq/moblin/commit/851b525cddabadc49cf8e84ca962c848f52f7868) Remove cycling power and heart rate device states and UI components - [`5bcef51`](https://github.com/eerimoq/moblin/commit/5bcef517ac9ea4d0cfac552ea1a4ff93c351502f) Delete old cycling power and heart rate device files and settings classes - [`8b831a3`](https://github.com/eerimoq/moblin/commit/8b831a318db1300bbe30e1e678adf18868e8849a) Encapsulate cycling power specific state in CyclingPowerState class - [`8cfcc59`](https://github.com/eerimoq/moblin/commit/8cfcc59ca98de6b7b8a84bd94bc18f8a456329ab) Split workout device into separate heart rate and cycling power files, and change icon to figure.walk.motion ### 📊 Changes **17 files changed** (+565 additions, -513 deletions) <details> <summary>View changed files</summary> ➕ `Moblin/Integrations/WorkoutDevice/WorkoutDevice.swift` (+194 -0) ➕ `Moblin/Integrations/WorkoutDevice/WorkoutDeviceCyclingPower.swift` (+203 -0) ➕ `Moblin/Integrations/WorkoutDevice/WorkoutDeviceHeartRate.swift` (+22 -0) 📝 `Moblin/Various/Model/Model.swift` (+9 -20) ➖ `Moblin/Various/Model/ModelCyclingPowerDevice.swift` (+0 -75) ➖ `Moblin/Various/Model/ModelHeartRateDevice.swift` (+0 -78) ➕ `Moblin/Various/Model/ModelWorkoutDevice.swift` (+85 -0) 📝 `Moblin/Various/Settings/Settings.swift` (+12 -81) ➖ `Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDeviceScannerSettingsView.swift` (+0 -46) ➖ `Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDeviceSettingsView.swift` (+0 -92) ➖ `Moblin/View/Settings/CyclingPowerDevices/CyclingPowerDevicesSettingsView.swift` (+0 -38) 📝 `Moblin/View/Settings/Display/LocalOverlays/LocalOverlaysSettingsView.swift` (+2 -7) 📝 `Moblin/View/Settings/SettingsView.swift` (+2 -7) 📝 `Moblin/View/Settings/WorkoutDevices/WorkoutDeviceScannerSettingsView.swift` (+2 -2) 📝 `Moblin/View/Settings/WorkoutDevices/WorkoutDeviceSettingsView.swift` (+13 -15) 📝 `Moblin/View/Settings/WorkoutDevices/WorkoutDevicesSettingsView.swift` (+13 -13) 📝 `Moblin/View/Stream/Overlay/StreamOverlayRightView.swift` (+8 -39) </details> ### 📄 Description ## Summary Merged separate `HeartRateDevice` and `CyclingPowerDevice` implementations into a single `WorkoutDevice` that handles both BLE protocols (heart rate service 180D, cycling power service 1818) simultaneously. ## Changes ### Core Implementation - **WorkoutDevice** scans for both service IDs and manages connections to devices supporting either/both protocols - Protocol-specific logic extracted to `WorkoutDeviceHeartRate.swift` (22 lines) and `WorkoutDeviceCyclingPower.swift` (203 lines) - `CyclingPowerState` class encapsulates power/cadence calculation with 3-sample rolling average and zero-filtering - Delegate pattern with callbacks: `workoutDeviceHeartRate()`, `workoutDeviceCyclingPower()`, `workoutDeviceState()` ```swift protocol WorkoutDeviceDelegate: AnyObject { func workoutDeviceState(_ device: WorkoutDevice, state: WorkoutDeviceState) func workoutDeviceHeartRate(_ device: WorkoutDevice, heartRate: Int) func workoutDeviceCyclingPower(_ device: WorkoutDevice, power: Int, cadence: Int) } ``` ### Settings Migration - **Reused `heartRateDevices` coding key** for `workoutDevices` property to maintain backward compatibility - Old settings files decode seamlessly without migration code - Removed redundant `cyclingPowerDevices` and `workoutDevices` coding keys ### Cleanup - Removed 10 files (649 lines): separate settings views, model extensions, device-specific status components - Unified state: `workoutDeviceStatus`/`workoutDeviceState` replaces separate heart rate and cycling power state - UI consolidated to single "Workout devices" menu with `figure.walk.motion` icon ### Data Flow ``` WorkoutDevice → WorkoutDeviceDelegate ├─ workoutDeviceHeartRate() → model.heartRates[deviceName] └─ workoutDeviceCyclingPower() → model.cyclingPower, model.cyclingCadence ``` Old integration files (`CyclingPowerDevice.swift`, `HeartRateDevice.swift`) remain but are unreferenced. <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem closed this issue 2026-03-02 04:00:48 +03:00
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/moblin#221
No description provided.