Philips Hue Control

AceInfinity

Emeritus, Contributor
Joined
Feb 21, 2012
Posts
1,728
Location
Canada
Preview:

I have a few Philips hue devices in my house and I got tired of looking at all of the limited and buggy apps that require further payment to make use of the features that the devices themselves already have.

I looked through the documentation for the API and read quite a bit about it. Supposedly these lights use the ZigBee protocol (which I am very familiar with) and create a mesh for communication with one another, and from my understanding back to the hub. The hub itself is the only device that I can see from a network scan so it seems to be the only device that uses IP communication. The hub acts as the central point for keeping/storing data, and pushing and polling it to and from the devices themselves.

I wrote some C code in a couple hours to communicate with the RESTful web API built into the hub over TCP sockets on the default HTTP port (80). From the API, I don't have anything in my code that currently pulls data about a device, since that would require me to incorporate a JSON library in with my existing code and I'm already too tired to really see my computer screen, but for changing device data, my code works for most of the visual changes you'd see. I still have to add in code that changes device name and such, but I'll get there when I have some more free time...

Supposedly Philips uses FreeRTOS and lwTCP (Lightweight TCP/IP stack)...

main.c
Code:
[NO-PARSE]/*
 * Author: AceInfinity (c) 2015
 * Date:   2015-01-06 19:34:23
 * Filename: main.c
 * Last Modified time: 2015-01-23 23:32:51
*/

#define HUE_HOST "192.168.1.35" // need to detect this automatically ideally
// #define HUE_PORT 80
#include "hue.h"

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

int main(void)
{
  if (ws_init() != 0)
  {
    fprintf(stderr, "Failed to initialize Winsock.\n");
    exit(1);
  }

  uint8_t bitflags = SET_ON | SET_BRIGHTNESS | SET_SATURATION | SET_HUE;
  struct hue_config_t config =
  {
    .on = false,
    .brightness = 0xFE,
    .saturation = 0xFF,
    .hue = HUE_RED,
    .color_temp = 0,
    .alert = "lselect",
    .effect = "none",
    .transitiontime = 10
  };

  // enumerate all light id's and set properties to all
  const int n_devices = 4;
  for (int id = 1; id <= n_devices; ++id)
  {
    char url[256];
    sprintf(url, "/api/newdeveloper/lights/%d/state", id);
    set_hue_light_opt(url, bitflags, &config);
  }

  if (ws_destroy() != 0)
    fprintf(stderr, "Failed to cleanup Winsock.\n");
}[/NO-PARSE]

hue.h
Code:
[NO-PARSE]/*
 * Author: AceInfinity (c) 2015
 * Date:   2015-01-23 22:56:52
 * File:   hue.h
 * Last Modified time: 2015-01-23 23:32:48
*/

#ifndef __HUE_HTTP_H__
#define __HUE_HTTP_H__

#include "socks.h"
#include "hue_config.h"
#include <stdbool.h>
#include <stdint.h>
 
bool send_put_request(const char *url, const char *data, const size_t send_max_len, char *recv_buf, const size_t recv_max_len)
{
  SOCKET fd; // socket descriptor
  if ((fd = ws_socket_create(TCP)) == INVALID_SOCKET)
  {
    int err = WSAGetLastError();
    if (err == WSAETIMEDOUT)
      fprintf(stderr, "[Socket Create] Connection timed out.\n");
    else
      fprintf(stderr, "Failed to create socket: %d\n", err);
    return false;
  }
  if (ws_socket_connect(fd, HUE_HOST, HUE_PORT) != 0)
  {
    int err = WSAGetLastError();
    if (err == WSAETIMEDOUT)
      fprintf(stderr, "[Socket Connect] Connection timed out.\n");
    else
      fprintf(stderr, "Failed to connect socket: %d\n", err);
    return false;
  }

  char send_buf[send_max_len];
  memset(send_buf, 0, send_max_len);

  sprintf(send_buf, "PUT %s HTTP/1.1\r\nContent-Length:%u\r\n\r\n%s", url, strlen(data), data);
  if (ws_socket_send(fd, send_buf, strlen(send_buf)) == SOCKET_ERROR)
  {
    fprintf(stderr, "send() error: %d\n", WSAGetLastError());
    return false;
  }

  if (ws_socket_recv(fd, recv_buf, recv_max_len) == SOCKET_ERROR)
  {
    fprintf(stderr, "recv() error: %d\n", WSAGetLastError());
    return false;
  }

  return true;
}

