Adding some checks to ensure objects aren't undefined before using them
[profile/ivi/Modello_Common.git] / js / services / carIndicator.js
1 /**
2  * @module Services
3  */
4
5 /**
6  * Class provides AMB related functionality utilizing `tizen.vehicle` API for signals used in HTML applications. This component is usually initialized by {{#crossLink "Bootstrap"}}{{/crossLink}} class
7  * and can be later accessed using {{#crossLink "Bootstrap/carIndicator:property"}}{{/crossLink}} property. Signals recognized by this class needs to be registered in property
8  * {{#crossLink "CarIndicator/_mappingTable:property"}}{{/crossLink}}.
9  *
10  * To attach and detach to particular property register new callback object using {{#crossLink "Bootstrap/carIndicator:addListener"}}{{/crossLink}} method, e.g.:
11  *
12  *     var listenerId = bootstrap.carIndicator.addListener({
13  *        onSteeringWheelAngleChanged: function(newValue){
14  *           // Process new value
15  *        },
16  *        onWheelBrakeChanged : function(newValue){
17  *           // Process new value
18  *        }
19  *     });
20  *
21  *     // Unregister listener
22  *     bootstrap.carIndicator.removeListener(listenerId);
23  *
24  * Currently following signals are recognized:
25  *
26  * * SteeringWheelAngle
27  *   * SteeringWheelAngle
28  * * WheelBrake
29  *   * Engaged
30  * * TirePressure
31  *   * leftFront
32  *   * rightFront
33  *   * leftRear
34  *   * rightRear
35  * * DoorStatus
36  *   * ChildLockStatus
37  * * WindowStatus
38  *   * FrontDefrost
39  *   * RearDefrost
40  * * HVAC
41  *   * FanSpeed
42  *   * TargetTemperatureRight
43  *   * TargetTemperatureLeft
44  *   * SeatHeaterRight
45  *   * SeatHeaterLeft
46  *   * AirConditioning
47  *   * AirRecirculation
48  *   * AirflowDirection
49  * * LightStatus
50  *   * Hazard
51  *   * Head
52  *   * Parking
53  * * BatteryStatus
54  * * FullBatteryRange
55  * * ExteriorTemperature
56  *   * Exterior
57  * * InteriorTemperature
58  *   * Interior
59  * * WheelInformation
60  *   * FrontWheelRadius
61  * * AvgKW
62  *   * AvgKW
63  * * VehicleSpeed
64  * * Odometer
65  * * Transmission
66  *   * ShiftPosition
67  * * ExteriorBrightness
68  * * NightMode
69  * * DirectionIndicationINST
70  * * DirectionIndicationMS
71  * * ACCommand
72  * * RecircReq
73  * * FrontTSetRightCmd
74  * * FrontTSetLeftCmd
75  * * FrontBlwrSpeedCmd
76  * * HeatedSeatFRModeRequest
77  * * HeatedSeatFRRequest
78  * * HeatedSeatFLModeRequest
79  * * HeatedSeatFLRequest
80  * * FLHSDistrCmd
81  * * FRHSDistrCmd
82  *
83  * @class CarIndicator
84  * @constructor
85  */
86 var CarIndicator = function() {
87         "use strict";
88         console.info("Starting up service CarIndicator");
89 };
90
91 function parseInteger(value) {
92         "use strict";
93         return parseInt(value, 10);
94 }
95
96 function parseTirePressure(value) {
97         "use strict";
98         var floatValue = parseFloat(value).toFixed(2);
99         if (floatValue > 180 && floatValue < 220) {
100                 floatValue = "OK";
101         }
102         return floatValue;
103 }
104
105 /**
106  * Array of registered listeners
107  * @type Object
108  * @property _listeners
109  * @private
110  */
111 CarIndicator.prototype._listeners = {};
112
113 /**
114  * Array of registered listener IDs.
115  * @type Array
116  * @property _listenerIDs
117  * @private
118  */
119 CarIndicator.prototype._listenerIDs = [];
120
121 /**
122  * Signal mapping table.
123  * Each entry should form an object
124  * @property _mappingTable
125  * @private
126  * @type Object
127  */
128 CarIndicator.prototype._mappingTable = {
129         /*
130         ZONE_None   = 000000;
131         ZONE_Front  = 000001;
132         ZONE_Middle = 000010;
133         ZONE_Right  = 000100;
134         ZONE_Left   = 001000;
135         ZONE_Rear   = 010000;
136         ZONE_Center = 100000;
137         */
138         /* this is for steeringWheel game controler */
139         "SteeringWheelAngle" : {
140                 propertyName : "SteeringWheelAngle",
141                 callBackPropertyName : "SteeringWheelAngle",
142                 subscribeName : "deadReckoning",
143                 conversionFunction : function(value) {
144                         "use strict";
145                         value = parseInt(value, 10);
146                         var returnValue = 0;
147                         if (value <= 180 && value > 0) {
148                                 returnValue = (1 * (value / 6)) - 30;
149                         } else if (value <= 360 && value > 180) {
150                                 returnValue = ((value - 179) / 6);
151                         } else if (value === 0) {
152                                 returnValue = -30;
153                         }
154                         return returnValue;
155                 }
156         },
157         "WheelBrake" : {
158                 propertyName : "Engaged",
159                 callBackPropertyName : "WheelBrake",
160                 subscribeName : "WheelBrake"
161         },
162         /* end steeringWheel game controler*/
163         "TirePressureLeftFront" : {
164                 propertyName : "leftFront",
165                 callBackPropertyName : "tirePressureLeftFront",
166                 subscribeName : "Tire",
167                 conversionFunction : parseTirePressure,
168                 zone : ["Front","Left"]
169         },
170         "TirePressureRightFront" : {
171                 propertyName : "rightFront",
172                 callBackPropertyName : "tirePressureRightFront",
173                 subscribeName : "Tire",
174                 conversionFunction : parseTirePressure,
175                 zone : ["Front","Right"]
176         },
177         "TirePressureLeftRear" : {
178                 propertyName : "leftRear",
179                 callBackPropertyName : "tirePressureLeftRear",
180                 subscribeName : "Tire",
181                 conversionFunction : parseTirePressure,
182                 zone : ["Rear","Left"]
183         },
184         "TirePressureRightRear" : {
185                 propertyName : "rightRear",
186                 callBackPropertyName : "tirePressureRightRear",
187                 subscribeName : "Tire",
188                 conversionFunction : parseTirePressure,
189                 zone : ["Rear","Right"]
190         },
191         "ChildLock" : {
192                 propertyName : "ChildLockStatus",
193                 callBackPropertyName : "childLock",
194                 subscribeName : "childSafetyLock"
195         },
196         "FrontDefrost" : {
197                 propertyName : "Defrost",
198                 callBackPropertyName : "frontDefrost",
199                 subscribeName : "defrostWindshield"
200         },
201         "RearDefrost" : {
202                 propertyName : "Defrost",
203                 callBackPropertyName : "rearDefrost",
204                 subscribeName : "defrostRearWindow"
205         },
206         "FanSpeed" : {
207                 propertyName : "FanSpeed",
208                 callBackPropertyName : "fanSpeed",
209                 subscribeName : "fanSpeedLevel",
210                 conversionFunction : parseInteger
211         },
212         "TargetTemperatureRight" : {
213                 propertyName : "TargetTemperature",
214                 callBackPropertyName : "targetTemperatureRight",
215                 subscribeName : "targetTemperature",
216                 conversionFunction : parseInteger,
217                 zone : ["Right"]
218         },
219         "TargetTemperatureLeft" : {
220                 propertyName : "TargetTemperature",
221                 callBackPropertyName : "targetTemperatureLeft",
222                 subscribeName : "targetTemperature",
223                 conversionFunction : parseInteger,
224                 zone : ["Left"]
225         },
226         "Hazard" : {
227                 propertyName : "Hazard",
228                 callBackPropertyName : "hazard",
229                 subscribeName : "lightStatus"
230         },
231         "Head" : {
232                 propertyName : "Head",
233                 callBackPropertyName : "frontLights",
234                 subscribeName : "lightStatus"
235         },
236         "SeatHeaterRight" : {
237                 propertyName : "SeatHeater",
238                 callBackPropertyName : "seatHeaterRight",
239                 subscribeName : "climateControl",
240                 zone : ["Right"]
241         },
242         "SeatHeaterLeft" : {
243                 propertyName : "SeatHeater",
244                 callBackPropertyName : "seatHeaterLeft",
245                 subscribeName : "climateControl",
246                 zone : ["Left"]
247         },
248         "Parking" : {
249                 propertyName : "Parking",
250                 callBackPropertyName : "rearLights",
251                 subscribeName : "parkingLights",
252                 zone : "None"
253         },
254         "AirConditioning" : {
255                 propertyName : "AirConditioning",
256                 callBackPropertyName : "fan",
257                 subscribeName : "climateControl"
258         },
259         "AirRecirculation" : {
260                 propertyName : "AirRecirculation",
261                 callBackPropertyName : "airRecirculation",
262                 subscribeName : "climateControl"
263         },
264         "AirflowDirection" : {
265                 propertyName : "AirflowDirection",
266                 callBackPropertyName : "airflowDirection",
267                 subscribeName : "climateControl",
268                 conversionFunction : parseInteger
269         },
270         "BatteryStatus" : {
271                 propertyName : "BatteryStatus",
272                 callBackPropertyName : "batteryStatus",
273                 subscribeName : "batteryStatus",
274                 conversionFunction : parseInteger
275         },
276         "FullBatteryRange" : {
277                 propertyName : "FullBatteryRange",
278                 callBackPropertyName : "fullBatteryRange",
279                 conversionFunction : parseInteger
280         },
281         "Exterior" : {
282                 propertyName : "Exterior",
283                 callBackPropertyName : "outsideTemp",
284                 subscribeName : "temperature",
285                 conversionFunction : parseInteger
286         },
287         "Interior" : {
288                 propertyName : "Interior",
289                 callBackPropertyName : "insideTemp",
290                 subscribeName : "temperature",
291                 conversionFunction : parseInteger
292         },
293         "WheelAngle" : {
294                 propertyName : "FrontWheelRadius",
295                 callBackPropertyName : "wheelAngle",
296                 subscribeName : "wheelConfiguration",
297                 conversionFunction : parseInteger
298         },
299         "Weather" : {
300                 propertyName : "Weather",
301                 callBackPropertyName : "weather",
302                 conversionFunction : parseInteger
303         },
304         "AvgKW" : {
305                 propertyName : "AvgKW",
306                 callBackPropertyName : "avgKW",
307                 subscribeName : "AvgKW",
308                 conversionFunction : function(newValue) {
309                         "use strict";
310                         return parseFloat(newValue).toFixed(2);
311                 }
312         },
313         "VehicleSpeed" : {
314                 propertyName : "VehicleSpeed",
315                 callBackPropertyName : "speed",
316                 subscribeName : "vehicleSpeed",
317                 conversionFunction : parseInteger
318         },
319         "Odometer" : {
320                 propertyName : "Odometer",
321                 callBackPropertyName : "odoMeter",
322                 subscribeName : "odometer",
323                 conversionFunction : parseInteger
324         },
325         "TransmissionShiftPosition" : {
326                 propertyName : "ShiftPosition",
327                 callBackPropertyName : "gear",
328                 conversionFunction : function(value) {
329                         "use strict";
330                         switch (value) {
331                         case 0:
332                                 value = "N";
333                                 break;
334                         case 64:
335                                 value = "C";
336                                 break;
337                         case 96:
338                                 value = "D";
339                                 break;
340                         case 128:
341                                 value = "R";
342                                 break;
343                         case 255:
344                                 value = "P";
345                                 break;
346                         }
347                         return value;
348                 },
349                 subscribeName : "transmission"
350         },
351         "Randomize" : {
352                 propertyName : "Randomize",
353                 callBackPropertyName : "randomize",
354                 subscribeName : "Randomize"
355         },
356         "ExteriorBrightness" : {
357                 propertyName : "ExteriorBrightness",
358                 callBackPropertyName : "exteriorBrightness"
359         },
360         "NightMode" : {
361                 propertyName : "NightMode",
362                 callBackPropertyName : "nightMode"
363         },
364         "DirectionIndicationINST" : {
365                 propertyName : "DirectionIndicationINST",
366                 callBackPropertyName : "DirectionIndicationINST",
367                 subscribeName : "DirectionIndicationINST"
368         },
369         "DirectionIndicationMS" : {
370                 propertyName : "DirectionIndicationMS",
371                 callBackPropertyName : "DirectionIndicationMS",
372                 subscribeName : "DirectionIndicationMS"
373         },
374         "ACCommand" : {
375                 propertyName : "ACCommand",
376                 callBackPropertyName : "ACCommand",
377                 subscribeName : "ACCommand"
378         },
379         "RecircReq" : {
380                 propertyName : "RecircReq",
381                 callBackPropertyName : "RecircReq",
382                 subscribeName : "RecircReq"
383         },
384         "FrontTSetRightCmd" : {
385                 propertyName : "FrontTSetRightCmd",
386                 callBackPropertyName : "FrontTSetRightCmd",
387                 subscribeName : "FrontTSetRightCmd"
388         },
389         "FrontTSetLeftCmd" : {
390                 propertyName : "FrontTSetLeftCmd",
391                 callBackPropertyName : "FrontTSetLeftCmd",
392                 subscribeName : "FrontTSetLeftCmd"
393         },
394         "FrontBlwrSpeedCmd" : {
395                 propertyName : "FrontBlwrSpeedCmd",
396                 callBackPropertyName : "FrontBlwrSpeedCmd",
397                 subscribeName : "FrontBlwrSpeedCmd"
398         },
399         "HeatedSeatFRModeRequest" : {
400                 propertyName : "HeatedSeatFRModeRequest",
401                 callBackPropertyName : "HeatedSeatFRModeRequest",
402                 subscribeName : "HeatedSeatFRModeRequest"
403         },
404         "HeatedSeatFRRequest" : {
405                 propertyName : "HeatedSeatFRRequest",
406                 callBackPropertyName : "HeatedSeatFRRequest",
407                 subscribeName : "HeatedSeatFRRequest"
408         },
409         "HeatedSeatFLModeRequest" : {
410                 propertyName : "HeatedSeatFLModeRequest",
411                 callBackPropertyName : "HeatedSeatFLModeRequest",
412                 subscribeName : "HeatedSeatFLModeRequest"
413         },
414         "HeatedSeatFLRequest" : {
415                 propertyName : "HeatedSeatFLRequest",
416                 callBackPropertyName : "HeatedSeatFLRequest",
417                 subscribeName : "HeatedSeatFLRequest"
418         },
419         "FLHSDistrCmd" : {
420                 propertyName : "FLHSDistrCmd",
421                 callBackPropertyName : "FLHSDistrCmd",
422                 subscribeName : "FLHSDistrCmd"
423         },
424         "FRHSDistrCmd" : {
425                 propertyName : "FRHSDistrCmd",
426                 callBackPropertyName : "FRHSDistrCmd",
427                 subscribeName : "FRHSDistrCmd"
428         }
429 };
430
431 /**
432  * This method adds listener object for car events. Object should define function callbacks taking signal names from mapping table, e.g.:
433  * @example
434  *     {
435  *        onBatteryChange: function(newValue, oldValue) {}
436  *     }
437  * Methods are called back with new and last known values.
438  * @method addListener
439  * @param callback {Object} object with callback functions.
440  * @return {Integer} WatchID for later removal of listener.
441  */
442 CarIndicator.prototype.addListener = function(aCallbackObject) {
443         "use strict";
444         var id = Math.floor(Math.random() * 1000000);
445         var self = this;
446         this._listeners[id] = aCallbackObject;
447         this._listenerIDs.push(id);
448
449         var subscribeCallback = function(data) {
450                 self.onDataUpdate(data, self);
451         };
452         for ( var i in aCallbackObject) {
453                 if (aCallbackObject.hasOwnProperty(i)) {
454                         var prop = i.replace("on", "").replace("Changed", "");
455
456                         for ( var signal in this._mappingTable) {
457                                 if (this._mappingTable.hasOwnProperty(signal)) {
458                                         var mapping = this._mappingTable[signal];
459                                         var zone = mapping.zone;
460                                         var subscribeName = signal;
461
462                                         if (mapping.subscribeName !== undefined) {
463                                                 subscribeName = mapping.subscribeName;
464                                         }
465
466                                         if (mapping.callBackPropertyName.toLowerCase() === prop.toLowerCase() && !mapping.subscribeCount) {
467                                                 mapping.subscribeCount = typeof (mapping.subscribeCount) === 'undefined' ? 0 : mapping.subscribeCount++;
468
469                                                 if (typeof (tizen) !== 'undefined' && tizen.vehicle !== undefined && tizen.vehicle[subscribeName] !== undefined) {
470                                                         if (!(subscribeName.toString().trim().toLowerCase() === "nightmode" && id === this._listenerIDs[0])) {
471                                                                 if (tizen.vehicle[subscribeName]){
472                                                                         var setUpData = tizen.vehicle[subscribeName].get(zone);
473                                                                         if (setUpData !== undefined)
474                                                                                 self.onDataUpdate(setUpData, self, id);
475                                                                 }
476                                                         }
477                                                         tizen.vehicle[subscribeName].subscribe(subscribeCallback, zone);
478                                                 } else {
479                                                         console.warn("Tizen API is not available, cannot subscribe to signal", signal);
480                                                 }
481                                         }
482                                 }
483                         }
484                 }
485         }
486
487         return id;
488 };
489 /**
490  * This method is call as callback if data oon tizen.vehicle was change onDataUpdate
491  * @method onDataUpdate
492  * @param data {object} object whit new data.
493  * @param self {object} this carIndicator Object.
494  * @param lisenersID {int} id of listener.
495  */
496 CarIndicator.prototype.onDataUpdate = function(data, self, lisenersID) {
497         "use strict";
498         if (data !== undefined) {
499                 if (data.zone !== undefined);
500                         var zone = data.zone.toString(2);
501                 else
502                         var zone = "0";
503                 var mapping;
504
505                 for ( var property in data) {
506                         if (data.hasOwnProperty(property)) {
507                                 mapping = undefined;
508                                 if (property !== "time" && property !== "zone" && property.search("Sequence") === -1) {
509                                         for ( var element in self._mappingTable) {
510                                                 if (self._mappingTable.hasOwnProperty(element)) {
511                                                         if (self._mappingTable[element].propertyName.toLowerCase() === property.toLowerCase()) {
512                                                                 /* jshint bitwise: false */
513                                                                 if (!(zone ^ self._mappingTable[element].zone)) {
514                                                                         /* jshint bitwise: true */
515                                                                         mapping = self._mappingTable[element];
516                                                                         break;
517                                                                 }
518                                                         }
519                                                 }
520                                         }
521
522                                         if (typeof (mapping) !== 'undefined') {
523                                                 var value = data[property];
524                                                 value = mapping.conversionFunction ? mapping.conversionFunction(value) : value;
525
526                                                 var oldValue = self.status[mapping.callBackPropertyName];
527                                                 if (oldValue !== value || property.toUpperCase() === "nightMode".toUpperCase()) {
528                                                         console.info("AMB property '" + property + "' has changed to new value:" + value);
529                                                         self.status[mapping.callBackPropertyName] = value;
530
531                                                         var callbackName = "on" + mapping.callBackPropertyName[0].toUpperCase() + mapping.callBackPropertyName.substring(1) + "Changed";
532                                                         var listener;
533
534                                                         if (lisenersID !== undefined) {
535                                                                 listener = self._listeners[lisenersID];
536
537                                                                 if (typeof (listener[callbackName]) === 'function') {
538                                                                         try {
539                                                                                 listener[callbackName](value, oldValue);
540                                                                         } catch (ex) {
541                                                                                 console.error("Error occured during executing listener", ex);
542                                                                         }
543                                                                 }
544                                                         } else {
545                                                                 for ( var i in self._listeners) {
546                                                                         if (self._listeners.hasOwnProperty(i)) {
547                                                                                 listener = self._listeners[i];
548
549                                                                                 if (typeof (listener[callbackName]) === 'function') {
550                                                                                         try {
551                                                                                                 listener[callbackName](value, oldValue);
552                                                                                         } catch (ex) {
553                                                                                                 console.error("Error occured during executing listener", ex);
554                                                                                         }
555                                                                                 }
556                                                                         }
557                                                                 }
558                                                         }
559                                                 }
560
561                                         } else {
562                                                 console.warn("Mapping for property '" + property + "' is not defined");
563                                         }
564                                 }
565                         }
566                 }
567         }
568 };
569
570 /**
571  * This method removes previosly added listener object. Use WatchID returned from addListener method.
572  * @method removeListener
573  * @param aId {Integer} WatchID.
574  */
575 CarIndicator.prototype.removeListener = function(aId) {
576         "use strict";
577         var listener = this._listeners[aId];
578
579         for ( var i in listener) {
580                 if (listener.hasOwnProperty(i)) {
581                         var prop = i.replace("on", "").replace("Changed", "");
582
583                         for ( var signal in this._mappingTable) {
584                                 if (this._mappingTable.hasOwnProperty(signal)) {
585                                         var mapping = this._mappingTable[signal];
586
587                                         if (mapping.subscribeCount === 0) { // Last signal, unscubscribe
588                                                 tizen.vehicle.unsubscribe(signal);
589                                                 mapping.subscribeCount = undefined;
590                                         } else if (typeof (mapping.subscribeCount) !== 'undefined') {
591                                                 mapping.subscribeCount--;
592                                         }
593                                 }
594                         }
595                 }
596         }
597
598         this._listeners[aId] = undefined;
599 };
600
601 /**
602  * status object
603  * @property status
604  * @type Object
605  * @private
606  */
607 CarIndicator.prototype.status = {
608         fanSpeed : 0,
609         targetTemperatureRight : 0,
610         targetTemperatureLeft : 0,
611         hazard : false,
612         frontDefrost : false,
613         rearDefrost : false,
614         frontLeftwhell : "",
615         frontRightwhell : "",
616         rearLeftwhell : "",
617         rearRightwhell : "",
618         childLock : false,
619         frontLights : false,
620         rearLights : false,
621         fan : false,
622         seatHeaterRight : 0,
623         seatHeaterLeft : 0,
624         airRecirculation : false,
625         airflowDirection : 0,
626         batteryStatus : 58,
627         fullBatteryRange : 350,
628         outsideTemp : 74.2,
629         insideTemp : 68.2,
630         wheelAngle : 0,
631         weather : 1,
632         avgKW : 0.28,
633         speed : 65,
634         odoMeter : 75126,
635         gear : "D",
636         nightMode : false,
637         randomize : false,
638         exteriorBrightness : 1000
639 };
640
641 /**
642  * This method return status object in callback
643  * @method getStatus
644  * @param callback {function} callback function.
645  */
646 CarIndicator.prototype.getStatus = function(callback) {
647         "use strict";
648         callback(this.status);
649 };
650
651 /**
652  * this method set status for property in tizen.vehicle and status object
653  * @method setStatus
654  * @param indicator {string} indicator name.
655  * @param status {??} ??.
656  * @param text_status {string} new status .
657  * @param callback {function} callback function.
658  */
659 CarIndicator.prototype.setStatus = function(indicator, newValue, callback, zone) {
660         "use strict";
661         var mappingElement, mappingProperty;
662         for ( var element in this._mappingTable) {
663                 if (this._mappingTable.hasOwnProperty(element)) {
664                         mappingProperty = undefined;
665                         if (this._mappingTable[element].callBackPropertyName.toLowerCase() === indicator.toLowerCase()) {
666                                 mappingElement = this._mappingTable[element];
667                                 mappingProperty = this._mappingTable[element].propertyName;
668                                 break;
669                         }
670                 }
671         }
672
673         // this.status[indicator] = status === "true";
674         if (mappingProperty !== undefined) {
675                 var objectName = mappingElement.subscribeName;
676                 var propertyZone = parseInt(mappingElement.zone, 2);
677                 var propertyValue = {};
678                 propertyValue[mappingProperty] = newValue;
679                 propertyValue.zone = propertyZone;
680
681                 tizen.vehicle.set(objectName, propertyValue, function(msg) {
682                         console.error("Set error: " + msg);
683                 });
684         }
685         if (!!callback) {
686                 callback();
687         }
688 };