UNPKG

bluelinky2

Version:

An unofficial nodejs API wrapper for Hyundai bluelink

3 lines (2 loc) 44.8 kB
/* @preserve bluelinky / MIT License / https://github.com/Hacksore/bluelinky */ "use strict";var e=require("got"),t=require("winston"),r=require("fs"),i=require("util"),s=require("url"),n=require("push-receiver"),o=require("tough-cookie"),a=require("events");function c(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function h(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var d=c(e),l=h(t),u=c(s),g=h(n);const p=process.env.LOG_LEVEL||"info",{colorize:f,combine:v,timestamp:m,printf:w}=l.format,y=w((e=>("object"==typeof e.message&&(e.message=JSON.stringify(e.message,null,2)),`[${e.timestamp}] ${e.level}: ${e.message}`))),b=v(m({format:"YYYY-MM-DD HH:mm:ss"}),f(),y),C=l.createLogger({format:b,level:p,transports:[new l.transports.Console]}),k=e=>({login:`${e}/tods/api/lgn`,logout:`${e}/tods/api/lgout`,vehicleList:`${e}/tods/api/vhcllst`,vehicleInfo:`${e}/tods/api/sltvhcl`,status:`${e}/tods/api/lstvhclsts`,remoteStatus:`${e}/tods/api/rltmvhclsts`,lock:`${e}/tods/api/drlck`,unlock:`${e}/tods/api/drulck`,start:`${e}/tods/api/evc/rfon`,stop:`${e}/tods/api/evc/rfoff`,startCharge:`${e}/tods/api/evc/rcstrt`,stopCharge:`${e}/tods/api/evc/rcstp`,setChargeTarget:`${e}/tods/api/evc/setsoc`,locate:`${e}/tods/api/fndmcr`,hornlight:`${e}/tods/api/hornlight`,verifyAccountToken:`${e}/tods/api/vrfyacctkn`,verifyPin:`${e}/tods/api/vrfypin`,verifyToken:`${e}/tods/api/vrfytnc`}),S=e=>{const t=`https://${e}`;return{host:e,baseUrl:t,origin:"SPA",endpoints:Object.freeze(k(t))}},T=e=>{switch(e){case"hyundai":return Object.freeze({brand:"hyundai",...S("mybluelink.ca")});case"kia":return Object.freeze({brand:"hyundai",...S("kiaconnect.ca")});default:throw new Error(`Constructor ${e} is not managed.`)}},A=["cs","da","nl","en","fi","fr","de","it","pl","hu","no","sk","es","sv"],$=new Map,E=e=>async t=>{const{stamps:s,generated:n,frequency:o}=$.get(e)??await(async(e,t=`https://raw.githubusercontent.com/neoPix/bluelinky-stamps/master/${e}.v2.json`)=>{if(t.startsWith("file://")){const[,e]=t.split("file://"),s=await i.promisify(r.readFile)(e);return JSON.parse(s.toString("utf-8"))}const{body:s}=await d.default(t,{json:!0});return $.set(e,s),s})(e,t),a=new Date(n),c=Date.now()-a.getTime(),h=Math.floor(c/o);return h/(s.length-1)>=.9&&$.delete(e),s[Math.min(h,s.length-1)]},O=(e,t)=>({session:`${e}/api/v1/user/oauth2/authorize?response_type=code&state=test&client_id=${t}&redirect_uri=${e}/api/v1/user/oauth2/redirect`,login:`${e}/api/v1/user/signin`,language:`${e}/api/v1/user/language`,redirectUri:`${e}/api/v1/user/oauth2/redirect`,token:`${e}/api/v1/user/oauth2/token`,integration:`${e}/api/v1/user/integrationinfo`,silentSignIn:`${e}/api/v1/user/silentsignin`}),L=e=>{switch(e){case"hyundai":return Object.freeze((()=>{const e="prd.eu-ccapi.hyundai.com:8080",t=`https://${e}`,r="6d477c38-3ca4-4cf3-9557-2a1929a94654",i="014d2225-8495-4735-812d-2616334fd15d";return{brand:"hyundai",host:e,baseUrl:t,clientId:r,appId:i,endpoints:Object.freeze(O(t,r)),basicToken:"Basic NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==",GCMSenderID:"414998006775",stamp:E(`hyundai-${i}`),brandAuthUrl:({language:e,serviceId:r,userId:i})=>`https://eu-account.hyundai.com/auth/realms/euhyundaiidm/protocol/openid-connect/auth?client_id=64621b96-0f0d-11ec-82a8-0242ac130003&scope=openid%20profile%20email%20phone&response_type=code&hkid_session_reset=true&redirect_uri=${t}/api/v1/user/integration/redirect/login&ui_locales=${e}&state=${r}:${i}`}})());case"kia":return Object.freeze((()=>{const e="prd.eu-ccapi.kia.com:8080",t=`https://${e}`,r="fdc85c00-0a2f-4c64-bcb4-2cfb1500730a",i="e7bcd186-a5fd-410d-92cb-6876a42288bd";return{brand:"kia",host:e,baseUrl:t,clientId:r,appId:i,endpoints:Object.freeze(O(t,r)),basicToken:"Basic ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==",GCMSenderID:"345127537656",stamp:E(`kia-${i}`),brandAuthUrl:({language:e,serviceId:r,userId:i})=>`https://eu-account.kia.com/auth/realms/eukiaidm/protocol/openid-connect/auth?client_id=572e0304-5f8d-4b4c-9dd5-41aa84eed160&scope=openid%20profile%20email%20phone&response_type=code&hkid_session_reset=true&redirect_uri=${t}/api/v1/user/integration/redirect/login&ui_locales=${e}&state=${r}:${i}`}})());default:throw new Error(`Constructor ${e} is not managed.`)}};var I;!function(e){e.US="US",e.CA="CA",e.EU="EU"}(I||(I={}));const P=[50,60,70,80,90,100],D={refresh:!1,parsed:!1};class x{constructor(e,t){this.vehicleConfig=e,this.controller=t,this._fullStatus=null,this._status=null,this._location=null,this._odometer=null,this.userConfig={username:void 0,password:void 0,region:I.EU,brand:"hyundai",autoLogin:!0,pin:void 0,vin:void 0,vehicleId:void 0},this.userConfig=t.userConfig}vin(){return this.vehicleConfig.vin}name(){return this.vehicleConfig.name}nickname(){return this.vehicleConfig.nickname}id(){return this.vehicleConfig.id}brandIndicator(){return this.vehicleConfig.brandIndicator}}class _ extends x{constructor(e,t){super(e,t),this.vehicleConfig=e,this.controller=t,this.region=I.US,C.debug(`US Vehicle ${this.vehicleConfig.regId} created`)}getDefaultHeaders(){return{access_token:this.controller.session.accessToken,client_id:this.controller.environment.clientId,Host:this.controller.environment.host,"User-Agent":"okhttp/3.12.0",registrationId:this.vehicleConfig.regId,gen:this.vehicleConfig.generation,username:this.userConfig.username,vin:this.vehicleConfig.vin,"APPCLOUD-VIN":this.vehicleConfig.vin,Language:"0",to:"ISS",encryptFlag:"false",from:"SPA",brandIndicator:this.vehicleConfig.brandIndicator,bluelinkservicepin:this.userConfig.pin,offset:"-5"}}fullStatus(){throw new Error("Method not implemented.")}async odometer(){const e=await this._request(`/ac/v2/enrollment/details/${this.userConfig.username}`,{method:"GET",headers:{...this.getDefaultHeaders()}});if(200!==e.statusCode)throw"Failed to get odometer reading!";const t=JSON.parse(e.body).enrolledVehicleDetails.find((e=>e.vehicleDetails.vin===this.vin()));return this._odometer={value:t.vehicleDetails.odometer,unit:0},this._odometer}async location(){const e=await this._request("/ac/v2/rcs/rfc/findMyCar",{method:"GET",headers:{...this.getDefaultHeaders()}});if(200!==e.statusCode)throw"Failed to get location!";const t=JSON.parse(e.body);return{latitude:t.coord.lat,longitude:t.coord.lon,altitude:t.coord.alt,speed:{unit:t.speed.unit,value:t.speed.value},heading:t.head}}async start(e){const t={airCtrl:!1,igniOnDuration:10,airTempvalue:70,defrost:!1,heating1:!1,...e},r={Ims:0,airCtrl:+t.airCtrl,airTemp:{unit:1,value:`${t.airTempvalue}`},defrost:t.defrost,heating1:+t.heating1,igniOnDuration:t.igniOnDuration,seatHeaterVentInfo:null,username:this.userConfig.username,vin:this.vehicleConfig.vin};return 200===(await this._request("/ac/v2/rcs/rsc/start",{method:"POST",headers:{...this.getDefaultHeaders(),offset:"-4"},body:r,json:!0})).statusCode?"Vehicle started!":"Failed to start vehicle"}async stop(){if(200===(await this._request("/ac/v2/rcs/rsc/stop",{method:"POST",headers:{...this.getDefaultHeaders(),offset:"-4"}})).statusCode)return"Vehicle stopped";throw"Failed to stop vehicle!"}async status(e){const t={...D,...e},r=await this._request("/ac/v2/rcs/rvs/vehicleStatus",{method:"GET",headers:{REFRESH:t.refresh.toString(),...this.getDefaultHeaders()}}),{vehicleStatus:i}=JSON.parse(r.body),s={chassis:{hoodOpen:i?.hoodOpen,trunkOpen:i?.trunkOpen,locked:i?.doorLock,openDoors:{frontRight:!!i?.doorOpen?.frontRight,frontLeft:!!i?.doorOpen?.frontLeft,backLeft:!!i?.doorOpen?.backLeft,backRight:!!i?.doorOpen?.backRight},tirePressureWarningLamp:{rearLeft:!!i?.tirePressureLamp?.tirePressureWarningLampRearLeft,frontLeft:!!i?.tirePressureLamp?.tirePressureWarningLampFrontLeft,frontRight:!!i?.tirePressureLamp?.tirePressureWarningLampFrontRight,rearRight:!!i?.tirePressureLamp?.tirePressureWarningLampRearRight,all:!!i?.tirePressureLamp?.tirePressureWarningLampAll}},climate:{active:i?.airCtrlOn,steeringwheelHeat:!!i?.steerWheelHeat,sideMirrorHeat:!1,rearWindowHeat:!!i?.sideBackWindowHeat,defrost:i?.defrost,temperatureSetpoint:i?.airTemp?.value,temperatureUnit:i?.airTemp?.unit},engine:{ignition:i?.engine,accessory:i?.acc,range:i?.evStatus?.drvDistance[0]?.rangeByFuel?.totalAvailableRange?.value||i?.dte?.value,charging:i?.evStatus?.batteryCharge,batteryCharge12v:i?.battery?.batSoc,batteryChargeHV:i?.evStatus?.batteryStatus},lastupdate:new Date(i?.dateTime)};return this._status=t.parsed?s:i,this._status}async unlock(){const e=new s.URLSearchParams;e.append("userName",this.userConfig.username||""),e.append("vin",this.vehicleConfig.vin);return 200===(await this._request("/ac/v2/rcs/rdo/on",{method:"POST",headers:{...this.getDefaultHeaders()},body:e.toString()})).statusCode?"Unlock successful":"Something went wrong!"}async lock(){const e=new s.URLSearchParams;e.append("userName",this.userConfig.username||""),e.append("vin",this.vehicleConfig.vin);return 200===(await this._request("/ac/v2/rcs/rdo/off",{method:"POST",headers:{...this.getDefaultHeaders()},body:e.toString()})).statusCode?"Lock successful":"Something went wrong!"}async startCharge(){if(200===(await this._request(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/charge`,{method:"POST"})).statusCode)return C.debug(`Send start charge command to Vehicle ${this.vehicleConfig.id}`),"Start charge successful";throw"Something went wrong!"}async stopCharge(){if(200===(await d.default(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/charge`,{method:"POST"})).statusCode)return C.debug(`Send stop charge command to vehicle ${this.vehicleConfig.id}`),"Stop charge successful";throw"Something went wrong!"}async _request(e,t){await this.controller.refreshAccessToken(),t.headers.access_token=this.controller.session.accessToken;const r=await d.default(`${this.controller.environment.baseUrl}/${e}`,{throwHttpErrors:!1,...t});return r?.body&&C.debug(r.body),r}}class R{constructor(e){this.userConfig=e,this.session={accessToken:"",refreshToken:"",controlToken:"",deviceId:"",tokenExpiresAt:0}}}class U extends Error{constructor(e,t){super(e),this.source=t,this.name=U.ErrorName}}U.ErrorName="ManagedBluelinkyError";const H=(t,r)=>t instanceof e.HTTPError?new U(`${r?`@${r}: `:""}[${t.statusCode}] ${t.statusMessage} on [${t.method}] ${t.url} - ${JSON.stringify(t.body)}`,t):t instanceof e.ParseError?new U(`${r?`@${r}: `:""} Parsing error on [${t.method}] ${t.url} - ${JSON.stringify(t.response?.body)}`,t):(Error,t),V=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}));class M extends R{constructor(e){super(e),this.vehicles=[],this._environment=(e=>{if("hyundai"===e)return Object.freeze((()=>{const e="api.telematics.hyundaiusa.com";return{brand:"hyundai",host:e,baseUrl:`https://${e}`,clientId:"m66129Bb-em93-SPAHYN-bZ91-am4540zp19920",clientSecret:"v558o935-6nne-423i-baa8"}})());throw new Error(`Constructor ${e} is not managed.`)})(e.brand),C.debug("US Controller created")}get environment(){return this._environment}async refreshAccessToken(){const e=Math.floor(Date.now()/1e3-this.session.tokenExpiresAt)>=-10;try{if(this.session.refreshToken&&e){C.debug("refreshing token");const e=await d.default(`${this.environment.baseUrl}/v2/ac/oauth/token/refresh`,{method:"POST",body:{refresh_token:this.session.refreshToken},headers:{"User-Agent":"PostmanRuntime/7.26.10",client_secret:this.environment.clientSecret,client_id:this.environment.clientId},json:!0});return C.debug(e.body),this.session.accessToken=e.body.access_token,this.session.refreshToken=e.body.refresh_token,this.session.tokenExpiresAt=Math.floor(+new Date/1e3+parseInt(e.body.expires_in)),C.debug("Token refreshed"),"Token refreshed"}return C.debug("Token not expired, no need to refresh"),"Token not expired, no need to refresh"}catch(e){throw H(e,"AmericanController.refreshAccessToken")}}async login(){C.debug("Logging in to the API");try{const e=await d.default(`${this.environment.baseUrl}/v2/ac/oauth/token`,{method:"POST",body:{username:this.userConfig.username,password:this.userConfig.password},headers:{"User-Agent":"PostmanRuntime/7.26.10",client_id:this.environment.clientId,client_secret:this.environment.clientSecret},json:!0});return C.debug(e.body),200!==e.statusCode?"login bad":(this.session.accessToken=e.body.access_token,this.session.refreshToken=e.body.refresh_token,this.session.tokenExpiresAt=Math.floor(+new Date/1e3+parseInt(e.body.expires_in)),"login good")}catch(e){throw H(e,"AmericanController.login")}}async logout(){return"OK"}async getVehicles(){try{const e=await d.default(`${this.environment.baseUrl}/ac/v2/enrollment/details/${this.userConfig.username}`,{method:"GET",headers:{access_token:this.session.accessToken,client_id:this.environment.clientId,Host:this.environment.host,"User-Agent":"okhttp/3.12.0",payloadGenerated:"20200226171938",includeNonConnectedVehicles:"Y"}}),t=JSON.parse(e.body);return void 0===t.enrolledVehicleDetails?(this.vehicles=[],this.vehicles):(this.vehicles=t.enrolledVehicleDetails.map((e=>{const t=e.vehicleDetails,r={nickname:t.nickName,name:t.nickName,vin:t.vin,regDate:t.enrollmentDate,brandIndicator:t.brandIndicator,regId:t.regid,generation:t.modelYear>2016?"2":"1"};return new _(r,this)})),this.vehicles)}catch(e){throw H(e,"AmericanController.getVehicles")}}}var N,B;!function(e){e[e.UNPLUGED=0]="UNPLUGED",e[e.FAST=1]="FAST",e[e.PORTABLE=2]="PORTABLE",e[e.STATION=3]="STATION"}(N||(N={})),function(e){e[e.FAST=0]="FAST",e[e.SLOW=1]="SLOW"}(B||(B={}));const q=(e,t,r)=>{const i=[];for(let s=e;s<=t;s+=r)i.push(s);return i},j={EU:{start:14,end:30,step:.5},CA:{start:16,end:32,step:.5}},F=(e,t)=>{const{start:r,end:i,step:s}=j[e],n=q(r,i,s).indexOf(t);return`${("0x"+n.toString(16).substr(-4).toUpperCase()).split("x")[1].toUpperCase()}H`.padStart(3,"0")},z=(e,t)=>{const{start:r,end:i,step:s}=j[e];return q(r,i,s)[parseInt(t,16)]},J=e=>{const t=parseInt(e.substring(0,4)),r=parseInt(e.substring(4,6)),i=parseInt(e.substring(6,8));if(e.length<=8)return new Date(t,r-1,i);const s=parseInt(e.substring(8,10)),n=parseInt(e.substring(10,12)),o=parseInt(e.substring(12,14));return new Date(t,r-1,i,s,n,o)},W=(e,t)=>new Date(e.getTime()+6e4*t);var Y,G;!function(e){e[e.DAY=0]="DAY",e[e.MONTH=1]="MONTH",e[e.ALL=2]="ALL"}(Y||(Y={})),function(e){e[e.TOTAL=0]="TOTAL",e[e.AVERAGE=1]="AVERAGE",e[e.TODAY=2]="TODAY"}(G||(G={}));class K extends x{constructor(e,t){super(e,t),this.vehicleConfig=e,this.controller=t,this.region=I.EU,this.serverRates={max:-1,current:-1},C.debug(`EU Vehicle ${this.vehicleConfig.id} created`)}async start(e){const t=await this.controller.getVehicleHttpService();try{const r=this.updateRates(await t.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/temperature`,{body:{action:"start",hvacType:0,options:{defrost:e.defrost,heating1:e.windscreenHeating?1:0},tempCode:F(I.EU,e.temperature),unit:e.unit}}));return C.info(`Climate started for vehicle ${this.vehicleConfig.id}`),r.body}catch(e){throw H(e,"EuropeVehicle.start")}}async stop(){const e=await this.controller.getVehicleHttpService();try{const t=this.updateRates(await e.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/temperature`,{body:{action:"stop",hvacType:0,options:{defrost:!0,heating1:1},tempCode:"10H",unit:"C"}}));return C.info(`Climate stopped for vehicle ${this.vehicleConfig.id}`),t.body}catch(e){throw H(e,"EuropeVehicle.stop")}}async lock(){const e=await this.controller.getVehicleHttpService();try{return 200===this.updateRates(await e.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/door`,{body:{action:"close",deviceId:this.controller.session.deviceId}})).statusCode?(C.debug(`Vehicle ${this.vehicleConfig.id} locked`),"Lock successful"):"Something went wrong!"}catch(e){throw H(e,"EuropeVehicle.lock")}}async unlock(){const e=await this.controller.getVehicleHttpService();try{return 200===this.updateRates(await e.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/door`,{body:{action:"open",deviceId:this.controller.session.deviceId}})).statusCode?(C.debug(`Vehicle ${this.vehicleConfig.id} unlocked`),"Unlock successful"):"Something went wrong!"}catch(e){throw H(e,"EuropeVehicle.unlock")}}async fullStatus(e){const t={...D,...e},r=await this.controller.getVehicleHttpService();try{const e=this.updateRates(await r.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/status/latest`)).body.resMsg.vehicleStatusInfo;if(t.refresh){const t=this.updateRates(await r.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/status`));e.vehicleStatus=t.body.resMsg;const i=this.updateRates(await r.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/location`));e.vehicleLocation=i.body.resMsg.gpsDetail}return this._fullStatus=e,this._fullStatus}catch(e){throw H(e,"EuropeVehicle.fullStatus")}}async status(e){const t={...D,...e},r=await this.controller.getVehicleHttpService();try{const e=t.refresh?"":"/latest",i=this.updateRates(await r.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/status${e}`)),s=t.refresh?i.body.resMsg:i.body.resMsg.vehicleStatusInfo.vehicleStatus,n={chassis:{hoodOpen:s?.hoodOpen,trunkOpen:s?.trunkOpen,locked:s.doorLock,openDoors:{frontRight:!!s?.doorOpen?.frontRight,frontLeft:!!s?.doorOpen?.frontLeft,backLeft:!!s?.doorOpen?.backLeft,backRight:!!s?.doorOpen?.backRight},tirePressureWarningLamp:{rearLeft:!!s?.tirePressureLamp?.tirePressureLampRL,frontLeft:!!s?.tirePressureLamp?.tirePressureLampFL,frontRight:!!s?.tirePressureLamp?.tirePressureLampFR,rearRight:!!s?.tirePressureLamp?.tirePressureLampRR,all:!!s?.tirePressureLamp?.tirePressureWarningLampAll}},climate:{active:s?.airCtrlOn,steeringwheelHeat:!!s?.steerWheelHeat,sideMirrorHeat:!1,rearWindowHeat:!!s?.sideBackWindowHeat,defrost:s?.defrost,temperatureSetpoint:z(I.EU,s?.airTemp?.value),temperatureUnit:s?.airTemp?.unit},engine:{ignition:s.engine,accessory:s?.acc,rangeGas:s?.evStatus?.drvDistance[0]?.rangeByFuel?.gasModeRange?.value??s?.dte?.value,range:s?.evStatus?.drvDistance[0]?.rangeByFuel?.totalAvailableRange?.value,rangeEV:s?.evStatus?.drvDistance[0]?.rangeByFuel?.evModeRange?.value,plugedTo:s?.evStatus?.batteryPlugin??N.UNPLUGED,charging:s?.evStatus?.batteryCharge,estimatedCurrentChargeDuration:s?.evStatus?.remainTime2?.atc?.value,estimatedFastChargeDuration:s?.evStatus?.remainTime2?.etc1?.value,estimatedPortableChargeDuration:s?.evStatus?.remainTime2?.etc2?.value,estimatedStationChargeDuration:s?.evStatus?.remainTime2?.etc3?.value,batteryCharge12v:s?.battery?.batSoc,batteryChargeHV:s?.evStatus?.batteryStatus},lastupdate:s?.time?J(s?.time):null};return n.engine.range||(n.engine.rangeEV||n.engine.rangeGas)&&(n.engine.range=(n.engine.rangeEV??0)+(n.engine.rangeGas??0)),this._status=t.parsed?n:s,this._status}catch(e){throw H(e,"EuropeVehicle.status")}}async odometer(){const e=await this.controller.getVehicleHttpService();try{const t=this.updateRates(await e.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/status/latest`));return this._odometer=t.body.resMsg.vehicleStatusInfo.odometer,this._odometer}catch(e){throw H(e,"EuropeVehicle.odometer")}}async location(){const e=await this.controller.getVehicleHttpService();try{const t=this.updateRates(await e.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/location`)),r=t.body.resMsg?.gpsDetail??t.body.resMsg;return this._location={latitude:r?.coord?.lat,longitude:r?.coord?.lon,altitude:r?.coord?.alt,speed:{unit:r?.speed?.unit,value:r?.speed?.value},heading:r?.head},this._location}catch(e){throw H(e,"EuropeVehicle.location")}}async startCharge(){const e=await this.controller.getVehicleHttpService();try{if(200===this.updateRates(await e.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/charge`,{body:{action:"start",deviceId:this.controller.session.deviceId}})).statusCode)return C.debug(`Send start charge command to Vehicle ${this.vehicleConfig.id}`),"Start charge successful";throw"Something went wrong!"}catch(e){throw H(e,"EuropeVehicle.startCharge")}}async stopCharge(){const e=await this.controller.getVehicleHttpService();try{if(200===this.updateRates(await e.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/control/charge`,{body:{action:"stop",deviceId:this.controller.session.deviceId}})).statusCode)return C.debug(`Send stop charge command to Vehicle ${this.vehicleConfig.id}`),"Stop charge successful";throw"Something went wrong!"}catch(e){throw H(e,"EuropeVehicle.stopCharge")}}async monthlyReport(e={year:(new Date).getFullYear(),month:(new Date).getMonth()+1}){const t=await this.controller.getVehicleHttpService();try{const r=this.updateRates(await t.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/monthlyreport`,{body:{setRptMonth:Q(e)}})),i=r.body.resMsg?.monthlyReport;return i?{start:i.ifo?.mvrMonthStart,end:i.ifo?.mvrMonthEnd,breakdown:i.breakdown,driving:i.driving?{distance:i.driving?.runDistance,startCount:i.driving?.engineStartCount,durations:{idle:i.driving?.engineIdleTime,drive:i.driving?.engineOnTime}}:void 0,vehicleStatus:i.vehicleStatus?{tpms:i.vehicleStatus?.tpmsSupport?Boolean(i.vehicleStatus?.tpmsSupport):void 0,tirePressure:{all:"1"==i.vehicleStatus?.tirePressure?.tirePressureLampAll}}:void 0}:void 0}catch(e){throw H(e,"EuropeVehicle.monthyReports")}}async tripInfo(e={year:(new Date).getFullYear(),month:(new Date).getMonth()+1}){const t=await this.controller.getApiHttpService();try{const r=Boolean(e.day),i=this.updateRates(await t.post(`/api/v1/spa/vehicles/${this.vehicleConfig.id}/tripinfo`,{body:{setTripLatest:10,setTripMonth:r?void 0:Q(e),setTripDay:r?Z(e):void 0,tripPeriodType:r?1:0}}));if(!r){const e=i.body.resMsg;return{days:Array.isArray(e?.tripDayList)?e?.tripDayList.map((e=>({dayRaw:e.tripDayInMonth,date:e.tripDayInMonth?J(e.tripDayInMonth):void 0,tripsCount:e.tripCntDay}))):[],durations:{drive:e?.tripDrvTime,idle:e?.tripIdleTime},distance:e?.tripDist,speed:{avg:e?.tripAvgSpeed,max:e?.tripMaxSpeed}}}{const e=i.body.resMsg.dayTripList;if(e&&Array.isArray(e))return e.map((e=>({dayRaw:e.tripDay,tripsCount:e.dayTripCnt,distance:e.tripDist,durations:{drive:e.tripDrvTime,idle:e.tripIdleTime},speed:{avg:e.tripAvgSpeed,max:e.tripMaxSpeed},trips:Array.isArray(e.tripList)?e.tripList.map((t=>{const r=J(`${e.tripDay}${t.tripTime}`);return{timeRaw:t.tripTime,start:r,end:W(r,t.tripDrvTime),durations:{drive:t.tripDrvTime,idle:t.tripIdleTime},speed:{avg:t.tripAvgSpeed,max:t.tripMaxSpeed},distance:t.tripDist}})):[]})))}return}catch(e){throw H(e,"EuropeVehicle.history")}}async driveHistory(e=Y.DAY){const t=await this.controller.getApiHttpService();try{const r=await t.post(`/api/v1/spa/vehicles/${this.vehicleConfig.id}/drvhistory`,{body:{periodTarget:e}});return{cumulated:r.body.resMsg.drivingInfo.map((e=>({period:e.drivingPeriod,consumption:{total:e.totalPwrCsp,engine:e.motorPwrCsp,climate:e.climatePwrCsp,devices:e.eDPwrCsp,battery:e.batteryMgPwrCsp},regen:e.regenPwr,distance:e.calculativeOdo}))),history:r.body.resMsg.drivingInfoDetail.map((e=>({period:e.drivingPeriod,rawDate:e.drivingDate,date:e.drivingDate?J(e.drivingDate):void 0,consumption:{total:e.totalPwrCsp,engine:e.motorPwrCsp,climate:e.climatePwrCsp,devices:e.eDPwrCsp,battery:e.batteryMgPwrCsp},regen:e.regenPwr,distance:e.calculativeOdo})))}}catch(e){throw H(e,"EuropeVehicle.history")}}async getChargeTargets(){const e=await this.controller.getVehicleHttpService();try{const t=this.updateRates(await e.get(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/charge/target`)),r=t.body.resMsg?.targetSOClist;return r&&Array.isArray(r)?r.map((e=>({distance:e.drvDistance?.distanceType?.distanceValue,targetLevel:e.targetSOClevel,type:e.plugType}))):void 0}catch(e){throw H(e,"EuropeVehicle.getChargeTargets")}}async setChargeTargets(e){const t=await this.controller.getVehicleHttpService();if(!P.includes(e.fast)||!P.includes(e.slow))throw new U(`Charge target values are limited to ${P.join(", ")}`);try{this.updateRates(await t.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/charge/target`,{body:{targetSOClist:[{plugType:B.FAST,targetSOClevel:e.fast},{plugType:B.SLOW,targetSOClevel:e.slow}]}}))}catch(e){throw H(e,"EuropeVehicle.setChargeTargets")}}async setNavigation(e){const t=await this.controller.getVehicleHttpService();try{this.updateRates(await t.post(`/api/v2/spa/vehicles/${this.vehicleConfig.id}/location/routes`,{body:{deviceID:this.controller.session.deviceId,poiInfoList:e}}))}catch(e){throw H(e,"EuropeVehicle.setNavigation")}}updateRates(e){return e.headers?.["x-ratelimit-limit"]&&(this.serverRates.max=Number(e.headers?.["x-ratelimit-limit"]),this.serverRates.current=Number(e.headers?.["x-ratelimit-remaining"]),e.headers?.["x-ratelimit-reset"]&&(this.serverRates.reset=new Date(Number(`${e.headers?.["x-ratelimit-reset"]}000`))),this.serverRates.updatedAt=new Date),e}}function Q(e){return`${e.year}${e.month.toString().padStart(2,"0")}`}function Z(e){return e.day?`${Q(e)}${e.day.toString().padStart(2,"0")}`:Q(e)}async function X(e,t="en",r){const i=r??new o.CookieJar;return await d.default(e.endpoints.session,{cookieJar:i}),await d.default(e.endpoints.language,{method:"POST",body:`{"lang":"${t}"}`,cookieJar:i}),i}const ee={"User-Agent":"Mozilla/5.0 (iPhone; CPU iPhone OS 11_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 Mobile/15B92 Safari/604.1"},te=e=>e.catch((e=>"HTTPError"===e.name&&302===e.statusCode?e.response:Promise.reject(e)));class re{constructor(e,t){this.environment=e,this.language=t}get name(){return"EuropeanBrandAuthStrategy"}async login(e,t){const r=await X(this.environment,this.language,t?.cookieJar),{body:{userId:i,serviceId:n}}=await d.default(this.environment.endpoints.integration,{cookieJar:r,json:!0,headers:ee}),o=this.environment.brandAuthUrl({language:this.language,userId:i,serviceId:n}),a=u.default.parse(o,!0),{body:c}=await d.default(o,{cookieJar:r,headers:ee}),h=/action="([a-z0-9:/\-.?_=&;]*)"/gi.exec(c),l=h?.[1].replace(/&amp;/g,"&");if(!l)throw new Error("@EuropeanBrandAuthStrategy.login: cannot found the auth url from the form.");const g=new s.URLSearchParams;g.append("username",e.username),g.append("password",e.password),g.append("credentialId",""),g.append("rememberMe","on");const{headers:{location:p},body:f}=await te(d.default.post(l,{cookieJar:r,body:g.toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",...ee}}));if(!p){const e=/<span class="kc-feedback-text">(.+)<\/span>/gm.exec(f);if(e)throw new Error(`@EuropeanBrandAuthStrategy.login: Authentication failed with message : ${e[1]}`);throw new Error("@EuropeanBrandAuthStrategy.login: Authentication failed, cannot retrieve error message")}const v=await d.default(p,{cookieJar:r,headers:ee});let m=v.url,w=v.body;if(!m)throw new Error(`@EuropeanBrandAuthStrategy.login: after login redirection got stuck : ${w}`);if(m.includes("login-actions/required-action")){const e=/action="([a-z0-9:/\-.?_=&;]*)"/gi.exec(w),t=/name="code" value="(.*)"/gi.exec(w);if(!e)throw new Error("@EuropeanBrandAuthStrategy.login: Cannot find login-actions url.");if(!t)throw new Error("@EuropeanBrandAuthStrategy.login: Cannot find login-actions code.");const i=e[1].startsWith("/")?`${a.protocol}//${a.host}${e[1]}`:e[1],n=new s.URLSearchParams;n.append("code",t[1]),n.append("accept","");const{headers:{location:o},body:c}=await te(d.default.post(i,{cookieJar:r,body:n.toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",...ee}}));if(!o){const e=/<span class="kc-feedback-text">(.+)<\/span>/gm.exec(c);if(e)throw new Error(`@EuropeanBrandAuthStrategy.login: Authentication action failed with message : ${e[1]}`);throw new Error("@EuropeanBrandAuthStrategy.login: Authentication action failed, cannot retrieve error message")}const h=await d.default(o,{cookieJar:r,headers:ee});m=h.url,w=h.body}const{intUserId:y}=u.default.parse(m,!0).query;if(!y)throw C.warn("@EuropeanBrandAuthStrategy.login: Cannot find the argument userId, please use the following url and connect from a browser."),C.warn(this.environment.endpoints.session),new Error(`@EuropeanBrandAuthStrategy.login: Cannot find the argument userId in ${m}.\n\tPlease use the following url and connect from a browser, it may probably ask you to "Change your password" or "Accept the new conditions"\n\tOnce done, try again.\n\t${this.environment.endpoints.session}`);const{body:b,statusCode:k}=await d.default.post(this.environment.endpoints.silentSignIn,{cookieJar:r,body:{intUserId:y},json:!0,headers:{...ee,"ccsp-service-id":this.environment.clientId}});if(!b.redirectUrl)throw new Error(`@EuropeanBrandAuthStrategy.login: silent sign In didn't work, could not retrieve auth code. status: ${k}, body: ${JSON.stringify(b)}`);const{code:S}=u.default.parse(b.redirectUrl,!0).query;if(!S)throw new Error(`@EuropeanBrandAuthStrategy.login: Cannot find the argument code in ${b.redirectUrl}.`);return{code:S,cookies:r}}}class ie{constructor(e,t){this.environment=e,this.language=t}get name(){return"EuropeanLegacyAuthStrategy"}async login(e,t){const r=await X(this.environment,this.language,t?.cookieJar),{body:i,statusCode:s}=await d.default(this.environment.endpoints.login,{method:"POST",json:!0,body:{email:e.username,password:e.password},cookieJar:r});if(!i.redirectUrl)throw new Error(`@EuropeanLegacyAuthStrategy.login: sign In didn't work, could not retrieve auth code. status: ${s}, body: ${JSON.stringify(i)}`);const{code:n}=u.default.parse(i.redirectUrl,!0).query;if(!n)throw new Error("@EuropeanLegacyAuthStrategy.login: AuthCode was not found, you probably need to migrate your account.");return{code:n,cookies:r}}}class se extends R{constructor(e){if(super(e),this.session={accessToken:void 0,refreshToken:void 0,controlToken:void 0,deviceId:V(),tokenExpiresAt:0,controlTokenExpiresAt:0},this.vehicles=[],this.userConfig.language=e.language??"en",!A.includes(this.userConfig.language))throw new Error(`The language code ${this.userConfig.language} is not managed. Only ${A.join(", ")} are.`);this.session.deviceId=V(),this._environment=L(e.brand),this.authStrategies={main:new re(this._environment,this.userConfig.language),fallback:new ie(this._environment,this.userConfig.language)},C.debug("EU Controller created")}get environment(){return this._environment}async refreshAccessToken(){const e=Math.floor(Date.now()/1e3-this.session.tokenExpiresAt)>=-10;if(!this.session.refreshToken)return C.debug("Need refresh token to refresh access token. Use login()"),"Need refresh token to refresh access token. Use login()";if(!e)return C.debug("Token not expired, no need to refresh"),"Token not expired, no need to refresh";const t=new s.URLSearchParams;t.append("grant_type","refresh_token"),t.append("redirect_uri","https://www.getpostman.com/oauth2/callback"),t.append("refresh_token",this.session.refreshToken);try{const e=await d.default(this.environment.endpoints.token,{method:"POST",headers:{Authorization:this.environment.basicToken,"Content-Type":"application/x-www-form-urlencoded",Host:this.environment.host,Connection:"Keep-Alive","Accept-Encoding":"gzip","User-Agent":"okhttp/3.10.0"},body:t.toString(),throwHttpErrors:!1});if(200!==e.statusCode)return C.debug(`Refresh token failed: ${e.body}`),`Refresh token failed: ${e.body}`;const r=JSON.parse(e.body);this.session.accessToken="Bearer "+r.access_token,this.session.tokenExpiresAt=Math.floor(Date.now()/1e3+r.expires_in)}catch(e){throw H(e,"EuropeController.refreshAccessToken")}return C.debug("Token refreshed"),"Token refreshed"}async enterPin(){if(""===this.session.accessToken)throw"Token not set";try{const e=await d.default(`${this.environment.baseUrl}/api/v1/user/pin`,{method:"PUT",headers:{Authorization:this.session.accessToken,"Content-Type":"application/json"},body:{deviceId:this.session.deviceId,pin:this.userConfig.pin},json:!0});return this.session.controlToken="Bearer "+e.body.controlToken,this.session.controlTokenExpiresAt=Math.floor(Date.now()/1e3+e.body.expiresTime),"PIN entered OK, The pin is valid for 10 minutes"}catch(e){throw H(e,"EuropeController.pin")}}async login(){try{if(!this.userConfig.password||!this.userConfig.username)throw new Error("@EuropeController.login: username and password must be defined.");let e=null;try{C.debug(`@EuropeController.login: Trying to sign in with ${this.authStrategies.main.name}`),e=await this.authStrategies.main.login({password:this.userConfig.password,username:this.userConfig.username})}catch(t){C.error(`@EuropeController.login: sign in with ${this.authStrategies.main.name} failed with error ${t.toString()}`),C.debug(`@EuropeController.login: Trying to sign in with ${this.authStrategies.fallback.name}`),e=await this.authStrategies.fallback.login({password:this.userConfig.password,username:this.userConfig.username})}C.debug("@EuropeController.login: Authenticated properly with user and password");const t=await g.register(this.environment.GCMSenderID),r=await d.default(`${this.environment.baseUrl}/api/v1/spa/notifications/register`,{method:"POST",headers:{"ccsp-service-id":this.environment.clientId,"Content-Type":"application/json;charset=UTF-8",Host:this.environment.host,Connection:"Keep-Alive","Accept-Encoding":"gzip","User-Agent":"okhttp/3.10.0","ccsp-application-id":this.environment.appId,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},body:{pushRegId:t.gcm.token,pushType:"GCM",uuid:this.session.deviceId},json:!0});r&&(this.session.deviceId=r.body.resMsg.deviceId),C.debug("@EuropeController.login: Device registered");const i=new s.URLSearchParams;i.append("grant_type","authorization_code"),i.append("redirect_uri",this.environment.endpoints.redirectUri),i.append("code",e.code);const n=await d.default(this.environment.endpoints.token,{method:"POST",headers:{Authorization:this.environment.basicToken,"Content-Type":"application/x-www-form-urlencoded",Host:this.environment.host,Connection:"Keep-Alive","Accept-Encoding":"gzip","User-Agent":"okhttp/3.10.0",grant_type:"authorization_code","ccsp-application-id":this.environment.appId,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},body:i.toString(),cookieJar:e.cookies});if(200!==n.statusCode)throw new Error(`@EuropeController.login: Could not manage to get token: ${n.body}`);if(n){const e=JSON.parse(n.body);this.session.accessToken=`Bearer ${e.access_token}`,this.session.refreshToken=e.refresh_token,this.session.tokenExpiresAt=Math.floor(Date.now()/1e3+e.expires_in)}return C.debug("@EuropeController.login: Session defined properly"),"Login success"}catch(e){throw H(e,"EuropeController.login")}}async logout(){return"OK"}async getVehicles(){if(void 0===this.session.accessToken)throw"Token not set";try{const e=await d.default(`${this.environment.baseUrl}/api/v1/spa/vehicles`,{method:"GET",headers:{...this.defaultHeaders,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},json:!0});this.vehicles=await(async(e,t)=>{const r=[];for(let i=0;i<e.length;i++)r.push(await t(e[i],i,e));return r})(e.body.resMsg.vehicles,(async e=>{const t=(await d.default(`${this.environment.baseUrl}/api/v1/spa/vehicles/${e.vehicleId}/profile`,{method:"GET",headers:{...this.defaultHeaders,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},json:!0})).body.resMsg,r={nickname:e.nickname,name:e.vehicleName,regDate:e.regDate,brandIndicator:"H",id:e.vehicleId,vin:t.vinInfo[0].basic.vin,generation:t.vinInfo[0].basic.modelYear};return C.debug(`@EuropeController.getVehicles: Added vehicle ${r.id}`),new K(r,this)}))}catch(e){throw H(e,"EuropeController.getVehicles")}return this.vehicles}async checkControlToken(){await this.refreshAccessToken(),void 0!==this.session?.controlTokenExpiresAt&&(!this.session.controlToken||Date.now()/1e3>this.session.controlTokenExpiresAt)&&await this.enterPin()}async getVehicleHttpService(){return await this.checkControlToken(),d.default.extend({baseUrl:this.environment.baseUrl,headers:{...this.defaultHeaders,Authorization:this.session.controlToken,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},json:!0})}async getApiHttpService(){return await this.refreshAccessToken(),d.default.extend({baseUrl:this.environment.baseUrl,headers:{...this.defaultHeaders,Stamp:await this.environment.stamp(this.userConfig.stampsFile)},json:!0})}get defaultHeaders(){return{Authorization:this.session.accessToken,offset:((new Date).getTimezoneOffset()/60).toFixed(2),"ccsp-device-id":this.session.deviceId,"ccsp-application-id":this.environment.appId,"Content-Type":"application/json"}}}class ne extends x{constructor(e,t){super(e,t),this.vehicleConfig=e,this.controller=t,this.region=I.CA,this.timeOffset=-(new Date).getTimezoneOffset()/60,C.debug(`CA Vehicle ${this.vehicleConfig.id} created`)}fullStatus(){throw new Error("Method not implemented.")}async status(e){const t={...D,...e};C.debug("Begin status request, polling car: "+e.refresh);try{const e=t.refresh?this.controller.environment.endpoints.remoteStatus:this.controller.environment.endpoints.status,r=await this.request(e,{}),i=r.result?.status;if(r?.error)throw r?.error?.errorDesc;C.debug(i);const s={chassis:{hoodOpen:i?.hoodOpen,trunkOpen:i?.trunkOpen,locked:i?.doorLock,openDoors:{frontRight:!!i?.doorOpen?.frontRight,frontLeft:!!i?.doorOpen?.frontLeft,backLeft:!!i?.doorOpen?.backLeft,backRight:!!i?.doorOpen?.backRight},tirePressureWarningLamp:{rearLeft:!!i?.tirePressureLamp?.tirePressureWarningLampRearLeft,frontLeft:!!i?.tirePressureLamp?.tirePressureWarningLampFrontLeft,frontRight:!!i?.tirePressureLamp?.tirePressureWarningLampFrontRight,rearRight:!!i?.tirePressureLamp?.tirePressureWarningLampRearRight,all:!!i?.tirePressureLamp?.tirePressureWarningLampAll}},climate:{active:i?.airCtrlOn,steeringwheelHeat:!!i?.steerWheelHeat,sideMirrorHeat:!1,rearWindowHeat:!!i?.sideBackWindowHeat,defrost:i?.defrost,temperatureSetpoint:i?.airTemp?.value,temperatureUnit:i?.airTemp?.unit},engine:{ignition:i?.engine,accessory:i?.acc,range:i?.dte?.value,charging:i?.evStatus?.batteryCharge,batteryCharge12v:i?.battery?.batSoc,batteryChargeHV:i?.evStatus?.batteryStatus},lastupdate:i?.time?J(i?.lastStatusDate):null};return this._status=t.parsed?s:i,this._status}catch(e){throw e.message}}async lock(){C.debug("Begin lock request");try{const e=await this.getPreAuth();return await this.request(this.controller.environment.endpoints.lock,{},{pAuth:e}),"Lock successful"}catch(e){throw e.message}}async unlock(){C.debug("Begin unlock request");try{const e=await this.getPreAuth();return await this.request(this.controller.environment.endpoints.unlock,{},{pAuth:e}),"Unlock successful"}catch(e){throw e.message}}async start(e){C.debug("Begin startClimate request");try{const t={hvacInfo:{airCtrl:e.airCtrl||e.defrost?1:0,defrost:e.defrost??!1,heating1:e.heating1?1:0}},r=e.airTempvalue;if(null!=r)t.hvacInfo.airTemp={value:F(I.CA,r),unit:0,hvacTempType:1};else if(e.airCtrl||e.defrost)throw"air temperature should be specified";const i=await this.getPreAuth(),s=await this.request(this.controller.environment.endpoints.start,t,{pAuth:i});return C.debug(s),s.responseHeader&&0===s.responseHeader.responseCode?"Vehicle started!":"Failed to start vehicle"}catch(e){throw e.message}}async stop(){C.debug("Begin stop request");try{const e=await this.getPreAuth();return await this.request(this.controller.environment.endpoints.stop,{pAuth:e})}catch(e){throw"error: "+e}}async lights(e=!1){C.debug("Begin lights request with horn "+e);try{const t=await this.getPreAuth();return await this.request(this.controller.environment.endpoints.hornlight,{horn:e},{pAuth:t})}catch(e){throw"error: "+e}}async stopCharge(){C.debug("Begin stopCharge");const{stopCharge:e}=this.controller.environment.endpoints;try{const t=await this.getPreAuth();return await this.request(e,{pin:this.controller.userConfig.pin,pAuth:t})}catch(e){throw"error: "+e}}async startCharge(){C.debug("Begin startCharge");const{startCharge:e}=this.controller.environment.endpoints;try{const t=await this.getPreAuth();return await this.request(e,{pin:this.controller.userConfig.pin,pAuth:t})}catch(e){throw"error: "+e}}async setChargeTargets(e){if(C.debug("Begin setChargeTarget"),!P.includes(e.fast)||!P.includes(e.slow))throw new U(`Charge target values are limited to ${P.join(", ")}`);const{setChargeTarget:t}=this.controller.environment.endpoints;try{const r=await this.getPreAuth();return await this.request(t,{pin:this.controller.userConfig.pin,pAuth:r,tsoc:[{plugType:B.FAST,level:e.fast},{plugType:B.SLOW,level:e.slow}]})}catch(e){throw"error: "+e}}odometer(){throw new Error("Method not implemented.")}async location(){C.debug("Begin locate request");try{const e=await this.getPreAuth(),t=await this.request(this.controller.environment.endpoints.locate,{},{pAuth:e});return this._location=t.result,this._location}catch(e){throw"error: "+e}}async getPreAuth(){C.info("Begin pre-authentication");try{return(await this.request(this.controller.environment.endpoints.verifyPin,{})).result.pAuth}catch(e){throw"error: "+e}}async request(e,t,r={}){C.debug(`[${e}] ${JSON.stringify(r)} ${JSON.stringify(t)}`),await this.controller.refreshAccessToken();const i={method:"POST",json:!0,throwHttpErrors:!1,headers:{from:this.controller.environment.origin,language:1,offset:this.timeOffset,accessToken:this.controller.session.accessToken,vehicleId:this.vehicleConfig.id,...r},body:{pin:this.userConfig.pin,...t}};try{const t=await d.default(e,i);return 0!=t.body.responseHeader.responseCode?t.body.responseHeader.responseDesc:t.body}catch(e){throw"error: "+e}}}class oe extends R{constructor(e){super(e),this.vehicles=[],this.timeOffset=-(new Date).getTimezoneOffset()/60,C.debug("CA Controller created"),this._environment=T(e.brand)}get environment(){return this._environment}async refreshAccessToken(){const e=Math.floor(Date.now()/1e3-this.session.tokenExpiresAt)>=-10;return C.debug("shouldRefreshToken: "+e.toString()),this.session.refreshToken&&e?(await this.login(),C.debug("Token refreshed"),"Token refreshed"):(C.debug("Token not expired, no need to refresh"),"Token not expired, no need to refresh")}async login(){C.info("Begin login request");try{const e=await this.request(this.environment.endpoints.login,{loginId:this.userConfig.username,password:this.userConfig.password});return C.debug(e.result),this.session.accessToken=e.result.accessToken,this.session.refreshToken=e.result.refreshToken,this.session.tokenExpiresAt=Math.floor(+new Date/1e3+e.result.expireIn),"login good"}catch(e){return"error: "+e}}async logout(){return"OK"}async getVehicles(){C.info("Begin getVehicleList request");try{const e=(await this.request(this.environment.endpoints.vehicleList,{})).result;return void 0===e.vehicles?(this.vehicles=[],this.vehicles):(e.vehicles.forEach((e=>{const t={nickname:e.nickName,name:e.nickName,vin:e.vin,regDate:e.enrollmentDate,brandIndicator:e.brandIndicator,regId:e.regid,id:e.vehicleId,generation:e.genType};this.vehicles.push(new ne(t,this))})),this.vehicles)}catch(e){return C.debug(e),this.vehicles}}async request(e,t,r={}){C.debug(`[${e}] ${JSON.stringify(r)} ${JSON.stringify(t)}`);try{const i=await d.default(e,{method:"POST",json:!0,headers:{from:this.environment.origin,language:1,offset:this.timeOffset,accessToken:this.session.accessToken,...r},body:{...t}});if(0!=i.body.responseHeader.responseCode)throw i.body.responseHeader.responseDesc;return i.body}catch(e){throw H(e,"CanadianController")}}}const ae={username:"",password:"",region:I.US,brand:"hyundai",autoLogin:!0,pin:"1234",vin:"",vehicleId:void 0};class ce extends a.EventEmitter{constructor(e){switch(super(),this.vehicles=[],this.config={...ae,...e},e.region){case I.EU:this.controller=new se(this.config);break;case I.US:this.controller=new M(this.config);break;case I.CA:this.controller=new oe(this.config);break;default:throw new Error("Your region is not supported yet.")}void 0===e.autoLogin&&(this.config.autoLogin=!0),this.onInit()}on(e,t){return super.on(e,t)}onInit(){this.config.autoLogin&&(C.debug("Bluelinky is logging in automatically, to disable use autoLogin: false"),this.login())}async login(){try{const e=await this.controller.login();return this.vehicles=await this.getVehicles(),C.debug(`Found ${this.vehicles.length} on the account`),this.emit("ready",this.vehicles),e}catch(e){return this.emit("error",e),e}}async getVehicles(){return await this.controller.getVehicles()||[]}getVehicle(e){try{const t=this.vehicles.find((t=>t.vin()===e||t.id()===e));if(!t&&this.vehicles.length>0)throw new Error(`Could not find vehicle with id: ${e}`);return t}catch(t){throw new Error(`Vehicle not found: ${e}!`)}}async refreshAccessToken(){return this.controller.refreshAccessToken()}async logout(){return this.controller.logout()}getSession(){return this.controller.session}get cachedVehicles(){return this.vehicles??[]}}module.exports=ce;