void set_hue_light_opt(const char *url, uint8_t flags, const struct hue_config_t *config)
{
  char data[MAX_BUF], recv_buf[MAX_BUF];
  
  /* Transition Time (100ms multiple variant) */
  if (flags & SET_TRANSITIONTIME)
  {
    sprintf(data, "{\"transitiontime\":%u}", config->transitiontime);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* ON(/OFF) */
  if (flags & SET_ON)
  {
    sprintf(data, "{\"on\":%s}", config->on ? "true" : "false");
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Hue */
  if (flags & SET_HUE)
  {
    sprintf(data, "{\"hue\":%u}", config->hue);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Brightness */
  if (flags & SET_BRIGHTNESS)
  {
    sprintf(data, "{\"bri\":%u}", config->brightness);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Saturation */
  if (flags & SET_SATURATION)
  {
    sprintf(data, "{\"sat\":%u}", config->saturation);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Color Temp */
  if (flags & SET_COLOR_TEMP)
  {
    sprintf(data, "{\"ct\":%u}", config->color_temp);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Alert */
  if (flags & SET_ALERT)
  {
    sprintf(data, "{\"alert\":\"%s\"}", config->alert);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

  /* Effect */
  if (flags & SET_EFFECT)
  {
    sprintf(data, "{\"effect\":\"%s\"}", config->effect);
    send_put_request(url, data, MAX_BUF, recv_buf, MAX_BUF);
  }

}

#endif // __HUE_HTTP_H__[/NO-PARSE]

hue_config.h
Code:
[NO-PARSE]/*
 * Author: AceInfinity (c) 2015
 * Date:   2015-01-23 22:53:23
 * File:   hue_config.h
 * Last Modified time: 2015-01-23 23:32:43
*/

#ifndef __PHILIPS_HUE_H__
#define __PHILIPS_HUE_H__

#ifndef HUE_HOST
#error HUE_HOST is not defined (this must be determined automatically in future revisions)
#endif

#ifndef HUE_PORT
#define HUE_PORT 80 // use default http port (80)
#endif

#include <stdbool.h>
#include <stdint.h>

#define LIGHT_ID(id) #id

// predefined hue numeric values
#define HUE_RED 0
#define HUE_YELLOW 12750
#define HUE_GREEN 25500
#define HUE_BLUE 46920
#define HUE_MAGENTA 56100

// set option flags
#define SET_NONE            0
#define SET_ON              1
#define SET_BRIGHTNESS      2
#define SET_SATURATION      4
#define SET_HUE             8
#define SET_COLOR_TEMP      16
#define SET_ALERT           32
#define SET_EFFECT          64
#define SET_TRANSITIONTIME  128

struct hue_config_t
{
  bool on;
  uint8_t brightness;   // 1 - 254   (8 bit unsigned)
  uint8_t saturation;   // 0 - 255   (8 bit unsigned)
  uint16_t hue;         // 0 - 65535 (16 bit unsigned)

  // Mired color temp of the light
  // 2012 connected lights are capable of 153 (6500K) to 500 (2000K).
  // note - LED light strip does not support this state property
  uint16_t color_temp;

  char alert[8]; // "none", "select", "lselect"
  char effect[10]; // "none", "colorloop"

  // duration of the transition from the light’s current state to the new state.
  // This is given as a multiple of 100ms and defaults to 4 (400ms).
  // For example, setting transistiontime:10 will make the transition last 1 second. (10 * 100ms = 1sec)
  uint16_t transitiontime;

  // [reserved for future use]
  // currently always returns true (will be updated in a future patch of the hue software)
  bool reachable;
};

#endif // __PHILIPS_HUE_H__[/NO-PARSE]

socks.h
Code:
[NO-PARSE]/*
 * Author: AceInfinity (c) 2015
 * Date:   2015-01-23 19:52:22
 * File:   socks.h
 * Last Modified time: 2015-01-23 23:32:38
*/

#ifndef __SOCKS_H__
#define __SOCKS_H__

#include <stdio.h>
#include <winsock2.h>

#define MAX_BUF 1024

enum WS_PROTO_TYPE { UDP, TCP };

int  ws_init()
{
  static WSADATA wsa_data;
  return WSAStartup(WINSOCK_VERSION, &wsa_data);
}

int ws_destroy() { return WSACleanup(); }

int ws_socket_close(SOCKET fd)
{
  shutdown(fd, SD_BOTH);
  int ret = closesocket(fd);
  if (ret == SOCKET_ERROR)
    fprintf(stderr, "closesocket() failed: %d\n", WSAGetLastError());
  return ret;
}

int tcp_keep_alive(SOCKET fd)
{
  int keep_alive = 1;
  if (setsockopt(fd , SOL_SOCKET , SO_KEEPALIVE
                 , (void *)&keep_alive, sizeof(keep_alive)) == SOCKET_ERROR)
  {
    fprintf(stderr, "Set sockets option SO_KEEPALIVE failed: %d\n", WSAGetLastError());
    return -1;
  }

  /* When large blocks of data (for example, 3-4 MB) are sent over a
   * blocking socket, send eventually fails with error 10055, WSAENOBUFS.
   * (https://support.microsoft.com/kb/201213?wa=wsignin1.0)
  */
  int sock_opt = 0;
  if (setsockopt(fd , SOL_SOCKET , SO_SNDBUF, (void *)&sock_opt, sizeof(sock_opt)) == SOCKET_ERROR)
  {
    fprintf(stderr, "Set sockets option SO_SNDBUF failed: %d\n", WSAGetLastError());
    return -1;
  }

  // reuse sockets, only effective for bind() function call
  BOOL sock_reuse = TRUE;
  if (setsockopt(fd , SOL_SOCKET , SO_REUSEADDR, (void *)&sock_reuse, sizeof(sock_reuse)) == SOCKET_ERROR)
  {
    fprintf(stderr, "Set sockets option SO_REUSEADDR failed: %d\n", WSAGetLastError());
    return -1;
  }
  return 0;
}

int netaddr_set(char *name, struct sockaddr_in *addr)
{
  if ((addr->sin_addr.s_addr = inet_addr(name)) == INADDR_NONE)
    return -1;

  struct hostent *host;
  host = gethostbyname(name);
  if (host)
    addr->sin_addr.s_addr = *(size_t *) host->h_addr_list[0];
  else
    fprintf(stderr, "Failed to resolve IP address\n");
  return 1;
}

SOCKET ws_socket_create(enum WS_PROTO_TYPE protocol)
{
  SOCKET fd = 0;
  switch (protocol)
  {
    case TCP:
      fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      tcp_keep_alive(fd);
      break;
    case UDP:
      fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      break;
    default:
      return INVALID_SOCKET;
  }
  return fd;
}

int ws_socket_connect(SOCKET fd, char *ip, unsigned short port)
{
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(struct sockaddr_in));
  addr.sin_family = AF_INET;
  netaddr_set(ip, &addr);
  addr.sin_port = htons(port);

  if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
  {
    return -1;
  }
  return 0;
}

int ws_socket_send(SOCKET fd, const char *buf, size_t len)
{ return send(fd, buf, len, 0); }

int ws_socket_recv(SOCKET fd, char *buf, size_t len)
{ return recv(fd, buf, len, 0); }

#endif[/NO-PARSE]

NOTE: I have 3 hue light bulbs, and 1 LED light strip, so I enumerate and update 4 devices to the settings defined. What you see in main() is meant to be example code to demonstrate usage of what I've written. You can keep the winsock initialization and destroy however.

Lastly, I do not have code to automatically identify the hub on the network. That is probably going to be the very next thing I do if I continue to work on this. You'll have to do a network scan and find the hub yourself.. On Windows 8.1, while on the same network as the hub it just shows up in the network locations on my computer with the IP address, so that may be a convenient way for you to figure it out if you don't have a scanner.

Enjoy :)
:thumbsup2:
 
Last edited:
I believe the other issue with my current code is that I make multiple requests to set multiple pieces of data. If I combined the changes into 1 JSON structure, I'm sure that could be done with a single request, and thus a single HTTP response in return. The reason why it is like this currently is because I had to test functionality for each state property one at a time, and I wasn't thinking about combining anything at the time.
 
One other thing with my code is that the code I provided above (unlike my local updated code) hardcodes the developer api authenticated name/id. You'll have to change this unless you authenticate 'newdeveloper' as a developer to your hub.

* I've updated my local code so far (haven't edited the thread with the newest code yet). It is much much better, and better structured for reading the response from sending the PUT requests now. I've been playing with the code and coming up with lighting effects all day haha!

I have 10 lights + 1 LED lightstrip in one of the rooms in my house, so I've written programs to make them do "the wave" (based on brightness), I've added stepper "scenes" for changing a light 1 by 1 with a noticable delay and going in a counterclockwise direction. I've also written a loop to create my own color phase effect (even though there is an option "colorloop" provided by default). I've also picked 2 colors, loop through all the lights in the room in a counterclockwise direction to make them alternate between displaying these 2 colors, and added a loop to alternate each individual color between these 2 colors after I set them to alternate initially, and I've tested the color loop program I wrote while I alternate the colors like this and it looks awesome!

Right now my code grabs device states from the hub, but I have yet to parse the JSON data into a struct I've created for all of the data.

I can however now scan for new devices, and rename devices. :)

edit: Output example:
Code:
[success] > setting device state for device id -> 2
response: [{"success":{"/lights/2/state/on":true}},{"success":{"/lights/2/state/hue":46920}},{"success":{"/lights/2/state/sat":254}},{"success":{"/lights/2/state/bri":254}},{"success":{"/lights/2/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 1
response: [{"success":{"/lights/1/state/on":true}},{"success":{"/lights/1/state/hue":46920}},{"success":{"/lights/1/state/sat":254}},{"success":{"/lights/1/state/bri":254}},{"success":{"/lights/1/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 3
response: [{"success":{"/lights/3/state/on":true}},{"success":{"/lights/3/state/hue":46920}},{"success":{"/lights/3/state/sat":254}},{"success":{"/lights/3/state/bri":254}},{"success":{"/lights/3/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 11
response: [{"success":{"/lights/11/state/on":true}},{"success":{"/lights/11/state/hue":46920}},{"success":{"/lights/11/state/sat":254}},{"success":{"/lights/11/state/bri":254}},{"success":{"/lights/11/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 5
response: [{"success":{"/lights/5/state/on":true}},{"success":{"/lights/5/state/hue":46920}},{"success":{"/lights/5/state/sat":254}},{"success":{"/lights/5/state/bri":254}},{"success":{"/lights/5/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 10
response: [{"success":{"/lights/10/state/on":true}},{"success":{"/lights/10/state/hue":46920}},{"success":{"/lights/10/state/sat":254}},{"success":{"/lights/10/state/bri":254}},{"success":{"/lights/10/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 9
response: [{"success":{"/lights/9/state/on":true}},{"success":{"/lights/9/state/hue":46920}},{"success":{"/lights/9/state/sat":254}},{"success":{"/lights/9/state/bri":254}},{"success":{"/lights/9/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 6
response: [{"success":{"/lights/6/state/on":true}},{"success":{"/lights/6/state/hue":46920}},{"success":{"/lights/6/state/sat":254}},{"success":{"/lights/6/state/bri":254}},{"success":{"/lights/6/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 8
response: [{"success":{"/lights/8/state/on":true}},{"success":{"/lights/8/state/hue":46920}},{"success":{"/lights/8/state/sat":254}},{"success":{"/lights/8/state/bri":254}},{"success":{"/lights/8/state/effect":"colorloop"}}]

[success] > setting device state for device id -> 7
response: [{"success":{"/lights/7/state/on":true}},{"success":{"/lights/7/state/hue":46920}},{"success":{"/lights/7/state/sat":254}},{"success":{"/lights/7/state/bri":254}},{"success":{"/lights/7/state/effect":"colorloop"}}]

I think it's time I upload a video to demonstrate what my code does btw. It has definitely been a while since I've uploaded a youtube video.
 
Last edited:
That is great Ace!

You have no idea how much everyone learns from all the code in this section. Unfortunately the HUE bulbs and setup is very costly <_<


-Pranav
 
It is. First starter kit cost me I think $150, for the hub and 3 lamps, then I got a lightstrip for $80... The last 7 lamps cost me $450. It'll be much cheaper to run those lights in the room however at ~7.5W per light, rather than ~50W... I'll make my money back in a while given enough time, plus the enjoyment out of it is priceless. We kinda bit the bullet on the initial cost, but long term it should be worth it. The only regret is that lightstrip... Because I know you can buy regular LED strips for about 1/8th of the cost that I got that one for, and it's only 6ft long...

I know this because of what we pay for LED lighting for some of the automation stuff we do at my work. That LED lighting is a scam.
 
Last edited:
A quick update, I just read the about the SSDP protocol and specification. I created a snippet of code to test directly within main for sending out an M-SEARCH packet to the SSDP standard host at port 1900, and I can get a list of responses from each SSDP discoverable device on the network including my Philips Hue hub. Right now the problem is that the multicast UDP sockets I'm using to make the requests are still using blocking function calls, therefore after each device is expected to send out their responses, I don't receive any more data, and the function call blocks all else from happening within my code. I will have to change this to an asynchronous alternative. I've also noticed that Microsoft provides a set of functions for SSDP, but I have yet to look into them or test them at this point.

Among the list of response header data that I get, the hue response looks like this:
Code:
HTTP/1.1 200 OK

CACHE-CONTROL: max-age=100

EXT:

LOCATION: http://192.168.1.35:80/description.xml

SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1

ST: upnp:rootdevice

USN: uuid:2f402f80-da50-11e1-9b23-00178817122c::upnp:rootdevice

Now, the data from the SERVER response header looks like an okay method for determining the bridge on my network. And I can grab the respondee IP address using the inet_ntoa() function pretty easily.

I'm dreading on whether I should make another TCP request over to http://192.168.1.35:80/description.xml though, and grab data from there as well for information about the device. I only really needed to use SSDP to get the IP/host of the bridge on the network, and if I take this a step further to complete my SSDP code if I turn it into a full wrapper, this might mean introducing an XML parser library into my code as well.

XML file looks like this:
Code:
<root><specVersion><major>1</major><minor>0</minor></specVersion><URLBase>http://192.168.1.35:80/</URLBase><device><deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType><friendlyName>Philips hue (192.168.1.35)</friendlyName><manufacturer>Royal Philips Electronics</manufacturer><manufacturerURL>http://www.philips.com</manufacturerURL><modelDescription>Philips hue Personal Wireless Lighting</modelDescription><modelName>Philips hue bridge 2012</modelName><modelNumber>929000226503</modelNumber><modelURL>http://www.meethue.com</modelURL><serialNumber>00178817122c</serialNumber><UDN>uuid:2f402f80-da50-11e1-9b23-00178817122c</UDN><presentationURL>index.html</presentationURL><iconList><icon><mimetype>image/png</mimetype><height>48</height><width>48</width><depth>24</depth><url>hue_logo_0.png</url></icon><icon><mimetype>image/png</mimetype><height>120</height><width>120</width><depth>24</depth><url>hue_logo_3.png</url></icon></iconList></device></root>

*I can't reiterate enough that the code in post #1 is very ugly and should only be compiled and tested as is (with slight modifications to allow it to work for others), if and only if you just want to fool around and say that you could control the lights from some C code lol. I have a much better and up to date version on my local filesystem, but there's still things I'm adding to it, so I didn't feel it to be necessary quite yet that I update the code within this thread. Part of the problem was that it was developed using a mediocre sockets wrapper that I wrote, so the implementation code can't be good no matter what I do to it.
 
Last edited:
It is. First starter kit cost me I think $150, for the hub and 3 lamps, then I got a lightstrip for $80... The last 7 lamps cost me $450. It'll be much cheaper to run those lights in the room however at ~7.5W per light, rather than ~50W... I'll make my money back in a while given enough time, plus the enjoyment out of it is priceless. We kinda bit the bullet on the initial cost, but long term it should be worth it. The only regret is that lightstrip... Because I know you can buy regular LED strips for about 1/8th of the cost that I got that one for, and it's only 6ft long...

I know this because of what we pay for LED lighting for some of the automation stuff we do at my work. That LED lighting is a scam.

The CFL's and traditional tubelights in my home are now being replaced with the LED's as the old lightings die out. Everyone in the home is appreciating the power savings and the bright white light.
 
Actually, what is interesting is that these Hue lights seem to default to an incandescent color temperature after power is taken away and re-applied to the load. I created a true WHITE scene to set all of the lights to, and it seems to be much more white than the default that these lights come on to. The reason for this may be because there are lots of people who hate the bright white LED light, and enjoy the relaxing yellow luminescense of an incandescent bulb. Even more interestingly, these LED lights are not *true* RGB. It was designed by Philips and modified a bit so that they could get more colors out of the available RGB lights within the bulb itself. I can't get a real green for instance, the green is more of a lawn green color.

Btw, as for the video I promised of the demo for my program... Here's a video of me testing alternating lights and making the lights do the "wave" based on the brightness of a leading light that circulates the room in a counterclockwise motion at the same time:
 
I'd need some kind of lighting controller, then from there some kind of code algorithm that determines what I do for different frequencies. :)

I changed my sockets to nonblocking and added a decent timeout. I now have the results save to an HTML format just for fun. :)
ADmZxhR.png


Very useful little tool though! I ran it in our office today and from the links specified in the LOCATION headers from the responses, there was some informative data in a few of the XML files I looked at. :) SONOS has an xml file that shows you dimension measurements for the product even. Although I was able to see the receivers, all of the samsung smart TV's in the boardroom, among other things. I'll keep this in my networking utilities repository and update it when necessary. :thumbsup2:

Btw Tom, sorry I haven't been able to get around to your program. I have a local copy of the source with some modifications made, I've been meaning to send it back for you to review, but I would like to still make a few other improvements and changes/updates before giving you a revision. Thanks for the patience.

Been busy with a work project (IP camera database and storage, DNS stuff, etc... *Along with this lighting project that I started mostly just to clear my head with a fun project of my own*). The boss has been asking me about getting an IOS app developed for him, and he's got some freelance websites for me to build, along with other web service stuff. I'm not really working directly on this Hue Lighting control source anymore (for now), and the code is improved, but still can be improved if I have time. If people are interested, I'll post the current source for the hue control written in C though.

I've got a million (and one) projects on the go, and it's overwhelming, but I seem to like to pile a lot of work onto my plate. ;) May even set up the home theater system and configure a network for a (possible) future girlfriend too lol!

Cheers!
 
Last edited:
Btw Tom, sorry I haven't been able to get around to your program. I have a local copy of the source with some modifications made, I've been meaning to send it back for you to review, but I would like to still make a few other improvements and changes/updates before giving you a revision. Thanks for the patience.

Hey no problem Ace, I know you're really busy and appreciate any help you can give. I discovered JetBrain's ReSharper the other day (it's free for students) and have been having a little play with it, so far I've found 7 namespaces I don't need lol. Okay, some are for commented out code but still, it's nice having some extra features in VS - even if I don't understand 90% of them :p The syntax highlighting modifications are nice though.

I've got a million (and one) projects on the go, and it's overwhelming, but I seem to like to pile a lot of work onto my plate. ;) May even set up the home theater system and configure a network for a (possible) future girlfriend too lol!

Cheers!

Congrats! Hope everything works out well - with the home theatre and the girlfriend :p
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top