def _prepare_profile(original, critcalTempature): clamped = ((temp, clamp(duty, 0, _MAX_FAN_RPM)) for temp, duty in original) normal = normalize_profile(clamped, critcalTempature, _MAX_FAN_RPM) missing = _PROFILE_LENGTH - len(normal) if missing < 0: raise ValueError(f'too many points in profile (remove {-missing})') if missing > 0: normal += missing * [(critcalTempature, _MAX_FAN_RPM)] return normal
def _prepare_profile(original): clamped = ((temp, clamp(duty, 0, 100)) for temp, duty in original) normal = normalize_profile(clamped, _CRITICAL_TEMPERATURE) missing = _PROFILE_LENGTH - len(normal) if missing < 0: raise ValueError(f'too many points in profile (remove {-missing})') if missing > 0: normal += missing * [(_CRITICAL_TEMPERATURE, 100)] return normal
def set_speed_profile(self, channel, profile, **kwargs): """Set channel to use a speed profile.""" if channel != 'pump': assert False, 'kraken X3 devices only support changing pump speeds' header = [0x72, 0x01, 0x00, 0x00] norm = normalize_profile(profile, _CRITICAL_TEMPERATURE) interp = [(interpolate_profile(norm, t)) for t in range(20, 60)] LOGGER.debug('setting pump curve: %s', [(num + 20, duty) for (num, duty) in enumerate(interp)]) self._write(header + interp)
def set_speed_profile(self, channel, profile, **kwargs): """Set channel to use a speed profile.""" cid, dmin, dmax = self._speed_channels[channel] header = [0x72, cid, 0x00, 0x00] norm = normalize_profile(profile, _CRITICAL_TEMPERATURE) stdtemps = list(range(20, _CRITICAL_TEMPERATURE + 1)) interp = [clamp(interpolate_profile(norm, t), dmin, dmax) for t in stdtemps] for temp, duty in zip(stdtemps, interp): _LOGGER.info('setting %s PWM duty to %d%% for liquid temperature >= %d°C', channel, duty, temp) self._write(header + interp)
def parse_profile(arg, mintemp=0, maxtemp=100, minduty=0, maxduty=100): """Parse, validate and normalize a temperature–duty profile. >>> parse_profile('(20,30),(30,50),(34,80),(40,90)', 0, 60, 25, 100) [(20, 30), (30, 50), (34, 80), (40, 90), (60, 100)] >>> parse_profile('35', 0, 60, 25, 100) [(0, 35), (59, 35), (60, 100)] The profile is validated in structure and acceptable ranges. Duty is checked against `minduty` and `maxduty`. Temperature must be between `mintemp` and `maxtemp`. >>> parse_profile('(20,30),(50,100', 0, 60, 25, 100) Traceback (most recent call last): ... ValueError: Profile must be comma-separated (temperature, duty) tuples >>> parse_profile('(20,30),(50,100,2)', 0, 60, 25, 100) Traceback (most recent call last): ... ValueError: Profile must be comma-separated (temperature, duty) tuples >>> parse_profile('(20,30),(50,97.6)', 0, 60, 25, 100) Traceback (most recent call last): ... ValueError: Duty must be integer number between 25 and 100 >>> parse_profile('(20,15),(50,100)', 0, 60, 25, 100) Traceback (most recent call last): ... ValueError: Duty must be integer number between 25 and 100 >>> parse_profile('(20,30),(70,100)', 0, 60, 25, 100) Traceback (most recent call last): ... ValueError: Temperature must be integer number between 0 and 60 """ try: val = ast.literal_eval('[' + arg + ']') if len(val) == 1 and isinstance(val[0], int): # for arg == '<number>' set fixed duty between mintemp and maxtemp - 1 val = [(mintemp, val[0]), (maxtemp - 1, val[0])] except: raise ValueError('profile must be comma-separated (temperature, duty) tuples') for step in val: if not isinstance(step, tuple) or len(step) != 2: raise ValueError('profile must be comma-separated (temperature, duty) tuples') temp, duty = step if not isinstance(temp, int) or temp < mintemp or temp > maxtemp: raise ValueError('temperature must be integer between {} and {}'.format(mintemp, maxtemp)) if not isinstance(duty, int) or duty < minduty or duty > maxduty: raise ValueError('duty must be integer between {} and {}'.format(minduty, maxduty)) return normalize_profile(val, critx=maxtemp)
def set_speed_profile(self, channel, profile, **kwargs): """Set channel to use a speed profile.""" if not self.supports_cooling_profiles: raise NotSupportedByDevice() norm = normalize_profile(profile, _CRITICAL_TEMPERATURE) # due to a firmware limitation the same set of temperatures must be # used on both channels; we reduce the number of writes by trimming the # interval and/or resolution to the most useful range stdtemps = list(range(20, 50)) + list(range(50, 60, 2)) + [60] interp = [(t, interpolate_profile(norm, t)) for t in stdtemps] cbase, dmin, dmax = _SPEED_CHANNELS[channel] for i, (temp, duty) in enumerate(interp): duty = clamp(duty, dmin, dmax) LOGGER.info('setting %s PWM duty to %i%% for liquid temperature >= %i°C', channel, duty, temp) self._write([0x2, 0x4d, cbase + i, temp, duty])
def set_speed_profile(self, channel, profile, **kwargs): """Set channel to use a speed profile.""" if not self.supports_cooling_profiles: raise NotImplementedError() cbase, dmin, dmax = _SPEED_CHANNELS[channel] # ideally we could just call normalize_profile (optionally followed by autofill_profile), # but Kraken devices currently require the same set of temperatures on both channels stdtemps = range(20, 62, 2) tmp = normalize_profile(profile, _CRITICAL_TEMPERATURE) norm = [(t, interpolate_profile(tmp, t)) for t in stdtemps] for i, (temp, duty) in enumerate(norm): duty = clamp(duty, dmin, dmax) LOGGER.info('setting %s PWM duty to %i%% for liquid temperature >= %i°C', channel, duty, temp) self._write([0x2, 0x4d, cbase + i, temp, duty]) self.device.release()