/* * Example UPnP device/service, implementing the BinaryLight device and * SwitchPower services to emulate a light switch. * * The user interface is as minimal as possible so that the GUPnP concepts and * best practises are more apparent. For a better implementation of * BinaryLight, see gupnp-tools. * * This example code is in the public domain. */ #include #include #include #include #include #include static gboolean status; G_BEGIN_DECLS G_MODULE_EXPORT void set_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void get_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void get_status_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void query_target_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data); G_MODULE_EXPORT void query_status_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data); G_END_DECLS /* * Action handlers */ GMainContext* worker_context; struct Args { GUPnPService* service; GUPnPServiceAction* action; gboolean* target; }; void *set_target_thread( void* arguments ); static gboolean do_set_target( gpointer arguments ); /* SetTarget */ G_MODULE_EXPORT void set_target_cb (GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); struct Args* args = malloc( sizeof( struct Args ) ); memset( args, 0, sizeof( struct Args )); args->service = service; args->action = action; args->target = malloc( sizeof( gboolean ) ); /* Get the new target value */ gupnp_service_action_get (action, "newTargetValue", G_TYPE_BOOLEAN, args->target, NULL); pthread_t do_thread; pthread_create( &do_thread, NULL, &set_target_thread, (void*)args ); } void *set_target_thread( void* arguments ) { struct Args* args = (struct Args*) arguments; g_print( "%ld: %s service: %p action: %p target: %s\n", syscall(SYS_gettid), __func__, args->service, args->action, (args->target)?"true":"false" ); g_main_context_invoke_full (worker_context, G_PRIORITY_DEFAULT, do_set_target, arguments, NULL ); } static gboolean do_set_target( gpointer arguments ) { struct Args* args = (struct Args*) arguments; g_print( "%ld: %s service: %p action: %p target: %s\n", syscall(SYS_gettid), __func__, args->service, args->action, (args->target)?"true":"false" ); /* If the new target doesn't match the current status, change the status and emit a notification that the status has changed. */ if (*(args->target) != status) { status = *(args->target); gupnp_service_notify (args->service, "Status", G_TYPE_BOOLEAN, status, NULL); g_print ("The light is now %s.\n", status ? "on" : "off"); } /* Return success to the client */ gupnp_service_action_return (args->action); return G_SOURCE_REMOVE; } /* GetTarget */ G_MODULE_EXPORT void get_target_cb (G_GNUC_UNUSED GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); gupnp_service_action_set (action, "RetTargetValue", G_TYPE_BOOLEAN, status, NULL); gupnp_service_action_return (action); } /* GetStatus */ G_MODULE_EXPORT void get_status_cb (G_GNUC_UNUSED GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); gupnp_service_action_set (action, "ResultStatus", G_TYPE_BOOLEAN, status, NULL); gupnp_service_action_return (action); } /* * State Variable query handlers */ /* Target */ G_MODULE_EXPORT void query_target_cb (G_GNUC_UNUSED GUPnPService *service, G_GNUC_UNUSED char *variable, GValue *value, G_GNUC_UNUSED gpointer user_data) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, status); } /* Status */ G_MODULE_EXPORT void query_status_cb (G_GNUC_UNUSED GUPnPService *service, G_GNUC_UNUSED char *variable, GValue *value, G_GNUC_UNUSED gpointer user_data) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, status); } void *run_worker_thread( void* args ) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); GMainLoop* worker_loop; GUPnPContext *context; GUPnPRootDevice *dev; GUPnPServiceInfo *service; GError *error = NULL; GMainContext *thread1_main_context = args; g_main_context_push_thread_default( worker_context ); /* Create the UPnP context */ context = gupnp_context_new (NULL, NULL, 0, &error); if (error) { g_printerr ("Error creating the GUPnP context: %s\n", error->message); g_error_free (error); return; } /* Create root device */ dev = gupnp_root_device_new (context, "BinaryLight1.xml", "."); gupnp_root_device_set_available (dev, TRUE); /* Get the switch service from the root device */ service = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (dev), "urn:schemas-upnp-org:service:SwitchPower:1"); if (!service) { g_printerr ("Cannot get SwitchPower1 service\n"); return; } g_signal_connect( service, "action-invoked::SetTarget", G_CALLBACK( set_target_cb ), NULL ); g_signal_connect( service, "action-invoked::GetTarget", G_CALLBACK( get_target_cb ), NULL ); g_signal_connect( service, "action-invoked::GetStatus", G_CALLBACK( get_status_cb ), NULL ); g_signal_connect( service, "query-variable::Target", G_CALLBACK( query_target_cb ), NULL ); g_signal_connect( service, "query-variable::Status", G_CALLBACK( query_status_cb ), NULL ); worker_loop = g_main_loop_new( worker_context, FALSE ); g_main_loop_run( worker_loop ); g_main_loop_unref (worker_loop); g_main_context_pop_thread_default (worker_context); g_main_context_unref (worker_context); g_object_unref (service); g_object_unref (dev); g_object_unref (context); } int main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) { g_print( "%ld: %s\n", syscall(SYS_gettid), __func__ ); GMainLoop *main_loop; worker_context = g_main_context_new(); pthread_t worker_thread; pthread_create( &worker_thread, NULL, &run_worker_thread, g_main_context_ref( worker_context ) ); /* By default the light is off */ status = FALSE; g_print ("The light is now %s.\n", status ? "on" : "off"); /* Run the main loop */ main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); /* Cleanup */ g_main_loop_unref (main_loop); return EXIT_SUCCESS; }