Skip to content

Commit 2bdb4e5

Browse files
committed
feat: add protect aws device support
1 parent 3ffc951 commit 2bdb4e5

File tree

8 files changed

+322
-102
lines changed

8 files changed

+322
-102
lines changed

src/blueair_api/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .errors import BaseError, RateError, AuthError
22
from .http_blueair import HttpBlueair
3-
from .util_bootstrap import get_devices
3+
from .http_aws_blueair import HttpAwsBlueair
4+
from .util_bootstrap import get_devices, get_aws_devices
45
from .device import Device

src/blueair_api/const.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,18 @@
1616
)
1717

1818
MeasurementList = List[Mapping[str, Union[int, float]]]
19+
20+
AWS_APIKEYS = {
21+
'us': {
22+
'gigyaRegion': 'us1',
23+
'restApiId': 'on1keymlmh',
24+
'awsRegion': 'us-east-2',
25+
'apiKey': '3_-xUbbrIY8QCbHDWQs1tLXE-CZBQ50SGElcOY5hF1euE11wCoIlNbjMGAFQ6UwhMY',
26+
},
27+
'eu': {
28+
'gigyaRegion': 'eu1',
29+
'restApiId': 'hkgmr8v960',
30+
'awsRegion': 'eu-west-1',
31+
'apiKey': '3_qRseYzrUJl1VyxvSJANalu_kNgQ83swB1B9uzgms58--5w1ClVNmrFdsDnWVQQCl',
32+
},
33+
}

src/blueair_api/device_aws.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import logging
2+
3+
from .callbacks import CallbacksMixin
4+
from .http_aws_blueair import HttpAwsBlueair
5+
from .util import convert_api_array_to_dict
6+
7+
_LOGGER = logging.getLogger(__name__)
8+
9+
10+
class DeviceAws(CallbacksMixin):
11+
uuid: str = None
12+
name: str = None
13+
name_api: str = None
14+
mac: str = None
15+
firmware: str = None
16+
mcu_firmware: str = None
17+
serial_number: str = None
18+
19+
brightness: int = None
20+
child_lock: int = None
21+
fan_speed: int = None
22+
fan_mode: str = None
23+
filter_expired: bool = None
24+
running: bool = None
25+
night_mode: bool = None
26+
germ_shield: bool = None
27+
28+
pm1: int = None
29+
pm2_5: int = None
30+
pm10: int = None
31+
tVOC: int = None
32+
temperature: int = None
33+
humidity: int = None
34+
35+
def __init__(
36+
self,
37+
api: HttpAwsBlueair,
38+
uuid: str = None,
39+
name_api: str = None,
40+
mac: str = None,
41+
):
42+
self.api = api
43+
self.uuid = uuid
44+
self.name_api = name_api
45+
self.mac = mac
46+
_LOGGER.debug(f"creating blueair device: {self.uuid}")
47+
48+
async def refresh(self):
49+
info = await self.api.device_info(self.name_api, self.uuid)
50+
sensor_data = convert_api_array_to_dict(info["sensordata"])
51+
self.pm1 = int(sensor_data["pm1"])
52+
self.pm2_5 = int(sensor_data["pm2_5"])
53+
self.pm10 = int(sensor_data["pm10"])
54+
self.tVOC = int(sensor_data["tVOC"])
55+
self.temperature = int(sensor_data["t"])
56+
self.humidity = int(sensor_data["h"])
57+
58+
configuration = info["configuration"]
59+
self.name = configuration["di"]["name"]
60+
self.firmware = configuration["di"]["cfv"]
61+
self.mcu_firmware = configuration["di"]["mfv"]
62+
self.serial_number = configuration["di"]["ds"]
63+
64+
states = convert_api_array_to_dict(info["states"])
65+
self.running = states["standby"] is False
66+
self.night_mode = states["nightmode"]
67+
self.germ_shield = states["germshield"]
68+
self.brightness = int(states["brightness"])
69+
self.child_lock = states["childlock"]
70+
self.fan_speed = int(states["fanspeed"])
71+
self.fan_mode = states["automode"]
72+
self.filter_expired = states["filterusage"] >= 95
73+
74+
self.publish_updates()
75+
76+
async def set_fan_speed(self, new_speed):
77+
await self.api.set_fan_speed(self.uuid, new_speed)
78+
79+
def __repr__(self):
80+
return {
81+
"uuid": self.uuid,
82+
"name": self.name,
83+
"name_api": self.name_api,
84+
"mac": self.mac,
85+
"firmware": self.firmware,
86+
"mcu_firmware": self.mcu_firmware,
87+
"serial_number": self.serial_number,
88+
"brightness": self.brightness,
89+
"child_lock": self.child_lock,
90+
"fan_speed": self.fan_speed,
91+
"filter_expired": self.filter_expired,
92+
"fan_mode": self.fan_mode,
93+
"running": self.running,
94+
"night_mode": self.night_mode,
95+
"germ_shield": self.germ_shield,
96+
"pm1": self.pm1,
97+
"pm2_5": self.pm2_5,
98+
"pm10": self.pm10,
99+
"tVOC": self.tVOC,
100+
"temperature": self.temperature,
101+
"humidity": self.humidity,
102+
}
103+
104+
def __str__(self):
105+
return f"{self.__repr__()}"

