2 # Model Dreamlink WH1080
13 #WIND_DIRS = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
18 Open a connection to the device, using the PRODUCT and VENDOR information
19 @return reference to the device
21 usb_device = usb.core.find(idVendor=VENDOR, idProduct=PRODUCT)
23 if usb_device is None:
24 raise ValueError('Device not found')
26 usb_device.get_active_configuration()
28 # If we don't detach the kernel driver we get I/O errors
29 if usb_device.is_kernel_driver_active(0):
30 usb_device.detach_kernel_driver(0)
35 def read_block(device, offset):
37 Read a block of data from the specified device, starting at the given offset.
46 least_significant_bit = offset & 0xFF
47 most_significant_bit = offset >> 8 & 0xFF
49 # Construct a binary message
50 tbuf = struct.pack('BBBBBBBB',
53 least_significant_bit,
57 least_significant_bit,
60 timeout = 1000 # Milliseconds
61 retval = device.ctrl_transfer(0x21, # USB Requesttype
68 return device.read(0x81, 32, timeout)
73 dev.set_configuration()
77 # Get the first 32 Bytes of the fixed
78 fixed_block = read_block(dev, 0)
80 # Check that we have good data
81 if (fixed_block[0] != 0x55):
82 raise ValueError('Bad data returned')
84 # Bytes 31 and 32 when combined create an unsigned short int that tells us where to find the weather data we want
85 curpos = struct.unpack('H', fixed_block[30:32])[0]
86 current_block = read_block(dev, curpos)
89 indoor_humidity = current_block[1]
90 tlsb = current_block[2]
91 tmsb = current_block[3] & 0x7f
92 tsign = current_block[3] >> 7
93 indoor_temperature = (tmsb * 256 + tlsb) * 0.1
94 # Check if temperature is less than zero
96 indoor_temperature *= -1
99 outdoor_humidity = current_block[4]
100 tlsb = current_block[5]
101 tmsb = current_block[6] & 0x7f
102 tsign = current_block[6] >> 7
103 outdoor_temperature = (tmsb * 256 + tlsb) * 0.1
104 # Check if temperature is less than zero
106 outdoor_temperature *= -1
108 # Bytes 8 and 9 when combined create an unsigned short int that we multiply by 0.1 to find the absolute pressure
109 abs_pressure = struct.unpack('H', current_block[7:9])[0]*0.1
111 wind = current_block[9]
112 gust = current_block[10]
113 wind_extra = current_block[11]
114 wind_dir = current_block[12]
116 # Bytes 14 and 15 when combined create an unsigned short int
117 # that we multiply by 0.3 to find the total rain
118 # I'm not confident that this is correct. Neither abs_pressure nor
119 # total_rain are returning sane values. In fact total_rain has
120 # stayed static despite rainfall
121 # Looks like I fixed it. They used fixed_block instead of current_block
122 total_rain = struct.unpack('H', current_block[13:15])[0]*0.3
124 # Calculate wind speeds
125 wind_speed = (wind + ((wind_extra & 0x0F) << 8)) * 0.38 # Was 0.1
126 gust_speed = (gust + ((wind_extra & 0xF0) << 4)) * 0.38 # Was 0.1
129 ############### print data
131 print('"indoor_humidity": "%i",' %indoor_humidity)
132 print('"indoor_temperature": "%2.1f",' %indoor_temperature)
133 print('"outdoor_humidity": "%i",' %outdoor_humidity)
134 print('"outdoor_temperature": "%2.1f",' %outdoor_temperature)
135 print('"abs_pressure": "%4.1f",' %abs_pressure)
136 print('"total_rain": "%3.1f",' %total_rain)
137 print('"wind_speed": "%2.1f",' %wind_speed)
138 print('"gust_speed": "%2.1f",' %gust_speed)
140 print('"wind_dir_deg": "%s",' %(wind_dir*22.5))
141 # print('"wind_dir": "%s",' %WIND_DIRS[wind_dir])
142 print('"TS": "%s"' %(datetime.datetime.utcnow().isoformat()))
146 if __name__ == "__main__":