diff --git a/modules/soc_smarteq/README.txt b/modules/soc_smarteq/README.txt deleted file mode 100755 index 78ea0831a..000000000 --- a/modules/soc_smarteq/README.txt +++ /dev/null @@ -1,25 +0,0 @@ -Das smart SOC Modul ist vom smartEQ Modul des iobroker inspiriert. -Der js code wurde in python implementiert und etwas vereinfacht. - -Das Modul unterstützt die Authentication mit Passwort oder 2-Factor-Authentication (2FA). -Die Passwort-Authentication wird benutzt wenn ein Passwort konfiguriert ist. -Wenn das Passwort leer ist, wird die 2FA benutzt, die in 2 Schritten durchgeführt wird. -Zu Beginn sind Passwort und PIN leer. -Das SOC-Modul fordert eine PIN (6-stellige Zahl) an. -Vom Authentication Server wird die PIN per email gesendet. -Die PIN ist vom Anwender in die Konfiguration des SOC-Moduls einzutragen. -Das SOC-Modul benutzt nach Speichern die PIN um die Authentication abzuschliessen. -Die PIN ist 15 Minuten gültig, daher ist dies zügig zu machen. - - -Für beide Methoden gilt: -Bei erfolgreicher Authentication wird vom Authentication Server ein refresh token und access token erzeugt. -Bei Ablauf des access token wird in OAUTh ein Token Refresh durchgeführt, der ein neues Paar token erzeugt. - -Der access Token ist nur 2 Stunden gültig. -Der refresh Token ist länger gültig, ist aber nur ein mal nutzbar. -Daher muss der refresh token immer gespeichert werden wenn der access token ungültig ist. -Daher ist es empfohlen, die Intervalle in der Modulkonfiguration auf weniger als 2 Stunden zu setzen. -90 Minuten im Standby und 10 Minuten während des Ladens haben sich im Test bewährt. - - diff --git a/modules/soc_smarteq/main.sh b/modules/soc_smarteq/main.sh deleted file mode 100755 index e2aceffe5..000000000 --- a/modules/soc_smarteq/main.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -OPENWBBASEDIR=$(cd `dirname $0`/../../ && pwd) -RAMDISKDIR="$OPENWBBASEDIR/ramdisk" -MODULEDIR=$(cd `dirname $0` && pwd) -DMOD="EVSOC" -CHARGEPOINT=$1 -export OPENWBBASEDIR RAMDISKDIR MODULEDIR - -export debug=1 - -# check if config file is already in env -if [[ -z "$debug" ]]; then - echo "soc_smarteq: Seems like openwb.conf is not loaded. Reading file." - # try to load config - . $OPENWBBASEDIR/loadconfig.sh - # load helperFunctions - . $OPENWBBASEDIR/helperFunctions.sh -fi - -case $CHARGEPOINT in - 2) - # second charge point - ladeleistung=$(<$RAMDISKDIR/llaktuells1) - soctimerfile="$RAMDISKDIR/soctimer1" - socfile="$RAMDISKDIR/soc1" - fztype=$soc2type - username=$soc2user - password=$soc2pass - pin=$soc2pint - vin=$soc2vin - intervall=$(( soc2intervall * 6 )) - intervallladen=$(( soc2intervallladen * 6 )) - ;; - *) - # defaults to first charge point for backward compatibility - # set CHARGEPOINT in case it is empty (needed for logging) - CHARGEPOINT=1 - ladeleistung=$(<$RAMDISKDIR/llaktuell) - soctimerfile="$RAMDISKDIR/soctimer" - socfile="$RAMDISKDIR/soc" - username=$soc_smarteq_username - password=$soc_smarteq_passwort - pin=$soc_smarteq_pin - vin=$soc_smarteq_vin - intervall=$(( soc_smarteq_intervall * 6 )) - intervallladen=$(( soc_smarteq_intervallladen * 6 )) - ;; -esac - -incrementTimer(){ - case $dspeed in - 1) - # Regelgeschwindigkeit 10 Sekunden - ticksize=1 - ;; - 2) - # Regelgeschwindigkeit 20 Sekunden - ticksize=2 - ;; - 3) - # Regelgeschwindigkeit 60 Sekunden - ticksize=1 - ;; - *) - # Regelgeschwindigkeit unbekannt - ticksize=1 - ;; - esac - soctimer=$((soctimer+$ticksize)) - echo $soctimer > $soctimerfile -} - -getAndWriteSoc(){ - openwbDebugLog ${DMOD} 2 "Lp$CHARGEPOINT: Requesting SoC" - echo 0 > $soctimerfile - - if [ "$password" != "" ] - then - answer=$($MODULEDIR/soc_smarteq_pass.py --user "$username" --password "$password" --vin "$vin" --chargepoint "$CHARGEPOINT" 2>>$RAMDISKDIR/soc.log) - else - answer=$($MODULEDIR/soc_smarteq_2fa.py --user "$username" --pin "$pin" --vin "$vin" --chargepoint "$CHARGEPOINT" 2>>$RAMDISKDIR/soc.log) - fi - if [ $? -eq 0 ]; then - # we got a valid answer - echo $answer > $socfile - openwbDebugLog ${DMOD} 2 "Lp$CHARGEPOINT: SoC: $answer" - else - # we have a problem - openwbDebugLog ${DMOD} 0 "Lp$CHARGEPOINT: Error from soc_smart: $answer" - fi -} - -soctimer=$(<$soctimerfile) -if (( ladeleistung > 500 )); then - if (( soctimer < intervallladen )); then - openwbDebugLog ${DMOD} 2 "Lp$CHARGEPOINT: Charging, but nothing to do yet. Incrementing timer." - incrementTimer - else - getAndWriteSoc - fi -else - if (( soctimer < intervall )); then - openwbDebugLog ${DMOD} 2 "Lp$CHARGEPOINT: Nothing to do yet. Incrementing timer." - incrementTimer - - else - getAndWriteSoc - fi -fi - diff --git a/modules/soc_smarteq/soc_smarteq_2fa.py b/modules/soc_smarteq/soc_smarteq_2fa.py deleted file mode 100755 index b9c795ee9..000000000 --- a/modules/soc_smarteq/soc_smarteq_2fa.py +++ /dev/null @@ -1,504 +0,0 @@ -#!/usr/bin/python3 - -from argparse import ArgumentParser -import requests -import json -import os -import time -import datetime -import logging -import copy -import urllib -import uuid - -# Constants -BASE_URL = "https://id.mercedes-benz.com" -TOKEN_URL = BASE_URL + "/as/token.oauth2" -STATUS_URL_SMART = "https://oneapp.microservice.smart.mercedes-benz.com" -STATUS_URL_MERCEDES = "https://bff.emea-prod.mobilesdk.mercedes-benz.com" -SCOPE = "openid+profile+email+phone+ciam-uid+offline_access" -CLIENT_ID = "70d89501-938c-4bec-82d0-6abb550b0825" -GUID = "280C6B55-F179-4428-88B6-E0CCF5C22A7C" -ACCEPT_LANGUAGE = "de-DE;q=1.0" -SSL_VERIFY_STATUS = True -LOGIN_APP_ID = "01398c1c-dc45-4b42-882b-9f5ba9f175f1" -COUNTRY_CODE = "DE" -X_APPLICATIONNAME_ECE = "mycar-store-ece" -RIS_APPLICATION_VERSION = "1.40.0 (2097)" -RIS_OS_NAME = "ios" -RIS_OS_VERSION = "17.3" -RIS_SDK_VERSION = "2.111.1" -X_LOCALE = "de-DE" -WEBSOCKET_USER_AGENT = "MyCar/1.40.0 (com.daimler.ris.mercedesme.ece.ios; build:2097; iOS 17.3.0) Alamofire/5.4.0" -STATUS_USER_AGENT = "Device: iPhone 6; OS-version: iOS_12.5.1; App-Name: smart EQ control; App-Version: 3.0;\ - Build: 202108260942; Language: de_DE" -CONTENT_TYPE_OAUTH = "application/x-www-form-urlencoded" -CONTENT_TYPE = "application/json" -ACCEPT = "*/*" -ACCEPT_LANGUAGE = "de-DE;q=1.0" - - -# helper functions -def nested_key_exists(element: dict, *keys: str) -> bool: - # Check if *keys (nested) exists in `element` (dict). - if not isinstance(element, dict): - raise AttributeError('nested_key_exists() expects dict as first argument - got type ' + str(type(element))) - if len(keys) == 0: - raise AttributeError('nested_key_exists() expects at least two arguments, one given.') - - _element = element - for key in keys: - try: - _element = _element[key] - except KeyError: - return False - return True - - -class smarteq: - def __init__(self, storeFile: str): - self.storeFile = storeFile - - self.log = logging.getLogger("soc_smarteq_2fa") - debug = os.environ.get('debug', '0') - LOGLEVEL = 'WARN' - if debug == '1': - LOGLEVEL = 'INFO' - if debug == '2': - LOGLEVEL = 'DEBUG' - RAMDISKDIR = os.environ.get("RAMDISKDIR", "undefined") - logFile = RAMDISKDIR+'/soc.log' - format = '%(asctime)s %(levelname)s:%(name)s:%(message)s' - datefmt = '%Y-%m-%d %H:%M:%S' - logging.basicConfig(filename=logFile, - filemode='a', - format=format, - datefmt=datefmt, - level=LOGLEVEL) - - # for testing send logs to console - if os.environ.get('logConsole', '0') == '1': - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(logging.Formatter(format, datefmt)) - self.log.addHandler(consoleHandler) - - # self.method keeps a high level trace of actions - self.method = '' - self.soc_ts = 'n/a' - - self._country_code = COUNTRY_CODE - self.session = requests.session() - - self.load_store() - self.oldTokens = copy.deepcopy(self.store['Tokens']) - - # set_authState - # authState: 'init' - # 'authenticated' - # 'tokenRequested' - # 'pinRequested' - # 'accessTokenExpired' - def set_authState(self, state: str): - if 'authState' in self.store: - old_state = self.store['authState'] - else: - old_state = 'n/a' - self.store['authState'] = state - self.log.debug('set_authState from ' + old_state + ' to ' + state) - - # initialize store structures when no store is available - def init_store(self): - self.store['Tokens'] = {} - self.store['Tokens']['access_token'] = "" - self.store['Tokens']['refresh_token'] = "" - # self.store['refresh_token_history'] = [] - self.store['refresh_timestamp'] = int(0) - self.set_authState('init') - self.store['last_pin_used'] = '' - - # load store from file, initialize store structure if no file exists - def load_store(self): - try: - tf = open(self.storeFile, 'r', encoding='utf-8') - self.store = json.load(tf) - if 'Tokens' not in self.store: - self.init_store() - tf.close() - except FileNotFoundError: - self.log.warning("init: store file not found, new 2FA authentication required") - self.store = {} - self.init_store() - self.set_authState('init') - except Exception as e: - self.log.debug("init: loading stored data failed, file: " + - self.storeFile + ", error=" + str(e)) - self.store = {} - self.init_store() - - # write store file - def write_store(self): - try: - tf = open(self.storeFile, 'w', encoding='utf-8') - except Exception as e: - self.log.debug("write_store_file: Exception " + str(e)) - os.system("sudo rm -f " + self.storeFile) - tf = open(self.storeFile, 'w', encoding='utf-8') - json.dump(self.store, tf, indent=4) - tf.close() - try: - os.chmod(self.storeFile, 0o777) - except Exception as e: - os.system("sudo chmod 0777 " + self.storeFile) - - # set username and pin - def set_credentials(self, username: str, pin: str): - self.username = username - self.pin = pin - - # set vin - def set_vin(self, vin: str): - self.vin = vin - - # set chargepoint number - def set_chargepoint(self, chargepoint: str): - self.chargepoint = chargepoint - - -# 2fa authentication functions - # send request for new pin to oauth server - def request_pin(self, email: str, nonce: str): - self.log.debug("Start request_pin: email=" + email + ", nonce=" + nonce) - - headers = { - "Host": "bff.emea-prod.mobilesdk.mercedes-benz.com", - "Ris-Os-Name": RIS_OS_NAME, - "Ris-Os-Version": RIS_OS_VERSION, - "Ris-Sdk-Version": RIS_SDK_VERSION, - "X-Locale": X_LOCALE, - "X-Trackingid": str(uuid.uuid4()), - "X-Sessionid": str(uuid.uuid4()), - "User-Agent": WEBSOCKET_USER_AGENT, - "Content-Type": CONTENT_TYPE, - "X-Applicationname": X_APPLICATIONNAME_ECE, - "Accept": ACCEPT, - "Accept-Encoding": "gzip, deflate, br", - "Ris-Application-Version": RIS_APPLICATION_VERSION - } - - url = STATUS_URL_MERCEDES + "/v1/config" - self.log.info("request_pin-get: url=" + url + - ", headers=" + json.dumps(headers, indent=4)) - response1 = self.session.get(url, headers=headers) - self.log.info("Result request_pin get: %s", response1) - - url = STATUS_URL_MERCEDES + "/v1/login" - d = { - "emailOrPhoneNumber": self.username, - "countryCode": self._country_code, - "nonce": nonce - } - data = json.dumps(d) - - self.log.info("request_pin-post: url=" + url + - ", data=" + json.dumps(data, indent=4) + - ", headers=" + json.dumps(headers, indent=4)) - response = self.session.post(url, data=data, headers=headers) - self.log.debug("Result request_pin%s", response) - self.set_authState('pinRequested') - return response - - # request new token set using pin - def request_new_token_set(self, user_input=None): - errors = {} - nonce = self.store['nonce'] - self.set_authState('tokenRequested') - self.store['last_pin_used'] = self.pin - try: - self.log.debug("calling request_access_token") - result = self.request_access_token(self.username, self.pin, nonce) - except Exception as error: - errors = error - self.log.error("Request token error: %s", errors) - if not errors: - self.log.debug("Token received: " + str(result)) - - # authenticate: request pin and get tokens - def authenticate(self): - errors = {} - nonce = str(uuid.uuid4()) - user_input = {} - user_input["nonce"] = nonce - - try: - self.log.debug("calling request_pin") - response = self.request_pin(self.username, nonce) - self.log.debug("request_pin done, response=" + str(response)) - self.log.debug("response.status_code = " + str(response.status_code)) - if response.status_code > 200: - errors["request_pin"] = "Authentication error " + str(response.status_code) - except Exception as error: - errors = error - self.log.error("Request PIN error: %s", errors) - - # request_access_token - part of 2FA process - def request_access_token(self, email: str, pin: str, nonce: str): - self.log.debug("enter request_access_token: email=" + email + ", pin=" + pin + ", nonce=" + nonce) - self.method += " 3-request_access_token" - url = TOKEN_URL - encoded_email = urllib.parse.quote_plus(email, safe="@") - - data = ( - "client_id=" + LOGIN_APP_ID + - "&grant_type=password&username=" + encoded_email + - "&password=" + nonce + ":" + pin + - "&scope=" + SCOPE - ) - - headers = { - "X-Applicationname": X_APPLICATIONNAME_ECE, - "Ris-Application-Version": RIS_APPLICATION_VERSION, - "Content-Type": CONTENT_TYPE_OAUTH, - "Stage": "prod", - "X-Device-Id": str(uuid.uuid4()), - "X-Request-Id": str(uuid.uuid4()) - } - self.log.debug("request_access_token-post: url=" + url + - ", data=" + json.dumps(data, indent=4) + - ", headers=" + json.dumps(headers, indent=4)) - try: - token_info = self.session.post(url, data=data, headers=headers) - self.log.debug("request_access_token.status_code = " + str(token_info.status_code)) - Tokens = json.loads(token_info.text) - if not Tokens['access_token']: - self.log.warning("request_access_token failed") - return None - self.log.debug("Tokens=\n" + json.dumps(Tokens, indent=4)) - self.store['Tokens'] = Tokens - - if token_info is not None: - ts = int(time.time()) - self.store['refresh_timestamp'] = ts - self.store['refresh_time'] = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') - self.set_authState('authenticated') - self.write_store() - return token_info - except Exception as e: - self.log.error("request_access_token exception: " + str(e)) - return None - - # refresh_token using existing refresh_token - def refresh_token(self, refresh_token: str) -> dict: - self.method += " 2-refresh_token" - - url = TOKEN_URL - - headers = { - "Accept": ACCEPT, - "User-Agent": "sOAF/202108260942 CFNetwork/978.0.7 Darwin/18.7.0", - "Accept-Language": ACCEPT_LANGUAGE, - "Authorization": "Bearer " + self.store['Tokens']['refresh_token'], - "Content-Type": CONTENT_TYPE_OAUTH, - } - - data = {'grant_type': 'refresh_token', - 'refresh_token': refresh_token} - - try: - response = self.session.post(url, - headers=headers, - data=data, - verify=True, - allow_redirects=False, - timeout=(30, 30)) - self.log.debug("refresh_token.status_code = " + str(response.status_code)) - self.log.debug("refresh_token.text = " + str(response.text)) - except Exception as e: - self.log.error("refresh_token exception: " + str(e)) - resperr = {} - resperr.status_code = 500 - resperr.test = str(e) - return resperr - return response - - # refresh access_token - def refresh_access_token(self) -> dict: - response = self.refresh_token(self.store['Tokens']['refresh_token']) - self.log.debug("refresh_access_token.status_code = " + str(response.status_code)) - - if response.status_code > 200: - self.log.warning("refresh_access_token failed, start 2FA process") - self.set_authState('init') - return None - else: - self.log.debug("refresh_access_token.text = " + str(response.text)) - Tokens = json.loads(response.text) - self.log.debug("Tokens=\n" + json.dumps(self.store['Tokens'], indent=4)) - - if Tokens['access_token']: - self.store['Tokens'] = Tokens - ts = int(time.time()) - self.store['refresh_timestamp'] = ts - self.store['refresh_time'] = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') - self.set_authState('authenticated') - self.write_store() - return self.store['Tokens']['access_token'] - - # get Soc of Vehicle - def get_status(self, vin: str) -> int: - self.method += " 1-get_status" - - url = STATUS_URL_SMART + "/seqc/v0/vehicles/" + vin + "/refresh-data" - - headers = { - "Accept": ACCEPT, - "Accept-Language": ACCEPT_LANGUAGE, - "Authorization": "Bearer " + self.store['Tokens']['access_token'], - "X-Applicationname": CLIENT_ID, - "User-Agent": STATUS_USER_AGENT, - "Guid": GUID - } - # soc = -1 - try: - response = self.session.get(url, headers=headers, verify=SSL_VERIFY_STATUS) - res = json.loads(response.text) - res_json = json.dumps(res, indent=4) - self.log.debug("get_status: result json:\n" + res_json) - if nested_key_exists(res, 'precond', 'data', 'soc', 'value'): - res_json = json.dumps(res['precond']['data']['soc'], indent=4) - try: - soc = res['precond']['data']['soc']['value'] - _ts = res['precond']['data']['soc']['ts'] - self.soc_ts = datetime.datetime.fromtimestamp(_ts).strftime('%Y-%m-%d %H:%M:%S') - self.log.debug("get_status: result json:\n" + res_json) - except Exception as e: - self.log.exception("get_status: exception0 e=" + str(e)) - soc = -1 - elif 'error' in res and res['error'] == 'unauthorized': - self.log.debug("get_status: access_token expired or invalid - try refresh") - self.log.debug("get_status: error - result json:\n" + res_json) - soc = -1 - else: - self.log.debug("get_status: unexpected error - response.status_code:" + str(response.status_code)) - self.log.debug("get_status: unexpected error - response.text:" + response.text) - self.log.debug("get_status: unexpected error - result json:\n" + res_json) - soc = -1 - - except Exception as e: - self.log.error("get_status: Exception1: " + str(e)) - self.log.error("get_status: result:\n" + res_json) - soc = -1 - if "Vehicle not found" in res_json: - soc = -2 - return soc - - # fetch_soc: execute next step dependent on authState - # authState: 'init' - # 'authenticated' - # 'pinRequested' - # 'tokenRequested' - # 'accessTokenExpired' - def fetch_soc(self: str, start_ts: float) -> int: - self.log.debug('fetch_soc/' + self.store['authState'] + - ': username=' + self.username + - ', pin=' + self.pin + - ', vin=' + self.vin) - self.Soc = -1 - while self.Soc == -1: - if self.store['authState'] == 'authenticated': - try: - if 'access_token' in self.store['Tokens']: - self.Soc = self.get_status(self.vin) - if self.Soc >= 0: - self.log.debug("fetch_soc/authenticated: 1st attempt successful") - - if self.Soc == -1: - self.set_authState('accessTokenExpired') - self.log.debug("fetch_soc/authenticated: get_status failed, refresh access_token ...") - self.store['Tokens']['access_token'] = self.refresh_access_token() - if self.store['authState'] == 'authenticated' and 'access_token' in self.store['Tokens']: - self.Soc = self.get_status(self.vin) - if self.Soc >= 0: - self.log.debug("fetch_soc/authenticated: 2nd attempt successful") - else: - self.log.warning("fetch_soc/authenticated: 2nd attempt failed - soc=" + str(self.Soc)) - else: - self.log.error("fetch_soc/authenticated: refresh_access_token failed") - self.Soc = -1 - elif self.Soc == -2: - self.log.error("fetch_soc/authenticated: failed, Vehicle not found, check VIN") - self.Soc = -1 - else: - self.log.debug("fetch_soc/authenticated: get_status 1st attempt success") - except Exception as e: - self.log.error("fetch_soc/authenticated: get_status exception, refresh_access_token ..." + str(e)) - self.store['Tokens']['access_token'] = self.refresh_access_token() - if self.store['authState'] == 'authenticated' and 'access_token' in self.store['Tokens']: - self.Soc = self.get_status(self.vin) - else: - self.log.error("fetch_soc/authenticated: refresh_access_token failed") - elapsed = time.time() - start_ts - self.log.info("Lp" + self.chargepoint + - " SOC: " + str(self.Soc) + '%' + - '@' + self.soc_ts + - ', Elapsed: ' + str(round(elapsed, 2)) + ' s' + - ', Method:' + self.method) - if self.store['Tokens'] != self.oldTokens: - self.log.debug("fetch_soc/authenticated: tokens changed, store token file") - self.write_store() - - # authState == pinRequested and pin != last_pin_used: get new token set - if self.store['authState'] == 'pinRequested': - self.log.debug('fetch_soc/pinRequested: old_pin = ' + self.store['last_pin_used'] + ', pin=' + self.pin) - if self.store['last_pin_used'] != self.pin: - self.log.debug('fetch_soc/pinRequested: call request_new_token_set') - self.request_new_token_set() - self.write_store() - self.Soc = -1 - else: - self.log.warning('fetch_soc/pinRequested: waiting for new pin in configuration') - self.Soc = 0 - - # authState == init: request pin - if self.store['authState'] == 'init' or self.pin == 'neu': - self.log.debug('fetch_soc/init: request_pin') - self.store['nonce'] = str(uuid.uuid4()) - self.request_pin(self.username, self.store['nonce']) - self.write_store() - self.Soc = 0 - - return self.Soc - - -# main program -def main(): - start_ts = time.time() - parser = ArgumentParser() - parser.add_argument("-v", "--vin", - help="VIN of vehicle", metavar="VIN", required=True) - parser.add_argument("-u", "--user", - help="user", metavar="user", required=True) - parser.add_argument("-p", "--pin", - help="pin", metavar="pin", required=True) - parser.add_argument("-c", "--chargepoint", - help="chargepoint", metavar="chargepoint", required=True) - args = vars(parser.parse_args()) - user_id = args['user'] - pin = args['pin'] - vin = args['vin'] - chargepoint = args['chargepoint'] - - OPENWBBASEDIR = os.environ.get("OPENWBBASEDIR", "undefined") - storeFile = OPENWBBASEDIR+'/soc_smarteq_store_lp'+chargepoint+'.json' - - Smart = smarteq(storeFile) - Smart.set_credentials(user_id, pin) - Smart.set_vin(vin) - Smart.set_chargepoint(chargepoint) - soc = Smart.fetch_soc(start_ts) - if soc == -1: - soc = 0 - print(soc) - - -if __name__ == "__main__": - main() diff --git a/modules/soc_smarteq/soc_smarteq_pass.py b/modules/soc_smarteq/soc_smarteq_pass.py deleted file mode 100755 index 6bf144eb7..000000000 --- a/modules/soc_smarteq/soc_smarteq_pass.py +++ /dev/null @@ -1,475 +0,0 @@ -#!/usr/bin/python3 - -from argparse import ArgumentParser -import requests -import json -import bs4 -import os -import time -import datetime -import logging -import pickle -import copy -import base64 -import re -import hashlib - -# Constants -BASE_URL = "https://id.mercedes-benz.com" -OAUTH_URL = BASE_URL + "/as/authorization.oauth2" -LOGIN_URL = BASE_URL + "/ciam/auth/login" -TOKEN_URL = BASE_URL + "/as/token.oauth2" -STATUS_URL = "https://oneapp.microservice.smart.mercedes-benz.com" -REDIRECT_URI = STATUS_URL -SCOPE = "openid+profile+email+phone+ciam-uid+offline_access" -CLIENT_ID = "70d89501-938c-4bec-82d0-6abb550b0825" -GUID = "280C6B55-F179-4428-88B6-E0CCF5C22A7C" -ACCEPT_LANGUAGE = "de-de" - -TOKENS_REFRESH_THRESHOLD = 3600 -SSL_VERIFY_AUTH = True -SSL_VERIFY_STATUS = True - - -# helper functions -def nested_key_exists(element: dict, *keys: str) -> bool: - # Check if *keys (nested) exists in `element` (dict). - if not isinstance(element, dict): - raise AttributeError('nested_key_exists() expects dict as first argument - got type ' + str(type(element))) - if len(keys) == 0: - raise AttributeError('nested_key_exists() expects at least two arguments, one given.') - - _element = element - for key in keys: - try: - _element = _element[key] - except KeyError: - return False - return True - - -def generateCodeChallengePair() -> tuple: - code_verifier = base64.urlsafe_b64encode(os.urandom(40)).decode('utf-8') - code_verifier = re.sub('[^a-zA-Z0-9]+', '', code_verifier) - code_challenge = hashlib.sha256(code_verifier.encode('utf-8')).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8') - code_challenge = code_challenge.replace('=', '') - return (code_verifier, code_challenge) - - -class smarteq: - def __init__(self, storeFile: str): - self.storeFile = storeFile - self.log = logging.getLogger("soc_smarteq_pass") - debug = os.environ.get('debug', '0') - LOGLEVEL = 'WARN' - if debug == '1': - LOGLEVEL = 'INFO' - if debug == '2': - LOGLEVEL = 'DEBUG' - RAMDISKDIR = os.environ.get("RAMDISKDIR", "undefined") - logFile = RAMDISKDIR+'/soc.log' - format = '%(asctime)s %(levelname)s:%(name)s:%(message)s' - datefmt = '%Y-%m-%d %H:%M:%S' - logging.basicConfig(filename=logFile, - filemode='a', - format=format, - datefmt=datefmt, - level=LOGLEVEL) - - # for testing send logs to console - if os.environ.get('logConsole', '0') == '1': - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(logging.Formatter(format, datefmt)) - self.log.addHandler(consoleHandler) - - # self.method keeps a high level trach of actions - self.method = '' - self.soc_ts = 'n/a' - # self.store is read from ramdisk at start and saved at end. - # currently is contains: - # Tokens: refresh- and access-tokens of OAUTH - # refresh_timestamp: epoch of last refresh_token. - - self.session = requests.session() - - self.load_store() - self.oldTokens = copy.deepcopy(self.store['Tokens']) - - def load_store(self): - try: - tf = open(self.storeFile, "rb") - self.store = pickle.load(tf) - if 'Tokens' not in self.store: - self.store['Tokens'] = {} - self.store['refresh_timestamp'] = int(0) - tf.close() - except FileNotFoundError: - self.log.warning("init: no store file found, full reconnect required") - self.store = {} - self.store['Tokens'] = {} - self.store['refresh_timestamp'] = int(0) - except Exception as e: - self.log.debug("init: loading stored data failed, file: " + self.storeFile) - self.store = {} - self.store['Tokens'] = {} - self.store['refresh_timestamp'] = int(0) - - def write_store(self): - try: - tf = open(self.storeFile, "wb") - except Exception as e: - self.log.debug("write_store: Exception " + str(e)) - os.system("sudo rm -f " + self.storeFile) - tf = open(self.storeFile, "wb") - pickle.dump(self.store, tf) - tf.close() - try: - os.chmod(self.storeFile, 0o777) - except Exception as e: - os.system("sudo chmod 0777 " + self.storeFile) - - # set username and password - def set_credentials(self, username: str, password: str): - self.username = username - self.password = password - - # set vin - def set_vin(self, vin: str): - self.vin = vin - - # set chargepoint number - def set_chargepoint(self, chargepoint: str): - self.chargepoint = chargepoint - - # ===== get resume string ====== - def get_resume(self) -> str: - response_type = "code" - self.code_verifier, self.code_challenge = generateCodeChallengePair() - self.code_challenge_method = "S256" - url = OAUTH_URL + '?client_id=' + CLIENT_ID + '&response_type=' + response_type + '&scope=' + SCOPE - url = url + '&redirect_uri=' + REDIRECT_URI - url = url + '&code_challenge=' + self.code_challenge + '&code_challenge_method=' + self.code_challenge_method - headers = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": ACCEPT_LANGUAGE, - "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_5_1 like Mac OS X)\ - AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" - } - - try: - response = self.session.get(url, headers=headers, verify=SSL_VERIFY_AUTH) - self.log.debug("get_resume: status_code = " + str(response.status_code)) - self.log.debug("get_resume: text = " + str(response.text)) - soup = bs4.BeautifulSoup(response.text, 'html.parser') - - for cd in soup.findAll(text=True): - if "CDATA" in cd: - self.log.debug("get_resume: cd.CData= " + str(cd)) - for w in cd.split(','): - if w.find("const initialState = ") != -1: - iS = w - if iS: - js = iS.split('{')[1].split('}')[0].replace('\\', '').replace('\\"', '"').replace('""', '"') - self.resume = js[1:len(js)-1].split(':')[1][2:] - self.log.debug("get_resume: resume = " + self.resume) - except Exception as e: - self.log.error('get_resume: Exception: ' + str(e)) - return self.resume - - # login to website, return (intermediate) token - def login(self) -> str: - self.resume = self.get_resume() - url = LOGIN_URL + "/pass" - headers = { - "Content-Type": "application/json", - "Accept": "application/json, text/plain, */*", - "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_5_1 like Mac OS X)\ - AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", - "Referer": LOGIN_URL, - "Accept-Language": ACCEPT_LANGUAGE - } - d = {} - d['username'] = self.username - d['password'] = self.password - d['rememberMe'] = 'true' - data = json.dumps(d) - data = json.dumps({'username': self.username, - 'password': self.password, - 'rememberMe': 'true'}) - - try: - response = self.session.post(url, headers=headers, data=data, verify=SSL_VERIFY_AUTH) - self.log.debug("login: status_code = " + str(response.status_code)) - if response.status_code > 400: - self.log.error("login: failed, status_code = " + str(response.status_code) + - ", check username/password") - token = "" - else: - result_json = json.loads(str(bs4.BeautifulSoup(response.text, 'html.parser'))) - self.log.debug("login: result_json:\n" + json.dumps(result_json)) - token = result_json['token'] - self.log.debug("login: token = " + token) - except Exception as e: - self.log.error('login: Exception: ' + str(e)) - token = "" - return token - - # get code - def get_code(self) -> str: - token = self.login() - if token == "": - self.log.error("login: Login failed - check username/password") - return "" - url = BASE_URL + '/' + self.resume - headers = { - "Content-Type": "application/x-www-form-urlencoded", - "Accept": "application/json, text/plain, */*", - "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_5_1 like Mac OS X)\ - AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", - "Referer": LOGIN_URL, - "Accept-Language": ACCEPT_LANGUAGE, - } - # d = {} - # d['token'] = token - # data = json.dumps(d) - data = json.dumps({'token': token}) - - try: - response = self.session.post(url, headers=headers, data=data, verify=SSL_VERIFY_AUTH) - code = response.url.split('?')[1].split('=')[1] - self.log.debug("get_code: code=" + code) - except Exception as e: - self.log.error("get_code: Exception: " + str(e)) - return code - - # get Tokens - def get_tokens(self) -> dict: - self.method += " 3-full (re)connect" - code = self.get_code() - if code == "": - self.log.warn("get_tokens: get_code failed") - return {} - url = TOKEN_URL - headers = { - "Accept": "*/*", - "User-Agent": "sOAF/202108260942 CFNetwork/978.0.7 Darwin/18.7.0", - "Accept-Language": ACCEPT_LANGUAGE, - "Content-Type": "application/x-www-form-urlencoded", - } - data = "grant_type=authorization_code&code=" + code + "&code_verifier=" + self.code_verifier +\ - "&redirect_uri=" + REDIRECT_URI + "&client_id=" + CLIENT_ID - - try: - response = self.session.post(url, headers=headers, data=data, verify=SSL_VERIFY_AUTH) - self.log.debug("get_tokens: status_code = " + str(response.status_code)) - Tokens = json.loads(response.text) - if not Tokens['access_token']: - self.log.warn("get_tokens: no access_token found") - Tokens = {} - else: - self.log.debug("Tokens=\n" + json.dumps(Tokens, indent=4)) - except Exception as e: - self.log.exception("get_tokens: Exception: " + str(e)) - return Tokens - - # refresh tokens - def refresh_tokens(self) -> dict: - self.method += " 2-refresh_token" - url = TOKEN_URL - headers = { - "Accept": "*/*", - "User-Agent": "sOAF/202108260942 CFNetwork/978.0.7 Darwin/18.7.0", - "Accept-Language": ACCEPT_LANGUAGE, - "Content-Type": "application/x-www-form-urlencoded", - } - # data = {'grant_type': 'refresh_token', - # 'client_id': CLIENT_ID, - # 'refresh_token': self.store['Tokens']['refresh_token']} - data = "grant_type=refresh_token&client_id=" + CLIENT_ID + "&refresh_token=" +\ - self.store['Tokens']['refresh_token'] - - try: - response = self.session.post(url, - headers=headers, - data=data, - verify=SSL_VERIFY_AUTH, - allow_redirects=False, - timeout=(30, 30)) - self.log.debug("refresh_tokens: status_code = " + str(response.status_code)) - self.log.debug("refresh_tokens: text = " + str(response.text)) - newTokens = json.loads(response.text) - if 'error' in newTokens and newTokens['error'] == 'unauthorized': - self.log.warning("refresh_tokens: error: " + newTokens['error'] + ', ' + newTokens['error_description']) - if 'access_token' not in newTokens: - self.log.debug("refresh_tokens: new access_token not found") - newTokens['access_token'] = "" - if 'refresh_token' not in newTokens: - self.log.debug("refresh_tokens: new refresh_token not found") - newTokens['refresh_token'] = "" - self.log.debug("refresh_tokens: newTokens=\n" + json.dumps(newTokens, indent=4)) - except Exception as e: - self.log.error("refresh_tokens: Exception: " + str(e)) - newTokens['access_token'] = "" - newTokens['refresh_token'] = "" - return newTokens - - # reconnect to Server - def reconnect(self) -> dict: - # check if we have a refresh token and last refresh was more then 1h ago (3600s) - if 'refresh_token' in self.store['Tokens']: - now = int(time.time()) - secs_since_refresh = now - self.store['refresh_timestamp'] - if secs_since_refresh > TOKENS_REFRESH_THRESHOLD: - # try to refresh tokens - new_tokens = self.refresh_tokens() - self.store['refresh_timestamp'] = int(time.time()) - _ref = True - else: - # keep existing tokens - return self.store['Tokens'] - else: - self.log.debug("reconnect: refresh_token not found in self.store['Tokens']=" + - json.dumps(self.store['Tokens'], indent=4)) - new_tokens = {'refresh_token': '', 'access_token': ''} - _ref = False - self.log.debug("reconnect: new_tokens=" + json.dumps(new_tokens, indent=4)) - if new_tokens['access_token'] == '': - if _ref: - self.log.warning("reconnect: refresh access_token failed, try full reconnect") - Tokens = self.get_tokens() - else: - self.log.debug("reconnect: refresh access_token successful") - Tokens = self.store['Tokens'] # replace expired access and refresh token by new tokens - for key in new_tokens: - Tokens[key] = new_tokens[key] - self.log.debug("reconnect: replace Tokens[" + key + "], new value: " + str(Tokens[key])) - - return Tokens - - # get Soc of Vehicle - def get_status(self, vin: str) -> int: - self.method += " 1-get_status" - - url = STATUS_URL + "/seqc/v0/vehicles/" + vin + "/refresh-data" - - headers = { - "accept": "*/*", - "accept-language": "de-DE;q=1.0", - "authorization": "Bearer " + self.store['Tokens']['access_token'], - "x-applicationname": CLIENT_ID, - "user-agent": "Device: iPhone 6; OS-version: iOS_12.5.1; App-Name: smart EQ control; App-Version: 3.0;\ - Build: 202108260942; Language: de_DE", - "guid": GUID - } - - try: - response = self.session.get(url, headers=headers, verify=SSL_VERIFY_STATUS) - res = json.loads(response.text) - res_json = json.dumps(res, indent=4) - if nested_key_exists(res, 'precond', 'data', 'soc', 'value'): - res_json = json.dumps(res['precond']['data']['soc'], indent=4) - try: - soc = res['precond']['data']['soc']['value'] - _ts = res['precond']['data']['soc']['ts'] - self.soc_ts = datetime.datetime.fromtimestamp(_ts).strftime('%Y-%m-%d %H:%M:%S') - self.log.debug("get_status: result json:\n" + res_json) - except: - soc = -1 - elif 'error' in res and res['error'] == 'unauthorized': - self.log.warning("get_status: access_token expired - try refresh") - self.log.debug("get_status: error - result json:\n" + res_json) - soc = -1 - - except Exception as e: - self.log.error("get_status: Exception: " + str(e)) - self.log.error("get_status: result:\n" + res_json) - soc = -1 - if "Vehicle not found" in res_json: - soc = -2 - return soc - - # fetch soc in 3 stages: - # 1. get_status via stored access_token - # 2. if expired: refresh_access_token using id and refresh token, then get_status - # 3. if refresh token expired: login, get tokens, then get_status - def fetch_soc(self: str, start_ts: float) -> int: - soc = -1 - try: - if 'refresh_token' in self.store['Tokens']: - self.store['Tokens'] = self.reconnect() - if 'access_token' in self.store['Tokens']: - soc = self.get_status(self.vin) - if soc > 0: - self.log.debug("fetch_soc: 1st attempt successful") - else: - self.log.debug("fetch_soc: 1st attempt failed - soc=" + str(soc)) - - if soc == -1: - self.log.debug("fetch_soc: (re)connecting ...") - self.store['Tokens'] = self.reconnect() - if 'access_token' in self.store['Tokens']: - soc = self.get_status(self.vin) - if soc > 0: - self.log.debug("fetch_soc: 2nd attempt successful") - else: - self.log.warning("fetch_soc: 2nd attempt failed - soc=" + str(soc)) - else: - self.log.error("fetch_soc: (re-)connect failed") - soc = 0 - elif soc == -2: - self.log.error("fetch_soc: failed, Vehicle not found, check VIN") - soc = 0 - - except Exception as e: - self.log.error("fetch_soc: exception, (re-)connecting ..." + str(e)) - self.store['Tokens'] = self.reconnect() - if 'access_token' in self.store['Tokens']: - soc = self.get_status(self.vin) - - elapsed = time.time() - start_ts - self.log.info("Lp" + self.chargepoint + - " SOC: " + str(soc) + '%' + - '@' + self.soc_ts + - ', Elapsed: ' + str(round(elapsed, 2)) + ' s' + - ', Method:' + self.method) - - if self.store['Tokens'] != self.oldTokens: - self.log.debug("reconnect: tokens changed, store token file") - self.write_store() - - return soc - - -# main program -def main(): - start_ts = time.time() - - parser = ArgumentParser() - parser.add_argument("-v", "--vin", - help="VIN of vehicle", metavar="VIN", required=True) - parser.add_argument("-u", "--user", - help="user", metavar="user", required=True) - parser.add_argument("-p", "--password", - help="password", metavar="password", required=True) - parser.add_argument("-c", "--chargepoint", - help="chargepoint", metavar="chargepoint", required=True) - args = vars(parser.parse_args()) - user_id = args['user'] - password = args['password'] - vin = args['vin'] - chargepoint = args['chargepoint'] - - RAMDISKDIR = os.environ.get("RAMDISKDIR", "undefined") - storeFile = RAMDISKDIR+'/soc_smarteq_store_lp'+chargepoint - - Smart = smarteq(storeFile) - Smart.set_credentials(user_id, password) - Smart.set_vin(vin) - Smart.set_chargepoint(chargepoint) - soc = Smart.fetch_soc(start_ts) - print(soc) - - -if __name__ == "__main__": - main() diff --git a/runs/updateConfig.sh b/runs/updateConfig.sh index b2a80c702..a4c88c1be 100755 --- a/runs/updateConfig.sh +++ b/runs/updateConfig.sh @@ -593,12 +593,6 @@ updateConfig(){ if ! grep -Fq "soc_ovms_intervall=" $ConfigFile; then echo "soc_ovms_intervall=120" >> $ConfigFile fi - if ! grep -Fq "soc_smarteq_intervallladen=" $ConfigFile; then - echo "soc_smarteq_intervallladen=20" >> $ConfigFile - fi - if ! grep -Fq "soc_smarteq_intervall=" $ConfigFile; then - echo "soc_smarteq_intervall=120" >> $ConfigFile - fi if ! grep -Fq "releasetrain=" $ConfigFile; then echo "releasetrain=stable" >> $ConfigFile fi @@ -1707,9 +1701,6 @@ updateConfig(){ if ! grep -Fq "soc2pin=" $ConfigFile; then echo "soc2pin=pin" >> $ConfigFile fi - if ! grep -Fq "soc2pint=" $ConfigFile; then - echo "soc2pint=''" >> $ConfigFile - fi if ! grep -Fq "lgessv1ip=" $ConfigFile; then echo "lgessv1ip=youripaddress" >> $ConfigFile fi @@ -2138,22 +2129,6 @@ updateConfig(){ if ! grep -Fq "soc_ovms_vehicleid=" $ConfigFile; then echo "soc_ovms_vehicleid=vehicleid" >> $ConfigFile fi - if ! grep -Fq "soc_smarteq_username=" $ConfigFile; then - echo "soc_smarteq_username=User" >> $ConfigFile - fi - if ! grep -Fq "soc_smarteq_passwort=" $ConfigFile; then - echo "soc_smarteq_passwort=''" >> $ConfigFile - else - sed -i "/soc_smarteq_passwort='/b; s/^soc_smarteq_passwort=\(.*\)/soc_smarteq_passwort=\'\1\'/g" $ConfigFile - fi - if ! grep -Fq "soc_smarteq_pin=" $ConfigFile; then - echo "soc_smarteq_pin=''" >> $ConfigFile - else - sed -i "/soc_smarteq_pin='/b; s/^soc_smarteq_pin=\(.*\)/soc_smarteq_pin=\'\1\'/g" $ConfigFile - fi - if ! grep -Fq "soc_smarteq_vin=" $ConfigFile; then - echo "soc_smarteq_vin=VIN" >> $ConfigFile - fi if ! grep -Fq "soc2vin=" $ConfigFile; then echo "soc2vin=" >> $ConfigFile echo "soc2intervall=60" >> $ConfigFile @@ -2404,5 +2379,32 @@ updateConfig(){ fi fi fi + echo "remove soc_smarteq entries from Config file" + cp $ConfigFile $ConfigFile.tmp + # check for socmodul1=soc_smarteqlp2 + ism2=`grep "socmodul1=soc_smarteqlp2" $ConfigFile.tmp | wc -l | awk '{print $1}'` + if [ $ism2 -ne 0 ] + then + echo "soc_smarteq found configured as socmodul1 - cleanup soc2 entries" + sed -e " + s/soc2user=.*$/soc2user=demo@demo.de/ + s/soc2pass=.*$/soc2pass=\'\'/ + s/soc2pin=.*$/soc2pin=pin/ + s/soc2vin=.*$/soc2vin=/ + s/soc2intervall=.*$/soc2intervall=60/ + s/soc2intervallladen=.*$/soc2intervallladen=10/ + " $ConfigFile.tmp > $ConfigFile.tmp.out + cp $ConfigFile.tmp.out $ConfigFile.tmp + fi + # modify configured smarteq modules to none + sed -e ' + s/socmodul=soc_smarteq/socmodul=none/ + s/socmodul1=soc_smarteqlp2/socmodul1=none/ + /soc2pint=/d + /soc_smarteq/d + ' $ConfigFile.tmp > $ConfigFile.tmp.out + cp $ConfigFile.tmp.out $ConfigFile + rm $ConfigFile.tmp $ConfigFile.tmp.out + echo "Config file Update done." } diff --git a/web/settings/modulconfiglp.php b/web/settings/modulconfiglp.php index d227821ba..083fa343f 100644 --- a/web/settings/modulconfiglp.php +++ b/web/settings/modulconfiglp.php @@ -764,7 +764,6 @@ function visibility_twcmanagerlp1_connection() { - @@ -1359,76 +1358,6 @@ function visibility_kia_advanced() { -
-
-
- Für smart EQ Fahrzeuge. Es wird benötigt:
- - smart Control Account aktiv
- Für smart EQ Fahrzeuge. Es wird benötigt:
- - smart Control Account aktiv
- Das Modul unterstützt 2 Loginverfahren
- - Login mit Passwort - wird benutzt wenn das Passwort nicht leer ist
- - Login über 2FA/Pin - wird benutzt wenn das Password leer ist.
- Bei 2FA wird nach Konfiguration ein Pin per Email übermittelt. Dieser PIN (6-stellig) ist in das Feld Pin einzutragen.
- Wichtig: Der Pin ist nach Empfang nur 15 Minuten gültig.
-
-
- -
- - - Email Adresse des Logins. - -
-
-
- -
- - - Password des Logins. - -
-
-
- -
- - - PIN des Accounts.
- Bei Smart EQ kommt die PIN (OTP Code) via Email.
-
-
-
-
- -
- - - Vollständige VIN des Fahrzeugs. - -
-
-
- -
- - - Wie oft das Fahrzeug abgefragt wird, wenn nicht geladen wird. Angabe in Minuten. - -
-
-
- -
- - - Wie oft das Fahrzeug abgefragt wird, wenn geladen wird. Angabe in Minuten. - -
-
-
-
@@ -2476,7 +2405,6 @@ function display_socmodul() { hideSection('#socmid'); hideSection('#socmvwid'); hideSection('#socmovms'); - hideSection('#socmsmarteq'); hideSection('#socvag'); hideSection('#socevcc'); hideSection('#socmqtt'); @@ -2516,11 +2444,6 @@ function display_socmodul() { showSection('#socsupportinfo'); showSection('#socmovms'); } - if($('#socmodul').val() == 'soc_smarteq') { - $('#socsuportlink').attr('href', 'https://openwb.de/forum/viewtopic.php?f=12&t=6222') - showSection('#socsupportinfo'); - showSection('#socmsmarteq'); - } if($('#socmodul').val() == 'soc_vag') { showSection('#socoldevccwarning'); showSection('#socvag'); @@ -3196,7 +3119,6 @@ function visibility_twcmanagerlp2_connection() { - @@ -3251,15 +3173,6 @@ function visibility_twcmanagerlp2_connection() { Für Fahrzeuge mit OVMS Modul. Es wird benötigt:
- OVMS Account in z.B. dexters-web.de
-
- Für smart EQ Fahrzeuge. Es wird benötigt:
- - smart Control Account aktiv
- Das Modul unterstützt 2 Loginverfahren
- - Login mit Passwort - wird benutzt wenn das Passwort nicht leer ist
- - Login über 2FA/Pin - wird benutzt wenn das Password leer ist.
- Bei 2FA wird nach Konfiguration ein Pin per Email übermittelt. Dieser PIN (6-stellig) ist in das Feld Pin einzutragen.
- Wichtig: Der Pin ist nach Empfang nur 15 Minuten gültig.
-
@@ -3788,20 +3701,6 @@ function visibility_i3_soccalclp2() {
-
-
-
- -
- - - PIN des Accounts.
- Bei Smart EQ kommt die PIN (OTP Code) via Email.
-
-
-
-
-
@@ -4565,7 +4464,6 @@ function display_socmodul1() { hideSection('#socmuser2'); hideSection('#socmpass2'); hideSection('#socmpin2'); - hideSection('#socmpin2t'); hideSection('#socmnone1'); hideSection('#socmhttp1'); hideSection('#socleaf1'); @@ -4589,7 +4487,6 @@ function display_socmodul1() { hideSection('#socoldevccwarninglp2'); hideSection('#socmvwidinfolp2'); hideSection('#socmovmsinfolp2'); - hideSection('#socmsmarteqinfolp2'); hideSection('#socsupportinfolp2'); hideSection('#socnosupportinfolp2'); @@ -4650,17 +4547,6 @@ function display_socmodul1() { showSection('#socmintervall2'); showSection('#socmintervallladen2'); } - if($('#socmodul1').val() == 'soc_smarteqlp2') { - $('#socsuportlinklp2').attr('href', 'https://openwb.de/forum/viewtopic.php?f=12&t=6222') - showSection('#socsupportinfolp2'); - showSection('#socmsmarteqinfolp2'); - showSection('#socmuser2'); - showSection('#socmpass2'); - showSection('#socmpin2t'); - showSection('#socmvin2'); - showSection('#socmintervall2'); - showSection('#socmintervallladen2'); - } if($('#socmodul1').val() == 'soc_vaglp2') { showSection('#socoldevccwarninglp2'); showSection('#socmtype2');