Autonomous CarLogBook with Oracle Apex on OCI (Part 1)
with Remote Control Features
Motivations for this project: Since June 2022, I have been driving an e-Niro, which means I fuel up with electricity. Unfortunately, the charging station network in the world of electric mobility lacks cost transparency. There are numerous charging cards and apps available. The charging speed, dwell times, and the provider all play a role in the overall experience. However, there are no price tags displayed on the charging stations...
Charging Costs
Consumption Stats
Some automaker apps aren't exactly helpful. Take, for example, the UVO app from Kia. While it does offer the ability to view a few aggregated reports and remotely control your car, the overall experience is somewhat limited in enjoyment.
- Driver's logbook
The UVO App from Kia is completely forgettable.
Alternative: tronity.io This app currently costs 4 Euros per month. I've tested it, but it doesn't work accurately with my car. (The status updates from Kia are not queried frequently enough, leading to inaccuracies that I had to fix myself.) help.tronity.io/hc/de-de/articles/360018314..
- Are there already solutions available to replicate the features of manufacturer apps?
Absolutely! In the home automation scene, there's a lot to discover. At first, I came across FHEM forum.fhem.de/index.php/topic,118822.0/all...
then iobroker forum.iobroker.net/topic/43592/adapter-hyun..
How to connect the API interface was discovered by a diligent developer.https://github.com/Hacksore/bluel..
By the way, if you're wondering about other manufacturers? teslaapi.io
**CAUTION: Essentially, manufacturers don't want this to happen.. **
Quote from a forum: "A few months ago, I found an alternative Bluelink app in the Android Store and installed it. Since it was only available in Norwegian and English, but not in German, I offered to help the developer translate the app. Unfortunately, the developer was directly warned by Hyundai and had to remove the app from the platform just as the first German version went online."
Alright, I don't intend to commercialize it; I've created a step-by-step guide. It doesn't harm anyone, so feel free to use it as you wish.
Architekturbild
Of course, one could directly recode the entire logic of [github.com/Hacksore/bluelinky] using APEX. However, this alternative has been working for a long time and has been extensively tested, which is why we're taking the detour through node.js.
- Steps on the LINUX VM (OCI Instance)
Node JS install
yum install nodejs rpm
npm cache clean -f
npm update npm -g
- Step node.js + bluelinky
mkdir carlogbook
cd carlogbook
npm install bluelinky
npm update
npm fund
npm audit
npm install oracledb
npm install pm2 -g
npm install dotenv
Thanks to the groundwork laid by bluelinky, it's now easy to query KIA's service using Node.js.
I created a carcommunications.js and a .env file for this purpose. The .env file contains all the required login credentials.
The carcommunication.js acts as an HTTP server and waits for requests with the argument apiendpoint = status or drivehistory in this example.
Create the .env file
[root@instance carlogbook]# vi .env
APPUSERNAME=
APPPWD=
PIN=
BRAND=kia/hyundai
REGION=
FIN=
DBUSERNAME=
DBPWD=
DBHOST=
PORT=85
Create the carcommunications.js file
require('dotenv').config()
const BlueLinky = require('bluelinky');
const http = require('http');
const host = 'localhost';
const port = process.env.PORT;
//dbpart
//const oracledb = require('oracledb');
params=function(req){
let q=req.url.split('?'),result={};
if(q.length>=2){
q[1].split('&').forEach((item)=>{
try {
result[item.split('=')[0]]=item.split('=')[1];
} catch (e) {
result[item.split('=')[0]]='';
}
})
}
return result;
}
const requestListener = function(req, res) {
req.params=params(req);
const client = new BlueLinky({
username: process.env.APPUSERNAME,
password: process.env.APPPWD,
brand: process.env.BRAND,
region: process.env.REGION,
pin: process.env.PIN
});
client.on('ready', async () => {
const vehicle = client.getVehicle(process.env.FIN);
try {
switch (req.params.apiendpoint) {
case "status":
res.writeHead(200);
res.end(req.params.apiendpoint);
console.log("get api infos status");
var resp = await vehicle.status({ parsed: false, refresh: true });
if (resp.evStatus.batteryStatus > 0) { var letsgo = 1; }
break;
case "drivehistory":
res.writeHead(200);
res.end(req.params.apiendpoint);
console.log("get api infos dh");
var resp= await vehicle.driveHistory();
var letsgo = 1;
break;
default:
res.writeHead(404);
res.end(JSON.stringify({error:"Resource not found"}));
}
if (letsgo === 1) {
try {
console.log(status);
// dbpart
// let connection;
// connection = await oracledb.getConnection({ user: process.env.DBUSERNAME,password: process.env.DBPWD,connectionString: process.env.DBHOST });
// await connection.execute(`INSERT INTO eniro VALUES (systimestamp at time zone 'CET' , :str, upper(:reqname))`,{ str: { type: oracledb.DB_TYPE_JSON, val: status } ,reqname: { type: oracledb.DB_TYPE_VARCHAR, val: req.params.apiendpoint} },{autoCommit: true});
} catch (e){
console.log(e.stack);
process.exit();
}
} // if
} catch (err) {
console.log(err);
// dbpart
// let connection;
// connection = await oracledb.getConnection({ user: process.env.DBUSERNAME,
// password: process.env.DBPWD,
// connectionString: process.env.DBHOST });
// await connection.execute(`INSERT INTO eniro VALUES (systimestamp at time zone 'CET' , '',upper('NO'|| :reqname))`,{reqname: { type: oracledb.DB_TYPE_VARCHAR, val: req.params.apiendpoint} },{autoCommit: true});
}
});
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});
Start Node Server:
node carcommunication.js
Testing with:
curl localhost:85/?apiendpoint=drivehistory
curl localhost:85/?apiendpoint=status
Continuing with Part 2: yaitcon.hashnode.dev/carlogbook-with-oracle..