Monday, August 7, 2017

(#26) - Let there be light! 



Insufficient coffee to fuel any witticisms or snarky remarks means I'll cut to the chase. The lighting controller in my house is from a company called TCP Connected. I've discussed the challenges I've had with their product back here.  



Bottom line: it works (it controls the lights well), the company has abandoned the product and I've invested enough in the product that I don't want to abandon it.



Standing on the Shoulders -- Again

Several others out there in 'net land must feel the same way, in that they've reverse engineered the protocol used and resurrected the product. Thanks to folks like this person here, we're wresting control of the lighting gateway from the manufacturer and making the thing useful again.  Thank you!


The Code Chase We're Cutting to (towards?)

If you promise not to make fun of my Python, I'll share the relevant snippets.


RoomGetCarousel 

The RoomGetCarousel command is the command sent to the Lighting Gateway that returns a lot of information in an XML payload. In this large XML stream, you'll find the name of the rooms, the names of the bulbs, and their state.

[ Nota Bene: You may be able to update the Gateway with new room and bulb information, but I've been doing that using the phone application when connected locally. ]


I have a TCPGatewayManager object who's responsibility is to send the https commands to the gateway
.  



The Gateway supports SSDP but mine's assigned a dedicated IP address so I don't bother with that discovery protocol.


Here's the call to the Gateway using HTTPS:




Parsing the XML payload slowly to discover rooms, bulbs and other neat things:

def load_rooms_and_devices(self):
    logging.debug('reloading rooms and devices from gateway')
    xmldata = self.my_gateway.get_response_room_get_carousel_command()
    root = ET.fromstring(xmldata)
    gwrcmd = root.find('gwrcmd')
    gcmd = gwrcmd.find('gcmd')
    gdata = gwrcmd.find('gdata')
    gip = gdata.find('gip')
    returnCode = gip.find('rc').text

    #
    # Iterate through the rooms and devices
    # Bear in mind that you must send the right XML to the
    # RoomGetCarousel command
    # to retreive all of these fields below.
    self.rooms = []
    self.lights = []
    self.light_names = []
    for a_room in gip.findall('room'):
        r_id = a_room.find('rid').text
        r_name = a_room.find('name').text
        r = {r_name: r_id}
        self.rooms.append(r)

        ## print 'Room ID: ', r_id, ' Name:[', r_name, ']'
        for a_device in a_room.findall('device'):
            a_did = a_device.find('did').text
            a_name = a_device.find('name').text

            a_state = a_device.find('state').text
            if a_state == '1':
               a_device_powered_on = True
            else:
                a_device_powered_on = False

            if a_device.find('offline') is not None:
                a_online = False
                a_level = "Unknown"
            else:
                a_online = True
                a_level = a_device.find('level').text
            ## print '   Name:[', a_name, ']  DID:', a_did, '  
            ## Powered On:', a_device_powered_on, 
            ## '  Connected To GW:', a_online, '  Level:', a_level

            a_bulb = {'name': a_name, 'did': a_did, 'level': a_level, 
                      'on': a_device_powered_on,
                      'connected': a_online, 'rname': r_name, 
                      'rid': r_id}
            self.lights.append(a_bulb)

            self.light_names.append({a_name: a_did})



I'm still playing with the Python structures that hold my return values. Right now I care about: rooms (their names and numeric IDs) and bulbs (their name, their Device ID [did], their state and their room).


Next post will cover how to turn the lights on, off and dim them.


No comments :

Post a Comment