Skip to content

Commit

Permalink
Merge pull request #10 from gentslava/feature/android_useragent
Browse files Browse the repository at this point in the history
Feature - Android API
  • Loading branch information
gentslava authored Nov 14, 2024
2 parents 6f2f82b + 278003c commit 680132d
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 98 deletions.
28 changes: 14 additions & 14 deletions custom_components/elektronny_gorod/api.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
from aiohttp import ClientSession, ClientError
import json
from .helpers import (
is_json,
generate_user_agent
)
from .helpers import is_json
from .const import (
LOGGER,
BASE_API_URL,
)

from .user_agent import UserAgent

class ElektronnyGorodAPI:
def __init__(
self,
user_agent: UserAgent,
access_token: str | None = None,
refresh_token: str | None = None,
headers: dict = {}
headers: dict = {},
) -> None:
self.base_url: str = f"https://{BASE_API_URL}"
self.user_agent: UserAgent = user_agent
self.headers: dict = {
**{
"Host": BASE_API_URL,
"User-Agent": generate_user_agent(),
"Content-Type": "application/json; charset=UTF-8",
"Authorization": "",
"Accept": "*/*",
"Accept-Language": "ru"
"Connection": "Keep-Alive",
"Accept-Encoding": "gzip",
},
**headers
}
Expand All @@ -49,22 +45,23 @@ async def request_sms_code(self, contract: dict):
"accountId": contract["accountId"],
"address": contract["address"],
"operatorId": contract["operatorId"],
"subscriberId": contract["subscriberId"],
"subscriberId": str(contract["subscriberId"]),
"placeId": contract["placeId"],
}
)
return await self.request(api_url, data, method="POST")

async def verify_sms_code(self, contract: dict, code: str) -> dict:
"""Verify the SMS code."""
api_url = f"{self.base_url}/auth/v2/auth/{self.phone}/confirmation"
api_url = f"{self.base_url}/auth/v3/auth/{self.phone}/confirmation"
data = json.dumps(
{
"accountId": contract["accountId"],
"confirm1": code,
"confirm2": code,
"login": self.phone,
"operatorId": contract["operatorId"],
"subscriberId": contract["subscriberId"],
"subscriberId": str(contract["subscriberId"]),
}
)
return await self.request(api_url, data, method="POST")
Expand Down Expand Up @@ -124,6 +121,9 @@ async def request(
):
"""Make a HTTP request."""
if self.access_token is not None: self.headers["Authorization"] = f"Bearer {self.access_token}"
self.headers["User-Agent"] = str(self.user_agent)
if method == "POST":
self.headers["Content-Type"] = "application/json; charset=UTF-8"

async with ClientSession() as session:
LOGGER.info("Sending API request to %s with headers=%s and data=%s", url, self.headers, data)
Expand Down
37 changes: 23 additions & 14 deletions custom_components/elektronny_gorod/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@
)
from homeassistant.const import CONF_NAME
from .const import (
DOMAIN,
LOGGER,
CONF_ACCESS_TOKEN,
CONF_REFRESH_TOKEN,
CONF_PHONE,
CONF_CONTRACT,
CONF_SMS,
CONF_OPERATOR_ID,
CONF_ACCOUNT_ID,
CONF_SUBSCRIBER_ID
DOMAIN,
LOGGER,
CONF_ACCESS_TOKEN,
CONF_REFRESH_TOKEN,
CONF_PHONE,
CONF_CONTRACT,
CONF_SMS,
CONF_OPERATOR_ID,
CONF_ACCOUNT_ID,
CONF_SUBSCRIBER_ID,
USER_AGENT
)
from .api import ElektronnyGorodAPI
from .helpers import find
from .user_agent import UserAgent

class ElektronnyGorodConfigFlow(ConfigFlow, domain=DOMAIN):
"""Elektronny Gorod config flow."""
Expand All @@ -27,11 +29,12 @@ class ElektronnyGorodConfigFlow(ConfigFlow, domain=DOMAIN):