src/blueair_api/http_aws_blueair.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import logging
2+
3+
from aiohttp import ClientSession, ClientResponse, FormData
4+
from .const import AWS_APIKEYS
5+
6+
from .util_http import request_with_logging
7+
8+
_LOGGER = logging.getLogger(__name__)
9+
10+
11+
class HttpAwsBlueair:
12+
def __init__(
13+
self,
14+
username: str,
15+
password: str,
16+
region: str = "us",
17+
client_session: ClientSession = None,
18+
):
19+
self.username = username
20+
self.password = password
21+
self.region = region
22+
23+
self.session_token = None
24+
self.session_secret = None
25+
26+
self.access_token = None
27+
28+
self.jwt = None
29+
30+
if client_session is None:
31+
self.api_session = ClientSession(raise_for_status=False)
32+
else:
33+
self.api_session = client_session
34+
35+
async def cleanup_client_session(self):
36+
await self.api_session.close()
37+
38+
@request_with_logging
39+
async def _get_request_with_logging_and_errors_raised(
40+
self, url: str, headers: dict = None
41+
) -> ClientResponse:
42+
return await self.api_session.get(url=url, headers=headers)
43+
44+
@request_with_logging
45+
async def _post_request_with_logging_and_errors_raised(
46+
self, url: str, json_body: dict = None, form_data: FormData = None, headers: dict = None
47+
) -> ClientResponse:
48+
return await self.api_session.post(url=url, data=form_data, json=json_body, headers=headers)
49+
50+
async def refresh_session(self):
51+
url = f"https://accounts.{AWS_APIKEYS[self.region]['gigyaRegion']}.gigya.com/accounts.login"
52+
form_data = FormData()
53+
form_data.add_field("apikey", AWS_APIKEYS[self.region]['apiKey'])
54+
form_data.add_field("loginID", self.username)
55+
form_data.add_field("password", self.password)
56+
form_data.add_field("targetEnv", 'mobile')
57+
response: ClientResponse = (
58+
await self._post_request_with_logging_and_errors_raised(
59+
url=url, form_data=form_data
60+
)
61+
)
62+
response_json = await response.json(content_type="text/javascript")
63+
self.session_token = response_json["sessionInfo"]["sessionToken"]
64+
self.session_secret = response_json["sessionInfo"]["sessionSecret"]
65+
66+
async def refresh_jwt(self):
67+
if self.session_token is None or self.session_secret is None:
68+
await self.refresh_session()
69+
url = f"https://accounts.{AWS_APIKEYS[self.region]['gigyaRegion']}.gigya.com/accounts.getJWT";
70+
form_data = FormData()
71+
form_data.add_field("oauth_token", self.session_token)
72+
form_data.add_field("secret", self.session_secret)
73+
form_data.add_field("targetEnv", 'mobile')
74+
response: ClientResponse = (
75+
await self._post_request_with_logging_and_errors_raised(
76+
url=url, form_data=form_data
77+
)
78+
)
79+
response_json = await response.json(content_type="text/javascript")
80+
self.jwt = response_json["id_token"]
81+
82+
async def refresh_access_token(self):
83+
if self.jwt is None:
84+
await self.refresh_jwt()
85+
url = f"https://{AWS_APIKEYS[self.region]['restApiId']}.execute-api.{AWS_APIKEYS[self.region]['awsRegion']}.amazonaws.com/prod/c/login"
86+
headers = {
87+
"idtoken": self.jwt
88+
}
89+
response: ClientResponse = (
90+
await self._post_request_with_logging_and_errors_raised(
91+
url=url, headers=headers
92+
)
93+
)
94+
response_json = await response.json()
95+
self.access_token = response_json['access_token']
96+
97+
async def get_access_token(self):
98+
if self.access_token is None:
99+
await self.refresh_access_token()
100+
return self.access_token
101+
102+
async def devices(self):
103+
url = f"https://{AWS_APIKEYS[self.region]['restApiId']}.execute-api.{AWS_APIKEYS[self.region]['awsRegion']}.amazonaws.com/prod/c/registered-devices"
104+
headers = {
105+
'Authorization': f"Bearer {await self.get_access_token()}",
106+
}
107+
response: ClientResponse = (
108+
await self._get_request_with_logging_and_errors_raised(
109+
url=url, headers=headers
110+
)
111+
)
112+
response_json = await response.json()
113+
return response_json["devices"]
114+
115+
async def device_info(self, device_name, device_uuid):
116+
"""
117+
sample; [{'id': '1b41a7c6-8f02-42fa-9af8-868dad9be98a', 'configuration': {'df': {'a': 877, 'ot': 'G4Filter', 'alg': 'FilterAlg1'}, 'di': {'cfv': '2.1.1', 'cma': '3c:61:05:45:56:98', 'mt': '2', 'name': 'sage', 'sku': '105826', 'mfv': '1.0.12', 'ofv': '2.1.1', 'hw': 'high_1.5', 'ds': '110582600000110110016855'}, '_ot': 'CmConfig', '_f': False, '_it': 'urn:blueair:openapi:version:healthprotect:0.0.5', '_eid': 'f8cb142f-c77d-11eb-b045-c98ff4f5a769', '_sc': 'Instance', 'ds': {'tVOC': {'tf': 'senml+json', 'ot': 'TVOC', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/tVOC', 'ttl': 0, 'n': 'tVOC', 'fe': True}, 'fu0': {'tf': 'senml+json', 'ot': 'FU', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/fu0', 'ttl': -1, 'n': 'fu0', 'fe': True}, 'ledb': {'tf': 'senml+json', 'ot': 'Led', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/ledb', 'ttl': 0, 'n': 'ledb', 'fe': True}, 'co2': {'tf': 'senml+json', 'ot': 'Co2', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/co2', 'ttl': 0, 'n': 'co2', 'fe': True}, 'pm2_5c': {'tf': 'senml+json', 'ot': 'PMParticleCount', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm2_5c', 'ttl': 0, 'n': 'pm2_5c', 'fe': True}, 'sb': {'tf': 'senml+json', 'ot': 'Standby', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/sb', 'ttl': -1, 'n': 'sb', 'fe': True}, 'ss0': {'tf': 'senml+json', 'ot': 'SafetySwitch', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/ss0', 'ttl': -1, 'n': 'ss0', 'fe': True}, 'rt1s': {'tf': 'senml+json', 'ot': 'RT1s', 'e': True, 'i': 1000, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/1s', 'sn': ['pm1', 'pm2_5', 'pm10', 't', 'h', 'tVOC', 'rssi'], 'ttl': 0, 'n': 'rt1s', 'fe': True}, 'rt5s': {'tf': 'senml+json', 'ot': 'RT5s', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/5s', 'sn': ['pm1', 'pm2_5', 'pm10', 't', 'h', 'tVOC', 'rssi'], 'ttl': -1, 'n': 'rt5s', 'fe': True}, 'pm1': {'tf': 'senml+json', 'ot': 'PM1', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm1', 'ttl': 0, 'n': 'pm1', 'fe': True}, 'pm2_5': {'tf': 'senml+json', 'ot': 'PM2_5', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm2_5', 'ttl': 0, 'n': 'pm2_5', 'fe': True}, 'pm10c': {'tf': 'senml+json', 'ot': 'PMParticleCount', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm10c', 'ttl': 0, 'n': 'pm10c', 'fe': True}, 'sp': {'tf': 'senml+json', 'ot': 'SensorBoxPresense', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/sp', 'ttl': -1, 'n': 'sp', 'fe': True}, 'rssi': {'tf': 'senml+json', 'ot': 'RSSI', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/rssi', 'ttl': 0, 'n': 'rssi', 'fe': True}, 'chl': {'tf': 'senml+json', 'ot': 'ChildLock', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/chl', 'ttl': -1, 'n': 'chl', 'fe': True}, 'fp0': {'tf': 'senml+json', 'ot': 'FilterPresence', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/fp0', 'ttl': -1, 'n': 'fp0', 'fe': True}, 'pm10': {'tf': 'senml+json', 'ot': 'PM10', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm10', 'ttl': 0, 'n': 'pm10', 'fe': True}, 'h': {'tf': 'senml+json', 'ot': 'Humidity', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/h', 'ttl': 0, 'n': 'h', 'fe': True}, 'is': {'tf': 'senml+json', 'ot': 'IonizerState', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/is', 'ttl': -1, 'n': 'is', 'fe': True}, 'gs': {'tf': 'senml+json', 'ot': 'GermShield', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/gs', 'ttl': -1, 'n': 'gs', 'fe': True}, 'am': {'tf': 'senml+json', 'ot': 'AutoMode', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/am', 'ttl': -1, 'n': 'am', 'fe': True}, 'tVOCbaseline': {'t': 0, 'e': False, 'ot': 'TVOCbaseline', 'i': 60000, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/TVOCbaseline', 'ttl': 600, 'n': 'tVOCbaseline', 'fe': True}, 'rt5m': {'tf': 'senml+json', 'ot': 'RT5m', 'e': True, 'i': 300000, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/5m', 'sn': ['pm1', 'pm2_5', 'pm10', 't', 'h', 'tVOC'], 'ttl': 0, 'n': 'rt5m', 'fe': True}, 't': {'tf': 'senml+json', 'ot': 'Temperature', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/t', 'ttl': 0, 'n': 't', 'fe': True}, 'pm1c': {'tf': 'senml+json', 'ot': 'PMParticleCount', 'e': False, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/pm1c', 'ttl': 0, 'n': 'pm1c', 'fe': True}, 'b5m': {'tf': 'senml+json', 'th': ['5kb', '4h'], 'ot': 'Batch5m', 'e': True, 'i': 300000, 'tn': '$aws/rules/telemetry_ingest_rule/d/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/batch/b5m', 'sn': ['pm1', 'pm2_5', 'pm10', 't', 'h', 'tVOC', 'fsp0'], 'ttl': -1, 'n': 'b5m', 'fe': True}, 'fsp0': {'st': 'fsp0', 'tf': 'senml+json', 't': 10, 'ot': 'Fanspeed', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/fsp0', 'sn': ['fsp0'], 'ttl': -1, 'n': 'fsp0', 'fe': True}, 'nm': {'tf': 'senml+json', 'ot': 'NightMode', 'e': True, 'i': 0, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/s/nm', 'ttl': -1, 'n': 'nm', 'fe': True}}, '_r': 'us-east-2', '_s': {'sig': 'f812cd40acae289ad965c159051abadb28ed6c8b1eb2df917fedaa135f5a69d2', 'salg': 'SHA256'}, '_t': 'Diff', '_v': 1655924801, '_cas': 1623062101, '_id': '1b41a7c6-8f02-42fa-9af8-868dad9be98a', 'fc': {'pwd': '9102revreSyrotcaF', 'ssid': 'BAFactory', 'url': ' '}, 'da': {'reboot': {'p': False, 'a': False, 'ot': 'Reboot', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/reboot', 'n': 'reboot', 'fe': True}, 'uitest': {'a': 'off', 'ot': 'UiTest', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/uitest', 'n': 'uitest', 'fe': True}, 'ledb': {'p': True, 'a': 0, 'tf': 'senml+json', 'ot': 'LedBrightness', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/ledb', 'n': 'ledb', 'fe': True}, 'chl': {'p': True, 'a': False, 'tf': 'senml+json', 'ot': 'ChildLock', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/chl', 'n': 'chl', 'fe': True}, 'sflu': {'p': False, 'a': False, 'ot': 'SensorFlush', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/sflu', 'n': 'sflu', 'fe': True}, 'buttontest': {'p': False, 'a': False, 'ot': 'ButtonTest', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/buttontest', 'n': 'buttontest', 'fe': True}, 'is': {'p': True, 'a': False, 'tf': 'senml+json', 'ot': 'IonizerState', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/is', 'n': 'is', 'fe': True}, 'gs': {'p': True, 'a': False, 'tf': 'senml+json', 'ot': 'GermShield', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/gs', 'n': 'gs', 'fe': True}, 'am': {'p': True, 'a': False, 'tf': 'senml+json', 'ot': 'AutoMode', 'e': True, 'amt': 'PM', 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/am', 'n': 'am', 'fe': True}, 'tVOCbaseline': {'a': 0, 'ot': 'TVOCbaseline', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/tVOCbaseline', 'n': 'tVOCbaseline', 'fe': True}, 'sb': {'p': True, 'a': False, 'tf': 'senml+json', 'sbt': 'sensor_on', 'ot': 'Standby', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/sb', 'n': 'sb', 'fe': True}, 'fsp0': {'p': True, 'st': 'fsp0', 'a': 20, 'tf': 'senml+json', 'ot': 'Fanspeed', 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/fsp0', 'n': 'fsp0', 'fe': True}, 'nm': {'p': True, 'maxfsp': 25, 'a': False, 'tf': 'senml+json', 'nmt': 'PM', 'ot': 'NightMode', 'ledb': 12, 'e': True, 'tn': 'd/1b41a7c6-8f02-42fa-9af8-868dad9be98a/a/nm', 'n': 'nm', 'fe': True}}, 'dc': {'cfv': {'d': 'di.cfv', 't': 'integer', 'v': 0, 'n': 'cfv'}, 'germshield': {'a': 'gs', 's': 'gs', 't': 'boolean', 'v': False, 'n': 'germshield'}, 'filterusage': {'s': 'fu0', 't': 'integer', 'v': 0, 'n': 'filterusage'}, 'brightness': {'a': 'ledb', 's': 'ledb', 't': 'integer', 'v': 100, 'n': 'brightness'}, 'standby': {'a': 'sb', 's': 'sb', 't': 'boolean', 'v': False, 'n': 'standby'}, 'fanspeed': {'a': 'fsp0', 's': 'fsp0', 't': 'integer', 'v': 11, 'n': 'fanspeed'}, 'nightmode': {'a': 'nm', 's': 'nm', 't': 'boolean', 'v': False, 'n': 'nightmode'}, 'childlock': {'a': 'chl', 's': 'chl', 't': 'boolean', 'v': False, 'n': 'childlock'}, 'safetyswitch': {'s': 'ss0', 't': 'boolean', 'v': True, 'n': 'safetyswitch'}, 'mfv': {'d': 'di.mfv', 't': 'integer', 'v': 0, 'n': 'mfv'}, 'ofv': {'d': 'di.ofv', 't': 'integer', 'v': 0, 'n': 'ofv'}, 'automode': {'a': 'am', 's': 'am', 't': 'boolean', 'v': False, 'n': 'automode'}}}, 'alarms': [], 'events': [], 'sensordata': [{'v': '0', 'n': 'pm1', 't': 1656198091}, {'v': '0', 'n': 'pm2_5', 't': 1656198091}, {'v': '0', 'n': 'pm10', 't': 1656198091}, {'v': '23', 'n': 't', 't': 1656198091}, {'v': '45', 'n': 'h', 't': 1656198091}, {'v': '170', 'n': 'tVOC', 't': 1656198091}, {'v': '11', 'n': 'fsp0', 't': 1656198091}], 'states': [{'n': 'cfv', 'v': 33554689, 't': 1656203510}, {'n': 'germshield', 'vb': True, 't': 1656203510}, {'n': 'filterusage', 'v': 1, 't': 1656203510}, {'n': 'brightness', 'v': 0, 't': 1656203510}, {'n': 'standby', 'vb': False, 't': 1656203510}, {'n': 'fanspeed', 'v': 11, 't': 1656203510}, {'n': 'nightmode', 'vb': False, 't': 1656203510}, {'n': 'childlock', 'vb': False, 't': 1656203510}, {'n': 'safetyswitch', 'vb': True, 't': 1656203510}, {'n': 'mfv', 'v': 16777228, 't': 1656203510}, {'n': 'ofv', 'v': 33554689, 't': 1656203510}, {'n': 'automode', 'vb': False, 't': 1656203510}, {'t': 1656203510, 'vb': True, 'n': 'online'}], 'welcomehome': {'setting': []}, 'fleet_info': []}]
118+
119+
:param device_name:
120+
:param device_uuid:
121+
:return:
122+
"""
123+
url = f"https://{AWS_APIKEYS[self.region]['restApiId']}.execute-api.{AWS_APIKEYS[self.region]['awsRegion']}.amazonaws.com/prod/c/{device_name}/r/initial"
124+
headers = {
125+
'Authorization': f"Bearer {await self.get_access_token()}",
126+
}
127+
json_body = {
128+
"deviceconfigquery": [
129+
{
130+
"id": device_uuid,
131+
"r": {
132+
"r": [
133+
"sensors",
134+
],
135+
},
136+
},
137+
],
138+
"includestates": True,
139+
"eventsubscription": {
140+
"include": [
141+
{
142+
"filter": {
143+
"o": f"= {device_uuid}",
144+
},
145+
},
146+
],
147+
},
148+
}
149+
response: ClientResponse = (
150+
await self._post_request_with_logging_and_errors_raised(
151+
url=url, headers=headers, json_body=json_body
152+
)
153+
)
154+
response_json = await response.json()
155+
return response_json["deviceInfo"][0]

src/blueair_api/stub.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
path_root = Path(__file__).parents[2]
1313
sys.path.append(str(path_root))
14-
from src.blueair_api import get_devices
14+
from src.blueair_api import get_devices, get_aws_devices
1515

1616

1717
logger = logging.getLogger("src.blueair_api")
@@ -26,6 +26,13 @@
2626
async def testing():
2727
username = input("Username: ")
2828
password = getpass()
29+
try:
30+
api, devices = await get_aws_devices(username=username, password=password)
31+
await devices[0].refresh()
32+
logger.debug(devices[0])
33+
finally:
34+
if api:
35+
await api.cleanup_client_session()
2936
try:
3037
api, devices = await get_devices(username=username, password=password)
3138
for device in devices:

0 commit comments

Comments
 (0)