Closed ci-vamp closed 1 year ago
@tom-churchill actually i think this will not work because the write_data
is to be specific to each device.
may i ask how you discovered what write_data
to send to the device you wrote the program for? i am trying to use for TEMPer2 probes with vendor 0x1a86
and product 0xe025
write_data = [0x00, 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00]
update: i have sorted out. this was indeed the correct write data to send to the any TEMPer USB device. however, the issue had to do with my probe having 2 temperature sensors inside.
i have refactored the code to handle both single and multiple sensor readings. it is backwards compatible by making it an option to load both with default of False
this was a tough problem to solve. i had to find a "USB analyzer" tool to view the data packets and work out what was happening with the GUI program compared to the TemperWindows
program.
i used this one freeusbanalyzer from HDD Software. i do not know this software so ran it through virus scan and it came clean for the exe download on 12/01/23. i did enable the free trial to use the "HID report" view which helped.
you can see here the HID report
from TEMPer v26 GUI
from
TemperWindows
thank you again for your guidance. i did not know any of this before. hope this helps you or others.
"""
Windows USB direct access to TEMPerX probes
adapted from: https://github.com/tom-churchill/temper-windows
Original Author: Tom Churchill
Original License: MIT
"""
import time
import pywinusb.hid as hid
# all TEMPerX devices begin with this prefix
# magic string used as a fallback to load all devices if no HID filter criteria are provided
_TEMPer_DEVICE_PREFIX = "TEMPer"
# this is the "USB device output report data" used to trigger a temperature data response from the device
# determined by using FreeUsbAnalyzer (a USB traffic analyzer tool from HDD Software)
# https://freeusbanalyzer.com/
# virus scan as of 12/01/23: https://www.virustotal.com/gui/file/3bc7f717f1c83778786fca09b8f50b886596c1e86813be9fb50aa507a26ace4d
_TEMPer_DEVICE_TEMPERATURE_OUTPUT_REPORT_DATA = [
0x00, 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00]
class HidDeviceReader:
def __init__(self, device, timeout_seconds=0.1) -> None:
self.device = device
self.read_data = []
self.timeout_seconds = timeout_seconds
def __raw_data_handler(self, data):
self.read_data = [*self.read_data, data]
def load_data(self, output_report_data=[]):
device_is_active = (self.device.is_plugged()
and self.device.is_active())
assert device_is_active, f"Inactive device {self.device} cannot be opened"
try:
if not self.device.is_opened():
self.device.open()
self.device.set_raw_data_handler(self.__raw_data_handler)
self.device.send_output_report(output_report_data)
load_time = 0
sleep_time = 0.01
while load_time <= self.timeout_seconds:
time.sleep(sleep_time)
load_time += sleep_time
# progressively increase
sleep_time += (2 * sleep_time)
finally:
self.device.set_raw_data_handler(None)
self.device.close()
return self.read_data
class TemperWindows:
def __init__(self, product_name=None, vendor_id=None, product_id=None):
self.product_name = product_name
self.vendor_id = vendor_id
self.product_id = product_id
@staticmethod
def convert_data_to_temperature(data):
if not data:
return None
return float(data[3] * 256 + data[4]) / 100
def get_active_temper_devices(self, product_name=None, vendor_id=None, product_id=None):
filter_dict = dict()
if product_name or self.product_name:
filter_dict = dict(product_name=(
product_name or self.product_name))
if vendor_id or self.vendor_id:
filter_dict = dict(
**filter_dict, vendor_id=(vendor_id or self.vendor_id))
if product_id or self.product_id:
filter_dict = dict(
**filter_dict, product_id=(product_id or self.product_id))
if filter_dict:
return hid.HidDeviceFilter(**filter_dict).get_devices()
return [dev for dev in hid.find_all_hid_devices() if _TEMPer_DEVICE_PREFIX in dev.product_name]
def get_temperature(self, temp_probe_device_index: int = 0, with_outer_temp=False, product_name=None, vendor_id=None, product_id=None):
active_devices = self.get_active_temper_devices(
product_name=product_name, vendor_id=vendor_id, product_id=product_id)
if temp_probe_device_index > len(active_devices) - 1:
return (None, None) if with_outer_temp else None
device = active_devices[temp_probe_device_index]
device_reader = HidDeviceReader(device, timeout_seconds=0.1)
temp_data = device_reader.load_data(
_TEMPer_DEVICE_TEMPERATURE_OUTPUT_REPORT_DATA)
inner_temp_c = self.convert_data_to_temperature(temp_data[0])
outer_temp_c = self.convert_data_to_temperature(temp_data[1])
return (inner_temp_c, outer_temp_c) if with_outer_temp else inner_temp_c
hello your code really helped me. i could not get this working on windows through the GUI because it only allowed a single device to be polled at a time. after research i found your library. thank you
i have made some small adjustments to it that allows it to be more dynamic for discovery of TEMPerX probe devices when loading temperatures.
you can see the following changes. these all have defaults so will be backward compatible with previous version:
TemperWindows
constructorget_temper_devices
method with device filter criteria and fallback to all TEMPer devices if none are providedget_temperature
method as an override to theTemperWindows
instance fieldsif you would like please include this in your codebase for release. if not i understand and will use my local version.
thank you again for your help.