def __init__(self) -> None:
"""Initialize."""
self.api: ElektronnyGorodAPI = ElektronnyGorodAPI()
self.user_agent = UserAgent()
self.api: ElektronnyGorodAPI = ElektronnyGorodAPI(user_agent = self.user_agent)
self.entry: ConfigEntry | None = None
self.access_token: str | None = None
self.refresh_token: str | None = None
self.operator_id: int = 1
self.operator_id: str | int = "null"
self.phone: str | None = None
self.contract: object | None = None
self.contracts: list | None = None
Expand Down Expand Up @@ -91,7 +94,7 @@ async def async_step_contract(

# Prepare contract choices for user
contract_choices = {
str(contract["subscriberId"]): f"{contract['address']} (Account ID: {contract['accountId']})"
str(contract["subscriberId"]): f"{contract["address"]} (Account ID: {contract["accountId"]})"
for contract in self.contracts
}

Expand Down Expand Up @@ -131,6 +134,10 @@ async def async_step_sms(
self.refresh_token = auth["refreshToken"]
self.operator_id = auth["operatorId"]

self.user_agent.place_id = self.contract["placeId"]
self.user_agent.account_id = self.contract["accountId"]
self.user_agent.operator_id = self.contract["operatorId"]

# Verify the SMS code
if self.access_token:
# If code is verified, create config entry
Expand All @@ -142,6 +149,7 @@ async def async_step_sms(
CONF_ACCESS_TOKEN: self.access_token,
CONF_REFRESH_TOKEN: self.refresh_token,
CONF_OPERATOR_ID: self.operator_id,
USER_AGENT: self.user_agent,
}

for entry in self._async_current_entries():
Expand Down Expand Up @@ -173,8 +181,9 @@ async def get_account(self) -> dict:
await self.api.update_access_token(self.access_token)
profile = await self.api.query_profile()
subscriber = profile["subscriber"]
self.user_agent.account_id = subscriber["accountId"]
return {
CONF_NAME: f"{subscriber['name']} ({subscriber['accountId']})",
CONF_NAME: f"{subscriber["name"]} ({subscriber["accountId"]})",
CONF_ACCOUNT_ID: subscriber["accountId"],
CONF_SUBSCRIBER_ID: subscriber["id"]
}
95 changes: 50 additions & 45 deletions custom_components/elektronny_gorod/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Final

DOMAIN = "elektronny_gorod"
BASE_API_URL: Final = "api-mh.ertelecom.ru"
BASE_API_URL: Final = "myhome.proptech.ru"
LOGGER = logging.getLogger(__name__)

CONF_ACCESS_TOKEN: Final = "access_token"
Expand All @@ -14,52 +14,57 @@
CONF_OPERATOR_ID: Final = "operator_id"
CONF_ACCOUNT_ID: Final = "account_id"
CONF_SUBSCRIBER_ID: Final = "subscriber_id"
USER_AGENT: Final = "user_agent"

CONF_WIDTH: Final = 300
CONF_HEIGHT: Final = 300

APP_VERSION: Final = "6.16.5 (build 1)"
iOS_11: Final = ["iOS 11.0", "iOS 11.0.1", "iOS 11.0.2", "iOS 11.0.3", "iOS 11.1", "iOS 11.1.1", "iOS 11.1.2", "iOS 11.2", "iOS 11.2.1", "iOS 11.2.2", "iOS 11.2.5", "iOS 11.2.6", "iOS 11.3", "iOS 11.3.1", "iOS 11.4", "iOS 11.4.1"]
iOS_12: Final = ["iOS 12.0", "iOS 12.0.1", "iOS 12.1", "iOS 12.1.1", "iOS 12.1.2", "iOS 12.1.3", "iOS 12.1.4", "iOS 12.2", "iOS 12.3", "iOS 12.3.1", "iOS 12.3.2", "iOS 12.4", "iOS 12.4.1"]
iOS_13: Final = ["iOS 13.0", "iOS 13.1", "iOS 13.1.1", "iOS 13.1.2", "iOS 13.1.3", "iOS 13.2", "iOS 13.2.2", "iOS 13.2.3", "iOS 13.3", "iOS 13.3.1", "iOS 13.4", "iOS 13.4.1", "iOS 13.5", "iOS 13.5.1", "iOS 13.6", "iOS 13.6.1", "iOS 13.6.1", "iOS 13.7"]
iOS_14: Final = ["iOS 14.0", "iOS 14.0.1", "iOS 14.1", "iOS 14.2", "iOS 14.2.1", "iOS 14.3", "iOS 14.4", "iOS 14.4.1", "iOS 14.4.2", "iOS 14.5", "iOS 14.5.1", "iOS 14.6", "iOS 14.7", "iOS 14.7.1", "iOS 14.8", "iOS 14.8.1"]
iOS_15: Final = ["iOS 15.0", "iOS 15.0.1", "15.0.2", "iOS 15.1", "iOS 15.2", "iOS 15.2.1", "iOS 15.3", "iOS 15.3.1", "iOS 15.4", "iOS 15.4.1", "iOS 15.5", "iOS 15.6", "iOS 15.6.1", "iOS 15.7", "iOS 15.7.1", "iOS 15.7.2", "iOS 15.7.3", "iOS 15.7.4", "iOS 15.7.5", "iOS 15.7.6", "iOS 15.7.7", "iOS 15.7.8", "iOS 15.7.9", "iOS 15.8"]
iOS_16: Final = ["iOS 16.0", "iOS 16.0.1", "iOS 16.0.2", "iOS 16.0.3", "iOS 16.1", "iOS 16.1.1", "iOS 16.2", "iOS 16.3", "iOS 16.3.1", "iOS 16.4", "iOS 16.4.1", "iOS 16.5", "iOS 16.6", "iOS 16.6.1", "iOS 16.7", "iOS 16.7.1", "iOS 16.7.2", "iOS 16.7.3", "iOS 16.7.4", "iOS 16.7.5", "iOS 16.7.6", "iOS 16.7.7"]
iOS_17: Final = ["iOS 17.0", "iOS 17.0.1", "iOS 17.0.2", "iOS 17.0.3", "iOS 17.1", "iOS 17.1.1", "iOS 17.1.2", "iOS 17.2", "iOS 17.2.1", "iOS 17.3", "iOS 17.3.1", "iOS 17.4", "iOS 17.4.1"]
iPHONE_iOS_CODES: Final = [
{
"name": "iPhone 8/X",
"code": ["iPhone10,1", "iPhone10,2", "iPhone10,3", "iPhone10,4", "iPhone10,5", "iPhone10,6"],
"os": [*iOS_11, *iOS_12, *iOS_13, *iOS_14, *iOS_15, *iOS_16]
},
{
"name": "iPhone XS/XR",
"code": ["iPhone11,2", "iPhone11,4", "iPhone11,6", "iPhone11,8"],
"os": [*iOS_12, *iOS_13, *iOS_14, *iOS_15, *iOS_16, *iOS_17]
},
{
"name": "iPhone 11/SE 2",
"code": ["iPhone12,1", "iPhone12,3", "iPhone12,5", "iPhone12,8"],
"os": [*iOS_13, *iOS_14, *iOS_15, *iOS_16, *iOS_17]
},
{
"name": "iPhone 12",
"code": ["iPhone13,1", "iPhone13,2", "iPhone13,3", "iPhone13,4"],
"os": [*iOS_14, *iOS_15, *iOS_16, *iOS_17]
},
{
"name": "iPhone 13/SE 3",
"code": ["iPhone14,2", "iPhone14,3", "iPhone14,4", "iPhone14,5", "iPhone14,6"],
"os": [*iOS_15, *iOS_16, *iOS_17]
},
{
"name": "iPhone 14",
"code": ["iPhone14,7", "iPhone14,8", "iPhone15,2", "iPhone15,3"],
"os": [*iOS_16, *iOS_17]
},
{
"name": "iPhone 15",
"code": ["iPhone15,4", "iPhone15,5", "iPhone16,1", "iPhone16,2"],
"os": [*iOS_17]
},
APP_VERSION: Final = {
"name": "8.13.0",
"code": "81300000"
}

ANDROID_OS_VER: Final = "14"
ANDROID_DEVICES_CSV: Final = "https://storage.googleapis.com/play_public/supported_devices.csv"
ANDROID_DEVICES: Final = [
{
"manufacturer": "Google",
"model": "Pixel 5a",
},
{
"manufacturer": "Google",
"model": "Pixel 6",
},
{
"manufacturer": "Google",
"model": "Pixel 6 Pro",
},
{
"manufacturer": "Google",
"model": "Pixel 6a",
},
{
"manufacturer": "Google",
"model": "Pixel 7",
},
{
"manufacturer": "Google",
"model": "Pixel 7 Pro",
},
{
"manufacturer": "Google",
"model": "Pixel 7a",
},
{
"manufacturer": "Google",
"model": "Pixel 8",
},
{
"manufacturer": "Google",
"model": "Pixel 8 Pro",
},
{
"manufacturer": "Google",
"model": "Pixel 8a",
},
]
17 changes: 10 additions & 7 deletions custom_components/elektronny_gorod/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
DOMAIN,
LOGGER,
CONF_ACCESS_TOKEN,
CONF_REFRESH_TOKEN,
CONF_OPERATOR_ID,
DOMAIN,
LOGGER,
CONF_ACCESS_TOKEN,
CONF_REFRESH_TOKEN,
CONF_OPERATOR_ID,
USER_AGENT,
)
from .api import ElektronnyGorodAPI
from .helpers import find
Expand All @@ -26,12 +27,14 @@ def __init__(
"""Initialize global Elektronny Gorod data updater."""
self.access_token = entry.data[CONF_ACCESS_TOKEN]
self.refresh_token = entry.data[CONF_REFRESH_TOKEN]
self.operatorId = entry.data[CONF_OPERATOR_ID]
self.operator_id = entry.data[CONF_OPERATOR_ID]
self.user_agent = entry.data[USER_AGENT]
self.api = ElektronnyGorodAPI(
user_agent = self.user_agent,
access_token = self.access_token,
refresh_token = self.refresh_token,
headers = {
"Operator": str(self.operatorId),
"Operator": str(self.operator_id),
"Content-Type": "application/json"
}
)
Expand Down
18 changes: 0 additions & 18 deletions custom_components/elektronny_gorod/helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import json
import uuid
from random import choice
from collections.abc import Callable
from .const import APP_VERSION, iPHONE_iOS_CODES

def is_json(value: str) -> bool:
try:
Expand All @@ -19,18 +16,3 @@ def find(items: list, condition: Callable) -> object:
if condition(item):
return item
return None

def generate_user_agent(
iphone: str | None = None,
ios: str | None = None,
app_ver: str = APP_VERSION,
account_id: str = "_",
operator: str = "1"
) -> str:
iphone_code = iphone
ios_code = ios
if iphone is None or ios is None:
rand_iphone = choice(iPHONE_iOS_CODES)
iphone_code = choice(rand_iphone["code"])
ios_code = choice(rand_iphone["os"])
return f"{iphone_code} | {ios_code} | ntk | {app_ver} | {account_id} | {operator} | {str(uuid.uuid4()).upper()}"
30 changes: 30 additions & 0 deletions custom_components/elektronny_gorod/user_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import uuid
from random import choice
from .const import (
APP_VERSION,
ANDROID_DEVICES,
ANDROID_OS_VER,
)

class UserAgent:
def __init__(self) -> None:
rand_phone = choice(ANDROID_DEVICES)
self.phone_manufacturer: str = rand_phone["manufacturer"]
self.phone_model: str = rand_phone["model"]
self.android_ver: str = ANDROID_OS_VER
self.app_version: dict = APP_VERSION
self.account_id: str = ""
self.operator_id: str | int = "null"
self.uuid: str = str(uuid.uuid4())
self.place_id: str = "null"

def __str__(self):
manufacturer = self.phone_manufacturer
model = self.phone_model
ver = self.android_ver
app_ver = self.app_version
account_id = self.account_id
operator_id = self.operator_id
uuid = self.uuid
place_id = self.place_id
return f"{manufacturer} {model} | Android {ver} | ntk | {app_ver["name"]} ({app_ver["code"]}) | {account_id} | {operator_id} | {uuid} | {place_id}"

0 comments on commit 680132d

Please sign in to comment.