blueair-api 1.9.1__tar.gz → 1.33.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {blueair_api-1.9.1/src/blueair_api.egg-info → blueair_api-1.33.2}/PKG-INFO +9 -13
  2. {blueair_api-1.9.1 → blueair_api-1.33.2}/README.md +0 -2
  3. blueair_api-1.33.2/pyproject.toml +38 -0
  4. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/__init__.py +1 -0
  5. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/callbacks.py +3 -1
  6. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/const.py +9 -10
  7. blueair_api-1.33.2/src/blueair_api/device.py +134 -0
  8. blueair_api-1.33.2/src/blueair_api/device_aws.py +250 -0
  9. blueair_api-1.33.2/src/blueair_api/http_aws_blueair.py +226 -0
  10. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/http_blueair.py +117 -8
  11. blueair_api-1.33.2/src/blueair_api/intermediate_representation_aws.py +183 -0
  12. blueair_api-1.33.2/src/blueair_api/model_enum.py +90 -0
  13. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/stub.py +4 -8
  14. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/util.py +26 -12
  15. blueair_api-1.33.2/src/blueair_api/util_bootstrap.py +69 -0
  16. {blueair_api-1.9.1 → blueair_api-1.33.2/src/blueair_api.egg-info}/PKG-INFO +9 -13
  17. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api.egg-info/SOURCES.txt +5 -2
  18. blueair_api-1.33.2/tests/test_device_aws.py +548 -0
  19. blueair_api-1.33.2/tests/test_intermediate_representation_aws.py +127 -0
  20. blueair_api-1.9.1/pyproject.toml +0 -6
  21. blueair_api-1.9.1/setup.py +0 -39
  22. blueair_api-1.9.1/src/blueair_api/device.py +0 -97
  23. blueair_api-1.9.1/src/blueair_api/device_aws.py +0 -133
  24. blueair_api-1.9.1/src/blueair_api/http_aws_blueair.py +0 -234
  25. blueair_api-1.9.1/src/blueair_api/util_bootstrap.py +0 -67
  26. {blueair_api-1.9.1 → blueair_api-1.33.2}/LICENSE +0 -0
  27. {blueair_api-1.9.1 → blueair_api-1.33.2}/setup.cfg +0 -0
  28. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/errors.py +0 -0
  29. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api/util_http.py +0 -0
  30. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api.egg-info/dependency_links.txt +0 -0
  31. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api.egg-info/requires.txt +0 -0
  32. {blueair_api-1.9.1 → blueair_api-1.33.2}/src/blueair_api.egg-info/top_level.txt +0 -0
@@ -1,21 +1,19 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: blueair_api
3
- Version: 1.9.1
3
+ Version: 1.33.2
4
4
  Summary: Blueair Api Wrapper
5
- Home-page: https://github.com/dahlb/blueair_api
6
- Author: Brendan Dahl
7
- Author-email: dahl.brendan@gmail.com
8
- Project-URL: Bug Reports, https://github.com/dahlb/blueair_api/issues
9
- Project-URL: Source, https://github.com/dahlb/blueair_api
10
- Keywords: blueair,api
5
+ Author-email: Brendan Dahl <dahl.brendan@gmail.com>
6
+ Project-URL: Homepage, https://github.com/dahlb/blueair_api
7
+ Project-URL: Bug Tracker, https://github.com/dahlb/blueair_api/issues
8
+ Keywords: hatch,rest-mini,api
11
9
  Classifier: Development Status :: 4 - Beta
12
- Classifier: Framework :: AsyncIO
13
10
  Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Framework :: AsyncIO
14
12
  Classifier: Natural Language :: English
15
13
  Classifier: Intended Audience :: Developers
16
- Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
17
15
  Classifier: Operating System :: MacOS :: MacOS X
18
- Requires-Python: >=3.9, <4
16
+ Requires-Python: <4,>=3.12.0
19
17
  Description-Content-Type: text/markdown
