diff --git a/pypowerwall/__init__.py b/pypowerwall/__init__.py index d06c4fc..f92840e 100644 --- a/pypowerwall/__init__.py +++ b/pypowerwall/__init__.py @@ -130,7 +130,8 @@ class Powerwall(object): def __init__(self, host="", password="", email="nobody@nowhere.com", timezone="America/Los_Angeles", pwcacheexpire=5, timeout=5, poolmaxsize=10, cloudmode=False, siteid=None, authpath="", authmode="cookie", cachefile=".powerwall", - fleetapi=False, auto_select=False, retry_modes=False, gw_pwd=None): + fleetapi=False, auto_select=False, retry_modes=False, gw_pwd=None, + tedapi_auth_mode="basic"): """ Represents a Tesla Energy Gateway Powerwall device. @@ -176,6 +177,7 @@ def __init__(self, host="", password="", email="nobody@nowhere.com", self.retry_modes = retry_modes self.mode = "unknown" self.gw_pwd = gw_pwd # TEG Gateway password for TEDAPI mode + self.tedapi_auth_mode = tedapi_auth_mode # basic or bearer for TEDAPI self.tedapi = False self.tedapi_mode = "off" # off, full, hybrid @@ -250,7 +252,8 @@ def connect(self, retry=False) -> bool: self.client = PyPowerwallTEDAPI(self.gw_pwd, pwcacheexpire=self.pwcacheexpire, pwconfigexpire=self.pwcacheexpire, timeout=self.timeout, host=self.host, - poolmaxsize=self.poolmaxsize) + poolmaxsize=self.poolmaxsize, + auth_mode=self.tedapi_auth_mode) else: self.tedapi_mode = "hybrid" self.client = PyPowerwallLocal(self.host, self.password, self.email, self.timezone, self.timeout, diff --git a/pypowerwall/tedapi/__init__.py b/pypowerwall/tedapi/__init__.py index 3ce6d7a..c896c69 100644 --- a/pypowerwall/tedapi/__init__.py +++ b/pypowerwall/tedapi/__init__.py @@ -33,6 +33,12 @@ get_pw3_vitals() - Get the Powerwall 3 Vitals Information get_device_controller() - Get the Powerwall Device Controller Status get_fan_speed() - Get the fan speeds in RPM + get_ieee20305() - Get IEEE 2030.5 smart inverter data + get_grid_codes() - Get available grid codes + get_grid_code_details(grid_code, point_names) - Get grid code details + get_eaton_sbii() - Get Eaton SBII transfer switch data + get_pinv_self_test() - Get inverter self-test results + get_protection_trip_test() - Get protection trip test results Note: This module requires access to the Powerwall Gateway. You can add a route to @@ -85,6 +91,243 @@ log.debug('%s version %s', __name__, __version__) log.debug('Python %s on %s', sys.version, sys.platform) +# --- TEDAPI Query and Payload Constants --- +# All queries below are from the Tesla One APK. Each query+signature pair is +# ECDSA-P521 signed (SHA256 hash, ASN.1/DER format). DO NOT modify query +# whitespace - it invalidates the signature. +# The APK uses inline filters (no variablesJson needed), so all queries use "{}". +# +# The ECDSA signature is over the serialized SignedGraphQLQuery protobuf bytes +# (NOT just the query text). Use _build_signed_query() to construct these bytes. + + +def _build_signed_query(query_text: str) -> bytes: + """Build serialized SignedGraphQLQuery bytes from query text. + + The gateway expects field 2 (query) of GraphQLAPIQueryRequest to contain + serialized SignedGraphQLQuery{version:2, query:queryText} bytes. The ECDSA + signature is computed over these exact bytes. + """ + sq = tedapi_pb2.SignedGraphQLQuery() + sq.version = 2 + sq.query = query_text.encode('utf-8') + return sq.SerializeToString() + +# ECDSA-SHA256 signature for DEVICE_CONTROLLER query +SEND_CODE_DEVICE_CONTROLLER = bytes.fromhex( + '308187024159f83efaa7736c038110b06dbb125618' + 'ca4e0c6c3dbbd78a32aaeb6b8c11c848f10a0d0478' + 'b1a5f66fe9a565a20637dcf0bc97a93c7eb5790c27' + '42db663ee74393024201685dd07b85b0f888bc284e' + 'dd3614cb6fdb969b52c622d11b1014207994d52b58' + '5e0cf9b110d581780b16106db6234d577e809119fc' + '62577ea62bf82c6459ec2733' +) + +# GraphQL query for get_status() and get_device_controller() +# NOTE: This query MUST remain byte-exact (minified) - the signature above is an +# ECDSA-SHA256 signature of this exact byte sequence. Any change invalidates it. +QUERY_DEVICE_CONTROLLER = ( + 'query DeviceControllerQuery{control{systemStatus{nominalFullPackEnergyWh nominalEnergyRemainingWh}is' + 'landing{customerIslandMode contactorClosed microGridOK gridOK disableReasons}meterAggregates{locatio' + 'n realPowerW}alerts{active}siteShutdown{isShutDown reasons}batteryBlocks{din disableReasons}pvInvert' + 'ers{din disableReasons}protectionTripTests{isRunning}}system{time supportMode{remoteService{isEnable' + 'd expiryTime sessionId}}sitemanagerStatus{isRunning}}neurio{isDetectingWiredMeters readings{firmware' + 'Version serial dataRead{voltageV realPowerW reactivePowerVAR currentA}timestamp}pairings{serial shor' + 'tId status errors macAddress hostname isWired modbusPort modbusId lastUpdateTimestamp}}teslaRemoteMe' + 'ter{meters{din reading{timestamp firmwareVersion rssiDb ctReadings{voltageV realPowerW reactivePower' + 'VAR energyExportedWs energyImportedWs currentA}}firmwareUpdate{updating numSteps currentStep current' + 'StepProgress progress}}detectedWired{din serialPort}}pw3Can{firmwareUpdate{isUpdating progress{updat' + 'ing numSteps currentStep currentStepProgress progress}}enumeration{inProgress}}esCan{bus{PVAC{packag' + 'ePartNumber packageSerialNumber subPackagePartNumber subPackageSerialNumber PVAC_Status{isMIA PVAC_P' + 'out PVAC_State PVAC_Vout PVAC_Fout}PVAC_ControlMeasurements{PVAC_FanSelfTestState}PVAC_InfoMsg{PVAC_' + 'appGitHash}PVAC_Logging{isMIA PVAC_PVCurrent_A PVAC_PVCurrent_B PVAC_PVCurrent_C PVAC_PVCurrent_D PV' + 'AC_PVMeasuredVoltage_A PVAC_PVMeasuredVoltage_B PVAC_PVMeasuredVoltage_C PVAC_PVMeasuredVoltage_D PV' + 'AC_VL1Ground PVAC_VL2Ground PVAC_Fan_Speed_Actual_RPM PVAC_Fan_Speed_Target_RPM}alerts{isComplete is' + 'MIA active}}PINV{PINV_Status{isMIA PINV_Fout PINV_Pout PINV_Vout PINV_State PINV_GridState}PINV_AcMe' + 'asurements{isMIA PINV_VSplit1 PINV_VSplit2}PINV_PowerCapability{isComplete isMIA PINV_Pnom}alerts{is' + 'Complete isMIA active}}PVS{PVS_Status{isMIA PVS_State PVS_vLL PVS_StringA_Connected PVS_StringB_Conn' + 'ected PVS_StringC_Connected PVS_StringD_Connected PVS_SelfTestState}PVS_Logging{PVS_numStringsLockou' + 'tBits PVS_sbsComplete}alerts{isComplete isMIA active}}THC{packagePartNumber packageSerialNumber THC_' + 'InfoMsg{isComplete isMIA THC_appGitHash}THC_Logging{THC_LOG_PW_2_0_EnableLineState}alerts{isComplete' + ' isMIA active}}POD{POD_EnergyStatus{isMIA POD_nom_energy_remaining POD_nom_full_pack_energy}POD_Info' + 'Msg{POD_appGitHash}alerts{isComplete isMIA active}}SYNC{packagePartNumber packageSerialNumber subPac' + 'kagePartNumber subPackageSerialNumber SYNC_InfoMsg{isMIA SYNC_appGitHash SYNC_assemblyId}METER_X_AcM' + 'easurements{isMIA isComplete METER_X_CTA_InstRealPower METER_X_CTA_InstReactivePower METER_X_CTA_I M' + 'ETER_X_VL1N METER_X_CTB_InstRealPower METER_X_CTB_InstReactivePower METER_X_CTB_I METER_X_VL2N METER' + '_X_CTC_InstRealPower METER_X_CTC_InstReactivePower METER_X_CTC_I METER_X_VL3N}METER_Y_AcMeasurements' + '{isMIA isComplete METER_Y_CTA_InstRealPower METER_Y_CTA_InstReactivePower METER_Y_CTA_I METER_Y_VL1N' + ' METER_Y_CTB_InstRealPower METER_Y_CTB_InstReactivePower METER_Y_CTB_I METER_Y_VL2N METER_Y_CTC_Inst' + 'RealPower METER_Y_CTC_InstReactivePower METER_Y_CTC_I METER_Y_VL3N}alerts{isComplete isMIA active}}I' + 'SLANDER{ISLAND_GridConnection{ISLAND_GridConnected isComplete}ISLAND_AcMeasurements{ISLAND_VL1N_Main' + ' ISLAND_FreqL1_Main ISLAND_VL2N_Main ISLAND_FreqL2_Main ISLAND_VL3N_Main ISLAND_FreqL3_Main ISLAND_V' + 'L1N_Load ISLAND_FreqL1_Load ISLAND_VL2N_Load ISLAND_FreqL2_Load ISLAND_VL3N_Load ISLAND_FreqL3_Load ' + 'ISLAND_GridState isComplete isMIA}}}enumeration{inProgress numACPW numPVI}firmwareUpdate{isUpdating ' + 'powerwalls{updating numSteps currentStep currentStepProgress progress}msa{updating numSteps currentS' + 'tep currentStepProgress progress}msa1{updating numSteps currentStep currentStepProgress progress}syn' + 'c{updating numSteps currentStep currentStepProgress progress}pvInverters{updating numSteps currentSt' + 'ep currentStepProgress progress}}phaseDetection{inProgress lastUpdateTimestamp powerwalls{din progre' + 'ss phase}}}components{msa:components(filter:{types:[TEMSA]}){partNumber serialNumber signals(names:[' + '"MSA_pcbaId" "MSA_usageId" "MSA_appGitHash" "MSA_HeatingRateOccurred" "METER_Z_CTA_InstRealPower" "M' + 'ETER_Z_CTA_InstReactivePower" "METER_Z_CTA_I" "METER_Z_VL1G" "METER_Z_CTB_InstRealPower" "METER_Z_CT' + 'B_InstReactivePower" "METER_Z_CTB_I" "METER_Z_VL2G"]){name value textValue boolValue}activeAlerts{na' + 'me}}}}' +) + +# ECDSA-SHA256 signature for COMPONENTS query +SEND_CODE_COMPONENTS = bytes.fromhex( + '3081870241786800ad176df8c4ab2835d2f0d31efc' + '901cda3c6bb26a0dcb0fa9d7bc7e11e31981c1867b' + '2e8d770c69f9a7d796cb2668570400a71d0f0802b1' + 'b1e2aa5a46406f024200f2c014dcb5585c73cb90c5' + 'e49944033f7ab3255b0b68b74e68719bb1fc192246' + 'a6dd8d71a0f138ab698357ba5c05bd1460c680a844' + 'd5870e458629c657b032f0a3' +) + +# GraphQL query for get_components/get_pw3_vitals/get_battery_block +# NOTE: This query MUST remain byte-exact (minified) - the signature above is an +# ECDSA-SHA256 signature of this exact byte sequence. Any change invalidates it. +QUERY_COMPONENTS = ( + 'query ComponentsQuery{pw3Can{firmwareUpdate{isUpdating progress{updating numSteps currentStep curren' + 'tStepProgress progress}}}components{pws:components(filter:{types:[PW3SAF]}){signals(names:["PWS_asse' + 'mblyId" "PWS_SelfTest" "PWS_PeImpTestState" "PWS_PvIsoTestState" "PWS_RelaySelfTest_State" "PWS_MciT' + 'estState" "PWS_ProdSwitch_State" "PWS_RSD_State" "PWS_RSDSelfTest_State" "PWS_RSDSelfTest_Result" "P' + 'WS_ExtSwitch_State" "PWS_reversePolarityStrings"]){name value textValue boolValue}activeAlerts{name}' + '}pch:components(filter:{types:[PCH]}){signals(names:["PCH_appGitHash" "PCH_State" "PCH_AcFrequency" ' + '"PCH_AcMode" "PCH_AcRealPowerAB" "PCH_AcVoltageAB" "PCH_AcVoltageAN" "PCH_AcVoltageBN" "PCH_BatteryP' + 'ower" "PCH_DcdcState_A" "PCH_DcdcState_B" "PCH_PvState_A" "PCH_PvState_B" "PCH_PvState_C" "PCH_PvSta' + 'te_D" "PCH_PvState_E" "PCH_PvState_F" "PCH_PvVoltageA" "PCH_PvVoltageB" "PCH_PvVoltageC" "PCH_PvVolt' + 'ageD" "PCH_PvVoltageE" "PCH_PvVoltageF" "PCH_PvCurrentA" "PCH_PvCurrentB" "PCH_PvCurrentC" "PCH_PvCu' + 'rrentD" "PCH_PvCurrentE" "PCH_PvCurrentF" "PCH_SlowPvPowerSum"]){name value textValue boolValue}acti' + 'veAlerts{name}}bms:components(filter:{types:[PW3BMS]}){signals(names:["BMS_nominalEnergyRemaining" "' + 'BMS_nominalFullPackEnergy"]){name value textValue boolValue}activeAlerts{name}}hvp:components(filter' + ':{types:[PW3HVP]}){partNumber serialNumber signals(names:["HVP_State" "HVP_SafetyBiDisconnectState"]' + '){name value textValue boolValue}activeAlerts{name}}baggr:components(filter:{types:[BAGGR]}){signals' + '(names:["BAGGR_State" "BAGGR_OperationRequest" "BAGGR_NumBatteriesConnected" "BAGGR_NumBatteriesPres' + 'ent" "BAGGR_NumBatteriesExpected" "BAGGR_LOG_BattConnectionStatus0" "BAGGR_LOG_BattConnectionStatus1' + '" "BAGGR_LOG_BattConnectionStatus2" "BAGGR_LOG_BattConnectionStatus3" "BAGGR_ExpectedEnergyRemaining' + '" "BAGGR_ExpectedFullPackEnergy"]){name value textValue boolValue}activeAlerts{name}}}}' +) + +# ECDSA-SHA256 signature for IEEE20305 query +SEND_CODE_IEEE20305 = bytes.fromhex( + '308188024201ed8814348e8f9df393fc149659358b' + '12aa30fead0460745e0ce1ce7d9e4a6251b6b1c977' + 'dbfe50a86542e67434b6425fb6046038cf5b065ed3' + '07698da6fb9990d1024200c94d155840700889e4f5' + 'bec765b0d37cbba18fa5d33361e85e11d1d0965d0a' + '6825684dfe72ee754ec64278aeeefe240cf8ab89e1' + '1906182e6353042e9e14a789d8' +) + +# GraphQL query for IEEE 2030.5 data +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_IEEE20305 = ( + 'query IEEE20305Query{ieee20305{longFormDeviceID polledResources{url name pollRateSeconds lastPolledT' + 'imestamp}controls{defaultControl{mRID setGradW opModEnergize opModMaxLimW opModImpLimW opModExpLimW ' + 'opModGenLimW opModLoadLimW}activeControls{opModEnergize opModMaxLimW opModImpLimW opModExpLimW opMod' + 'GenLimW opModLoadLimW}}registration{dateTimeRegistered pin}}}' +) + +# ECDSA-SHA256 signature for GRID_CODE_DETAILS query +SEND_CODE_GRID_CODE_DETAILS = bytes.fromhex( + '3081880242015e85521f3ac12f9f63b82e1777c149' + 'cdf384ec0edce4b5d8a8553d94dd9d581937cf5fea' + '21a65edbddbff82090844f0531929b556903d39aca' + 'ea531667e9a50e0602420132e94ffabbd8c0091058' + '0ed2575b7276c21052c0d92b31058fba80f610f2ff' + '9dffb094a94e2d51ad9a0991585e663f6dacf642ac' + 'b9ccbbfbaf1cbe7372db514579' +) + +# GraphQL query for grid code details (uses variables: $gridCode, $pointNames) +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_GRID_CODE_DETAILS = ( + 'query GridCodeDetailsQuery($gridCode:String!$pointNames:[String!]){system{gridCodeSettings(gridCode:' + '$gridCode){gridCode gridVoltageSetting gridFrequencySetting gridPhaseSetting gridPerPhaseNetMeterEna' + 'bled gridCodePoints(names:$pointNames){name units min max fileValue}}}}' +) + +# ECDSA-SHA256 signature for GRID_CODES query +SEND_CODE_GRID_CODES = bytes.fromhex( + '308186024135da0bb771a881feb151227b92e5cdbf' + '60c035f36f79517962c5c7f8138087322d2ba958ee' + 'eeb7a47bbbae595f56e1e49f93ad1c49f4514e4aec' + '43946955ca76b8024131064c2913404dd80a9f4d0e' + '71cd42738463e49a50f62f9783028c6549013eb580' + '1544edc88dd7e53757b28f354fcf295ca2fd19ba49' + '7f7aeea7548dc65c35e9d7' +) + +# GraphQL query for available grid codes +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_GRID_CODES = ( + 'query GridCodesQuery{system{gridCodes}}' +) + +# ECDSA-SHA256 signature for EATON_SBII query +SEND_CODE_EATON_SBII = bytes.fromhex( + '30818802420110b3ff5de5c1e64fbda251170b1c85' + 'f5c892dde1e5225eccdb6a4be555a2e7ff1e8326c1' + 'c3b69eec23800ce9c94fe63ba94147f19a1628f7ab' + 'b1c5599f5394ab5c0242012f3133187f4000022826' + 'c028d4799b4579750a7c29afd2c80609b5ba2ee2d5' + '91d0e06d2a59ad21bcb752b8efc266592892739f74' + '643729206cc82543d60557c25d' +) + +# GraphQL query for Eaton SBII transfer switch data +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_EATON_SBII = ( + 'query EatonSBIIQuery{components{nodes(filter:{nodes:["EATON_SBII"]}){serialNumber subPackagePartNumb' + 'er signals(names:["EATON_SBII_realPower" "EATON_SBII_primaryHandleState" "EATON_SBII_secondaryHandle' + 'State"]){name value textValue boolValue}}}}' +) + +# ECDSA-SHA256 signature for PINV_SELF_TEST query +SEND_CODE_PINV_SELF_TEST = bytes.fromhex( + '308188024200a9d1a9cee2cf7e50d23c6b95cabaac' + '394e039f28613141d1c0c947d50613c417a9d08534' + 'b26556349e8bdc5df89362c16ee8108e6da4c0669b' + '812d1eda48f25ecf02420143cb418c2d665bd4ca1a' + '58f02be0d431c0aceb125c1664ed7d6b5362c22ba3' + '47f95ae4eb81438960f303aad38ab35b0ed38bff8c' + '36c0f711a729eae2f8393c22e6' +) + +# GraphQL query for inverter self-test results +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_PINV_SELF_TEST = ( + 'query PinvSelfTestQuery{esCan{inverterSelfTests{isRunning isCanceled pinvSelfTestsResults{din overal' + 'l{status test summary setMagnitude setTime tripMagnitude tripTime accuracyMagnitude accuracyTime cur' + 'rentMagnitude timestamp lastError}testResults{status test summary setMagnitude setTime tripMagnitude' + ' tripTime accuracyMagnitude accuracyTime currentMagnitude timestamp lastError}}}}}' +) + +# ECDSA-SHA256 signature for PROTECTION_TRIP_TEST query +SEND_CODE_PROTECTION_TRIP_TEST = bytes.fromhex( + '3081880242008eb1e3ceaf5ad65ad3ff75a6fa3eeb' + 'ccf80eaf528ada3fa7fef51a234044599cad4671fe' + '6f2d0bf24677b6756e2f5f52673439cca90accad56' + '118dad1e8bdb7988024200cc72dee1c8abd9c9d341' + '4ae85b4e06a0ecf12425d190d4f32a53356150d208' + '59a4666557364c43fe30de5cd665800bc7d184d6ac' + 'e76364a3551532045e9e7aae96' +) + +# GraphQL query for protection trip test results +# NOTE: Byte-exact, ECDSA-SHA256 signed. +QUERY_PROTECTION_TRIP_TEST = ( + 'query ProtectionTripTestQuery{control{protectionTripTests{isRunning results{testType status timestam' + 'p mandatedTripThreshold{value unit}mandatedTripTime{value unit}rampStepSize{value unit}rampInterval{' + 'value unit}tripThresholdDeviationMax{value unit}tripTimeDeviationMax{value unit}observedTripThreshol' + 'd{value unit}observedTripTime{value unit}observedMeasurementAtTrip{value unit}observedTripThresholdD' + 'eviation{value unit}observedTripTimeDeviation{value unit}tripThresholdAccuracy{value unit}tripTimeAc' + 'curacy{value unit}measurementAccuracy{value unit}measurementTimeAccuracy{value unit}}}}}' +) + + # Utility Functions def lookup(data, keylist): """ @@ -131,11 +374,81 @@ def decompress_response(content: bytes) -> bytes: log.debug(f"Gzip decompression failed: {e}") return content + +def _pb_extract(data: bytes, *field_path: int) -> Optional[bytes]: + """Extract a length-delimited field from raw protobuf bytes by field number path. + + Navigates nested protobuf messages by following the given field numbers. + Returns the raw bytes of the final field, or None if not found. + Only supports length-delimited (wire type 2) fields in the path. + """ + for target in field_path: + pos = 0 + found = False + while pos < len(data): + # Decode varint tag + tag = 0 + shift = 0 + while pos < len(data): + b = data[pos] + pos += 1 + tag |= (b & 0x7F) << shift + if not (b & 0x80): + break + shift += 7 + wire_type = tag & 0x07 + field_number = tag >> 3 + if wire_type == 0: # varint + while pos < len(data) and data[pos] & 0x80: + pos += 1 + pos += 1 + elif wire_type == 2: # length-delimited + length = 0 + shift = 0 + while pos < len(data): + b = data[pos] + pos += 1 + length |= (b & 0x7F) << shift + if not (b & 0x80): + break + shift += 7 + if field_number == target: + data = data[pos:pos + length] + found = True + break + pos += length + elif wire_type == 1: # 64-bit + pos += 8 + elif wire_type == 5: # 32-bit + pos += 4 + else: + break + if not found: + return None + return data + + # TEDAPI Class class TEDAPI: - def __init__(self, gw_pwd: str, debug: bool = False, pwcacheexpire: int = 5, timeout: int = 5, - pwconfigexpire: int = 5, host: str = GW_IP, poolmaxsize: int = 10,) -> None: - """Initialize the TEDAPI client for Powerwall Gateway communication.""" + def __init__(self, gw_pwd: str, debug: bool = False, pwcacheexpire: int = 5, timeout: int = 5, + pwconfigexpire: int = 5, host: str = GW_IP, poolmaxsize: int = 10, + auth_mode: str = "basic") -> None: + """Initialize the TEDAPI client for Powerwall Gateway communication. + + Args: + gw_pwd: Gateway password (printed on device or QR code) + debug: Enable verbose debug logging + pwcacheexpire: Status cache expiration in seconds + timeout: API request timeout in seconds + pwconfigexpire: Config cache expiration in seconds + host: Gateway IP address. For basic auth, use 192.168.91.1 (default). + For bearer auth, use the gateway's home network IP. + poolmaxsize: Maximum connection pool size + auth_mode: Authentication mode - "basic" (HTTP Basic Auth, default) or + "bearer" (Bearer token via /api/login/Basic). Bearer mode + enables TEDAPI access from the home network without requiring + a static route to 192.168.91.1. + """ self.debug = debug self.pwcachetime = {} # holds the cached data timestamps for api self.pwcacheexpire = pwcacheexpire # seconds to expire status cache @@ -147,12 +460,16 @@ def __init__(self, gw_pwd: str, debug: bool = False, pwcacheexpire: int = 5, tim self.gw_ip = host self.din = None self.pw3 = False # Powerwall 3 Gateway only supports TEDAPI + self.auth_mode = auth_mode.lower() + self.token = None # Bearer token (only used in bearer mode) + if self.auth_mode not in ("basic", "bearer"): + raise ValueError(f"Invalid auth_mode '{auth_mode}': must be 'basic' or 'bearer'") if not gw_pwd: raise ValueError("Missing gw_pwd") if self.debug: self.set_debug(True) self.gw_pwd = gw_pwd - log.debug(f"TEDAPI initialized with pwcacheexpire={self.pwcacheexpire}s, pwconfigexpire={self.pwconfigexpire}s") + log.debug(f"TEDAPI initialized with auth_mode={self.auth_mode}, pwcacheexpire={self.pwcacheexpire}s, pwconfigexpire={self.pwconfigexpire}s") # Connect to Powerwall Gateway if not self.connect(): log.error("Failed to connect to Powerwall Gateway") @@ -277,15 +594,14 @@ def get_config(self, self_function=None, force=False) -> Optional[Dict[Any, Any] log.debug("Get Configuration from Powerwall") # Build Protobuf to fetch config pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = self.din # DIN of Powerwall - pb.message.config.send.num = 1 - pb.message.config.send.file = "config.json" + pb.message.filestore.readFileRequest.domain = 1 # CONFIG_JSON + pb.message.filestore.readFileRequest.name = "config.json" pb.tail.value = 1 - url = f'https://{self.gw_ip}/tedapi/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -296,13 +612,16 @@ def get_config(self, self_function=None, force=False) -> Optional[Dict[Any, Any] log.error(f"Error fetching config: {r.status_code}") return None # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.config.recv.file.text - try: - data = json.loads(payload) - except json.JSONDecodeError as e: - log.error(f"Error Decoding JSON: {e}") + tedapi = self._parse_response(r.content) + payload = tedapi.message.filestore.readFileResponse.file.blob.decode('utf-8') + if payload: + try: + data = json.loads(payload) + except json.JSONDecodeError as e: + log.error(f"Error Decoding JSON: {e}") + data = {} + else: + log.warning("Empty config payload from gateway") data = {} log.debug(f"Configuration: {data}") self.pwcachetime["config"] = time.time() @@ -390,18 +709,16 @@ def get_status(self, self_function=None, force=False) -> Optional[Dict[Any, Any] log.debug("Get Status from Powerwall") # Build Protobuf to fetch status pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = self.din # DIN of Powerwall - pb.message.payload.send.num = 2 - pb.message.payload.send.payload.value = 1 - pb.message.payload.send.payload.text = " query DeviceControllerQuery {\n control {\n systemStatus {\n nominalFullPackEnergyWh\n nominalEnergyRemainingWh\n }\n islanding {\n customerIslandMode\n contactorClosed\n microGridOK\n gridOK\n }\n meterAggregates {\n location\n realPowerW\n }\n alerts {\n active\n },\n siteShutdown {\n isShutDown\n reasons\n }\n batteryBlocks {\n din\n disableReasons\n }\n pvInverters {\n din\n disableReasons\n }\n }\n system {\n time\n sitemanagerStatus {\n isRunning\n }\n updateUrgencyCheck {\n urgency\n version {\n version\n gitHash\n }\n timestamp\n }\n }\n neurio {\n isDetectingWiredMeters\n readings {\n serial\n dataRead {\n voltageV\n realPowerW\n reactivePowerVAR\n currentA\n }\n timestamp\n }\n pairings {\n serial\n shortId\n status\n errors\n macAddress\n isWired\n modbusPort\n modbusId\n lastUpdateTimestamp\n }\n }\n pw3Can {\n firmwareUpdate {\n isUpdating\n progress {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n }\n }\n esCan {\n bus {\n PVAC {\n packagePartNumber\n packageSerialNumber\n subPackagePartNumber\n subPackageSerialNumber\n PVAC_Status {\n isMIA\n PVAC_Pout\n PVAC_State\n PVAC_Vout\n PVAC_Fout\n }\n PVAC_InfoMsg {\n PVAC_appGitHash\n }\n PVAC_Logging {\n isMIA\n PVAC_PVCurrent_A\n PVAC_PVCurrent_B\n PVAC_PVCurrent_C\n PVAC_PVCurrent_D\n PVAC_PVMeasuredVoltage_A\n PVAC_PVMeasuredVoltage_B\n PVAC_PVMeasuredVoltage_C\n PVAC_PVMeasuredVoltage_D\n PVAC_VL1Ground\n PVAC_VL2Ground\n }\n alerts {\n isComplete\n isMIA\n active\n }\n }\n PINV {\n PINV_Status {\n isMIA\n PINV_Fout\n PINV_Pout\n PINV_Vout\n PINV_State\n PINV_GridState\n }\n PINV_AcMeasurements {\n isMIA\n PINV_VSplit1\n PINV_VSplit2\n }\n PINV_PowerCapability {\n isComplete\n isMIA\n PINV_Pnom\n }\n alerts {\n isComplete\n isMIA\n active\n }\n }\n PVS {\n PVS_Status {\n isMIA\n PVS_State\n PVS_vLL\n PVS_StringA_Connected\n PVS_StringB_Connected\n PVS_StringC_Connected\n PVS_StringD_Connected\n PVS_SelfTestState\n }\n alerts {\n isComplete\n isMIA\n active\n }\n }\n THC {\n packagePartNumber\n packageSerialNumber\n THC_InfoMsg {\n isComplete\n isMIA\n THC_appGitHash\n }\n THC_Logging {\n THC_LOG_PW_2_0_EnableLineState\n }\n }\n POD {\n POD_EnergyStatus {\n isMIA\n POD_nom_energy_remaining\n POD_nom_full_pack_energy\n }\n POD_InfoMsg {\n POD_appGitHash\n }\n }\n MSA {\n packagePartNumber\n packageSerialNumber\n MSA_InfoMsg {\n isMIA\n MSA_appGitHash\n MSA_assemblyId\n }\n METER_Z_AcMeasurements {\n isMIA\n lastRxTime\n METER_Z_CTA_InstRealPower\n METER_Z_CTA_InstReactivePower\n METER_Z_CTA_I\n METER_Z_VL1G\n METER_Z_CTB_InstRealPower\n METER_Z_CTB_InstReactivePower\n METER_Z_CTB_I\n METER_Z_VL2G\n }\n MSA_Status {\n lastRxTime\n }\n }\n SYNC {\n packagePartNumber\n packageSerialNumber\n SYNC_InfoMsg {\n isMIA\n SYNC_appGitHash\n }\n METER_X_AcMeasurements {\n isMIA\n isComplete\n lastRxTime\n METER_X_CTA_InstRealPower\n METER_X_CTA_InstReactivePower\n METER_X_CTA_I\n METER_X_VL1N\n METER_X_CTB_InstRealPower\n METER_X_CTB_InstReactivePower\n METER_X_CTB_I\n METER_X_VL2N\n METER_X_CTC_InstRealPower\n METER_X_CTC_InstReactivePower\n METER_X_CTC_I\n METER_X_VL3N\n }\n METER_Y_AcMeasurements {\n isMIA\n isComplete\n lastRxTime\n METER_Y_CTA_InstRealPower\n METER_Y_CTA_InstReactivePower\n METER_Y_CTA_I\n METER_Y_VL1N\n METER_Y_CTB_InstRealPower\n METER_Y_CTB_InstReactivePower\n METER_Y_CTB_I\n METER_Y_VL2N\n METER_Y_CTC_InstRealPower\n METER_Y_CTC_InstReactivePower\n METER_Y_CTC_I\n METER_Y_VL3N\n }\n SYNC_Status {\n lastRxTime\n }\n }\n ISLANDER {\n ISLAND_GridConnection {\n ISLAND_GridConnected\n isComplete\n }\n ISLAND_AcMeasurements {\n ISLAND_VL1N_Main\n ISLAND_FreqL1_Main\n ISLAND_VL2N_Main\n ISLAND_FreqL2_Main\n ISLAND_VL3N_Main\n ISLAND_FreqL3_Main\n ISLAND_VL1N_Load\n ISLAND_FreqL1_Load\n ISLAND_VL2N_Load\n ISLAND_FreqL2_Load\n ISLAND_VL3N_Load\n ISLAND_FreqL3_Load\n ISLAND_GridState\n lastRxTime\n isComplete\n isMIA\n }\n }\n }\n enumeration {\n inProgress\n numACPW\n numPVI\n }\n firmwareUpdate {\n isUpdating\n powerwalls {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n msa {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n sync {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n pvInverters {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n }\n phaseDetection {\n inProgress\n lastUpdateTimestamp\n powerwalls {\n din\n progress\n phase\n }\n }\n inverterSelfTests {\n isRunning\n isCanceled\n pinvSelfTestsResults {\n din\n overall {\n status\n test\n summary\n setMagnitude\n setTime\n tripMagnitude\n tripTime\n accuracyMagnitude\n accuracyTime\n currentMagnitude\n timestamp\n lastError\n }\n testResults {\n status\n test\n summary\n setMagnitude\n setTime\n tripMagnitude\n tripTime\n accuracyMagnitude\n accuracyTime\n currentMagnitude\n timestamp\n lastError\n }\n }\n }\n }\n}\n" - pb.message.payload.send.code = b'0\201\206\002A\024\261\227\245\177\255\265\272\321r\032\250\275j\305\030\2300\266\022B\242\264pO\262\024vd\267\316\032\f\376\322V\001\f\177*\366\345\333g_/`\v\026\225_qc\023$\323\216y\276~\335A1\022x\002Ap\a_\264\037]\304>\362\356\005\245V\301\177*\b\307\016\246]\037\202\242\353I~\332\317\021\336\006\033q\317\311\264\315\374\036\365s\272\225\215#o!\315z\353\345z\226\365\341\f\265\256r\373\313/\027\037' - pb.message.payload.send.b.value = "{}" + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_DEVICE_CONTROLLER) + pb.message.graphql.send.signature = SEND_CODE_DEVICE_CONTROLLER + pb.message.graphql.send.variablesJson.value = "{}" pb.tail.value = 1 - url = f'https://{self.gw_ip}/tedapi/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -412,13 +729,16 @@ def get_status(self, self_function=None, force=False) -> Optional[Dict[Any, Any] log.error(f"Error fetching status: {r.status_code}") return None # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.payload.recv.text - try: - data = json.loads(payload) - except json.JSONDecodeError as e: - log.error(f"Error Decoding JSON: {e}") + tedapi = self._parse_response(r.content) + payload = tedapi.message.graphql.recv.data + if payload: + try: + data = json.loads(payload) + except json.JSONDecodeError as e: + log.error(f"Error Decoding JSON: {e}") + data = {} + else: + log.warning("Empty status payload from gateway") data = {} log.debug(f"Status: {data}") self.pwcachetime["status"] = time.time() @@ -433,19 +753,8 @@ def get_status(self, self_function=None, force=False) -> Optional[Dict[Any, Any] def get_device_controller(self, self_function=None, force=False): """ Get the Powerwall Device Controller Status. - Similar to get_status but with additional data: - { - "components": {}, // Additional data - "control": {}, - "esCan": {}, - "ieee20305": {}, // Additional data - "neurio": {}, - "pw3Can": {}, - "system": {}, - "teslaRemoteMeter": {} // Additional data - } - - TODO: Refactor to combine tedapi queries + Now uses the same QUERY_DEVICE_CONTROLLER as get_status() since + the APK unified these into a single signed query. """ # Check Cache BEFORE acquiring lock if not force and "controller" in self.pwcachetime: @@ -483,18 +792,16 @@ def get_device_controller(self, self_function=None, force=False): log.debug("Get controller data from Powerwall") # Build Protobuf to fetch controller data pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = self.din # DIN of Powerwall - pb.message.payload.send.num = 2 - pb.message.payload.send.payload.value = 1 - pb.message.payload.send.payload.text = 'query DeviceControllerQuery($msaComp:ComponentFilter$msaSignals:[String!]){control{systemStatus{nominalFullPackEnergyWh nominalEnergyRemainingWh}islanding{customerIslandMode contactorClosed microGridOK gridOK disableReasons}meterAggregates{location realPowerW}alerts{active}siteShutdown{isShutDown reasons}batteryBlocks{din disableReasons}pvInverters{din disableReasons}}system{time supportMode{remoteService{isEnabled expiryTime sessionId}}sitemanagerStatus{isRunning}updateUrgencyCheck{urgency version{version gitHash}timestamp}}neurio{isDetectingWiredMeters readings{firmwareVersion serial dataRead{voltageV realPowerW reactivePowerVAR currentA}timestamp}pairings{serial shortId status errors macAddress hostname isWired modbusPort modbusId lastUpdateTimestamp}}teslaRemoteMeter{meters{din reading{timestamp firmwareVersion ctReadings{voltageV realPowerW reactivePowerVAR energyExportedWs energyImportedWs currentA}}firmwareUpdate{updating numSteps currentStep currentStepProgress progress}}detectedWired{din serialPort}}pw3Can{firmwareUpdate{isUpdating progress{updating numSteps currentStep currentStepProgress progress}}enumeration{inProgress}}esCan{bus{PVAC{packagePartNumber packageSerialNumber subPackagePartNumber subPackageSerialNumber PVAC_Status{isMIA PVAC_Pout PVAC_State PVAC_Vout PVAC_Fout}PVAC_InfoMsg{PVAC_appGitHash}PVAC_Logging{isMIA PVAC_PVCurrent_A PVAC_PVCurrent_B PVAC_PVCurrent_C PVAC_PVCurrent_D PVAC_PVMeasuredVoltage_A PVAC_PVMeasuredVoltage_B PVAC_PVMeasuredVoltage_C PVAC_PVMeasuredVoltage_D PVAC_VL1Ground PVAC_VL2Ground}alerts{isComplete isMIA active}}PINV{PINV_Status{isMIA PINV_Fout PINV_Pout PINV_Vout PINV_State PINV_GridState}PINV_AcMeasurements{isMIA PINV_VSplit1 PINV_VSplit2}PINV_PowerCapability{isComplete isMIA PINV_Pnom}alerts{isComplete isMIA active}}PVS{PVS_Status{isMIA PVS_State PVS_vLL PVS_StringA_Connected PVS_StringB_Connected PVS_StringC_Connected PVS_StringD_Connected PVS_SelfTestState}PVS_Logging{PVS_numStringsLockoutBits PVS_sbsComplete}alerts{isComplete isMIA active}}THC{packagePartNumber packageSerialNumber THC_InfoMsg{isComplete isMIA THC_appGitHash}THC_Logging{THC_LOG_PW_2_0_EnableLineState}}POD{POD_EnergyStatus{isMIA POD_nom_energy_remaining POD_nom_full_pack_energy}POD_InfoMsg{POD_appGitHash}}SYNC{packagePartNumber packageSerialNumber SYNC_InfoMsg{isMIA SYNC_appGitHash SYNC_assemblyId}METER_X_AcMeasurements{isMIA isComplete METER_X_CTA_InstRealPower METER_X_CTA_InstReactivePower METER_X_CTA_I METER_X_VL1N METER_X_CTB_InstRealPower METER_X_CTB_InstReactivePower METER_X_CTB_I METER_X_VL2N METER_X_CTC_InstRealPower METER_X_CTC_InstReactivePower METER_X_CTC_I METER_X_VL3N}METER_Y_AcMeasurements{isMIA isComplete METER_Y_CTA_InstRealPower METER_Y_CTA_InstReactivePower METER_Y_CTA_I METER_Y_VL1N METER_Y_CTB_InstRealPower METER_Y_CTB_InstReactivePower METER_Y_CTB_I METER_Y_VL2N METER_Y_CTC_InstRealPower METER_Y_CTC_InstReactivePower METER_Y_CTC_I METER_Y_VL3N}}ISLANDER{ISLAND_GridConnection{ISLAND_GridConnected isComplete}ISLAND_AcMeasurements{ISLAND_VL1N_Main ISLAND_FreqL1_Main ISLAND_VL2N_Main ISLAND_FreqL2_Main ISLAND_VL3N_Main ISLAND_FreqL3_Main ISLAND_VL1N_Load ISLAND_FreqL1_Load ISLAND_VL2N_Load ISLAND_FreqL2_Load ISLAND_VL3N_Load ISLAND_FreqL3_Load ISLAND_GridState isComplete isMIA}}}enumeration{inProgress numACPW numPVI}firmwareUpdate{isUpdating powerwalls{updating numSteps currentStep currentStepProgress progress}msa{updating numSteps currentStep currentStepProgress progress}msa1{updating numSteps currentStep currentStepProgress progress}sync{updating numSteps currentStep currentStepProgress progress}pvInverters{updating numSteps currentStep currentStepProgress progress}}phaseDetection{inProgress lastUpdateTimestamp powerwalls{din progress phase}}inverterSelfTests{isRunning isCanceled pinvSelfTestsResults{din overall{status test summary setMagnitude setTime tripMagnitude tripTime accuracyMagnitude accuracyTime currentMagnitude timestamp lastError}testResults{status test summary setMagnitude setTime tripMagnitude tripTime accuracyMagnitude accuracyTime currentMagnitude timestamp lastError}}}}components{msa:components(filter:$msaComp){partNumber serialNumber signals(names:$msaSignals){name value textValue boolValue timestamp}activeAlerts{name}}}ieee20305{longFormDeviceID polledResources{url name pollRateSeconds lastPolledTimestamp}controls{defaultControl{mRID setGradW opModEnergize opModMaxLimW opModImpLimW opModExpLimW opModGenLimW opModLoadLimW}activeControls{opModEnergize opModMaxLimW opModImpLimW opModExpLimW opModGenLimW opModLoadLimW}}registration{dateTimeRegistered pin}}}' - pb.message.payload.send.code = b'0\x81\x87\x02B\x01A\x95\x12\xe3B\xd1\xca\x1a\xd3\x00\xf6}\x0bE@/\x9a\x9f\xc0\r\x06%\xac,\x0ej!)\nd\xef\xe67\x8b\xafb\xd7\xf8&\x0b.\xc1\xac\xd9!\x1f\xd6\x83\xffkIm\xf3\\J\xd8\xeeiTY\xde\x7f\xc5xR\x02A\x1dC\x03H\xfb8"\xb0\xe4\xd6\x18\xde\x11\xc45\xb2\xa9VB\xa6J\x8f\x08\x9d\xba\x86\xf1 W\xcdJ\x8c\x02*\x05\x12\xcb{<\x9b\xc8g\xc9\x9d9\x8bR\xb3\x89\xb8\xf1\xf1\x0f\x0e\x16E\xed\xd7\xbf\xd5&)\x92.\x12' - pb.message.payload.send.b.value = '{"msaComp":{"types" :["PVS","PVAC", "TESYNC", "TEPINV", "TETHC", "STSTSM", "TEMSA", "TEPINV" ]},\n\t"msaSignals":[\n\t"MSA_pcbaId",\n\t"MSA_usageId",\n\t"MSA_appGitHash",\n\t"PVAC_Fan_Speed_Actual_RPM",\n\t"PVAC_Fan_Speed_Target_RPM",\n\t"MSA_HeatingRateOccurred",\n\t"THC_AmbientTemp",\n\t"METER_Z_CTA_InstRealPower",\n\t"METER_Z_CTA_InstReactivePower",\n\t"METER_Z_CTA_I",\n\t"METER_Z_VL1G",\n\t"METER_Z_CTB_InstRealPower",\n\t"METER_Z_CTB_InstReactivePower",\n\t"METER_Z_CTB_I",\n\t"METER_Z_VL2G",\n\t"METER_Z_CTC_InstRealPower",\n\t"METER_Z_CTC_InstReactivePower",\n\t"METER_Z_CTC_I",\n\t"METER_Z_VL3G",\n\t"METER_Z_LifetimeEnergyExport",\n\t"METER_Z_LifetimeEnergyImport"]}' + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_DEVICE_CONTROLLER) + pb.message.graphql.send.signature = SEND_CODE_DEVICE_CONTROLLER + pb.message.graphql.send.variablesJson.value = "{}" pb.tail.value = 1 - url = f'https://{self.gw_ip}/tedapi/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -505,14 +812,17 @@ def get_device_controller(self, self_function=None, force=False): log.error(f"Error fetching controller data: {r.status_code}") return None # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.payload.recv.text + tedapi = self._parse_response(r.content) + payload = tedapi.message.graphql.recv.data log.debug(f"Payload: {payload}") - try: - data = json.loads(payload) - except json.JSONDecodeError as e: - log.error(f"Error Decoding JSON: {e}") + if payload: + try: + data = json.loads(payload) + except json.JSONDecodeError as e: + log.error(f"Error Decoding JSON: {e}") + data = {} + else: + log.warning("Empty controller payload from gateway") data = {} log.debug(f"Status: {data}") self.pwcachetime["controller"] = time.time() @@ -564,16 +874,19 @@ def get_firmware_version(self, self_function=None, force=False, details=False): return None # Fetch Current Status from Powerwall log.debug("Get Firmware Version from Powerwall") - # Build Protobuf to fetch status + # Build Protobuf to fetch system info (wire-compatible with old firmware request) + # Old: field 4 (firmware) → field 2 (request="") + # New: field 4 (common) → field 2 (getSystemInfoRequest={}) pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = self.din # DIN of Powerwall - pb.message.firmware.request = "" + pb.message.common.getSystemInfoRequest.CopyFrom( + tedapi_pb2.CommonAPIGetSystemInfoRequest() + ) pb.tail.value = 1 - url = f'https://{self.gw_ip}/tedapi/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -583,39 +896,36 @@ def get_firmware_version(self, self_function=None, force=False, details=False): if r.status_code != HTTPStatus.OK: log.error(f"Error fetching firmware version: {r.status_code}") return None - # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - firmware_version = tedapi.message.firmware.system.version.text + # Decode response - the gateway returns firmware data inside + # getSystemInfoResponse (field 3 of CommonMessages). Since the + # proto defines it as empty, firmware fields are preserved as + # raw bytes. Use _pb_extract to navigate the wire format: + # FirmwarePayload.version (field 3) → FirmwareVersion.text (field 1) + # FirmwarePayload.gateway (field 1) → EcuId + # FirmwarePayload.din (field 2) + tedapi = self._parse_response(r.content) + sys_info = tedapi.message.common.getSystemInfoResponse + raw = sys_info.SerializeToString() + version_text = _pb_extract(raw, 3, 1) # version.text + firmware_version = version_text.decode('utf-8') if version_text else None if details: + din_bytes = _pb_extract(raw, 2) # din + gw_part = _pb_extract(raw, 1, 1) # gateway.partNumber + gw_serial = _pb_extract(raw, 1, 2) # gateway.serialNumber + githash = _pb_extract(raw, 3, 2) # version.githash payload = { "system": { "gateway": { - "partNumber": tedapi.message.firmware.system.gateway.partNumber, - "serialNumber": tedapi.message.firmware.system.gateway.serialNumber + "partNumber": gw_part.decode('utf-8') if gw_part else "", + "serialNumber": gw_serial.decode('utf-8') if gw_serial else "" }, - "din": tedapi.message.firmware.system.din, + "din": din_bytes.decode('utf-8') if din_bytes else "", "version": { - "text": tedapi.message.firmware.system.version.text, - "githash": tedapi.message.firmware.system.version.githash + "text": firmware_version or "", + "githash": githash or b"" }, - "five": tedapi.message.firmware.system.five, - "six": tedapi.message.firmware.system.six, - "wireless": { - "device": [] - } } } - try: - for device in tedapi.message.firmware.system.wireless.device: - payload["system"]["wireless"]["device"].append({ - "company": device.company.value, - "model": device.model.value, - "fcc_id": device.fcc_id.value, - "ic": device.ic.value - }) - except Exception as e: - log.debug(f"Error parsing wireless devices: {e}") log.debug(f"Firmware Version: {payload}") else: payload = firmware_version @@ -670,18 +980,16 @@ def get_components(self, self_function=None, force=False): log.debug("Get PW3 Components from Powerwall") # Build Protobuf to fetch config pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = self.din # DIN of Powerwall - pb.message.payload.send.num = 2 - pb.message.payload.send.payload.value = 1 - pb.message.payload.send.payload.text = " query ComponentsQuery (\n $pchComponentsFilter: ComponentFilter,\n $pchSignalNames: [String!],\n $pwsComponentsFilter: ComponentFilter,\n $pwsSignalNames: [String!],\n $bmsComponentsFilter: ComponentFilter,\n $bmsSignalNames: [String!],\n $hvpComponentsFilter: ComponentFilter,\n $hvpSignalNames: [String!],\n $baggrComponentsFilter: ComponentFilter,\n $baggrSignalNames: [String!],\n ) {\n # TODO STST-57686: Introduce GraphQL fragments to shorten\n pw3Can {\n firmwareUpdate {\n isUpdating\n progress {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n }\n }\n components {\n pws: components(filter: $pwsComponentsFilter) {\n signals(names: $pwsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n pch: components(filter: $pchComponentsFilter) {\n signals(names: $pchSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n bms: components(filter: $bmsComponentsFilter) {\n signals(names: $bmsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n hvp: components(filter: $hvpComponentsFilter) {\n partNumber\n serialNumber\n signals(names: $hvpSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n baggr: components(filter: $baggrComponentsFilter) {\n signals(names: $baggrSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n }\n}\n" - pb.message.payload.send.code = b'0\201\210\002B\000\270q\354>\243m\325p\371S\253\231\346~:\032\216~\242\263\207\017L\273O\203u\241\270\333w\233\354\276\246h\262\243\255\261\007\202D\277\353x\023O\022\303\216\264\010-\'i6\360>B\237\236\304\244m\002B\001\023Pk\033)\277\236\342R\264\247g\260u\036\023\3662\354\242\353\035\221\234\027\245\321J\342\345\037q\262O\3446-\353\315m1\237zai0\341\207C4\307\300Z\177@h\335\327\0239\252f\n\206W' - pb.message.payload.send.b.value = "{\"pwsComponentsFilter\":{\"types\":[\"PW3SAF\"]},\"pwsSignalNames\":[\"PWS_SelfTest\",\"PWS_PeImpTestState\",\"PWS_PvIsoTestState\",\"PWS_RelaySelfTest_State\",\"PWS_MciTestState\",\"PWS_appGitHash\",\"PWS_ProdSwitch_State\"],\"pchComponentsFilter\":{\"types\":[\"PCH\"]},\"pchSignalNames\":[\"PCH_State\",\"PCH_PvState_A\",\"PCH_PvState_B\",\"PCH_PvState_C\",\"PCH_PvState_D\",\"PCH_PvState_E\",\"PCH_PvState_F\",\"PCH_AcFrequency\",\"PCH_AcVoltageAB\",\"PCH_AcVoltageAN\",\"PCH_AcVoltageBN\",\"PCH_packagePartNumber_1_7\",\"PCH_packagePartNumber_8_14\",\"PCH_packagePartNumber_15_20\",\"PCH_packageSerialNumber_1_7\",\"PCH_packageSerialNumber_8_14\",\"PCH_PvVoltageA\",\"PCH_PvVoltageB\",\"PCH_PvVoltageC\",\"PCH_PvVoltageD\",\"PCH_PvVoltageE\",\"PCH_PvVoltageF\",\"PCH_PvCurrentA\",\"PCH_PvCurrentB\",\"PCH_PvCurrentC\",\"PCH_PvCurrentD\",\"PCH_PvCurrentE\",\"PCH_PvCurrentF\",\"PCH_BatteryPower\",\"PCH_AcRealPowerAB\",\"PCH_SlowPvPowerSum\",\"PCH_AcMode\",\"PCH_AcFrequency\",\"PCH_DcdcState_A\",\"PCH_DcdcState_B\",\"PCH_appGitHash\"],\"bmsComponentsFilter\":{\"types\":[\"PW3BMS\"]},\"bmsSignalNames\":[\"BMS_nominalEnergyRemaining\",\"BMS_nominalFullPackEnergy\",\"BMS_appGitHash\"],\"hvpComponentsFilter\":{\"types\":[\"PW3HVP\"]},\"hvpSignalNames\":[\"HVP_State\",\"HVP_appGitHash\"],\"baggrComponentsFilter\":{\"types\":[\"BAGGR\"]},\"baggrSignalNames\":[\"BAGGR_State\",\"BAGGR_OperationRequest\",\"BAGGR_NumBatteriesConnected\",\"BAGGR_NumBatteriesPresent\",\"BAGGR_NumBatteriesExpected\",\"BAGGR_LOG_BattConnectionStatus0\",\"BAGGR_LOG_BattConnectionStatus1\",\"BAGGR_LOG_BattConnectionStatus2\",\"BAGGR_LOG_BattConnectionStatus3\"]}" + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_COMPONENTS) + pb.message.graphql.send.signature = SEND_CODE_COMPONENTS + pb.message.graphql.send.variablesJson.value = "{}" pb.tail.value = 1 - url = f'https://{self.gw_ip}/tedapi/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -692,9 +1000,8 @@ def get_components(self, self_function=None, force=False): log.error(f"Error fetching components: {r.status_code}") return None # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.payload.recv.text + tedapi = self._parse_response(r.content) + payload = tedapi.message.graphql.recv.data log.debug(f"Payload (len={len(payload)}): {payload}") # Append payload to components components = json.loads(payload) @@ -780,14 +1087,13 @@ def get_pw3_vitals(self, force=False): continue # Fetch Device ComponentsQuery from each Powerwall pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.recipient.din = pw_din # DIN of Powerwall of Interest - pb.message.payload.send.num = 2 - pb.message.payload.send.payload.value = 1 - pb.message.payload.send.payload.text = " query ComponentsQuery (\n $pchComponentsFilter: ComponentFilter,\n $pchSignalNames: [String!],\n $pwsComponentsFilter: ComponentFilter,\n $pwsSignalNames: [String!],\n $bmsComponentsFilter: ComponentFilter,\n $bmsSignalNames: [String!],\n $hvpComponentsFilter: ComponentFilter,\n $hvpSignalNames: [String!],\n $baggrComponentsFilter: ComponentFilter,\n $baggrSignalNames: [String!],\n ) {\n # TODO STST-57686: Introduce GraphQL fragments to shorten\n pw3Can {\n firmwareUpdate {\n isUpdating\n progress {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n }\n }\n components {\n pws: components(filter: $pwsComponentsFilter) {\n signals(names: $pwsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n pch: components(filter: $pchComponentsFilter) {\n signals(names: $pchSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n bms: components(filter: $bmsComponentsFilter) {\n signals(names: $bmsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n hvp: components(filter: $hvpComponentsFilter) {\n partNumber\n serialNumber\n signals(names: $hvpSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n baggr: components(filter: $baggrComponentsFilter) {\n signals(names: $baggrSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n }\n}\n" - pb.message.payload.send.code = b'0\201\210\002B\000\270q\354>\243m\325p\371S\253\231\346~:\032\216~\242\263\207\017L\273O\203u\241\270\333w\233\354\276\246h\262\243\255\261\007\202D\277\353x\023O\022\303\216\264\010-\'i6\360>B\237\236\304\244m\002B\001\023Pk\033)\277\236\342R\264\247g\260u\036\023\3662\354\242\353\035\221\234\027\245\321J\342\345\037q\262O\3446-\353\315m1\237zai0\341\207C4\307\300Z\177@h\335\327\0239\252f\n\206W' - pb.message.payload.send.b.value = "{\"pwsComponentsFilter\":{\"types\":[\"PW3SAF\"]},\"pwsSignalNames\":[\"PWS_SelfTest\",\"PWS_PeImpTestState\",\"PWS_PvIsoTestState\",\"PWS_RelaySelfTest_State\",\"PWS_MciTestState\",\"PWS_appGitHash\",\"PWS_ProdSwitch_State\"],\"pchComponentsFilter\":{\"types\":[\"PCH\"]},\"pchSignalNames\":[\"PCH_State\",\"PCH_PvState_A\",\"PCH_PvState_B\",\"PCH_PvState_C\",\"PCH_PvState_D\",\"PCH_PvState_E\",\"PCH_PvState_F\",\"PCH_AcFrequency\",\"PCH_AcVoltageAB\",\"PCH_AcVoltageAN\",\"PCH_AcVoltageBN\",\"PCH_packagePartNumber_1_7\",\"PCH_packagePartNumber_8_14\",\"PCH_packagePartNumber_15_20\",\"PCH_packageSerialNumber_1_7\",\"PCH_packageSerialNumber_8_14\",\"PCH_PvVoltageA\",\"PCH_PvVoltageB\",\"PCH_PvVoltageC\",\"PCH_PvVoltageD\",\"PCH_PvVoltageE\",\"PCH_PvVoltageF\",\"PCH_PvCurrentA\",\"PCH_PvCurrentB\",\"PCH_PvCurrentC\",\"PCH_PvCurrentD\",\"PCH_PvCurrentE\",\"PCH_PvCurrentF\",\"PCH_BatteryPower\",\"PCH_AcRealPowerAB\",\"PCH_SlowPvPowerSum\",\"PCH_AcMode\",\"PCH_AcFrequency\",\"PCH_DcdcState_A\",\"PCH_DcdcState_B\",\"PCH_appGitHash\"],\"bmsComponentsFilter\":{\"types\":[\"PW3BMS\"]},\"bmsSignalNames\":[\"BMS_nominalEnergyRemaining\",\"BMS_nominalFullPackEnergy\",\"BMS_appGitHash\"],\"hvpComponentsFilter\":{\"types\":[\"PW3HVP\"]},\"hvpSignalNames\":[\"HVP_State\",\"HVP_appGitHash\"],\"baggrComponentsFilter\":{\"types\":[\"BAGGR\"]},\"baggrSignalNames\":[\"BAGGR_State\",\"BAGGR_OperationRequest\",\"BAGGR_NumBatteriesConnected\",\"BAGGR_NumBatteriesPresent\",\"BAGGR_NumBatteriesExpected\",\"BAGGR_LOG_BattConnectionStatus0\",\"BAGGR_LOG_BattConnectionStatus1\",\"BAGGR_LOG_BattConnectionStatus2\",\"BAGGR_LOG_BattConnectionStatus3\"]}" + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_COMPONENTS) + pb.message.graphql.send.signature = SEND_CODE_COMPONENTS + pb.message.graphql.send.variablesJson.value = "{}" if single_pw: # If only one Powerwall, use basic tedapi URL pb.tail.value = 1 @@ -797,12 +1103,11 @@ def get_pw3_vitals(self, force=False): pb.tail.value = 2 pb.message.sender.din = din # DIN of Primary Powerwall 3 / System url = f'https://{self.gw_ip}/tedapi/device/{pw_din}/v1' - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb, url=url) if r.status_code == HTTPStatus.OK: # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.payload.recv.text + tedapi = self._parse_response(r.content) + payload = tedapi.message.graphql.recv.data if payload: data = json.loads(payload) # TEDPOD @@ -973,19 +1278,18 @@ def get_battery_block(self, self_function=None, din=None, force=False): log.debug(f"Get Battery Block from Powerwall ({din})") # Build Protobuf to fetch config pb = tedapi_pb2.Message() - pb.message.deliveryChannel = 1 - pb.message.sender.local = 1 + pb.message.deliveryChannel = tedapi_pb2.DELIVERY_CHANNEL_LOCAL_HTTPS + pb.message.sender.local = tedapi_pb2.LOCAL_PARTICIPANT_INSTALLER pb.message.sender.din = self.din # DIN of Primary Powerwall 3 / System pb.message.recipient.din = din # DIN of Powerwall of Interest - pb.message.payload.send.num = 2 - pb.message.payload.send.payload.value = 1 - pb.message.payload.send.payload.text = " query ComponentsQuery (\n $pchComponentsFilter: ComponentFilter,\n $pchSignalNames: [String!],\n $pwsComponentsFilter: ComponentFilter,\n $pwsSignalNames: [String!],\n $bmsComponentsFilter: ComponentFilter,\n $bmsSignalNames: [String!],\n $hvpComponentsFilter: ComponentFilter,\n $hvpSignalNames: [String!],\n $baggrComponentsFilter: ComponentFilter,\n $baggrSignalNames: [String!],\n ) {\n # TODO STST-57686: Introduce GraphQL fragments to shorten\n pw3Can {\n firmwareUpdate {\n isUpdating\n progress {\n updating\n numSteps\n currentStep\n currentStepProgress\n progress\n }\n }\n }\n components {\n pws: components(filter: $pwsComponentsFilter) {\n signals(names: $pwsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n pch: components(filter: $pchComponentsFilter) {\n signals(names: $pchSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n bms: components(filter: $bmsComponentsFilter) {\n signals(names: $bmsSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n hvp: components(filter: $hvpComponentsFilter) {\n partNumber\n serialNumber\n signals(names: $hvpSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n baggr: components(filter: $baggrComponentsFilter) {\n signals(names: $baggrSignalNames) {\n name\n value\n textValue\n boolValue\n timestamp\n }\n activeAlerts {\n name\n }\n }\n }\n}\n" - pb.message.payload.send.code = b'0\201\210\002B\000\270q\354>\243m\325p\371S\253\231\346~:\032\216~\242\263\207\017L\273O\203u\241\270\333w\233\354\276\246h\262\243\255\261\007\202D\277\353x\023O\022\303\216\264\010-\'i6\360>B\237\236\304\244m\002B\001\023Pk\033)\277\236\342R\264\247g\260u\036\023\3662\354\242\353\035\221\234\027\245\321J\342\345\037q\262O\3446-\353\315m1\237zai0\341\207C4\307\300Z\177@h\335\327\0239\252f\n\206W' - pb.message.payload.send.b.value = "{\"pwsComponentsFilter\":{\"types\":[\"PW3SAF\"]},\"pwsSignalNames\":[\"PWS_SelfTest\",\"PWS_PeImpTestState\",\"PWS_PvIsoTestState\",\"PWS_RelaySelfTest_State\",\"PWS_MciTestState\",\"PWS_appGitHash\",\"PWS_ProdSwitch_State\"],\"pchComponentsFilter\":{\"types\":[\"PCH\"]},\"pchSignalNames\":[\"PCH_State\",\"PCH_PvState_A\",\"PCH_PvState_B\",\"PCH_PvState_C\",\"PCH_PvState_D\",\"PCH_PvState_E\",\"PCH_PvState_F\",\"PCH_AcFrequency\",\"PCH_AcVoltageAB\",\"PCH_AcVoltageAN\",\"PCH_AcVoltageBN\",\"PCH_packagePartNumber_1_7\",\"PCH_packagePartNumber_8_14\",\"PCH_packagePartNumber_15_20\",\"PCH_packageSerialNumber_1_7\",\"PCH_packageSerialNumber_8_14\",\"PCH_PvVoltageA\",\"PCH_PvVoltageB\",\"PCH_PvVoltageC\",\"PCH_PvVoltageD\",\"PCH_PvVoltageE\",\"PCH_PvVoltageF\",\"PCH_PvCurrentA\",\"PCH_PvCurrentB\",\"PCH_PvCurrentC\",\"PCH_PvCurrentD\",\"PCH_PvCurrentE\",\"PCH_PvCurrentF\",\"PCH_BatteryPower\",\"PCH_AcRealPowerAB\",\"PCH_SlowPvPowerSum\",\"PCH_AcMode\",\"PCH_AcFrequency\",\"PCH_DcdcState_A\",\"PCH_DcdcState_B\",\"PCH_appGitHash\"],\"bmsComponentsFilter\":{\"types\":[\"PW3BMS\"]},\"bmsSignalNames\":[\"BMS_nominalEnergyRemaining\",\"BMS_nominalFullPackEnergy\",\"BMS_appGitHash\"],\"hvpComponentsFilter\":{\"types\":[\"PW3HVP\"]},\"hvpSignalNames\":[\"HVP_State\",\"HVP_appGitHash\"],\"baggrComponentsFilter\":{\"types\":[\"BAGGR\"]},\"baggrSignalNames\":[\"BAGGR_State\",\"BAGGR_OperationRequest\",\"BAGGR_NumBatteriesConnected\",\"BAGGR_NumBatteriesPresent\",\"BAGGR_NumBatteriesExpected\",\"BAGGR_LOG_BattConnectionStatus0\",\"BAGGR_LOG_BattConnectionStatus1\",\"BAGGR_LOG_BattConnectionStatus2\",\"BAGGR_LOG_BattConnectionStatus3\"]}" + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_COMPONENTS) + pb.message.graphql.send.signature = SEND_CODE_COMPONENTS + pb.message.graphql.send.variablesJson.value = "{}" pb.tail.value = 2 url = f'https://{self.gw_ip}/tedapi/device/{din}/v1' try: - r = self.session.post(url, data=pb.SerializeToString(), timeout=self.timeout) + r = self._send_cmd(pb, url=url) log.debug(f"Response Code: {r.status_code}") if r.status_code in BUSY_CODES: # Rate limited - Switch to cooldown mode for 5 minutes @@ -999,13 +1303,16 @@ def get_battery_block(self, self_function=None, din=None, force=False): log.error(f"Error fetching config: {r.status_code}") return None # Decode response - tedapi = tedapi_pb2.Message() - tedapi.ParseFromString(decompress_response(r.content)) - payload = tedapi.message.config.recv.file.text - try: - data = json.loads(payload) - except json.JSONDecodeError as e: - log.error(f"Error Decoding JSON: {e}") + tedapi = self._parse_response(r.content) + payload = tedapi.message.filestore.readFileResponse.file.blob.decode('utf-8') + if payload: + try: + data = json.loads(payload) + except json.JSONDecodeError as e: + log.error(f"Error Decoding JSON: {e}") + data = {} + else: + log.warning(f"Empty device config payload from gateway for {din}") data = {} log.debug(f"Configuration: {data}") self.pwcachetime[din] = time.time() @@ -1030,10 +1337,110 @@ def _init_session(self): else: session.headers.update({'Connection': 'close'}) # This disables keep-alive session.verify = False - session.auth = ('Tesla_Energy_Device', self.gw_pwd) - session.headers.update({'Content-type': 'application/octet-string'}) + if self.auth_mode == "bearer": + session.headers.update({'Content-type': 'application/octet-stream'}) + else: + session.auth = ('Tesla_Energy_Device', self.gw_pwd) + session.headers.update({'Content-type': 'application/octet-string'}) return session + def _bearer_login(self): + """Authenticate via /api/login/Basic and store Bearer token on session.""" + url = f'https://{self.gw_ip}/api/login/Basic' + payload = { + "username": "installer", + "password": self.gw_pwd, + "email": "installer@tesla.com", + "clientInfo": {"timezone": "America/Los_Angeles"}, + } + log.debug(f"Bearer login to {url}") + r = self.session.post(url, json=payload, timeout=self.timeout) + r.raise_for_status() + data = r.json() + if "token" not in data: + raise ValueError("Login response missing 'token' field") + self.token = data["token"] + self.session.headers["Authorization"] = f"Bearer {self.token}" + log.debug(f"Bearer token acquired ({len(self.token)} chars)") + + def _bearer_logout(self): + """Invalidate the Bearer token session.""" + if not self.token: + return + try: + self.session.get( + f'https://{self.gw_ip}/api/logout', + headers={"Authorization": f"Bearer {self.token}"}, + timeout=self.timeout, + ) + except Exception: + pass + self.token = None + self.session.headers.pop("Authorization", None) + + def _send_cmd(self, pb, url=None): + """Send a protobuf command to a TEDAPI endpoint. + + In basic mode, sends the raw serialized Message (MessageEnvelope + Tail). + In bearer mode, wraps the serialized MessageEnvelope in an AuthEnvelope + with EXTERNAL_AUTH_TYPE_PRESENCE. + + Args: + pb: The tedapi_pb2.Message to send + url: Override URL (default: https://{gw_ip}/tedapi/v1) + + Returns the requests.Response object. + """ + if self.auth_mode == "bearer": + auth_env = tedapi_pb2.AuthEnvelope() + auth_env.payload = pb.message.SerializeToString() + auth_env.externalAuth.type = 1 # EXTERNAL_AUTH_TYPE_PRESENCE + data = auth_env.SerializeToString() + else: + data = pb.SerializeToString() + + if url is None: + url = f'https://{self.gw_ip}/tedapi/v1' + r = self.session.post(url, data=data, timeout=self.timeout) + + # Bearer mode: auto-relogin on 401/403 + if self.auth_mode == "bearer" and r.status_code in ( + HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN + ): + log.debug("Bearer token expired or rejected, re-authenticating...") + try: + self._bearer_login() + # Re-wrap with fresh session state + auth_env = tedapi_pb2.AuthEnvelope() + auth_env.payload = pb.message.SerializeToString() + auth_env.externalAuth.type = 1 + data = auth_env.SerializeToString() + r = self.session.post(url, data=data, timeout=self.timeout) + except Exception as e: + log.error(f"Bearer re-authentication failed: {e}") + + return r + + def _parse_response(self, content): + """Parse a TEDAPI protobuf response. + + In basic mode, parses raw content as a full Message. + In bearer mode, unwraps the AuthEnvelope and parses the + inner payload as a MessageEnvelope. + + Returns a tedapi_pb2.Message object (with .message populated). + """ + content = decompress_response(content) + if self.auth_mode == "bearer": + auth_resp = tedapi_pb2.AuthEnvelope() + auth_resp.ParseFromString(content) + tedapi = tedapi_pb2.Message() + tedapi.message.ParseFromString(auth_resp.payload) + else: + tedapi = tedapi_pb2.Message() + tedapi.ParseFromString(content) + return tedapi + def connect(self): """Connect to the Powerwall Gateway and retrieve the DIN.""" # Test IP Connection to Powerwall Gateway @@ -1042,15 +1449,22 @@ def connect(self): self.din = None self.session = self._init_session() try: - resp = self.session.get(url, timeout=self.timeout) - if resp.status_code != HTTPStatus.OK: - # Connected but appears to be Powerwall 3 - log.debug("Detected Powerwall 3 Gateway") - self.pw3 = True + if self.auth_mode == "bearer": + # Bearer mode: login first to get token + self._bearer_login() + else: + resp = self.session.get(url, timeout=self.timeout) + if resp.status_code != HTTPStatus.OK: + # Connected but appears to be Powerwall 3 + log.debug("Detected Powerwall 3 Gateway") + self.pw3 = True self.din = self.get_din() except Exception as e: log.error(f"Unable to connect to Powerwall Gateway {self.gw_ip}") - log.error("Please verify your your host has a route to the Gateway.") + if self.auth_mode == "basic": + log.error("Please verify your host has a route to the Gateway.") + else: + log.error("Please verify the gateway password and that the host is reachable.") log.error(f"Error Details: {e}") return self.din @@ -1108,32 +1522,314 @@ def battery_level(self, force=False): return battery_level + @uses_api_lock + def get_ieee20305(self, self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get IEEE 2030.5 data (smart inverter protocol).""" + if not force and "ieee20305" in self.pwcachetime: + if time.time() - self.pwcachetime["ieee20305"] < self.pwcacheexpire: + return self.pwcache["ieee20305"] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and "ieee20305" in self.pwcachetime: + if time.time() - self.pwcachetime["ieee20305"] < self.pwcacheexpire: + return self.pwcache["ieee20305"] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get IEEE 2030.5 data") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_IEEE20305) + pb.message.graphql.send.signature = SEND_CODE_IEEE20305 + pb.message.graphql.send.variablesJson.value = "{}" + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching IEEE 2030.5 data: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime["ieee20305"] = time.time() + self.pwcache["ieee20305"] = data + except Exception as e: + log.error(f"Error fetching IEEE 2030.5 data: {e}") + data = None + return data + + @uses_api_lock + def get_grid_codes(self, self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get available grid codes.""" + if not force and "grid_codes" in self.pwcachetime: + if time.time() - self.pwcachetime["grid_codes"] < self.pwcacheexpire: + return self.pwcache["grid_codes"] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and "grid_codes" in self.pwcachetime: + if time.time() - self.pwcachetime["grid_codes"] < self.pwcacheexpire: + return self.pwcache["grid_codes"] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get grid codes") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_GRID_CODES) + pb.message.graphql.send.signature = SEND_CODE_GRID_CODES + pb.message.graphql.send.variablesJson.value = "{}" + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching grid codes: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime["grid_codes"] = time.time() + self.pwcache["grid_codes"] = data + except Exception as e: + log.error(f"Error fetching grid codes: {e}") + data = None + return data + + @uses_api_lock + def get_grid_code_details(self, grid_code: str, point_names: List[str], + self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get grid code details for a specific grid code and point names.""" + cache_key = f"grid_code_details_{grid_code}" + if not force and cache_key in self.pwcachetime: + if time.time() - self.pwcachetime[cache_key] < self.pwcacheexpire: + return self.pwcache[cache_key] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and cache_key in self.pwcachetime: + if time.time() - self.pwcachetime[cache_key] < self.pwcacheexpire: + return self.pwcache[cache_key] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get grid code details") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_GRID_CODE_DETAILS) + pb.message.graphql.send.signature = SEND_CODE_GRID_CODE_DETAILS + pb.message.graphql.send.variablesJson.value = json.dumps({ + "gridCode": grid_code, + "pointNames": point_names, + }) + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching grid code details: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime[cache_key] = time.time() + self.pwcache[cache_key] = data + except Exception as e: + log.error(f"Error fetching grid code details: {e}") + data = None + return data + + @uses_api_lock + def get_eaton_sbii(self, self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get Eaton SBII transfer switch data.""" + if not force and "eaton_sbii" in self.pwcachetime: + if time.time() - self.pwcachetime["eaton_sbii"] < self.pwcacheexpire: + return self.pwcache["eaton_sbii"] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and "eaton_sbii" in self.pwcachetime: + if time.time() - self.pwcachetime["eaton_sbii"] < self.pwcacheexpire: + return self.pwcache["eaton_sbii"] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get Eaton SBII data") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_EATON_SBII) + pb.message.graphql.send.signature = SEND_CODE_EATON_SBII + pb.message.graphql.send.variablesJson.value = "{}" + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching Eaton SBII data: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime["eaton_sbii"] = time.time() + self.pwcache["eaton_sbii"] = data + except Exception as e: + log.error(f"Error fetching Eaton SBII data: {e}") + data = None + return data + + @uses_api_lock + def get_pinv_self_test(self, self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get inverter self-test results.""" + if not force and "pinv_self_test" in self.pwcachetime: + if time.time() - self.pwcachetime["pinv_self_test"] < self.pwcacheexpire: + return self.pwcache["pinv_self_test"] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and "pinv_self_test" in self.pwcachetime: + if time.time() - self.pwcachetime["pinv_self_test"] < self.pwcacheexpire: + return self.pwcache["pinv_self_test"] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get inverter self-test data") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_PINV_SELF_TEST) + pb.message.graphql.send.signature = SEND_CODE_PINV_SELF_TEST + pb.message.graphql.send.variablesJson.value = "{}" + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching inverter self-test data: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime["pinv_self_test"] = time.time() + self.pwcache["pinv_self_test"] = data + except Exception as e: + log.error(f"Error fetching inverter self-test data: {e}") + data = None + return data + + @uses_api_lock + def get_protection_trip_test(self, self_function=None, force=False) -> Optional[Dict[Any, Any]]: + """Get protection trip test results.""" + if not force and "protection_trip_test" in self.pwcachetime: + if time.time() - self.pwcachetime["protection_trip_test"] < self.pwcacheexpire: + return self.pwcache["protection_trip_test"] + if not force and self.pwcooldown > time.perf_counter(): + return None + data = None + with acquire_lock_with_backoff(self_function, self.timeout): + if not force and "protection_trip_test" in self.pwcachetime: + if time.time() - self.pwcachetime["protection_trip_test"] < self.pwcacheexpire: + return self.pwcache["protection_trip_test"] + if not self.din: + if not self.connect(): + log.error("Not Connected - Unable to get protection trip test data") + return None + pb = tedapi_pb2.Message() + pb.message.deliveryChannel = 1 + pb.message.sender.local = 1 + pb.message.recipient.din = self.din + pb.message.graphql.send.format = tedapi_pb2.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1 + pb.message.graphql.send.query = _build_signed_query(QUERY_PROTECTION_TRIP_TEST) + pb.message.graphql.send.signature = SEND_CODE_PROTECTION_TRIP_TEST + pb.message.graphql.send.variablesJson.value = "{}" + pb.tail.value = 1 + try: + r = self._send_cmd(pb) + if r.status_code in BUSY_CODES: + self.pwcooldown = time.perf_counter() + 300 + return None + if r.status_code != HTTPStatus.OK: + log.error(f"Error fetching protection trip test data: {r.status_code}") + return None + tedapi = self._parse_response(r.content) + data = json.loads(tedapi.message.graphql.recv.data) + self.pwcachetime["protection_trip_test"] = time.time() + self.pwcache["protection_trip_test"] = data + except Exception as e: + log.error(f"Error fetching protection trip test data: {e}") + data = None + return data + # Helper Function def extract_fan_speeds(self, data) -> Dict[str, Dict[str, str]]: - """Extract fan speed signals from device controller data.""" + """Extract fan speed signals from device controller data. + + Primary path: esCan.bus.PVAC[].PVAC_Logging (new APK query location). + Fallback: components.msa[].signals[] (older firmware compatibility). + """ if not isinstance(data, dict): return {} fan_speed_signal_names = {"PVAC_Fan_Speed_Actual_RPM", "PVAC_Fan_Speed_Target_RPM"} - - # List to store the valid fan speed values result = {} - # Iterate over each component in the "msa" list - components = data.get("components", {}) - if isinstance(components, dict): - for component in components.get("msa", []): - signals = component.get("signals", []) - fan_speeds = { - signal["name"]: signal["value"] - for signal in signals - if signal.get("name") in fan_speed_signal_names and signal.get("value") is not None - } - if not fan_speeds: - continue - componentPartNumber = component.get("partNumber") - componentSerialNumber = component.get("serialNumber") - result[f"PVAC--{componentPartNumber}--{componentSerialNumber}"] = fan_speeds + # Primary: fan speeds are now in esCan.bus.PVAC[].PVAC_Logging + pvac_list = lookup(data, ['esCan', 'bus', 'PVAC']) or [] + for pvac in pvac_list: + logging_data = pvac.get("PVAC_Logging", {}) or {} + fan_speeds = { + name: logging_data[name] + for name in fan_speed_signal_names + if name in logging_data and logging_data[name] is not None + } + if not fan_speeds: + continue + part = pvac.get("packagePartNumber") + serial = pvac.get("packageSerialNumber") + result[f"PVAC--{part}--{serial}"] = fan_speeds + + # Fallback: older firmware may still have fan speeds in components.msa signals + if not result: + components = data.get("components", {}) + if isinstance(components, dict): + for component in components.get("msa", []): + signals = component.get("signals", []) + fan_speeds = { + signal["name"]: signal["value"] + for signal in signals + if signal.get("name") in fan_speed_signal_names and signal.get("value") is not None + } + if not fan_speeds: + continue + part = component.get("partNumber") + serial = component.get("serialNumber") + result[f"PVAC--{part}--{serial}"] = fan_speeds + return result def get_fan_speeds(self, force=False): diff --git a/pypowerwall/tedapi/__main__.py b/pypowerwall/tedapi/__main__.py index d17d957..565ec81 100644 --- a/pypowerwall/tedapi/__main__.py +++ b/pypowerwall/tedapi/__main__.py @@ -46,6 +46,9 @@ def set_debug(toggle=True, color=True): parser = argparse.ArgumentParser(description='Tesla Powerwall Gateway TEDAPI Reader') parser.add_argument('gw_pwd', nargs='?', help='Powerwall Gateway Password') parser.add_argument('--gw_ip', default=GW_IP, help='Powerwall Gateway IP Address') + parser.add_argument('--auth-mode', default='basic', choices=['basic', 'bearer'], + help='Authentication mode: basic (default, requires static route to 192.168.91.1) ' + 'or bearer (installer login, works from home network)') parser.add_argument('--debug', action='store_true', help='Enable Debug Output') # Parse arguments args = parser.parse_args() @@ -90,7 +93,7 @@ def set_debug(toggle=True, color=True): # Create TEDAPI Object and get Configuration and Status print() print(f"Connecting to Powerwall Gateway {GW_IP}") - ted = TEDAPI(gw_pwd, host=GW_IP) + ted = TEDAPI(gw_pwd, host=GW_IP, auth_mode=args.auth_mode) if ted.din is None: print("\nERROR: Unable to connect to Powerwall Gateway. Check your password and try again") sys.exit(1) diff --git a/pypowerwall/tedapi/pypowerwall_tedapi.py b/pypowerwall/tedapi/pypowerwall_tedapi.py index df8b03d..fb46b03 100644 --- a/pypowerwall/tedapi/pypowerwall_tedapi.py +++ b/pypowerwall/tedapi/pypowerwall_tedapi.py @@ -80,9 +80,10 @@ def compute_LL_voltage(v1n=0, v2n=0, v3n=0): # noinspection PyMethodMayBeStatic class PyPowerwallTEDAPI(PyPowerwallBase): def __init__(self, gw_pwd: str, debug: bool = False, pwcacheexpire: int = 5, timeout: int = 5, - pwconfigexpire: int = 5, host: str = GW_IP, poolmaxsize: int = 10) -> None: + pwconfigexpire: int = 5, host: str = GW_IP, poolmaxsize: int = 10, + auth_mode: str = "basic") -> None: super().__init__("nobody@nowhere.com") - self.tedapi = None + self.tedapi: Optional[TEDAPI] = None self.timeout = timeout self.pwcacheexpire = pwcacheexpire self.pwconfigexpire = pwconfigexpire @@ -90,15 +91,17 @@ def __init__(self, gw_pwd: str, debug: bool = False, pwcacheexpire: int = 5, tim self.host = host self.gw_pwd = gw_pwd self.debug = debug + self.auth_mode = auth_mode self.poll_api_map = self.init_poll_api_map() self.post_api_map = self.init_post_api_map() self.siteid = None self.auth = {'AuthCookie': 'local', 'UserRecord': 'local'} # Bogus local auth record # Initialize TEDAPI - self.tedapi = TEDAPI(gw_pwd=self.gw_pwd, debug=self.debug, host=self.host, + self.tedapi = TEDAPI(gw_pwd=self.gw_pwd, debug=self.debug, host=self.host, timeout=self.timeout, pwcacheexpire=self.pwcacheexpire, - pwconfigexpire=self.pwconfigexpire, poolmaxsize=self.poolmaxsize) + pwconfigexpire=self.pwconfigexpire, poolmaxsize=self.poolmaxsize, + auth_mode=self.auth_mode) log.debug(f" -- tedapi: Attempting to connect to {self.host}...") if not self.tedapi.connect(): raise ConnectionError(f"Unable to connect to Tesla TEDAPI at {self.host}") @@ -123,7 +126,6 @@ def init_poll_api_map(self) -> dict: "/api/system_status/grid_status": self.get_api_system_status_grid_status, "/api/system_status/soe": self.get_api_system_status_soe, "/vitals": self.vitals, - # Possible Actions "/api/login/Basic": self.api_login_basic, "/api/logout": self.api_logout, # Mock Actions @@ -642,15 +644,26 @@ def get_api_system_status(self, **kwargs) -> Optional[Union[dict, list, str, byt }) return data - # pylint: disable=unused-argument - # noinspection PyUnusedLocal - @not_implemented_mock_data def api_logout(self, **kwargs) -> Optional[Union[dict, list, str, bytes]]: + """Logout from the Tesla Gateway, invalidating the bearer token session.""" + if self.auth_mode == "bearer": + self.tedapi._bearer_logout() return {"status": "ok"} - # noinspection PyUnusedLocal - @not_implemented_mock_data def api_login_basic(self, **kwargs) -> Optional[Union[dict, list, str, bytes]]: + """Login to the Tesla Gateway via bearer token auth. + + In bearer mode, delegates to the TEDAPI bearer login which POSTs to + /api/login/Basic with stored credentials. In basic mode, auth is handled + via HTTP Basic Auth so this is a no-op. + """ + if self.auth_mode == "bearer": + try: + self.tedapi._bearer_login() + return {"status": "ok", "token": self.tedapi.token} + except Exception as e: + log.error(f"api_login_basic: {e}") + return {"status": "error", "message": str(e)} return {"status": "ok"} # noinspection PyUnusedLocal diff --git a/pypowerwall/tedapi/tedapi.proto b/pypowerwall/tedapi/tedapi.proto index f49207c..5b6f000 100644 --- a/pypowerwall/tedapi/tedapi.proto +++ b/pypowerwall/tedapi/tedapi.proto @@ -1,38 +1,124 @@ -// Tesla tedapi API Protocol Buffer definition (tedapi.proto) +// Tesla TEDAPI Protocol Buffer definition // -// Create tedapi_pb2.py for use in projects using the protoc compiler: +// VERIFIED against APK JavaScript bundle (index.android.bundle) +// Package: tesla.proto.energy_device.v1 +// +// This is the GROUND TRUTH extracted from the Tesla One app's +// protobufjs encode/decode functions. All field numbers verified +// against uint32() wire tags in the minified JS. +// +// Create Python bindings: // protoc --python_out=. tedapi.proto -// -// Author: Jason A. Cox - Date: 22 Nov 2023 - Version: 1.1 +// Or: +// python -m grpc_tools.protoc --python_out=. --proto_path=. tedapi.proto // -// For more information see https://github.com/jasonacox/pypowerwall +// Date: 12 Feb 2026 syntax = "proto3"; package tedapi; -// ***** Message ***** +// =================================================================== +// Auth Envelope (outermost wrapper for all TEDAPI messages) +// =================================================================== + +message AuthEnvelope { + bytes payload = 1; // Serialized MessageEnvelope + oneof auth { + ExternalAuth externalAuth = 2; + } +} + +message ExternalAuth { + ExternalAuthType type = 1; +} + +enum ExternalAuthType { + EXTERNAL_AUTH_TYPE_INVALID = 0; + EXTERNAL_AUTH_TYPE_PRESENCE = 1; // Local network presence auth + EXTERNAL_AUTH_TYPE_MTLS = 2; // Mutual TLS (device-to-device) + // 3 is intentionally skipped + EXTERNAL_AUTH_TYPE_HERMES_COMMAND = 4; // Cloud/Hermes command auth +} + +// =================================================================== +// Enums +// =================================================================== + +enum DeliveryChannel { + DELIVERY_CHANNEL_INVALID = 0; + DELIVERY_CHANNEL_LOCAL_HTTPS = 1; + DELIVERY_CHANNEL_HERMES_COMMAND = 2; + DELIVERY_CHANNEL_BLE = 3; +} + +enum LocalParticipant { + LOCAL_PARTICIPANT_INVALID = 0; + LOCAL_PARTICIPANT_INSTALLER = 1; + LOCAL_PARTICIPANT_CUSTOMER = 2; +} + +enum TeslaService { + TESLA_SERVICE_INVALID = 0; + TESLA_SERVICE_COMMAND = 1; +} + +enum AuthorizedClientType { + AUTHORIZED_CLIENT_TYPE_INVALID = 0; + AUTHORIZED_CLIENT_TYPE_CUSTOMER_MOBILE_APP = 1; + AUTHORIZED_CLIENT_TYPE_VEHICLE = 2; +} + +// =================================================================== +// Top-level Message wrapper +// =================================================================== message Message { MessageEnvelope message = 1; Tail tail = 2; } +// =================================================================== +// MessageEnvelope - the core message structure +// +// CRITICAL: field numbers verified against JS uint32() wire tags: +// field 4 = uint32(34) = common +// field 5 = uint32(42) = teg +// field 6 = uint32(50) = wc +// field 9 = uint32(74) = neuriometer +// field 10 = uint32(82) = energysitenet +// field 12 = uint32(98) = authorization +// field 15 = uint32(122) = filestore <-- NOT config, NOT field 17! +// field 16 = uint32(130) = graphql <-- NOT query! +// =================================================================== + message MessageEnvelope { - int32 deliveryChannel = 1; + DeliveryChannel deliveryChannel = 1; Participant sender = 2; Participant recipient = 3; - FirmwareType firmware = 4; - optional ConfigType config = 15; - optional QueryType payload = 16; + + // NO firmware field! NO standalone fields outside this oneof! + oneof payload { + CommonMessages common = 4; + TEGMessages teg = 5; + WCMessages wc = 6; + // fields 7-8 unused + NeurioMeterMessages neuriometer = 9; + EnergySiteNetMessages energysitenet = 10; + // field 11 unused + AuthorizationMessages authorization = 12; + // fields 13-14 unused + FileStoreMessages filestore = 15; // DIRECTLY FileStoreMessages, no wrapper! + GraphQLMessages graphql = 16; + } } message Participant { oneof id { string din = 1; - int32 teslaService = 2; - int32 local = 3; - int32 authorizedClient = 4; + int32 teslaService = 2; // TeslaService enum + int32 local = 3; // LocalParticipant enum (int32 on wire) + int32 authorizedClient = 4; // AuthorizedClientType enum } } @@ -40,269 +126,667 @@ message Tail { int32 value = 1; } -// ***** Query = 4 **** +// =================================================================== +// CommonMessages (field 4 of MessageEnvelope payload oneof) +// +// Contains 45+ request/response pairs. Only error handling and +// key operations defined here. +// =================================================================== -message FirmwareType { - oneof id { - string request = 2; - FirmwarePayload system = 3; +message CommonMessages { + oneof message { + ErrorResponse errorResponse = 1; + CommonAPIGetSystemInfoRequest getSystemInfoRequest = 2; + CommonAPIGetSystemInfoResponse getSystemInfoResponse = 3; + CommonAPISetLocalSiteConfigRequest setLocalSiteConfigRequest = 4; + CommonAPISetLocalSiteConfigResponse setLocalSiteConfigResponse = 5; + CommonAPIPerformUpdateRequest performUpdateRequest = 6; + CommonAPIPerformUpdateResponse performUpdateResponse = 7; + CommonAPIFactoryResetRequest factoryResetRequest = 8; + CommonAPIFactoryResetResponse factoryResetResponse = 9; + CommonAPIWifiScanRequest wifiScanRequest = 10; + CommonAPIWifiScanResponse wifiScanResponse = 11; + CommonAPIConfigureWifiRequest configureWifiRequest = 12; + CommonAPIConfigureWifiResponse configureWifiResponse = 13; + CommonAPICheckForUpdateRequest checkForUpdateRequest = 14; + CommonAPICheckForUpdateResponse checkForUpdateResponse = 15; + CommonAPIClearUpdateRequest clearUpdateRequest = 16; + CommonAPIClearUpdateResponse clearUpdateResponse = 17; + CommonAPIDeviceCertRequest deviceCertRequest = 18; + CommonAPIDeviceCertResponse deviceCertResponse = 19; + CommonAPIConfigureWifiEncryptedRequest configureWifiEncryptedRequest = 20; + CommonAPIConfigureWifiEncryptedResponse configureWifiEncryptedResponse = 21; + CommonAPIGetNetworkingStatusRequest getNetworkingStatusRequest = 22; + CommonAPIGetNetworkingStatusResponse getNetworkingStatusResponse = 23; + CommonAPIGetCellularInfoRequest getCellularInfoRequest = 24; + CommonAPIGetCellularInfoResponse getCellularInfoResponse = 25; + CommonAPIConfigureEthernetRequest configureEthernetRequest = 26; + CommonAPIConfigureEthernetResponse configureEthernetResponse = 27; + CommonAPIForgetWifiNetworkRequest forgetWifiNetworkRequest = 28; + CommonAPIForgetWifiNetworkResponse forgetWifiNetworkResponse = 29; + CommonAPICheckInternetRequest checkInternetRequest = 30; + CommonAPICheckInternetResponse checkInternetResponse = 31; + // fields 32-37: checkForUpdateUrgency, negotiateUpdateLocal, prepareRegistration } } -message FirmwarePayload { - EcuId gateway = 1; - string din = 2; - FirmwareVersion version = 3; - FirmwareFive five = 5; - int32 six = 6; - DeviceArray wireless = 7; - bytes field8 = 8; - bytes field9 = 9; +message ErrorResponse { + Status status = 1; +} + +// google.rpc.Status +message Status { + int32 code = 1; + string message = 2; + // repeated google.protobuf.Any details = 3; // exists but omitted for simplicity } -message EcuId { - string partNumber = 1; - string serialNumber = 2; +// Placeholder messages for CommonAPI operations +message CommonAPIGetSystemInfoRequest {} +message CommonAPIGetSystemInfoResponse {} +message CommonAPISetLocalSiteConfigRequest {} +message CommonAPISetLocalSiteConfigResponse {} +message CommonAPIPerformUpdateRequest {} +message CommonAPIPerformUpdateResponse {} +message CommonAPIFactoryResetRequest {} +message CommonAPIFactoryResetResponse {} +message CommonAPICheckForUpdateRequest {} +message CommonAPICheckForUpdateResponse {} +message CommonAPIClearUpdateRequest {} +message CommonAPIClearUpdateResponse {} +message CommonAPICheckInternetRequest {} +message CommonAPIForgetWifiNetworkResponse {} + +// =================================================================== +// WiFi messages +// =================================================================== + +message CommonAPIWifiScanRequest { + uint32 maxScanDurationS = 1; + repeated int32 desiredSecurityTypes = 2; // packed + uint32 maximumTotalAps = 3; } -message FirmwareVersion { - string text = 1; - bytes githash = 2; +message CommonAPIWifiScanResponse { + repeated WifiNetwork wifiNetworks = 1; } -message FirmwareFive { - int32 d = 2; +message WifiNetwork { + bytes placeholder = 1; // structure TBD } -message DeviceArray { - repeated DeviceInfo device = 1; +message CommonAPIConfigureWifiRequest { + bool enabled = 1; + WifiConfig wifiConfig = 2; } -message DeviceInfo { - StringValue company = 1; - StringValue model = 2; - StringValue fcc_id = 3; - StringValue ic = 4; +message CommonAPIConfigureWifiResponse { + WifiConfig wifiConfig = 1; + NetworkInterface wifi = 2; } -// ***** Query = 16 ***** +message CommonAPIConfigureWifiEncryptedRequest { + bool enabled = 1; + WifiConfig wifiConfig = 2; + bytes encryptedPassword = 3; +} -message QueryType { // 16 - optional PayloadQuerySend send = 1; - optional PayloadString recv = 2; +message CommonAPIConfigureWifiEncryptedResponse { + WifiConfig wifiConfig = 1; + NetworkInterface wifi = 2; + WifiConfigureResult result = 3; } -message PayloadQuerySend { // 1 - optional int32 num = 1; - optional PayloadString payload = 2; - optional bytes code = 3; - optional StringValue b = 4; +message CommonAPIDeviceCertRequest {} + +message CommonAPIDeviceCertResponse { + int32 format = 1; + bytes deviceCert = 2; } -// ***** Config = 15 ***** +message CommonAPIGetCellularInfoRequest {} -message ConfigType { // 15 - oneof config { - PayloadConfigSend send = 1; - PayloadConfigRecv recv = 2; - } +message CommonAPIGetCellularInfoResponse { + CellularEID eid = 1; } -message PayloadConfigSend { // 1 - int32 num = 1; - string file = 2; +message CommonAPIForgetWifiNetworkRequest { + string ssid = 1; } -message PayloadConfigRecv { // 2 - ConfigString file = 1; - bytes code = 2; +// =================================================================== +// Network configuration messages (verified against APK JS bundle) +// +// IP addresses are fixed32 (4-byte little-endian unsigned integers) +// e.g. 192.168.1.1 = bytes [192,168,1,1] = LE uint32 0x0101A8C0 +// Python: struct.unpack(' FileStoreMessages.readFileRequest(1) +// PayloadConfigSend.num=1,file="config.json" <-> ReadFileReq.domain=1,name="config.json" +// ConfigType.recv(2) <-> FileStoreMessages.readFileResponse(2) +// PayloadConfigRecv.file(1) <-> ReadFileResp.file(1) +// PayloadConfigRecv.code(2) <-> ReadFileResp.hash(2) +// ConfigString.text(100) <-> FileStoreAPIFile.blob(100) // -// RESPONSE - firmware -// 1 { -// 1: 1 -// 2 { -// 1: "1707000-00-J--TG9999999999XP" -// } -// 3 { -// 3: 1 -// } -// 4 { -// 3 { -// 1 { -// 1: "1707000-00-J" -// 2: "TG9999999999XP" -// } -// 2: "1707000-00-J--TG9999999999XP" -// 3 { -// 1: "24.12.6-PW3-AFCI 008bf6ff" <--- PW3 firmware version -// 2: "\000\213\366\...Redacted..." -// } -// 5 { -// 2: 1 -// } -// 6: 4 -// 7 { -// 1 { -// 1 { -// 1: "Quectel" -// } -// 2 { -// 1: "BG95-M2" -// } -// 3 { -// 1: "XMR2020BG95M2" -// } -// 4 { -// 1: "10224A-2020BG95M2" -// } -// } -// 1 { -// 1 { -// 1: "Texas Instruments" -// } -// 2 { -// 1: "WL18MODGI" -// } -// 3 { -// 1: "Z64-WL18DBMOD" -// } -// 4 { -// 1: "451I-WL18DBMOD" -// } -// } -// } -// 8: "\370!s\306\212...Redacted..." -// 9: "\373U\353\322...Redacted..." -// } +// This is why old code that uses ConfigType for reading config.json +// works - it's accidentally wire-compatible with the FileStore API! +// =================================================================== + +// NOT USED - for documentation only +// message ConfigType { +// oneof config { +// PayloadConfigSend send = 1; // wire-compat with readFileRequest +// PayloadConfigRecv recv = 2; // wire-compat with readFileResponse // } // } -// 2 { -// 1: 1 -// } \ No newline at end of file diff --git a/pypowerwall/tedapi/tedapi_pb2.py b/pypowerwall/tedapi/tedapi_pb2.py index c0d8c1d..9efc39d 100644 --- a/pypowerwall/tedapi/tedapi_pb2.py +++ b/pypowerwall/tedapi/tedapi_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: tedapi.proto -# Protobuf Python Version: 4.25.2 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'tedapi.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,49 +24,223 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ctedapi.proto\x12\x06tedapi\"O\n\x07Message\x12(\n\x07message\x18\x01 \x01(\x0b\x32\x17.tedapi.MessageEnvelope\x12\x1a\n\x04tail\x18\x02 \x01(\x0b\x32\x0c.tedapi.Tail\"\x88\x02\n\x0fMessageEnvelope\x12\x17\n\x0f\x64\x65liveryChannel\x18\x01 \x01(\x05\x12#\n\x06sender\x18\x02 \x01(\x0b\x32\x13.tedapi.Participant\x12&\n\trecipient\x18\x03 \x01(\x0b\x32\x13.tedapi.Participant\x12&\n\x08\x66irmware\x18\x04 \x01(\x0b\x32\x14.tedapi.FirmwareType\x12\'\n\x06\x63onfig\x18\x0f \x01(\x0b\x32\x12.tedapi.ConfigTypeH\x00\x88\x01\x01\x12\'\n\x07payload\x18\x10 \x01(\x0b\x32\x11.tedapi.QueryTypeH\x01\x88\x01\x01\x42\t\n\x07_configB\n\n\x08_payload\"g\n\x0bParticipant\x12\r\n\x03\x64in\x18\x01 \x01(\tH\x00\x12\x16\n\x0cteslaService\x18\x02 \x01(\x05H\x00\x12\x0f\n\x05local\x18\x03 \x01(\x05H\x00\x12\x1a\n\x10\x61uthorizedClient\x18\x04 \x01(\x05H\x00\x42\x04\n\x02id\"\x15\n\x04Tail\x12\r\n\x05value\x18\x01 \x01(\x05\"R\n\x0c\x46irmwareType\x12\x11\n\x07request\x18\x02 \x01(\tH\x00\x12)\n\x06system\x18\x03 \x01(\x0b\x32\x17.tedapi.FirmwarePayloadH\x00\x42\x04\n\x02id\"\xe0\x01\n\x0f\x46irmwarePayload\x12\x1e\n\x07gateway\x18\x01 \x01(\x0b\x32\r.tedapi.EcuId\x12\x0b\n\x03\x64in\x18\x02 \x01(\t\x12(\n\x07version\x18\x03 \x01(\x0b\x32\x17.tedapi.FirmwareVersion\x12\"\n\x04\x66ive\x18\x05 \x01(\x0b\x32\x14.tedapi.FirmwareFive\x12\x0b\n\x03six\x18\x06 \x01(\x05\x12%\n\x08wireless\x18\x07 \x01(\x0b\x32\x13.tedapi.DeviceArray\x12\x0e\n\x06\x66ield8\x18\x08 \x01(\x0c\x12\x0e\n\x06\x66ield9\x18\t \x01(\x0c\"1\n\x05\x45\x63uId\x12\x12\n\npartNumber\x18\x01 \x01(\t\x12\x14\n\x0cserialNumber\x18\x02 \x01(\t\"0\n\x0f\x46irmwareVersion\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x0f\n\x07githash\x18\x02 \x01(\x0c\"\x19\n\x0c\x46irmwareFive\x12\t\n\x01\x64\x18\x02 \x01(\x05\"1\n\x0b\x44\x65viceArray\x12\"\n\x06\x64\x65vice\x18\x01 \x03(\x0b\x32\x12.tedapi.DeviceInfo\"\x9c\x01\n\nDeviceInfo\x12$\n\x07\x63ompany\x18\x01 \x01(\x0b\x32\x13.tedapi.StringValue\x12\"\n\x05model\x18\x02 \x01(\x0b\x32\x13.tedapi.StringValue\x12#\n\x06\x66\x63\x63_id\x18\x03 \x01(\x0b\x32\x13.tedapi.StringValue\x12\x1f\n\x02ic\x18\x04 \x01(\x0b\x32\x13.tedapi.StringValue\"t\n\tQueryType\x12+\n\x04send\x18\x01 \x01(\x0b\x32\x18.tedapi.PayloadQuerySendH\x00\x88\x01\x01\x12(\n\x04recv\x18\x02 \x01(\x0b\x32\x15.tedapi.PayloadStringH\x01\x88\x01\x01\x42\x07\n\x05_sendB\x07\n\x05_recv\"\xac\x01\n\x10PayloadQuerySend\x12\x10\n\x03num\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12+\n\x07payload\x18\x02 \x01(\x0b\x32\x15.tedapi.PayloadStringH\x01\x88\x01\x01\x12\x11\n\x04\x63ode\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12#\n\x01\x62\x18\x04 \x01(\x0b\x32\x13.tedapi.StringValueH\x03\x88\x01\x01\x42\x06\n\x04_numB\n\n\x08_payloadB\x07\n\x05_codeB\x04\n\x02_b\"l\n\nConfigType\x12)\n\x04send\x18\x01 \x01(\x0b\x32\x19.tedapi.PayloadConfigSendH\x00\x12)\n\x04recv\x18\x02 \x01(\x0b\x32\x19.tedapi.PayloadConfigRecvH\x00\x42\x08\n\x06\x63onfig\".\n\x11PayloadConfigSend\x12\x0b\n\x03num\x18\x01 \x01(\x05\x12\x0c\n\x04\x66ile\x18\x02 \x01(\t\"E\n\x11PayloadConfigRecv\x12\"\n\x04\x66ile\x18\x01 \x01(\x0b\x32\x14.tedapi.ConfigString\x12\x0c\n\x04\x63ode\x18\x02 \x01(\x0c\"*\n\x0c\x43onfigString\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x64 \x01(\t\",\n\rPayloadString\x12\r\n\x05value\x18\x01 \x01(\x05\x12\x0c\n\x04text\x18\x02 \x01(\t\"\x1c\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\tb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ctedapi.proto\x12\x06tedapi\"U\n\x0c\x41uthEnvelope\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\x12,\n\x0c\x65xternalAuth\x18\x02 \x01(\x0b\x32\x14.tedapi.ExternalAuthH\x00\x42\x06\n\x04\x61uth\"6\n\x0c\x45xternalAuth\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.tedapi.ExternalAuthType\"O\n\x07Message\x12(\n\x07message\x18\x01 \x01(\x0b\x32\x17.tedapi.MessageEnvelope\x12\x1a\n\x04tail\x18\x02 \x01(\x0b\x32\x0c.tedapi.Tail\"\x8b\x04\n\x0fMessageEnvelope\x12\x30\n\x0f\x64\x65liveryChannel\x18\x01 \x01(\x0e\x32\x17.tedapi.DeliveryChannel\x12#\n\x06sender\x18\x02 \x01(\x0b\x32\x13.tedapi.Participant\x12&\n\trecipient\x18\x03 \x01(\x0b\x32\x13.tedapi.Participant\x12(\n\x06\x63ommon\x18\x04 \x01(\x0b\x32\x16.tedapi.CommonMessagesH\x00\x12\"\n\x03teg\x18\x05 \x01(\x0b\x32\x13.tedapi.TEGMessagesH\x00\x12 \n\x02wc\x18\x06 \x01(\x0b\x32\x12.tedapi.WCMessagesH\x00\x12\x32\n\x0bneuriometer\x18\t \x01(\x0b\x32\x1b.tedapi.NeurioMeterMessagesH\x00\x12\x36\n\renergysitenet\x18\n \x01(\x0b\x32\x1d.tedapi.EnergySiteNetMessagesH\x00\x12\x36\n\rauthorization\x18\x0c \x01(\x0b\x32\x1d.tedapi.AuthorizationMessagesH\x00\x12.\n\tfilestore\x18\x0f \x01(\x0b\x32\x19.tedapi.FileStoreMessagesH\x00\x12*\n\x07graphql\x18\x10 \x01(\x0b\x32\x17.tedapi.GraphQLMessagesH\x00\x42\t\n\x07payload\"g\n\x0bParticipant\x12\r\n\x03\x64in\x18\x01 \x01(\tH\x00\x12\x16\n\x0cteslaService\x18\x02 \x01(\x05H\x00\x12\x0f\n\x05local\x18\x03 \x01(\x05H\x00\x12\x1a\n\x10\x61uthorizedClient\x18\x04 \x01(\x05H\x00\x42\x04\n\x02id\"\x15\n\x04Tail\x12\r\n\x05value\x18\x01 \x01(\x05\"\x8b\x12\n\x0e\x43ommonMessages\x12.\n\rerrorResponse\x18\x01 \x01(\x0b\x32\x15.tedapi.ErrorResponseH\x00\x12\x45\n\x14getSystemInfoRequest\x18\x02 \x01(\x0b\x32%.tedapi.CommonAPIGetSystemInfoRequestH\x00\x12G\n\x15getSystemInfoResponse\x18\x03 \x01(\x0b\x32&.tedapi.CommonAPIGetSystemInfoResponseH\x00\x12O\n\x19setLocalSiteConfigRequest\x18\x04 \x01(\x0b\x32*.tedapi.CommonAPISetLocalSiteConfigRequestH\x00\x12Q\n\x1asetLocalSiteConfigResponse\x18\x05 \x01(\x0b\x32+.tedapi.CommonAPISetLocalSiteConfigResponseH\x00\x12\x45\n\x14performUpdateRequest\x18\x06 \x01(\x0b\x32%.tedapi.CommonAPIPerformUpdateRequestH\x00\x12G\n\x15performUpdateResponse\x18\x07 \x01(\x0b\x32&.tedapi.CommonAPIPerformUpdateResponseH\x00\x12\x43\n\x13\x66\x61\x63toryResetRequest\x18\x08 \x01(\x0b\x32$.tedapi.CommonAPIFactoryResetRequestH\x00\x12\x45\n\x14\x66\x61\x63toryResetResponse\x18\t \x01(\x0b\x32%.tedapi.CommonAPIFactoryResetResponseH\x00\x12;\n\x0fwifiScanRequest\x18\n \x01(\x0b\x32 .tedapi.CommonAPIWifiScanRequestH\x00\x12=\n\x10wifiScanResponse\x18\x0b \x01(\x0b\x32!.tedapi.CommonAPIWifiScanResponseH\x00\x12\x45\n\x14\x63onfigureWifiRequest\x18\x0c \x01(\x0b\x32%.tedapi.CommonAPIConfigureWifiRequestH\x00\x12G\n\x15\x63onfigureWifiResponse\x18\r \x01(\x0b\x32&.tedapi.CommonAPIConfigureWifiResponseH\x00\x12G\n\x15\x63heckForUpdateRequest\x18\x0e \x01(\x0b\x32&.tedapi.CommonAPICheckForUpdateRequestH\x00\x12I\n\x16\x63heckForUpdateResponse\x18\x0f \x01(\x0b\x32\'.tedapi.CommonAPICheckForUpdateResponseH\x00\x12\x41\n\x12\x63learUpdateRequest\x18\x10 \x01(\x0b\x32#.tedapi.CommonAPIClearUpdateRequestH\x00\x12\x43\n\x13\x63learUpdateResponse\x18\x11 \x01(\x0b\x32$.tedapi.CommonAPIClearUpdateResponseH\x00\x12?\n\x11\x64\x65viceCertRequest\x18\x12 \x01(\x0b\x32\".tedapi.CommonAPIDeviceCertRequestH\x00\x12\x41\n\x12\x64\x65viceCertResponse\x18\x13 \x01(\x0b\x32#.tedapi.CommonAPIDeviceCertResponseH\x00\x12W\n\x1d\x63onfigureWifiEncryptedRequest\x18\x14 \x01(\x0b\x32..tedapi.CommonAPIConfigureWifiEncryptedRequestH\x00\x12Y\n\x1e\x63onfigureWifiEncryptedResponse\x18\x15 \x01(\x0b\x32/.tedapi.CommonAPIConfigureWifiEncryptedResponseH\x00\x12Q\n\x1agetNetworkingStatusRequest\x18\x16 \x01(\x0b\x32+.tedapi.CommonAPIGetNetworkingStatusRequestH\x00\x12S\n\x1bgetNetworkingStatusResponse\x18\x17 \x01(\x0b\x32,.tedapi.CommonAPIGetNetworkingStatusResponseH\x00\x12I\n\x16getCellularInfoRequest\x18\x18 \x01(\x0b\x32\'.tedapi.CommonAPIGetCellularInfoRequestH\x00\x12K\n\x17getCellularInfoResponse\x18\x19 \x01(\x0b\x32(.tedapi.CommonAPIGetCellularInfoResponseH\x00\x12M\n\x18\x63onfigureEthernetRequest\x18\x1a \x01(\x0b\x32).tedapi.CommonAPIConfigureEthernetRequestH\x00\x12O\n\x19\x63onfigureEthernetResponse\x18\x1b \x01(\x0b\x32*.tedapi.CommonAPIConfigureEthernetResponseH\x00\x12M\n\x18\x66orgetWifiNetworkRequest\x18\x1c \x01(\x0b\x32).tedapi.CommonAPIForgetWifiNetworkRequestH\x00\x12O\n\x19\x66orgetWifiNetworkResponse\x18\x1d \x01(\x0b\x32*.tedapi.CommonAPIForgetWifiNetworkResponseH\x00\x12\x45\n\x14\x63heckInternetRequest\x18\x1e \x01(\x0b\x32%.tedapi.CommonAPICheckInternetRequestH\x00\x12G\n\x15\x63heckInternetResponse\x18\x1f \x01(\x0b\x32&.tedapi.CommonAPICheckInternetResponseH\x00\x42\t\n\x07message\"/\n\rErrorResponse\x12\x1e\n\x06status\x18\x01 \x01(\x0b\x32\x0e.tedapi.Status\"\'\n\x06Status\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x1f\n\x1d\x43ommonAPIGetSystemInfoRequest\" \n\x1e\x43ommonAPIGetSystemInfoResponse\"$\n\"CommonAPISetLocalSiteConfigRequest\"%\n#CommonAPISetLocalSiteConfigResponse\"\x1f\n\x1d\x43ommonAPIPerformUpdateRequest\" \n\x1e\x43ommonAPIPerformUpdateResponse\"\x1e\n\x1c\x43ommonAPIFactoryResetRequest\"\x1f\n\x1d\x43ommonAPIFactoryResetResponse\" \n\x1e\x43ommonAPICheckForUpdateRequest\"!\n\x1f\x43ommonAPICheckForUpdateResponse\"\x1d\n\x1b\x43ommonAPIClearUpdateRequest\"\x1e\n\x1c\x43ommonAPIClearUpdateResponse\"\x1f\n\x1d\x43ommonAPICheckInternetRequest\"$\n\"CommonAPIForgetWifiNetworkResponse\"k\n\x18\x43ommonAPIWifiScanRequest\x12\x18\n\x10maxScanDurationS\x18\x01 \x01(\r\x12\x1c\n\x14\x64\x65siredSecurityTypes\x18\x02 \x03(\x05\x12\x17\n\x0fmaximumTotalAps\x18\x03 \x01(\r\"F\n\x19\x43ommonAPIWifiScanResponse\x12)\n\x0cwifiNetworks\x18\x01 \x03(\x0b\x32\x13.tedapi.WifiNetwork\"\"\n\x0bWifiNetwork\x12\x13\n\x0bplaceholder\x18\x01 \x01(\x0c\"X\n\x1d\x43ommonAPIConfigureWifiRequest\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12&\n\nwifiConfig\x18\x02 \x01(\x0b\x32\x12.tedapi.WifiConfig\"p\n\x1e\x43ommonAPIConfigureWifiResponse\x12&\n\nwifiConfig\x18\x01 \x01(\x0b\x32\x12.tedapi.WifiConfig\x12&\n\x04wifi\x18\x02 \x01(\x0b\x32\x18.tedapi.NetworkInterface\"|\n&CommonAPIConfigureWifiEncryptedRequest\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12&\n\nwifiConfig\x18\x02 \x01(\x0b\x32\x12.tedapi.WifiConfig\x12\x19\n\x11\x65ncryptedPassword\x18\x03 \x01(\x0c\"\xa6\x01\n\'CommonAPIConfigureWifiEncryptedResponse\x12&\n\nwifiConfig\x18\x01 \x01(\x0b\x32\x12.tedapi.WifiConfig\x12&\n\x04wifi\x18\x02 \x01(\x0b\x32\x18.tedapi.NetworkInterface\x12+\n\x06result\x18\x03 \x01(\x0e\x32\x1b.tedapi.WifiConfigureResult\"\x1c\n\x1a\x43ommonAPIDeviceCertRequest\"A\n\x1b\x43ommonAPIDeviceCertResponse\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\x05\x12\x12\n\ndeviceCert\x18\x02 \x01(\x0c\"!\n\x1f\x43ommonAPIGetCellularInfoRequest\"D\n CommonAPIGetCellularInfoResponse\x12 \n\x03\x65id\x18\x01 \x01(\x0b\x32\x13.tedapi.CellularEID\"1\n!CommonAPIForgetWifiNetworkRequest\x12\x0c\n\x04ssid\x18\x01 \x01(\t\"%\n#CommonAPIGetNetworkingStatusRequest\"\xc4\x01\n$CommonAPIGetNetworkingStatusResponse\x12&\n\nwifiConfig\x18\x01 \x01(\x0b\x32\x12.tedapi.WifiConfig\x12&\n\x04wifi\x18\x02 \x01(\x0b\x32\x18.tedapi.NetworkInterface\x12%\n\x03\x65th\x18\x03 \x01(\x0b\x32\x18.tedapi.NetworkInterface\x12%\n\x03gsm\x18\x04 \x01(\x0b\x32\x18.tedapi.NetworkInterface\"[\n!CommonAPIConfigureEthernetRequest\x12\x36\n\nipv4Config\x18\x01 \x01(\x0b\x32\".tedapi.NetworkInterfaceIPv4Config\"K\n\"CommonAPIConfigureEthernetResponse\x12%\n\x03\x65th\x18\x01 \x01(\x0b\x32\x18.tedapi.NetworkInterface\"\x96\x01\n\x1e\x43ommonAPICheckInternetResponse\x12&\n\x04wifi\x18\x01 \x01(\x0b\x32\x18.tedapi.NetworkInterface\x12%\n\x03\x65th\x18\x02 \x01(\x0b\x32\x18.tedapi.NetworkInterface\x12%\n\x03gsm\x18\x03 \x01(\x0b\x32\x18.tedapi.NetworkInterface\"t\n\x1aNetworkInterfaceIPv4Config\x12\x13\n\x0b\x64hcpEnabled\x18\x02 \x01(\x08\x12\x0f\n\x07\x61\x64\x64ress\x18\x03 \x01(\x07\x12\x12\n\nsubnetMask\x18\x04 \x01(\x07\x12\x0f\n\x07gateway\x18\x05 \x01(\x07\x12\x0b\n\x03\x64ns\x18\x06 \x03(\x07\"\xc3\x01\n\x10NetworkInterface\x12\x12\n\nmacAddress\x18\x01 \x01(\x0c\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x13\n\x0b\x61\x63tiveRoute\x18\x03 \x01(\x08\x12\x36\n\nipv4Config\x18\x04 \x01(\x0b\x32\".tedapi.NetworkInterfaceIPv4Config\x12=\n\x12\x63onnectivityStatus\x18\x05 \x01(\x0b\x32!.tedapi.NetworkConnectivityStatus\"i\n\x19NetworkConnectivityStatus\x12\x19\n\x11\x63onnectedPhysical\x18\x01 \x01(\x08\x12\x19\n\x11\x63onnectedInternet\x18\x02 \x01(\x08\x12\x16\n\x0e\x63onnectedTesla\x18\x03 \x01(\x08\"y\n\nWifiConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12&\n\x08password\x18\x02 \x01(\x0b\x32\x14.tedapi.WifiPassword\x12\x35\n\x0csecurityType\x18\x03 \x01(\x0e\x32\x1f.tedapi.WifiNetworkSecurityType\"\x1d\n\x0cWifiPassword\x12\r\n\x05value\x18\x01 \x01(\t\"\x1c\n\x0b\x43\x65llularEID\x12\r\n\x05value\x18\x01 \x01(\t\"\"\n\x0bTEGMessages\x12\x13\n\x0bplaceholder\x18\x01 \x01(\x0c\"!\n\nWCMessages\x12\x13\n\x0bplaceholder\x18\x01 \x01(\x0c\"@\n\x0eNeurioCTConfig\x12\x10\n\x08location\x18\x01 \x01(\x05\x12\x1c\n\x14realPowerScaleFactor\x18\x02 \x01(\x02\"^\n\x11NeurioMeterConfig\x12\x0f\n\x07shortId\x18\x01 \x01(\t\x12\x0e\n\x06serial\x18\x02 \x01(\t\x12(\n\x08\x63tConfig\x18\x03 \x03(\x0b\x32\x16.tedapi.NeurioCTConfig\"\xf2\x01\n\x15NeurioMeterConnection\x12\x38\n\x10\x63onnectionStatus\x18\x01 \x01(\x0e\x32\x1e.tedapi.NeurioConnectionStatus\x12\x36\n\x0f\x63onnectionError\x18\x02 \x01(\x0e\x32\x1d.tedapi.NeurioConnectionError\x12\x1a\n\x04rssi\x18\x03 \x01(\x0b\x32\x0c.tedapi.Rssi\x12\x17\n\x0f\x66irmwareVersion\x18\x04 \x01(\t\x12\x32\n\rmeterReadings\x18\x05 \x01(\x0b\x32\x1b.tedapi.NeurioMeterReadings\"4\n\x04Rssi\x12\r\n\x05value\x18\x01 \x01(\x11\x12\x1d\n\x15signalStrengthPercent\x18\x02 \x01(\r\"T\n\x0fNeurioCTReading\x12\x12\n\nrealPowerW\x18\x01 \x01(\x02\x12\x18\n\x10scaledRealPowerW\x18\x02 \x01(\x02\x12\x13\n\x0b\x63urrentAmps\x18\x03 \x01(\x02\"B\n\x13NeurioMeterReadings\x12+\n\nctReadings\x18\x01 \x03(\x0b\x32\x17.tedapi.NeurioCTReading\"t\n\x14NeurioMeterInterface\x12)\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x19.tedapi.NeurioMeterConfig\x12\x31\n\nconnection\x18\x02 \x01(\x0b\x32\x1d.tedapi.NeurioMeterConnection\"J\n\x1dNeurioMeterAPIAddMeterRequest\x12)\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x19.tedapi.NeurioMeterConfig\"K\n\x1eNeurioMeterAPIAddMeterResponse\x12)\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x19.tedapi.NeurioMeterConfig\"2\n NeurioMeterAPIRemoveMeterRequest\x12\x0e\n\x06serial\x18\x01 \x01(\t\"#\n!NeurioMeterAPIRemoveMeterResponse\"]\n!NeurioMeterAPIConfigureCtsRequest\x12\x0e\n\x06serial\x18\x01 \x01(\t\x12(\n\x08\x63tConfig\x18\x02 \x03(\x0b\x32\x16.tedapi.NeurioCTConfig\"N\n\"NeurioMeterAPIConfigureCtsResponse\x12(\n\x08\x63tConfig\x18\x01 \x03(\x0b\x32\x16.tedapi.NeurioCTConfig\"\xce\x03\n\x13NeurioMeterMessages\x12@\n\x0f\x61\x64\x64MeterRequest\x18\x01 \x01(\x0b\x32%.tedapi.NeurioMeterAPIAddMeterRequestH\x00\x12\x42\n\x10\x61\x64\x64MeterResponse\x18\x02 \x01(\x0b\x32&.tedapi.NeurioMeterAPIAddMeterResponseH\x00\x12\x46\n\x12removeMeterRequest\x18\x03 \x01(\x0b\x32(.tedapi.NeurioMeterAPIRemoveMeterRequestH\x00\x12H\n\x13removeMeterResponse\x18\x04 \x01(\x0b\x32).tedapi.NeurioMeterAPIRemoveMeterResponseH\x00\x12H\n\x13\x63onfigureCtsRequest\x18\x05 \x01(\x0b\x32).tedapi.NeurioMeterAPIConfigureCtsRequestH\x00\x12J\n\x14\x63onfigureCtsResponse\x18\x06 \x01(\x0b\x32*.tedapi.NeurioMeterAPIConfigureCtsResponseH\x00\x42\t\n\x07message\",\n\x15\x45nergySiteNetMessages\x12\x13\n\x0bplaceholder\x18\x01 \x01(\x0c\"\xd4\x02\n\x13\x41uthorizationRecord\x12*\n\x04type\x18\x01 \x01(\x0e\x32\x1c.tedapi.AuthorizedClientType\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12*\n\x07keyType\x18\x03 \x01(\x0e\x32\x19.tedapi.AuthorizedKeyType\x12\x11\n\tpublicKey\x18\x04 \x01(\x0c\x12(\n\x05roles\x18\x05 \x03(\x0e\x32\x19.tedapi.AuthorizationRole\x12&\n\x05state\x18\x06 \x01(\x0e\x32\x17.tedapi.AuthorizedState\x12\x38\n\x0cverification\x18\x07 \x01(\x0e\x32\".tedapi.AuthorizedVerificationType\x12\x12\n\nidentifier\x18\t \x01(\t\x12\x1d\n\x15\x61uthorizedByPublicKey\x18\n \x01(\x0c\"\xea\t\n\x15\x41uthorizationMessages\x12X\n\x1a\x61\x64\x64\x41uthorizedClientRequest\x18\x01 \x01(\x0b\x32\x32.tedapi.AuthorizationAPIAddAuthorizedClientRequestH\x00\x12Z\n\x1b\x61\x64\x64\x41uthorizedClientResponse\x18\x02 \x01(\x0b\x32\x33.tedapi.AuthorizationAPIAddAuthorizedClientResponseH\x00\x12^\n\x1dremoveAuthorizedClientRequest\x18\x03 \x01(\x0b\x32\x35.tedapi.AuthorizationAPIRemoveAuthorizedClientRequestH\x00\x12`\n\x1eremoveAuthorizedClientResponse\x18\x04 \x01(\x0b\x32\x36.tedapi.AuthorizationAPIRemoveAuthorizedClientResponseH\x00\x12\\\n\x1clistAuthorizedClientsRequest\x18\x05 \x01(\x0b\x32\x34.tedapi.AuthorizationAPIListAuthorizedClientsRequestH\x00\x12^\n\x1dlistAuthorizedClientsResponse\x18\x06 \x01(\x0b\x32\x35.tedapi.AuthorizationAPIListAuthorizedClientsResponseH\x00\x12\x66\n!getSignedCommandsPublicKeyRequest\x18\x07 \x01(\x0b\x32\x39.tedapi.AuthorizationAPIGetSignedCommandsPublicKeyRequestH\x00\x12h\n\"getSignedCommandsPublicKeyResponse\x18\x08 \x01(\x0b\x32:.tedapi.AuthorizationAPIGetSignedCommandsPublicKeyResponseH\x00\x12|\n,addAuthorizedClientByTrustedSignatureRequest\x18\t \x01(\x0b\x32\x44.tedapi.AuthorizationAPIAddAuthorizedClientByTrustedSignatureRequestH\x00\x12~\n-addAuthorizedClientByTrustedSignatureResponse\x18\n \x01(\x0b\x32\x45.tedapi.AuthorizationAPIAddAuthorizedClientByTrustedSignatureResponseH\x00\x12^\n\x1d\x63onfigureRemoteServiceRequest\x18\x0b \x01(\x0b\x32\x35.tedapi.AuthorizationAPIConfigureRemoteServiceRequestH\x00\x12`\n\x1e\x63onfigureRemoteServiceResponse\x18\x0c \x01(\x0b\x32\x36.tedapi.AuthorizationAPIConfigureRemoteServiceResponseH\x00\x42\t\n\x07message\"\xac\x01\n*AuthorizationAPIAddAuthorizedClientRequest\x12*\n\x04type\x18\x01 \x01(\x0e\x32\x1c.tedapi.AuthorizedClientType\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12*\n\x07keyType\x18\x03 \x01(\x0e\x32\x19.tedapi.AuthorizedKeyType\x12\x11\n\tpublicKey\x18\x04 \x01(\x0c\"Z\n+AuthorizationAPIAddAuthorizedClientResponse\x12+\n\x06\x63lient\x18\x01 \x01(\x0b\x32\x1b.tedapi.AuthorizationRecord\"B\n-AuthorizationAPIRemoveAuthorizedClientRequest\x12\x11\n\tpublicKey\x18\x01 \x01(\x0c\"0\n.AuthorizationAPIRemoveAuthorizedClientResponse\".\n,AuthorizationAPIListAuthorizedClientsRequest\"z\n-AuthorizationAPIListAuthorizedClientsResponse\x12,\n\x07\x63lients\x18\x01 \x03(\x0b\x32\x1b.tedapi.AuthorizationRecord\x12\x1b\n\x13\x65nableLineSwitchOff\x18\x02 \x01(\x08\"3\n1AuthorizationAPIGetSignedCommandsPublicKeyRequest\"G\n2AuthorizationAPIGetSignedCommandsPublicKeyResponse\x12\x11\n\tpubKeyEcc\x18\x01 \x01(\x0c\"\xfc\x01\n\n\x0freadFileRequest\x18\x01 \x01(\x0b\x32#.tedapi.FileStoreAPIReadFileRequestH\x00\x12@\n\x10readFileResponse\x18\x02 \x01(\x0b\x32$.tedapi.FileStoreAPIReadFileResponseH\x00\x12J\n\x15\x66orceWriteFileRequest\x18\x03 \x01(\x0b\x32).tedapi.FileStoreAPIForceWriteFileRequestH\x00\x12L\n\x16\x66orceWriteFileResponse\x18\x04 \x01(\x0b\x32*.tedapi.FileStoreAPIForceWriteFileResponseH\x00\x12\x42\n\x11updateFileRequest\x18\x05 \x01(\x0b\x32%.tedapi.FileStoreAPIUpdateFileRequestH\x00\x12\x44\n\x12updateFileResponse\x18\x06 \x01(\x0b\x32&.tedapi.FileStoreAPIUpdateFileResponseH\x00\x42\t\n\x07message\";\n\x10\x46ileStoreAPIFile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x04\x62lob\x18\x64 \x01(\x0cH\x00\x42\t\n\x07\x63ontent\"p\n\x1b\x46ileStoreAPIReadFileRequest\x12*\n\x06\x64omain\x18\x01 \x01(\x0e\x32\x1a.tedapi.FileStoreAPIDomain\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x17\n\x0fifDifferentHash\x18\x03 \x01(\x0c\"T\n\x1c\x46ileStoreAPIReadFileResponse\x12&\n\x04\x66ile\x18\x01 \x01(\x0b\x32\x18.tedapi.FileStoreAPIFile\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"\x81\x01\n\x1d\x46ileStoreAPIUpdateFileRequest\x12*\n\x06\x64omain\x18\x01 \x01(\x0e\x32\x1a.tedapi.FileStoreAPIDomain\x12&\n\x04\x66ile\x18\x02 \x01(\x0b\x32\x18.tedapi.FileStoreAPIFile\x12\x0c\n\x04hash\x18\x03 \x01(\x0c\"V\n\x1e\x46ileStoreAPIUpdateFileResponse\x12&\n\x04\x66ile\x18\x01 \x01(\x0b\x32\x18.tedapi.FileStoreAPIFile\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n!FileStoreAPIForceWriteFileRequest\x12*\n\x06\x64omain\x18\x01 \x01(\x0e\x32\x1a.tedapi.FileStoreAPIDomain\x12&\n\x04\x66ile\x18\x02 \x01(\x0b\x32\x18.tedapi.FileStoreAPIFile\"2\n\"FileStoreAPIForceWriteFileResponse\x12\x0c\n\x04hash\x18\x01 \x01(\x0c*\x97\x01\n\x10\x45xternalAuthType\x12\x1e\n\x1a\x45XTERNAL_AUTH_TYPE_INVALID\x10\x00\x12\x1f\n\x1b\x45XTERNAL_AUTH_TYPE_PRESENCE\x10\x01\x12\x1b\n\x17\x45XTERNAL_AUTH_TYPE_MTLS\x10\x02\x12%\n!EXTERNAL_AUTH_TYPE_HERMES_COMMAND\x10\x04*\x90\x01\n\x0f\x44\x65liveryChannel\x12\x1c\n\x18\x44\x45LIVERY_CHANNEL_INVALID\x10\x00\x12 \n\x1c\x44\x45LIVERY_CHANNEL_LOCAL_HTTPS\x10\x01\x12#\n\x1f\x44\x45LIVERY_CHANNEL_HERMES_COMMAND\x10\x02\x12\x18\n\x14\x44\x45LIVERY_CHANNEL_BLE\x10\x03*r\n\x10LocalParticipant\x12\x1d\n\x19LOCAL_PARTICIPANT_INVALID\x10\x00\x12\x1f\n\x1bLOCAL_PARTICIPANT_INSTALLER\x10\x01\x12\x1e\n\x1aLOCAL_PARTICIPANT_CUSTOMER\x10\x02*D\n\x0cTeslaService\x12\x19\n\x15TESLA_SERVICE_INVALID\x10\x00\x12\x19\n\x15TESLA_SERVICE_COMMAND\x10\x01*\x8e\x01\n\x14\x41uthorizedClientType\x12\"\n\x1e\x41UTHORIZED_CLIENT_TYPE_INVALID\x10\x00\x12.\n*AUTHORIZED_CLIENT_TYPE_CUSTOMER_MOBILE_APP\x10\x01\x12\"\n\x1e\x41UTHORIZED_CLIENT_TYPE_VEHICLE\x10\x02*\xf0\x01\n\x17WifiNetworkSecurityType\x12&\n\"WIFI_NETWORK_SECURITY_TYPE_INVALID\x10\x00\x12#\n\x1fWIFI_NETWORK_SECURITY_TYPE_NONE\x10\x01\x12*\n&WIFI_NETWORK_SECURITY_TYPE_DYNAMIC_WEP\x10\x02\x12,\n(WIFI_NETWORK_SECURITY_TYPE_WPA2_PERSONAL\x10\x03\x12.\n*WIFI_NETWORK_SECURITY_TYPE_WPA2_ENTERPRISE\x10\x04*\xe9\x01\n\x13WifiConfigureResult\x12!\n\x1dWIFI_CONFIGURE_RESULT_INVALID\x10\x00\x12!\n\x1dWIFI_CONFIGURE_RESULT_SUCCESS\x10\x01\x12)\n%WIFI_CONFIGURE_RESULT_FAILURE_GENERIC\x10\x02\x12\x34\n0WIFI_CONFIGURE_RESULT_FAILED_WITH_INVALID_CONFIG\x10\x03\x12+\n\'WIFI_CONFIGURE_RESULT_FAILED_TO_CONNECT\x10\x04*\xe8\x01\n\x16NeurioConnectionStatus\x12$\n NEURIO_CONNECTION_STATUS_INVALID\x10\x00\x12%\n!NEURIO_CONNECTION_STATUS_NO_COMMS\x10\x01\x12$\n NEURIO_CONNECTION_STATUS_PAIRING\x10\x02\x12&\n\"NEURIO_CONNECTION_STATUS_CONNECTED\x10\x03\x12\x33\n/NEURIO_CONNECTION_STATUS_CONFIG_CHANGE_UNDERWAY\x10\x04*\x81\x02\n\x15NeurioConnectionError\x12#\n\x1fNEURIO_CONNECTION_ERROR_INVALID\x10\x00\x12 \n\x1cNEURIO_CONNECTION_ERROR_NONE\x10\x01\x12#\n\x1fNEURIO_CONNECTION_ERROR_UNKNOWN\x10\x02\x12#\n\x1fNEURIO_CONNECTION_ERROR_WIFI_AP\x10\x03\x12+\n\'NEURIO_CONNECTION_ERROR_PAIRING_COMMAND\x10\x04\x12*\n&NEURIO_CONNECTION_ERROR_REBOOT_COMMAND\x10\x05*t\n\x11\x41uthorizationRole\x12\x1e\n\x1a\x41UTHORIZATION_ROLE_INVALID\x10\x00\x12\x1f\n\x1b\x41UTHORIZATION_ROLE_CUSTOMER\x10\x01\x12\x1e\n\x1a\x41UTHORIZATION_ROLE_VEHICLE\x10\x02*\xca\x01\n\x0f\x41uthorizedState\x12\x1c\n\x18\x41UTHORIZED_STATE_INVALID\x10\x00\x12)\n%AUTHORIZED_STATE_PENDING_VERIFICATION\x10\x01\x12\x31\n-AUTHORIZED_STATE_PENDING_VERIFICATION_TIMEOUT\x10\x02\x12\x1d\n\x19\x41UTHORIZED_STATE_VERIFIED\x10\x03\x12\x1c\n\x18\x41UTHORIZED_STATE_REMOVED\x10\x04*\xa8\x01\n\x1a\x41uthorizedVerificationType\x12(\n$AUTHORIZED_VERIFICATION_TYPE_INVALID\x10\x00\x12/\n+AUTHORIZED_VERIFICATION_TYPE_PRESENCE_PROOF\x10\x01\x12/\n+AUTHORIZED_VERIFICATION_TYPE_HERMES_COMMAND\x10\x04*n\n\x11\x41uthorizedKeyType\x12\x1f\n\x1b\x41UTHORIZED_KEY_TYPE_INVALID\x10\x00\x12\x1b\n\x17\x41UTHORIZED_KEY_TYPE_RSA\x10\x01\x12\x1b\n\x17\x41UTHORIZED_KEY_TYPE_ECC\x10\x02*\x8a\x01\n\x12GraphQLQueryFormat\x12!\n\x1dGRAPH_QL_QUERY_FORMAT_INVALID\x10\x00\x12\x1d\n\x19GRAPH_QL_QUERY_FORMAT_RAW\x10\x01\x12\x32\n.GRAPH_QL_QUERY_FORMAT_SIGNED_SHA256_ECDSA_ASN1\x10\x02*\xf4\x02\n\x12\x46ileStoreAPIDomain\x12!\n\x1d\x46ILE_STORE_API_DOMAIN_INVALID\x10\x00\x12%\n!FILE_STORE_API_DOMAIN_CONFIG_JSON\x10\x01\x12/\n+FILE_STORE_API_DOMAIN_GRID_CODE_REGIONS_CSV\x10\x02\x12\x32\n.FILE_STORE_API_DOMAIN_CERTIFIED_INSTALLERS_CSV\x10\x03\x12,\n(FILE_STORE_API_DOMAIN_SUPERCHARGER_FILES\x10\x04\x12*\n&FILE_STORE_API_DOMAIN_OPTICASTER_FILES\x10\x05\x12(\n$FILE_STORE_API_DOMAIN_WALLBOX_CONFIG\x10\x06\x12+\n\'FILE_STORE_API_DOMAIN_OCPP_CSMS_ROOT_CA\x10\x07\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tedapi_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_MESSAGE']._serialized_start=24 - _globals['_MESSAGE']._serialized_end=103 - _globals['_MESSAGEENVELOPE']._serialized_start=106 - _globals['_MESSAGEENVELOPE']._serialized_end=370 - _globals['_PARTICIPANT']._serialized_start=372 - _globals['_PARTICIPANT']._serialized_end=475 - _globals['_TAIL']._serialized_start=477 - _globals['_TAIL']._serialized_end=498 - _globals['_FIRMWARETYPE']._serialized_start=500 - _globals['_FIRMWARETYPE']._serialized_end=582 - _globals['_FIRMWAREPAYLOAD']._serialized_start=585 - _globals['_FIRMWAREPAYLOAD']._serialized_end=809 - _globals['_ECUID']._serialized_start=811 - _globals['_ECUID']._serialized_end=860 - _globals['_FIRMWAREVERSION']._serialized_start=862 - _globals['_FIRMWAREVERSION']._serialized_end=910 - _globals['_FIRMWAREFIVE']._serialized_start=912 - _globals['_FIRMWAREFIVE']._serialized_end=937 - _globals['_DEVICEARRAY']._serialized_start=939 - _globals['_DEVICEARRAY']._serialized_end=988 - _globals['_DEVICEINFO']._serialized_start=991 - _globals['_DEVICEINFO']._serialized_end=1147 - _globals['_QUERYTYPE']._serialized_start=1149 - _globals['_QUERYTYPE']._serialized_end=1265 - _globals['_PAYLOADQUERYSEND']._serialized_start=1268 - _globals['_PAYLOADQUERYSEND']._serialized_end=1440 - _globals['_CONFIGTYPE']._serialized_start=1442 - _globals['_CONFIGTYPE']._serialized_end=1550 - _globals['_PAYLOADCONFIGSEND']._serialized_start=1552 - _globals['_PAYLOADCONFIGSEND']._serialized_end=1598 - _globals['_PAYLOADCONFIGRECV']._serialized_start=1600 - _globals['_PAYLOADCONFIGRECV']._serialized_end=1669 - _globals['_CONFIGSTRING']._serialized_start=1671 - _globals['_CONFIGSTRING']._serialized_end=1713 - _globals['_PAYLOADSTRING']._serialized_start=1715 - _globals['_PAYLOADSTRING']._serialized_end=1759 - _globals['_STRINGVALUE']._serialized_start=1761 - _globals['_STRINGVALUE']._serialized_end=1789 +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EXTERNALAUTHTYPE']._serialized_start=12117 + _globals['_EXTERNALAUTHTYPE']._serialized_end=12268 + _globals['_DELIVERYCHANNEL']._serialized_start=12271 + _globals['_DELIVERYCHANNEL']._serialized_end=12415 + _globals['_LOCALPARTICIPANT']._serialized_start=12417 + _globals['_LOCALPARTICIPANT']._serialized_end=12531 + _globals['_TESLASERVICE']._serialized_start=12533 + _globals['_TESLASERVICE']._serialized_end=12601 + _globals['_AUTHORIZEDCLIENTTYPE']._serialized_start=12604 + _globals['_AUTHORIZEDCLIENTTYPE']._serialized_end=12746 + _globals['_WIFINETWORKSECURITYTYPE']._serialized_start=12749 + _globals['_WIFINETWORKSECURITYTYPE']._serialized_end=12989 + _globals['_WIFICONFIGURERESULT']._serialized_start=12992 + _globals['_WIFICONFIGURERESULT']._serialized_end=13225 + _globals['_NEURIOCONNECTIONSTATUS']._serialized_start=13228 + _globals['_NEURIOCONNECTIONSTATUS']._serialized_end=13460 + _globals['_NEURIOCONNECTIONERROR']._serialized_start=13463 + _globals['_NEURIOCONNECTIONERROR']._serialized_end=13720 + _globals['_AUTHORIZATIONROLE']._serialized_start=13722 + _globals['_AUTHORIZATIONROLE']._serialized_end=13838 + _globals['_AUTHORIZEDSTATE']._serialized_start=13841 + _globals['_AUTHORIZEDSTATE']._serialized_end=14043 + _globals['_AUTHORIZEDVERIFICATIONTYPE']._serialized_start=14046 + _globals['_AUTHORIZEDVERIFICATIONTYPE']._serialized_end=14214 + _globals['_AUTHORIZEDKEYTYPE']._serialized_start=14216 + _globals['_AUTHORIZEDKEYTYPE']._serialized_end=14326 + _globals['_GRAPHQLQUERYFORMAT']._serialized_start=14329 + _globals['_GRAPHQLQUERYFORMAT']._serialized_end=14467 + _globals['_FILESTOREAPIDOMAIN']._serialized_start=14470 + _globals['_FILESTOREAPIDOMAIN']._serialized_end=14842 + _globals['_AUTHENVELOPE']._serialized_start=24 + _globals['_AUTHENVELOPE']._serialized_end=109 + _globals['_EXTERNALAUTH']._serialized_start=111 + _globals['_EXTERNALAUTH']._serialized_end=165 + _globals['_MESSAGE']._serialized_start=167 + _globals['_MESSAGE']._serialized_end=246 + _globals['_MESSAGEENVELOPE']._serialized_start=249 + _globals['_MESSAGEENVELOPE']._serialized_end=772 + _globals['_PARTICIPANT']._serialized_start=774 + _globals['_PARTICIPANT']._serialized_end=877 + _globals['_TAIL']._serialized_start=879 + _globals['_TAIL']._serialized_end=900 + _globals['_COMMONMESSAGES']._serialized_start=903 + _globals['_COMMONMESSAGES']._serialized_end=3218 + _globals['_ERRORRESPONSE']._serialized_start=3220 + _globals['_ERRORRESPONSE']._serialized_end=3267 + _globals['_STATUS']._serialized_start=3269 + _globals['_STATUS']._serialized_end=3308 + _globals['_COMMONAPIGETSYSTEMINFOREQUEST']._serialized_start=3310 + _globals['_COMMONAPIGETSYSTEMINFOREQUEST']._serialized_end=3341 + _globals['_COMMONAPIGETSYSTEMINFORESPONSE']._serialized_start=3343 + _globals['_COMMONAPIGETSYSTEMINFORESPONSE']._serialized_end=3375 + _globals['_COMMONAPISETLOCALSITECONFIGREQUEST']._serialized_start=3377 + _globals['_COMMONAPISETLOCALSITECONFIGREQUEST']._serialized_end=3413 + _globals['_COMMONAPISETLOCALSITECONFIGRESPONSE']._serialized_start=3415 + _globals['_COMMONAPISETLOCALSITECONFIGRESPONSE']._serialized_end=3452 + _globals['_COMMONAPIPERFORMUPDATEREQUEST']._serialized_start=3454 + _globals['_COMMONAPIPERFORMUPDATEREQUEST']._serialized_end=3485 + _globals['_COMMONAPIPERFORMUPDATERESPONSE']._serialized_start=3487 + _globals['_COMMONAPIPERFORMUPDATERESPONSE']._serialized_end=3519 + _globals['_COMMONAPIFACTORYRESETREQUEST']._serialized_start=3521 + _globals['_COMMONAPIFACTORYRESETREQUEST']._serialized_end=3551 + _globals['_COMMONAPIFACTORYRESETRESPONSE']._serialized_start=3553 + _globals['_COMMONAPIFACTORYRESETRESPONSE']._serialized_end=3584 + _globals['_COMMONAPICHECKFORUPDATEREQUEST']._serialized_start=3586 + _globals['_COMMONAPICHECKFORUPDATEREQUEST']._serialized_end=3618 + _globals['_COMMONAPICHECKFORUPDATERESPONSE']._serialized_start=3620 + _globals['_COMMONAPICHECKFORUPDATERESPONSE']._serialized_end=3653 + _globals['_COMMONAPICLEARUPDATEREQUEST']._serialized_start=3655 + _globals['_COMMONAPICLEARUPDATEREQUEST']._serialized_end=3684 + _globals['_COMMONAPICLEARUPDATERESPONSE']._serialized_start=3686 + _globals['_COMMONAPICLEARUPDATERESPONSE']._serialized_end=3716 + _globals['_COMMONAPICHECKINTERNETREQUEST']._serialized_start=3718 + _globals['_COMMONAPICHECKINTERNETREQUEST']._serialized_end=3749 + _globals['_COMMONAPIFORGETWIFINETWORKRESPONSE']._serialized_start=3751 + _globals['_COMMONAPIFORGETWIFINETWORKRESPONSE']._serialized_end=3787 + _globals['_COMMONAPIWIFISCANREQUEST']._serialized_start=3789 + _globals['_COMMONAPIWIFISCANREQUEST']._serialized_end=3896 + _globals['_COMMONAPIWIFISCANRESPONSE']._serialized_start=3898 + _globals['_COMMONAPIWIFISCANRESPONSE']._serialized_end=3968 + _globals['_WIFINETWORK']._serialized_start=3970 + _globals['_WIFINETWORK']._serialized_end=4004 + _globals['_COMMONAPICONFIGUREWIFIREQUEST']._serialized_start=4006 + _globals['_COMMONAPICONFIGUREWIFIREQUEST']._serialized_end=4094 + _globals['_COMMONAPICONFIGUREWIFIRESPONSE']._serialized_start=4096 + _globals['_COMMONAPICONFIGUREWIFIRESPONSE']._serialized_end=4208 + _globals['_COMMONAPICONFIGUREWIFIENCRYPTEDREQUEST']._serialized_start=4210 + _globals['_COMMONAPICONFIGUREWIFIENCRYPTEDREQUEST']._serialized_end=4334 + _globals['_COMMONAPICONFIGUREWIFIENCRYPTEDRESPONSE']._serialized_start=4337 + _globals['_COMMONAPICONFIGUREWIFIENCRYPTEDRESPONSE']._serialized_end=4503 + _globals['_COMMONAPIDEVICECERTREQUEST']._serialized_start=4505 + _globals['_COMMONAPIDEVICECERTREQUEST']._serialized_end=4533 + _globals['_COMMONAPIDEVICECERTRESPONSE']._serialized_start=4535 + _globals['_COMMONAPIDEVICECERTRESPONSE']._serialized_end=4600 + _globals['_COMMONAPIGETCELLULARINFOREQUEST']._serialized_start=4602 + _globals['_COMMONAPIGETCELLULARINFOREQUEST']._serialized_end=4635 + _globals['_COMMONAPIGETCELLULARINFORESPONSE']._serialized_start=4637 + _globals['_COMMONAPIGETCELLULARINFORESPONSE']._serialized_end=4705 + _globals['_COMMONAPIFORGETWIFINETWORKREQUEST']._serialized_start=4707 + _globals['_COMMONAPIFORGETWIFINETWORKREQUEST']._serialized_end=4756 + _globals['_COMMONAPIGETNETWORKINGSTATUSREQUEST']._serialized_start=4758 + _globals['_COMMONAPIGETNETWORKINGSTATUSREQUEST']._serialized_end=4795 + _globals['_COMMONAPIGETNETWORKINGSTATUSRESPONSE']._serialized_start=4798 + _globals['_COMMONAPIGETNETWORKINGSTATUSRESPONSE']._serialized_end=4994 + _globals['_COMMONAPICONFIGUREETHERNETREQUEST']._serialized_start=4996 + _globals['_COMMONAPICONFIGUREETHERNETREQUEST']._serialized_end=5087 + _globals['_COMMONAPICONFIGUREETHERNETRESPONSE']._serialized_start=5089 + _globals['_COMMONAPICONFIGUREETHERNETRESPONSE']._serialized_end=5164 + _globals['_COMMONAPICHECKINTERNETRESPONSE']._serialized_start=5167 + _globals['_COMMONAPICHECKINTERNETRESPONSE']._serialized_end=5317 + _globals['_NETWORKINTERFACEIPV4CONFIG']._serialized_start=5319 + _globals['_NETWORKINTERFACEIPV4CONFIG']._serialized_end=5435 + _globals['_NETWORKINTERFACE']._serialized_start=5438 + _globals['_NETWORKINTERFACE']._serialized_end=5633 + _globals['_NETWORKCONNECTIVITYSTATUS']._serialized_start=5635 + _globals['_NETWORKCONNECTIVITYSTATUS']._serialized_end=5740 + _globals['_WIFICONFIG']._serialized_start=5742 + _globals['_WIFICONFIG']._serialized_end=5863 + _globals['_WIFIPASSWORD']._serialized_start=5865 + _globals['_WIFIPASSWORD']._serialized_end=5894 + _globals['_CELLULAREID']._serialized_start=5896 + _globals['_CELLULAREID']._serialized_end=5924 + _globals['_TEGMESSAGES']._serialized_start=5926 + _globals['_TEGMESSAGES']._serialized_end=5960 + _globals['_WCMESSAGES']._serialized_start=5962 + _globals['_WCMESSAGES']._serialized_end=5995 + _globals['_NEURIOCTCONFIG']._serialized_start=5997 + _globals['_NEURIOCTCONFIG']._serialized_end=6061 + _globals['_NEURIOMETERCONFIG']._serialized_start=6063 + _globals['_NEURIOMETERCONFIG']._serialized_end=6157 + _globals['_NEURIOMETERCONNECTION']._serialized_start=6160 + _globals['_NEURIOMETERCONNECTION']._serialized_end=6402 + _globals['_RSSI']._serialized_start=6404 + _globals['_RSSI']._serialized_end=6456 + _globals['_NEURIOCTREADING']._serialized_start=6458 + _globals['_NEURIOCTREADING']._serialized_end=6542 + _globals['_NEURIOMETERREADINGS']._serialized_start=6544 + _globals['_NEURIOMETERREADINGS']._serialized_end=6610 + _globals['_NEURIOMETERINTERFACE']._serialized_start=6612 + _globals['_NEURIOMETERINTERFACE']._serialized_end=6728 + _globals['_NEURIOMETERAPIADDMETERREQUEST']._serialized_start=6730 + _globals['_NEURIOMETERAPIADDMETERREQUEST']._serialized_end=6804 + _globals['_NEURIOMETERAPIADDMETERRESPONSE']._serialized_start=6806 + _globals['_NEURIOMETERAPIADDMETERRESPONSE']._serialized_end=6881 + _globals['_NEURIOMETERAPIREMOVEMETERREQUEST']._serialized_start=6883 + _globals['_NEURIOMETERAPIREMOVEMETERREQUEST']._serialized_end=6933 + _globals['_NEURIOMETERAPIREMOVEMETERRESPONSE']._serialized_start=6935 + _globals['_NEURIOMETERAPIREMOVEMETERRESPONSE']._serialized_end=6970 + _globals['_NEURIOMETERAPICONFIGURECTSREQUEST']._serialized_start=6972 + _globals['_NEURIOMETERAPICONFIGURECTSREQUEST']._serialized_end=7065 + _globals['_NEURIOMETERAPICONFIGURECTSRESPONSE']._serialized_start=7067 + _globals['_NEURIOMETERAPICONFIGURECTSRESPONSE']._serialized_end=7145 + _globals['_NEURIOMETERMESSAGES']._serialized_start=7148 + _globals['_NEURIOMETERMESSAGES']._serialized_end=7610 + _globals['_ENERGYSITENETMESSAGES']._serialized_start=7612 + _globals['_ENERGYSITENETMESSAGES']._serialized_end=7656 + _globals['_AUTHORIZATIONRECORD']._serialized_start=7659 + _globals['_AUTHORIZATIONRECORD']._serialized_end=7999 + _globals['_AUTHORIZATIONMESSAGES']._serialized_start=8002 + _globals['_AUTHORIZATIONMESSAGES']._serialized_end=9260 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTREQUEST']._serialized_start=9263 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTREQUEST']._serialized_end=9435 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTRESPONSE']._serialized_start=9437 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTRESPONSE']._serialized_end=9527 + _globals['_AUTHORIZATIONAPIREMOVEAUTHORIZEDCLIENTREQUEST']._serialized_start=9529 + _globals['_AUTHORIZATIONAPIREMOVEAUTHORIZEDCLIENTREQUEST']._serialized_end=9595 + _globals['_AUTHORIZATIONAPIREMOVEAUTHORIZEDCLIENTRESPONSE']._serialized_start=9597 + _globals['_AUTHORIZATIONAPIREMOVEAUTHORIZEDCLIENTRESPONSE']._serialized_end=9645 + _globals['_AUTHORIZATIONAPILISTAUTHORIZEDCLIENTSREQUEST']._serialized_start=9647 + _globals['_AUTHORIZATIONAPILISTAUTHORIZEDCLIENTSREQUEST']._serialized_end=9693 + _globals['_AUTHORIZATIONAPILISTAUTHORIZEDCLIENTSRESPONSE']._serialized_start=9695 + _globals['_AUTHORIZATIONAPILISTAUTHORIZEDCLIENTSRESPONSE']._serialized_end=9817 + _globals['_AUTHORIZATIONAPIGETSIGNEDCOMMANDSPUBLICKEYREQUEST']._serialized_start=9819 + _globals['_AUTHORIZATIONAPIGETSIGNEDCOMMANDSPUBLICKEYREQUEST']._serialized_end=9870 + _globals['_AUTHORIZATIONAPIGETSIGNEDCOMMANDSPUBLICKEYRESPONSE']._serialized_start=9872 + _globals['_AUTHORIZATIONAPIGETSIGNEDCOMMANDSPUBLICKEYRESPONSE']._serialized_end=9943 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTBYTRUSTEDSIGNATUREREQUEST']._serialized_start=9946 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTBYTRUSTEDSIGNATUREREQUEST']._serialized_end=10198 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTBYTRUSTEDSIGNATURERESPONSE']._serialized_start=10200 + _globals['_AUTHORIZATIONAPIADDAUTHORIZEDCLIENTBYTRUSTEDSIGNATURERESPONSE']._serialized_end=10308 + _globals['_AUTHORIZATIONAPICONFIGUREREMOTESERVICEREQUEST']._serialized_start=10310 + _globals['_AUTHORIZATIONAPICONFIGUREREMOTESERVICEREQUEST']._serialized_end=10425 + _globals['_AUTHORIZATIONAPICONFIGUREREMOTESERVICERESPONSE']._serialized_start=10427 + _globals['_AUTHORIZATIONAPICONFIGUREREMOTESERVICERESPONSE']._serialized_end=10475 + _globals['_GRAPHQLMESSAGES']._serialized_start=10477 + _globals['_GRAPHQLMESSAGES']._serialized_end=10602 + _globals['_SIGNEDGRAPHQLQUERY']._serialized_start=10604 + _globals['_SIGNEDGRAPHQLQUERY']._serialized_end=10656 + _globals['_GRAPHQLAPIQUERYREQUEST']._serialized_start=10659 + _globals['_GRAPHQLAPIQUERYREQUEST']._serialized_end=10812 + _globals['_GRAPHQLAPIQUERYRESPONSE']._serialized_start=10814 + _globals['_GRAPHQLAPIQUERYRESPONSE']._serialized_end=10907 + _globals['_GRAPHQLERROR']._serialized_start=10909 + _globals['_GRAPHQLERROR']._serialized_end=10968 + _globals['_GRAPHQLSTRINGVALUE']._serialized_start=10970 + _globals['_GRAPHQLSTRINGVALUE']._serialized_end=11005 + _globals['_FILESTOREMESSAGES']._serialized_start=11008 + _globals['_FILESTOREMESSAGES']._serialized_end=11460 + _globals['_FILESTOREAPIFILE']._serialized_start=11462 + _globals['_FILESTOREAPIFILE']._serialized_end=11521 + _globals['_FILESTOREAPIREADFILEREQUEST']._serialized_start=11523 + _globals['_FILESTOREAPIREADFILEREQUEST']._serialized_end=11635 + _globals['_FILESTOREAPIREADFILERESPONSE']._serialized_start=11637 + _globals['_FILESTOREAPIREADFILERESPONSE']._serialized_end=11721 + _globals['_FILESTOREAPIUPDATEFILEREQUEST']._serialized_start=11724 + _globals['_FILESTOREAPIUPDATEFILEREQUEST']._serialized_end=11853 + _globals['_FILESTOREAPIUPDATEFILERESPONSE']._serialized_start=11855 + _globals['_FILESTOREAPIUPDATEFILERESPONSE']._serialized_end=11941 + _globals['_FILESTOREAPIFORCEWRITEFILEREQUEST']._serialized_start=11943 + _globals['_FILESTOREAPIFORCEWRITEFILEREQUEST']._serialized_end=12062 + _globals['_FILESTOREAPIFORCEWRITEFILERESPONSE']._serialized_start=12064 + _globals['_FILESTOREAPIFORCEWRITEFILERESPONSE']._serialized_end=12114 # @@protoc_insertion_point(module_scope) diff --git a/pypowerwall/tedapi/tedapi_pb2.pyi b/pypowerwall/tedapi/tedapi_pb2.pyi new file mode 100644 index 0000000..a18410e --- /dev/null +++ b/pypowerwall/tedapi/tedapi_pb2.pyi @@ -0,0 +1,163 @@ +"""Type stubs for tedapi_pb2 (generated from tedapi.proto).""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class AuthEnvelope(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + payload: bytes + externalAuth: ExternalAuth + def __init__(self, *, payload: bytes = ..., externalAuth: ExternalAuth | None = ...) -> None: ... + +class ExternalAuth(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + type: int + def __init__(self, *, type: int = ...) -> None: ... + +class Message(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + message: MessageEnvelope + tail: Tail + def __init__(self, *, message: MessageEnvelope | None = ..., tail: Tail | None = ...) -> None: ... + +class MessageEnvelope(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + deliveryChannel: int + sender: Participant + recipient: Participant + firmware: FirmwareType + config: ConfigType + payload: QueryType + def __init__( + self, + *, + deliveryChannel: int = ..., + sender: Participant | None = ..., + recipient: Participant | None = ..., + firmware: FirmwareType | None = ..., + config: ConfigType | None = ..., + payload: QueryType | None = ..., + ) -> None: ... + +class Participant(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + din: str + teslaService: int + local: int + authorizedClient: int + def __init__(self, *, din: str = ..., teslaService: int = ..., local: int = ..., authorizedClient: int = ...) -> None: ... + +class Tail(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + value: int + def __init__(self, *, value: int = ...) -> None: ... + +class FirmwareType(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + request: str + system: FirmwarePayload + def __init__(self, *, request: str = ..., system: FirmwarePayload | None = ...) -> None: ... + +class FirmwarePayload(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + gateway: EcuId + din: str + version: FirmwareVersion + five: FirmwareFive + six: int + wireless: DeviceArray + field8: bytes + field9: bytes + def __init__( + self, + *, + gateway: EcuId | None = ..., + din: str = ..., + version: FirmwareVersion | None = ..., + five: FirmwareFive | None = ..., + six: int = ..., + wireless: DeviceArray | None = ..., + field8: bytes = ..., + field9: bytes = ..., + ) -> None: ... + +class EcuId(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + partNumber: str + serialNumber: str + def __init__(self, *, partNumber: str = ..., serialNumber: str = ...) -> None: ... + +class FirmwareVersion(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + text: str + githash: bytes + def __init__(self, *, text: str = ..., githash: bytes = ...) -> None: ... + +class FirmwareFive(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + d: int + def __init__(self, *, d: int = ...) -> None: ... + +class DeviceArray(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + device: list[DeviceInfo] + def __init__(self, *, device: list[DeviceInfo] | None = ...) -> None: ... + +class DeviceInfo(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + company: StringValue + model: StringValue + fcc_id: StringValue + ic: StringValue + def __init__(self, *, company: StringValue | None = ..., model: StringValue | None = ..., fcc_id: StringValue | None = ..., ic: StringValue | None = ...) -> None: ... + +class QueryType(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + send: PayloadQuerySend + recv: PayloadString + def __init__(self, *, send: PayloadQuerySend | None = ..., recv: PayloadString | None = ...) -> None: ... + +class PayloadQuerySend(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + num: int + payload: PayloadString + code: bytes + b: StringValue + def __init__(self, *, num: int = ..., payload: PayloadString | None = ..., code: bytes = ..., b: StringValue | None = ...) -> None: ... + +class ConfigType(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + send: PayloadConfigSend + recv: PayloadConfigRecv + def __init__(self, *, send: PayloadConfigSend | None = ..., recv: PayloadConfigRecv | None = ...) -> None: ... + +class PayloadConfigSend(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + num: int + file: str + def __init__(self, *, num: int = ..., file: str = ...) -> None: ... + +class PayloadConfigRecv(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + file: ConfigString + code: bytes + def __init__(self, *, file: ConfigString | None = ..., code: bytes = ...) -> None: ... + +class ConfigString(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + name: str + text: str + def __init__(self, *, name: str = ..., text: str = ...) -> None: ... + +class PayloadString(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + value: int + text: str + def __init__(self, *, value: int = ..., text: str = ...) -> None: ... + +class StringValue(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + value: str + def __init__(self, *, value: str = ...) -> None: ...