summaryrefslogtreecommitdiff
path: root/include/py/homekit/modem/e3372.py
blob: 8164b8816f9157e77218525aa9ea8b5c2141f4a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import requests
import xml.etree.ElementTree as ElementTree

from datetime import datetime
from ..util import Addr
from enum import Enum
from ..http import HTTPMethod
from typing import Union


class Error(Enum):
    ERROR_SYSTEM_NO_SUPPORT = 100002
    ERROR_SYSTEM_NO_RIGHTS = 100003
    ERROR_SYSTEM_BUSY = 100004
    ERROR_LOGIN_USERNAME_WRONG = 108001
    ERROR_LOGIN_PASSWORD_WRONG = 108002
    ERROR_LOGIN_ALREADY_LOGIN = 108003
    ERROR_LOGIN_USERNAME_PWD_WRONG = 108006
    ERROR_LOGIN_USERNAME_PWD_ORERRUN = 108007
    ERROR_LOGIN_TOUCH_ALREADY_LOGIN = 108009
    ERROR_VOICE_BUSY = 120001
    ERROR_WRONG_TOKEN = 125001
    ERROR_WRONG_SESSION = 125002
    ERROR_WRONG_SESSION_TOKEN = 125003


class WifiStatus(Enum):
    WIFI_CONNECTING = '900'
    WIFI_CONNECTED = '901'
    WIFI_DISCONNECTED = '902'
    WIFI_DISCONNECTING = '903'


class Cradle(Enum):
    CRADLE_CONNECTING = '900'
    CRADLE_CONNECTED = '901'
    CRADLE_DISCONNECTED = '902'
    CRADLE_DISCONNECTING = '903'
    CRADLE_CONNECTFAILED = '904'
    CRADLE_CONNECTSTATUSNULL = '905'
    CRANDLE_CONNECTSTATUSERRO = '906'


class MacroEVDOLevel(Enum):
    MACRO_EVDO_LEVEL_ZERO = '0'
    MACRO_EVDO_LEVEL_ONE = '1'
    MACRO_EVDO_LEVEL_TWO = '2'
    MACRO_EVDO_LEVEL_THREE = '3'
    MACRO_EVDO_LEVEL_FOUR = '4'
    MACRO_EVDO_LEVEL_FIVE = '5'


class MacroNetWorkType(Enum):
    MACRO_NET_WORK_TYPE_NOSERVICE = 0
    MACRO_NET_WORK_TYPE_GSM = 1
    MACRO_NET_WORK_TYPE_GPRS = 2
    MACRO_NET_WORK_TYPE_EDGE = 3
    MACRO_NET_WORK_TYPE_WCDMA = 4
    MACRO_NET_WORK_TYPE_HSDPA = 5
    MACRO_NET_WORK_TYPE_HSUPA = 6
    MACRO_NET_WORK_TYPE_HSPA = 7
    MACRO_NET_WORK_TYPE_TDSCDMA = 8
    MACRO_NET_WORK_TYPE_HSPA_PLUS = 9
    MACRO_NET_WORK_TYPE_EVDO_REV_0 = 10
    MACRO_NET_WORK_TYPE_EVDO_REV_A = 11
    MACRO_NET_WORK_TYPE_EVDO_REV_B = 12
    MACRO_NET_WORK_TYPE_1xRTT = 13
    MACRO_NET_WORK_TYPE_UMB = 14
    MACRO_NET_WORK_TYPE_1xEVDV = 15
    MACRO_NET_WORK_TYPE_3xRTT = 16
    MACRO_NET_WORK_TYPE_HSPA_PLUS_64QAM = 17
    MACRO_NET_WORK_TYPE_HSPA_PLUS_MIMO = 18
    MACRO_NET_WORK_TYPE_LTE = 19
    MACRO_NET_WORK_TYPE_EX_NOSERVICE = 0
    MACRO_NET_WORK_TYPE_EX_GSM = 1
    MACRO_NET_WORK_TYPE_EX_GPRS = 2
    MACRO_NET_WORK_TYPE_EX_EDGE = 3
    MACRO_NET_WORK_TYPE_EX_IS95A = 21
    MACRO_NET_WORK_TYPE_EX_IS95B = 22
    MACRO_NET_WORK_TYPE_EX_CDMA_1x = 23
    MACRO_NET_WORK_TYPE_EX_EVDO_REV_0 = 24
    MACRO_NET_WORK_TYPE_EX_EVDO_REV_A = 25
    MACRO_NET_WORK_TYPE_EX_EVDO_REV_B = 26
    MACRO_NET_WORK_TYPE_EX_HYBRID_CDMA_1x = 27
    MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_0 = 28
    MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_A = 29
    MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_B = 30
    MACRO_NET_WORK_TYPE_EX_EHRPD_REL_0 = 31
    MACRO_NET_WORK_TYPE_EX_EHRPD_REL_A = 32
    MACRO_NET_WORK_TYPE_EX_EHRPD_REL_B = 33
    MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_0 = 34
    MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_A = 35
    MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_B = 36
    MACRO_NET_WORK_TYPE_EX_WCDMA = 41
    MACRO_NET_WORK_TYPE_EX_HSDPA = 42
    MACRO_NET_WORK_TYPE_EX_HSUPA = 43
    MACRO_NET_WORK_TYPE_EX_HSPA = 44
    MACRO_NET_WORK_TYPE_EX_HSPA_PLUS = 45
    MACRO_NET_WORK_TYPE_EX_DC_HSPA_PLUS = 46
    MACRO_NET_WORK_TYPE_EX_TD_SCDMA = 61
    MACRO_NET_WORK_TYPE_EX_TD_HSDPA = 62
    MACRO_NET_WORK_TYPE_EX_TD_HSUPA = 63
    MACRO_NET_WORK_TYPE_EX_TD_HSPA = 64
    MACRO_NET_WORK_TYPE_EX_TD_HSPA_PLUS = 65
    MACRO_NET_WORK_TYPE_EX_802_16E = 81
    MACRO_NET_WORK_TYPE_EX_LTE = 101