20
18
  License-File: LICENSE
21
19
  Requires-Dist: aiohttp>=3.8.1
@@ -42,5 +40,3 @@ a lot of this is based on [hass-blueair](https://github.com/aijayadams/hass-blue
42
40
  [releases]: https://github.com/dahlb/blueair_api/releases
43
41
  [buymecoffee]: https://www.buymeacoffee.com/dahlb
44
42
  [buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge
45
-
46
-
@@ -20,5 +20,3 @@ a lot of this is based on [hass-blueair](https://github.com/aijayadams/hass-blue
20
20
  [releases]: https://github.com/dahlb/blueair_api/releases
21
21
  [buymecoffee]: https://www.buymeacoffee.com/dahlb
22
22
  [buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge
23
-
24
-
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=68.0.0",
4
+ "wheel",
5
+ "aiohttp>=3.8.1",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "blueair_api"
11
+ version = "1.33.2"
12
+ authors = [
13
+ { name="Brendan Dahl", email="dahl.brendan@gmail.com" },
14
+ ]
15
+ description = "Blueair Api Wrapper"
16
+ readme = "README.md"
17
+ requires-python = ">=3.12.0, <4"
18
+ dependencies = [
19
+ "aiohttp>=3.8.1",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 4 - Beta",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Framework :: AsyncIO",
25
+ "Natural Language :: English",
26
+ "Intended Audience :: Developers",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Operating System :: MacOS :: MacOS X",
29
+ ]
30
+ keywords = [
31
+ "hatch",
32
+ "rest-mini",
33
+ "api"
34
+ ]
35
+
36
+ [project.urls]
37
+ "Homepage" = "https://github.com/dahlb/blueair_api"
38
+ "Bug Tracker" = "https://github.com/dahlb/blueair_api/issues"
@@ -4,3 +4,4 @@ from .http_aws_blueair import HttpAwsBlueair
4
4
  from .util_bootstrap import get_devices, get_aws_devices
5
5
  from .device import Device
6
6
  from .device_aws import DeviceAws
7
+ from .model_enum import ModelEnum, FeatureEnum
@@ -4,6 +4,8 @@ _LOGGER = logging.getLogger(__name__)
4
4
 
5
5
 
6
6
  class CallbacksMixin:
7
+ __slots__ = ['_callbacks']
8
+
7
9
  def _setup_callbacks(self):
8
10
  self._callbacks = set()
9
11
 
@@ -20,6 +22,6 @@ class CallbacksMixin:
20
22
  def publish_updates(self) -> None:
21
23
  if not hasattr(self, "_callbacks"):
22
24
  self._setup_callbacks()
23
- _LOGGER.debug(f"{self.name} publishing updates")
25
+ _LOGGER.debug(f"{id(self)} publishing updates")
24
26
  for callback in self._callbacks:
25
27
  callback()
@@ -1,7 +1,5 @@
1
- from typing import Union
2
1
  from collections.abc import Mapping
3
- from typing_extensions import TypedDict
4
-
2
+ from typing import TypedDict
5
3
 
6
4
  SENSITIVE_FIELD_NAMES = [
7
5
  "username",
@@ -11,13 +9,6 @@ SENSITIVE_FIELD_NAMES = [
11
9
  # The BlueAir API uses a fixed API key.
12
10
  API_KEY = "eyJhbGciOiJIUzI1NiJ9.eyJncmFudGVlIjoiYmx1ZWFpciIsImlhdCI6MTQ1MzEyNTYzMiwidmFsaWRpdHkiOi0xLCJqdGkiOiJkNmY3OGE0Yi1iMWNkLTRkZDgtOTA2Yi1kN2JkNzM0MTQ2NzQiLCJwZXJtaXNzaW9ucyI6WyJhbGwiXSwicXVvdGEiOi0xLCJyYXRlTGltaXQiOi0xfQ.CJsfWVzFKKDDA6rWdh-hjVVVE9S3d6Hu9BzXG9htWFw" # noqa: E501
13
11
 
14
- MeasurementBundle = TypedDict(
15
- "MeasurementBundle",
16
- {"sensors": list[str], "datapoints": list[list[Union[int, float]]]},
17
- )
18
-
19
- MeasurementList = list[Mapping[str, Union[int, float]]]
20
-
21
12
  AWS_APIKEYS = {
22
13
  "us": {
23
14
  "gigyaRegion": "us1",
@@ -32,3 +23,11 @@ AWS_APIKEYS = {
32
23
  "apiKey": "3_qRseYzrUJl1VyxvSJANalu_kNgQ83swB1B9uzgms58--5w1ClVNmrFdsDnWVQQCl",
33
24
  },
34
25
  }
26
+
27
+
28
+ class MeasurementBundle(TypedDict):
29
+ sensors: list[str]
30
+ datapoints: list[list[int | float]]
31
+
32
+
33
+ MeasurementList = list[Mapping[str, int | float]]
@@ -0,0 +1,134 @@
1
+ from .callbacks import CallbacksMixin
2
+ from .http_blueair import HttpBlueair
3
+ from .util import transform_data_points, safely_get_json_value, convert_none_to_not_implemented
4
+ from dataclasses import dataclass, field
5
+ from logging import getLogger
6
+
7
+ _LOGGER = getLogger(__name__)
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class Device(CallbacksMixin):
12
+ @classmethod
13
+ async def create_device(cls, api, uuid, name, mac, refresh=False):
14
+ _LOGGER.debug("UUID:"+uuid)
15
+ info = await api.get_info(uuid)
16
+ device = Device(
17
+ api=api,
18
+ uuid=uuid,
19
+ name=name,
20
+ mac=mac,
21
+ timezone=info["timezone"],
22
+ compatibility=info["compatibility"],
23
+ model=info["model"],
24
+ firmware=info["firmware"],
25
+ mcu_firmware=info["mcuFirmware"],
26
+ wlan_driver=info["wlanDriver"],
27
+ room_location=info["roomLocation"]
28
+ )
29
+ if refresh:
30
+ await device.refresh()
31
+ _LOGGER.debug(f"create_device blueair device: {device}")
32
+ return device
33
+
34
+ api: HttpBlueair = field(repr=False)
35
+ raw_info : dict[str: any] = field(repr=False, init=False)
36
+
37
+ uuid: str | None = None
38
+ name: str | None = None
39
+ timezone: str | None = None
40
+ compatibility: str | None = None
41
+ model: str | None = None
42
+ mac: str | None = None
43
+ firmware: str | None = None
44
+ mcu_firmware: str | None = None
45
+ wlan_driver: str | None = None
46
+ room_location: str | None = None
47
+
48
+ brightness: int | None = None
49
+ child_lock: bool | None = None
50
+ night_mode: bool | None = None
51
+ fan_speed: int | None = None
52
+ fan_auto_mode: bool | None = None
53
+ filter_expired: bool | None = None
54
+ wifi_working: bool | None = None
55
+
56
+ pm1: int | None = NotImplemented
57
+ pm10: int | None = NotImplemented
58
+ pm25: int | None = NotImplemented
59
+ voc: int | None = NotImplemented
60
+ co2: int | None = NotImplemented
61
+ temperature: float | None = NotImplemented
62
+ humidity: float | None = NotImplemented
63
+ all_pollution: float | None = NotImplemented
64
+
65
+ async def refresh(self):
66
+ _LOGGER.debug("Requesting current attributes...")
67
+ self.raw_info = {}
68
+ attributes = await self.api.get_attributes(self.uuid)
69
+ self.raw_info["attributes"] = attributes
70
+ _LOGGER.debug(f"result: {attributes}")
71
+ if "brightness" in attributes:
72
+ self.brightness = int(attributes["brightness"])
73
+ else:
74
+ self.brightness = NotImplemented
75
+ if "child_lock" in attributes:
76
+ self.child_lock = attributes["child_lock"] == "1"
77
+ else:
78
+ self.child_lock = NotImplemented
79
+ if "night_mode" in attributes:
80
+ self.night_mode = attributes["night_mode"] == "1"
81
+ else:
82
+ self.night_mode = NotImplemented
83
+ if "fan_speed" in attributes:
84
+ self.fan_speed = int(attributes["fan_speed"])
85
+ else:
86
+ self.fan_speed = NotImplemented
87
+ if "filter_status" in attributes:
88
+ self.filter_expired = attributes["filter_status"] != "OK"
89
+ else:
90
+ self.filter_expired = NotImplemented
91
+ if "mode" in attributes:
92
+ self.fan_auto_mode = attributes["mode"] == "auto"
93
+ else:
94
+ self.fan_auto_mode = NotImplemented
95
+ if "wifi_status" in attributes:
96
+ self.wifi_working = attributes["wifi_status"] == "1"
97
+ else:
98
+ self.wifi_working = False
99
+ if self.compatibility != "sense+":
100
+ datapoints = transform_data_points(await self.api.get_data_points_since(self.uuid))
101
+ self.raw_info["datapoints"] = datapoints
102
+ for data_point in datapoints:
103
+ _LOGGER.debug(data_point)
104
+ self.pm25 = convert_none_to_not_implemented(safely_get_json_value(data_point, "pm25", int))
105
+ self.pm10 = convert_none_to_not_implemented(safely_get_json_value(data_point, "pm10", int))
106
+ self.pm1 = convert_none_to_not_implemented(safely_get_json_value(data_point, "pm1", int))
107
+ self.voc = convert_none_to_not_implemented(safely_get_json_value(data_point, "voc", int))
108
+ self.co2 = convert_none_to_not_implemented(safely_get_json_value(data_point, "co2", int))
109
+ self.temperature = convert_none_to_not_implemented(safely_get_json_value(data_point, "temperature", int))
110
+ self.humidity = convert_none_to_not_implemented(safely_get_json_value(data_point, "humidity", int))
111
+ self.all_pollution = convert_none_to_not_implemented(safely_get_json_value(data_point, "all_pollution", int))
112
+ _LOGGER.debug(f"refreshed blueair device: {self}")
113
+ self.publish_updates()
114
+
115
+ async def set_fan_speed(self, new_speed: str):
116
+ self.fan_speed = int(new_speed)
117
+ await self.api.set_fan_speed(self.uuid, new_speed)
118
+ self.publish_updates()
119
+
120
+ async def set_brightness(self, new_brightness: int):
121
+ self.brightness = new_brightness
122
+ await self.api.set_brightness(self.uuid, new_brightness)
123
+ self.publish_updates()
124
+
125
+ async def set_child_lock(self, enabled: bool):
126
+ self.child_lock = enabled
127
+ await self.api.set_child_lock(self.uuid, enabled)
128
+ self.publish_updates()
129
+
130
+ async def set_fan_auto_mode(self, fan_auto_mode: bool):
131
+ self.fan_auto_mode = fan_auto_mode
132
+ await self.api.set_fan_auto_mode(self.uuid, fan_auto_mode)
133
+ self.publish_updates()
134
+
@@ -0,0 +1,250 @@
1
+ import logging
2
+ from json import dumps
3
+
4
+ from .callbacks import CallbacksMixin
5
+ from .http_aws_blueair import HttpAwsBlueair
6
+ from .model_enum import ModelEnum
7
+ from . import intermediate_representation_aws as ir
8
+ from dataclasses import dataclass, field
9
+
10
+ _LOGGER = logging.getLogger(__name__)
11
+
12
+ type AttributeType[T] = T | None
13
+
14
+ @dataclass(slots=True)
15
+ class DeviceAws(CallbacksMixin):
16
+ @classmethod
17
+ async def create_device(cls, api, uuid, name, mac, type_name, refresh=False):
18
+ _LOGGER.debug("UUID:"+uuid)
19
+ device_aws = DeviceAws(
20
+ api=api,
21
+ uuid=uuid,
22
+ name_api=name,
23
+ mac=mac,
24
+ type_name=type_name,
25
+ )
26
+ if refresh:
27
+ await device_aws.refresh()
28
+ _LOGGER.debug(f"create_device blueair device_aws: {device_aws}")
29
+ return device_aws
30
+
31
+ api: HttpAwsBlueair = field(repr=False)
32
+ raw_info : dict[str: any] = field(repr=False, init=False)
33
+
34
+ uuid : str | None = None
35
+ name : str | None = None
36
+ name_api : str | None = None
37
+ mac : str | None = None
38
+ type_name : str | None = None
39
+
40
+ # Attributes are defined below.
41
+ # We mandate that unittests shall test all fields of AttributeType.
42
+ sku : AttributeType[str] = None
43
+ firmware : AttributeType[str] = None
44
+ mcu_firmware : AttributeType[str] = None
45
+ serial_number : AttributeType[str] = None
46
+
47
+ brightness : AttributeType[int] = None
48
+ child_lock : AttributeType[bool] = None
49
+ fan_speed : AttributeType[int] = None
50
+ fan_auto_mode : AttributeType[bool] = None
51
+ standby : AttributeType[bool] = None
52
+ night_mode : AttributeType[bool] = None
53
+ germ_shield : AttributeType[bool] = None
54
+
55
+ pm1 : AttributeType[int] = None
56
+ pm2_5 : AttributeType[int] = None
57
+ pm10 : AttributeType[int] = None
58
+ tVOC : AttributeType[int] = None
59
+ temperature : AttributeType[int] = None
60
+ humidity : AttributeType[int] = None
61
+ filter_usage_percentage : AttributeType[int] = None
62
+ wifi_working : AttributeType[bool] = None
63
+
64
+ wick_usage_percentage : AttributeType[int] = None
65
+ wick_dry_mode : AttributeType[bool] = None
66
+ water_shortage : AttributeType[bool] = None
67
+ auto_regulated_humidity : AttributeType[int] = None
68
+
69
+ main_mode: AttributeType[int] = None # api value 0 purify only, 1 heat on, 2 cool on
70
+ heat_sub_mode: AttributeType[int] = None # api value 1 heat on, 2 heat on with fan auto
71
+ heat_fan_speed: AttributeType[int] = None # api value 11/37/64/91
72
+ heat_temp: AttributeType[int] = None # api value is celsius * 10
73
+ cool_sub_mode: AttributeType[int] = None # api value 1 cool on, 2 cool on with fan auto
74
+ cool_fan_speed: AttributeType[int] = None # api value 11/37/64/91
75
+ ap_sub_mode: AttributeType[int] = None # api value 1 manual speeed, 2 auto fan speed
76
+ fan_speed_0: AttributeType[int] = None # api value 11/37/64/91
77
+ temperature_unit: AttributeType[int] = None # api value of 1 is celcius
78
+
79
+ async def refresh(self):
80
+ _LOGGER.debug(f"refreshing blueair device aws: {self}")
81
+ self.raw_info = await self.api.device_info(self.name_api, self.uuid)
82
+ _LOGGER.debug(dumps(self.raw_info, indent=2))
83
+
84
+ # ir.parse_json(ir.Attribute, ir.query_json(info, "configuration.da"))
85
+ ds = ir.parse_json(ir.Sensor, ir.query_json(self.raw_info, "configuration.ds"))
86
+ dc = ir.parse_json(ir.Control, ir.query_json(self.raw_info, "configuration.dc"))
87
+
88
+ sensor_data = ir.SensorPack(self.raw_info["sensordata"]).to_latest_value()
89
+
90
+ def sensor_data_safe_get(key):
91
+ return sensor_data.get(key) if key in ds else NotImplemented
92
+
93
+ self.pm1 = sensor_data_safe_get("pm1")
94
+ self.pm2_5 = sensor_data_safe_get("pm2_5")
95
+ self.pm10 = sensor_data_safe_get("pm10")
96
+ self.tVOC = sensor_data_safe_get("tVOC")
97
+ self.temperature = sensor_data_safe_get("t")
98
+ self.humidity = sensor_data_safe_get("h")
99
+
100
+ def info_safe_get(path):
101
+ # directly reads for the schema. If the schema field is
102
+ # undefined, it is NotImplemented, not merely unavailable.
103
+ value = ir.query_json(self.raw_info, path)
104
+ if value is None:
105
+ return NotImplemented
106
+ return value
107
+
108
+ self.name = info_safe_get("configuration.di.name")
109
+ self.firmware = info_safe_get("configuration.di.cfv")
110
+ self.mcu_firmware = info_safe_get("configuration.di.mfv")
111
+ self.serial_number = info_safe_get("configuration.di.ds")
112
+ self.sku = info_safe_get("configuration.di.sku")
113
+
114
+ states = ir.SensorPack(self.raw_info["states"]).to_latest_value()
115
+
116
+ def states_safe_get(key):
117
+ return states.get(key) if key in dc else NotImplemented
118
+
119
+ # "online" is not defined in the schema.
120
+ self.wifi_working = states.get("online")
121
+
122
+ self.standby = states_safe_get("standby")
123
+ self.night_mode = states_safe_get("nightmode")
124
+ self.germ_shield = states_safe_get("germshield")
125
+ self.brightness = states_safe_get("brightness")
126
+ self.child_lock = states_safe_get("childlock")
127
+ self.fan_speed = states_safe_get("fanspeed")
128
+ self.fan_auto_mode = states_safe_get("automode")
129
+ self.filter_usage_percentage = states_safe_get("filterusage")
130
+
131
+ self.wick_usage_percentage = states_safe_get("wickusage")
132
+ self.wick_dry_mode = states_safe_get("wickdrys")
133
+ self.auto_regulated_humidity = states_safe_get("autorh")
134
+ self.water_shortage = states_safe_get("wshortage")
135
+
136
+ self.main_mode = states_safe_get("mainmode")
137
+ self.heat_temp = states_safe_get("heattemp")
138
+ self.heat_sub_mode = states_safe_get("heatsubmode")
139
+ self.heat_fan_speed = states_safe_get("heatfs")
140
+ self.cool_sub_mode = states_safe_get("coolsubmode")
141
+ self.cool_fan_speed = states_safe_get("coolfs")
142
+ self.ap_sub_mode = states_safe_get("apsubmode")
143
+ self.fan_speed_0 = states_safe_get("fsp0")
144
+ self.temperature_unit = states_safe_get("tu")
145
+
146
+ self.publish_updates()
147
+ _LOGGER.debug(f"refreshed blueair device aws: {self}")
148
+
149
+ async def set_brightness(self, value: int):
150
+ self.brightness = value
151
+ await self.api.set_device_info(self.uuid, "brightness", "v", value)
152
+ self.publish_updates()
153
+
154
+ async def set_fan_speed(self, value: int):
155
+ self.fan_speed = value
156
+ await self.api.set_device_info(self.uuid, "fanspeed", "v", value)
157
+ self.publish_updates()
158
+
159
+ async def set_standby(self, value: bool):
160
+ self.standby = value
161
+ await self.api.set_device_info(self.uuid, "standby", "vb", value)
162
+ self.publish_updates()
163
+
164
+ async def set_fan_auto_mode(self, fan_auto_mode: bool):
165
+ self.fan_auto_mode = fan_auto_mode
166
+ await self.api.set_device_info(self.uuid, "automode", "vb", fan_auto_mode)
167
+ self.publish_updates()
168
+
169
+ async def set_auto_regulated_humidity(self, value: int):
170
+ self.auto_regulated_humidity = value
171
+ await self.api.set_device_info(self.uuid, "autorh", "v", value)
172
+ self.publish_updates()
173
+
174
+ async def set_child_lock(self, child_lock: bool):
175
+ self.child_lock = child_lock
176
+ await self.api.set_device_info(self.uuid, "childlock", "vb", child_lock)
177
+ self.publish_updates()
178
+
179
+ async def set_night_mode(self, night_mode: bool):
180
+ self.night_mode = night_mode
181
+ await self.api.set_device_info(self.uuid, "nightmode", "vb", night_mode)
182
+ self.publish_updates()
183
+
184
+ async def set_wick_dry_mode(self, value: bool):
185
+ self.wick_dry_mode = value
186
+ await self.api.set_device_info(self.uuid, "wickdrys", "vb", value)
187
+ self.publish_updates()
188
+
189
+ async def set_germ_shield(self, value: bool):
190
+ self.germ_shield = value
191
+ await self.api.set_device_info(self.uuid, "germshield", "vb", value)
192
+ self.publish_updates()
193
+
194
+ async def set_main_mode(self, value: int):
195
+ self.main_mode = value
196
+ await self.api.set_device_info(self.uuid, "mainmode", "v", value)
197
+ self.publish_updates()
198
+
199
+ async def set_heat_temp(self, value: int):
200
+ self.heat_temp = value
201
+ await self.api.set_device_info(self.uuid, "heattemp", "v", value)
202
+ self.publish_updates()
203
+
204
+ async def set_heat_sub_mode(self, value: int):
205
+ self.heat_sub_mode = value
206
+ await self.api.set_device_info(self.uuid, "heatsubmode", "v", value)
207
+ self.publish_updates()
208
+
209
+ async def set_heat_fan_speed(self, value: int):
210
+ self.heat_fan_speed = value
211
+ await self.api.set_device_info(self.uuid, "heatfs", "v", value)
212
+ self.publish_updates()
213
+
214
+ async def set_cool_sub_mode(self, value: int):
215
+ self.cool_sub_mode = value
216
+ await self.api.set_device_info(self.uuid, "coolsubmode", "v", value)
217
+ self.publish_updates()
218
+
219
+ async def set_cool_fan_speed(self, value: int):
220
+ self.cool_fan_speed = value
221
+ await self.api.set_device_info(self.uuid, "coolfs", "v", value)
222
+ self.publish_updates()
223
+
224
+ async def set_ap_sub_mode(self, value: int):
225
+ self.ap_sub_mode = value
226
+ await self.api.set_device_info(self.uuid, "apsubmode", "v", value)
227
+ self.publish_updates()
228
+
229
+ async def set_fan_speed_0(self, value: int):
230
+ self.fan_speed_0 = value
231
+ await self.api.set_device_info(self.uuid, "fsp0", "v", value)
232
+ self.publish_updates()
233
+
234
+ @property
235
+ def model(self) -> ModelEnum:
236
+ if self.sku == "111633":
237
+ return ModelEnum.HUMIDIFIER_H35I
238
+ if self.sku == "105820":
239
+ return ModelEnum.PROTECT_7440I
240
+ if self.sku == "105826":
241
+ return ModelEnum.PROTECT_7470I
242
+ if self.sku == "110059":
243
+ return ModelEnum.MAX_211I
244
+ if self.sku == "110092":
245
+ return ModelEnum.MAX_311I
246
+ if self.sku == "110057":
247
+ return ModelEnum.MAX_411I
248
+ if self.sku == "112124":
249
+ return ModelEnum.T10I
250
+ return ModelEnum.UNKNOWN