diff --git a/.gitignore b/.gitignore index 749a4c4..9d39e57 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,7 @@ target/ # python-fitparse specific FitSDK* + +/vendor/ +_fitparse +_scripts diff --git a/README.md b/README.md index b221c59..baa9d1d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,23 @@ Install from [![PyPI](https://img.shields.io/pypi/v/fitparse.svg)](https://pypi. pip install fitparse ``` +Dependencies +------------ +``` +curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" +python get-pip.py +pip install simplejson +``` + +Problems +------------ +If the error `There is no item named 'c/fit.h' in the archive"` appears: +``` +Replace two files names in scripts/generate_profile.py may help. +'c/fit.h' to 'FitSDKRelease_xx.xx.00/c/fit.h' +'Profile.xls' to 'FitSDKRelease_xx.xx.00/Profile.xls' +``` + FIT files ------------ - FIT files contain data stored in a binary file format. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b4d8928 --- /dev/null +++ b/composer.json @@ -0,0 +1,11 @@ +{ + "name": "time2tri/parser-fit", + "description": "Python FIT Parser", + "authors": [ + { + "name": "Soeren Kroell", + "email": "soeren@time2tri.me" + } + ], + "require": {} +} diff --git a/etc/requirements-test.in b/etc/requirements-test.in new file mode 100644 index 0000000..5c90533 --- /dev/null +++ b/etc/requirements-test.in @@ -0,0 +1,2 @@ +pip-tools +coverage diff --git a/fitparse/__init__.py b/fitparse/__init__.py index 1bbc49e..9053f66 100644 --- a/fitparse/__init__.py +++ b/fitparse/__init__.py @@ -3,7 +3,7 @@ from fitparse.processors import FitFileDataProcessor, StandardUnitsDataProcessor -__version__ = '1.0.1' +__version__ = '1.1.0' __all__ = [ 'FitFileDataProcessor', 'FitFile', 'FitParseError', 'StandardUnitsDataProcessor', 'DataMessage' diff --git a/fitparse/base.py b/fitparse/base.py index 125d620..751f94a 100644 --- a/fitparse/base.py +++ b/fitparse/base.py @@ -19,7 +19,7 @@ class FitFile(object): - def __init__(self, fileish, check_crc=True, data_processor=None): + def __init__(self, fileish, check_crc=False, data_processor=None): self._file = fileish_open(fileish, 'rb') self.check_crc = check_crc @@ -30,6 +30,7 @@ def __init__(self, fileish, check_crc=True, data_processor=None): self._file.seek(0, os.SEEK_END) self._filesize = self._file.tell() self._file.seek(0, os.SEEK_SET) + self._messages = [] # Start off by parsing the file header (sets initial attribute values) self._parse_file_header() @@ -98,8 +99,8 @@ def _parse_file_header(self): self._compressed_ts_accumulator = 0 self._crc = Crc() self._local_mesgs = {} - self._messages = [] + # Every FIT must have 'FIT' string in the 8th byte. So, it seems your FIT is wrong. header_data = self._read(12) if header_data[8:12] != b'.FIT': raise FitHeaderError("Invalid .FIT File Header") @@ -319,7 +320,10 @@ def _parse_data_message(self, header): if field.components: for component in field.components: # Render its raw value - cmp_raw_value = component.render(raw_value) + try: + cmp_raw_value = component.render(raw_value) + except ValueError: + continue # Apply accumulated value if component.accumulate and cmp_raw_value is not None: diff --git a/fitparse/profile.py b/fitparse/profile.py index 3e59617..aa6ddbd 100644 --- a/fitparse/profile.py +++ b/fitparse/profile.py @@ -1,7 +1,8 @@ -################# BEGIN AUTOMATICALLY GENERATED FIT PROFILE ################## -########################### DO NOT EDIT THIS FILE ############################ -####### EXPORTED PROFILE FROM SDK VERSION 20.33 AT 2017-05-17 22:36:12 ####### -########## PARSED 118 TYPES (1699 VALUES), 76 MESSAGES (950 FIELDS) ########## + +# ***************** BEGIN AUTOMATICALLY GENERATED FIT PROFILE ****************** +# *************************** DO NOT EDIT THIS FILE **************************** +# ************ EXPORTED PROFILE FROM SDK VERSION 20.8 ON 2019-03-05 ************ +# ********* PARSED 161 TYPES (2985 VALUES), 85 MESSAGES (1038 FIELDS) ********** from fitparse.records import ( ComponentField, @@ -14,6 +15,9 @@ ) +FIELD_NUM_TIMESTAMP = 253 + + FIELD_TYPES = { 'activity': FieldType( name='activity', @@ -219,6 +223,13 @@ 6: 'key_and_messages_and_smart_notifications', }, ), + 'backlight_timeout': FieldType( # Timeout in seconds. + name='backlight_timeout', + base_type=BASE_TYPES[0x02], # uint8 + values={ + 0: 'infinite', # Backlight stays on forever. + }, + ), 'battery_status': FieldType( name='battery_status', base_type=BASE_TYPES[0x02], # uint8 @@ -232,6 +243,39 @@ 7: 'unknown', }, ), + 'bench_press_exercise_name': FieldType( + name='bench_press_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'alternating_dumbbell_chest_press_on_swiss_ball', + 1: 'barbell_bench_press', + 2: 'barbell_board_bench_press', + 3: 'barbell_floor_press', + 4: 'close_grip_barbell_bench_press', + 5: 'decline_dumbbell_bench_press', + 6: 'dumbbell_bench_press', + 7: 'dumbbell_floor_press', + 8: 'incline_barbell_bench_press', + 9: 'incline_dumbbell_bench_press', + 10: 'incline_smith_machine_bench_press', + 11: 'isometric_barbell_bench_press', + 12: 'kettlebell_chest_press', + 13: 'neutral_grip_dumbbell_bench_press', + 14: 'neutral_grip_dumbbell_incline_bench_press', + 15: 'one_arm_floor_press', + 16: 'weighted_one_arm_floor_press', + 17: 'partial_lockout', + 18: 'reverse_grip_barbell_bench_press', + 19: 'reverse_grip_incline_bench_press', + 20: 'single_arm_cable_chest_press', + 21: 'single_arm_dumbbell_bench_press', + 22: 'smith_machine_bench_press', + 23: 'swiss_ball_dumbbell_chest_press', + 24: 'triple_stop_barbell_bench_press', + 25: 'wide_grip_barbell_bench_press', + 26: 'alternating_dumbbell_chest_press', + }, + ), 'bike_light_beam_angle_mode': FieldType( name='bike_light_beam_angle_mode', base_type=BASE_TYPES[0x02], # uint8 @@ -311,6 +355,33 @@ 4: 'error_irregular_heart_rate', }, ), + 'calf_raise_exercise_name': FieldType( + name='calf_raise_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: '3_way_calf_raise', + 1: '3_way_weighted_calf_raise', + 2: '3_way_single_leg_calf_raise', + 3: '3_way_weighted_single_leg_calf_raise', + 4: 'donkey_calf_raise', + 5: 'weighted_donkey_calf_raise', + 6: 'seated_calf_raise', + 7: 'weighted_seated_calf_raise', + 8: 'seated_dumbbell_toe_raise', + 9: 'single_leg_bent_knee_calf_raise', + 10: 'weighted_single_leg_bent_knee_calf_raise', + 11: 'single_leg_decline_push_up', + 12: 'single_leg_donkey_calf_raise', + 13: 'weighted_single_leg_donkey_calf_raise', + 14: 'single_leg_hip_raise_with_knee_hold', + 15: 'single_leg_standing_calf_raise', + 16: 'single_leg_standing_dumbbell_calf_raise', + 17: 'standing_barbell_calf_raise', + 18: 'standing_calf_raise', + 19: 'weighted_standing_calf_raise', + 20: 'standing_dumbbell_calf_raise', + }, + ), 'camera_event_type': FieldType( name='camera_event_type', base_type=BASE_TYPES[0x00], # enum @@ -340,6 +411,45 @@ 3: 'camera_orientation_270', }, ), + 'cardio_exercise_name': FieldType( + name='cardio_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'bob_and_weave_circle', + 1: 'weighted_bob_and_weave_circle', + 2: 'cardio_core_crawl', + 3: 'weighted_cardio_core_crawl', + 4: 'double_under', + 5: 'weighted_double_under', + 6: 'jump_rope', + 7: 'weighted_jump_rope', + 8: 'jump_rope_crossover', + 9: 'weighted_jump_rope_crossover', + 10: 'jump_rope_jog', + 11: 'weighted_jump_rope_jog', + 12: 'jumping_jacks', + 13: 'weighted_jumping_jacks', + 14: 'ski_moguls', + 15: 'weighted_ski_moguls', + 16: 'split_jacks', + 17: 'weighted_split_jacks', + 18: 'squat_jacks', + 19: 'weighted_squat_jacks', + 20: 'triple_under', + 21: 'weighted_triple_under', + }, + ), + 'carry_exercise_name': FieldType( + name='carry_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'bar_holds', + 1: 'farmers_walk', + 2: 'farmers_walk_on_toes', + 3: 'hex_dumbbell_hold', + 4: 'overhead_carry', + }, + ), 'checksum': FieldType( name='checksum', base_type=BASE_TYPES[0x02], # uint8 @@ -348,6 +458,35 @@ 1: 'ok', # Set to mark checksum as valid if computes to invalid values 0 or 0xFF. Checksum can also be set to ok to save encoding computation time. }, ), + 'chop_exercise_name': FieldType( + name='chop_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'cable_pull_through', + 1: 'cable_rotational_lift', + 2: 'cable_woodchop', + 3: 'cross_chop_to_knee', + 4: 'weighted_cross_chop_to_knee', + 5: 'dumbbell_chop', + 6: 'half_kneeling_rotation', + 7: 'weighted_half_kneeling_rotation', + 8: 'half_kneeling_rotational_chop', + 9: 'half_kneeling_rotational_reverse_chop', + 10: 'half_kneeling_stability_chop', + 11: 'half_kneeling_stability_reverse_chop', + 12: 'kneeling_rotational_chop', + 13: 'kneeling_rotational_reverse_chop', + 14: 'kneeling_stability_chop', + 15: 'kneeling_woodchopper', + 16: 'medicine_ball_wood_chops', + 17: 'power_squat_chops', + 18: 'weighted_power_squat_chops', + 19: 'standing_rotational_chop', + 20: 'standing_split_rotational_chop', + 21: 'standing_split_rotational_reverse_chop', + 22: 'standing_stability_reverse_chop', + }, + ), 'comm_timeout_type': FieldType( name='comm_timeout_type', base_type=BASE_TYPES[0x84], # uint16 @@ -396,6 +535,58 @@ 0x80000000: 'instant_input', # Device supports instant input feature }, ), + 'core_exercise_name': FieldType( + name='core_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'abs_jabs', + 1: 'weighted_abs_jabs', + 2: 'alternating_plate_reach', + 3: 'barbell_rollout', + 4: 'weighted_barbell_rollout', + 5: 'body_bar_oblique_twist', + 6: 'cable_core_press', + 7: 'cable_side_bend', + 8: 'side_bend', + 9: 'weighted_side_bend', + 10: 'crescent_circle', + 11: 'weighted_crescent_circle', + 12: 'cycling_russian_twist', + 13: 'weighted_cycling_russian_twist', + 14: 'elevated_feet_russian_twist', + 15: 'weighted_elevated_feet_russian_twist', + 16: 'half_turkish_get_up', + 17: 'kettlebell_windmill', + 18: 'kneeling_ab_wheel', + 19: 'weighted_kneeling_ab_wheel', + 20: 'modified_front_lever', + 21: 'open_knee_tucks', + 22: 'weighted_open_knee_tucks', + 23: 'side_abs_leg_lift', + 24: 'weighted_side_abs_leg_lift', + 25: 'swiss_ball_jackknife', + 26: 'weighted_swiss_ball_jackknife', + 27: 'swiss_ball_pike', + 28: 'weighted_swiss_ball_pike', + 29: 'swiss_ball_rollout', + 30: 'weighted_swiss_ball_rollout', + 31: 'triangle_hip_press', + 32: 'weighted_triangle_hip_press', + 33: 'trx_suspended_jackknife', + 34: 'weighted_trx_suspended_jackknife', + 35: 'u_boat', + 36: 'weighted_u_boat', + 37: 'windmill_switches', + 38: 'weighted_windmill_switches', + 39: 'alternating_slide_out', + 40: 'weighted_alternating_slide_out', + 41: 'ghd_back_extensions', + 42: 'weighted_ghd_back_extensions', + 43: 'overhead_walk', + 44: 'inchworm', + 45: 'weighted_modified_front_lever', + }, + ), 'course_capabilities': FieldType( name='course_capabilities', base_type=BASE_TYPES[0x8C], # uint32z @@ -445,6 +636,146 @@ 25: 'segment_end', }, ), + 'crunch_exercise_name': FieldType( + name='crunch_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'bicycle_crunch', + 1: 'cable_crunch', + 2: 'circular_arm_crunch', + 3: 'crossed_arms_crunch', + 4: 'weighted_crossed_arms_crunch', + 5: 'cross_leg_reverse_crunch', + 6: 'weighted_cross_leg_reverse_crunch', + 7: 'crunch_chop', + 8: 'weighted_crunch_chop', + 9: 'double_crunch', + 10: 'weighted_double_crunch', + 11: 'elbow_to_knee_crunch', + 12: 'weighted_elbow_to_knee_crunch', + 13: 'flutter_kicks', + 14: 'weighted_flutter_kicks', + 15: 'foam_roller_reverse_crunch_on_bench', + 16: 'weighted_foam_roller_reverse_crunch_on_bench', + 17: 'foam_roller_reverse_crunch_with_dumbbell', + 18: 'foam_roller_reverse_crunch_with_medicine_ball', + 19: 'frog_press', + 20: 'hanging_knee_raise_oblique_crunch', + 21: 'weighted_hanging_knee_raise_oblique_crunch', + 22: 'hip_crossover', + 23: 'weighted_hip_crossover', + 24: 'hollow_rock', + 25: 'weighted_hollow_rock', + 26: 'incline_reverse_crunch', + 27: 'weighted_incline_reverse_crunch', + 28: 'kneeling_cable_crunch', + 29: 'kneeling_cross_crunch', + 30: 'weighted_kneeling_cross_crunch', + 31: 'kneeling_oblique_cable_crunch', + 32: 'knees_to_elbow', + 33: 'leg_extensions', + 34: 'weighted_leg_extensions', + 35: 'leg_levers', + 36: 'mcgill_curl_up', + 37: 'weighted_mcgill_curl_up', + 38: 'modified_pilates_roll_up_with_ball', + 39: 'weighted_modified_pilates_roll_up_with_ball', + 40: 'pilates_crunch', + 41: 'weighted_pilates_crunch', + 42: 'pilates_roll_up_with_ball', + 43: 'weighted_pilates_roll_up_with_ball', + 44: 'raised_legs_crunch', + 45: 'weighted_raised_legs_crunch', + 46: 'reverse_crunch', + 47: 'weighted_reverse_crunch', + 48: 'reverse_crunch_on_a_bench', + 49: 'weighted_reverse_crunch_on_a_bench', + 50: 'reverse_curl_and_lift', + 51: 'weighted_reverse_curl_and_lift', + 52: 'rotational_lift', + 53: 'weighted_rotational_lift', + 54: 'seated_alternating_reverse_crunch', + 55: 'weighted_seated_alternating_reverse_crunch', + 56: 'seated_leg_u', + 57: 'weighted_seated_leg_u', + 58: 'side_to_side_crunch_and_weave', + 59: 'weighted_side_to_side_crunch_and_weave', + 60: 'single_leg_reverse_crunch', + 61: 'weighted_single_leg_reverse_crunch', + 62: 'skater_crunch_cross', + 63: 'weighted_skater_crunch_cross', + 64: 'standing_cable_crunch', + 65: 'standing_side_crunch', + 66: 'step_climb', + 67: 'weighted_step_climb', + 68: 'swiss_ball_crunch', + 69: 'swiss_ball_reverse_crunch', + 70: 'weighted_swiss_ball_reverse_crunch', + 71: 'swiss_ball_russian_twist', + 72: 'weighted_swiss_ball_russian_twist', + 73: 'swiss_ball_side_crunch', + 74: 'weighted_swiss_ball_side_crunch', + 75: 'thoracic_crunches_on_foam_roller', + 76: 'weighted_thoracic_crunches_on_foam_roller', + 77: 'triceps_crunch', + 78: 'weighted_bicycle_crunch', + 79: 'weighted_crunch', + 80: 'weighted_swiss_ball_crunch', + 81: 'toes_to_bar', + 82: 'weighted_toes_to_bar', + 83: 'crunch', + }, + ), + 'curl_exercise_name': FieldType( + name='curl_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'alternating_dumbbell_biceps_curl', + 1: 'alternating_dumbbell_biceps_curl_on_swiss_ball', + 2: 'alternating_incline_dumbbell_biceps_curl', + 3: 'barbell_biceps_curl', + 4: 'barbell_reverse_wrist_curl', + 5: 'barbell_wrist_curl', + 6: 'behind_the_back_barbell_reverse_wrist_curl', + 7: 'behind_the_back_one_arm_cable_curl', + 8: 'cable_biceps_curl', + 9: 'cable_hammer_curl', + 10: 'cheating_barbell_biceps_curl', + 11: 'close_grip_ez_bar_biceps_curl', + 12: 'cross_body_dumbbell_hammer_curl', + 13: 'dead_hang_biceps_curl', + 14: 'decline_hammer_curl', + 15: 'dumbbell_biceps_curl_with_static_hold', + 16: 'dumbbell_hammer_curl', + 17: 'dumbbell_reverse_wrist_curl', + 18: 'dumbbell_wrist_curl', + 19: 'ez_bar_preacher_curl', + 20: 'forward_bend_biceps_curl', + 21: 'hammer_curl_to_press', + 22: 'incline_dumbbell_biceps_curl', + 23: 'incline_offset_thumb_dumbbell_curl', + 24: 'kettlebell_biceps_curl', + 25: 'lying_concentration_cable_curl', + 26: 'one_arm_preacher_curl', + 27: 'plate_pinch_curl', + 28: 'preacher_curl_with_cable', + 29: 'reverse_ez_bar_curl', + 30: 'reverse_grip_wrist_curl', + 31: 'reverse_grip_barbell_biceps_curl', + 32: 'seated_alternating_dumbbell_biceps_curl', + 33: 'seated_dumbbell_biceps_curl', + 34: 'seated_reverse_dumbbell_curl', + 35: 'split_stance_offset_pinky_dumbbell_curl', + 36: 'standing_alternating_dumbbell_curls', + 37: 'standing_dumbbell_biceps_curl', + 38: 'standing_ez_bar_biceps_curl', + 39: 'static_curl', + 40: 'swiss_ball_dumbbell_overhead_triceps_extension', + 41: 'swiss_ball_ez_bar_preacher_curl', + 42: 'twisting_standing_dumbbell_biceps_curl', + 43: 'wide_grip_ez_bar_biceps_curl', + }, + ), 'date_mode': FieldType( name='date_mode', base_type=BASE_TYPES[0x00], # enum @@ -470,6 +801,31 @@ 6: 'saturday', }, ), + 'deadlift_exercise_name': FieldType( + name='deadlift_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'barbell_deadlift', + 1: 'barbell_straight_leg_deadlift', + 2: 'dumbbell_deadlift', + 3: 'dumbbell_single_leg_deadlift_to_row', + 4: 'dumbbell_straight_leg_deadlift', + 5: 'kettlebell_floor_to_shelf', + 6: 'one_arm_one_leg_deadlift', + 7: 'rack_pull', + 8: 'rotational_dumbbell_straight_leg_deadlift', + 9: 'single_arm_deadlift', + 10: 'single_leg_barbell_deadlift', + 11: 'single_leg_barbell_straight_leg_deadlift', + 12: 'single_leg_deadlift_with_barbell', + 13: 'single_leg_rdl_circuit', + 14: 'single_leg_romanian_deadlift_with_dumbbell', + 15: 'sumo_deadlift', + 16: 'sumo_deadlift_high_pull', + 17: 'trap_bar_deadlift', + 18: 'wide_grip_barbell_deadlift', + }, + ), 'device_index': FieldType( name='device_index', base_type=BASE_TYPES[0x02], # uint8 @@ -571,6 +927,31 @@ 1: 'percent_ftp', }, ), + 'dive_alarm_type': FieldType( + name='dive_alarm_type', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'depth', + 1: 'time', + }, + ), + 'dive_backlight_mode': FieldType( + name='dive_backlight_mode', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'at_depth', + 1: 'always_on', + }, + ), + 'dive_gas_status': FieldType( + name='dive_gas_status', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'disabled', + 1: 'enabled', + 2: 'backup_only', + }, + ), 'event': FieldType( name='event', base_type=BASE_TYPES[0x00], # enum @@ -785,6 +1166,7 @@ 93: 'vmg', 94: 'ambient_pressure', 95: 'pressure', + 96: 'vam', }, ), 'exd_display_type': FieldType( @@ -856,6 +1238,7 @@ 31: 'last_sport', 32: 'moving', 33: 'stopped', + 34: 'estimated_total', 242: 'zone_9', 243: 'zone_8', 244: 'zone_7', @@ -867,6 +1250,46 @@ 250: 'zone_1', }, ), + 'exercise_category': FieldType( + name='exercise_category', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'bench_press', + 1: 'calf_raise', + 2: 'cardio', + 3: 'carry', + 4: 'chop', + 5: 'core', + 6: 'crunch', + 7: 'curl', + 8: 'deadlift', + 9: 'flye', + 10: 'hip_raise', + 11: 'hip_stability', + 12: 'hip_swing', + 13: 'hyperextension', + 14: 'lateral_raise', + 15: 'leg_curl', + 16: 'leg_raise', + 17: 'lunge', + 18: 'olympic_lift', + 19: 'plank', + 20: 'plyo', + 21: 'pull_up', + 22: 'push_up', + 23: 'row', + 24: 'shoulder_press', + 25: 'shoulder_stability', + 26: 'shrug', + 27: 'sit_up', + 28: 'squat', + 29: 'total_body', + 30: 'triceps_extension', + 31: 'warm_up', + 32: 'run', + 65534: 'unknown', + }, + ), 'file': FieldType( name='file', base_type=BASE_TYPES[0x00], # enum @@ -944,6 +1367,20 @@ 3: 'unknown', # lost connection to fitness equipment }, ), + 'flye_exercise_name': FieldType( + name='flye_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'cable_crossover', + 1: 'decline_dumbbell_flye', + 2: 'dumbbell_flye', + 3: 'incline_dumbbell_flye', + 4: 'kettlebell_flye', + 5: 'kneeling_rear_flye', + 6: 'single_arm_standing_cable_reverse_flye', + 7: 'swiss_ball_dumbbell_flye', + }, + ), 'garmin_product': FieldType( name='garmin_product', base_type=BASE_TYPES[0x84], # uint16 @@ -1093,6 +1530,7 @@ 2531: 'edge_explore_820', 2544: 'fenix5s', 2547: 'd2_bravo_titanium', + 2567: 'varia_ut800', # Varia UT 800 SW 2593: 'running_dynamics_pod', 2604: 'fenix5x', 2606: 'vivo_fit_jr', @@ -1148,6 +1586,104 @@ 2: 'user', # Manually generated }, ), + 'hip_raise_exercise_name': FieldType( + name='hip_raise_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'barbell_hip_thrust_on_floor', + 1: 'barbell_hip_thrust_with_bench', + 2: 'bent_knee_swiss_ball_reverse_hip_raise', + 3: 'weighted_bent_knee_swiss_ball_reverse_hip_raise', + 4: 'bridge_with_leg_extension', + 5: 'weighted_bridge_with_leg_extension', + 6: 'clam_bridge', + 7: 'front_kick_tabletop', + 8: 'weighted_front_kick_tabletop', + 9: 'hip_extension_and_cross', + 10: 'weighted_hip_extension_and_cross', + 11: 'hip_raise', + 12: 'weighted_hip_raise', + 13: 'hip_raise_with_feet_on_swiss_ball', + 14: 'weighted_hip_raise_with_feet_on_swiss_ball', + 15: 'hip_raise_with_head_on_bosu_ball', + 16: 'weighted_hip_raise_with_head_on_bosu_ball', + 17: 'hip_raise_with_head_on_swiss_ball', + 18: 'weighted_hip_raise_with_head_on_swiss_ball', + 19: 'hip_raise_with_knee_squeeze', + 20: 'weighted_hip_raise_with_knee_squeeze', + 21: 'incline_rear_leg_extension', + 22: 'weighted_incline_rear_leg_extension', + 23: 'kettlebell_swing', + 24: 'marching_hip_raise', + 25: 'weighted_marching_hip_raise', + 26: 'marching_hip_raise_with_feet_on_a_swiss_ball', + 27: 'weighted_marching_hip_raise_with_feet_on_a_swiss_ball', + 28: 'reverse_hip_raise', + 29: 'weighted_reverse_hip_raise', + 30: 'single_leg_hip_raise', + 31: 'weighted_single_leg_hip_raise', + 32: 'single_leg_hip_raise_with_foot_on_bench', + 33: 'weighted_single_leg_hip_raise_with_foot_on_bench', + 34: 'single_leg_hip_raise_with_foot_on_bosu_ball', + 35: 'weighted_single_leg_hip_raise_with_foot_on_bosu_ball', + 36: 'single_leg_hip_raise_with_foot_on_foam_roller', + 37: 'weighted_single_leg_hip_raise_with_foot_on_foam_roller', + 38: 'single_leg_hip_raise_with_foot_on_medicine_ball', + 39: 'weighted_single_leg_hip_raise_with_foot_on_medicine_ball', + 40: 'single_leg_hip_raise_with_head_on_bosu_ball', + 41: 'weighted_single_leg_hip_raise_with_head_on_bosu_ball', + 42: 'weighted_clam_bridge', + }, + ), + 'hip_stability_exercise_name': FieldType( + name='hip_stability_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'band_side_lying_leg_raise', + 1: 'dead_bug', + 2: 'weighted_dead_bug', + 3: 'external_hip_raise', + 4: 'weighted_external_hip_raise', + 5: 'fire_hydrant_kicks', + 6: 'weighted_fire_hydrant_kicks', + 7: 'hip_circles', + 8: 'weighted_hip_circles', + 9: 'inner_thigh_lift', + 10: 'weighted_inner_thigh_lift', + 11: 'lateral_walks_with_band_at_ankles', + 12: 'pretzel_side_kick', + 13: 'weighted_pretzel_side_kick', + 14: 'prone_hip_internal_rotation', + 15: 'weighted_prone_hip_internal_rotation', + 16: 'quadruped', + 17: 'quadruped_hip_extension', + 18: 'weighted_quadruped_hip_extension', + 19: 'quadruped_with_leg_lift', + 20: 'weighted_quadruped_with_leg_lift', + 21: 'side_lying_leg_raise', + 22: 'weighted_side_lying_leg_raise', + 23: 'sliding_hip_adduction', + 24: 'weighted_sliding_hip_adduction', + 25: 'standing_adduction', + 26: 'weighted_standing_adduction', + 27: 'standing_cable_hip_abduction', + 28: 'standing_hip_abduction', + 29: 'weighted_standing_hip_abduction', + 30: 'standing_rear_leg_raise', + 31: 'weighted_standing_rear_leg_raise', + 32: 'supine_hip_internal_rotation', + 33: 'weighted_supine_hip_internal_rotation', + }, + ), + 'hip_swing_exercise_name': FieldType( + name='hip_swing_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'single_arm_kettlebell_swing', + 1: 'single_arm_dumbbell_swing', + 2: 'step_out_swing', + }, + ), 'hr_type': FieldType( name='hr_type', base_type=BASE_TYPES[0x00], # enum @@ -1165,6 +1701,49 @@ 2: 'percent_hrr', }, ), + 'hyperextension_exercise_name': FieldType( + name='hyperextension_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'back_extension_with_opposite_arm_and_leg_reach', + 1: 'weighted_back_extension_with_opposite_arm_and_leg_reach', + 2: 'base_rotations', + 3: 'weighted_base_rotations', + 4: 'bent_knee_reverse_hyperextension', + 5: 'weighted_bent_knee_reverse_hyperextension', + 6: 'hollow_hold_and_roll', + 7: 'weighted_hollow_hold_and_roll', + 8: 'kicks', + 9: 'weighted_kicks', + 10: 'knee_raises', + 11: 'weighted_knee_raises', + 12: 'kneeling_superman', + 13: 'weighted_kneeling_superman', + 14: 'lat_pull_down_with_row', + 15: 'medicine_ball_deadlift_to_reach', + 16: 'one_arm_one_leg_row', + 17: 'one_arm_row_with_band', + 18: 'overhead_lunge_with_medicine_ball', + 19: 'plank_knee_tucks', + 20: 'weighted_plank_knee_tucks', + 21: 'side_step', + 22: 'weighted_side_step', + 23: 'single_leg_back_extension', + 24: 'weighted_single_leg_back_extension', + 25: 'spine_extension', + 26: 'weighted_spine_extension', + 27: 'static_back_extension', + 28: 'weighted_static_back_extension', + 29: 'superman_from_floor', + 30: 'weighted_superman_from_floor', + 31: 'swiss_ball_back_extension', + 32: 'weighted_swiss_ball_back_extension', + 33: 'swiss_ball_hyperextension', + 34: 'weighted_swiss_ball_hyperextension', + 35: 'swiss_ball_opposite_arm_and_leg_lift', + 36: 'weighted_swiss_ball_opposite_arm_and_leg_lift', + }, + ), 'intensity': FieldType( name='intensity', base_type=BASE_TYPES[0x00], # enum @@ -1303,6 +1882,44 @@ 8: 'fitness_equipment', }, ), + 'lateral_raise_exercise_name': FieldType( + name='lateral_raise_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: '45_degree_cable_external_rotation', + 1: 'alternating_lateral_raise_with_static_hold', + 2: 'bar_muscle_up', + 3: 'bent_over_lateral_raise', + 4: 'cable_diagonal_raise', + 5: 'cable_front_raise', + 6: 'calorie_row', + 7: 'combo_shoulder_raise', + 8: 'dumbbell_diagonal_raise', + 9: 'dumbbell_v_raise', + 10: 'front_raise', + 11: 'leaning_dumbbell_lateral_raise', + 12: 'lying_dumbbell_raise', + 13: 'muscle_up', + 14: 'one_arm_cable_lateral_raise', + 15: 'overhand_grip_rear_lateral_raise', + 16: 'plate_raises', + 17: 'ring_dip', + 18: 'weighted_ring_dip', + 19: 'ring_muscle_up', + 20: 'weighted_ring_muscle_up', + 21: 'rope_climb', + 22: 'weighted_rope_climb', + 23: 'scaption', + 24: 'seated_lateral_raise', + 25: 'seated_rear_lateral_raise', + 26: 'side_lying_lateral_raise', + 27: 'standing_lift', + 28: 'suspended_row', + 29: 'underhand_grip_rear_lateral_raise', + 30: 'wall_slide', + 31: 'weighted_wall_slide', + }, + ), 'left_right_balance': FieldType( name='left_right_balance', base_type=BASE_TYPES[0x02], # uint8 @@ -1319,6 +1936,52 @@ 0x8000: 'right', # data corresponds to right if set, otherwise unknown }, ), + 'leg_curl_exercise_name': FieldType( + name='leg_curl_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'leg_curl', + 1: 'weighted_leg_curl', + 2: 'good_morning', + 3: 'seated_barbell_good_morning', + 4: 'single_leg_barbell_good_morning', + 5: 'single_leg_sliding_leg_curl', + 6: 'sliding_leg_curl', + 7: 'split_barbell_good_morning', + 8: 'split_stance_extension', + 9: 'staggered_stance_good_morning', + 10: 'swiss_ball_hip_raise_and_leg_curl', + 11: 'zercher_good_morning', + }, + ), + 'leg_raise_exercise_name': FieldType( + name='leg_raise_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'hanging_knee_raise', + 1: 'hanging_leg_raise', + 2: 'weighted_hanging_leg_raise', + 3: 'hanging_single_leg_raise', + 4: 'weighted_hanging_single_leg_raise', + 5: 'kettlebell_leg_raises', + 6: 'leg_lowering_drill', + 7: 'weighted_leg_lowering_drill', + 8: 'lying_straight_leg_raise', + 9: 'weighted_lying_straight_leg_raise', + 10: 'medicine_ball_leg_drops', + 11: 'quadruped_leg_raise', + 12: 'weighted_quadruped_leg_raise', + 13: 'reverse_leg_raise', + 14: 'weighted_reverse_leg_raise', + 15: 'reverse_leg_raise_on_swiss_ball', + 16: 'weighted_reverse_leg_raise_on_swiss_ball', + 17: 'single_leg_lowering_drill', + 18: 'weighted_single_leg_lowering_drill', + 19: 'weighted_hanging_knee_raise', + 20: 'lateral_stepover', + 21: 'weighted_lateral_stepover', + }, + ), 'length_type': FieldType( name='length_type', base_type=BASE_TYPES[0x00], # enum @@ -1334,10 +1997,101 @@ 0x10000000: 'min', # if date_time is < 0x10000000 then it is system time (seconds from device power on) }, ), + 'local_device_type': FieldType( + name='local_device_type', + base_type=BASE_TYPES[0x02], # uint8 + ), 'localtime_into_day': FieldType( # number of seconds into the day since local 00:00:00 name='localtime_into_day', base_type=BASE_TYPES[0x86], # uint32 ), + 'lunge_exercise_name': FieldType( + name='lunge_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'overhead_lunge', + 1: 'lunge_matrix', + 2: 'weighted_lunge_matrix', + 3: 'alternating_barbell_forward_lunge', + 4: 'alternating_dumbbell_lunge_with_reach', + 5: 'back_foot_elevated_dumbbell_split_squat', + 6: 'barbell_box_lunge', + 7: 'barbell_bulgarian_split_squat', + 8: 'barbell_crossover_lunge', + 9: 'barbell_front_split_squat', + 10: 'barbell_lunge', + 11: 'barbell_reverse_lunge', + 12: 'barbell_side_lunge', + 13: 'barbell_split_squat', + 14: 'core_control_rear_lunge', + 15: 'diagonal_lunge', + 16: 'drop_lunge', + 17: 'dumbbell_box_lunge', + 18: 'dumbbell_bulgarian_split_squat', + 19: 'dumbbell_crossover_lunge', + 20: 'dumbbell_diagonal_lunge', + 21: 'dumbbell_lunge', + 22: 'dumbbell_lunge_and_rotation', + 23: 'dumbbell_overhead_bulgarian_split_squat', + 24: 'dumbbell_reverse_lunge_to_high_knee_and_press', + 25: 'dumbbell_side_lunge', + 26: 'elevated_front_foot_barbell_split_squat', + 27: 'front_foot_elevated_dumbbell_split_squat', + 28: 'gunslinger_lunge', + 29: 'lawnmower_lunge', + 30: 'low_lunge_with_isometric_adduction', + 31: 'low_side_to_side_lunge', + 32: 'lunge', + 33: 'weighted_lunge', + 34: 'lunge_with_arm_reach', + 35: 'lunge_with_diagonal_reach', + 36: 'lunge_with_side_bend', + 37: 'offset_dumbbell_lunge', + 38: 'offset_dumbbell_reverse_lunge', + 39: 'overhead_bulgarian_split_squat', + 40: 'overhead_dumbbell_reverse_lunge', + 41: 'overhead_dumbbell_split_squat', + 42: 'overhead_lunge_with_rotation', + 43: 'reverse_barbell_box_lunge', + 44: 'reverse_box_lunge', + 45: 'reverse_dumbbell_box_lunge', + 46: 'reverse_dumbbell_crossover_lunge', + 47: 'reverse_dumbbell_diagonal_lunge', + 48: 'reverse_lunge_with_reach_back', + 49: 'weighted_reverse_lunge_with_reach_back', + 50: 'reverse_lunge_with_twist_and_overhead_reach', + 51: 'weighted_reverse_lunge_with_twist_and_overhead_reach', + 52: 'reverse_sliding_box_lunge', + 53: 'weighted_reverse_sliding_box_lunge', + 54: 'reverse_sliding_lunge', + 55: 'weighted_reverse_sliding_lunge', + 56: 'runners_lunge_to_balance', + 57: 'weighted_runners_lunge_to_balance', + 58: 'shifting_side_lunge', + 59: 'side_and_crossover_lunge', + 60: 'weighted_side_and_crossover_lunge', + 61: 'side_lunge', + 62: 'weighted_side_lunge', + 63: 'side_lunge_and_press', + 64: 'side_lunge_jump_off', + 65: 'side_lunge_sweep', + 66: 'weighted_side_lunge_sweep', + 67: 'side_lunge_to_crossover_tap', + 68: 'weighted_side_lunge_to_crossover_tap', + 69: 'side_to_side_lunge_chops', + 70: 'weighted_side_to_side_lunge_chops', + 71: 'siff_jump_lunge', + 72: 'weighted_siff_jump_lunge', + 73: 'single_arm_reverse_lunge_and_press', + 74: 'sliding_lateral_lunge', + 75: 'weighted_sliding_lateral_lunge', + 76: 'walking_barbell_lunge', + 77: 'walking_dumbbell_lunge', + 78: 'walking_lunge', + 79: 'weighted_walking_lunge', + 80: 'wide_grip_overhead_barbell_split_squat', + }, + ), 'manufacturer': FieldType( name='manufacturer', base_type=BASE_TYPES[0x84], # uint16 @@ -1451,6 +2205,18 @@ 110: 'salutron', 111: 'technogym', 112: 'bryton_sensors', + 113: 'latitude_limited', + 114: 'soaring_technology', + 115: 'igpsport', + 116: 'thinkrider', + 117: 'gopher_sport', + 118: 'waterrower', + 119: 'orangetheory', + 120: 'inpeak', + 121: 'kinetic', + 122: 'johnson_health_tech', + 123: 'polar_electro', + 124: 'seesense', 255: 'development', 257: 'healthandlife', 258: 'lezyne', @@ -1473,6 +2239,22 @@ 275: 'podoon', 276: 'life_time_fitness', 277: 'falco_e_motors', # Falco eMotors Inc. + 278: 'minoura', + 279: 'cycliq', + 280: 'luxottica', + 281: 'trainer_road', + 282: 'the_sufferfest', + 283: 'fullspeedahead', + 284: 'virtualtraining', + 285: 'feedbacksports', + 286: 'omata', + 287: 'vdo', + 288: 'magneticdays', + 289: 'hammerhead', + 290: 'kinetic_by_kurt', + 291: 'shapelog', + 292: 'dabuziduo', + 293: 'jetblack', 5759: 'actigraphcorp', }, ), @@ -1566,6 +2348,15 @@ 206: 'field_description', 207: 'developer_data_id', 208: 'magnetometer_data', + 209: 'barometer_data', + 210: 'one_d_sensor_calibration', + 225: 'set', + 227: 'stress_level', + 258: 'dive_settings', + 259: 'dive_gas', + 262: 'dive_alarm', + 264: 'exercise_title', + 268: 'dive_summary', }, ), 'message_index': FieldType( @@ -1577,6 +2368,206 @@ 0x8000: 'selected', # message is selected if set }, ), + 'olympic_lift_exercise_name': FieldType( + name='olympic_lift_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'barbell_hang_power_clean', + 1: 'barbell_hang_squat_clean', + 2: 'barbell_power_clean', + 3: 'barbell_power_snatch', + 4: 'barbell_squat_clean', + 5: 'clean_and_jerk', + 6: 'barbell_hang_power_snatch', + 7: 'barbell_hang_pull', + 8: 'barbell_high_pull', + 9: 'barbell_snatch', + 10: 'barbell_split_jerk', + 11: 'clean', + 12: 'dumbbell_clean', + 13: 'dumbbell_hang_pull', + 14: 'one_hand_dumbbell_split_snatch', + 15: 'push_jerk', + 16: 'single_arm_dumbbell_snatch', + 17: 'single_arm_hang_snatch', + 18: 'single_arm_kettlebell_snatch', + 19: 'split_jerk', + 20: 'squat_clean_and_jerk', + }, + ), + 'plank_exercise_name': FieldType( + name='plank_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: '45_degree_plank', + 1: 'weighted_45_degree_plank', + 2: '90_degree_static_hold', + 3: 'weighted_90_degree_static_hold', + 4: 'bear_crawl', + 5: 'weighted_bear_crawl', + 6: 'cross_body_mountain_climber', + 7: 'weighted_cross_body_mountain_climber', + 8: 'elbow_plank_pike_jacks', + 9: 'weighted_elbow_plank_pike_jacks', + 10: 'elevated_feet_plank', + 11: 'weighted_elevated_feet_plank', + 12: 'elevator_abs', + 13: 'weighted_elevator_abs', + 14: 'extended_plank', + 15: 'weighted_extended_plank', + 16: 'full_plank_passe_twist', + 17: 'weighted_full_plank_passe_twist', + 18: 'inching_elbow_plank', + 19: 'weighted_inching_elbow_plank', + 20: 'inchworm_to_side_plank', + 21: 'weighted_inchworm_to_side_plank', + 22: 'kneeling_plank', + 23: 'weighted_kneeling_plank', + 24: 'kneeling_side_plank_with_leg_lift', + 25: 'weighted_kneeling_side_plank_with_leg_lift', + 26: 'lateral_roll', + 27: 'weighted_lateral_roll', + 28: 'lying_reverse_plank', + 29: 'weighted_lying_reverse_plank', + 30: 'medicine_ball_mountain_climber', + 31: 'weighted_medicine_ball_mountain_climber', + 32: 'modified_mountain_climber_and_extension', + 33: 'weighted_modified_mountain_climber_and_extension', + 34: 'mountain_climber', + 35: 'weighted_mountain_climber', + 36: 'mountain_climber_on_sliding_discs', + 37: 'weighted_mountain_climber_on_sliding_discs', + 38: 'mountain_climber_with_feet_on_bosu_ball', + 39: 'weighted_mountain_climber_with_feet_on_bosu_ball', + 40: 'mountain_climber_with_hands_on_bench', + 41: 'mountain_climber_with_hands_on_swiss_ball', + 42: 'weighted_mountain_climber_with_hands_on_swiss_ball', + 43: 'plank', + 44: 'plank_jacks_with_feet_on_sliding_discs', + 45: 'weighted_plank_jacks_with_feet_on_sliding_discs', + 46: 'plank_knee_twist', + 47: 'weighted_plank_knee_twist', + 48: 'plank_pike_jumps', + 49: 'weighted_plank_pike_jumps', + 50: 'plank_pikes', + 51: 'weighted_plank_pikes', + 52: 'plank_to_stand_up', + 53: 'weighted_plank_to_stand_up', + 54: 'plank_with_arm_raise', + 55: 'weighted_plank_with_arm_raise', + 56: 'plank_with_knee_to_elbow', + 57: 'weighted_plank_with_knee_to_elbow', + 58: 'plank_with_oblique_crunch', + 59: 'weighted_plank_with_oblique_crunch', + 60: 'plyometric_side_plank', + 61: 'weighted_plyometric_side_plank', + 62: 'rolling_side_plank', + 63: 'weighted_rolling_side_plank', + 64: 'side_kick_plank', + 65: 'weighted_side_kick_plank', + 66: 'side_plank', + 67: 'weighted_side_plank', + 68: 'side_plank_and_row', + 69: 'weighted_side_plank_and_row', + 70: 'side_plank_lift', + 71: 'weighted_side_plank_lift', + 72: 'side_plank_with_elbow_on_bosu_ball', + 73: 'weighted_side_plank_with_elbow_on_bosu_ball', + 74: 'side_plank_with_feet_on_bench', + 75: 'weighted_side_plank_with_feet_on_bench', + 76: 'side_plank_with_knee_circle', + 77: 'weighted_side_plank_with_knee_circle', + 78: 'side_plank_with_knee_tuck', + 79: 'weighted_side_plank_with_knee_tuck', + 80: 'side_plank_with_leg_lift', + 81: 'weighted_side_plank_with_leg_lift', + 82: 'side_plank_with_reach_under', + 83: 'weighted_side_plank_with_reach_under', + 84: 'single_leg_elevated_feet_plank', + 85: 'weighted_single_leg_elevated_feet_plank', + 86: 'single_leg_flex_and_extend', + 87: 'weighted_single_leg_flex_and_extend', + 88: 'single_leg_side_plank', + 89: 'weighted_single_leg_side_plank', + 90: 'spiderman_plank', + 91: 'weighted_spiderman_plank', + 92: 'straight_arm_plank', + 93: 'weighted_straight_arm_plank', + 94: 'straight_arm_plank_with_shoulder_touch', + 95: 'weighted_straight_arm_plank_with_shoulder_touch', + 96: 'swiss_ball_plank', + 97: 'weighted_swiss_ball_plank', + 98: 'swiss_ball_plank_leg_lift', + 99: 'weighted_swiss_ball_plank_leg_lift', + 100: 'swiss_ball_plank_leg_lift_and_hold', + 101: 'swiss_ball_plank_with_feet_on_bench', + 102: 'weighted_swiss_ball_plank_with_feet_on_bench', + 103: 'swiss_ball_prone_jackknife', + 104: 'weighted_swiss_ball_prone_jackknife', + 105: 'swiss_ball_side_plank', + 106: 'weighted_swiss_ball_side_plank', + 107: 'three_way_plank', + 108: 'weighted_three_way_plank', + 109: 'towel_plank_and_knee_in', + 110: 'weighted_towel_plank_and_knee_in', + 111: 't_stabilization', + 112: 'weighted_t_stabilization', + 113: 'turkish_get_up_to_side_plank', + 114: 'weighted_turkish_get_up_to_side_plank', + 115: 'two_point_plank', + 116: 'weighted_two_point_plank', + 117: 'weighted_plank', + 118: 'wide_stance_plank_with_diagonal_arm_lift', + 119: 'weighted_wide_stance_plank_with_diagonal_arm_lift', + 120: 'wide_stance_plank_with_diagonal_leg_lift', + 121: 'weighted_wide_stance_plank_with_diagonal_leg_lift', + 122: 'wide_stance_plank_with_leg_lift', + 123: 'weighted_wide_stance_plank_with_leg_lift', + 124: 'wide_stance_plank_with_opposite_arm_and_leg_lift', + 125: 'weighted_mountain_climber_with_hands_on_bench', + 126: 'weighted_swiss_ball_plank_leg_lift_and_hold', + 127: 'weighted_wide_stance_plank_with_opposite_arm_and_leg_lift', + }, + ), + 'plyo_exercise_name': FieldType( + name='plyo_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'alternating_jump_lunge', + 1: 'weighted_alternating_jump_lunge', + 2: 'barbell_jump_squat', + 3: 'body_weight_jump_squat', + 4: 'weighted_jump_squat', + 5: 'cross_knee_strike', + 6: 'weighted_cross_knee_strike', + 7: 'depth_jump', + 8: 'weighted_depth_jump', + 9: 'dumbbell_jump_squat', + 10: 'dumbbell_split_jump', + 11: 'front_knee_strike', + 12: 'weighted_front_knee_strike', + 13: 'high_box_jump', + 14: 'weighted_high_box_jump', + 15: 'isometric_explosive_body_weight_jump_squat', + 16: 'weighted_isometric_explosive_jump_squat', + 17: 'lateral_leap_and_hop', + 18: 'weighted_lateral_leap_and_hop', + 19: 'lateral_plyo_squats', + 20: 'weighted_lateral_plyo_squats', + 21: 'lateral_slide', + 22: 'weighted_lateral_slide', + 23: 'medicine_ball_overhead_throws', + 24: 'medicine_ball_side_throw', + 25: 'medicine_ball_slam', + 26: 'side_to_side_medicine_ball_throws', + 27: 'side_to_side_shuffle_jump', + 28: 'weighted_side_to_side_shuffle_jump', + 29: 'squat_jump_onto_box', + 30: 'weighted_squat_jump_onto_box', + 31: 'squat_jumps_in_and_out', + 32: 'weighted_squat_jumps_in_and_out', + }, + ), 'power_phase_type': FieldType( name='power_phase_type', base_type=BASE_TYPES[0x00], # enum @@ -1587,6 +2578,135 @@ 3: 'power_phase_center', }, ), + 'pull_up_exercise_name': FieldType( + name='pull_up_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'banded_pull_ups', + 1: '30_degree_lat_pulldown', + 2: 'band_assisted_chin_up', + 3: 'close_grip_chin_up', + 4: 'weighted_close_grip_chin_up', + 5: 'close_grip_lat_pulldown', + 6: 'crossover_chin_up', + 7: 'weighted_crossover_chin_up', + 8: 'ez_bar_pullover', + 9: 'hanging_hurdle', + 10: 'weighted_hanging_hurdle', + 11: 'kneeling_lat_pulldown', + 12: 'kneeling_underhand_grip_lat_pulldown', + 13: 'lat_pulldown', + 14: 'mixed_grip_chin_up', + 15: 'weighted_mixed_grip_chin_up', + 16: 'mixed_grip_pull_up', + 17: 'weighted_mixed_grip_pull_up', + 18: 'reverse_grip_pulldown', + 19: 'standing_cable_pullover', + 20: 'straight_arm_pulldown', + 21: 'swiss_ball_ez_bar_pullover', + 22: 'towel_pull_up', + 23: 'weighted_towel_pull_up', + 24: 'weighted_pull_up', + 25: 'wide_grip_lat_pulldown', + 26: 'wide_grip_pull_up', + 27: 'weighted_wide_grip_pull_up', + 28: 'burpee_pull_up', + 29: 'weighted_burpee_pull_up', + 30: 'jumping_pull_ups', + 31: 'weighted_jumping_pull_ups', + 32: 'kipping_pull_up', + 33: 'weighted_kipping_pull_up', + 34: 'l_pull_up', + 35: 'weighted_l_pull_up', + 36: 'suspended_chin_up', + 37: 'weighted_suspended_chin_up', + 38: 'pull_up', + }, + ), + 'push_up_exercise_name': FieldType( + name='push_up_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'chest_press_with_band', + 1: 'alternating_staggered_push_up', + 2: 'weighted_alternating_staggered_push_up', + 3: 'alternating_hands_medicine_ball_push_up', + 4: 'weighted_alternating_hands_medicine_ball_push_up', + 5: 'bosu_ball_push_up', + 6: 'weighted_bosu_ball_push_up', + 7: 'clapping_push_up', + 8: 'weighted_clapping_push_up', + 9: 'close_grip_medicine_ball_push_up', + 10: 'weighted_close_grip_medicine_ball_push_up', + 11: 'close_hands_push_up', + 12: 'weighted_close_hands_push_up', + 13: 'decline_push_up', + 14: 'weighted_decline_push_up', + 15: 'diamond_push_up', + 16: 'weighted_diamond_push_up', + 17: 'explosive_crossover_push_up', + 18: 'weighted_explosive_crossover_push_up', + 19: 'explosive_push_up', + 20: 'weighted_explosive_push_up', + 21: 'feet_elevated_side_to_side_push_up', + 22: 'weighted_feet_elevated_side_to_side_push_up', + 23: 'hand_release_push_up', + 24: 'weighted_hand_release_push_up', + 25: 'handstand_push_up', + 26: 'weighted_handstand_push_up', + 27: 'incline_push_up', + 28: 'weighted_incline_push_up', + 29: 'isometric_explosive_push_up', + 30: 'weighted_isometric_explosive_push_up', + 31: 'judo_push_up', + 32: 'weighted_judo_push_up', + 33: 'kneeling_push_up', + 34: 'weighted_kneeling_push_up', + 35: 'medicine_ball_chest_pass', + 36: 'medicine_ball_push_up', + 37: 'weighted_medicine_ball_push_up', + 38: 'one_arm_push_up', + 39: 'weighted_one_arm_push_up', + 40: 'weighted_push_up', + 41: 'push_up_and_row', + 42: 'weighted_push_up_and_row', + 43: 'push_up_plus', + 44: 'weighted_push_up_plus', + 45: 'push_up_with_feet_on_swiss_ball', + 46: 'weighted_push_up_with_feet_on_swiss_ball', + 47: 'push_up_with_one_hand_on_medicine_ball', + 48: 'weighted_push_up_with_one_hand_on_medicine_ball', + 49: 'shoulder_push_up', + 50: 'weighted_shoulder_push_up', + 51: 'single_arm_medicine_ball_push_up', + 52: 'weighted_single_arm_medicine_ball_push_up', + 53: 'spiderman_push_up', + 54: 'weighted_spiderman_push_up', + 55: 'stacked_feet_push_up', + 56: 'weighted_stacked_feet_push_up', + 57: 'staggered_hands_push_up', + 58: 'weighted_staggered_hands_push_up', + 59: 'suspended_push_up', + 60: 'weighted_suspended_push_up', + 61: 'swiss_ball_push_up', + 62: 'weighted_swiss_ball_push_up', + 63: 'swiss_ball_push_up_plus', + 64: 'weighted_swiss_ball_push_up_plus', + 65: 't_push_up', + 66: 'weighted_t_push_up', + 67: 'triple_stop_push_up', + 68: 'weighted_triple_stop_push_up', + 69: 'wide_hands_push_up', + 70: 'weighted_wide_hands_push_up', + 71: 'parallette_handstand_push_up', + 72: 'weighted_parallette_handstand_push_up', + 73: 'ring_handstand_push_up', + 74: 'weighted_ring_handstand_push_up', + 75: 'ring_push_up', + 76: 'weighted_ring_push_up', + 77: 'push_up', + }, + ), 'pwr_zone_calc': FieldType( name='pwr_zone_calc', base_type=BASE_TYPES[0x00], # enum @@ -1605,6 +2725,56 @@ 3: 'transition_to_standing', }, ), + 'row_exercise_name': FieldType( + name='row_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'barbell_straight_leg_deadlift_to_row', + 1: 'cable_row_standing', + 2: 'dumbbell_row', + 3: 'elevated_feet_inverted_row', + 4: 'weighted_elevated_feet_inverted_row', + 5: 'face_pull', + 6: 'face_pull_with_external_rotation', + 7: 'inverted_row_with_feet_on_swiss_ball', + 8: 'weighted_inverted_row_with_feet_on_swiss_ball', + 9: 'kettlebell_row', + 10: 'modified_inverted_row', + 11: 'weighted_modified_inverted_row', + 12: 'neutral_grip_alternating_dumbbell_row', + 13: 'one_arm_bent_over_row', + 14: 'one_legged_dumbbell_row', + 15: 'renegade_row', + 16: 'reverse_grip_barbell_row', + 17: 'rope_handle_cable_row', + 18: 'seated_cable_row', + 19: 'seated_dumbbell_row', + 20: 'single_arm_cable_row', + 21: 'single_arm_cable_row_and_rotation', + 22: 'single_arm_inverted_row', + 23: 'weighted_single_arm_inverted_row', + 24: 'single_arm_neutral_grip_dumbbell_row', + 25: 'single_arm_neutral_grip_dumbbell_row_and_rotation', + 26: 'suspended_inverted_row', + 27: 'weighted_suspended_inverted_row', + 28: 't_bar_row', + 29: 'towel_grip_inverted_row', + 30: 'weighted_towel_grip_inverted_row', + 31: 'underhand_grip_cable_row', + 32: 'v_grip_cable_row', + 33: 'wide_grip_seated_cable_row', + }, + ), + 'run_exercise_name': FieldType( + name='run_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'run', + 1: 'walk', + 2: 'jog', + 3: 'sprint', + }, + ), 'schedule': FieldType( name='schedule', base_type=BASE_TYPES[0x00], # enum @@ -1662,6 +2832,7 @@ 0: 'accelerometer', 1: 'gyroscope', 2: 'compass', # Magnetometer + 3: 'barometer', }, ), 'session_trigger': FieldType( @@ -1674,6 +2845,106 @@ 3: 'fitness_equipment', # Auto sport change caused by user linking to fitness equipment. }, ), + 'set_type': FieldType( + name='set_type', + base_type=BASE_TYPES[0x02], # uint8 + values={ + 0: 'rest', + 1: 'active', + }, + ), + 'shoulder_press_exercise_name': FieldType( + name='shoulder_press_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'alternating_dumbbell_shoulder_press', + 1: 'arnold_press', + 2: 'barbell_front_squat_to_push_press', + 3: 'barbell_push_press', + 4: 'barbell_shoulder_press', + 5: 'dead_curl_press', + 6: 'dumbbell_alternating_shoulder_press_and_twist', + 7: 'dumbbell_hammer_curl_to_lunge_to_press', + 8: 'dumbbell_push_press', + 9: 'floor_inverted_shoulder_press', + 10: 'weighted_floor_inverted_shoulder_press', + 11: 'inverted_shoulder_press', + 12: 'weighted_inverted_shoulder_press', + 13: 'one_arm_push_press', + 14: 'overhead_barbell_press', + 15: 'overhead_dumbbell_press', + 16: 'seated_barbell_shoulder_press', + 17: 'seated_dumbbell_shoulder_press', + 18: 'single_arm_dumbbell_shoulder_press', + 19: 'single_arm_step_up_and_press', + 20: 'smith_machine_overhead_press', + 21: 'split_stance_hammer_curl_to_press', + 22: 'swiss_ball_dumbbell_shoulder_press', + 23: 'weight_plate_front_raise', + }, + ), + 'shoulder_stability_exercise_name': FieldType( + name='shoulder_stability_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: '90_degree_cable_external_rotation', + 1: 'band_external_rotation', + 2: 'band_internal_rotation', + 3: 'bent_arm_lateral_raise_and_external_rotation', + 4: 'cable_external_rotation', + 5: 'dumbbell_face_pull_with_external_rotation', + 6: 'floor_i_raise', + 7: 'weighted_floor_i_raise', + 8: 'floor_t_raise', + 9: 'weighted_floor_t_raise', + 10: 'floor_y_raise', + 11: 'weighted_floor_y_raise', + 12: 'incline_i_raise', + 13: 'weighted_incline_i_raise', + 14: 'incline_l_raise', + 15: 'weighted_incline_l_raise', + 16: 'incline_t_raise', + 17: 'weighted_incline_t_raise', + 18: 'incline_w_raise', + 19: 'weighted_incline_w_raise', + 20: 'incline_y_raise', + 21: 'weighted_incline_y_raise', + 22: 'lying_external_rotation', + 23: 'seated_dumbbell_external_rotation', + 24: 'standing_l_raise', + 25: 'swiss_ball_i_raise', + 26: 'weighted_swiss_ball_i_raise', + 27: 'swiss_ball_t_raise', + 28: 'weighted_swiss_ball_t_raise', + 29: 'swiss_ball_w_raise', + 30: 'weighted_swiss_ball_w_raise', + 31: 'swiss_ball_y_raise', + 32: 'weighted_swiss_ball_y_raise', + }, + ), + 'shrug_exercise_name': FieldType( + name='shrug_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'barbell_jump_shrug', + 1: 'barbell_shrug', + 2: 'barbell_upright_row', + 3: 'behind_the_back_smith_machine_shrug', + 4: 'dumbbell_jump_shrug', + 5: 'dumbbell_shrug', + 6: 'dumbbell_upright_row', + 7: 'incline_dumbbell_shrug', + 8: 'overhead_barbell_shrug', + 9: 'overhead_dumbbell_shrug', + 10: 'scaption_and_shrug', + 11: 'scapular_retraction', + 12: 'serratus_chair_shrug', + 13: 'weighted_serratus_chair_shrug', + 14: 'serratus_shrug', + 15: 'weighted_serratus_shrug', + 16: 'wide_grip_jump_shrug', + }, + ), 'side': FieldType( name='side', base_type=BASE_TYPES[0x00], # enum @@ -1682,6 +2953,50 @@ 1: 'left', }, ), + 'sit_up_exercise_name': FieldType( + name='sit_up_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'alternating_sit_up', + 1: 'weighted_alternating_sit_up', + 2: 'bent_knee_v_up', + 3: 'weighted_bent_knee_v_up', + 4: 'butterfly_sit_up', + 5: 'weighted_butterfly_situp', + 6: 'cross_punch_roll_up', + 7: 'weighted_cross_punch_roll_up', + 8: 'crossed_arms_sit_up', + 9: 'weighted_crossed_arms_sit_up', + 10: 'get_up_sit_up', + 11: 'weighted_get_up_sit_up', + 12: 'hovering_sit_up', + 13: 'weighted_hovering_sit_up', + 14: 'kettlebell_sit_up', + 15: 'medicine_ball_alternating_v_up', + 16: 'medicine_ball_sit_up', + 17: 'medicine_ball_v_up', + 18: 'modified_sit_up', + 19: 'negative_sit_up', + 20: 'one_arm_full_sit_up', + 21: 'reclining_circle', + 22: 'weighted_reclining_circle', + 23: 'reverse_curl_up', + 24: 'weighted_reverse_curl_up', + 25: 'single_leg_swiss_ball_jackknife', + 26: 'weighted_single_leg_swiss_ball_jackknife', + 27: 'the_teaser', + 28: 'the_teaser_weighted', + 29: 'three_part_roll_down', + 30: 'weighted_three_part_roll_down', + 31: 'v_up', + 32: 'weighted_v_up', + 33: 'weighted_russian_twist_on_swiss_ball', + 34: 'weighted_sit_up', + 35: 'x_abs', + 36: 'weighted_x_abs', + 37: 'sit_up', + }, + ), 'source_type': FieldType( name='source_type', base_type=BASE_TYPES[0x00], # enum @@ -1856,6 +3171,99 @@ 8: 'touring', }, ), + 'squat_exercise_name': FieldType( + name='squat_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'leg_press', + 1: 'back_squat_with_body_bar', + 2: 'back_squats', + 3: 'weighted_back_squats', + 4: 'balancing_squat', + 5: 'weighted_balancing_squat', + 6: 'barbell_back_squat', + 7: 'barbell_box_squat', + 8: 'barbell_front_squat', + 9: 'barbell_hack_squat', + 10: 'barbell_hang_squat_snatch', + 11: 'barbell_lateral_step_up', + 12: 'barbell_quarter_squat', + 13: 'barbell_siff_squat', + 14: 'barbell_squat_snatch', + 15: 'barbell_squat_with_heels_raised', + 16: 'barbell_stepover', + 17: 'barbell_step_up', + 18: 'bench_squat_with_rotational_chop', + 19: 'weighted_bench_squat_with_rotational_chop', + 20: 'body_weight_wall_squat', + 21: 'weighted_wall_squat', + 22: 'box_step_squat', + 23: 'weighted_box_step_squat', + 24: 'braced_squat', + 25: 'crossed_arm_barbell_front_squat', + 26: 'crossover_dumbbell_step_up', + 27: 'dumbbell_front_squat', + 28: 'dumbbell_split_squat', + 29: 'dumbbell_squat', + 30: 'dumbbell_squat_clean', + 31: 'dumbbell_stepover', + 32: 'dumbbell_step_up', + 33: 'elevated_single_leg_squat', + 34: 'weighted_elevated_single_leg_squat', + 35: 'figure_four_squats', + 36: 'weighted_figure_four_squats', + 37: 'goblet_squat', + 38: 'kettlebell_squat', + 39: 'kettlebell_swing_overhead', + 40: 'kettlebell_swing_with_flip_to_squat', + 41: 'lateral_dumbbell_step_up', + 42: 'one_legged_squat', + 43: 'overhead_dumbbell_squat', + 44: 'overhead_squat', + 45: 'partial_single_leg_squat', + 46: 'weighted_partial_single_leg_squat', + 47: 'pistol_squat', + 48: 'weighted_pistol_squat', + 49: 'plie_slides', + 50: 'weighted_plie_slides', + 51: 'plie_squat', + 52: 'weighted_plie_squat', + 53: 'prisoner_squat', + 54: 'weighted_prisoner_squat', + 55: 'single_leg_bench_get_up', + 56: 'weighted_single_leg_bench_get_up', + 57: 'single_leg_bench_squat', + 58: 'weighted_single_leg_bench_squat', + 59: 'single_leg_squat_on_swiss_ball', + 60: 'weighted_single_leg_squat_on_swiss_ball', + 61: 'squat', + 62: 'weighted_squat', + 63: 'squats_with_band', + 64: 'staggered_squat', + 65: 'weighted_staggered_squat', + 66: 'step_up', + 67: 'weighted_step_up', + 68: 'suitcase_squats', + 69: 'sumo_squat', + 70: 'sumo_squat_slide_in', + 71: 'weighted_sumo_squat_slide_in', + 72: 'sumo_squat_to_high_pull', + 73: 'sumo_squat_to_stand', + 74: 'weighted_sumo_squat_to_stand', + 75: 'sumo_squat_with_rotation', + 76: 'weighted_sumo_squat_with_rotation', + 77: 'swiss_ball_body_weight_wall_squat', + 78: 'weighted_swiss_ball_wall_squat', + 79: 'thrusters', + 80: 'uneven_squat', + 81: 'weighted_uneven_squat', + 82: 'waist_slimming_squat', + 83: 'wall_ball', + 84: 'wide_stance_barbell_squat', + 85: 'wide_stance_goblet_squat', + 86: 'zercher_squat', + }, + ), 'stroke_type': FieldType( name='stroke_type', base_type=BASE_TYPES[0x00], # enum @@ -1896,7 +3304,7 @@ 21: 'warm_up', # Tennis 22: 'match', # Tennis 23: 'exercise', # Tennis - 24: 'challenge', # Tennis + 24: 'challenge', 25: 'indoor_skiing', # Fitness Equipment 26: 'cardio_training', # Training 27: 'indoor_walking', # Walking/Fitness Equipment @@ -1925,6 +3333,13 @@ 50: 'navigate', 51: 'track_me', 52: 'map', + 53: 'single_gas_diving', # Diving + 54: 'multi_gas_diving', # Diving + 55: 'gauge_diving', # Diving + 56: 'apnea_diving', # Diving + 57: 'apnea_hunting', # Diving + 58: 'virtual_activity', + 59: 'obstacle', # Used for events where participants run, crawl through mud, climb over walls, etc. 254: 'all', }, ), @@ -2101,6 +3516,89 @@ 2: 'fitness_equipment', }, ), + 'tissue_model_type': FieldType( + name='tissue_model_type', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'zhl_16c', # Buhlmann's decompression algorithm, version C + }, + ), + 'tone': FieldType( + name='tone', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'off', + 1: 'tone', + 2: 'vibrate', + 3: 'tone_and_vibrate', + }, + ), + 'total_body_exercise_name': FieldType( + name='total_body_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'burpee', + 1: 'weighted_burpee', + 2: 'burpee_box_jump', + 3: 'weighted_burpee_box_jump', + 4: 'high_pull_burpee', + 5: 'man_makers', + 6: 'one_arm_burpee', + 7: 'squat_thrusts', + 8: 'weighted_squat_thrusts', + 9: 'squat_plank_push_up', + 10: 'weighted_squat_plank_push_up', + 11: 'standing_t_rotation_balance', + 12: 'weighted_standing_t_rotation_balance', + }, + ), + 'triceps_extension_exercise_name': FieldType( + name='triceps_extension_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'bench_dip', + 1: 'weighted_bench_dip', + 2: 'body_weight_dip', + 3: 'cable_kickback', + 4: 'cable_lying_triceps_extension', + 5: 'cable_overhead_triceps_extension', + 6: 'dumbbell_kickback', + 7: 'dumbbell_lying_triceps_extension', + 8: 'ez_bar_overhead_triceps_extension', + 9: 'incline_dip', + 10: 'weighted_incline_dip', + 11: 'incline_ez_bar_lying_triceps_extension', + 12: 'lying_dumbbell_pullover_to_extension', + 13: 'lying_ez_bar_triceps_extension', + 14: 'lying_triceps_extension_to_close_grip_bench_press', + 15: 'overhead_dumbbell_triceps_extension', + 16: 'reclining_triceps_press', + 17: 'reverse_grip_pressdown', + 18: 'reverse_grip_triceps_pressdown', + 19: 'rope_pressdown', + 20: 'seated_barbell_overhead_triceps_extension', + 21: 'seated_dumbbell_overhead_triceps_extension', + 22: 'seated_ez_bar_overhead_triceps_extension', + 23: 'seated_single_arm_overhead_dumbbell_extension', + 24: 'single_arm_dumbbell_overhead_triceps_extension', + 25: 'single_dumbbell_seated_overhead_triceps_extension', + 26: 'single_leg_bench_dip_and_kick', + 27: 'weighted_single_leg_bench_dip_and_kick', + 28: 'single_leg_dip', + 29: 'weighted_single_leg_dip', + 30: 'static_lying_triceps_extension', + 31: 'suspended_dip', + 32: 'weighted_suspended_dip', + 33: 'swiss_ball_dumbbell_lying_triceps_extension', + 34: 'swiss_ball_ez_bar_lying_triceps_extension', + 35: 'swiss_ball_ez_bar_overhead_triceps_extension', + 36: 'tabletop_dip', + 37: 'weighted_tabletop_dip', + 38: 'triceps_extension_on_floor', + 39: 'triceps_pressdown', + 40: 'weighted_dip', + }, + ), 'turn_type': FieldType( name='turn_type', base_type=BASE_TYPES[0x00], # enum @@ -2157,6 +3655,43 @@ 0xFFFE: 'portable_max', }, ), + 'warm_up_exercise_name': FieldType( + name='warm_up_exercise_name', + base_type=BASE_TYPES[0x84], # uint16 + values={ + 0: 'quadruped_rocking', + 1: 'neck_tilts', + 2: 'ankle_circles', + 3: 'ankle_dorsiflexion_with_band', + 4: 'ankle_internal_rotation', + 5: 'arm_circles', + 6: 'bent_over_reach_to_sky', + 7: 'cat_camel', + 8: 'elbow_to_foot_lunge', + 9: 'forward_and_backward_leg_swings', + 10: 'groiners', + 11: 'inverted_hamstring_stretch', + 12: 'lateral_duck_under', + 13: 'neck_rotations', + 14: 'opposite_arm_and_leg_balance', + 15: 'reach_roll_and_lift', + 16: 'scorpion', + 17: 'shoulder_circles', + 18: 'side_to_side_leg_swings', + 19: 'sleeper_stretch', + 20: 'slide_out', + 21: 'swiss_ball_hip_crossover', + 22: 'swiss_ball_reach_roll_and_lift', + 23: 'swiss_ball_windshield_wipers', + 24: 'thoracic_rotation', + 25: 'walking_high_kicks', + 26: 'walking_high_knees', + 27: 'walking_knee_hugs', + 28: 'walking_leg_cradles', + 29: 'walkout', + 30: 'walkout_from_push_up_position', + }, + ), 'watchface_mode': FieldType( name='watchface_mode', base_type=BASE_TYPES[0x00], # enum @@ -2167,6 +3702,16 @@ 3: 'disabled', }, ), + 'water_type': FieldType( + name='water_type', + base_type=BASE_TYPES[0x00], # enum + values={ + 0: 'fresh', + 1: 'salt', + 2: 'en13319', + 3: 'custom', + }, + ), 'weather_report': FieldType( name='weather_report', base_type=BASE_TYPES[0x00], # enum @@ -2346,6 +3891,7 @@ 26: 'power_lap_greater_than', 27: 'repeat_until_training_peaks_tss', 28: 'repetition_time', + 29: 'reps', }, ), 'wkt_step_target': FieldType( @@ -2421,7 +3967,7 @@ MESSAGE_TYPES = { - ############################ Common Messages ############################# + # **************************** Common Messages ***************************** 0: MessageType( # Must be first message in file. name='file_id', mesg_num=0, @@ -2492,7 +4038,7 @@ ), - #################################### #################################### + # ************************************ ************************************ 1: MessageType( name='capabilities', mesg_num=1, @@ -2519,249 +4065,95 @@ ), }, ), - 3: MessageType( - name='user_profile', - mesg_num=3, + 4: MessageType( + name='hrm_profile', + mesg_num=4, fields={ 0: Field( - name='friendly_name', - type=BASE_TYPES[0x07], # string + name='enabled', + type=FIELD_TYPES['bool'], def_num=0, ), 1: Field( - name='gender', - type=FIELD_TYPES['gender'], + name='hrm_ant_id', + type=BASE_TYPES[0x8B], # uint16z def_num=1, ), 2: Field( - name='age', - type=BASE_TYPES[0x02], # uint8 + name='log_hrv', + type=FIELD_TYPES['bool'], def_num=2, - units='years', ), 3: Field( - name='height', - type=BASE_TYPES[0x02], # uint8 + name='hrm_ant_id_trans_type', + type=BASE_TYPES[0x0A], # uint8z + def_num=3, + ), + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), + }, + ), + 5: MessageType( + name='sdm_profile', + mesg_num=5, + fields={ + 0: Field( + name='enabled', + type=FIELD_TYPES['bool'], + def_num=0, + ), + 1: Field( + name='sdm_ant_id', + type=BASE_TYPES[0x8B], # uint16z + def_num=1, + ), + 2: Field( + name='sdm_cal_factor', + type=BASE_TYPES[0x84], # uint16 + def_num=2, + scale=10, + units='%', + ), + 3: Field( + name='odometer', + type=BASE_TYPES[0x86], # uint32 def_num=3, scale=100, units='m', ), - 4: Field( - name='weight', - type=BASE_TYPES[0x84], # uint16 + 4: Field( # Use footpod for speed source instead of GPS + name='speed_source', + type=FIELD_TYPES['bool'], def_num=4, - scale=10, - units='kg', ), 5: Field( - name='language', - type=FIELD_TYPES['language'], + name='sdm_ant_id_trans_type', + type=BASE_TYPES[0x0A], # uint8z def_num=5, ), - 6: Field( - name='elev_setting', - type=FIELD_TYPES['display_measure'], - def_num=6, - ), - 7: Field( - name='weight_setting', - type=FIELD_TYPES['display_measure'], + 7: Field( # Rollover counter that can be used to extend the odometer + name='odometer_rollover', + type=BASE_TYPES[0x02], # uint8 def_num=7, ), - 8: Field( - name='resting_heart_rate', - type=BASE_TYPES[0x02], # uint8 - def_num=8, - units='bpm', + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, ), - 9: Field( - name='default_max_running_heart_rate', - type=BASE_TYPES[0x02], # uint8 - def_num=9, - units='bpm', - ), - 10: Field( - name='default_max_biking_heart_rate', - type=BASE_TYPES[0x02], # uint8 - def_num=10, - units='bpm', - ), - 11: Field( - name='default_max_heart_rate', - type=BASE_TYPES[0x02], # uint8 - def_num=11, - units='bpm', - ), - 12: Field( - name='hr_setting', - type=FIELD_TYPES['display_heart'], - def_num=12, - ), - 13: Field( - name='speed_setting', - type=FIELD_TYPES['display_measure'], - def_num=13, - ), - 14: Field( - name='dist_setting', - type=FIELD_TYPES['display_measure'], - def_num=14, - ), - 16: Field( - name='power_setting', - type=FIELD_TYPES['display_power'], - def_num=16, - ), - 17: Field( - name='activity_class', - type=FIELD_TYPES['activity_class'], - def_num=17, - ), - 18: Field( - name='position_setting', - type=FIELD_TYPES['display_position'], - def_num=18, - ), - 21: Field( - name='temperature_setting', - type=FIELD_TYPES['display_measure'], - def_num=21, - ), - 22: Field( - name='local_id', - type=FIELD_TYPES['user_local_id'], - def_num=22, - ), - 23: Field( - name='global_id', - type=BASE_TYPES[0x0D], # byte - def_num=23, - ), - 28: Field( # Typical wake time - name='wake_time', - type=FIELD_TYPES['localtime_into_day'], - def_num=28, - ), - 29: Field( # Typical bed time - name='sleep_time', - type=FIELD_TYPES['localtime_into_day'], - def_num=29, - ), - 30: Field( - name='height_setting', - type=FIELD_TYPES['display_measure'], - def_num=30, - ), - 31: Field( # User defined running step length set to 0 for auto length - name='user_running_step_length', - type=BASE_TYPES[0x84], # uint16 - def_num=31, - scale=1000, - units='m', - ), - 32: Field( # User defined walking step length set to 0 for auto length - name='user_walking_step_length', - type=BASE_TYPES[0x84], # uint16 - def_num=32, - scale=1000, - units='m', - ), - 254: Field( - name='message_index', - type=FIELD_TYPES['message_index'], - def_num=254, - ), - }, - ), - 4: MessageType( - name='hrm_profile', - mesg_num=4, - fields={ - 0: Field( - name='enabled', - type=FIELD_TYPES['bool'], - def_num=0, - ), - 1: Field( - name='hrm_ant_id', - type=BASE_TYPES[0x8B], # uint16z - def_num=1, - ), - 2: Field( - name='log_hrv', - type=FIELD_TYPES['bool'], - def_num=2, - ), - 3: Field( - name='hrm_ant_id_trans_type', - type=BASE_TYPES[0x0A], # uint8z - def_num=3, - ), - 254: Field( - name='message_index', - type=FIELD_TYPES['message_index'], - def_num=254, - ), - }, - ), - 5: MessageType( - name='sdm_profile', - mesg_num=5, - fields={ - 0: Field( - name='enabled', - type=FIELD_TYPES['bool'], - def_num=0, - ), - 1: Field( - name='sdm_ant_id', - type=BASE_TYPES[0x8B], # uint16z - def_num=1, - ), - 2: Field( - name='sdm_cal_factor', - type=BASE_TYPES[0x84], # uint16 - def_num=2, - scale=10, - units='%', - ), - 3: Field( - name='odometer', - type=BASE_TYPES[0x86], # uint32 - def_num=3, - scale=100, - units='m', - ), - 4: Field( # Use footpod for speed source instead of GPS - name='speed_source', - type=FIELD_TYPES['bool'], - def_num=4, - ), - 5: Field( - name='sdm_ant_id_trans_type', - type=BASE_TYPES[0x0A], # uint8z - def_num=5, - ), - 7: Field( # Rollover counter that can be used to extend the odometer - name='odometer_rollover', - type=BASE_TYPES[0x02], # uint8 - def_num=7, - ), - 254: Field( - name='message_index', - type=FIELD_TYPES['message_index'], - def_num=254, - ), - }, - ), - 6: MessageType( - name='bike_profile', - mesg_num=6, - fields={ - 0: Field( - name='name', - type=BASE_TYPES[0x07], # string - def_num=0, + }, + ), + 6: MessageType( + name='bike_profile', + mesg_num=6, + fields={ + 0: Field( + name='name', + type=BASE_TYPES[0x07], # string + def_num=0, ), 1: Field( name='sport', @@ -3124,7 +4516,7 @@ def_num=11, units='kcal', ), - 13: Field( # If New Leaf + 13: Field( name='total_fat_calories', type=BASE_TYPES[0x84], # uint16 def_num=13, @@ -5142,6 +6534,56 @@ scale=10, units='mm', ), + 91: Field( # Includes atmospheric pressure + name='absolute_pressure', + type=BASE_TYPES[0x86], # uint32 + def_num=91, + units='Pa', + ), + 92: Field( # 0 if above water + name='depth', + type=BASE_TYPES[0x86], # uint32 + def_num=92, + scale=1000, + units='m', + ), + 93: Field( # 0 if above water + name='next_stop_depth', + type=BASE_TYPES[0x86], # uint32 + def_num=93, + scale=1000, + units='m', + ), + 94: Field( + name='next_stop_time', + type=BASE_TYPES[0x86], # uint32 + def_num=94, + units='s', + ), + 95: Field( + name='time_to_surface', + type=BASE_TYPES[0x86], # uint32 + def_num=95, + units='s', + ), + 96: Field( + name='ndl_time', + type=BASE_TYPES[0x86], # uint32 + def_num=96, + units='s', + ), + 97: Field( + name='cns_load', + type=BASE_TYPES[0x02], # uint8 + def_num=97, + units='percent', + ), + 98: Field( + name='n2_load', + type=BASE_TYPES[0x84], # uint16 + def_num=98, + units='percent', + ), 253: FIELD_TYPE_TIMESTAMP, }, ), @@ -5565,7 +7007,7 @@ ), 1: Field( name='device_type', - type=FIELD_TYPES['antplus_device_type'], # uint8 + type=BASE_TYPES[0x02], # uint8 def_num=1, subfields=( SubField( @@ -5793,6 +7235,19 @@ ), ), ), + SubField( + name='duration_reps', + def_num=2, + type=BASE_TYPES[0x86], # uint32 + ref_fields=( + ReferenceField( + name='duration_type', + def_num=1, + value='reps', + raw_value=29, + ), + ), + ), SubField( # message_index of step to loop back to. Steps are assumed to be in the order by message_index. custom_name and intensity members are undefined for this duration type. name='duration_step', def_num=2, @@ -6188,6 +7643,28 @@ type=FIELD_TYPES['workout_equipment'], def_num=9, ), + 10: Field( + name='exercise_category', + type=FIELD_TYPES['exercise_category'], + def_num=10, + ), + 11: Field( + name='exercise_name', + type=BASE_TYPES[0x84], # uint16 + def_num=11, + ), + 12: Field( + name='exercise_weight', + type=BASE_TYPES[0x84], # uint16 + def_num=12, + scale=100, + units='kg', + ), + 13: Field( + name='weight_display_unit', + type=FIELD_TYPES['fit_base_unit'], + def_num=13, + ), 254: Field( name='message_index', type=FIELD_TYPES['message_index'], @@ -7339,6 +8816,8 @@ name='time256', type=BASE_TYPES[0x02], # uint8 def_num=1, + scale=256, + units='s', components=( ComponentField( name='fractional_timestamp', @@ -9126,17 +10605,505 @@ 253: FIELD_TYPE_TIMESTAMP, # Whole second part of the timestamp }, ), - - - ######################### Activity File Messages ######################### - 34: MessageType( - name='activity', - mesg_num=34, + 209: MessageType( + name='barometer_data', + mesg_num=209, fields={ - 0: Field( # Exclude pauses - name='total_timer_time', - type=BASE_TYPES[0x86], # uint32 - def_num=0, + 0: Field( # Millisecond part of the timestamp. + name='timestamp_ms', + type=BASE_TYPES[0x84], # uint16 + def_num=0, + units='ms', + ), + 1: Field( # Each time in the array describes the time at which the barometer sample with the corrosponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal + name='sample_time_offset', + type=BASE_TYPES[0x84], # uint16 + def_num=1, + units='ms', + ), + 2: Field( # These are the raw ADC reading. The samples may span across seconds. A conversion will need to be done on this data once read. + name='baro_pres', + type=BASE_TYPES[0x86], # uint32 + def_num=2, + units='Pa', + ), + 253: FIELD_TYPE_TIMESTAMP, # Whole second part of the timestamp + }, + ), + 210: MessageType( + name='one_d_sensor_calibration', + mesg_num=210, + fields={ + 0: Field( # Indicates which sensor the calibration is for + name='sensor_type', + type=FIELD_TYPES['sensor_type'], + def_num=0, + ), + 1: Field( # Calibration factor used to convert from raw ADC value to degrees, g, etc. + name='calibration_factor', + type=BASE_TYPES[0x86], # uint32 + def_num=1, + subfields=( + SubField( # Barometer calibration factor + name='baro_cal_factor', + def_num=1, + type=BASE_TYPES[0x86], # uint32 + units='Pa', + ref_fields=( + ReferenceField( + name='sensor_type', + def_num=0, + value='barometer', + raw_value=3, + ), + ), + ), + ), + ), + 2: Field( # Calibration factor divisor + name='calibration_divisor', + type=BASE_TYPES[0x86], # uint32 + def_num=2, + units='counts', + ), + 3: Field( # Level shift value used to shift the ADC value back into range + name='level_shift', + type=BASE_TYPES[0x86], # uint32 + def_num=3, + ), + 4: Field( # Internal Calibration factor + name='offset_cal', + type=BASE_TYPES[0x85], # sint32 + def_num=4, + ), + 253: FIELD_TYPE_TIMESTAMP, # Whole second part of the timestamp + }, + ), + 225: MessageType( + name='set', + mesg_num=225, + fields={ + 0: Field( + name='duration', + type=BASE_TYPES[0x86], # uint32 + def_num=0, + scale=1000, + units='s', + ), + 3: Field( # # of repitions of the movement + name='repetitions', + type=BASE_TYPES[0x84], # uint16 + def_num=3, + ), + 4: Field( # Amount of weight applied for the set + name='weight', + type=BASE_TYPES[0x84], # uint16 + def_num=4, + scale=16, + units='kg', + ), + 5: Field( + name='set_type', + type=FIELD_TYPES['set_type'], + def_num=5, + ), + 6: Field( # Start time of the set + name='start_time', + type=FIELD_TYPES['date_time'], + def_num=6, + ), + 7: Field( + name='category', + type=FIELD_TYPES['exercise_category'], + def_num=7, + ), + 8: Field( # Based on the associated category, see [category]_exercise_names + name='category_subtype', + type=BASE_TYPES[0x84], # uint16 + def_num=8, + ), + 9: Field( + name='weight_display_unit', + type=FIELD_TYPES['fit_base_unit'], + def_num=9, + ), + 10: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=10, + ), + 11: Field( + name='wkt_step_index', + type=FIELD_TYPES['message_index'], + def_num=11, + ), + 254: Field( # Timestamp of the set + name='timestamp', + type=FIELD_TYPES['date_time'], + def_num=254, + ), + }, + ), + 227: MessageType( # Value from 1 to 100 calculated by FirstBeat + name='stress_level', + mesg_num=227, + fields={ + 0: Field( + name='stress_level_value', + type=BASE_TYPES[0x83], # sint16 + def_num=0, + ), + 1: Field( # Time stress score was calculated + name='stress_level_time', + type=FIELD_TYPES['date_time'], + def_num=1, + units='s', + ), + }, + ), + 258: MessageType( + name='dive_settings', + mesg_num=258, + fields={ + 0: Field( + name='name', + type=BASE_TYPES[0x07], # string + def_num=0, + ), + 1: Field( + name='model', + type=FIELD_TYPES['tissue_model_type'], + def_num=1, + ), + 2: Field( + name='gf_low', + type=BASE_TYPES[0x02], # uint8 + def_num=2, + units='percent', + ), + 3: Field( + name='gf_high', + type=BASE_TYPES[0x02], # uint8 + def_num=3, + units='percent', + ), + 4: Field( + name='water_type', + type=FIELD_TYPES['water_type'], + def_num=4, + ), + 5: Field( # Fresh water is usually 1000; salt water is usually 1025 + name='water_density', + type=BASE_TYPES[0x88], # float32 + def_num=5, + units='kg/m^3', + ), + 6: Field( # Typically 1.40 + name='po2_warn', + type=BASE_TYPES[0x02], # uint8 + def_num=6, + scale=100, + units='percent', + ), + 7: Field( # Typically 1.60 + name='po2_critical', + type=BASE_TYPES[0x02], # uint8 + def_num=7, + scale=100, + units='percent', + ), + 8: Field( + name='po2_deco', + type=BASE_TYPES[0x02], # uint8 + def_num=8, + scale=100, + units='percent', + ), + 9: Field( + name='safety_stop_enabled', + type=FIELD_TYPES['bool'], + def_num=9, + ), + 10: Field( + name='bottom_depth', + type=BASE_TYPES[0x88], # float32 + def_num=10, + ), + 11: Field( + name='bottom_time', + type=BASE_TYPES[0x86], # uint32 + def_num=11, + ), + 12: Field( + name='apnea_countdown_enabled', + type=FIELD_TYPES['bool'], + def_num=12, + ), + 13: Field( + name='apnea_countdown_time', + type=BASE_TYPES[0x86], # uint32 + def_num=13, + ), + 14: Field( + name='backlight_mode', + type=FIELD_TYPES['dive_backlight_mode'], + def_num=14, + ), + 15: Field( + name='backlight_brightness', + type=BASE_TYPES[0x02], # uint8 + def_num=15, + ), + 16: Field( + name='backlight_timeout', + type=FIELD_TYPES['backlight_timeout'], + def_num=16, + ), + 17: Field( # Time between surfacing and ending the activity + name='repeat_dive_interval', + type=BASE_TYPES[0x84], # uint16 + def_num=17, + units='s', + ), + 18: Field( # Time at safety stop (if enabled) + name='safety_stop_time', + type=BASE_TYPES[0x84], # uint16 + def_num=18, + units='s', + ), + 19: Field( + name='heart_rate_source_type', + type=FIELD_TYPES['source_type'], + def_num=19, + ), + 20: Field( + name='heart_rate_source', + type=BASE_TYPES[0x02], # uint8 + def_num=20, + subfields=( + SubField( + name='heart_rate_antplus_device_type', + def_num=20, + type=FIELD_TYPES['antplus_device_type'], + ref_fields=( + ReferenceField( + name='heart_rate_source_type', + def_num=19, + value='antplus', + raw_value=1, + ), + ), + ), + SubField( + name='heart_rate_local_device_type', + def_num=20, + type=FIELD_TYPES['local_device_type'], + ref_fields=( + ReferenceField( + name='heart_rate_source_type', + def_num=19, + value='local', + raw_value=5, + ), + ), + ), + ), + ), + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), + }, + ), + 259: MessageType( + name='dive_gas', + mesg_num=259, + fields={ + 0: Field( + name='helium_content', + type=BASE_TYPES[0x02], # uint8 + def_num=0, + units='percent', + ), + 1: Field( + name='oxygen_content', + type=BASE_TYPES[0x02], # uint8 + def_num=1, + units='percent', + ), + 2: Field( + name='status', + type=FIELD_TYPES['dive_gas_status'], + def_num=2, + ), + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), + }, + ), + 262: MessageType( + name='dive_alarm', + mesg_num=262, + fields={ + 0: Field( + name='depth', + type=BASE_TYPES[0x86], # uint32 + def_num=0, + scale=1000, + units='m', + ), + 1: Field( + name='time', + type=BASE_TYPES[0x85], # sint32 + def_num=1, + units='s', + ), + 2: Field( + name='enabled', + type=FIELD_TYPES['bool'], + def_num=2, + ), + 3: Field( + name='alarm_type', + type=FIELD_TYPES['dive_alarm_type'], + def_num=3, + ), + 4: Field( + name='sound', + type=FIELD_TYPES['tone'], + def_num=4, + ), + 5: Field( + name='dive_types', + type=FIELD_TYPES['sub_sport'], + def_num=5, + ), + 254: Field( # Index of the alarm + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), + }, + ), + 264: MessageType( + name='exercise_title', + mesg_num=264, + fields={ + 0: Field( + name='exercise_category', + type=FIELD_TYPES['exercise_category'], + def_num=0, + ), + 1: Field( + name='exercise_name', + type=BASE_TYPES[0x84], # uint16 + def_num=1, + ), + 2: Field( + name='wkt_step_name', + type=BASE_TYPES[0x07], # string + def_num=2, + ), + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), + }, + ), + 268: MessageType( + name='dive_summary', + mesg_num=268, + fields={ + 0: Field( + name='reference_mesg', + type=FIELD_TYPES['mesg_num'], + def_num=0, + ), + 1: Field( + name='reference_index', + type=FIELD_TYPES['message_index'], + def_num=1, + ), + 2: Field( # 0 if above water + name='avg_depth', + type=BASE_TYPES[0x86], # uint32 + def_num=2, + scale=1000, + units='m', + ), + 3: Field( # 0 if above water + name='max_depth', + type=BASE_TYPES[0x86], # uint32 + def_num=3, + scale=1000, + units='m', + ), + 4: Field( # Time since end of last dive + name='surface_interval', + type=BASE_TYPES[0x86], # uint32 + def_num=4, + units='s', + ), + 5: Field( + name='start_cns', + type=BASE_TYPES[0x02], # uint8 + def_num=5, + units='percent', + ), + 6: Field( + name='end_cns', + type=BASE_TYPES[0x02], # uint8 + def_num=6, + units='percent', + ), + 7: Field( + name='start_n2', + type=BASE_TYPES[0x84], # uint16 + def_num=7, + units='percent', + ), + 8: Field( + name='end_n2', + type=BASE_TYPES[0x84], # uint16 + def_num=8, + units='percent', + ), + 9: Field( + name='o2_toxicity', + type=BASE_TYPES[0x84], # uint16 + def_num=9, + units='OTUs', + ), + 10: Field( + name='dive_number', + type=BASE_TYPES[0x86], # uint32 + def_num=10, + ), + 11: Field( + name='bottom_time', + type=BASE_TYPES[0x86], # uint32 + def_num=11, + scale=1000, + units='s', + ), + 253: FIELD_TYPE_TIMESTAMP, + }, + ), + + + # ************************* Activity File Messages ************************* + 34: MessageType( + name='activity', + mesg_num=34, + fields={ + 0: Field( # Exclude pauses + name='total_timer_time', + type=BASE_TYPES[0x86], # uint32 + def_num=0, scale=1000, units='s', ), @@ -9175,7 +11142,7 @@ ), - ###################### Blood Pressure File Messages ###################### + # ********************** Blood Pressure File Messages ********************** 51: MessageType( name='blood_pressure', mesg_num=51, @@ -9242,7 +11209,7 @@ ), - ########################## Course File Messages ########################## + # ************************** Course File Messages ************************** 31: MessageType( name='course', mesg_num=31, @@ -9271,7 +11238,7 @@ ), - ########################## Device File Messages ########################## + # ************************** Device File Messages ************************** 35: MessageType( name='software', mesg_num=35, @@ -9296,7 +11263,7 @@ ), - ########################## Goals File Messages ########################### + # ************************** Goals File Messages *************************** 15: MessageType( name='goal', mesg_num=15, @@ -9370,7 +11337,7 @@ ), - ######################## Monitoring File Messages ######################## + # ************************ Monitoring File Messages ************************ 103: MessageType( name='monitoring_info', mesg_num=103, @@ -9411,7 +11378,7 @@ ), - ############################# Other Messages ############################# + # ***************************** Other Messages ***************************** 145: MessageType( name='memo_glob', mesg_num=145, @@ -9440,7 +11407,7 @@ ), - ######################### Schedule File Messages ######################### + # ************************* Schedule File Messages ************************* 28: MessageType( name='schedule', mesg_num=28, @@ -9511,7 +11478,7 @@ ), - ######################### Segment File Messages ########################## + # ************************* Segment File Messages ************************** 148: MessageType( # Unique Identification data for a segment file name='segment_id', mesg_num=148, @@ -9565,7 +11532,7 @@ ), - ####################### Segment List File Messages ####################### + # *********************** Segment List File Messages *********************** 151: MessageType( # Summary of the unique segment and leaderboard information associated with a segment file. This message is used to compile a segment list file describing all segment files on a device. The segment list file is used when refreshing the contents of a segment file with the latest available leaderboard information. name='segment_file', mesg_num=151, @@ -9619,7 +11586,7 @@ ), - ######################### Settings File Messages ######################### + # ************************* Settings File Messages ************************* 2: MessageType( name='device_settings', mesg_num=2, @@ -9739,11 +11706,180 @@ type=FIELD_TYPES['display_orientation'], def_num=95, ), + 134: Field( + name='tap_interface', + type=FIELD_TYPES['switch'], + def_num=134, + ), + }, + ), + 3: MessageType( + name='user_profile', + mesg_num=3, + fields={ + 0: Field( + name='friendly_name', + type=BASE_TYPES[0x07], # string + def_num=0, + ), + 1: Field( + name='gender', + type=FIELD_TYPES['gender'], + def_num=1, + ), + 2: Field( + name='age', + type=BASE_TYPES[0x02], # uint8 + def_num=2, + units='years', + ), + 3: Field( + name='height', + type=BASE_TYPES[0x02], # uint8 + def_num=3, + scale=100, + units='m', + ), + 4: Field( + name='weight', + type=BASE_TYPES[0x84], # uint16 + def_num=4, + scale=10, + units='kg', + ), + 5: Field( + name='language', + type=FIELD_TYPES['language'], + def_num=5, + ), + 6: Field( + name='elev_setting', + type=FIELD_TYPES['display_measure'], + def_num=6, + ), + 7: Field( + name='weight_setting', + type=FIELD_TYPES['display_measure'], + def_num=7, + ), + 8: Field( + name='resting_heart_rate', + type=BASE_TYPES[0x02], # uint8 + def_num=8, + units='bpm', + ), + 9: Field( + name='default_max_running_heart_rate', + type=BASE_TYPES[0x02], # uint8 + def_num=9, + units='bpm', + ), + 10: Field( + name='default_max_biking_heart_rate', + type=BASE_TYPES[0x02], # uint8 + def_num=10, + units='bpm', + ), + 11: Field( + name='default_max_heart_rate', + type=BASE_TYPES[0x02], # uint8 + def_num=11, + units='bpm', + ), + 12: Field( + name='hr_setting', + type=FIELD_TYPES['display_heart'], + def_num=12, + ), + 13: Field( + name='speed_setting', + type=FIELD_TYPES['display_measure'], + def_num=13, + ), + 14: Field( + name='dist_setting', + type=FIELD_TYPES['display_measure'], + def_num=14, + ), + 16: Field( + name='power_setting', + type=FIELD_TYPES['display_power'], + def_num=16, + ), + 17: Field( + name='activity_class', + type=FIELD_TYPES['activity_class'], + def_num=17, + ), + 18: Field( + name='position_setting', + type=FIELD_TYPES['display_position'], + def_num=18, + ), + 21: Field( + name='temperature_setting', + type=FIELD_TYPES['display_measure'], + def_num=21, + ), + 22: Field( + name='local_id', + type=FIELD_TYPES['user_local_id'], + def_num=22, + ), + 23: Field( + name='global_id', + type=BASE_TYPES[0x0D], # byte + def_num=23, + ), + 28: Field( # Typical wake time + name='wake_time', + type=FIELD_TYPES['localtime_into_day'], + def_num=28, + ), + 29: Field( # Typical bed time + name='sleep_time', + type=FIELD_TYPES['localtime_into_day'], + def_num=29, + ), + 30: Field( + name='height_setting', + type=FIELD_TYPES['display_measure'], + def_num=30, + ), + 31: Field( # User defined running step length set to 0 for auto length + name='user_running_step_length', + type=BASE_TYPES[0x84], # uint16 + def_num=31, + scale=1000, + units='m', + ), + 32: Field( # User defined walking step length set to 0 for auto length + name='user_walking_step_length', + type=BASE_TYPES[0x84], # uint16 + def_num=32, + scale=1000, + units='m', + ), + 47: Field( + name='depth_setting', + type=FIELD_TYPES['display_measure'], + def_num=47, + ), + 49: Field( + name='dive_count', + type=BASE_TYPES[0x86], # uint32 + def_num=49, + ), + 254: Field( + name='message_index', + type=FIELD_TYPES['message_index'], + def_num=254, + ), }, ), - ###################### Sport Settings File Messages ###################### + # ********************** Sport Settings File Messages ********************** 7: MessageType( name='zones_target', mesg_num=7, @@ -9777,7 +11913,7 @@ ), - ########################## Totals File Messages ########################## + # ************************** Totals File Messages ************************** 33: MessageType( name='totals', mesg_num=33, @@ -9837,7 +11973,7 @@ ), - ####################### Weight Scale File Messages ####################### + # *********************** Weight Scale File Messages *********************** 30: MessageType( name='weight_scale', mesg_num=30, @@ -9924,7 +12060,7 @@ ), - ######################### Workout File Messages ########################## + # ************************* Workout File Messages ************************** 26: MessageType( name='workout', mesg_num=26, diff --git a/fitparse/records.py b/fitparse/records.py index 71edc93..d007f0a 100644 --- a/fitparse/records.py +++ b/fitparse/records.py @@ -299,7 +299,7 @@ class SubField(FieldAndSubFieldBase): class DevField(FieldAndSubFieldBase): __slots__ = ('dev_data_index', 'def_num', 'type', 'name', 'units', 'native_field_num', # The rest of these are just to be compatible with Field objects. They're always None - 'scale', 'offset', 'components', 'subfields') + 'scale', 'offset', 'components', 'subfields') field_type = 'devfield' @@ -318,6 +318,14 @@ def render(self, raw_value): # If it's a tuple, then it's a byte array and unpack it as such # (only type that uses this is compressed speed/distance) if isinstance(raw_value, tuple): + # Profile.xls sometimes contains more components than the read raw + # value is able to hold (typically the *event_timestamp_12* field in + # *hr* messages). + # This test allows to ensure *unpacked_num* is not right-shifted + # more than necessary. + if self.bit_offset and self.bit_offset >= len(raw_value) << 3: + raise ValueError() + unpacked_num = 0 # Unpack byte array as little endian @@ -381,11 +389,23 @@ def calculate(cls, byte_arr, crc=0): def parse_string(string): try: - end = string.index(0x00) - except TypeError: # Python 2 compat - end = string.index('\x00') - - return string[:end].decode('utf-8', errors='replace') or None + try: + s = string[:string.index(0x00)] + except TypeError: # Python 2 compat + s = string[:string.index('\x00')] + except ValueError: + # FIT specification defines the 'string' type as follows: "Null + # terminated string encoded in UTF-8 format". + # + # However 'string' values are not always null-terminated when encoded, + # according to FIT files created by Garmin devices (e.g. DEVICE.FIT file + # from a fenix3). + # + # So in order to be more flexible, in case index() could not find any + # null byte, we just decode the whole bytes-like object. + s = string + + return s.decode(encoding='utf-8', errors='replace') or None # The default base type BASE_TYPE_BYTE = BaseType(name='byte', identifier=0x0D, fmt='B', parse=lambda x: None if all(b == 0xFF for b in x) else x) @@ -405,6 +425,9 @@ def parse_string(string): 0x8B: BaseType(name='uint16z', identifier=0x8B, fmt='H', parse=lambda x: None if x == 0x0 else x), 0x8C: BaseType(name='uint32z', identifier=0x8C, fmt='I', parse=lambda x: None if x == 0x0 else x), 0x0D: BASE_TYPE_BYTE, + 0x8E: BaseType(name='sint64', identifier=0x8E, fmt='q', parse=lambda x: None if x == 0x7FFFFFFFFFFFFFFF else x), + 0x8F: BaseType(name='uint64', identifier=0x8F, fmt='Q', parse=lambda x: None if x == 0xFFFFFFFFFFFFFFFF else x), + 0x90: BaseType(name='uint64z', identifier=0x90, fmt='Q', parse=lambda x: None if x == 0 else x), } diff --git a/fitparse/utils.py b/fitparse/utils.py index d3e427a..d3e4e02 100644 --- a/fitparse/utils.py +++ b/fitparse/utils.py @@ -1,6 +1,9 @@ import io import re -from collections import Iterable +try: + from collections.abc import Iterable +except ImportError: + from collections import Iterable class FitParseError(ValueError): diff --git a/import.py b/import.py new file mode 100644 index 0000000..0f71a1b --- /dev/null +++ b/import.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +from fitparse import FitFile + +import sys +import datetime +import os +import simplejson as json + +from datetime import date + +def importFitAndReturnJson(fileName, fieldName): + fitfile = FitFile(fileName) + return json.dumps(fitfile.get_messages(fieldName, False, True), sort_keys=True, default=str, iterable_as_array=True) + +def importFit(fileName, fieldName): + now = datetime.datetime.now() + + if fileName: + print ("importing file %s" % (fileName)) + print + print ("Start " + now.strftime("%H:%M:%S")) + print + + fitfile = FitFile(fileName) + + # Get all data messages that are of type record + for record in fitfile.get_messages(fieldName): + + # Go through all the data entries in this record + for record_data in record: + + # Print the records name and value (and units if it has any) + if record_data.units: + print (" * %s: %s %s" % ( + record_data.name, record_data.value, record_data.units, + )) + else: + print (" * %s: %s" % (record_data.name, record_data.value)) + print + + now = datetime.datetime.now() + + print + print ("End " + now.strftime("%H:%M:%S")) + print + else: + print ("Missing filname") + + +if __name__ == "__main__": + fileName = False + + if len(sys.argv) >= 2: + fileName = sys.argv[1] + + if len(sys.argv) >= 3: + fieldName = sys.argv[2] + if fieldName == 'all': + fieldName = None; + else: + fieldName = None + + if len(sys.argv) >= 4: + asJson = sys.argv[3] + else: + asJson = False + + if asJson: + print (importFitAndReturnJson(fileName, fieldName)) + else: + print (importFit(fileName, fieldName)) diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 0000000..b6627b4 --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,12 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --output-file requirements-test.txt etc/requirements-test.in +# + +click==7.0 # via pip-tools +coverage==4.5.2 +pip-tools==3.2.0 +six==1.12.0 # via pip-tools +coveralls==1.7.0 # via pip-tools diff --git a/run_tests.py b/run_tests.py index 3670d1a..4fdbbdc 100755 --- a/run_tests.py +++ b/run_tests.py @@ -5,8 +5,8 @@ if sys.version_info >= (2, 7): import unittest -else: - import unittest2 as unittest +# else: +# import unittest2 as unittest TEST_PATH = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'tests') diff --git a/scripts/generate_profile.py b/scripts/generate_profile.py index 52e5c00..d68c816 100755 --- a/scripts/generate_profile.py +++ b/scripts/generate_profile.py @@ -20,11 +20,19 @@ import xlrd # Dev requirement for parsing Excel spreadsheet +FIELD_NUM_TIMESTAMP = 253 + XLS_HEADER_MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' +SYMBOL_NAME_SCRUBBER = re.compile(r'\W|^(?=\d)') + def header(header, indent=0): - return '%s%s' % (' ' * indent, (' %s ' % header).center(78 - indent, '#')) + return '%s# %s' % (' ' * indent, (' %s ' % header).center(78 - indent, '*')) + + +def scrub_symbol_name(symbol_name): + return SYMBOL_NAME_SCRUBBER.sub('_', symbol_name) PROFILE_HEADER_FIRST_PART = "%s\n%s" % ( @@ -42,7 +50,17 @@ def header(header, indent=0): BASE_TYPES, )''' -SPECIAL_FIELD_DECLARTIONS = "FIELD_TYPE_TIMESTAMP = Field(name='timestamp', type=FIELD_TYPES['date_time'], def_num=253, units='s')" +# This allows to prepend the declaration of some message numbers to the +# generated file. +# E.g. 'hr' -> MESG_NUM_HR = 132 +MESSAGE_NUM_DECLARATIONS = () + +# This allows to prepend the declaration of some field numbers of specific +# messages to the generated file. +# E.g. 'hr.event_timestamp' -> FIELD_NUM_HR_EVENT_TIMESTAMP = 9 +FIELD_NUM_DECLARATIONS = () + +SPECIAL_FIELD_DECLARATIONS = "FIELD_TYPE_TIMESTAMP = Field(name='timestamp', type=FIELD_TYPES['date_time'], def_num=" + str(FIELD_NUM_TIMESTAMP) + ", units='s')" IGNORE_TYPE_VALUES = ( # of the form 'type_name:value_name' @@ -52,10 +70,23 @@ def header(header, indent=0): ) BASE_TYPES = { - 'enum': '0x00', 'sint8': '0x01', 'uint8': '0x02', 'sint16': '0x83', - 'uint16': '0x84', 'sint32': '0x85', 'uint32': '0x86', 'string': '0x07', - 'float32': '0x88', 'float64': '0x89', 'uint8z': '0x0A', 'uint16z': '0x8B', - 'uint32z': '0x8C', 'byte': '0x0D', + 'enum': '0x00', + 'sint8': '0x01', + 'uint8': '0x02', + 'sint16': '0x83', + 'uint16': '0x84', + 'sint32': '0x85', + 'uint32': '0x86', + 'string': '0x07', + 'float32': '0x88', + 'float64': '0x89', + 'uint8z': '0x0A', + 'uint16z': '0x8B', + 'uint32z': '0x8C', + 'byte': '0x0D', + 'sint64': '0x8E', + 'uint64': '0x8F', + 'uint64z': '0x90', } @@ -143,6 +174,22 @@ def __str__(self): s += '}' return s + def get_by_name(self, mesg_name): + for mesg in self.messages: + if mesg.name == mesg_name: + return mesg + + raise ValueError('message "%s" not found' % mesg_name) + + def get_field_by_name(self, mesg_name, field_name): + mesg = self.get_by_name(mesg_name) + + for field in mesg.fields: + if field.name == field_name: + return mesg, field + + raise ValueError('field "%s" not found in message "%s"' % (field_name, mesg_name)) + class MessageInfo(namedtuple('MessageInfo', ('name', 'num', 'group_name', 'fields', 'comment'))): def get(self, field_name): @@ -166,7 +213,7 @@ def __str__(self): class FieldInfo(namedtuple('FieldInfo', ('name', 'type', 'num', 'scale', 'offset', 'units', 'components', 'subfields', 'comment'))): def __str__(self): - if self.num == 253: + if self.num == FIELD_NUM_TIMESTAMP: # Add trailing comma here because of comment assert not self.components and not self.subfields return 'FIELD_TYPE_TIMESTAMP,%s' % render_comment(self.comment) @@ -359,6 +406,7 @@ def maybe_decode(o): return o.decode() return o + def parse_messages(messages_rows, type_list): message_list = MessageList([]) @@ -389,10 +437,10 @@ def parse_messages(messages_rows, type_list): ) for cmp_name, cmp_scale, cmp_offset, cmp_units, cmp_bits, cmp_accumulate in zip( component_names, # name - parse_csv_fields(maybe_decode(row[6]), num_components), # scale - parse_csv_fields(maybe_decode(row[7]), num_components), # offset - parse_csv_fields(maybe_decode(row[8]), num_components), # units - parse_csv_fields(maybe_decode(row[9]), num_components), # bits + parse_csv_fields(maybe_decode(row[6]), num_components), # scale + parse_csv_fields(maybe_decode(row[7]), num_components), # offset + parse_csv_fields(maybe_decode(row[8]), num_components), # units + parse_csv_fields(maybe_decode(row[9]), num_components), # bits parse_csv_fields(maybe_decode(row[10]), num_components), # accumulate ) ] @@ -402,12 +450,12 @@ def parse_messages(messages_rows, type_list): assert component.name assert component.bits - # Otherwise a field + # Otherwise a field # Not a subfield if first row has definition num if row[1] is not None and row[1] != b'': field = FieldInfo( name=row[2].decode(), type=row[3].decode(), num=maybe_decode(row[1]), scale=fix_scale(row[6]), - offset=row[7], units=fix_units(row[8].decode()), components=[], + offset=maybe_decode(row[7]), units=fix_units(row[8].decode()), components=[], subfields=[], comment=row[13].decode(), ) @@ -417,15 +465,17 @@ def parse_messages(messages_rows, type_list): # Add components if they exist if components: field.components.extend(components) - # Wipe out scale, units, offset from field since it's a component - field = field._replace(scale=None, offset=None, units=None) + + # Wipe out scale, units, offset from field since components scale is None or b'' or is not digit + if row[6] is None or row[6] == b'' or not str(row[6]).isdigit(): + field = field._replace(scale=None, offset=None, units=None) message.fields.append(field) elif row[2] != b'': # Sub fields subfield = SubFieldInfo( name=row[2].decode(), num=field.num, type=row[3].decode(), scale=fix_scale(row[6]), - offset=row[7], units=fix_units(row[8].decode()), ref_fields=[], + offset=maybe_decode(row[7]), units=fix_units(row[8].decode()), ref_fields=[], components=[], comment=row[13].decode(), ) @@ -447,7 +497,7 @@ def parse_messages(messages_rows, type_list): ) assert len(subfield.ref_fields) == len(ref_field_names) - if not "alert_type" in ref_field_names: + if "alert_type" not in ref_field_names: field.subfields.append(subfield) # Resolve reference fields for subfields and components @@ -483,15 +533,15 @@ def get_xls_and_version_from_zip(path): version_match = re.search( r'Profile Version.+?(\d+\.?\d*).*', - archive.open('c/fit.h').read().decode(), + archive.open('FitSDKRelease_21.27.00/c/fit.h').read().decode(), ) if version_match: profile_version = ("%f" % float(version_match.group(1))).rstrip('0').ljust(4, '0') try: - return archive.open('Profile.xls'), profile_version + return archive.open('FitSDKRelease_21.27.00/Profile.xls'), profile_version except KeyError: - return archive.open('Profile.xlsx'), profile_version + return archive.open('FitSDKRelease_21.27.00/Profile.xlsx'), profile_version def main(input_xls_or_zip, output_py_path=None): @@ -509,19 +559,48 @@ def main(input_xls_or_zip, output_py_path=None): type_list = parse_types(types_rows) message_list = parse_messages(messages_rows, type_list) + mesg_num_declarations = [] + for mesg_name in MESSAGE_NUM_DECLARATIONS: + mesg_info = message_list.get_by_name(mesg_name) + + mesg_num_declarations.append('MESG_NUM_%s = %s' % ( + scrub_symbol_name(mesg_name).upper(), + str(mesg_info.num) if mesg_info else 'None')) + + field_num_declarations = [ + 'FIELD_NUM_TIMESTAMP = ' + str(FIELD_NUM_TIMESTAMP)] + for field_fqn in FIELD_NUM_DECLARATIONS: + mesg_name, field_name = field_fqn.split('.', maxsplit=1) + mesg_info, field_info = message_list.get_field_by_name(mesg_name, field_name) + + field_decl = 'FIELD_NUM_%s_%s = %s' % ( + scrub_symbol_name(mesg_name).upper(), + scrub_symbol_name(field_name).upper(), + str(field_info.num)) + + field_num_declarations.append(field_decl) + output = '\n'.join([ "\n%s" % PROFILE_HEADER_FIRST_PART, - header('EXPORTED PROFILE FROM %s AT %s' % ( + header('EXPORTED PROFILE FROM %s ON %s' % ( ('SDK VERSION %s' % profile_version) if profile_version else 'SPREADSHEET', - datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + datetime.datetime.now().strftime('%Y-%m-%d'), )), header('PARSED %d TYPES (%d VALUES), %d MESSAGES (%d FIELDS)' % ( len(type_list.types), sum(len(ti.values) for ti in type_list.types), len(message_list.messages), sum(len(mi.fields) for mi in message_list.messages), )), - '', IMPORT_HEADER, '\n', + '', IMPORT_HEADER + ]) + '\n' + + if mesg_num_declarations: + output += '\n\n' + '\n'.join(mesg_num_declarations) + '\n' + if field_num_declarations: + output += '\n\n' + '\n'.join(field_num_declarations) + '\n' + + output += '\n\n' + '\n'.join([ str(type_list), '\n', - SPECIAL_FIELD_DECLARTIONS, '\n', + SPECIAL_FIELD_DECLARATIONS, '\n', str(message_list), '' ]) @@ -530,17 +609,16 @@ def main(input_xls_or_zip, output_py_path=None): if output_py_path: open(output_py_path, 'w').write(output) - print("Profile%s written to %s", - ' version %s' % profile_version if profile_version else '', - output_py_path - ) + print('Profile version %s written to %s' % ( + profile_version if profile_version else '', + output_py_path)) else: print(output.strip()) if __name__ == '__main__': if len(sys.argv) < 2: - print("Usage: %s [profile.py]", os.path.basename(__file__)) + print("Usage: %s [profile.py]" % os.path.basename(__file__)) sys.exit(0) xls = sys.argv[1]