def post_data_to_xml(data: dict, depth: int = 1) -> str:
    if depth == 1:
        return '<?xml version: "1.0" encoding="UTF-8"?>'+post_data_to_xml({'request': data}, depth+1)

    items = []
    for k, v in data.items():
        if isinstance(v, dict):
            v = post_data_to_xml(v, depth+1)
        elif isinstance(v, list):
            raise TypeError('list type is unsupported here')
        items.append(f'<{k}>{v}</{k}>')

    return ''.join(items)


class E3372:
    _addr: Addr
    _need_auth: bool
    _legacy_token_auth: bool
    _get_raw_data: bool
    _headers: dict[str, str]
    _authorized: bool

    def __init__(self,
                 addr: Addr,
                 need_auth: bool = True,
                 legacy_token_auth: bool = False,
                 get_raw_data: bool = False):
        self._addr = addr
        self._need_auth = need_auth
        self._legacy_token_auth = legacy_token_auth
        self._get_raw_data = get_raw_data
        self._authorized = False
        self._headers = {}

    @property
    def device_information(self):
        self.auth()
        return self.request('device/information')

    @property
    def device_signal(self):
        self.auth()
        return self.request('device/signal')

    @property
    def monitoring_status(self):
        self.auth()
        return self.request('monitoring/status')

    @property
    def notifications(self):
        self.auth()
        return self.request('monitoring/check-notifications')

    @property
    def dialup_connection(self):
        self.auth()
        return self.request('dialup/connection')

    @property
    def traffic_stats(self):
        self.auth()
        return self.request('monitoring/traffic-statistics')

    @property
    def sms_count(self):
        self.auth()
        return self.request('sms/sms-count')

    def sms_send(self, phone: str, text: str):
        self.auth()
        return self.request('sms/send-sms', HTTPMethod.POST, {
            'Index': -1,
            'Phones': {
                'Phone': phone
            },
            'Sca': '',
            'Content': text,
            'Length': -1,
            'Reserved': 1,
            'Date': -1
        })

    def sms_list(self, page: int = 1, count: int = 20, outbox: bool = False):
        self.auth()
        xml = self.request('sms/sms-list', HTTPMethod.POST, {
            'PageIndex': page,
            'ReadCount': count,
            'BoxType': 1 if not outbox else 2,
            'SortType': 0,
            'Ascending': 0,
            'UnreadPreferred': 1 if not outbox else 0
        }, return_body=True)

        root = ElementTree.fromstring(xml)
        messages = []
        for message_elem in root.find('Messages').findall('Message'):
            message_dict = {child.tag: child.text for child in message_elem}
            message_dict['UnixTime'] = int(datetime.strptime(message_dict['Date'], '%Y-%m-%d %H:%M:%S').timestamp())
            messages.append(message_dict)
        return messages

    def auth(self):
        if self._authorized:
            return

        if not self._legacy_token_auth:
            data = self.request('webserver/SesTokInfo')
            self._headers = {
                'Cookie': data['SesInfo'],
                '__RequestVerificationToken': data['TokInfo'],
                'Content-Type': 'text/xml'
            }
        else:
            data = self.request('webserver/token')
            self._headers = {
                '__RequestVerificationToken': data['token'],
                'Content-Type': 'text/xml'
            }

        self._authorized = True

    def request(self,
                method: str,
                http_method: HTTPMethod = HTTPMethod.GET,
                data: dict = {},
                return_body: bool = False) -> Union[str, dict]:
        url = f'http://{self._addr}/api/{method}'
        if http_method == HTTPMethod.POST:
            data = post_data_to_xml(data)
            f = requests.post
        else:
            data = None
            f = requests.get
        r = f(url, data=data, headers=self._headers)
        r.raise_for_status()
        r.encoding = 'utf-8'

        if return_body:
            return r.text

        root = ElementTree.fromstring(r.text)
        data_dict = {}
        for elem in root:
            data_dict[elem.tag] = elem.text
        return data_dict