Conversation
|
This may solve #416. |
|
Additional unit tests are needed. It is recommended to create a base class |
|
@elgris @zerzhang I integrated my code into the new structure, removed my own time-implementation and started implementing unit-tests (more are coming when i have more time).
I have no way to verify which methods would actually work on a MeterPro, so i am not sure abbout that. |
|
|
||
| COMMAND_TEMPERATURE_UPDATE_INTERVAL = f"{SETTINGS_HEADER}070105" | ||
| COMMAND_CO2_UPDATE_INTERVAL = f"{SETTINGS_HEADER}0b06" | ||
| COMMAND_FORCE_NEW_CO2_Measurement = f"{SETTINGS_HEADER}0b04" |
There was a problem hiding this comment.
(style) it should be all uppercase COMMAND_FORCE_NEW_CO2_MEASUREMENT.
| + absolute_humidity_low_bytes | ||
| ) | ||
|
|
||
| async def set_alert_co2(self, on: bool, co2_low: int, co2_high: max, reverse: bool): |
|
|
||
| mode = 0x00 if not on else (0x04 if reverse else 0x03) | ||
| await self._send_command( | ||
| COMMAND_ALERT_CO2 + f"{mode:02x}" + f"{co2_high:04x}" + f"{co2_low:04x}" |
There was a problem hiding this comment.
Do we need to send these limits if on is False? I haven't tried this with the device, but I'd assume that it expects all zeroes. If so, then we can probably make set default values for co2_high and co2_low args to 0 and drop their validation when on is False.
There was a problem hiding this comment.
No, this would be a reasonable design, but is not what the original app does.
The test I pushed for this method contains only payloads from the original App.
When a mode gets turned off, all values remain set as they were before. And all of them get transmitted.
I tried to stick as close to this as possible. I did not test how the device would react to your proposal.
| Sets the interval in which temperature and humidity are measured in battery powered mode. | ||
| Original App assumes minutes in {5, 10, 30} | ||
| """ | ||
| seconds = minutes * 60 |
There was a problem hiding this comment.
Agreed, it is recommended to use seconds as the parameter.
|
|
||
| async def set_button_function(self, change_unit: bool, change_data_source: bool): | ||
| """ | ||
| Sets the function of te top button: |
|
I'd also suggest checking |
|
I added more tests and fixed the typos you found. |
|
@zerzhang Could you provide me with some guidance, what actions are required to move this forward? |
|
@ahrmn : maybe remove |
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
|
@zerzhang May I ask what is blocking this? |
@ahrmn Here is the latest communication protocol file; you can make adjustments based on it. |
| async def show_battery_level(self, show_battery: bool): | ||
| """Show or hide battery level on the display.""" | ||
| show_battery_byte = "01" if show_battery else "00" | ||
| await super()._send_command(COMMAND_SHOW_BATTERY_LEVEL + show_battery_byte) |
There was a problem hiding this comment.
00 indicates that the battery icon will be displayed when the battery level is <= 20%, 01 indicates the default display.
| await super()._send_command( | ||
| COMMAND_COMFORTLEVEL | ||
| + hot_byte | ||
| + f"{wet:02x}" |
There was a problem hiding this comment.
bit7 is reserved, bit6-0 represent the humidity.
| point_five += 0x05 | ||
| if int(hot * 10) % 10 == 5: | ||
| point_five += 0x50 | ||
| return f"{point_five:02x}" |
There was a problem hiding this comment.
def get_temp_decimal_byte(cold: float, hot: float) -> int:
cold_dec = int(round(cold * 10)) % 10
hot_dec = int(round(hot * 10)) % 10
if not (0 <= cold_dec <= 9 and 0 <= hot_dec <= 9):
raise ValueError("Decimal part out of range")
return (hot_dec << 4) | cold_dec
"Temperature ValueH decimal
bit [7-4]: Temperature ValueH decimal, upper limit of temperature alarm, decimal part
Temperature ValueL decimal
bit [3-0]: Temperature ValueL decimal, lower limit of temperature alarm, decimal part
0000 –1001: 0~9"
This section itself supports setting decimal numbers from 0 to 9.
| + f"{wet:02x}" | ||
| + point_five | ||
| + cold_byte | ||
| + f"{dry:02x}" |
| await super()._send_command( | ||
| COMMAND_ALERT_SOUND + f"{volume:02x}" + sound_on_byte | ||
| ) | ||
|
|
There was a problem hiding this comment.
Alarm Volume
0xFF -hold
0x01 Off
0x02 Low
0x03 Medium (Default)
0x04 High
Audible and visual alarm mode
0x00 - disable
0x01 - Flashing only
0x02 - Sound + flashing
| seconds = minutes * 60 | ||
| await self._send_command(COMMAND_TEMPERATURE_UPDATE_INTERVAL + f"{seconds:04x}") | ||
|
|
||
| async def set_co2_update_interval(self, minutes: int): |
| seconds = minutes * 60 | ||
| await self._send_command(COMMAND_CO2_UPDATE_INTERVAL + f"{seconds:04x}") | ||
|
|
||
| async def set_button_function(self, change_unit: bool, change_data_source: bool): |
| COMMAND_BUTTON_FUNCTION + change_unit_byte + change_data_source_byte | ||
| ) | ||
|
|
||
| async def force_new_co2_measurement(self): |
| """Requests a new CO2 measurement, regardless of update interval""" | ||
| await self._send_command(COMMAND_FORCE_NEW_CO2_MEASUREMENT) | ||
|
|
||
| async def calibrate_co2_sensor(self): |
| """ | ||
| await self._send_command(COMMAND_CALIBRATE_CO2_SENSOR) | ||
|
|
||
| async def set_alert_sound(self, sound_on: bool, volume: int): |
I reverse-engineered the settings of the Meter Pro (CO2) and implemented them here.
I have no means of testing if this work for the other Meter models.
An example script to fully configure a device using this would be for example
Additionally one can trigger a new measurement regardless of the frequency using
await meter.force_new_CO2_measurement()and calibrate the sensor (at fresh air!) usingawait meter.calibrate_co2_sensor().