Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
web/EVNotifyAPI/
web/EVNotifyAPI/
__pycache__/
115 changes: 58 additions & 57 deletions cars/IONIQ_BEV.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,71 @@
class IONIQ_BEV:
from car import *
from time import time

POLL_DELAY_2180 = 60 # Rate limit b2180 to once a minute
b2101 = bytes.fromhex(hex(0x2101)[2:])
b2105 = bytes.fromhex(hex(0x2105)[2:])
b2180 = bytes.fromhex(hex(0x2180)[2:])

class IONIQ_BEV(car):

def __init__(self, dongle):
self.dongle = dongle
self.dongle.setProtocol('CAN_11_500')
self.dongle.setCANRxFilter('7EC')
self.dongle.setCANRxMask('7FF')
self.dongle.setCANRxFilter(0x7ec)
self.dongle.setCANRxMask(0x7ff)
self.last_raw = {}
self.last_poll_2180 = 0

def getData(self):
now = time()
raw = {}

for cmd in [2101,2105]:
raw[cmd] = self.dongle.sendCommand(str(cmd))

chargingBits = raw[2101][0x7EC21][5] \
if 0x7EC21 in raw[2101] else None
dcBatteryCurrent = int.from_bytes(raw[2101][0x7EC21][6:7] + raw[2101][0x7EC22][0:1],
byteorder='big', signed=True) / 10.0 \
if 0x7EC21 in raw[2101] and 0x7EC22 in raw[2101] else None
dcBatteryVoltage = int.from_bytes(raw[2101][0x7EC22][1:3],
byteorder='big', signed=False) / 10.0 \
if 0x7EC22 in raw[2101] else None

