homebridge-gree-ac 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,286 +0,0 @@
1
- 'use strict';
2
-
3
- const dgram = require('dgram');
4
- const socket = dgram.createSocket('udp4');
5
- const encryptionService = require('./encryptionService')();
6
- const cmd = require('./commandEnums');
7
-
8
- /**
9
- * Class representing a single connected device
10
- */
11
- class Device {
12
-
13
- /**
14
- * Create device model and establish UDP connection with remote host
15
- * @param {object} [options] Options
16
- * @param {string} [options.address] HVAC IP address
17
- * @callback [options.onStatus] Callback function run on each status update
18
- * @callback [options.onUpdate] Callback function run after command
19
- * @callback [options.onConnected] Callback function run once connection is established
20
- */
21
- constructor(options) {
22
-
23
- // Set defaults
24
- this.options = {
25
- host: options.host || '192.168.1.255',
26
- onStatus: options.onStatus || function() {},
27
- onUpdate: options.onUpdate || function() {},
28
- onConnected: options.onConnected || function() {},
29
- onError: options.onError || function() {},
30
- onDisconnected: options.onDisconnected || function() {},
31
- updateInterval: options.updateInterval || 10000
32
- }
33
-
34
- /**
35
- * Device object
36
- * @typedef {object} Device
37
- * @property {string} id - ID
38
- * @property {string} name - Name
39
- * @property {string} address - IP address
40
- * @property {number} port - Port number
41
- * @property {boolean} bound - If is already bound
42
- * @property {object} props - Properties
43
- */
44
- this.device = {};
45
-
46
- // Initialize connection and bind with device
47
- this._connectToDevice(this.options.host);
48
-
49
- // Handle incoming messages
50
- socket.on('message', (msg, rinfo) => this._handleResponse(msg, rinfo));
51
- }
52
-
53
- /**
54
- * Initialize connection
55
- * @param {string} address - IP/host address
56
- */
57
- _connectToDevice(address) {
58
- try {
59
- socket.bind(() => {
60
- const message = new Buffer(JSON.stringify({t: 'scan'}));
61
-
62
- socket.setBroadcast(false);
63
- socket.send(message, 0, message.length, 7000, address);
64
- });
65
- } catch (err) {
66
- const timeout = 5
67
- this.options.onDisconnected(this.device);
68
- setTimeout(() => {
69
- this._connectToDevice(address);
70
- }, timeout * 1000);
71
- }
72
- }
73
-
74
- /**
75
- * Register new device locally
76
- * @param {string} id - CID received in handshake message
77
- * @param {string} name - Device name received in handshake message
78
- * @param {string} address - IP/host address
79
- * @param {number} port - Port number
80
- */
81
- _setDevice (id, name, address, port) {
82
- this.device.id = id;
83
- this.device.name = name;
84
- this.device.address = address;
85
- this.device.port = port;
86
- this.device.bound = false;
87
- this.device.props = {};
88
-
89
- console.log('[GreeAC] New device registered: %s - %s', this.device.name, this.device.address);
90
- }
91
-
92
- /**
93
- * Send binding request to device
94
- * @param {Device} device Device object
95
- */
96
- _sendBindRequest(device) {
97
- const message = {
98
- mac: this.device.id,
99
- t: 'bind',
100
- uid: 0
101
- };
102
- const encryptedBoundMessage = encryptionService.encrypt(message);
103
- const request = {
104
- cid: 'app',
105
- i: 1,
106
- t: 'pack',
107
- uid: 0,
108
- pack: encryptedBoundMessage
109
- };
110
- const toSend = new Buffer(JSON.stringify(request));
111
- socket.send(toSend, 0, toSend.length, device.port, device.address);
112
- }
113
-
114
- /**
115
- * Confirm device is bound and update device status on list
116
- * @param {String} id - Device ID
117
- * @param {String} key - Encryption key
118
- */
119
- _confirmBinding(id, key) {
120
- this.device.bound = true;
121
- this.device.key = key;
122
- }
123
-
124
- /**
125
- * Confirm device is bound and update device status on list
126
- * @param {Device} device - Device
127
- */
128
- _requestDeviceStatus (device) {
129
- const message = {
130
- cols: Object.keys(cmd).map(key => cmd[key].code),
131
- mac: device.id,
132
- t: 'status'
133
- };
134
- this._sendRequest(message, device.address, device.port);
135
- }
136
-
137
- /**
138
- * Handle UDP response from device
139
- * @param {string} msg Serialized JSON string with message
140
- * @param {object} rinfo Additional request information
141
- * @param {string} rinfo.address IP/host address
142
- * @param {number} rinfo.port Port number
143
- */
144
- _handleResponse(msg, rinfo) {
145
-
146
- const message = JSON.parse(msg + '');
147
-
148
- // Extract encrypted package from message using device key (if available)
149
- const pack = encryptionService.decrypt(message, (this.device || {}).key);
150
-
151
- // If package type is response to handshake
152
- if (pack.t === 'dev') {
153
- this._setDevice(message.cid, pack.name, rinfo.address, rinfo.port);
154
- this._sendBindRequest(this.device);
155
- return;
156
- }
157
-
158
- // If package type is binding confirmation
159
- if (pack.t === 'bindok' && this.device.id) {
160
- this._confirmBinding(message.cid, pack.key);
161
-
162
- // Start requesting device status on set interval
163
- setInterval(this._requestDeviceStatus.bind(this, this.device), this.options.updateInterval);
164
- this.options.onConnected(this.device)
165
- return;
166
- }
167
-
168
- // If package type is device status
169
- if (pack.t === 'dat' && this.device.bound) {
170
- pack.cols.forEach((col, i) => {
171
- this.device.props[col] = pack.dat[i];
172
- });
173
- this.options.onStatus(this.device);
174
- return;
175
- }
176
-
177
- // If package type is response, update device properties
178
- if (pack.t === 'res' && this.device.bound) {
179
- pack.opt.forEach((opt, i) => {
180
- this.device.props[opt] = pack.val[i];
181
- });
182
- this.options.onUpdate(this.device);
183
- return;
184
- }
185
-
186
- this.options.onError(this.device);
187
- }
188
-
189
- /**
190
- * Send commands to a bound device
191
- * @param {string[]} commands List of commands
192
- * @param {number[]} values List of values
193
- */
194
- _sendCommand (commands = [], values = []) {
195
- const message = {
196
- opt: commands,
197
- p: values,
198
- t: 'cmd'
199
- };
200
- this._sendRequest(message);
201
- };
202
-
203
- /**
204
- * Send request to a bound device
205
- * @param {object} message
206
- * @param {string[]} message.opt
207
- * @param {number[]} message.p
208
- * @param {string} message.t
209
- * @param {string} [address] IP/host address
210
- * @param {number} [port] Port number
211
- */
212
- _sendRequest (message, address = this.device.address, port = this.device.port) {
213
- const encryptedMessage = encryptionService.encrypt(message, this.device.key);
214
- const request = {
215
- cid: 'app',
216
- i: 0,
217
- t: 'pack',
218
- uid: 0,
219
- pack: encryptedMessage
220
- };
221
- const serializedRequest = new Buffer(JSON.stringify(request));
222
- socket.send(serializedRequest, 0, serializedRequest.length, port, address);
223
- };
224
-
225
- /**
226
- * Turn on/off
227
- * @param {boolean} value State
228
- */
229
- setPower (value) {
230
- this._sendCommand(
231
- [cmd.power.code],
232
- [value ? 1 : 0]
233
- );
234
- };
235
-
236
- /**
237
- * Set temperature
238
- * @param {number} value Temperature
239
- * @param {number} [unit=0] Units (defaults to Celsius)
240
- */
241
- setTemp (value, unit = cmd.temperatureUnit.value.celsius) {
242
- this._sendCommand(
243
- [cmd.temperatureUnit.code, cmd.temperature.code],
244
- [unit, value]
245
- );
246
- };
247
-
248
- /**
249
- * Set mode
250
- * @param {number} value Mode value (0-4)
251
- */
252
- setMode (value) {
253
- this._sendCommand(
254
- [cmd.mode.code],
255
- [value]
256
- );
257
- };
258
-
259
- /**
260
- * Set fan speed
261
- * @param {number} value Fan speed value (0-5)
262
- */
263
- setFanSpeed (value) {
264
- this._sendCommand(
265
- [cmd.fanSpeed.code],
266
- [value]
267
- );
268
- };
269
-
270
- /**
271
- * Set vertical swing
272
- * @param {number} value Vertical swing value (0-11)
273
- */
274
- setSwingVert (value) {
275
- this._sendCommand(
276
- [cmd.swingVert.code],
277
- [value]
278
- );
279
- };
280
-
281
- };
282
-
283
- module.exports.connect = function(options) {
284
- return new Device(options);
285
- };
286
-
@@ -1,39 +0,0 @@
1
- 'use strict';
2
-
3
- const crypto = require('crypto');
4
-
5
- /**
6
- * Module containing encryption services
7
- * @param {String} key AES general key
8
- */
9
- module.exports = function(defaultKey = 'a3K8Bx%2r8Y7#xDh') {
10
- const EncryptionService = {
11
-
12
- /**
13
- * Decrypt UDP message
14
- * @param {object} input Response object
15
- * @param {string} input.pack Encrypted JSON string
16
- * @param {string} [key] AES key
17
- */
18
- decrypt: (input, key = defaultKey) => {
19
- const decipher = crypto.createDecipheriv('aes-128-ecb', key, '');
20
- const str = decipher.update(input.pack, 'base64', 'utf8');
21
- const response = JSON.parse(str + decipher.final('utf8'));
22
- return response;
23
- },
24
-
25
- /**
26
- * Encrypt UDP message
27
- * @param {object} output Request object
28
- * @param {string} [key] AES key
29
- */
30
- encrypt: (output, key = defaultKey) => {
31
- const cipher = crypto.createCipheriv('aes-128-ecb', key, '');
32
- const str = cipher.update(JSON.stringify(output), 'utf8', 'base64');
33
- const request = str + cipher.final('base64');
34
- return request;
35
- }
36
- };
37
-
38
- return EncryptionService;
39
- };
@@ -1,20 +0,0 @@
1
- {
2
- "bridge": {
3
- "name": "Homebridge",
4
- "username": "CC:22:3D:E3:CE:30",
5
- "port": 51826,
6
- "pin": "123-45-568"
7
- },
8
- "accessories": [
9
- {
10
- "accessory": "GreeAC",
11
- "host": "192.168.1.X",
12
- "name": "Office AC"
13
- },
14
- {
15
- "accessory": "GreeAC",
16
- "host": "192.168.1.Y",
17
- "name": "Bedroom AC"
18
- }
19
- ]
20
- }
package/index.js DELETED
@@ -1,339 +0,0 @@
1
- 'use strict';
2
- const commands = require('./app/commandEnums');
3
- var Accessory, Service, Characteristic;
4
- var mqtt = require('mqtt');
5
-
6
- module.exports = function(homebridge) {
7
- Accessory = homebridge.platformAccessory;
8
- Service = homebridge.hap.Service;
9
- Characteristic = homebridge.hap.Characteristic;
10
-
11
- homebridge.registerAccessory('homebridge-gree-ac', 'GreeAC', GreeAC);
12
- }
13
-
14
- function GreeAC(log, config) {
15
- this.log = log;
16
- this.name = config.name || 'Gree AC';
17
- this.host = config.host || '192.168.1.255';
18
- this.debug = ((config.debug || "false" ) === "true");
19
- this.updateInterval = config.updateInterval || 10000;
20
- this.currentTempTopic = config['currentTempTopic'] || "hvac/temperature";
21
- this.mqttUrl = config['mqttUrl'] || "mqtt://127.0.0.1";
22
- this.client_Id = 'mqttjs_' + Math.random().toString(16).substr(2, 8);
23
-
24
- this.mqttOptions = {
25
- keepalive: 10,
26
- clientId: this.client_Id,
27
- protocolId: 'MQTT',
28
- protocolVersion: 4,
29
- clean: true,
30
- reconnectPeriod: 1000,
31
- connectTimeout: 30 * 1000,
32
- serialnumber: config["serial"] || this.client_Id,
33
- max_temperature: config["maxTemperature"] || 100,
34
- min_temperature: config["minTemperature"] || -50,
35
- username: config["username"],
36
- password: config["password"],
37
- will: {
38
- topic: 'WillMsg',
39
- payload: 'Connection Closed abnormally..!',
40
- qos: 0,
41
- retain: false
42
- },
43
- rejectUnauthorized: false
44
- };
45
-
46
- this.client = mqtt.connect(this.mqttUrl, this.mqttOptions);
47
-
48
- this.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.OFF;
49
- this.TargetTemperature = 22;
50
- this.CurrentTemperature = 0;
51
- this.services = [];
52
-
53
- this.GreeACService = new Service.Thermostat(this.name);
54
- this.temperatureDisplayUnits = Characteristic.TemperatureDisplayUnits.CELSIUS;
55
-
56
- this.GreeACService
57
- .getCharacteristic(Characteristic.TemperatureDisplayUnits)
58
- .on('get', this.getTemperatureDisplayUnits.bind(this))
59
- .on('set', this.setTemperatureDisplayUnits.bind(this));
60
-
61
- this.GreeACService
62
- .getCharacteristic(Characteristic.TargetHeatingCoolingState)
63
- .on('set', this.setTargetHeatingCoolingState.bind(this))
64
- .on('get', this.getTargetHeatingCoolingState.bind(this));
65
-
66
- this.GreeACService
67
- .getCharacteristic(Characteristic.CurrentHeatingCoolingState)
68
- .on('get', this.getTargetHeatingCoolingState.bind(this));
69
-
70
- this.GreeACService
71
- .getCharacteristic(Characteristic.TargetTemperature)
72
- .setProps({
73
- maxValue: 30,
74
- minValue: 16,
75
- minStep: 1
76
- })
77
- .on('set', this.setTargetTemperature.bind(this))
78
- .on('get', this.getTargetTemperature.bind(this));
79
-
80
- this.GreeACService
81
- .getCharacteristic(Characteristic.CurrentTemperature)
82
- .setProps({
83
- maxValue: 30,
84
- minValue: 16,
85
- minStep: 1
86
- })
87
- .on('get', this.getCurrentTemperature.bind(this));
88
-
89
- // this.GreeACService
90
- // .getCharacteristic(Characteristic.SwingMode)
91
- // .setProps({
92
- // minValue: 0,
93
- // maxValue: 11,
94
- // minStep: 1
95
- // })
96
- // .on('get', this.getSwing.bind(this))
97
- // .on('set', this.setSwing.bind(this));
98
-
99
- // this.GreeACService
100
- // .getCharacteristic(Characteristic.RotationSpeed)
101
- // .setProps({
102
- // minValue: 0,
103
- // maxValue: 5,
104
- // minStep: 1
105
- // })
106
- // .on('get', this.getRotationSpeed.bind(this))
107
- // .on('set', this.setRotationSpeed.bind(this));
108
-
109
- this.services.push(this.GreeACService);
110
-
111
- this.serviceInfo = new Service.AccessoryInformation();
112
-
113
- this.serviceInfo
114
- .setCharacteristic(Characteristic.Manufacturer, 'Gree')
115
- .setCharacteristic(Characteristic.Model, 'Bora A5')
116
- .setCharacteristic(Characteristic.SerialNumber, 'GREEBORAA5');
117
-
118
- this.services.push(this.serviceInfo);
119
-
120
- this.discover();
121
- }
122
-
123
- GreeAC.prototype = {
124
-
125
- discover: function(){
126
- var accessory = this;
127
- var log = this.log;
128
- var host = this.host;
129
- var debug = this.debug;
130
-
131
- const deviceOptions = {
132
- host: host,
133
- updateInterval: this.updateInterval,
134
- onStatus: (deviceModel) => {
135
- var device_power_status = deviceModel.props[commands.power.code];
136
- var device_mode_status = deviceModel.props[commands.mode.code];
137
- var device_temperature = deviceModel.props[commands.temperature.code];
138
- var device_swingvert = deviceModel.props[commands.swingVert.code] === 0 ? 0:1;
139
- accessory.TargetTemperature = parseFloat(device_temperature);
140
-
141
- if (device_power_status === commands.power.value.off) {
142
- accessory.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.OFF;
143
- } else if (device_mode_status === commands.mode.value.auto) {
144
- accessory.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.AUTO;
145
- } else if (device_mode_status === commands.mode.value.cool) {
146
- accessory.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.COOL;
147
- } else if (device_mode_status === commands.mode.value.heat) {
148
- accessory.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.HEAT;
149
- } else {
150
- accessory.TargetHeatingCoolingState = Characteristic.TargetHeatingCoolingState.AUTO;
151
- }
152
-
153
- this.GreeACService
154
- .getCharacteristic(Characteristic.TargetHeatingCoolingState)
155
- .updateValue(accessory.TargetHeatingCoolingState);
156
-
157
- this.GreeACService
158
- .getCharacteristic(Characteristic.TargetTemperature)
159
- .updateValue(accessory.TargetTemperature);
160
-
161
- if (accessory.debug === true) {
162
- this.log.info('Get mode: %s', device_mode_status.toString());
163
- this.log.info('Get temperature %s', device_temperature.toString());
164
- this.log.info('Get fanspeed %s', deviceModel.props[commands.fanSpeed.code].toString());
165
- this.log.info('Get swingvert %s', device_swingvert.toString());
166
- this.log.info('Get powerstatus %s', device_power_status.toString());
167
- }
168
- },
169
- onUpdate: (deviceModel) => {
170
- this.log.info('Status updated on %s', deviceModel.name)
171
- },
172
- onConnected: (deviceModel) => {
173
- var accesorry = this;
174
- accesorry.log.info('Connecting on %s with ip address %s[%d] debug=%s: %s', deviceModel.name, deviceModel.address, deviceModel.port, accesorry.debug, deviceModel.bound ? "SUCCESS": "FAIL");
175
- accesorry.client.subscribe(this.currentTempTopic);
176
- accesorry.client.on('message', function (topic, message) {
177
- try {
178
- data = JSON.parse(message);
179
- } catch (e) {
180
- return null;
181
- }
182
-
183
- if (data === null) {return null}
184
- var that = this;
185
- that.temperature = parseFloat(data);
186
- if (!isNaN(that.temperature)) {
187
- if (accesorry.debug === true) {
188
- accesorry.log.info('Current temperature is ' + that.temperature);
189
- }
190
- accesorry.CurrentTemperature = parseFloat(that.temperature);
191
- accesorry.GreeACService
192
- .getCharacteristic(Characteristic.CurrentTemperature)
193
- .updateValue(accesorry.CurrentTemperature);
194
- }
195
- });
196
- },
197
- onError: (deviceModel) => {
198
- if (this.debug === true) {
199
- this.log.info('Connecting on %s with ip address %s[%d]: %s', deviceModel.name, deviceModel.address, deviceModel.port, deviceModel.bound ? "SUCCESS": "FAIL");
200
- }
201
- },
202
- onDisconnected: (deviceModel) => {
203
- if (this.debug === true ) {
204
- this.log.info('Connecting on %s with ip address %s[%d]: %s', deviceModel.name, deviceModel.address, deviceModel.port, deviceModel.bound ? "SUCCESS": "FAIL");
205
- }
206
- }
207
- };
208
- accessory.hvac = require('./app/deviceFactory').connect(deviceOptions);
209
- },
210
-
211
- setTemperatureDisplayUnits: function(value, callback) {
212
- if (this.debug === true) {
213
- this.log.info("setTemperatureDisplayUnits from %s to %s", this.temperatureDisplayUnits, value);
214
- }
215
- this.temperatureDisplayUnits = value;
216
- callback(null);
217
- },
218
-
219
- getTemperatureDisplayUnits: function(callback) {
220
- try {
221
- this.temperatureDisplayUnits = this.hvac.device.props[commands.temperatureUnit.code];
222
- if (this.debug === true) {
223
- this.log.info("getTemperatureDisplayUnits: ", this.temperatureDisplayUnits);
224
- }
225
- callback(null, this.temperatureDisplayUnits);
226
- } catch (err) {callback();}
227
- },
228
-
229
- getTargetHeatingCoolingState: function(callback) {
230
- try
231
- {
232
- var state = Characteristic.TargetHeatingCoolingState.AUTO;
233
- var accessory = this;
234
- if (this.hvac.device.props[commands.power.code] === commands.power.value.off) {
235
- state = Characteristic.TargetHeatingCoolingState.OFF;
236
- } else if (this.hvac.device.props[commands.mode.code] === commands.mode.value.auto) {
237
- state = Characteristic.TargetHeatingCoolingState.AUTO;
238
- } else if (this.hvac.device.props[commands.mode.code] === commands.mode.value.cool) {
239
- state = Characteristic.TargetHeatingCoolingState.COOL;
240
- } else if (this.hvac.device.props[commands.mode.code] === commands.mode.value.heat) {
241
- state = Characteristic.TargetHeatingCoolingState.HEAT;
242
- }
243
-
244
- accessory.TargetHeatingCoolingState = state;
245
- callback(null, this.TargetHeatingCoolingState);
246
- } catch (err) {
247
- if (this.debug === true) {
248
- this.log.info("getTemperatureDisplayUnits: error communicating with device");
249
- }
250
- callback();
251
- }
252
- },
253
-
254
- setTargetHeatingCoolingState: function(TargetHeatingCoolingState, callback, context) {
255
- try {
256
- if(context !== 'fromSetValue') {
257
- this.TargetHeatingCoolingState = TargetHeatingCoolingState;
258
- if (this.TargetHeatingCoolingState === Characteristic.TargetHeatingCoolingState.OFF) {
259
- this.hvac.setPower(commands.power.value.off);
260
- if (this.debug === true){
261
- this.log.info('setTargetHeatingCoolingState: Set mode power off');
262
- }
263
- } else {
264
- if (this.hvac.device.props[commands.power.code] === commands.power.value.off) {
265
- this.hvac.setPower(commands.power.value.on);
266
- if (this.debug === true){
267
- this.log.info('setTargetHeatingCoolingState: Set mode power on');
268
- }
269
- }
270
-
271
- if (this.TargetHeatingCoolingState === Characteristic.TargetHeatingCoolingState.HEAT) {
272
- this.hvac.setMode(commands.mode.value['heat']);
273
- if (this.debug === true) {
274
- this.log.info('setTargetHeatingCoolingState: Set mode to HEAT');
275
- }
276
- }
277
-
278
- if (this.TargetHeatingCoolingState === Characteristic.TargetHeatingCoolingState.COOL) {
279
- this.hvac.setMode(commands.mode.value['cool']);
280
- if (this.debug === true) {
281
- this.log.info('setTargetHeatingCoolingState: Set mode to COOL');
282
- }
283
- }
284
-
285
- if (this.TargetHeatingCoolingState === Characteristic.TargetHeatingCoolingState.AUTO) {
286
- this.hvac.setMode(commands.mode.value['auto']);
287
- if (this.debug === true) {
288
- this.log.info('setTargetHeatingCoolingState: Set mode to AUTO');
289
- }
290
- }
291
- }
292
- }
293
- callback();
294
- } catch (err) {callback();}
295
- },
296
-
297
- getTargetTemperature: function(callback) {
298
- try {
299
- this.TargetTemperature = parseInt(this.hvac.device.props[commands.temperature.code]);
300
- callback(null, this.TargetTemperature);
301
- } catch (err) {callback();}
302
- },
303
-
304
- setTargetTemperature: function(TargetTemperature, callback, context) {
305
- if(context !== 'fromSetValue') {
306
- this.TargetTemperature = TargetTemperature;
307
- // if ac is off, then turn it on by setting to auto mode
308
- if (this.TargetHeatingCoolingState == Characteristic.TargetHeatingCoolingState.OFF) {
309
- this.hvac.setPower(commands.power.value.on);
310
- }
311
-
312
- // Update current temperature
313
- // this.GreeACService
314
- // .getCharacteristic(Characteristic.CurrentTemperature)
315
- // .updateValue(parseFloat(TargetTemperature));
316
- this.hvac.setTemp(parseInt(TargetTemperature));
317
- if (this.debug === true) {
318
- this.log.info('setTargetTemperature: Set temperature: ' + TargetTemperature);
319
- }
320
- }
321
- callback();
322
- },
323
-
324
- getCurrentTemperature: function(callback) {
325
- if (this.debug === true) {
326
- this.log.info("getCurrentTemperature: current temperature is %s", this.CurrentTemperature);
327
- }
328
- callback(null, parseFloat(this.CurrentTemperature));
329
- },
330
-
331
- identify: function(callback) {
332
- callback();
333
- },
334
-
335
- getServices: function() {
336
- return this.services;
337
- }
338
- };
339
-