From 194716c779c87f0c915d62b0fd53d44062f26d43 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 13 Dec 2015 13:59:51 +0100 Subject: [PATCH 1/2] Support for Node > 0.10 --- telldus.cc | 328 +++++++++++++++++++++++++++------------------ telldus.js | 3 - test/async.test.js | 2 - 3 files changed, 194 insertions(+), 139 deletions(-) diff --git a/telldus.cc b/telldus.cc index 32e4b75..7a115c7 100644 --- a/telldus.cc +++ b/telldus.cc @@ -9,6 +9,8 @@ #include #include +#include + #include using namespace v8; @@ -17,15 +19,20 @@ using namespace std; namespace telldus_v8 { + struct EventContext { + v8::Persistent> callback; + Isolate *isolate; + }; + struct DeviceEventBaton { - Persistent callback; + EventContext *callback; int deviceId; int lastSentCommand; int levelNum; }; struct SensorEventBaton { - Persistent callback; + EventContext *callback; int sensorId; char *model; char *protocol; @@ -35,7 +42,7 @@ namespace telldus_v8 { }; struct RawDeviceEventBaton { - Persistent callback; + EventContext *callback; int controllerId; char *data; }; @@ -63,69 +70,69 @@ namespace telldus_v8 { char *protocol; }; - Local GetSupportedMethods(int id, int supportedMethods){ + Local GetSupportedMethods(int id, int supportedMethods, Isolate* isolate){ - Local methodsObj = Array::New(); + Local methodsObj = Array::New(isolate); int i = 0; - if (supportedMethods & TELLSTICK_TURNON) methodsObj->Set(i++, String::New("TURNON")); - if (supportedMethods & TELLSTICK_TURNOFF) methodsObj->Set(i++, String::New("TURNOFF")); - if (supportedMethods & TELLSTICK_BELL) methodsObj->Set(i++, String::New("BELL")); - if (supportedMethods & TELLSTICK_TOGGLE) methodsObj->Set(i++, String::New("TOGGLE")); - if (supportedMethods & TELLSTICK_DIM) methodsObj->Set(i++, String::New("DIM")); - if (supportedMethods & TELLSTICK_UP) methodsObj->Set(i++, String::New("UP")); - if (supportedMethods & TELLSTICK_DOWN) methodsObj->Set(i++, String::New("DOWN")); - if (supportedMethods & TELLSTICK_STOP) methodsObj->Set(i++, String::New("STOP")); - if (supportedMethods & TELLSTICK_LEARN) methodsObj->Set(i++, String::New("LEARN")); + if (supportedMethods & TELLSTICK_TURNON) methodsObj->Set(i++, String::NewFromUtf8(isolate, "TURNON")); + if (supportedMethods & TELLSTICK_TURNOFF) methodsObj->Set(i++, String::NewFromUtf8(isolate, "TURNOFF")); + if (supportedMethods & TELLSTICK_BELL) methodsObj->Set(i++, String::NewFromUtf8(isolate, "BELL")); + if (supportedMethods & TELLSTICK_TOGGLE) methodsObj->Set(i++, String::NewFromUtf8(isolate, "TOGGLE")); + if (supportedMethods & TELLSTICK_DIM) methodsObj->Set(i++, String::NewFromUtf8(isolate, "DIM")); + if (supportedMethods & TELLSTICK_UP) methodsObj->Set(i++, String::NewFromUtf8(isolate, "UP")); + if (supportedMethods & TELLSTICK_DOWN) methodsObj->Set(i++, String::NewFromUtf8(isolate, "DOWN")); + if (supportedMethods & TELLSTICK_STOP) methodsObj->Set(i++, String::NewFromUtf8(isolate, "STOP")); + if (supportedMethods & TELLSTICK_LEARN) methodsObj->Set(i++, String::NewFromUtf8(isolate, "LEARN")); return methodsObj; } - Local GetDeviceType(int id, int type){ + Local GetDeviceType(int id, int type, Isolate* isolate){ - if(type & TELLSTICK_TYPE_DEVICE) return String::New("DEVICE"); - if(type & TELLSTICK_TYPE_GROUP) return String::New("GROUP"); - if(type & TELLSTICK_TYPE_SCENE) return String::New("SCENE"); + if(type & TELLSTICK_TYPE_DEVICE) return String::NewFromUtf8(isolate, "DEVICE"); + if(type & TELLSTICK_TYPE_GROUP) return String::NewFromUtf8(isolate, "GROUP"); + if(type & TELLSTICK_TYPE_SCENE) return String::NewFromUtf8(isolate, "SCENE"); - return String::New("UNKNOWN"); + return String::NewFromUtf8(isolate, "UNKNOWN"); } - Local GetDeviceStatus(int id, int lastSentCommand, int level){ + Local GetDeviceStatus(int id, int lastSentCommand, int level, Isolate* isolate){ - Local status = Object::New(); + Local status = Object::New(isolate); switch(lastSentCommand) { case TELLSTICK_TURNON: - status->Set(String::NewSymbol("name"), String::New("ON")); + status->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, "ON")); break; case TELLSTICK_TURNOFF: - status->Set(String::NewSymbol("name"), String::New("OFF")); + status->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, "OFF")); break; case TELLSTICK_DIM: - status->Set(String::NewSymbol("name"), String::New("DIM")); - status->Set(String::NewSymbol("level"), Number::New(level)); + status->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, "DIM")); + status->Set(String::NewFromUtf8(isolate, "level"), Number::New(isolate, level)); break; default: - status->Set(String::NewSymbol("name"), String::New("UNNKOWN")); + status->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, "UNNKOWN")); } return status; } - Local GetDevice( telldusDeviceInternals deviceInternals ) { + Local GetDevice( telldusDeviceInternals deviceInternals, Isolate* isolate ) { - Local obj = Object::New(); - obj->Set(String::NewSymbol("name"), String::New(deviceInternals.name, strlen(deviceInternals.name))); - obj->Set(String::NewSymbol("id"), Number::New(deviceInternals.id)); - obj->Set(String::NewSymbol("methods"), GetSupportedMethods(deviceInternals.id,deviceInternals.supportedMethods)); - obj->Set(String::NewSymbol("model"), String::New(deviceInternals.model, strlen(deviceInternals.model))); - obj->Set(String::NewSymbol("protocol"), String::New(deviceInternals.protocol, strlen(deviceInternals.protocol))); - obj->Set(String::NewSymbol("type"), GetDeviceType(deviceInternals.id,deviceInternals.deviceType)); - obj->Set(String::NewSymbol("status"), GetDeviceStatus(deviceInternals.id,deviceInternals.lastSentCommand,deviceInternals.level)); + Local obj = Object::New(isolate); + obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate,deviceInternals.name)); + obj->Set(String::NewFromUtf8(isolate, "id"), Number::New(isolate,deviceInternals.id)); + obj->Set(String::NewFromUtf8(isolate, "methods"), GetSupportedMethods(deviceInternals.id, deviceInternals.supportedMethods, isolate)); + obj->Set(String::NewFromUtf8(isolate, "model"), String::NewFromUtf8(isolate,deviceInternals.model)); + obj->Set(String::NewFromUtf8(isolate, "protocol"), String::NewFromUtf8(isolate,deviceInternals.protocol)); + obj->Set(String::NewFromUtf8(isolate, "type"), GetDeviceType(deviceInternals.id,deviceInternals.deviceType, isolate)); + obj->Set(String::NewFromUtf8(isolate, "status"), GetDeviceStatus(deviceInternals.id,deviceInternals.lastSentCommand,deviceInternals.level, isolate)); // Cleanup tdReleaseString(deviceInternals.name); @@ -136,19 +143,17 @@ namespace telldus_v8 { } - Handle getDevicesFromInternals( list t ) { - - HandleScope scope; + Local getDevicesFromInternals( list t, Isolate* isolate ) { // Destination array - Local devices = Array::New(t.size()); + Local devices = Array::New(isolate,t.size()); int i=0; for (list::const_iterator iterator = t.begin(), end = t.end(); iterator != end; ++iterator) { - devices->Set(i, GetDevice(*iterator)); + devices->Set(i, GetDevice(*iterator, isolate)); i++; } - return scope.Close(devices); + return devices; } @@ -216,22 +221,25 @@ namespace telldus_v8 { } - } void DeviceEventCallbackAfter(uv_work_t *req, int status) { - HandleScope scope; DeviceEventBaton *baton = static_cast(req->data); + + v8::HandleScope handleScope(baton->callback->isolate); + + EventContext *ctx = static_cast(baton->callback); + + v8::Local func = v8::Local::New(baton->callback->isolate, ((v8::Persistent>)ctx->callback)); Local args[] = { - Number::New(baton->deviceId), - GetDeviceStatus(baton->deviceId,baton->lastSentCommand,baton->levelNum), + Number::New(baton->callback->isolate,baton->deviceId), + GetDeviceStatus(baton->deviceId,baton->lastSentCommand,baton->levelNum, baton->callback->isolate), }; - baton->callback->Call(baton->callback, 2, args); - scope.Close(Undefined()); - + func->Call(baton->callback->isolate->GetCurrentContext()->Global(), 2, args); + delete baton; delete req; @@ -240,8 +248,9 @@ namespace telldus_v8 { void DeviceEventCallback( int deviceId, int method, const char * data, int callbackId, void* callbackVoid ) { DeviceEventBaton *baton = new DeviceEventBaton(); + EventContext *ctx = static_cast(callbackVoid); - baton->callback = static_cast(callbackVoid); + baton->callback = ctx; baton->deviceId = deviceId; uv_work_t* req = new uv_work_t; @@ -251,18 +260,26 @@ namespace telldus_v8 { } - Handle addDeviceEventListener( const Arguments& args ) { - HandleScope scope; + void addDeviceEventListener(const v8::FunctionCallbackInfo& args){ + + Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope(isolate); if (!args[0]->IsFunction()) { - return ThrowException(Exception::TypeError(String::New("Expected 1 argument: (function callback)"))); + v8::Local exception = Exception::TypeError(v8::String::NewFromUtf8(isolate, "Expected 1 argument: (function callback)")); + isolate->ThrowException(exception); } - Persistent callback = Persistent::New(Handle::Cast(args[0])); - Local num = Number::New(tdRegisterDeviceEvent((TDDeviceEvent)&DeviceEventCallback, *callback)); + v8::Local cb = v8::Local::Cast(args[0]); + v8::Persistent> value(isolate, cb); - return scope.Close(num); + EventContext *ctx = new EventContext(); + ctx->callback = value; + ctx->isolate = isolate; + + Local num = Number::New(isolate, tdRegisterDeviceEvent((TDDeviceEvent)&DeviceEventCallback, ctx)); + args.GetReturnValue().Set(num); } @@ -270,25 +287,29 @@ namespace telldus_v8 { void SensorEventCallbackAfter(uv_work_t *req, int status) { - HandleScope scope; - SensorEventBaton *baton = static_cast(req->data); + v8::HandleScope handleScope(baton->callback->isolate); + + EventContext *ctx = static_cast(baton->callback); + + v8::Local func = v8::Local::New(baton->callback->isolate, ((v8::Persistent>)ctx->callback)); + Local args[] = { - Number::New(baton->sensorId), - String::New(baton->model), - String::New(baton->protocol), - Number::New(baton->dataType), - String::New(baton->value), - Number::New(baton->ts) + Number::New(baton->callback->isolate,baton->sensorId), + String::NewFromUtf8(baton->callback->isolate, baton->model), + String::NewFromUtf8(baton->callback->isolate, baton->protocol), + Number::New(baton->callback->isolate,baton->dataType), + String::NewFromUtf8(baton->callback->isolate, baton->value), + Number::New(baton->callback->isolate,baton->ts) }; - baton->callback->Call(baton->callback, 6, args); - scope.Close(Undefined()); + func->Call(baton->callback->isolate->GetCurrentContext()->Global(), 6, args); free(baton->model); free(baton->protocol); free(baton->value); + delete baton; delete req; @@ -297,9 +318,11 @@ namespace telldus_v8 { void SensorEventCallback( const char *protocol, const char *model, int sensorId, int dataType, const char *value, int ts, int callbackId, void *callbackVoid ) { + EventContext *ctx = static_cast(callbackVoid); + SensorEventBaton *baton = new SensorEventBaton(); - baton->callback = static_cast(callbackVoid); + baton->callback = ctx; baton->sensorId = sensorId; baton->protocol = strdup(protocol); baton->model = strdup(model); @@ -314,37 +337,49 @@ namespace telldus_v8 { } - Handle addSensorEventListener( const Arguments& args ) { + void addSensorEventListener(const v8::FunctionCallbackInfo& args){ - HandleScope scope; + Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope(isolate); - if (!args[0]->IsFunction()) { - return ThrowException(Exception::TypeError(String::New("Expected 1 argument: (function callback)"))); - } + v8::Local cb = v8::Local::Cast(args[0]); + v8::Persistent> value(isolate, cb); - Persistent callback = Persistent::New(Handle::Cast(args[0])); - Local num = Number::New(tdRegisterSensorEvent((TDSensorEvent)&SensorEventCallback, *callback)); + EventContext *ctx = new EventContext(); + ctx->callback = value; + ctx->isolate = isolate; - return scope.Close(num); + if (!args[0]->IsFunction()) { + v8::Local exception = Exception::TypeError(v8::String::NewFromUtf8(isolate, "Expected 1 argument: (function callback)")); + isolate->ThrowException(exception); + } + Local num = Number::New(isolate, tdRegisterSensorEvent((TDSensorEvent)&SensorEventCallback, ctx)); + args.GetReturnValue().Set(num); + } void RawDataEventCallbackWorking(uv_work_t *req) { } void RawDataEventCallbackAfter(uv_work_t *req, int status) { - HandleScope scope; RawDeviceEventBaton *baton = static_cast(req->data); + + v8::HandleScope handleScope(baton->callback->isolate); + + EventContext *ctx = static_cast(baton->callback); + + v8::Local func = v8::Local::New(baton->callback->isolate, ((v8::Persistent>)ctx->callback)); Local args[] = { - Number::New(baton->controllerId), - String::New(baton->data), + Number::New(baton->callback->isolate, baton->controllerId), + String::NewFromUtf8(baton->callback->isolate, baton->data), }; - baton->callback->Call(baton->callback, 2, args); - scope.Close(Undefined()); + func->Call(baton->callback->isolate->GetCurrentContext()->Global(), 2, args); free(baton->data); + delete baton; delete req; @@ -354,7 +389,7 @@ namespace telldus_v8 { RawDeviceEventBaton *baton = new RawDeviceEventBaton(); - baton->callback = static_cast(callbackVoid); + baton->callback = static_cast(callbackVoid); baton->data = strdup(data); baton->controllerId = controllerId; @@ -365,17 +400,25 @@ namespace telldus_v8 { } + void addRawDeviceEventListener(const v8::FunctionCallbackInfo& args){ + + Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope(isolate); - Handle addRawDeviceEventListener( const Arguments& args ) { + v8::Local cb = v8::Local::Cast(args[0]); + v8::Persistent> value(isolate, cb); - HandleScope scope; if (!args[0]->IsFunction()) { - return ThrowException(Exception::TypeError(String::New("Expected 1 argument: (function callback)"))); + v8::Local exception = Exception::TypeError(v8::String::NewFromUtf8(isolate, "Expected 1 argument: (function callback)")); + isolate->ThrowException(exception); } - Persistent callback = Persistent::New(Handle::Cast(args[0])); - Local num = Number::New(tdRegisterRawDeviceEvent((TDRawDeviceEvent)&RawDataCallback, *callback)); - return scope.Close(num); + EventContext *ctx = new EventContext(); + ctx->callback = value; + ctx->isolate = isolate; + + Local num = Number::New(isolate, tdRegisterRawDeviceEvent((TDRawDeviceEvent)&RawDataCallback, ctx)); + args.GetReturnValue().Set(num); } @@ -383,7 +426,9 @@ namespace telldus_v8 { struct js_work { uv_work_t req; - Persistent callback; + + EventContext *callback; + //char* data; bool rb; // Return value, boolean int rn; // Return value, number @@ -395,6 +440,7 @@ namespace telldus_v8 { char* s; // Arbitrary string value char* s2; // Arbitrary string value bool string_used; + Isolate* isolate; // Global V8 isolate list l; @@ -497,17 +543,22 @@ namespace telldus_v8 { void RunCallback(uv_work_t* req, int status) { + js_work* work = static_cast(req->data); work->string_used = false; - Handle argv[3]; + v8::HandleScope handleScope(work->isolate); + Handle argv[3]; // This makes it possible to catch // the exception from JavaScript land using the // process.on('uncaughtException') event. TryCatch try_catch; + EventContext *ctx; + v8::Local func; + // Reenter the js-world switch(work->f) { @@ -526,10 +577,12 @@ namespace telldus_v8 { case 23: case 24: case 25: - argv[0] = Integer::New(work->rn); // Return number value - argv[1] = Integer::New(work->f); // Return worktype + argv[0] = Integer::New(work->isolate, work->rn); // Return number value + argv[1] = Integer::New(work->isolate, work->f); // Return worktype - work->callback->Call(Context::GetCurrent()->Global(), 2, argv); + ctx = static_cast(work->callback); + func = v8::Local::New(work->isolate, ((v8::Persistent>)ctx->callback)); + func->Call(work->isolate->GetCurrentContext()->Global(), 2, argv); break; @@ -541,10 +594,12 @@ namespace telldus_v8 { case 15: case 16: case 22: - argv[0] = Boolean::New(work->rb); // Return number value - argv[1] = Integer::New(work->f); // Return worktype + argv[0] = Boolean::New(work->isolate, work->rb); // Return number value + argv[1] = Integer::New(work->isolate, work->f); // Return worktype - work->callback->Call(Context::GetCurrent()->Global(), 2, argv); + ctx = static_cast(work->callback); + func = v8::Local::New(work->isolate, ((v8::Persistent>)ctx->callback)); + func->Call(work->isolate->GetCurrentContext()->Global(), 2, argv); break; @@ -554,19 +609,23 @@ namespace telldus_v8 { case 10: case 14: case 21: - argv[0] = String::New(work->rs); // Return string value - argv[1] = Integer::New(work->f); // Return callback function + argv[0] = String::NewFromUtf8(work->isolate, work->rs); // Return string value + argv[1] = Integer::New(work->isolate, work->f); // Return callback function - work->callback->Call(Context::GetCurrent()->Global(), 2, argv); + ctx = static_cast(work->callback); + func = v8::Local::New(work->isolate, ((v8::Persistent>)ctx->callback)); + func->Call(work->isolate->GetCurrentContext()->Global(), 2, argv); break; // Return list case 26: - argv[0] = getDevicesFromInternals(work->l); // Return Object - argv[1] = Integer::New(work->f); // Return callback function + argv[0] = getDevicesFromInternals(work->l, work->isolate); // Return Object + argv[1] = Integer::New(work->isolate, work->f); // Return callback function - work->callback->Call(Context::GetCurrent()->Global(), 2, argv); + ctx = static_cast(work->callback); + func = v8::Local::New(work->isolate, ((v8::Persistent>)ctx->callback)); + func->Call(work->isolate->GetCurrentContext()->Global(), 2, argv); break; @@ -574,7 +633,7 @@ namespace telldus_v8 { // Handle any exceptions thrown inside the callback if (try_catch.HasCaught()) { - node::FatalException(try_catch); + node::FatalException(work->isolate, try_catch); } // Check if we have an allocated string from telldus @@ -583,8 +642,8 @@ namespace telldus_v8 { } // properly cleanup, or death by millions of tiny leaks - work->callback.Dispose(); - work->callback.Clear(); + //work->callback.Dispose(); + //work->callback.Clear(); free(work->s); // char* Created in AsyncCaller free(work->s2); // char* Created in AsyncCaller @@ -593,13 +652,14 @@ namespace telldus_v8 { } - Handle AsyncCaller(const Arguments& args) { + void AsyncCaller(const FunctionCallbackInfo& args) { - HandleScope scope; + Isolate* isolate = args.GetIsolate(); // Make sure we don't get any funky data if(!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber() || !args[3]->IsString() || !args[4]->IsString()) { - return ThrowException(Exception::TypeError(String::New("Wrong arguments"))); + v8::Local exception = Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")); + isolate->ThrowException(exception); } // Make a deep copy of the string argument as we don't want @@ -610,33 +670,37 @@ namespace telldus_v8 { String::Utf8Value str2(args[4]); char * str_copy2 = strdup(*str2); // Deleted at end of RunCallback + v8::Local cb = v8::Local::Cast(args[5]); + v8::Persistent> value(isolate, cb); + + EventContext *ctx = new EventContext(); + ctx->callback = value; + js_work* work = new js_work; work->f = args[0]->NumberValue(); // Worktype work->devID = args[1]->NumberValue(); // Device ID work->v = args[2]->NumberValue(); // Arbitrary number value work->s = str_copy; // Arbitrary string value work->s2 = str_copy2; // Arbitrary string value - + work->isolate = isolate; + work->callback = ctx; work->req.data = work; - work->callback = Persistent::New(Handle::Cast(args[5])); uv_queue_work(uv_default_loop(), &work->req, RunWork, (uv_after_work_cb)RunCallback); - Local retstr = String::New("Running asynchronous process initializer"); - - return scope.Close(retstr); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Running asynchronous process initializer")); } + void SyncCaller(const FunctionCallbackInfo& args) { - Handle SyncCaller(const Arguments& args) { - - // Start a new scope - HandleScope scope; + Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope(isolate); // Make sure we don't get any funky data if(!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber() || !args[3]->IsString() || !args[4]->IsString()) { - return ThrowException(Exception::TypeError(String::New("Wrong arguments"))); + v8::Local exception = Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")); + isolate->ThrowException(exception); } // Make a deep copy of the string argument @@ -652,6 +716,7 @@ namespace telldus_v8 { work->v = args[2]->NumberValue(); // Arbitrary number value work->s = str_copy; // Arbitrary string value work->s2 = str_copy2; // Arbitrary string value + work->isolate = isolate; work->string_used = false; // Used to keep track of used telldus strings @@ -767,7 +832,7 @@ namespace telldus_v8 { case 23: case 24: case 25: - argv = Integer::New(work->rn); // Return number value + argv = Integer::New(isolate,work->rn); // Return number value break; // Return boolean @@ -778,7 +843,7 @@ namespace telldus_v8 { case 15: case 16: case 22: - argv = Boolean::New(work->rb); // Return number value + argv = Boolean::New(isolate,work->rb); // Return number value break; // Return String @@ -787,12 +852,12 @@ namespace telldus_v8 { case 10: case 14: case 21: - argv = String::New(work->rs); // Return string value + argv = String::NewFromUtf8(isolate, work->rs); // Return string value break; // Return list case 26: - argv = getDevicesFromInternals(work->l); // Return Object + argv = getDevicesFromInternals(work->l, work->isolate); // Return Object break; } @@ -806,32 +871,27 @@ namespace telldus_v8 { delete work; - return scope.Close(argv); + args.GetReturnValue().Set(argv); } } extern "C" -void init(Handle target) { - HandleScope scope; +void init(Handle exports) { // Asynchronous function wrapper - target->Set(String::NewSymbol("AsyncCaller"), - FunctionTemplate::New(telldus_v8::AsyncCaller)->GetFunction()); + NODE_SET_METHOD(exports, "AsyncCaller", telldus_v8::AsyncCaller); // Syncronous function wrapper - target->Set(String::NewSymbol("SyncCaller"), - FunctionTemplate::New(telldus_v8::SyncCaller)->GetFunction()); + NODE_SET_METHOD(exports, "SyncCaller", telldus_v8::SyncCaller); // Functions to add event-listener callbacks - target->Set(String::NewSymbol("addDeviceEventListener"), - FunctionTemplate::New(telldus_v8::addDeviceEventListener)->GetFunction()); - target->Set(String::NewSymbol("addSensorEventListener"), - FunctionTemplate::New(telldus_v8::addSensorEventListener)->GetFunction()); - target->Set(String::NewSymbol("addRawDeviceEventListener"), - FunctionTemplate::New(telldus_v8::addRawDeviceEventListener)->GetFunction()); + NODE_SET_METHOD(exports, "addDeviceEventListener", telldus_v8::addDeviceEventListener); + NODE_SET_METHOD(exports, "addSensorEventListener", telldus_v8::addSensorEventListener); + NODE_SET_METHOD(exports, "addRawDeviceEventListener", telldus_v8::addRawDeviceEventListener); } -NODE_MODULE(telldus, init) + +NODE_MODULE(telldus, init); diff --git a/telldus.js b/telldus.js index a5ae756..31ef099 100644 --- a/telldus.js +++ b/telldus.js @@ -7,7 +7,6 @@ var statusEnum = { TELLSTICK_ERROR_UNKNOWN: -99 }; - //initialize the telldus library telldus.SyncCaller(15, 0, 0, '', ''); @@ -187,7 +186,5 @@ process.on('exit', function () { }); }; - - })('object' === typeof module ? module.exports : (this.telldus = {}), this); diff --git a/test/async.test.js b/test/async.test.js index 12ff782..d25a5d3 100644 --- a/test/async.test.js +++ b/test/async.test.js @@ -52,10 +52,8 @@ describe('async methods', function () { describe('config related', function () { - var deviceId; - before(function (done) { telldus.addDevice(function(err, num){ should.not.exist(err); From 0b255f1e8d8a27260f59045692a6f0920526dd46 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Tue, 15 Dec 2015 00:42:31 +0100 Subject: [PATCH 2/2] Added getSensors, added examples, bugfixes --- README.md | 31 ++++++++ examples/tdtool.js | 48 +++++++---- package.json | 6 +- telldus.cc | 194 ++++++++++++++++++++++++++++++++++++++++++--- telldus.js | 3 +- 5 files changed, 253 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 628290e..878df79 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,37 @@ telldus.getDevices(function(err,devices) { ] ``` +getSensors +---------- + +Signature: + +```javascript +telldus.getSensors(function(err,sensors) { + if ( err ) { + console.log('Error: ' + err); + } else { + // The list of sensors and their values is returned + console.log(sensors); + } +}); +``` + + +```javascript + { model: 'temperaturehumidity', + protocol: 'mandolyn', + id: 41, + data: [ + { type: 'TEMPERATURE', + value: '17.6', + timestamp: '2015-12-14 23:33:01' }, + { type: 'HUMIDITY', + value: '26', + timestamp: '2015-12-14 23:33:01' } + ] + } +``` turnOn ------ diff --git a/examples/tdtool.js b/examples/tdtool.js index 4b26ffe..d855e56 100644 --- a/examples/tdtool.js +++ b/examples/tdtool.js @@ -1,23 +1,18 @@ -var telldus = require('..'); -var i; -var devices = telldus.getDevicesSync(); - -var DEVICE_PARAM=3 -var CMD_PARAM=2; - - -// FixMe: Asyncify +var telldus = require('..'), + util = require('util'), + i, + devices, sensors; function syntax(){ console.log("--list|-l\t\t\t List devices"); console.log("--on|-n \t\t Turn device on"); - console.log("--off|-n \t\t Turn device off"); + console.log("--off|-f \t\t Turn device off"); + console.log("--remove|-r \t\t Remove device"); + console.log("--learn|-a \t\t Learn a new device"); process.exit(1); } - - function checkAndSet(method, device){ //check if TURNON is a valid thing if(device.methods.indexOf(method) > -1){ @@ -30,11 +25,19 @@ function checkAndSet(method, device){ } } else { - console.log("Unsuported method, %s. %s", device.methods); + console.error("Unsuported method, %s. %s", device.methods); process.exit(1); } } +function removeDevice(device) { + telldus.removeDevice(device.id, function (err) { + if (err) { + console.error('Could not remove device, error code: ' + err); + } + console.log('Device %s (%s) removed', device.id, device.name); + }); +} function getDevice(id) { id = isNaN(parseInt(id, 10)) ? id: parseInt(id, 10); @@ -48,7 +51,9 @@ function getDevice(id) { return d; } } - console.log("No such device:%s", id); + + console.error("No such device: %s", id); + process.exit(1); } @@ -59,19 +64,32 @@ function parseArgs(args) { switch (args[i]){ case '--on': case '-n': + devices = telldus.getDevicesSync(); checkAndSet('TURNON', getDevice(args[++i])); break; case '--off': case '-f': + devices = telldus.getDevicesSync(); checkAndSet('TURNOFF', getDevice(args[++i])); break; case '--list': case '-l': + devices = telldus.getDevicesSync(); console.log(devices); break; + case '--sensors': + case '-s': + sensors = telldus.getSensors(function(err,sensors){ + console.log(util.inspect(sensors,false,null)); + }); + break; + case '--remove': + case '-r': + devices = telldus.getDevicesSync(); + removeDevice(getDevice(args[++i])); + break; default: syntax(); - break; } } diff --git a/package.json b/package.json index 619dada..ba724a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "telldus", - "version": "0.0.9", + "version": "0.1.0", "description": "node wrapper for telldus-core, based on telldus-core-js", "keywords": [ "telldus", @@ -14,7 +14,7 @@ "url": "git://github.com/Hexagon/node-telldus.git" }, "engines": { - "node": ">= 0.6.19" + "node": ">= 0.12.0" }, "license": "MIT", "main": "./telldus.js", @@ -31,4 +31,4 @@ "mocha": "~1.21.4", "should": "~4.0.4" } -} +} \ No newline at end of file diff --git a/telldus.cc b/telldus.cc index 7a115c7..d5eaaf1 100644 --- a/telldus.cc +++ b/telldus.cc @@ -19,6 +19,8 @@ using namespace std; namespace telldus_v8 { + const int DATA_LENGTH = 20; + struct EventContext { v8::Persistent> callback; Isolate *isolate; @@ -59,6 +61,11 @@ namespace telldus_v8 { | TELLSTICK_DOWN | TELLSTICK_STOP; + struct sensorValue { + char *timeStamp; + char *value; + }; + struct telldusDeviceInternals { int supportedMethods; int deviceType; @@ -70,6 +77,13 @@ namespace telldus_v8 { char *protocol; }; + struct telldusSensorInternals { + int sensorId; + int dataTypes; + char *model; + char *protocol; + }; + Local GetSupportedMethods(int id, int supportedMethods, Isolate* isolate){ Local methodsObj = Array::New(isolate); @@ -172,13 +186,13 @@ namespace telldus_v8 { if( deviceInternals.lastSentCommand == TELLSTICK_DIM ) { - char * levelStr = tdLastSentValue(deviceInternals.id); + char * levelStr = tdLastSentValue(deviceInternals.id); - // Convert to number and add to object - deviceInternals.level = atoi(levelStr); + // Convert to number and add to object + deviceInternals.level = atoi(levelStr); - // Clean up the mess - tdReleaseString(levelStr); + // Clean up the mess + tdReleaseString(levelStr); } @@ -186,6 +200,91 @@ namespace telldus_v8 { } + sensorValue GetSensorValue(int currentType, telldusSensorInternals si) { + + sensorValue sv; + + char value[DATA_LENGTH]; + char timeBuf[80]; + time_t timestamp = 0; + + tdSensorValue(si.protocol, si.model, si.sensorId, currentType, value, DATA_LENGTH, (int *)×tamp); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + + sv.timeStamp = strdup(timeBuf); + sv.value = strdup(value); + + return sv; + + } + + Local GetSupportedSensorValues(telldusSensorInternals si, Isolate* isolate){ + + Local methodsObj = Array::New(isolate); + + int i = 0; + + if (si.dataTypes & TELLSTICK_TEMPERATURE) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_TEMPERATURE, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "TEMPERATURE")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_HUMIDITY) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_HUMIDITY, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "HUMIDITY")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_RAINRATE) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_RAINRATE, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "RAINRATE")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_RAINTOTAL) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_RAINTOTAL, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "RAINTOTAL")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_WINDDIRECTION) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_WINDDIRECTION, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "WINDDIRECTION")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_WINDAVERAGE) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_WINDAVERAGE, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "WINDAVERAGE")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + if (si.dataTypes & TELLSTICK_WINDGUST) { + Local cur = Object::New(isolate); + sensorValue sv = GetSensorValue(TELLSTICK_WINDGUST, si); + cur->Set(String::NewFromUtf8(isolate, "type"), String::NewFromUtf8(isolate, "WINDGUST")); + cur->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, sv.value)), + cur->Set(String::NewFromUtf8(isolate, "timestamp"), String::NewFromUtf8(isolate, sv.timeStamp)), + methodsObj->Set(i++, cur); + } + + return methodsObj; + + } + list getDevicesRaw() { int intNumberOfDevices = tdGetNumberOfDevices(); @@ -199,6 +298,60 @@ namespace telldus_v8 { } + list getSensorsRaw() { + + char protocol[DATA_LENGTH], model[DATA_LENGTH]; + int sensorId = 0, dataTypes = 0; + + list sensorList; + + while(tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes) == TELLSTICK_SUCCESS) { + telldusSensorInternals t; + + t.protocol = strdup(protocol); + t.model = strdup(model); + t.sensorId = sensorId; + t.dataTypes = dataTypes; + + // TODO: Cleanup of char* ? + sensorList.push_back(t); + + } + + return sensorList; + + } + + Local GetSensor( telldusSensorInternals sensorInternals, Isolate* isolate ) { + + Local obj = Object::New(isolate); + + obj->Set(String::NewFromUtf8(isolate, "model"), String::NewFromUtf8(isolate,sensorInternals.model)); + obj->Set(String::NewFromUtf8(isolate, "protocol"), String::NewFromUtf8(isolate,sensorInternals.protocol)); + obj->Set(String::NewFromUtf8(isolate, "id"), Number::New(isolate, sensorInternals.sensorId)); + obj->Set(String::NewFromUtf8(isolate, "data"), GetSupportedSensorValues(sensorInternals, isolate)); + + free(sensorInternals.model); + free(sensorInternals.protocol); + + return obj; + + } + + Local getSensorsFromInternals( list s, Isolate* isolate ) { + + // Destination array + Local sensors = Array::New(isolate,s.size()); + int i=0; + for (list::const_iterator iterator = s.begin(), end = s.end(); iterator != end; ++iterator) { + sensors->Set(i, GetSensor(*iterator, isolate)); + i++; + } + + return sensors; + + } + void DeviceEventCallbackWorking(uv_work_t *req) { DeviceEventBaton *baton = static_cast(req->data); @@ -306,17 +459,14 @@ namespace telldus_v8 { func->Call(baton->callback->isolate->GetCurrentContext()->Global(), 6, args); - free(baton->model); - free(baton->protocol); - free(baton->value); - delete baton; delete req; } void SensorEventCallback( const char *protocol, const char *model, int sensorId, int dataType, const char *value, - int ts, int callbackId, void *callbackVoid ) { + + int ts, int callbackId, void *callbackVoid ) { EventContext *ctx = static_cast(callbackVoid); @@ -443,6 +593,7 @@ namespace telldus_v8 { Isolate* isolate; // Global V8 isolate list l; + list si; }; @@ -537,6 +688,9 @@ namespace telldus_v8 { case 26: // getDevices work->l = getDevicesRaw(); break; + case 27: // getSensors + work->si = getSensorsRaw(); + break; } } @@ -629,6 +783,17 @@ namespace telldus_v8 { break; + // Return list + case 27: + argv[0] = getSensorsFromInternals(work->si, work->isolate); // Return Object + argv[1] = Integer::New(work->isolate, work->f); // Return callback functio + + ctx = static_cast(work->callback); + func = v8::Local::New(work->isolate, ((v8::Persistent>)ctx->callback)); + func->Call(work->isolate->GetCurrentContext()->Global(), 2, argv); + + break; + } // Handle any exceptions thrown inside the callback @@ -655,6 +820,7 @@ namespace telldus_v8 { void AsyncCaller(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope(isolate); // Make sure we don't get any funky data if(!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber() || !args[3]->IsString() || !args[4]->IsString()) { @@ -675,6 +841,7 @@ namespace telldus_v8 { EventContext *ctx = new EventContext(); ctx->callback = value; + ctx->isolate = isolate; js_work* work = new js_work; work->f = args[0]->NumberValue(); // Worktype @@ -809,6 +976,8 @@ namespace telldus_v8 { break; case 26: // getDevices work->l = getDevicesRaw(); + case 27: // getSensors + work->si = getSensorsRaw(); } // Run callback @@ -859,6 +1028,11 @@ namespace telldus_v8 { case 26: argv = getDevicesFromInternals(work->l, work->isolate); // Return Object break; + + // Return list + case 27: + argv = getSensorsFromInternals(work->si, work->isolate); // Return Object + break; } // Check if we have an allocated string from telldus diff --git a/telldus.js b/telldus.js index 31ef099..b57d712 100644 --- a/telldus.js +++ b/telldus.js @@ -51,6 +51,7 @@ process.on('exit', function () { exports.up = function (id, callback) { return nodeAsyncCaller(24, id, 0, '', '', callback); }; exports.down = function (id, callback) { return nodeAsyncCaller(25, id, 0, '', '', callback); }; exports.getDevices = function (callback) { return nodeAsyncCaller(26, 0, 0, '', '', callback); }; + exports.getSensors = function (callback) { return nodeAsyncCaller(27, 0, 0, '', '', callback); }; // Sync versions exports.turnOnSync = function (id) { return telldus.SyncCaller(0, id, 0, '', ''); }; @@ -78,7 +79,7 @@ process.on('exit', function () { exports.upSync = function (id) { return telldus.SyncCaller(24, id, 0, '', ''); }; exports.downSync = function (id) { return telldus.SyncCaller(25, id, 0, '', ''); }; exports.getDevicesSync = function () { return telldus.SyncCaller(26, 0, 0, '', ''); }; - + exports.getSensorsSync = function () { return telldus.SyncCaller(27, 0, 0, '', ''); }; /**