data = {'SOC_BMS': raw[2101][0x7EC21][0] / 2.0 \
if 0x7EC21 in raw[2101] else None,
'SOC_DISPLAY': raw[2105][0x7EC24][6] / 2.0 \
if 0x7EC24 in raw[2105] else None,
'EXTENDED': {
'auxBatteryVoltage': raw[2101][0x7EC24][4] / 10.0 \
if 0x7EC24 in raw[2105] else None,

'batteryInletTemperature': int.from_bytes(raw[2101][0x7EC23][2:3],
byteorder='big', signed=True) \
if 0x7EC23 in raw[2105] else None,

'batteryMaxTemperature': int.from_bytes(raw[2101][0x7EC22][3:4],
byteorder='big', signed=True) \
if 0x7EC22 in raw[2105] else None,

'batteryMinTemperature': int.from_bytes(raw[2101][0x7EC22][4:5],
byteorder='big', signed=True) \
if 0x7EC22 in raw[2105] else None,

'charging': 1 if chargingBits != None and \
chargingBits & 0x80 == 0x80 else 0,

'normalChargePort': 1 if chargingBits != None and \
chargingBits & 0x20 == 0x20 else 0,

'rapidChargePort': 1 if chargingBits != None and \
chargingBits & 0x40 == 0x40 else 0,

'dcBatteryCurrent': dcBatteryCurrent,

'dcBatteryPower': dcBatteryCurrent * dcBatteryVoltage / 1000.0 \
if dcBatteryCurrent!= None and dcBatteryVoltage != None else None,

'dcBatteryVoltage': dcBatteryVoltage,

'soh': int.from_bytes(raw[2105][0x7EC24][0:2],
byteorder='big', signed=False) / 10.0 \
if 0x7EC24 in raw[2105] else None,
}
self.dongle.setCANRxFilter(0x7ec)
self.dongle.setCanID(0x7e4)
for cmd in [b2101,b2105]:
raw[cmd] = self.dongle.sendCommand(cmd)

if now - self.last_poll_2180 > POLL_DELAY_2180 or b2180 not in self.last_raw:
self.last_poll_2180 = now
self.dongle.setCANRxFilter(0x7ee)
self.dongle.setCanID(0x7e6)
raw[b2180] = self.dongle.sendCommand(b2180)
else:
raw[b2180] = self.last_raw[b2180]

if len(raw[b2101][0x7ec]) != 9 or \
len(raw[b2105][0x7ec]) != 7 or \
len(raw[b2180][0x7ee]) != 4:
raise IONIQ_BEV.NULL_BLOCK("Got wrong count of frames!\n"+str(raw))

self.last_raw[b2180] = raw[b2180]

data = self.getBaseData()

data['SOC_BMS'] = raw[b2101][0x7ec][1][0] / 2.0
data['SOC_DISPLAY'] = raw[b2105][0x7ec][4][6] / 2.0

chargingBits = raw[b2101][0x7ec][1][5]
dcBatteryCurrent = int.from_bytes(raw[b2101][0x7ec][1][6:7] + raw[b2101][0x7ec][2][0:1], byteorder='big', signed=True) / 10.0
dcBatteryVoltage = int.from_bytes(raw[b2101][0x7ec][2][1:3], byteorder='big', signed=False) / 10.0

data['EXTENDED'] = {
'auxBatteryVoltage': raw[b2101][0x7ec][4][4] / 10.0,
'batteryInletTemperature': int.from_bytes(raw[b2101][0x7ec][3][2:3], byteorder='big', signed=True),
'batteryMaxTemperature': int.from_bytes(raw[b2101][0x7ec][2][3:4], byteorder='big', signed=True),
'batteryMinTemperature': int.from_bytes(raw[b2101][0x7ec][2][4:5], byteorder='big', signed=True),
'cumulativeEnergyCharged': int.from_bytes(raw[b2101][0x7ec][5][6:7] + raw[b2101][0x7ec][6][0:3], byteorder='big', signed=False) / 10.0,
'cumulativeEnergyDischarged': int.from_bytes(raw[b2101][0x7ec][6][3:7], byteorder='big', signed=False) / 10.0,
'charging': 1 if chargingBits & 0x80 else 0,
'normalChargePort': 1 if chargingBits & 0x20 else 0,
'rapidChargePort': 1 if chargingBits & 0x40 else 0,
'dcBatteryCurrent': dcBatteryCurrent,
'dcBatteryPower': dcBatteryCurrent * dcBatteryVoltage / 1000.0,
'dcBatteryVoltage': dcBatteryVoltage,
'outsideTemp': (raw[b2180][0x7ee][2][1] - 80) / 2,
'soh': int.from_bytes(raw[b2105][0x7ec][4][0:2], byteorder='big', signed=False) / 10.0,
}

data.update(self.getBaseData())

return data

def getBaseData(self):
Expand Down
76 changes: 37 additions & 39 deletions cars/KONA_EV.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,60 @@
class KONA_EV:
from car import *

b220101 = bytes.fromhex(hex(0x220101)[2:])
b220105 = bytes.fromhex(hex(0x220105)[2:])

class KONA_EV(Car):

def __init__(self, dongle):
self.dongle = dongle
self.dongle.setProtocol('CAN_11_500')
self.dongle.setCANRxFilter('7EC')
self.dongle.setCANRxMask('7FF')
self.dongle.setCanID(0x7e4)

def getData(self):
raw = {}

for cmd in [220101,220105]:
raw[cmd] = self.dongle.sendCommand(str(cmd))

chargingBits = raw[220101][0x7EC27][5] \
if 0x7EC27 in raw[220101] else None
for cmd in [b220101,b220105]:
raw[cmd] = self.dongle.sendCommand(cmd)

normalChargePort = raw[220101][0x7EC21][6] == 3 \
if 0x7EC21 in raw[220101] else None
normalChargeBit = chargingBits & 0x02 == 0x02
# print("Raw lens {} {}".format(len(raw[b220101][0x7ec]),len(raw[b220105][0x7ec])))
#if len(raw[b220101][0x7ec]) != 9 or \ # XXX: Need to put in correct line count for Kona
# len(raw[b220105][0x7ec]) != 7:
# raise KONA_EV.NULL_BLOCK("Got wrong count of frames!\n"+str(raw))

dcBatteryCurrent = int.from_bytes(raw[220101][0x7EC22][0:2], byteorder='big', signed=True) / 10.0 \
if 0x7EC22 in raw[220101] else None
data = self.getBaseData()

dcBatteryVoltage = int.from_bytes(raw[220101][0x7EC22][2:4], byteorder='big', signed=False) / 10.0 \
if 0x7EC22 in raw[220101] else None
data['SOC_BMS'] = raw[b220101][0x7ec][1][1] / 2.0
data['SOC_DISPLAY'] = raw[b220105][0x7ec][5][0] / 2.0

data = {'SOC_BMS': raw[220101][0x7EC21][1] / 2.0 \
if 0x7EC21 in raw[220101] else None,
'SOC_DISPLAY': raw[220105][0x7EC25][0] / 2.0 \
if 0x7EC25 in raw[220105] else None,
'EXTENDED': {
'auxBatteryVoltage': raw[220101][0x7EC24][5] / 10.0 \
if 0x7EC24 in raw[220101] else None,
'batteryInletTemperature': int.from_bytes(raw[220101][0x7EC23][5:6], byteorder='big', signed=True) \
if 0x7EC23 in raw[220101] else None,
'batteryMaxTemperature': int.from_bytes(raw[220101][0x7EC22][4:5], byteorder='big', signed=True) \
if 0x7EC22 in raw[220101] else None,
'batteryMinTemperature': int.from_bytes(raw[220101][0x7EC22][5:6], byteorder='big', signed=True) \
if 0x7EC22 in raw[220101] else None,
'charging': 1 if chargingBits & 0xc == 0x8 else 0,
'normalChargePort': 1 if normalChargeBit and normalChargePort else 0,
'rapidChargePort': 1 if normalChargeBit and not normalChargePort else 0,
'dcBatteryCurrent': dcBatteryCurrent,
'dcBatteryPower': dcBatteryCurrent * dcBatteryVoltage / 1000.0 \
if dcBatteryCurrent!= None and dcBatteryVoltage != None else None,
'dcBatteryVoltage': dcBatteryVoltage,
'soh': int.from_bytes(raw[220105][0x7EC24][1:3], byteorder='big', signed=False) / 10.0 \
if 0x7EC24 in raw[220105] else None,
}
chargingBits = raw[b220101][0x7ec][7][5]
normalChargePort = raw[b220101][0x7ec][1][6] == 3
normalChargeBit = chargingBits & 0x02 == 0x02
dcBatteryCurrent = int.from_bytes(raw[b220101][0x7ec][2][0:2], byteorder='big', signed=True) / 10.0
dcBatteryVoltage = int.from_bytes(raw[b220101][0x7ec][2][2:4], byteorder='big', signed=False) / 10.0

data['EXTENDED'] = {
'auxBatteryVoltage': raw[b220101][0x7ec][4][5] / 10.0,
'batteryInletTemperature': int.from_bytes(raw[b220101][0x7ec][3][5:6], byteorder='big', signed=True),
'batteryMaxTemperature': int.from_bytes(raw[b220101][0x7ec][2][4:5], byteorder='big', signed=True),
'batteryMinTemperature': int.from_bytes(raw[b220101][0x7ec][2][5:6], byteorder='big', signed=True),
'cumulativeEnergyCharged': int.from_bytes(raw[b220101][0x7ec][6][0:4], byteorder='big', signed=False) / 10.0,
'cumulativeEnergyDischarged': int.from_bytes(raw[b220101][0x7ec][6][4:7] + raw[b220101][0x7ec][7][0:1], byteorder='big', signed=False) / 10.0,
'charging': 1 if (chargingBits & 0xc) == 0x8 else 0,
'normalChargePort': 1 if normalChargeBit and normalChargePort else 0,
'rapidChargePort': 1 if normalChargeBit and not normalChargePort else 0,
'dcBatteryCurrent': dcBatteryCurrent,
'dcBatteryPower': dcBatteryCurrent * dcBatteryVoltage / 1000.0,
'dcBatteryVoltage': dcBatteryVoltage,
'soh': int.from_bytes(raw[b220105][0x7ec][4][1:3], byteorder='big', signed=False) / 10.0,
}

data.update(self.getBaseData())

return data

def getBaseData(self):
return {
"CAPACITY": 28,
"CAPACITY": 64,
"SLOW_SPEED": 2.3,
"NORMAL_SPEED": 4.6,
"FAST_SPEED": 50
Expand Down
5 changes: 5 additions & 0 deletions cars/NIRO_EV.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from KONA_EV import *

class NIRO_EV(KONA_EV):
pass

7 changes: 7 additions & 0 deletions cars/car.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

class Car:

class NULL_BLOCK(Exception): pass
class LOW_VOLTAGE(Exception): pass


Loading