diff --git a/.buildinfo b/.buildinfo
index 459d8db..e67047d 100644
--- a/.buildinfo
+++ b/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: d132241e95d0015cdee23cb40aaf23cb
+config: bf0bc39cc69a3f1913b61306c5668515
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/_modules/collections.html b/_modules/collections.html
index d997339..ac6c5cb 100644
--- a/_modules/collections.html
+++ b/_modules/collections.html
@@ -5,18 +5,19 @@
collections — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -669,7 +670,8 @@ Source code for collections
>>> sorted(c.elements())
['A', 'A', 'B', 'B', 'C', 'C']
- # Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
+ Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
+
>>> import math
>>> prime_factors = Counter({2: 2, 3: 3, 17: 1})
>>> math.prod(prime_factors.elements())
@@ -710,7 +712,7 @@ Source code for collections
'''
# The regular dict.update() operation makes no sense here because the
- # replace behavior results in the some of original untouched counts
+ # replace behavior results in some of the original untouched counts
# being mixed-in with all of the other counts for a mismash that
# doesn't have a straight-forward interpretation in most counting
# contexts. Instead, we implement straight-addition. Both the inputs
@@ -1652,7 +1654,7 @@ Related Topics
-
+
@@ -1675,11 +1677,11 @@ Quick search
diff --git a/_modules/index.html b/_modules/index.html
index 2ab7bcc..97546ea 100644
--- a/_modules/index.html
+++ b/_modules/index.html
@@ -5,18 +5,19 @@
Overview: module code — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -41,6 +42,7 @@ All modules for which code is available
pycycling.ftms_parsers.training_status
pycycling.heart_rate_service
pycycling.rear_view_radar
+pycycling.rizer
pycycling.sterzo
pycycling.tacx_trainer_control
@@ -73,7 +75,7 @@ Related Topics
-
+
@@ -96,11 +98,11 @@ Quick search
diff --git a/_modules/pycycling/battery_service.html b/_modules/pycycling/battery_service.html
index c2ad61c..7508aa3 100644
--- a/_modules/pycycling/battery_service.html
+++ b/_modules/pycycling/battery_service.html
@@ -5,18 +5,19 @@
pycycling.battery_service — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -105,7 +106,7 @@ Related Topics
-
+
@@ -128,11 +129,11 @@ Quick search
diff --git a/_modules/pycycling/cycling_power_service.html b/_modules/pycycling/cycling_power_service.html
index 0a372fa..c7c2433 100644
--- a/_modules/pycycling/cycling_power_service.html
+++ b/_modules/pycycling/cycling_power_service.html
@@ -5,18 +5,19 @@
pycycling.cycling_power_service — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -401,7 +402,7 @@ Related Topics
-
+
@@ -424,11 +425,11 @@ Quick search
diff --git a/_modules/pycycling/cycling_speed_cadence_service.html b/_modules/pycycling/cycling_speed_cadence_service.html
index d5598df..e937437 100644
--- a/_modules/pycycling/cycling_speed_cadence_service.html
+++ b/_modules/pycycling/cycling_speed_cadence_service.html
@@ -5,18 +5,19 @@
pycycling.cycling_speed_cadence_service — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -146,7 +147,7 @@ Related Topics
-
+
@@ -169,11 +170,11 @@ Quick search
diff --git a/_modules/pycycling/fitness_machine_service.html b/_modules/pycycling/fitness_machine_service.html
index 22bb8d9..3e87b71 100644
--- a/_modules/pycycling/fitness_machine_service.html
+++ b/_modules/pycycling/fitness_machine_service.html
@@ -5,18 +5,19 @@
pycycling.fitness_machine_service — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -53,12 +54,13 @@ Source code for pycycling.fitness_machine_service .. literalinclude:: ../examples/fitness_machine_service_example.py
"""
+
from collections import namedtuple
from pycycling.ftms_parsers import (
parse_fitness_machine_status ,
parse_indoor_bike_data ,
-
parse_fitness_machine_feature ,
+
parse_all_features ,
parse_training_status ,
parse_control_point_response ,
form_ftms_control_command ,
@@ -91,7 +93,9 @@
Source code for pycycling.fitness_machine_service )
-
def _parse_supported_resistance_level_range ( message : bytearray ) -> SupportedResistanceLevelRange :
+
def _parse_supported_resistance_level_range (
+
message : bytearray ,
+
) -> SupportedResistanceLevelRange :
minimum_resistance = int . from_bytes ( message [ 0 : 2 ], "little" )
maximum_resistance = int . from_bytes ( message [ 2 : 4 ], "little" )
minimum_increment = int . from_bytes ( message [ 4 : 6 ], "little" )
@@ -126,7 +130,9 @@
Source code for pycycling.fitness_machine_service # === READ Characteristics ===
[docs]
-
async def get_supported_resistance_level_range ( self ) -> SupportedResistanceLevelRange :
+
async def get_supported_resistance_level_range (
+
self ,
+
) -> SupportedResistanceLevelRange :
message = await self . _client . read_gatt_char (
ftms_supported_resistance_level_range_characteristic_id
)
@@ -148,7 +154,7 @@
Source code for pycycling.fitness_machine_service message = await self . _client . read_gatt_char (
ftms_fitness_machine_feature_characteristic_id
)
- return parse_fitness_machine_feature ( message )
+
return parse_all_features ( message )
# === NOTIFY Characteristics ===
@@ -267,6 +273,7 @@
Source code for pycycling.fitness_machine_service if
self . _control_point_response_callback is not None :
self . _control_point_response_callback ( parse_control_point_response ( data ))
+
# ====== Control Point Commands ======
[docs]
async def request_control ( self ) -> None :
@@ -285,11 +292,37 @@
Source code for pycycling.fitness_machine_service )
+
+
[docs]
+
async def set_target_speed ( self , speed : int ) -> None :
+
if speed < 0 :
+
raise ValueError ( "Speed must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGET_SPEED , speed
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_target_incline ( self , inclination : int ) -> None :
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGET_INCLINE , inclination
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
[docs]
async def set_target_resistance_level ( self , level : int ) -> None :
+
if level < 0 :
+
raise ValueError ( "Resistance level must be non-negative" )
message = form_ftms_control_command (
-
FTMSControlPointOpCode . SET_TARGET_RESISTANCE_LEVEL , int ( level )
+
FTMSControlPointOpCode . SET_TARGET_RESISTANCE_LEVEL , level
)
await self . _client . write_gatt_char (
ftms_fitness_machine_control_point_characteristic_id , message , True
@@ -299,8 +332,210 @@
Source code for pycycling.fitness_machine_service
[docs]
async def set_target_power ( self , power : int ) -> None :
+
if power < 0 :
+
raise ValueError ( "Power must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGET_POWER , power
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_target_heart_rate ( self , heart_rate : int ) -> None :
+
if heart_rate < 0 :
+
raise ValueError ( "Heart rate must be non-negative" )
message = form_ftms_control_command (
-
FTMSControlPointOpCode . SET_TARGET_POWER , int ( power )
+
FTMSControlPointOpCode . SET_TARGET_HEART_RATE , heart_rate
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def start_or_resume ( self ) -> None :
+
message = form_ftms_control_command ( FTMSControlPointOpCode . START_OR_RESUME )
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def stop_or_pause ( self , pause : bool ) -> None :
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . STOP_OR_PAUSE , 0x02 if pause else 0x01
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_expended_energy ( self , energy : int ) -> None :
+
if energy < 0 :
+
raise ValueError ( "Energy must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_EXPENDED_ENERGY , energy
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_number_of_steps ( self , steps : int ) -> None :
+
if steps < 0 :
+
raise ValueError ( "Steps must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_NUMBER_OF_STEPS , steps
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_number_of_strides ( self , strides : int ) -> None :
+
if strides < 0 :
+
raise ValueError ( "Strides must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_NUMBER_OF_STRIDES , strides
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_distance ( self , distance : int ) -> None :
+
if distance < 0 :
+
raise ValueError ( "Distance must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_DISTANCE , distance
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_training_time ( self , time : int ) -> None :
+
if time < 0 :
+
raise ValueError ( "Time must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_TRAINING_TIME , time
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_time_in_two_heart_rate_zones ( self , times : list ) -> None :
+
if len ( times ) != 2 :
+
raise ValueError ( "Times must be a list of 2 elements" )
+
if times [ 0 ] < 0 or times [ 1 ] < 0 :
+
raise ValueError ( "Times must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_TIME_IN_TWO_HEART_RATE_ZONES , times
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_time_in_three_heart_rate_zones ( self , times : list ) -> None :
+
if len ( times ) != 3 :
+
raise ValueError ( "Times must be a list of 3 elements" )
+
if times [ 0 ] < 0 or times [ 1 ] < 0 or times [ 2 ] < 0 :
+
raise ValueError ( "Times must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_TIME_IN_THREE_HEART_RATE_ZONES , times
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_time_in_five_heart_rate_zones ( self , times : list ) -> None :
+
if len ( times ) != 5 :
+
raise ValueError ( "Times must be a list of 5 elements" )
+
if times [ 0 ] < 0 or times [ 1 ] < 0 or times [ 2 ] < 0 or times [ 3 ] < 0 or times [ 4 ] < 0 :
+
raise ValueError ( "Times must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_TIME_IN_FIVE_HEART_RATE_ZONES , times
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_simulation_parameters (
+
self , wind_speed : int , grade : int , crr : int , cw : int
+
) -> None :
+
if crr < 0 :
+
raise ValueError ( "Crr must be non-negative" )
+
if cw < 0 :
+
raise ValueError ( "Cw must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_INDOOR_BIKE_SIMULATION_PARAMETERS ,
+
[ wind_speed , grade , crr , cw ],
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_wheel_circumference ( self , circumference : int ) -> None :
+
if circumference < 0 :
+
raise ValueError ( "Circumference must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_WHEEL_CIRCUMFERENCE , circumference
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_spin_down_control ( self , control : int ) -> None :
+
if control < 0 :
+
raise ValueError ( "Control must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_SPIN_DOWN_CONTROL , control
+
)
+
await self . _client . write_gatt_char (
+
ftms_fitness_machine_control_point_characteristic_id , message , True
+
)
+
+
+
+
[docs]
+
async def set_targeted_cadence ( self , cadence : int ) -> None :
+
if cadence < 0 :
+
raise ValueError ( "Cadence must be non-negative" )
+
message = form_ftms_control_command (
+
FTMSControlPointOpCode . SET_TARGETED_CADENCE , cadence
)
await self . _client . write_gatt_char (
ftms_fitness_machine_control_point_characteristic_id , message , True
@@ -339,7 +574,7 @@
Related Topics
-
+
@@ -362,11 +597,11 @@
Quick search
diff --git a/_modules/pycycling/ftms_parsers/control_point.html b/_modules/pycycling/ftms_parsers/control_point.html
index cfdba58..c792456 100644
--- a/_modules/pycycling/ftms_parsers/control_point.html
+++ b/_modules/pycycling/ftms_parsers/control_point.html
@@ -5,18 +5,19 @@
pycycling.ftms_parsers.control_point — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -43,7 +44,6 @@
Source code for pycycling.ftms_parsers.control_point CONTROL_NOT_PERMITTED = 0x05
-
[docs]
class FTMSControlPointOpCode ( Enum ):
@@ -53,15 +53,34 @@
Source code for pycycling.ftms_parsers.control_point SET_TARGET_INCLINE = 0x03
SET_TARGET_RESISTANCE_LEVEL = 0x04
SET_TARGET_POWER = 0x05
- START_OR_RESUME = 0x06
- STOP_OR_PAUSE = 0x07
+ SET_TARGET_HEART_RATE = 0x06
+ START_OR_RESUME = 0x07
+ STOP_OR_PAUSE = 0x08
+ SET_TARGETED_EXPENDED_ENERGY = 0x09
+ SET_TARGETED_NUMBER_OF_STEPS = 0x0A
+ SET_TARGETED_NUMBER_OF_STRIDES = 0x0B
+ SET_TARGETED_DISTANCE = 0x0C
+ SET_TARGETED_TRAINING_TIME = 0x0D
+ SET_TARGETED_TIME_IN_TWO_HEART_RATE_ZONES = 0x0E
+ SET_TARGETED_TIME_IN_THREE_HEART_RATE_ZONES = 0x0F
+ SET_TARGETED_TIME_IN_FIVE_HEART_RATE_ZONES = 0x10
+ SET_INDOOR_BIKE_SIMULATION_PARAMETERS = 0x11
+ SET_WHEEL_CIRCUMFERENCE = 0x12
+ SET_SPIN_DOWN_CONTROL = 0x13
+ SET_TARGETED_CADENCE = 0x14
RESPONSE_CODE = 0x80
-
[docs]
def form_ftms_control_command ( opcode : FTMSControlPointOpCode , parameter : int = 0 ):
+
"""
+
Form a FTMS control command message
+
:param opcode: FTMSControlPointOpCode
+
:param parameter: scalar or list of scalar
+
:return: bytearray
+
"""
+
parameter = parameter if isinstance ( parameter , list ) else ( int )( parameter )
if opcode == FTMSControlPointOpCode . REQUEST_CONTROL :
return b " \x00 "
elif opcode == FTMSControlPointOpCode . RESET :
@@ -78,13 +97,62 @@
Source code for pycycling.ftms_parsers.control_point elif opcode == FTMSControlPointOpCode . SET_TARGET_POWER :
# parameter: sint16, 1W
return b " \x05 " + parameter . to_bytes ( 2 , "little" , signed = True )
+ elif opcode == FTMSControlPointOpCode . SET_TARGET_HEART_RATE :
+ # parameter: uint8, 1bpm
+ return b " \x06 " + parameter . to_bytes ( 1 , "little" , signed = False )
elif opcode == FTMSControlPointOpCode . START_OR_RESUME :
- # parameter: 01=stop, 02=pause
- return b " \x06 "
+ return b " \x07 "
elif opcode == FTMSControlPointOpCode . STOP_OR_PAUSE :
- return b " \x07 " + parameter . to_bytes ( 1 , "little" , signed = False )
+ # parameter: 01=stop, 02=pause
+ return b " \x08 " + parameter . to_bytes ( 1 , "little" , signed = False )
elif opcode == FTMSControlPointOpCode . RESPONSE_CODE :
return b " \x80 "
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_EXPENDED_ENERGY :
+ # parameter: uint16, 1calories
+ return b " \x09 " + parameter . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_NUMBER_OF_STEPS :
+ # parameter: uint16, 1
+ return b " \x0A " + parameter . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_NUMBER_OF_STRIDES :
+ # parameter: uint16, 1
+ return b " \x0B " + parameter . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_DISTANCE :
+ # parameter: uint24, 1m
+ return b " \x0C " + parameter . to_bytes ( 3 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_TRAINING_TIME :
+ # parameter: uint16, 1s
+ return b " \x0D " + parameter . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_TIME_IN_TWO_HEART_RATE_ZONES :
+ # parameter: list of 2 uint16, 1s
+ return b " \x0E " + parameter [ 0 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 1 ] . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_TIME_IN_THREE_HEART_RATE_ZONES :
+ # parameter: list of 3 uint16, 1s
+ return b " \x0F " + parameter [ 0 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 1 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 2 ] . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_TIME_IN_FIVE_HEART_RATE_ZONES :
+ # parameter: list of 5 uint16, 1s
+ return b " \x10 " + parameter [ 0 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 1 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 2 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 3 ] . to_bytes ( 2 , "little" , signed = False ) \
+ + parameter [ 4 ] . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_INDOOR_BIKE_SIMULATION_PARAMETERS :
+ # parameter: list of int16 0.001mps, int16 0.01%, uint8 0.0001, uint8 0.01kg/m
+ return b " \x11 " + parameter [ 0 ] . to_bytes ( 2 , "little" , signed = True ) \
+ + parameter [ 1 ] . to_bytes ( 2 , "little" , signed = True ) \
+ + parameter [ 2 ] . to_bytes ( 1 , "little" , signed = False ) \
+ + parameter [ 3 ] . to_bytes ( 1 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_WHEEL_CIRCUMFERENCE :
+ # parameter: uint16, 0.1mm
+ return b " \x12 " + parameter . to_bytes ( 2 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_SPIN_DOWN_CONTROL :
+ # parameter: 01=start, 02=ignore
+ return b " \x13 " + parameter . to_bytes ( 1 , "little" , signed = False )
+ elif opcode == FTMSControlPointOpCode . SET_TARGETED_CADENCE :
+ # parameter: uint16, 1rpm
+ return b " \x14 " + parameter . to_bytes ( 1 , "little" , signed = False )
else :
raise ValueError ( "Invalid opcode" )
@@ -132,7 +200,7 @@
Related Topics
-
+
@@ -155,11 +223,11 @@
Quick search
diff --git a/_modules/pycycling/ftms_parsers/fitness_machine_feature.html b/_modules/pycycling/ftms_parsers/fitness_machine_feature.html
index 76d0b54..9e79866 100644
--- a/_modules/pycycling/ftms_parsers/fitness_machine_feature.html
+++ b/_modules/pycycling/ftms_parsers/fitness_machine_feature.html
@@ -5,18 +5,19 @@
pycycling.ftms_parsers.fitness_machine_feature — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -55,6 +56,31 @@
Source code for pycycling.ftms_parsers.fitness_machine_feature )
+
TargetSettingFeatures = namedtuple (
+
"TargetSettingFeatures" ,
+
[
+
"speed_target_setting_supported" ,
+
"inclination_target_setting_supported" ,
+
"resistance_target_setting_supported" ,
+
"power_target_setting_supported" ,
+
"heart_rate_target_setting_supported" ,
+
"targeted_expended_energy_configuration_supported" ,
+
"targeted_step_number_configuration_supported" ,
+
"targeted_stride_number_configuration_supported" ,
+
"targeted_distance_configuration_supported" ,
+
"targeted_training_time_configuration_supported" ,
+
"targeted_time_in_two_heart_rate_zones_configuration_supported" ,
+
"targeted_time_in_three_heart_rate_zones_configuration_supported" ,
+
"targeted_time_in_five_heart_rate_zones_configuration_supported" ,
+
"indoor_bike_simulation_parameters_supported" ,
+
"wheel_circumference_configuration_supported" ,
+
"spin_down_control_supported" ,
+
"targeted_cadence_configuration_supported" ,
+
],
+
)
+
+
+
[docs]
def parse_fitness_machine_feature ( message : bytearray ) -> FitnessMachineFeature :
@@ -96,6 +122,56 @@
Source code for pycycling.ftms_parsers.fitness_machine_feature user_data_retention_supported,
)
+
+
+
+
[docs]
+
def parse_target_setting_features ( message : bytearray ) -> TargetSettingFeatures :
+
speed_target_setting_supported = bool ( message [ 0 ] & 0b00000001 )
+
inclination_target_setting_supported = bool ( message [ 0 ] & 0b00000010 )
+
resistance_target_setting_supported = bool ( message [ 0 ] & 0b00000100 )
+
power_target_setting_supported = bool ( message [ 0 ] & 0b00001000 )
+
heart_rate_target_setting_supported = bool ( message [ 0 ] & 0b00010000 )
+
targeted_expended_energy_configuration_supported = bool ( message [ 0 ] & 0b00100000 )
+
targeted_step_number_configuration_supported = bool ( message [ 0 ] & 0b01000000 )
+
targeted_stride_number_configuration_supported = bool ( message [ 0 ] & 0b10000000 )
+
+
targeted_distance_configuration_supported = bool ( message [ 1 ] & 0b00000001 )
+
targeted_training_time_configuration_supported = bool ( message [ 1 ] & 0b00000010 )
+
targeted_time_in_two_heart_rate_zones_configuration_supported = bool ( message [ 1 ] & 0b00000100 )
+
targeted_time_in_three_heart_rate_zones_configuration_supported = bool ( message [ 1 ] & 0b00001000 )
+
targeted_time_in_five_heart_rate_zones_configuration_supported = bool ( message [ 1 ] & 0b00010000 )
+
indoor_bike_simulation_parameters_supported = bool ( message [ 1 ] & 0b00100000 )
+
wheel_circumference_configuration_supported = bool ( message [ 1 ] & 0b01000000 )
+
spin_down_control_supported = bool ( message [ 1 ] & 0b10000000 )
+
+
targeted_cadence_configuration_supported = bool ( message [ 2 ] & 0b00000001 )
+
return TargetSettingFeatures (
+
speed_target_setting_supported ,
+
inclination_target_setting_supported ,
+
resistance_target_setting_supported ,
+
power_target_setting_supported ,
+
heart_rate_target_setting_supported ,
+
targeted_expended_energy_configuration_supported ,
+
targeted_step_number_configuration_supported ,
+
targeted_stride_number_configuration_supported ,
+
targeted_distance_configuration_supported ,
+
targeted_training_time_configuration_supported ,
+
targeted_time_in_two_heart_rate_zones_configuration_supported ,
+
targeted_time_in_three_heart_rate_zones_configuration_supported ,
+
targeted_time_in_five_heart_rate_zones_configuration_supported ,
+
indoor_bike_simulation_parameters_supported ,
+
wheel_circumference_configuration_supported ,
+
spin_down_control_supported ,
+
targeted_cadence_configuration_supported ,
+
)
+
+
+
+
[docs]
+
def parse_all_features ( message : bytearray ):
+
return parse_fitness_machine_feature ( message [ 0 : 4 ]), parse_target_setting_features ( message [ 4 : 8 ])
+
@@ -128,7 +204,7 @@
Related Topics
-
+
@@ -151,11 +227,11 @@
Quick search
diff --git a/_modules/pycycling/ftms_parsers/fitness_machine_status.html b/_modules/pycycling/ftms_parsers/fitness_machine_status.html
index d95fbad..da58767 100644
--- a/_modules/pycycling/ftms_parsers/fitness_machine_status.html
+++ b/_modules/pycycling/ftms_parsers/fitness_machine_status.html
@@ -5,18 +5,19 @@
pycycling.ftms_parsers.fitness_machine_status — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -299,7 +300,7 @@
Related Topics
-
+
@@ -322,11 +323,11 @@
Quick search
diff --git a/_modules/pycycling/ftms_parsers/indoor_bike_data.html b/_modules/pycycling/ftms_parsers/indoor_bike_data.html
index c96ed3d..8983b19 100644
--- a/_modules/pycycling/ftms_parsers/indoor_bike_data.html
+++ b/_modules/pycycling/ftms_parsers/indoor_bike_data.html
@@ -5,18 +5,19 @@
pycycling.ftms_parsers.indoor_bike_data — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -188,7 +189,7 @@
Related Topics
-
+
@@ -211,11 +212,11 @@ Quick search
diff --git a/_modules/pycycling/ftms_parsers/training_status.html b/_modules/pycycling/ftms_parsers/training_status.html
index 3835135..c29e704 100644
--- a/_modules/pycycling/ftms_parsers/training_status.html
+++ b/_modules/pycycling/ftms_parsers/training_status.html
@@ -5,18 +5,19 @@
pycycling.ftms_parsers.training_status — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -147,7 +148,7 @@ Related Topics
-
+
@@ -170,11 +171,11 @@ Quick search
diff --git a/_modules/pycycling/heart_rate_service.html b/_modules/pycycling/heart_rate_service.html
index 852fa66..ccbeada 100644
--- a/_modules/pycycling/heart_rate_service.html
+++ b/_modules/pycycling/heart_rate_service.html
@@ -5,18 +5,19 @@
pycycling.heart_rate_service — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -136,7 +137,7 @@ Related Topics
-
+
@@ -159,11 +160,11 @@ Quick search
diff --git a/_modules/pycycling/rear_view_radar.html b/_modules/pycycling/rear_view_radar.html
index 004b68a..9a7b874 100644
--- a/_modules/pycycling/rear_view_radar.html
+++ b/_modules/pycycling/rear_view_radar.html
@@ -5,18 +5,19 @@
pycycling.rear_view_radar — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -142,7 +143,7 @@ Related Topics
-
+
@@ -165,11 +166,11 @@ Quick search
diff --git a/_modules/pycycling/rizer.html b/_modules/pycycling/rizer.html
new file mode 100644
index 0000000..9aa5b69
--- /dev/null
+++ b/_modules/pycycling/rizer.html
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+ pycycling.rizer — pycycling documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pycycling.rizer
+import struct
+
+rizer_measurement_id = "347b0030-7635-408b-8918-8ff3949ce592"
+rizer_control_point_id = "347b0031-7635-408b-8918-8ff3949ce592"
+
+
+
+
[docs]
+
class Rizer :
+
def __init__ ( self , client ):
+
self . _client = client
+
self . _steering_measurement_callback = None
+
self . _latest_challenge = None
+
+
+
[docs]
+
async def enable_steering_measurement_notifications ( self ):
+
await self . _client . start_notify (
+
rizer_measurement_id , self . _steering_measurement_notification_handler
+
)
+
+
+
+
[docs]
+
async def disable_steering_measurement_notifications ( self ):
+
await self . _client . stop_notify ( rizer_measurement_id )
+
+
+
+
[docs]
+
def set_steering_measurement_callback ( self , callback ):
+
self . _steering_measurement_callback = callback
+
+
+
def _steering_measurement_notification_handler (
+
self , sender , data
+
): # pylint: disable=unused-argument
+
[ steering_angle ] = struct . unpack ( "<f" , data )
+
self . _steering_measurement_callback ( steering_angle )
+
+
+
[docs]
+
async def set_transmission_rate ( self , rate : int ):
+
"""sets the transmission rate of the rizer to 8, 16, or 32 Hz"""
+
if rate < 0 or rate > 2 :
+
raise ValueError (
+
"Invalid rate: choose 0, 1, or 2 for 8, 16, or 32 Hz respectively"
+
)
+
byte_array = b " \x02 " + rate . to_bytes ( 1 , "little" , signed = False )
+
await self . _client . write_gatt_char ( rizer_control_point_id , byte_array )
+
+
+
+
[docs]
+
async def set_center ( self ):
+
"""sets the zero position of the rizer"""
+
await self . _client . write_gatt_char ( rizer_control_point_id , b " \x01 " )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_modules/pycycling/sterzo.html b/_modules/pycycling/sterzo.html
index 639b9db..a7f106a 100644
--- a/_modules/pycycling/sterzo.html
+++ b/_modules/pycycling/sterzo.html
@@ -5,18 +5,19 @@
pycycling.sterzo — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -64,7 +65,7 @@ Source code for pycycling.sterzo
if sys . version_info >= ( 3 , 11 ):
challenge_file = importlib . resources . files ( pycycling . data ) . joinpath ( 'sterzo-challenge-codes.dat' ) . open ( 'rb' )
else : # legacy support < 3.9
- challenge_file = importlib . resources . open_binary ( pycycling . data , 'sterzo-challenge-codes.dat' )
+ challenge_file = importlib . resources . open_binary ( pycycling . data , 'sterzo-challenge-codes.dat' ) # pylint: disable=deprecated-method
with challenge_file :
challenge_file . seek ( self . _latest_challenge * 2 , 1 )
@@ -127,7 +128,7 @@ Related Topics
-
+
@@ -150,11 +151,11 @@ Quick search
diff --git a/_modules/pycycling/tacx_trainer_control.html b/_modules/pycycling/tacx_trainer_control.html
index e9688eb..36133fb 100644
--- a/_modules/pycycling/tacx_trainer_control.html
+++ b/_modules/pycycling/tacx_trainer_control.html
@@ -5,18 +5,19 @@
pycycling.tacx_trainer_control — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -527,7 +528,7 @@ Related Topics
-
+
@@ -550,11 +551,11 @@ Quick search
diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt
index 79ab846..54eae1e 100644
--- a/_sources/index.rst.txt
+++ b/_sources/index.rst.txt
@@ -1,5 +1,5 @@
.. pycycling documentation master file, created by
- sphinx-quickstart on Sat Jan 6 11:03:05 2024.
+ sphinx-quickstart on Fri Apr 26 20:13:48 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
diff --git a/_sources/pycycling.rizer.rst.txt b/_sources/pycycling.rizer.rst.txt
new file mode 100644
index 0000000..8e39153
--- /dev/null
+++ b/_sources/pycycling.rizer.rst.txt
@@ -0,0 +1,7 @@
+pycycling.rizer module
+======================
+
+.. automodule:: pycycling.rizer
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/_sources/pycycling.rst.txt b/_sources/pycycling.rst.txt
index c786fd3..92552fb 100644
--- a/_sources/pycycling.rst.txt
+++ b/_sources/pycycling.rst.txt
@@ -22,6 +22,7 @@ Submodules
pycycling.fitness_machine_service
pycycling.heart_rate_service
pycycling.rear_view_radar
+ pycycling.rizer
pycycling.sterzo
pycycling.tacx_trainer_control
diff --git a/_static/alabaster.css b/_static/alabaster.css
index 517d0b2..e3174bf 100644
--- a/_static/alabaster.css
+++ b/_static/alabaster.css
@@ -69,6 +69,11 @@ div.relations {
}
+div.sphinxsidebar {
+ max-height: 100%;
+ overflow-y: auto;
+}
+
div.sphinxsidebar a {
color: #444;
text-decoration: none;
@@ -155,6 +160,14 @@ div.sphinxsidebar input {
font-size: 1em;
}
+div.sphinxsidebar #searchbox input[type="text"] {
+ width: 160px;
+}
+
+div.sphinxsidebar .search > div {
+ display: table-cell;
+}
+
div.sphinxsidebar hr {
border: none;
height: 1px;
@@ -638,15 +651,7 @@ a:hover tt, a:hover code {
display: none!important;
}
-/* Make nested-list/multi-paragraph items look better in Releases changelog
- * pages. Without this, docutils' magical list fuckery causes inconsistent
- * formatting between different release sub-lists.
- */
-div#changelog > div.section > ul > li > p:only-child {
- margin-bottom: 0;
-}
-
-/* Hide fugly table cell borders in ..bibliography:: directive output */
+/* Hide ugly table cell borders in ..bibliography:: directive output */
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
border: none;
/* Below needed in some edge cases; if not applied, bottom shadows appear */
diff --git a/_static/basic.css b/_static/basic.css
index 30fee9d..e5179b7 100644
--- a/_static/basic.css
+++ b/_static/basic.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -222,7 +222,7 @@ table.modindextable td {
/* -- general body styles --------------------------------------------------- */
div.body {
- min-width: 360px;
+ min-width: inherit;
max-width: 800px;
}
diff --git a/_static/doctools.js b/_static/doctools.js
index d06a71d..4d67807 100644
--- a/_static/doctools.js
+++ b/_static/doctools.js
@@ -4,7 +4,7 @@
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/_static/language_data.js b/_static/language_data.js
index 250f566..367b8ed 100644
--- a/_static/language_data.js
+++ b/_static/language_data.js
@@ -5,7 +5,7 @@
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -13,7 +13,7 @@
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
-/* Non-minified version is copied as a separate JS file, is available */
+/* Non-minified version is copied as a separate JS file, if available */
/**
* Porter Stemmer
diff --git a/_static/pygments.css b/_static/pygments.css
index 57c7df3..04a4174 100644
--- a/_static/pygments.css
+++ b/_static/pygments.css
@@ -56,7 +56,7 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */
-.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
+.highlight .w { color: #f8f8f8 } /* Text.Whitespace */
.highlight .mb { color: #990000 } /* Literal.Number.Bin */
.highlight .mf { color: #990000 } /* Literal.Number.Float */
.highlight .mh { color: #990000 } /* Literal.Number.Hex */
diff --git a/_static/searchtools.js b/_static/searchtools.js
index 7918c3f..92da3f8 100644
--- a/_static/searchtools.js
+++ b/_static/searchtools.js
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for the full-text search.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -99,7 +99,7 @@ const _displayItem = (item, searchTerms, highlightTerms) => {
.then((data) => {
if (data)
listItem.appendChild(
- Search.makeSearchSummary(data, searchTerms)
+ Search.makeSearchSummary(data, searchTerms, anchor)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
@@ -116,8 +116,8 @@ const _finishSearch = (resultCount) => {
);
else
Search.status.innerText = _(
- `Search finished, found ${resultCount} page(s) matching the search query.`
- );
+ "Search finished, found ${resultCount} page(s) matching the search query."
+ ).replace('${resultCount}', resultCount);
};
const _displayNextItem = (
results,
@@ -137,6 +137,22 @@ const _displayNextItem = (
// search finished, update title and status message
else _finishSearch(resultCount);
};
+// Helper function used by query() to order search results.
+// Each input is an array of [docname, title, anchor, descr, score, filename].
+// Order the results by score (in opposite order of appearance, since the
+// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
+const _orderResultsByScoreThenName = (a, b) => {
+ const leftScore = a[4];
+ const rightScore = b[4];
+ if (leftScore === rightScore) {
+ // same score: sort alphabetically
+ const leftTitle = a[1].toLowerCase();
+ const rightTitle = b[1].toLowerCase();
+ if (leftTitle === rightTitle) return 0;
+ return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
+ }
+ return leftScore > rightScore ? 1 : -1;
+};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
@@ -160,13 +176,26 @@ const Search = {
_queued_query: null,
_pulse_status: -1,
- htmlToText: (htmlString) => {
+ htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
- htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
+ for (const removalQuery of [".headerlinks", "script", "style"]) {
+ htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
+ }
+ if (anchor) {
+ const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
+ if (anchorContent) return anchorContent.textContent;
+
+ console.warn(
+ `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
+ );
+ }
+
+ // if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
- if (docContent !== undefined) return docContent.textContent;
+ if (docContent) return docContent.textContent;
+
console.warn(
- "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
+ "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
);
return "";
},
@@ -239,16 +268,7 @@ const Search = {
else Search.deferQuery(query);
},
- /**
- * execute search (requires search index to be loaded)
- */
- query: (query) => {
- const filenames = Search._index.filenames;
- const docNames = Search._index.docnames;
- const titles = Search._index.titles;
- const allTitles = Search._index.alltitles;
- const indexEntries = Search._index.indexentries;
-
+ _parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
@@ -284,16 +304,32 @@ const Search = {
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
- // array of [docname, title, anchor, descr, score, filename]
- let results = [];
+ return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
+ },
+
+ /**
+ * execute search (requires search index to be loaded)
+ */
+ _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
+ const allTitles = Search._index.alltitles;
+ const indexEntries = Search._index.indexentries;
+
+ // Collect multiple result groups to be sorted separately and then ordered.
+ // Each is an array of [docname, title, anchor, descr, score, filename].
+ const normalResults = [];
+ const nonMainIndexResults = [];
+
_removeChildren(document.getElementById("search-progress"));
- const queryLower = query.toLowerCase();
+ const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
- if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
+ if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
let score = Math.round(100 * queryLower.length / title.length)
- results.push([
+ normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
@@ -308,46 +344,47 @@ const Search = {
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
- for (const [file, id] of foundEntries) {
- let score = Math.round(100 * queryLower.length / entry.length)
- results.push([
+ for (const [file, id, isMain] of foundEntries) {
+ const score = Math.round(100 * queryLower.length / entry.length);
+ const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
- ]);
+ ];
+ if (isMain) {
+ normalResults.push(result);
+ } else {
+ nonMainIndexResults.push(result);
+ }
}
}
}
// lookup as object
objectTerms.forEach((term) =>
- results.push(...Search.performObjectSearch(term, objectTerms))
+ normalResults.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
- results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
+ normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
- if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
-
- // now sort the results by score (in opposite order of appearance, since the
- // display function below uses pop() to retrieve items) and then
- // alphabetically
- results.sort((a, b) => {
- const leftScore = a[4];
- const rightScore = b[4];
- if (leftScore === rightScore) {
- // same score: sort alphabetically
- const leftTitle = a[1].toLowerCase();
- const rightTitle = b[1].toLowerCase();
- if (leftTitle === rightTitle) return 0;
- return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
- }
- return leftScore > rightScore ? 1 : -1;
- });
+ if (Scorer.score) {
+ normalResults.forEach((item) => (item[4] = Scorer.score(item)));
+ nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
+ }
+
+ // Sort each group of results by score and then alphabetically by name.
+ normalResults.sort(_orderResultsByScoreThenName);
+ nonMainIndexResults.sort(_orderResultsByScoreThenName);
+
+ // Combine the result groups in (reverse) order.
+ // Non-main index entries are typically arbitrary cross-references,
+ // so display them after other results.
+ let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
@@ -361,7 +398,12 @@ const Search = {
return acc;
}, []);
- results = results.reverse();
+ return results.reverse();
+ },
+
+ query: (query) => {
+ const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
+ const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
// for debugging
//Search.lastresults = results.slice(); // a copy
@@ -466,14 +508,18 @@ const Search = {
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
- Object.keys(terms).forEach((term) => {
- if (term.match(escapedWord) && !terms[word])
- arr.push({ files: terms[term], score: Scorer.partialTerm });
- });
- Object.keys(titleTerms).forEach((term) => {
- if (term.match(escapedWord) && !titleTerms[word])
- arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
- });
+ if (!terms.hasOwnProperty(word)) {
+ Object.keys(terms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: terms[term], score: Scorer.partialTerm });
+ });
+ }
+ if (!titleTerms.hasOwnProperty(word)) {
+ Object.keys(titleTerms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
+ });
+ }
}
// no match but word was a required one
@@ -496,9 +542,8 @@ const Search = {
// create the mapping
files.forEach((file) => {
- if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
- fileMap.get(file).push(word);
- else fileMap.set(file, [word]);
+ if (!fileMap.has(file)) fileMap.set(file, [word]);
+ else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
});
});
@@ -549,8 +594,8 @@ const Search = {
* search summary for a given text. keywords is a list
* of stemmed words.
*/
- makeSearchSummary: (htmlText, keywords) => {
- const text = Search.htmlToText(htmlText);
+ makeSearchSummary: (htmlText, keywords, anchor) => {
+ const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
diff --git a/genindex.html b/genindex.html
index e49b1e5..9fbc314 100644
--- a/genindex.html
+++ b/genindex.html
@@ -5,18 +5,19 @@
Index — pycycling documentation
-
-
+
+
-
+
+
+
-
@@ -207,8 +208,12 @@ D
disable_radar_measurement_notifications() (pycycling.rear_view_radar.RearViewRadarService method)
- disable_steering_measurement_notifications() (pycycling.sterzo.Sterzo method)
+ disable_steering_measurement_notifications() (pycycling.rizer.Rizer method)
+
+
disable_training_status_notify() (pycycling.fitness_machine_service.FitnessMachineService method)
distance (pycycling.rear_view_radar.RadarMeasurement attribute)
@@ -253,14 +258,18 @@ E
enable_hr_measurement_notifications() (pycycling.heart_rate_service.HeartRateService method)
-
-
+
HEART_RATE_CONTROL (pycycling.ftms_parsers.training_status.TrainingStatus attribute)
+
+ heart_rate_measurement_supported (pycycling.ftms_parsers.fitness_machine_feature.FitnessMachineFeature attribute)
+ instant_power (pycycling.ftms_parsers.indoor_bike_data.IndoorBikeData attribute)
+
instant_speed (pycycling.ftms_parsers.indoor_bike_data.IndoorBikeData attribute)
instantaneous_cadence (pycycling.tacx_trainer_control.SpecificTrainerData attribute)
@@ -560,6 +575,8 @@ M
pycycling.heart_rate_service
pycycling.rear_view_radar
+
+ pycycling.rizer
pycycling.sterzo
@@ -653,6 +670,8 @@ P
pace_supported (pycycling.ftms_parsers.fitness_machine_feature.FitnessMachineFeature attribute)
param (pycycling.ftms_parsers.training_status.TrainingStatusMessage attribute)
+
+ parse_all_features() (in module pycycling.ftms_parsers.fitness_machine_feature)
parse_control_point_response() (in module pycycling.ftms_parsers.control_point)
@@ -661,6 +680,8 @@ P
parse_fitness_machine_status() (in module pycycling.ftms_parsers.fitness_machine_status)
parse_indoor_bike_data() (in module pycycling.ftms_parsers.indoor_bike_data)
+
+ parse_target_setting_features() (in module pycycling.ftms_parsers.fitness_machine_feature)
parse_training_status() (in module pycycling.ftms_parsers.training_status)
@@ -675,6 +696,8 @@ P
power_calibration_required (pycycling.tacx_trainer_control.SpecificTrainerData attribute)
power_measurement_supported (pycycling.ftms_parsers.fitness_machine_feature.FitnessMachineFeature attribute)
+
+ power_target_setting_supported (pycycling.ftms_parsers.fitness_machine_feature.TargetSettingFeatures attribute)
PRE_WORKOUT (pycycling.ftms_parsers.training_status.TrainingStatus attribute)
@@ -706,6 +729,8 @@ P
module
+
+
pycycling.data
@@ -713,8 +738,6 @@ P
module
-
-
pycycling.fitness_machine_service
@@ -776,6 +799,13 @@ P
+
+ pycycling.rizer
+
+
@@ -857,6 +887,8 @@ R
resistance_level (pycycling.ftms_parsers.indoor_bike_data.IndoorBikeData attribute)
resistance_level_supported (pycycling.ftms_parsers.fitness_machine_feature.FitnessMachineFeature attribute)
+
+ resistance_target_setting_supported (pycycling.ftms_parsers.fitness_machine_feature.TargetSettingFeatures attribute)
RESPONSE_CODE (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
@@ -867,6 +899,8 @@ R
right_crank (pycycling.cycling_power_service.SensorLocation attribute)
right_pedal (pycycling.cycling_power_service.SensorLocation attribute)
+
+ Rizer (class in pycycling.rizer)
RoadSurface (class in pycycling.tacx_trainer_control)
@@ -889,6 +923,8 @@ S
SensorMeasurementContext (class in pycycling.cycling_power_service)
set_basic_resistance() (pycycling.tacx_trainer_control.TacxTrainerControl method)
+
+ set_center() (pycycling.rizer.Rizer method)
set_command_status_data_page_handler() (pycycling.tacx_trainer_control.TacxTrainerControl method)
@@ -907,16 +943,34 @@ S
set_hr_measurement_handler() (pycycling.heart_rate_service.HeartRateService method)
set_indoor_bike_data_handler() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ SET_INDOOR_BIKE_SIMULATION_PARAMETERS (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
set_neo_modes() (pycycling.tacx_trainer_control.TacxTrainerControl method)
set_radar_measurement_handler() (pycycling.rear_view_radar.RearViewRadarService method)
+
+ set_simulation_parameters() (pycycling.fitness_machine_service.FitnessMachineService method)
set_specific_trainer_data_page_handler() (pycycling.tacx_trainer_control.TacxTrainerControl method)
- set_steering_measurement_callback() (pycycling.sterzo.Sterzo method)
+ SET_SPIN_DOWN_CONTROL (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_spin_down_control() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ set_steering_measurement_callback() (pycycling.rizer.Rizer method)
+
+
+ SET_TARGET_HEART_RATE (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_target_heart_rate() (pycycling.fitness_machine_service.FitnessMachineService method)
SET_TARGET_INCLINE (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_target_incline() (pycycling.fitness_machine_service.FitnessMachineService method)
SET_TARGET_POWER (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
@@ -932,13 +986,57 @@ S
SET_TARGET_SPEED (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
- set_track_resistance() (pycycling.tacx_trainer_control.TacxTrainerControl method)
+ set_target_speed() (pycycling.fitness_machine_service.FitnessMachineService method)
- set_training_status_handler() (pycycling.fitness_machine_service.FitnessMachineService method)
+ SET_TARGETED_CADENCE (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_targeted_cadence() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ SET_TARGETED_DISTANCE (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_targeted_distance() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ SET_TARGETED_EXPENDED_ENERGY (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_targeted_expended_energy() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ SET_TARGETED_NUMBER_OF_STEPS (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_targeted_number_of_steps() (pycycling.fitness_machine_service.FitnessMachineService method)
+
+ SET_TARGETED_NUMBER_OF_STRIDES (pycycling.ftms_parsers.control_point.FTMSControlPointOpCode attribute)
+
+ set_targeted_number_of_strides() (pycycling.fitness_machine_service.FitnessMachineService method)
+
-
-
+
@@ -1164,11 +1292,11 @@ Quick search
diff --git a/index.html b/index.html
index e9387f4..3ba5c5a 100644
--- a/index.html
+++ b/index.html
@@ -6,10 +6,10 @@
Welcome to pycycling’s documentation! — pycycling documentation
-
-
+
+
-
+
@@ -17,8 +17,9 @@
+
+
-
@@ -90,6 +91,10 @@ Welcome to pycycling’s documentation!RearViewRadarService
+ pycycling.rizer module
+
pycycling.sterzo module
@@ -158,7 +163,7 @@ Related Topics
-
+
@@ -181,11 +186,11 @@ Quick search