aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt95
-rw-r--r--README11
-rw-r--r--config.xml.in11
-rw-r--r--export.map1
-rw-r--r--hvac-hybrid-qml-binding.c351
-rw-r--r--hvacplugin.cpp239
-rw-r--r--hvacplugin.h86
-rw-r--r--icon_hvac_hybrid_qml.pngbin0 -> 4236 bytes
-rw-r--r--qml/ClimateButton.qml30
-rw-r--r--qml/FanControl.qml54
-rw-r--r--qml/HVAC.qml89
-rw-r--r--qml/HazardButton.qml37
-rw-r--r--qml/MiddleColumn.qml33
-rw-r--r--qml/SeatHeatButton.qml39
-rw-r--r--qml/TempSlider.qml61
-rw-r--r--qml/TemperatureWheel.qml56
-rw-r--r--qml/hvac-hybrid-qml-app.qml87
-rw-r--r--qml/models/HVACModel.qml47
-rw-r--r--qml/models/TemperatureModel.qml28
-rw-r--r--qml/models/qmldir8
20 files changed, 1012 insertions, 351 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e772ce..21ed9b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,23 +1,88 @@
-PROJECT(hvacplugin CXX)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
-SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+###########################################################################
+# Copyright 2016 IoT.bzh
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+# author: Stéphane Desneux <stephane.desneux@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+project(hvac-hybrid-qml)
+
+cmake_minimum_required(VERSION 3.3)
+
+include(GNUInstallDirs)
+
+set(PROJECT_VERSION "0.1")
+set(PROJECT_ICON "icon_hvac_hybrid_qml.png")
+set(PROJECT_LIBDIR "lib")
+
+set(CMAKE_BUILD_TYPE Debug)
+
+###########################################################################
+
+link_libraries(-Wl,--as-needed -Wl,--gc-sections)
+
+add_compile_options(-Wall -Wextra -Wconversion)
+add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care?
+add_compile_options(-Werror=maybe-uninitialized)
+add_compile_options(-Werror=implicit-function-declaration)
+add_compile_options(-ffunction-sections -fdata-sections)
+add_compile_options(-Wl,--as-needed -Wl,--gc-sections)
+add_compile_options(-fPIC)
+
+set(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE")
+set(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb -Wp,-U_FORTIFY_SOURCE")
+set(CMAKE_C_FLAGS_RELEASE "-g -O2")
+set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage")
+
+###########################################################################
include(FindPkgConfig)
-pkg_check_modules(gio gio-2.0)
-pkg_check_modules(gio-unix gio-unix-2.0)
-pkg_check_modules(json-c json-c)
-pkg_check_modules(amb automotive-message-broker)
-find_package(Boost REQUIRED)
-include_directories(${CMAKE_SOURCE_DIR}/lib ${include_dirs} ${gio_INCLUDE_DIRS} ${gio-unix_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/plugins/common ${Boost_INCLUDE_DIRS} ${json-c_INCLUDE_DIRS} ${amb_INCLUDE_DIRS})
+pkg_check_modules(EXTRAS REQUIRED json-c afb-daemon)
+add_compile_options(${EXTRAS_CFLAGS})
+include_directories(${EXTRAS_INCLUDE_DIRS})
+link_libraries(${EXTRAS_LIBRARIES})
+
+###########################################################################
+# the binding for afb
+
+message(STATUS "Creation of ${PROJECT_NAME} for AFB-DAEMON")
-set(hvacplugin_headers hvacplugin.h)
-set(hvacplugin_sources hvacplugin.cpp)
+###############################################################
+add_library(${PROJECT_NAME} MODULE ${PROJECT_NAME}-binding.c
+ ./qml)
+# ./qml/${PROJECT_NAME}-app.qml)
-add_library(hvacplugin MODULE ${hvacplugin_sources} ${hvacplugin_headers})
-set_target_properties(hvacplugin PROPERTIES PREFIX "")
-target_link_libraries(hvacplugin amb amb-plugins-common -L${CMAKE_CURRENT_BINARY_DIR}/plugins/common ${link_libraries} -lrt ${json-c_LIBRARIES})
+set_target_properties(${PROJECT_NAME} PROPERTIES
+ PREFIX ""
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map"
+)
-install(TARGETS hvacplugin LIBRARY DESTINATION ${PLUGIN_INSTALL_PATH})
+configure_file(config.xml.in config.xml)
+add_custom_command(
+ OUTPUT ${PROJECT_NAME}.wgt
+ DEPENDS ${PROJECT_NAME}
+ COMMAND rm -rf package
+ COMMAND mkdir -p package/${PROJECT_LIBDIR} package/htdocs
+ COMMAND cp config.xml package/
+ COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_ICON} package/icon.png
+ COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/qml/ package/
+ COMMAND cp ${PROJECT_NAME}.so package/${PROJECT_LIBDIR}
+ COMMAND wgtpkg-pack -f -o ${PROJECT_NAME}.wgt package
+ # COMMAND rm -rf package
+)
+add_custom_target(widget ALL DEPENDS ${PROJECT_NAME}.wgt)
diff --git a/README b/README
deleted file mode 100644
index c2fa262..0000000
--- a/README
+++ /dev/null
@@ -1,11 +0,0 @@
-This folder contains the files to build the AMB source plugin from K2L to control the demo HVAC hardware provided by Microchip. It is a part of the AGL demo for CES 2016. See COPYING file for the license.
-
-Copyright (C) 2013-2015 K2L GmbH & Co. KG
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-This file is licensed under GPLv2.
-
diff --git a/config.xml.in b/config.xml.in
new file mode 100644
index 0000000..6ca09f1
--- /dev/null
+++ b/config.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@">
+ <name>App Framework - @PROJECT_NAME@</name>
+ <icon src="icon.png"/>
+ <content src="qml/@PROJECT_NAME@-app.qml" type="application/vnd.agl.qml.hybrid"/>
+ <description>This application is used to control and dialog with HVAC system</description>
+ <author>Romain Forlot &lt;romain.forlot@iot.bzh&gt;</author>
+ <license>APL 2.0</license>
+</widget>
+
+
diff --git a/export.map b/export.map
new file mode 100644
index 0000000..52c1b4a
--- /dev/null
+++ b/export.map
@@ -0,0 +1 @@
+{ global: afbBindingV1*; local: *; };
diff --git a/hvac-hybrid-qml-binding.c b/hvac-hybrid-qml-binding.c
new file mode 100644
index 0000000..634f12e
--- /dev/null
+++ b/hvac-hybrid-qml-binding.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2015, 2016 "IoT.bzh"
+ * Author "Manuel Bachmann"
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <json-c/json.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/can.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <afb/afb-binding.h>
+
+#define CAN_DEV "can0"
+
+const struct afb_binding_interface *interface;
+
+// Initialize CAN payload array that will be sent trough the socket
+__u8 can_payload[8] = {
+ 21,
+ 21,
+ 21,
+ 240, // Don't know why 240 but it was 0xF0 in the original amb hvacplugin
+ 0,
+ 1, // Don't know why 1 but it was 0xF0 in the original amb hvacplugin
+ 0, // Don't know why 0 but it was 0xF0 in the original amb hvacplugin
+ 0 // Don't know why 0 but it was 0xF0 in the original amb hvacplugin
+};
+
+struct can_handler{
+ int socket;
+ struct sockaddr_can txAddress;
+};
+
+struct can_handler *ch;
+
+/***************************************************************************************/
+/***************************************************************************************/
+/** **/
+/** **/
+/** SECTION: HANDLE CAN DEVICE **/
+/** **/
+/** **/
+/***************************************************************************************/
+/***************************************************************************************/
+
+static struct can_handler open_can_dev()
+{
+ struct can_handler ch;
+ ch.socket = -1;
+
+ ch.socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+ printf("Socket: %i\n", ch.socket);
+ if (ch.socket < 0)
+ {
+ printf("ERROR: socket could not be created\n");
+ }
+ else
+ {
+ struct ifreq ifr;
+
+ // Attemts to open a socket to CAN bus
+ strcpy(ifr.ifr_name, CAN_DEV);
+ if(ioctl(ch.socket, SIOCGIFINDEX, &ifr) < 0)
+ {
+ close(ch.socket);
+ ch.socket = -1;
+ printf("ERROR: ioctl failed\n");
+ }
+ else
+ {
+ ch.txAddress.can_family = AF_CAN;
+ ch.txAddress.can_ifindex = ifr.ifr_ifindex;
+
+ // And bind it to ch.txAddress struct
+ if (bind(ch.socket, (struct sockaddr *)&ch.txAddress, sizeof(ch.txAddress)) < 0)
+ {
+ close(ch.socket);
+ ch.socket = -1;
+ printf("ERROR: bind failed\n");
+ }
+ return ch;
+ }
+ }
+ printf("Error: unexpected behavior\n");
+ return ch;
+}
+
+static void close_can_dev(struct can_handler ch)
+{
+ int socketId = ch.socket;
+ if(socketId >= 0)
+ {
+ close(socketId);
+ printf("Socket closed\n");
+ }
+}
+
+static void write_can(struct can_handler ch)
+{
+ printf("Write to CAN bus\n");
+ // Hardcoded can_id and dlc (data lenght code)
+ struct can_frame txCanFrame;
+ txCanFrame.can_id = 0x30;
+ txCanFrame.can_dlc = 8;
+
+ if (ch.socket >= 0)
+ {
+ printf("Send CAN message\n");
+
+ txCanFrame.data[0] = can_payload[0];
+ txCanFrame.data[1] = can_payload[1];
+ txCanFrame.data[2] = (can_payload[0] + can_payload[1]) & 0x02;
+ txCanFrame.data[3] = can_payload[3];
+ txCanFrame.data[4] = can_payload[4];
+ txCanFrame.data[5] = can_payload[5];
+ txCanFrame.data[6] = can_payload[6];
+ txCanFrame.data[7] = can_payload[7];
+
+ sendto(ch.socket, &txCanFrame, sizeof(struct can_frame), 0,
+ (struct sockaddr*)&ch.txAddress, sizeof(ch.txAddress));
+ }
+ else
+ {
+ printf("Error: socket not initialized\n");
+ }
+ printf("Error sending on CAN bus. Unexpected behavior");
+}
+
+static int read_temp_left_zone()
+{
+ return can_payload[0];
+}
+
+static int read_temp_right_zone()
+{
+ return can_payload[1];
+}
+
+static int read_fanspeed()
+{
+ return can_payload[4];
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/** **/
+/** **/
+/** SECTION: BINDING VERBS IMPLEMENTATION **/
+/** **/
+/** **/
+/***************************************************************************************/
+/***************************************************************************************/
+
+/*
+ * @brief Simple afb-daemon ping
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void ping (struct afb_req request)
+{
+ static int pingcount = 0;
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, NULL, "Ping Binder Daemon count=%d query=%s", ++pingcount, json_object_to_json_string(query));
+}
+
+/*
+ * @brief Get fan speed HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void get_fanspeed(struct afb_req request)
+{
+ json_object *ret_json;
+ int fanspeed = read_fanspeed();
+
+ ret_json = json_object_new_object();
+ json_object_object_add(ret_json, "FanSpeed", json_object_new_int(fanspeed));
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, ret_json, "Fan Speed:%d query=%s", fanspeed, json_object_to_json_string(query));
+}
+
+/*
+ * @brief Set fan speed to HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void set_fanspeed(struct afb_req request)
+{
+ struct can_handler ch;
+ int fanspeed = (int)afb_req_value(request, "FanSpeed");
+
+ // Set can_payload array with the new fan speed value
+ can_payload[4] = fanspeed;
+
+ ch = open_can_dev();
+ write_can(ch);
+ close_can_dev(ch);
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, NULL, "Fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query));
+}
+
+/*
+ * @brief Read Consign right zone temperature for HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void get_temp_right_zone(struct afb_req request)
+{
+ json_object *ret_json;
+ int temp = read_temp_right_zone();
+
+ ret_json = json_object_new_object();
+ json_object_object_add(ret_json, "RightTemperature", json_object_new_int(temp));
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, ret_json, "Temperature right zone:%d query=%s", temp, json_object_to_json_string(query));
+}
+
+/*
+ * @brief Read Consign left zone temperature for HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void get_temp_left_zone(struct afb_req request)
+{
+ json_object *ret_json;
+ int temp = read_temp_left_zone();
+
+ ret_json = json_object_new_object();
+ json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(temp));
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, ret_json, "Temperature left zone:%d query=%s", temp, json_object_to_json_string(query));
+}
+
+
+/*
+ * @brief Set right zone temperature to HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void set_temp_right_zone(struct afb_req request)
+{
+ struct can_handler ch;
+ int tempright = (int)afb_req_value(request, "RightTemperature");
+
+ // Set can_payload array with the new fan speed value
+ can_payload[1] = tempright;
+
+ ch = open_can_dev();
+ write_can(ch);
+ close_can_dev(ch);
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, NULL, "Ping Binder Daemon fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query));
+}
+
+/*
+ * @brief Set left zone temperature to HVAC system
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void set_temp_left_zone(struct afb_req request)
+{
+ struct can_handler ch;
+ int templeft = (int)afb_req_value(request, "LeftTemperature");
+
+ // Set can_payload array with the new fan speed value
+ can_payload[0] = templeft;
+
+ ch = open_can_dev();
+ write_can(ch);
+ close_can_dev(ch);
+
+ json_object *query = afb_req_json(request);
+ afb_req_success_f(request, NULL, "Ping Binder Daemon fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query));
+}
+
+/*
+ * @brief Read all values
+ *
+ * @param struct afb_req : an afb request structure
+ *
+ */
+static void get_all(struct afb_req request)
+{
+ json_object *ret_json;
+
+ ret_json = json_object_new_object();
+ json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(read_temp_left_zone()));
+ json_object_object_add(ret_json, "RightTemperature", json_object_new_int(read_temp_right_zone()));
+ json_object_object_add(ret_json, "FanSpeed", json_object_new_int(read_fanspeed()));
+
+ afb_req_success_f(request, ret_json, NULL);
+}
+
+// TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth
+static const struct afb_verb_desc_v1 verbs[]= {
+ {"ping" , AFB_SESSION_NONE, ping , "Ping the binder"},
+ {"get_temp_left_zone" , AFB_SESSION_NONE, get_temp_left_zone , "Get the left zone temperature"},
+ {"get_temp_right_zone" , AFB_SESSION_NONE, get_temp_right_zone , "Get the right zone temperature"},
+ {"set_temp_left_zone" , AFB_SESSION_NONE, set_temp_left_zone , "Set the left zone temperature"},
+ {"set_temp_right_zone" , AFB_SESSION_NONE, set_temp_right_zone , "Set the right zone temperature"},
+ {"set_fanspeed" , AFB_SESSION_NONE, set_fanspeed , "Set fanspeed"},
+ {"get_fanspeed" , AFB_SESSION_NONE, get_fanspeed , "Read fan speed"},
+ {"get_all" , AFB_SESSION_NONE, get_all , "Read all values"},
+ {NULL}
+};
+
+static const struct afb_binding plugin_desc = {
+ .type = AFB_BINDING_VERSION_1,
+ .v1 = {
+ .info = "hvac hybrid service",
+ .prefix = "hvac",
+ .verbs = verbs
+ }
+};
+
+const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf)
+{
+ interface = itf;
+
+ return &plugin_desc;
+}
diff --git a/hvacplugin.cpp b/hvacplugin.cpp
deleted file mode 100644
index d67d9b4..0000000
--- a/hvacplugin.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * hvacplugin.cpp - AMB plugin to conttrol HVAC demo hardware for CES 2016
- *
- * Copyright (C) 2013-2015 K2L GmbH & Co. KG
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This file is licensed under GPLv2.
- */
-
-#include <vehicleproperty.h>
-#include <listplusplus.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <linux/can.h>
-
-#include <logger.h>
-
-#include "hvacplugin.h"
-
-static const char* DEFAULT_CAN_IF_NAME = "vcan0";
-
-//----------------------------------------------------------------------------
-// K2LHvacPlugin
-//----------------------------------------------------------------------------
-
-extern "C" void create(AbstractRoutingEngine* routingengine, std::map<std::string, std::string> config)
-{
- new K2LHvacPlugin(routingengine, config);
-}
-
-K2LHvacPlugin::K2LHvacPlugin(AbstractRoutingEngine* re, const map<string, string>& config)
- : AbstractSource(re, config)
-{
- printf("K2LHvacPlugin: created - 2015-12-08 10:04\n");
-
- addPropertySupport(VehicleProperty::TargetTemperature, Zone::FrontLeft | Zone::FrontRight);
-
- Zone::ZoneList tempartureZones;
- tempartureZones.push_back(Zone::FrontLeft);
- tempartureZones.push_back(Zone::FrontRight);
-
- temperatureValues[Zone::FrontLeft] = 21;
- temperatureValues[Zone::FrontRight] = 21;
-
- PropertyInfo tempartureZonesInfo(0, tempartureZones);
- propertyInfoMap[VehicleProperty::TargetTemperature] = tempartureZonesInfo;
-
- addPropertySupport(VehicleProperty::FanSpeed, Zone::None);
-
- socketId = socket(PF_CAN, SOCK_RAW, CAN_RAW);
- printf("Socket: %i\n", socketId);
- if (socketId < 0)
- {
- printf("ERROR: socket could not be created\n");
- }
- else
- {
- struct ifreq ifr;
-
- strcpy(ifr.ifr_name, "vcan0" );
- if(ioctl(this->socketId, SIOCGIFINDEX, &ifr) < 0)
- {
- close(socketId);
- socketId = -1;
- printf("ERROR: ioctl failed\n");
- }
- else
- {
- txAddress.can_family = AF_CAN;
- txAddress.can_ifindex = ifr.ifr_ifindex;
-
- if (bind(this->socketId, (struct sockaddr *)&txAddress, sizeof(txAddress)) < 0)
- {
- close(socketId);
- socketId = -1;
- printf("ERROR: bind failed\n");
- }
-
- txCanFrame.can_id = 0x30;
- txCanFrame.can_dlc = 8;
- }
- }
-}
-
-K2LHvacPlugin::~K2LHvacPlugin()
-{
- if(socketId >= 0)
- {
- close(socketId);
- printf("Socket closed\n");
- }
-}
-
-void K2LHvacPlugin::getPropertyAsync(AsyncPropertyReply *reply)
-{
- printf("K2LHvacPlugin::getPropertyAsync\n");
-
- if(reply->property == VehicleProperty::FanSpeed)
- {
- printf("VehicleProperty::FanSpeed=OK\n");
- VehicleProperty::FanSpeedType temp(fanSpeedValue);
- reply->value = &temp;
- reply->success = true;
- reply->completed(reply);
- }
- else if(reply->property == VehicleProperty::TargetTemperature)
- {
- printf("VehicleProperty::TargetTemperature=");
- if(temperatureValues.find(reply->zoneFilter) == temperatureValues.end())
- {
- printf("AsyncPropertyReply::ZoneNotSupported\n");
- reply->success = false;
- reply->error = AsyncPropertyReply::ZoneNotSupported;
- reply->completed(reply);
- }
- else
- {
- printf("OK\n");
- VehicleProperty::TargetTemperatureType temp(temperatureValues[reply->zoneFilter]);
- reply->success = true;
- reply->value = &temp;
- reply->completed(reply);
- }
- }
-}
-
-AsyncPropertyReply *K2LHvacPlugin::setProperty(AsyncSetPropertyRequest request )
-{
- printf("K2LHvacPlugin::setProperty\n");
- AsyncPropertyReply *reply = new AsyncPropertyReply(request);
- reply->success = false;
- reply->error = AsyncPropertyReply::NoError;
-
- if(reply->property == VehicleProperty::FanSpeed)
- {
- printf("VehicleProperty::FanSpeed=OK\n");
- fanSpeedValue = (uint8_t)reply->value->value<uint16_t>();
- }
- else if(reply->property == VehicleProperty::TargetTemperature)
- {
- printf("VehicleProperty::TargetTemperature=");
- if(temperatureValues.find(reply->zoneFilter) == temperatureValues.end())
- {
- printf("AsyncPropertyReply::ZoneNotSupported\n");
- reply->error = AsyncPropertyReply::ZoneNotSupported;
- }
- else
- {
- printf("OK\n");
- temperatureValues[reply->zoneFilter] = (uint8_t)reply->value->value<int>();
- }
- }
- else
- {
- reply->error = AsyncPropertyReply::InvalidOperation;
- }
-
- if(reply->error == AsyncPropertyReply::NoError)
- {
- reply->success = true;
-
- routingEngine->updateProperty(reply->value,uuid());
-
- if (socketId >= 0)
- {
- printf("Send CAN message\n");
-
- txCanFrame.data[0] = GetTemperature(temperatureValues[Zone::FrontLeft]);
- txCanFrame.data[1] = GetTemperature(temperatureValues[Zone::FrontRight]);
- txCanFrame.data[2] = GetTemperature((temperatureValues[Zone::FrontLeft]
- + temperatureValues[Zone::FrontRight]) / 2);
- txCanFrame.data[3] = 0xF0;
- txCanFrame.data[4] = fanSpeedValue;
- txCanFrame.data[5] = 0x01;
- txCanFrame.data[6] = 0x00;
- txCanFrame.data[7] = 0x00;
-
- sendto(socketId, &txCanFrame, sizeof(struct can_frame), 0,
- (struct sockaddr*)&txAddress, sizeof(txAddress));
- }
- }
- else
- {
- reply->success = false;
- }
-
- reply->completed(reply);
- return reply;
-}
-
-uint8_t K2LHvacPlugin::GetTemperature(uint8_t value)
-{
- uint8_t result = ((0xF0 - 0x10) / 15) * value - 16;
- if (result < 0x10)
- result = 0x10;
- if (result > 0xF0)
- result = 0xF0;
-
- return result;
-}
-
-void K2LHvacPlugin::subscribeToPropertyChanges(VehicleProperty::Property property)
-{
- mRequests.push_back(property);
-}
-
-PropertyList K2LHvacPlugin::supported()
-{
- return mSupported;
-}
-
-void K2LHvacPlugin::unsubscribeToPropertyChanges(VehicleProperty::Property property)
-{
- if(contains(mRequests,property))
- removeOne(&mRequests, property);
-}
-
-int K2LHvacPlugin::supportedOperations()
-{
- return Get | Set | GetRanged;
-}
-
-void K2LHvacPlugin::addPropertySupport(VehicleProperty::Property property, Zone::Type zone)
-{
- mSupported.push_back(property);
-
- Zone::ZoneList zones;
- zones.push_back(zone);
-
- PropertyInfo info(0, zones);
- propertyInfoMap[property] = info;
-}
diff --git a/hvacplugin.h b/hvacplugin.h
deleted file mode 100644
index 1229477..0000000
--- a/hvacplugin.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * hvacplugin.h - AMB plugin to conttrol HVAC demo hardware for CES 2016
- *
- * Copyright (C) 2013-2015 K2L GmbH & Co. KG
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This file is licensed under GPLv2.
- */
-
-#ifndef _HvacPlugin_H_
-#define _HvacPlugin_H_
-
-/*!
- * \defgroup hvacplugin hvacplugin shared library
- * \brief HVAC plug-in from K2L.
- *
- * To load this plugin at AMB starup, insert following rows into AMB configuration file:
- * \code
- * {
- * "name" : "HvacPlugin",
- * "path":"/usr/lib/automotive-message-broker/hvacplugin.so"
- * }
- * \endcode
- *
- * \note HvacPlugin has to be the last source plug-in listed in AMB configuration file. Otherwise it can accidentally unregister or try to simulate some AMB properties supported from other sources.
- *
- * @{
- */
-
-#include <abstractsource.h>
-#include <string>
-
-using namespace std;
-
-class K2LHvacPlugin: public AbstractSource
-{
-public:
- K2LHvacPlugin(): AbstractSource(nullptr, map<string, string>()) {}
- K2LHvacPlugin(AbstractRoutingEngine* re, const map<string, string>& config);
- virtual ~K2LHvacPlugin();
-
-public:
- const string uuid() { return "4B029567-A667-4F6B-8F23-44D6E49DBF1E"; }
-
- void getPropertyAsync(AsyncPropertyReply *reply);
- void getRangePropertyAsync(AsyncRangePropertyReply *reply) {}
- AsyncPropertyReply * setProperty(AsyncSetPropertyRequest request);
- void subscribeToPropertyChanges(VehicleProperty::Property property);
- void unsubscribeToPropertyChanges(VehicleProperty::Property property);
- PropertyList supported();
- int supportedOperations();
- void supportedChanged(const PropertyList &) {}
-
- PropertyInfo getPropertyInfo(const VehicleProperty::Property & property)
- {
- if(propertyInfoMap.find(property) != propertyInfoMap.end())
- return propertyInfoMap[property];
-
- return PropertyInfo::invalid();
- }
-private:
- void addPropertySupport(VehicleProperty::Property property, Zone::Type zone);
- uint8_t GetTemperature(uint8_t value);
- void printFrame(const can_frame& frame) const;
-
- std::map<VehicleProperty::Property, PropertyInfo> propertyInfoMap;
- std::map<Zone::Type, uint8_t> temperatureValues;
- uint8_t fanSpeedValue;
-
- PropertyList mRequests;
- PropertyList mSupported;
-
- VehicleProperty::TargetTemperatureType targetTemperature;
- VehicleProperty::FanSpeedType fanSpeed;
-
- int socketId = -1;
- struct can_frame txCanFrame;
- struct sockaddr_can txAddress;
-};
-#endif // _CANSIMPLUGINIMPL_H_
-
-/** @} */
diff --git a/icon_hvac_hybrid_qml.png b/icon_hvac_hybrid_qml.png
new file mode 100644
index 0000000..27df1a0
--- /dev/null
+++ b/icon_hvac_hybrid_qml.png
Binary files differ
diff --git a/qml/ClimateButton.qml b/qml/ClimateButton.qml
new file mode 100644
index 0000000..24802c7
--- /dev/null
+++ b/qml/ClimateButton.qml
@@ -0,0 +1,30 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import "models"
+
+Rectangle {
+ id: root
+
+ width: imageItem.width
+ height: imageItem.height
+ color: "#aa000000"
+
+ property string target: ""
+ property string image: ""
+ property bool value: HVACModel[target]
+
+ Image {
+ id: imageItem
+ source: "images/" + image + "_" + (value ? "on" : "off") + ".png"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: HVACModel[target] = !HVACModel[target]
+ }
+}
diff --git a/qml/FanControl.qml b/qml/FanControl.qml
new file mode 100644
index 0000000..00e6f9e
--- /dev/null
+++ b/qml/FanControl.qml
@@ -0,0 +1,54 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import "models"
+
+Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ property real value: 0
+
+ Image {
+ y: 15
+ source: "images/fan_icon_off.png"
+ }
+
+ Image {
+ id: fanBar
+ x: 100
+ source: "images/fan_bar_off.png"
+ }
+
+ Image {
+ x: 100
+ width: value * fanBar.width
+ fillMode: Image.PreserveAspectCrop
+ horizontalAlignment: Image.AlignLeft
+ source: "images/fan_bar_on.png"
+
+ Image {
+ width: 20
+ height: width
+ anchors.verticalCenter: parent.bottom
+ anchors.verticalCenterOffset: -1
+ anchors.horizontalCenter: parent.right
+ source: "images/drag_knob.svg"
+ }
+ }
+
+ MouseArea {
+ x: 100
+ width: fanBar.width
+ height: parent.height
+
+ onPositionChanged: {
+ value = Math.min(Math.max(mouse.x / fanBar.width, 0), 1)
+ HVACModel.fanSpeed = value;
+ }
+ }
+}
diff --git a/qml/HVAC.qml b/qml/HVAC.qml
new file mode 100644
index 0000000..8ca981e
--- /dev/null
+++ b/qml/HVAC.qml
@@ -0,0 +1,89 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import system 1.0
+import utils 1.0
+import "models"
+
+App {
+ appId: "hvac"
+
+ HazardButton {
+ id: hazardButton
+ y: 100
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ TempSlider {
+ id: lTempSlider
+ x: 30
+ anchors.top: hazardButton.bottom
+ anchors.topMargin: 115
+ side: "left"
+ }
+
+ Row {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: hazardButton.bottom
+ anchors.topMargin: 90
+ spacing: 200
+
+ MiddleColumn { side: "left" }
+ MiddleColumn { side: "right" }
+ }
+
+ TempSlider {
+ id: rTempSlider
+ anchors.top: hazardButton.bottom
+ anchors.topMargin: 115
+ anchors.right: parent.right
+ anchors.rightMargin: 30
+ side: "right"
+ }
+
+ Image {
+ y: 1057
+ source: "images/separator.png"
+ }
+
+ FanControl {
+ x: 259
+ y: 1092
+ }
+
+ Item {
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: childrenRect.width
+ height: childrenRect.height
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 40
+
+ Row {
+ spacing: 20
+
+ Column {
+ spacing: 10
+
+ ClimateButton { image: "fan_dir_down"; target: "fanDown" }
+ ClimateButton { image: "fan_dir_right"; target: "fanRight" }
+ ClimateButton { image: "fan_dir_up"; target: "fanUp" }
+ }
+
+ ClimateButton { y: 156; image: "fan_control_ac"; target: "fanAC" }
+ ClimateButton { y: 156; image: "fan_control_auto"; target: "fanAuto" }
+ ClimateButton { y: 156; image: "fan_control_circ"; target: "fanRecirc" }
+
+ Column {
+ spacing: 10
+
+ ClimateButton { image: "defrost_max"; target: "defrostMax" }
+ ClimateButton { image: "defrost_rear"; target: "defrostRear" }
+ ClimateButton { image: "defrost_front"; target: "defrostFront" }
+ }
+ }
+ }
+}
diff --git a/qml/HazardButton.qml b/qml/HazardButton.qml
new file mode 100644
index 0000000..ffae370
--- /dev/null
+++ b/qml/HazardButton.qml
@@ -0,0 +1,37 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+
+Rectangle {
+ id: hazardButton
+ width: 624
+ height: 122
+ color: "#aa000000"
+ border.color: "#ff53b5ce"
+
+ property bool value: false
+ property bool flash: false
+
+ Image {
+ id: image
+ source: "./images/hazard_" + (value ? (flash ? "blink" : "on") : "off") + ".png"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: value = !value
+ }
+
+ Timer {
+ id: timer
+ interval: 500
+ repeat: true
+ running: value
+
+ onTriggered: flash = !flash
+ }
+}
diff --git a/qml/MiddleColumn.qml b/qml/MiddleColumn.qml
new file mode 100644
index 0000000..89f0ebe
--- /dev/null
+++ b/qml/MiddleColumn.qml
@@ -0,0 +1,33 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import components 1.0
+import utils 1.0
+
+Item {
+ id: root
+
+ width: 239
+ height: 800
+
+ property string side: "left"
+
+ Column {
+ spacing: 50
+
+ BoxHeading {
+ color: Style.orangeViv
+ boxWidth: 45
+ boxHeight: 19
+ fontSize: 27
+ text: (side === "left" ? "L" : "R" ) + " CLIMATE"
+ }
+
+ SeatHeatButton { side: root.side }
+ TemperatureWheel { side: root.side }
+ }
+}
diff --git a/qml/SeatHeatButton.qml b/qml/SeatHeatButton.qml
new file mode 100644
index 0000000..43645fb
--- /dev/null
+++ b/qml/SeatHeatButton.qml
@@ -0,0 +1,39 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import "models"
+
+Rectangle {
+ width: 239
+ height: 194
+ color: "#aa000000"
+ border.color: "#ff53b5ce"
+
+ property string side: "left"
+ property string propertyName: side + "SeatHeat"
+ property int seatHeat: HVACModel[propertyName]
+
+ Image {
+ source: "./images/" + side + "_heat_seat_off.png"
+ }
+
+ Image {
+ y: 150 - seatHeat*40
+ height: implicitHeight - y
+ fillMode: Image.Tile
+ verticalAlignment: Image.AlignBottom
+ source: "./images/" + side + "_heat_seat_on.png"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ var value = HVACModel[propertyName]
+ HVACModel[propertyName] = value > 0 ? value - 1 : 3
+ }
+ }
+}
diff --git a/qml/TempSlider.qml b/qml/TempSlider.qml
new file mode 100644
index 0000000..f5500e4
--- /dev/null
+++ b/qml/TempSlider.qml
@@ -0,0 +1,61 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import utils 1.0
+import "models"
+
+Item {
+ id: root
+ width: 64
+ height: 716
+
+ property real value: HVACModel[propertyName]
+ property string propertyName: side + "Temperature"
+ property string side: "left"
+
+ function setProperty(v) {
+ HVACModel[propertyName] = Math.min(Math.max(v, 0), 1)
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#4a53b5ce"
+ }
+
+ Rectangle {
+ width: parent.width
+ height: value * parent.height
+ color: Style.orangeViv
+ anchors.bottom: parent.bottom
+ }
+
+ Rectangle {
+ x: side === "left" ? parent.width + 30 : -30
+ width: 2
+ height: value * parent.height
+ anchors.bottom: parent.bottom
+ color: Style.orangeLt
+
+ Image {
+ width: 30
+ height: width
+ anchors.verticalCenter: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ source: "images/drag_knob.svg"
+ }
+ }
+
+ MouseArea {
+ x: side === "left" ? 0 : -45
+ width: parent.width + 45
+ height: parent.height
+
+ onPressed: setProperty(1 - mouse.y / height)
+ onPositionChanged: setProperty(1 - mouse.y / height)
+ }
+}
+
diff --git a/qml/TemperatureWheel.qml b/qml/TemperatureWheel.qml
new file mode 100644
index 0000000..7a59dde
--- /dev/null
+++ b/qml/TemperatureWheel.qml
@@ -0,0 +1,56 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.0
+import "models"
+
+Rectangle {
+ width: 237
+ height: 350
+ color: "#aa000000"
+
+ property string side: "left"
+ property string propertyName: side + "Temperature"
+ property real value: HVACModel[propertyName]
+
+ ListView {
+ anchors.fill: parent
+ clip: true
+ snapMode: ListView.SnapToItem
+ model: TemperatureModel
+ header: Item { height: 120 }
+ footer: Item { height: 120 }
+ currentIndex: Math.min(value * count, count - 1)
+ flickDeceleration: 5000
+ onContentYChanged: {
+ if (dragging || flicking) {
+ var item = Math.round((contentY + 120) / 110)
+ item = Math.max(Math.min(item, count - 1), 0)
+ if (item != currentIndex) {
+ var temperature = item / (count - 1)
+ HVACModel[propertyName] = temperature
+ }
+ }
+ }
+ highlightMoveDuration: 100
+ interactive: true
+
+ delegate: Text {
+ x: side === "right" ? 40 : 10
+ height: 110
+ verticalAlignment: Text.AlignVCenter
+ color: "white"
+ font.pixelSize: 70
+ text: model.text
+ }
+ }
+
+ Image {
+ mirror: side === "left"
+ source: "./images/right_number_cover.svg"
+ anchors.fill: parent
+ }
+}
diff --git a/qml/hvac-hybrid-qml-app.qml b/qml/hvac-hybrid-qml-app.qml
new file mode 100644
index 0000000..eded2d0
--- /dev/null
+++ b/qml/hvac-hybrid-qml-app.qml
@@ -0,0 +1,87 @@
+import QtQuick 2.0
+import QtQuick.Window 2.0
+import QtQuick.Controls 1.4
+import QtWebSockets 1.0
+
+Window {
+ // VARIABLES
+
+ property string port_str: Qt.application.arguments[1]
+ property string token_str: Qt.application.arguments[2]
+ property string address_str: "ws://localhost:"+port_str+"/api?token="+token_str
+ property string request_str: ""
+ property string status_str: "waiting..."
+ property var msgid_enu: { "call":2, "retok":3, "reterr":4, "event":5 }
+
+ // WINDOW PROPERTIES
+
+ visible: true
+ width: 340
+ height: 160
+
+ // WEBSOCKET WIDGET (MAIN LOGIC)
+
+ WebSocket {
+ id: websocket
+ url: address_str
+ onTextMessageReceived: {
+ // VERB RESPONSE VALIDATION
+ var message_json = JSON.parse (message)
+ var request_json = message_json[2].request
+ if (message_json[0] != msgid_enu.retok) {
+ console.log ("Return value is not ok !")
+ status_str = "Bad return value, binding probably not installed"
+ return
+ }
+ // VERB RESPONSE PARSING AND LOGIC
+ status_str = request_json.info
+ }
+ onStatusChanged: {
+ if (websocket.status == WebSocket.Error)
+ status_str = "WebSocket error: " + websocket.errorString
+ }
+ active: true
+ }
+
+ // OTHER WIDGETS
+
+ Rectangle {
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.margins: 20
+
+ // TITLE SECTION
+ Label {
+ text: "QML Websocket Sample Application"
+ font.pixelSize: 18
+ font.bold: true
+ anchors.centerIn: parent
+ y: 0
+ }
+ Text {
+ id: url_notifier
+ text: "<b>URL:</b> " + websocket.url
+ y: 20
+ }
+
+ // PING BUTTON
+ Button {
+ text: "Ping!"
+ onClicked: {
+ request_str = '[' + msgid_enu.call + ',"99999","xxxxxx/ping", null ]';
+ websocket.sendTextMessage (request_str)
+ }
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 60
+ }
+
+ // STATUS SECTION
+ Text {
+ id: status_notifier
+ text: "<b>Status</b>: " + status_str
+ y: 100
+ }
+ }
+
+}
diff --git a/qml/models/HVACModel.qml b/qml/models/HVACModel.qml
new file mode 100644
index 0000000..e737af4
--- /dev/null
+++ b/qml/models/HVACModel.qml
@@ -0,0 +1,47 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+pragma Singleton
+
+import QtQuick 2.0
+import vehicle 1.0
+
+Item {
+ property bool fanUp: false
+ property bool fanRight: false
+ property bool fanDown: false
+
+ property bool fanAC: false
+ property bool fanAuto: false
+ property bool fanRecirc: false
+
+ property real fanSpeed: 0
+
+ property bool defrostMax: false
+ property bool defrostFront: false
+ property bool defrostRear: false
+
+ property real leftTemperature: 0
+ property real rightTemperature: 0
+
+ property int leftSeatHeat: 0
+ property int rightSeatHeat: 0
+
+ onFanSpeedChanged: {
+ var currentFan = ClimateModel.getRangeValue(fanSpeed,ClimateModel.fanStepSize);
+ ClimateModel.fanSpeed = currentFan;
+ }
+
+ onLeftTemperatureChanged: {
+ var temperature = ClimateModel.getRangeValue(leftTemperature,ClimateModel.temperatureStepSize);
+ ClimateModel.leftTemp = temperature;
+ }
+
+ onRightTemperatureChanged: {
+ var temperature = ClimateModel.getRangeValue(rightTemperature,ClimateModel.temperatureStepSize);
+ ClimateModel.rightTemp = temperature;
+ }
+}
diff --git a/qml/models/TemperatureModel.qml b/qml/models/TemperatureModel.qml
new file mode 100644
index 0000000..85ca415
--- /dev/null
+++ b/qml/models/TemperatureModel.qml
@@ -0,0 +1,28 @@
+/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+pragma Singleton
+
+import QtQuick 2.0
+
+ListModel {
+ ListElement { text: "LO" }
+ ListElement { text: "16\u00b0" }
+ ListElement { text: "17\u00b0" }
+ ListElement { text: "18\u00b0" }
+ ListElement { text: "19\u00b0" }
+ ListElement { text: "20\u00b0" }
+ ListElement { text: "21\u00b0" }
+ ListElement { text: "22\u00b0" }
+ ListElement { text: "23\u00b0" }
+ ListElement { text: "24\u00b0" }
+ ListElement { text: "25\u00b0" }
+ ListElement { text: "26\u00b0" }
+ ListElement { text: "27\u00b0" }
+ ListElement { text: "28\u00b0" }
+ ListElement { text: "29\u00b0" }
+ ListElement { text: "HI" }
+}
diff --git a/qml/models/qmldir b/qml/models/qmldir
new file mode 100644
index 0000000..d757168
--- /dev/null
+++ b/qml/models/qmldir
@@ -0,0 +1,8 @@
+#/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved.
+# *
+# * This Source Code Form is subject to the terms of the Mozilla Public
+# * License, v. 2.0. If a copy of the MPL was not distributed with this
+# * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+singleton HVACModel 1.0 HVACModel.qml
+singleton TemperatureModel 1.0 TemperatureModel.qml