Skip to content

Instantly share code, notes, and snippets.

@Intyre
Last active August 3, 2023 11:34
Show Gist options
  • Save Intyre/05957b64b3fb1306361ca9e79ca01ac1 to your computer and use it in GitHub Desktop.
Save Intyre/05957b64b3fb1306361ca9e79ca01ac1 to your computer and use it in GitHub Desktop.
Wahoo: bolt.cfg and comp.cfg reader from files in elemnt_config.zip

Config backup

The .zip contains bolt-65535.cfg, comp.cfg poi.cfg and routes.cfg

Create a directory with the name config_backup on the device, a elemnt_config.zip should appear. If not then reboot the device.

How to connect an ELEMNT, BOLT, or ROAM to a desktop or laptop computer

Create

# create special directory
adb shell mkdir /sdcard/config_backup

# save to pc
adb pull /sdcard/config_backup/elemnt_config.zip .

Restore

# create special directory
adb shell mkdir /sdcard/config_restore

# save to pc
adb push elemnt_config.zip /sdcard/config_restore/elemnt_config.zip

# reboot device
adb shell reboot
#!/usr/bin/python
import sys
boltFieldNames = [
"BACKLIGHT", "AUTO_UPLOAD_ENABLED", "BOLT_BATTERY", "INVALID",
"LED_MODE", "TOP_LED_WORKOUT", "TOP_LED_NOTIF", "TOP_LED_NAV",
"BUZZ_WORKOUT", "BUZZ_NOTIF", "BUZZ_NAV", "BUZZ_MARIO",
"AUTO_PAUSE_SPEED", "ALERT_PHONE", "ALERT_MSG", "ALERT_EMAIL",
"DND_END_TIME_OLD", "AUTO_SHUTDOWN_DURATION", "MUTE", "BOLTAPP_VERSION",
"KICKR_MODE_OVERRIDE_ALLOWED", "LED_MODE_OVERRIDE", "ZOOM_MIN_LEVEL",
"FIRST_RUN_STATE", "AUTO_LAP_MODE", "AUTO_LAP_DISTANCE_M", "AUTO_LAP_DURATION_SEC",
"BACKLIGHT_DURATION_SEC", "UPGRADE_STATE", "UPGRADE_DOWNLOAD_PERCENT", "DND_END_TIME",
"DND_INTERVAL", "FOLLOW_WITH_HEADING", "SEGMENTS_ENABLED", "SEGMENTS_AUTO_PAGE_CHANGE",
"SEGMENTS_NOTIF_ON_OTHER_PAGES", "SEGMENTS_LEDS", "SEGMENTS_MUTED", "WORKOUT_TYPE",
"USER_OUTDOOR_MODE", "INVALID", "PAGE_DEFN", "WIFI_NW_COUNT",
"INCLUDE_ZERO_IN_AVG_CADENCE", "PLANS_NOTIF_ON_OTHER_PAGES", "PLANS_BUZZER", "PLANS_LEDS",
"SEGMENTS_DURING_PLAN", "PLAN_AUTO_LAP_ON_INTERVAL", "INCLUDE_ZERO_IN_AVG_POWER", "CFG_PROFILE_IDS",
"CURRENT_CFG_PROFILE_ID", "CFG_PROFILE_NAME", "CFG_DISPLAY_CFG", "THEME",
"INVALID", "AUTO_UPDATE", "WATCHFACE_CFG", "POOL_LENGTH_CM",
"THEME_TINT_COLOR", "LOCATION_LAT", "LOCATION_LON", "INVALID",
"CFG_PROFILE_ID_NEXT", "GPS_OVERRIDE", "ENABLE_OPTICAL_HR", "APP_PROFILE",
"RUNNING_MODE", "RACE_LENGTH", "TRACK_LENGTH", "LAST_INTERACTION_TIME_SEC",
"POOL_LENGTH_CUSTOM_CM", "AUTO_INT_ENABLED", "POOL_LENGTH_CUSTOM_METRIC", "BOLT_BATTERY_STATUS",
"BOLT_SERIAL_NUMBER", "AUTO_RE_ROUT", "INVALID", "INVALID",
"SOUNDS_CFG", "VIBRATIONS_CFG", "DND_SCHEDULE_START_TIME_MIN", "DND_SCHEDULE_END_TIME_MIN",
"DND_CFG", "RADAR_CFG", "PMS_CFG", "PMS_SESSION_INDEX",
"BODY_POSITION", "SPECIALIZED_AUTH_KEY", "SPECIALIZED_STAGING_URL", "GPS_POS_ASSIST_VALID",
"UPGRADE_STATE_ROM", "BOLTAPP_WSM_ENABLED", "LOG_LEVEL", "RELEASE_CHANNEL",
"247_MASK", "GPS_POS_ASSIST_DATA_REQ", "WORKOUT_PAGE_COLOUR_MODE", "ENABLED_CLM_TYPES",
"BUTTON_SQUEEZE_CFG", "DISPLAY_CFG_HIDE_TITLES", "WORK_REST_CFG", "WORK_REST_AUTO_LAP_ON_INTERVAL",
"PLAN_SYNC_STATUS", "RACE_RUNNING_CFG", "WORKOUT_PINNED_PAGE_ID", "CUSTOM_ALERTS_ENABLED",
"CUSTOM_ALERTS_PLANNED_WORKOUT_ENABLED", "LEDS_MASK", "SUPERSAPIENS_ALLOWED", "SUPERSAPIENS_STATE",
"ROUTE_SYNC_STATUS", "INVALID", "INVALID", "SYNC_REQ_MASK",
"CLIMB_CFG", "CONNECTION_INFO", "ACTIVITY_FEEDBACK_ENABLED", "NEXT"
]
compFieldNames = [
"INVALID", "HEIGHT_CM", "WEIGHT_HG", "DOB",
"MALE", "METRIC_SPEED_DISTANCE", "LOCALE", "POWER_FTP",
"HR_RESTING", "HR_ZONE_1_CEIL", "HR_ZONE_2_CEIL", "HR_ZONE_3_CEIL",
"HR_ZONE_4_CEIL", "HR_MAX", "INVALID", "LIFESTYLE",
"INVALID", "TIME_FMT", "INVALID", "HR_BURN_RATE",
"HR_BURST_RATE", "INVALID", "PHONE_BATTERY", "BOLT_TIME",
"BOLT_TIME_ZONE", "SPD_ZONE_1_CEIL", "SPD_ZONE_2_CEIL", "SPD_ZONE_3_CEIL",
"SPD_ZONE_4_CEIL", "PWR_ZONE_1_CEIL", "PWR_ZONE_2_CEIL", "PWR_ZONE_3_CEIL",
"PWR_ZONE_4_CEIL", "INVALID", "USER_PROFILE", "METRIC_ELEVATION",
"METRIC_TEMPERATURE", "METRIC_WEIGHT", "PWR_ZONE_5_CEIL", "PWR_ZONE_6_CEIL",
"PWR_ZONE_7_CEIL", "INVALID", "PWR_ZONE_COUNT", "BOLT_TIME_INFO",
"LOG_LEVEL", "FIRST_DAY_OF_WEEK", "AUTO_UPLOAD_MASK", "TARGET_DAILY_STEPS",
"TARGET_WEEKLY_BIKE_DISTANCE", "TARGET_DAILY_CALORIES", "TARGET_WEEKLY_RUN_DISTANCE", "TARGET_WEEKLY_SWIM_DISTANCE",
"TARGET_WEEKLY_CALORIES", "TARGET_WEEKLY_ACTIVE_TIME","CALIBRATION_RUN", "LOCATION_LAT",
"LOCATION_LON", "PAIRED_ELEMNT_CFG", "247_WEEK_SUMMARY", "INVALID",
"GENDER", "AUTO_RIVAL_DATA_BROADCAST", "LOCATION", "INVALID",
"POWER_RUN_CRITICAL_POWER", "CLOUD_USER_ID", "CLOUD_SERVER_TYPE", "WAHOO_CLOUD_SUBSCRIPTION_STAT", "NEXT"
]
def readfile(name):
version = 0x00
profileID = 0xFFFF
dataFields = {}
timestampFields = {}
with open(name, 'rb') as f:
version = int.from_bytes(f.read(1), byteorder='little')
profileID = int.from_bytes(f.read(2), byteorder='little')
fieldCount = int.from_bytes(f.read(2), byteorder='little')
for _ in range(fieldCount):
fieldID = None
if version == 0x00:
fieldID = int.from_bytes(f.read(2), byteorder='little')
elif version == 0x01:
fieldID = int.from_bytes(f.read(4), byteorder='little')
dataSize = int.from_bytes(f.read(2), byteorder='little')
dataFields[fieldID] = f.read(dataSize)
return {
"version": version,
"profileID": profileID,
"dataFields": dataFields,
"timestampFields":timestampFields
}
def main(name):
config = readfile(name)
fieldNames = []
if 'comp' in name:
fieldNames = compFieldNames
elif 'bolt' in name:
fieldNames = boltFieldNames
print(f'Version: {config["version"]}')
print(f'Profile ID: {config["profileID"]}')
for fieldID, fieldData in config["dataFields"].items():
print(f'{fieldID:3d} {fieldNames[fieldID]:32s} => {fieldData}')
if __name__ == "__main__":
if len(sys.argv) != 2 or ('comp' not in sys.argv[1] and 'bolt' not in sys.argv[1]):
print('Usage: python elemnt-config.py comp.cfg or bolt-65535.cfg')
sys.exit(0)
main(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment