summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRoger Zanoni <rzanoni@igalia.com>2023-12-28 20:07:05 -0300
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2024-01-29 12:07:20 +0000
commitc323ab8fde212120d8d1914d453afeb55b3576e5 (patch)
tree2f3565da1e623d69297b00d2a5e1e2b261692810 /src
parent5f1b6075982b872b5db4e2195e53d19529278d5c (diff)
Update HVAC app to use grpc-web instead of websockets
Adapt the HTML5 applications to use kuksa.val service Bug-AGL: SPEC-4599 Signed-off-by: Roger Zanoni <rzanoni@igalia.com> Change-Id: I3e36a6c08041db8fb59fd7f20497c1c156bbb2f7
Diffstat (limited to 'src')
-rw-r--r--src/index.html2
-rw-r--r--src/js/buttons.js33
-rw-r--r--src/js/fan_speed.js7
-rw-r--r--src/js/kuksa.js232
-rw-r--r--src/js/paths.js12
-rw-r--r--src/js/seats.js7
-rw-r--r--src/js/temperature.js9
7 files changed, 205 insertions, 97 deletions
diff --git a/src/index.html b/src/index.html
index c8a5eed..ce61ba0 100644
--- a/src/index.html
+++ b/src/index.html
@@ -27,7 +27,7 @@
</div>
<div class="fanSpeed">
<div class="fanSpeedContainer">
- <input id="FanSpeed" type="range" min="1" value="1" max="100" onchange="FANSPEED.set()">
+ <input id="FanSpeed" type="range" min="1" value="1" max="100" onchange="FANSPEED.set(this.value)">
<progress value="1" max="100"></progress>
</div>
<div class="label">
diff --git a/src/js/buttons.js b/src/js/buttons.js
index 76d2143..c593445 100644
--- a/src/js/buttons.js
+++ b/src/js/buttons.js
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-
-
-var values = {
- ac: false,
- recirculation: false,
- rear: false,
- front: false
-};
-
var paths = {
ac: PATHS.airConditioning,
recirculation: PATHS.recirculation,
@@ -30,7 +21,7 @@ var paths = {
front: PATHS.frontDefroster,
};
-var nodes = {}
+var nodes = new Map();
export function init() {
nodes[PATHS.airConditioning] = document.getElementById('ac');
@@ -40,10 +31,15 @@ export function init() {
nodes['up'] = document.getElementById('up');
nodes['down'] = document.getElementById('down');
nodes['right'] = document.getElementById('right');
+
+ nodes.forEach(function(node, path) {
+ node.setAttribute('value', false);
+ });
}
-export function update(path, value) {
+export function update(path, dp) {
if (path == PATHS.leftAirDistribution) {
+ var value = dp.getString();
if (value == 'UP') {
nodes['up'].setAttribute('value', true);
nodes['down'].setAttribute('value', false);
@@ -60,20 +56,25 @@ export function update(path, value) {
}
} else {
var node = nodes[path];
+ var value = dp.getBool();
node.setAttribute('value', value);
}
}
export function toggle(node) {
var key = node.getAttribute('key');
- values[key] = !values[key];
+ if (KUKSA.isLocked(paths[key])) {
+ return;
+ }
+
if (key == 'up') {
- KUKSA.set(PATHS.leftAirDistribution, 'UP');
+ KUKSA.setString(PATHS.leftAirDistribution, 'UP');
} else if (key == 'down') {
- KUKSA.set(PATHS.leftAirDistribution, 'DOWN');
+ KUKSA.setString(PATHS.leftAirDistribution, 'DOWN');
} else if (key == 'right') {
- KUKSA.set(PATHS.leftAirDistribution, 'MIDDLE');
+ KUKSA.setString(PATHS.leftAirDistribution, 'MIDDLE');
} else {
- KUKSA.set(paths[key], values[key]);
+ var value = node.getAttribute('value').toLowerCase() == 'true';
+ KUKSA.setBool(paths[key], !value);
}
}
diff --git a/src/js/fan_speed.js b/src/js/fan_speed.js
index fa0ed01..74e4748 100644
--- a/src/js/fan_speed.js
+++ b/src/js/fan_speed.js
@@ -17,7 +17,8 @@
var value = 0;
var node = null;
-export function update(path, value) {
+export function update(path, dp) {
+ var value = dp.getUint32();
node.value = value;
node.parentNode.getElementsByTagName('progress')[0].value = value;
}
@@ -26,6 +27,6 @@ export function init() {
node = document.getElementById('FanSpeed');
}
-export function set() {
- KUKSA.set(PATHS.leftFanSpeed, node.value);
+export function set(value) {
+ KUKSA.setUInt32(PATHS.leftFanSpeed, value);
}
diff --git a/src/js/kuksa.js b/src/js/kuksa.js
index 9e9cc22..8542dcb 100644
--- a/src/js/kuksa.js
+++ b/src/js/kuksa.js
@@ -16,108 +16,214 @@
import { v4 as uuidv4 } from 'uuid';
-const DEFAULT_TARGET = "wss://localhost:8090";
+const DEFAULT_TARGET = "https://localhost:8888";
// TODO: use an application token when needed
// currently using https://github.com/eclipse/kuksa.val/blob/master/kuksa_certificates/jwt/super-admin.json.token
const TEST_TOKEN =
- 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJrdWtzYS52YWwiLCJpc3MiOiJFY2xpcHNlIEtVS1NBIERldiIsImFkbWluIjp0cnVlLCJtb2RpZnlUcmVlIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTc2NzIyNTU5OSwia3Vrc2EtdnNzIjp7IioiOiJydyJ9fQ.p2cnFGH16QoQ14l6ljPVKggFXZKmD-vrw8G6Vs6DvAokjsUG8FHh-F53cMsE-GDjyZH_1_CrlDCnbGlqjsFbgAylqA7IAJWp9_N6dL5p8DHZTwlZ4IV8L1CtCALs7XVqvcQKHCCzB63Y8PgVDCAqpQSRb79JPVD4pZwkBKpOknfEY5y9wfbswZiRKdgz7o61_oFnd-yywpse-23HD6v0htThVF1SuGL1PuvGJ8p334nt9bpkZO3gaTh1xVD_uJMwHzbuBCF33_f-I5QMZO6bVooXqGfe1zvl3nDrPEjq1aPulvtP8RgREYEqE6b2hB8jouTiC_WpE3qrdMw9sfWGFbm04qC-2Zjoa1yYSXoxmYd0SnliSYHAad9aXoEmFENezQV-of7sc-NX1-2nAXRAEhaqh0IRuJwB4_sG7SvQmnanwkz-sBYxKqkoFpOsZ6hblgPDOPYY2NAsZlYkjvAL2mpiInrsmY_GzGsfwPeAx31iozImX75rao8rm-XucAmCIkRlpBz6MYKCjQgyRz3UtZCJ2DYF4lKqTjphEAgclbYZ7KiCuTn9HualwtEmVzHHFneHMKl7KnRQk-9wjgiyQ5nlsVpCCblg6JKr9of4utuPO3cBvbjhB4_ueQ40cpWVOICcOLS7_w0i3pCq1ZKDEMrYDJfz87r2sU9kw1zeFQk';
+ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoiYWN0dWF0ZSBwcm92aWRlIn0.x-bUZwDCC663wGYrWCYjQZwQWhN1CMuKgxuIN5dUF_izwMutiqF6Xc-tnXgZa93BbT3I74WOMk4awKHBUSTWekGs3-qF6gajorbat6n5180TOqvNu4CXuIPZN5zpngf4id3smMkKOT699tPnSEbmlkj4vk-mIjeOAU-FcYA-VbkKBTsjvfFgKa2OdB5h9uZARBg5Rx7uBN3JsH1I6j9zoLid184Ewa6bhU2qniFt5iPsGJniNsKsRrrndN1KzthO13My44s56yvwSHIOrgDGbXdja_eLuOVOq9pHCjCtorPScgEuUUE4aldIuML-_j397taNP9Y3VZYVvofEK7AuiePTbzwxrZ1RAjK74h1-4ued3A2gUTjr5BsRlc9b7eLZzxLJkrqdfGAzBh_rtrB7p32TbvpjeFP30NW6bB9JS43XACUUm_S_RcyI7BLuUdnFyQDQr6l6sRz9XayYXceilHdCxbAVN0HVnBeui5Bb0mUZYIRZeY8k6zcssmokANTD8ZviDMpKlOU3t5AlXJ0nLkgyMhV9IUTwPUv6F8BTPc-CquJCUNbTyo4ywTSoODWbm3PmQ3Y46gWF06xqnB4wehLscBdVk3iAihQp3tckGhMnx5PI_Oy7utIncr4pRCMos63TnBkfrl7d43cHQTuK0kO76EWtv4ODEHgLvEAv4HA';
var kuksa_context = {
authToken: TEST_TOKEN,
- socket: undefined,
+ client: undefined,
target: DEFAULT_TARGET,
+ subscribe_entries: [],
+ subscribe_stream: null,
+ locks: new Map()
}
var pathToHandler = null;
-function logReceivedMessage(data) {
- console.log('Received message:', data)
+function updateVehicleInfo(path, dp) {
+ if (!pathToHandler.has(path)) {
+ console.log('Handler not found for', path);
+ return;
+ }
+
+ var handler = pathToHandler.get(path);
+ handler.update(path, dp)
}
-function logConnectionStatus(status, event) {
- console.log('Connection status:', status);
+function send(action, values) {
}
-function kuksaws_onopen(event) {
- logConnectionStatus('Connected.', event);
-
- authorize();
- subscribe(PATHS.leftAirDistribution);
- subscribe(PATHS.leftFanSpeed);
- subscribe(PATHS.leftSeatWarmer);
- subscribe(PATHS.rightSeatWarmer);
- subscribe(PATHS.leftSeatTemperature);
- subscribe(PATHS.rightSeatTemperature);
- subscribe(PATHS.airConditioning);
- subscribe(PATHS.recirculation);
- subscribe(PATHS.frontDefroster);
- subscribe(PATHS.rearDefroster);
+function add_subscribe_entry(path) {
+ console.log('Adding subscribe entry for ' + path + '...')
+ var entry = new VAL.SubscribeEntry();
+ entry.setPath(path);
+ entry.setView(TYPES.View.VIEW_ALL);
+ entry.addFields(TYPES.Field.FIELD_PATH);
+ entry.addFields(TYPES.Field.FIELD_VALUE);
+ kuksa_context.subscribe_entries.push(entry);
}
-function kuksaws_onerror(event) {
- logConnectionStatus('Failed to connect:', event);
+function subscribe() {
+ if (kuksa_context.client == undefined) {
+ console.log("Client not initialized.");
+ return;
+ }
+ var metadata = {'authorization': 'Bearer ' + kuksa_context.authToken };
+
+ var request = new VAL.SubscribeRequest();
+ for (var i in kuksa_context.subscribe_entries) {
+ var entry = kuksa_context.subscribe_entries[i];
+ entry.setView(TYPES.View.VIEW_CURRENT_VALUE);
+ request.addEntries(entry);
+ }
+
+ kuksa_context.subscribe_stream = kuksa_context.client.subscribe(request, metadata);
+
+ kuksa_context.subscribe_stream.on('data', function(response) {
+ var updates = response.getUpdatesList();
+ for (var i in updates) {
+ var update = updates[i];
+ var entry = update.getEntry();
+ var path = entry.getPath();
+ var dp = entry.getValue();
+
+ if (!(entry && path && dp)) {
+ continue;
+ }
+
+ lock(path);
+ updateVehicleInfo(path, dp);
+ unlock(path);
+ }
+ });
+
+ kuksa_context.subscribe_stream.on('error', function(error) {
+ console.log("Error code: " + error.code + " message: " + error.message);
+ // if an error happens here, the databroker will drop the subscriber, so
+ // we need to subscribe again
+ subscribe();
+ });
}
-function kuksaws_onmessage(event) {
- logReceivedMessage(event.data);
+// TODO: investigate why an empty response is always being returned on the
+// response
+export function get(path) {
+ if (kuksa_context.client == undefined) {
+ console.log("Client not initialized.");
+ return;
+ }
+ if (isLocked(path)) {
+ return;
+ }
- var jsonData = JSON.parse(event.data);
+ var metadata = {'authorization': 'Bearer ' + kuksa_context.authToken };
+
+ var request = new VAL.GetRequest();
+ var entry = new VAL.EntryRequest();
+ entry.setPath(path);
+ entry.setView(TYPES.View.VIEW_ALL);
+ entry.addFields(TYPES.Field.FIELD_METADATA);
+ entry.addFields(TYPES.Field.FIELD_VALUE);
+ request.addEntries(entry);
+
+ kuksa_context.client.get(request, metadata, function(error, response) {
+ if (error) {
+ console.log("Get error, code: " + error.code);
+ return;
+ }
+ if (!response) {
+ return;
+ }
+ var entries = response.getEntriesList();
+ for (var i in entries) {
+ var entry = entries[i];
+ var path = entry.getPath();
+ var dp = entry.getValue();
+
+ if (!(entry && path && dp)) {
+ continue;
+ }
+ lock(path);
+ updateVehicleInfo(path, dp);
+ unlock(path);
+ }
+ });
+}
- if (jsonData.action == 'get' ||
- jsonData.action =='subscription') {
- updateVehicleInfo(jsonData);
+export function set(path, dp) {
+ if (kuksa_context.client == undefined) {
+ console.log("Client not initialized.");
+ return;
+ }
+ if (isLocked(path)) {
+ return;
}
+ lock(path);
+
+ var metadata = {'authorization': 'Bearer ' + kuksa_context.authToken };
+
+ var entry = new TYPES.DataEntry();
+ entry.setPath(path);
+ entry.setValue(dp);
+
+ var update = new VAL.EntryUpdate();
+ update.addFields(TYPES.Field.FIELD_PATH);
+ update.addFields(TYPES.Field.FIELD_VALUE);
+ update.setEntry(entry);
+
+ var request = new VAL.SetRequest();
+ request.addUpdates(update);
+
+ kuksa_context.client.set(request, metadata, function(error, response) {
+ // don't unlock updates here, only when we get a value from
+ // the subscription updates
+ });
}
-function updateVehicleInfo(message) {
- var value = message.data.dp.value;
- var path = message.data.path;
+export function init(handlers) {
+ console.log("Initializing kuka-val module...");
+ pathToHandler = new Map(handlers);
+ pathToHandler.forEach(function(handler, path) {
+ add_subscribe_entry(path);
+ });
- if (!pathToHandler.has(path)) {
- console.log('Handler not found for', path);
- return;
+ if (kuksa_context.client == undefined) {
+ console.log("Creating kuksa-val client...");
+ kuksa_context.client = new VAL_WEB.VALClient(kuksa_context.target);
+
+ subscribe();
}
+}
- var handler = pathToHandler.get(path);
- handler.update(path, value)
+export function setInt32(path, value) {
+ var dp = new TYPES.Datapoint();
+ dp.setInt32(value);
+ set(path, dp);
}
-function send(action, values) {
- var uuid = uuidv4();
- var data = Object.assign({
- action: action,
- tokens: kuksa_context.authToken,
- requestId: uuid,
- }, values);
- var message = JSON.stringify(data);
- console.log('Sent message:', message);
- kuksa_context.socket.send(message);
- return uuid;
+export function setUInt32(path, value) {
+ var dp = new TYPES.Datapoint();
+ dp.setUint32(value);
+ set(path, dp);
}
-function subscribe(path) {
- return send('subscribe', { path: path });
+export function setBool(path, value) {
+ var dp = new TYPES.Datapoint();
+ dp.setBool(value);
+ set(path, dp);
}
-function authorize() {
- return send('authorize');
+export function setString(path, value) {
+ var dp = new TYPES.Datapoint();
+ dp.setString(value);
+ set(path, dp);
}
-export function get(path) {
- return send('get', { path: path });
+export function lock(path) {
+ kuksa_context.locks[path] = true;
}
-export function set(path, value) {
- return send('set', { path: path, value: value });
+export function unlock(path) {
+ kuksa_context.locks[path] = false;
}
-export function init(handlers) {
- pathToHandler = new Map(handlers);
- if (kuksa_context.socket == undefined) {
- kuksa_context.socket = new WebSocket(kuksa_context.target);
- kuksa_context.socket.onopen = kuksaws_onopen;
- kuksa_context.socket.onerror = kuksaws_onerror;
- kuksa_context.socket.onmessage = kuksaws_onmessage;
+export function isLocked(path) {
+ if (!kuksa_context.locks.has(path)) {
+ kuksa_context.locks[path] = false;
}
+ return kuksa_context.locks[path];
}
diff --git a/src/js/paths.js b/src/js/paths.js
index 4f3c5d3..3001b51 100644
--- a/src/js/paths.js
+++ b/src/js/paths.js
@@ -15,14 +15,14 @@
*/
// https://github.com/COVESA/vehicle_signal_specification/blob/master/spec/Cabin/SingleHVACStation.vspec
-export const leftAirDistribution = 'Vehicle.Cabin.HVAC.Station.Row1.Left.AirDistribution';
-export const leftFanSpeed = 'Vehicle.Cabin.HVAC.Station.Row1.Left.FanSpeed';
-export const leftSeatTemperature = 'Vehicle.Cabin.Seat.Row1.Pos1.Heating';
-export const rightSeatTemperature = 'Vehicle.Cabin.Seat.Row1.Pos2.Heating';
+export const leftAirDistribution = 'Vehicle.Cabin.HVAC.Station.Row1.Driver.AirDistribution';
+export const leftFanSpeed = 'Vehicle.Cabin.HVAC.Station.Row1.Driver.FanSpeed';
+export const leftSeatTemperature = 'Vehicle.Cabin.Seat.Row1.DriverSide.Heating';
+export const rightSeatTemperature = 'Vehicle.Cabin.Seat.Row1.PassengerSide.Heating';
// https://github.com/COVESA/vehicle_signal_specification/blob/master/spec/Cabin/SingleSeat.vspec
-export const leftSeatWarmer = 'Vehicle.Cabin.Seat.Row1.Pos1.Switch.IsWarmerEngaged';
-export const rightSeatWarmer = 'Vehicle.Cabin.Seat.Row1.Pos2.Switch.IsWarmerEngaged';
+export const leftSeatWarmer = 'Vehicle.Cabin.Seat.Row1.DriverSide.Switch.IsWarmerEngaged';
+export const rightSeatWarmer = 'Vehicle.Cabin.Seat.Row1.PassengerSide.Switch.IsWarmerEngaged';
// https://github.com/COVESA/vehicle_signal_specification/blob/master/spec/Cabin/HVAC.vspec
export const recirculation = 'Vehicle.Cabin.HVAC.IsRecirculationActive';
diff --git a/src/js/seats.js b/src/js/seats.js
index 3afc481..91b45f6 100644
--- a/src/js/seats.js
+++ b/src/js/seats.js
@@ -30,7 +30,8 @@ export function init() {
controls.rightSeatWarmer = document.getElementById('RightChair');
}
-export function update(path, value) {
+export function update(path, dp) {
+ var value = dp.getBool();
switch (path) {
case PATHS.leftSeatWarmer:
values.leftSeatWarmer = value;
@@ -44,8 +45,8 @@ export function update(path, value) {
}
export function left() {
- KUKSA.set(PATHS.leftSeatWarmer, !values.leftSeatWarmer);
+ KUKSA.setBool(PATHS.leftSeatWarmer, !values.leftSeatWarmer);
}
export function right() {
- KUKSA.set(PATHS.rightSeatWarmer, !values.rightSeatWarmer);
+ KUKSA.setBool(PATHS.rightSeatWarmer, !values.rightSeatWarmer);
}
diff --git a/src/js/temperature.js b/src/js/temperature.js
index 1e35ddf..2d29b7c 100644
--- a/src/js/temperature.js
+++ b/src/js/temperature.js
@@ -40,7 +40,8 @@ function createTemperatureElement() {
return element;
}
-export function update(path, value) {
+export function update(path, dp) {
+ var value = dp.getInt32();
var temperature, node;
if (path == PATHS.rightSeatTemperature) {
values.rightTemperature = value;
@@ -69,8 +70,7 @@ export function left() {
isScrolling = setTimeout(function() {
var index = Math.round(controls.leftTemperatureNode.scrollTop / elementHeight);
values.leftTemperature = temperatures[index];
- console.log('LEFT', values.leftTemperature);
- KUKSA.set(PATHS.leftSeatTemperature, values.leftTemperature);
+ KUKSA.setInt32(PATHS.leftSeatTemperature, values.leftTemperature);
}, 100);
}
@@ -80,8 +80,7 @@ export function right() {
isScrolling = setTimeout(function() {
var index = Math.round(controls.rightTemperatureNode.scrollTop / elementHeight);
values.rightTemperature = temperatures[index];
- console.log('RIGHT', values.rightTemperature);
- KUKSA.set(PATHS.rightSeatTemperature, values.rightTemperature);
+ KUKSA.setInt32(PATHS.rightSeatTemperature, values.rightTemperature);
}, 100);
}