diff options
-rw-r--r-- | app/HVAC.qml | 258 | ||||
-rw-r--r-- | app/api/Binding.qml | 126 | ||||
-rw-r--r-- | binding/hvac-demo-binding.c | 260 |
3 files changed, 453 insertions, 191 deletions
diff --git a/app/HVAC.qml b/app/HVAC.qml index b1925dd..2610164 100644 --- a/app/HVAC.qml +++ b/app/HVAC.qml @@ -24,137 +24,137 @@ import 'api' as API ApplicationWindow { id: root - Translator { - id: translator - language: binding.language - } + Translator { + id: translator + language: binding.language + } - API.Binding { - id: binding - url: bindingAddress - onFanSpeedChanged: fanSpeedSlider.value = fanSpeed - onLanguageChanged: translator.language = language - } + API.Binding { + id: binding + url: bindingAddress + onFanSpeedChanged: fanSpeedSlider.value = fanSpeed + onLanguageChanged: translator.language = language + } - ColumnLayout { - anchors.fill: parent - anchors.topMargin: width / 10 - anchors.bottomMargin: width / 10 - RowLayout { - Layout.fillHeight: true - Layout.alignment: Qt.AlignHCenter - Image { - source: './images/HMI_HVAC_Fan_Icon.svg' - } - Item { - width: root.width * 0.8 - Slider { - id: fanSpeedSlider - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - from: 0.0 - to: 255.0 - stepSize: 1.0 - onValueChanged: { - binding.fanSpeed = value - } - } - Label { - anchors.left: fanSpeedSlider.left - anchors.top: fanSpeedSlider.bottom - font.pixelSize: 32 - text: translator.translate(qsTr('FAN SPEED'), translator.language) - } - } - } - RowLayout { - Layout.fillHeight: true - Layout.fillWidth: true - Layout.alignment: Layout.Center - spacing: 20 - ColumnLayout { - Layout.fillWidth: true - spacing: 20 - SeatHeatButton { - id: leftSeat - side: 'Left' - } - HeatDegree { - onCurrentItemChanged: { - console.log("Left Temp changed",degree) - binding.leftTemperature = degree - } - } - } - ColumnLayout { - Layout.fillWidth: true - spacing: 20 - ToggleButton { - onImage: './images/HMI_HVAC_Active.svg' - offImage: './images/HMI_HVAC_Inactive.svg' - Label { - anchors.centerIn: parent - color: parent.checked ? '#00ADDC' : '#848286' - text: translator.translate(qsTr('A/C'), translator.language) - font.pixelSize: parent.height / 3 - } - onCheckedChanged: { - console.debug('A/C', checked) - } - } - ToggleButton { - onImage: './images/HMI_HVAC_Active.svg' - offImage: './images/HMI_HVAC_Inactive.svg' - Label { - anchors.centerIn: parent - color: parent.checked ? '#00ADDC' : '#848286' - text: translator.translate(qsTr('AUTO'), translator.language) - font.pixelSize: parent.height / 3 - } - onCheckedChanged: { - console.debug('AUTO', checked) - } - } - ToggleButton { - onImage: './images/HMI_HVAC_Circulation_Active.svg' - offImage: './images/HMI_HVAC_Circulation_Inactive.svg' - onCheckedChanged: { - console.debug('Circulation', checked) - } - } - } + ColumnLayout { + anchors.fill: parent + anchors.topMargin: width / 10 + anchors.bottomMargin: width / 10 + RowLayout { + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + Image { + source: './images/HMI_HVAC_Fan_Icon.svg' + } + Item { + width: root.width * 0.8 + Slider { + id: fanSpeedSlider + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + from: 0.0 + to: 255.0 + stepSize: 1.0 + onValueChanged: { + binding.fanSpeed = value + } + } + Label { + anchors.left: fanSpeedSlider.left + anchors.top: fanSpeedSlider.bottom + font.pixelSize: 32 + text: translator.translate(qsTr('FAN SPEED'), translator.language) + } + } + } + RowLayout { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.alignment: Layout.Center + spacing: 20 + ColumnLayout { + Layout.fillWidth: true + spacing: 20 + SeatHeatButton { + id: leftSeat + side: 'Left' + } + HeatDegree { + onCurrentItemChanged: { + console.log("Left Temp changed",degree) + binding.leftTemperature = degree + } + } + } + ColumnLayout { + Layout.fillWidth: true + spacing: 20 + ToggleButton { + onImage: './images/HMI_HVAC_Active.svg' + offImage: './images/HMI_HVAC_Inactive.svg' + Label { + anchors.centerIn: parent + color: parent.checked ? '#00ADDC' : '#848286' + text: translator.translate(qsTr('A/C'), translator.language) + font.pixelSize: parent.height / 3 + } + onCheckedChanged: { + console.debug('A/C', checked) + } + } + ToggleButton { + onImage: './images/HMI_HVAC_Active.svg' + offImage: './images/HMI_HVAC_Inactive.svg' + Label { + anchors.centerIn: parent + color: parent.checked ? '#00ADDC' : '#848286' + text: translator.translate(qsTr('AUTO'), translator.language) + font.pixelSize: parent.height / 3 + } + onCheckedChanged: { + console.debug('AUTO', checked) + } + } + ToggleButton { + onImage: './images/HMI_HVAC_Circulation_Active.svg' + offImage: './images/HMI_HVAC_Circulation_Inactive.svg' + onCheckedChanged: { + console.debug('Circulation', checked) + } + } + } - ColumnLayout { - Layout.fillWidth: true - spacing: 20 - SeatHeatButton { - id: rightSeat - side: 'Right' - } - HeatDegree { - onCurrentItemChanged: { - console.log("Right Temp changed",degree) - binding.rightTemperature = degree - } - } - } - } + ColumnLayout { + Layout.fillWidth: true + spacing: 20 + SeatHeatButton { + id: rightSeat + side: 'Right' + } + HeatDegree { + onCurrentItemChanged: { + console.log("Right Temp changed",degree) + binding.rightTemperature = degree + } + } + } + } - RowLayout { - Layout.fillHeight: true - Layout.alignment: Qt.AlignHCenter - spacing: root.width / 20 - Repeater { - model: ['AirDown', 'AirUp', 'AirRight', 'Rear', 'Front'] - ToggleButton { - onImage: './images/HMI_HVAC_%1_Active.svg'.arg(model.modelData) - offImage: './images/HMI_HVAC_%1_Inactive.svg'.arg(model.modelData) - onCheckedChanged: { - console.debug(model.modelData, checked) - } - } - } - } - } + RowLayout { + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + spacing: root.width / 20 + Repeater { + model: ['AirDown', 'AirUp', 'AirRight', 'Rear', 'Front'] + ToggleButton { + onImage: './images/HMI_HVAC_%1_Active.svg'.arg(model.modelData) + offImage: './images/HMI_HVAC_%1_Inactive.svg'.arg(model.modelData) + onCheckedChanged: { + console.debug(model.modelData, checked) + } + } + } + } + } } diff --git a/app/api/Binding.qml b/app/api/Binding.qml index 25388df..2d88087 100644 --- a/app/api/Binding.qml +++ b/app/api/Binding.qml @@ -19,67 +19,75 @@ import QtWebSockets 1.0 import 'MessageId.js' as MessageId WebSocket { - id: root - active: true + id: root + active: true - property string statusString: "waiting..." + property string statusString: "waiting..." - property real fanSpeed: 0.0 - property real leftTemperature: 21.0 - property real rightTemperature: 21.0 - property string language: "en_US" + property real fanSpeed: 0.0 + property real leftTemperature: 21.0 + property real rightTemperature: 21.0 + property string language: "en_US" - property Connections c : Connections { - target: root - onFanSpeedChanged: { - var json = [MessageId.call, '9999', 'hvac/set', {'FanSpeed': fanSpeed}] - console.debug(JSON.stringify(json)) - sendTextMessage(JSON.stringify(json)) - } - onLeftTemperatureChanged: { - var json = [MessageId.call, '9999', 'hvac/set', {'LeftTemperature': leftTemperature}] - console.debug(JSON.stringify(json)) - sendTextMessage(JSON.stringify(json)) - } - onRightTemperatureChanged: { - var json = [MessageId.call, '9999', 'hvac/set', {'RightTemperature': rightTemperature}] - console.debug(JSON.stringify(json)) - sendTextMessage(JSON.stringify(json)) - } - onLanguageChanged: { - var json = [MessageId.call, '9999', 'hvac/set', {'Language': language}] - console.debug(JSON.stringify(json)) - sendTextMessage(JSON.stringify(json)) - } - } + property Connections c : Connections { + target: root + onFanSpeedChanged: { + var json = [MessageId.call, '9999', 'hvac/set', {'FanSpeed': fanSpeed}] + console.debug(JSON.stringify(json)) + sendTextMessage(JSON.stringify(json)) + } + onLeftTemperatureChanged: { + var json = [MessageId.call, '9999', 'hvac/set', {'LeftTemperature': leftTemperature}] + console.debug(JSON.stringify(json)) + sendTextMessage(JSON.stringify(json)) - onTextMessageReceived: { - var json = JSON.parse(message) - var request = json[2].request - var response = json[2].response - console.log("HVAC Binding Message: ",message) - switch (json[0]) { - case MessageId.call: - break - case MessageId.retok: - root.statusString = request.status - break - case MessageId.reterr: - root.statusString = "Bad return value, binding probably not installed" - break - case MessageId.event: - if (json[1] == "hvac/language") - console.log("HVAC event received: ",json[2]) - root.language = json[2].data - root.statusString = "Language changed to "+language - break - } - } - onStatusChanged: { - switch (status) { - case WebSocket.Error: - root.statusString = "WebSocket error: " + root.errorString - break - } - } + var json1 = [MessageId.call, '9999', 'hvac/temp_left_zone_led', {'LeftLed': leftTemperature}] + console.debug(JSON.stringify(json1)) + sendTextMessage(JSON.stringify(json1)) + } + onRightTemperatureChanged: { + var json = [MessageId.call, '9999', 'hvac/set', {'RightTemperature': rightTemperature}] + console.debug(JSON.stringify(json)) + sendTextMessage(JSON.stringify(json)) + + var json1 = [MessageId.call, '9999', 'hvac/temp_right_zone_led', {'RightLed': rightTemperature}] + console.debug(JSON.stringify(json1)) + sendTextMessage(JSON.stringify(json1)) + } + onLanguageChanged: { + var json = [MessageId.call, '9999', 'hvac/set', {'Language': language}] + console.debug(JSON.stringify(json)) + sendTextMessage(JSON.stringify(json)) + } + } + + onTextMessageReceived: { + var json = JSON.parse(message) + var request = json[2].request + var response = json[2].response + console.log("HVAC Binding Message: ",message) + switch (json[0]) { + case MessageId.call: + break + case MessageId.retok: + root.statusString = request.status + break + case MessageId.reterr: + root.statusString = "Bad return value, binding probably not installed" + break + case MessageId.event: + if (json[1] == "hvac/language") + console.log("HVAC event received: ",json[2]) + root.language = json[2].data + root.statusString = "Language changed to "+language + break + } + } + onStatusChanged: { + switch (status) { + case WebSocket.Error: + root.statusString = "WebSocket error: " + root.errorString + break + } + } } diff --git a/binding/hvac-demo-binding.c b/binding/hvac-demo-binding.c index 8565b56..b4f922a 100644 --- a/binding/hvac-demo-binding.c +++ b/binding/hvac-demo-binding.c @@ -19,6 +19,7 @@ */ #define _GNU_SOURCE +#include <stdio.h> #include <string.h> #include <stdbool.h> #include <unistd.h> @@ -32,6 +33,9 @@ #include <json-c/json.h> #define AFB_BINDING_VERSION 2 +#define RED "/sys/class/leds/blinkm-3-9-red/brightness" +#define BLUE "/sys/class/leds/blinkm-3-9-blue/brightness" +#define GREEN "/sys/class/leds/blinkm-3-9-green/brightness" #include <afb/afb-binding.h> #define CAN_DEV "vcan0" @@ -94,7 +98,33 @@ static struct { { "LeftTemperature", 21 }, { "RightTemperature", 21 }, { "Temperature", 21 }, - { "FanSpeed", 0 } + { "FanSpeed", 0 }, + { "ACEnabled", 0 }, + { "LeftLed", 15 }, + { "RightLed", 15 } +}; + +// Holds RGB combinations for each temperature +static struct { + const int temperature; + const int rgb[3]; +} degree_colours[] = { + {15, {0, 0, 229} }, + {16, {22, 0, 204} }, + {17, {34, 0, 189} }, + {18, {46, 0, 175} }, + {19, {58, 0, 186} }, + {20, {70, 0, 146} }, + {21, {82, 0, 131} }, + {22, {104, 0, 116} }, + {23, {116, 0, 102} }, + {24, {128, 0, 87} }, + {25, {140, 0, 73} }, + {26, {152, 0, 58} }, + {27, {164, 0, 43} }, + {28, {176, 0, 29} }, + {29, {188, 0, 14} }, + {30, {201, 0, 5} } }; struct can_handler { @@ -182,6 +212,16 @@ static uint8_t read_temp_right_zone() return hvac_values[1].value; } +static uint8_t read_temp_left_led() +{ + return hvac_values[5].value; +} + +static uint8_t read_temp_right_led() +{ + return hvac_values[6].value; +} + static uint8_t read_temp() { return (uint8_t)(((int)read_temp_left_zone() + (int)read_temp_right_zone()) >> 1); @@ -192,6 +232,205 @@ static uint8_t read_fanspeed() return hvac_values[3].value; } +/* + * @brief Writing to LED for both temperature sliders + */ + +static int temp_write_led() +{ + int rc = 0, red_flag, blue_flag, green_flag, red_value, green_value, blue_value; + int right_temp; + int left_temp; + + // /sys/class/leds/blinkm-3-9-red/brightness + FILE* r = fopen(RED, "w"); + if (r == NULL) { + AFB_ERROR("Unable to open RED path for writing"); + red_flag = 1; + } + + // /sys/class/leds/blinkm-3-9-green/brightness + FILE* g = fopen(GREEN, "w"); + if (g == NULL) { + AFB_ERROR("Unable to open GREEN path for writing"); + green_flag = 1; + } + + // /sys/class/leds/blinkm-3-9-blue/brightness + FILE* b = fopen(BLUE, "w"); + if (b == NULL) { + AFB_ERROR("Unable to open BLUE path for writing"); + blue_flag = 1; + } + + if (red_flag || green_flag || blue_flag) + { + rc = 1; + } + + left_temp = read_temp_left_led() - 15; + right_temp = read_temp_right_led() - 15; + + // Calculates average colour value taken from the temperature toggles + red_value = (degree_colours[left_temp].rgb[0] + degree_colours[right_temp].rgb[0]) / 2; + green_value = (degree_colours[left_temp].rgb[1] + degree_colours[right_temp].rgb[1]) / 2; + blue_value = (degree_colours[left_temp].rgb[2] + degree_colours[right_temp].rgb[2]) / 2; + + // Writes to LED file the specific colour + fprintf(r, "%d", red_value); + fprintf(g, "%d", green_value); + fprintf(b, "%d", blue_value); + fclose(r); + fclose(g); + fclose(b); + return rc; + +} + +/* + * @brief Get temperature of left toggle in HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void temp_left_zone_led(struct afb_req request) +{ + int i = 5, rc, x, changed; + double d; + struct json_object *query, *val; + uint8_t values[sizeof hvac_values / sizeof *hvac_values]; + uint8_t saves[sizeof hvac_values / sizeof *hvac_values]; + + AFB_WARNING("In temp_left_zone_led."); + + query = afb_req_json(request); + + /* records initial values */ + AFB_WARNING("Records initial values"); + values[i] = saves[i] = hvac_values[i].value; + + + if (json_object_object_get_ex(query, hvac_values[i].name, &val)) + { + AFB_WARNING("Value of values[i] = %d", values[i]); + AFB_WARNING("We got it. Tests if it is an int or double."); + if (json_object_is_type(val, json_type_int)) { + x = json_object_get_int(val); + AFB_WARNING("We get an int: %d",x); + } + else if (json_object_is_type(val, json_type_double)) { + d = json_object_get_double(val); + x = (int)round(d); + AFB_WARNING("We get a double: %f => %d",d,x); + } + else { + afb_req_fail_f(request, "bad-request", + "argument '%s' isn't integer or double", hvac_values[i].name); + return; + } + if (x < 0 || x > 255) + { + afb_req_fail_f(request, "bad-request", + "argument '%s' is out of bounds", hvac_values[i].name); + return; + } + if (values[i] != x) { + values[i] = (uint8_t)x; + changed = 1; + AFB_WARNING("%s changed to %d", hvac_values[i].name,x); + } + } + else { + AFB_WARNING("%s not found in query!",hvac_values[i].name); + } + + + if (changed) { + hvac_values[i].value = values[i]; // update structure at line 102 + AFB_WARNING("WRITE_LED: value: %d", hvac_values[i].value); + rc = temp_write_led(); + if (rc >= 0) + afb_req_success(request, NULL, NULL); + else if (retry(temp_write_led)) { + /* restore initial values */ + hvac_values[i].value = saves[i]; + afb_req_fail(request, "error", "I2C error"); + } + } +} + +/* + * @brief Get temperature of right toggle in HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void temp_right_zone_led(struct afb_req request) +{ + int i = 6, rc, x, changed; + double d; + struct json_object *query, *val; + uint8_t values[sizeof hvac_values / sizeof *hvac_values]; + uint8_t saves[sizeof hvac_values / sizeof *hvac_values]; + + AFB_WARNING("In temp_right_zone_led."); + + query = afb_req_json(request); + + /* records initial values */ + AFB_WARNING("Records initial values"); + values[i] = saves[i] = hvac_values[i].value; + + + if (json_object_object_get_ex(query, hvac_values[i].name, &val)) + { + AFB_WARNING("Value of values[i] = %d", values[i]); + AFB_WARNING("We got it. Tests if it is an int or double."); + if (json_object_is_type(val, json_type_int)) { + x = json_object_get_int(val); + AFB_WARNING("We get an int: %d",x); + } + else if (json_object_is_type(val, json_type_double)) { + d = json_object_get_double(val); + x = (int)round(d); + AFB_WARNING("We get a double: %f => %d",d,x); + } + else { + afb_req_fail_f(request, "bad-request", + "argument '%s' isn't integer or double", hvac_values[i].name); + return; + } + if (x < 0 || x > 255) + { + afb_req_fail_f(request, "bad-request", + "argument '%s' is out of bounds", hvac_values[i].name); + return; + } + if (values[i] != x) { + values[i] = (uint8_t)x; + changed = 1; + AFB_WARNING("%s changed to %d", hvac_values[i].name,x); + } + } + else { + AFB_WARNING("%s not found in query!", hvac_values[i].name); + } + + + if (changed) { + hvac_values[i].value = values[i]; // update structure at line 102 + AFB_WARNING("WRITE_LED: value: %d", hvac_values[i].value); + rc = temp_write_led(); + if (rc >= 0) + afb_req_success(request, NULL, NULL); + else if (retry(temp_write_led)) { + /* restore initial values */ + hvac_values[i].value = saves[i]; + afb_req_fail(request, "error", "I2C error"); + } + } +} + static int write_can() { struct can_frame txCanFrame; @@ -221,7 +460,7 @@ static int write_can() if(!can_handler.simulation) { rc = (int)sendto(can_handler.socket, &txCanFrame, sizeof(struct can_frame), 0, - (struct sockaddr*)&can_handler.txAddress, sizeof(can_handler.txAddress)); + (struct sockaddr*)&can_handler.txAddress, sizeof(can_handler.txAddress)); if (rc < 0) { AFB_ERROR("Sending CAN frame failed."); @@ -478,6 +717,21 @@ static const struct afb_verb_v2 _afb_verbs_v2_hvac[]= { .session = AFB_SESSION_NONE_V2 }, { + .verb = "temp_left_zone_led", + .callback = temp_left_zone_led, + .auth = NULL, + .info = "Turn on LED on left temperature zone", + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "temp_right_zone_led", + .callback = temp_right_zone_led, + .auth = NULL, + .info = "Turn on LED on left temperature zone", + .session = AFB_SESSION_NONE_V2 + + }, + { .verb = NULL, .callback = NULL, .auth = NULL, @@ -495,4 +749,4 @@ const struct afb_binding_v2 afbBindingV2 = { .init = bindingServiceInit, .onevent = onEvent, .noconcurrency = 0 -}; +};
\ No newline at end of file |