from struct import pack, unpack, calcsize from binascii import hexlify as tohex from .lifxconstants import * BCAST = b'\00\00\00\00\00\00' class Packet: def __init__(self, payload = None): self.size = 0 self.proto = 0x3400 self.target = b'\00\00\00\00\00\00' self.site = b'\00\00\00\00\00\00' self.timestamp = 0 if payload is None: self.payload = LIFXPayload() else: self.payload = payload def __str__(self): return('<packet proto:%s, target:%s, site:%s, type:%s>' % (unicode(tohex(pack('>H', self.proto)), encoding='utf-8'), unicode(tohex(self.target), encoding='utf-8'), unicode(tohex(self.site), encoding='utf-8'), self.payload.name )) def __bytes__(self): encoded_payload = self.payload.encode() self.size = 36 + len(encoded_payload) header = pack('<HHI6sH6sHQHH', self.size, self.proto, 0x0000, self.target, 0x0000, self.site, 0x0000, self.timestamp, self.payload.packet_type, 0x0000) return header + encoded_payload class LIFXPayload: packet_type = 0 pack_str = '<' pack_struct = [] name = 'LIFX' def __init__(self, data = None): self.data = dict([ (datum_name, 0x00) for datum_name in self. pack_struct ]) if data is not None: self.decode(data) def encode(self): ordered_data = [self.data[datum_name] for datum_name in self.pack_struct] return pack(self.pack_str, *ordered_data) def decode(self, bs): if len(bs) != calcsize(self.pack_str): print( 'could not decode %s' % (self.name, ) ) print( tohex(bs)) return data = unpack(self.pack_str, bs) self.data = dict( zip(self.pack_struct, data) ) def __bytes__(self): return self.encode() class GetPANGatewayPayload(LIFXPayload): name = 'Get PAN Gateway' packet_type = GET_PAN_GATEWAY class PANGatewayPayload(LIFXPayload): name = 'PAN Gateway' packet_type = PAN_GATEWAY pack_str = '<BI' pack_struct = ['service', 'port'] class GetLightStatePayload(LIFXPayload): name = 'Get light state' packet_type = GET_LIGHT_STATE class SetLightColorPayload(LIFXPayload): name = 'Set light color' packet_type = SET_LIGHT_COLOR pack_str = '<BHHHHI' pack_struct = ['stream', 'hue', 'saturation', 'brightness', 'kelvin', 'fade_time'] class LightStatusPayload(LIFXPayload): name = 'Light status' packet_type = LIGHT_STATUS pack_str = '<HHHHHH32sQ' pack_struct = ['hue', 'saturation', 'brightness', 'kelvin', 'dim', 'power', 'bulb_label', 'tags'] class GetPowerStatePayload(LIFXPayload): name = 'Get power state' packet_type = GET_POWER_STATE class SetPowerStatePayload(LIFXPayload): name = 'Set power state' packet_type = SET_POWER_STATE pack_str = '<H' pack_struct = ['onoff'] class PowerStatePayload(SetPowerStatePayload): name = 'Power state' packet_type = POWER_STATE class GetTimeStatePayload(LIFXPayload): name = 'Get time state' packet_type = GET_TIME class SetTimeStatePayload(LIFXPayload): name = 'Set time state' packet_type = SET_TIME pack_str = '<Q' pack_struct = ['time'] class TimeStatePayload(LIFXPayload): name = 'Time state' packet_type = TIME_STATE pack_str = '<Q' pack_struct = ['time'] class GetBulbLabelPayload(LIFXPayload): name = "GetBulbLabel" packet_type = GET_BULB_LABEL class SetBulbLabelPayload(LIFXPayload): name = "SetBulbLabel" packet_type = SET_BULB_LABEL pack_str = '<32s' pack_struct = ['bulb_label'] class BulbLabelPayload(LIFXPayload): name = "BulbLabel" packet_type = BULB_LABEL pack_str = '<32s' pack_struct = ['bulb_label'] class GetVersionPayload(LIFXPayload): name = "Get version" packet_type = GET_VERSION class VersionStatePayload(LIFXPayload): name = "Version state" packet_type = VERSION_STATE pack_str = '<LLL' pack_struct = ['vendor', 'product', 'version'] class GetInfoPayload(LIFXPayload): name = "Get info" packet_type = GET_INFO class InfoStatePayload(LIFXPayload): name = "Info state" packet_type = INFO_STATE pack_str = '<QQQ' pack_struct = ['time', 'uptime', 'downtime'] def decode_packet(data): mapping = {GET_PAN_GATEWAY: GetPANGatewayPayload, PAN_GATEWAY: PANGatewayPayload, GET_LIGHT_STATE: GetLightStatePayload, SET_LIGHT_COLOR: SetLightColorPayload, LIGHT_STATUS: LightStatusPayload, GET_POWER_STATE: GetPowerStatePayload, SET_POWER_STATE: GetPowerStatePayload, POWER_STATE: PowerStatePayload, GET_TIME: GetTimeStatePayload, SET_TIME: SetTimeStatePayload, GET_BULB_LABEL: GetBulbLabelPayload, SET_BULB_LABEL: SetBulbLabelPayload, BULB_LABEL: BulbLabelPayload, TIME_STATE: TimeStatePayload, GET_VERSION: GetVersionPayload, VERSION_STATE: VersionStatePayload, GET_INFO: GetInfoPayload, INFO_STATE: InfoStatePayload} if len(data) < 36: return None p = Packet() data, payload = unpack('<HHI6sH6sHQHH', data[:36]), data[36:] p.size = data[0] p.proto = data[1] p.target = data[3] p.site = data[5] p.timestamp = data[7] packet_type = data[8] if packet_type in mapping: p.payload = mapping[packet_type](payload) else: p.payload = LIFXPayload() return p def encodepacket(p): pass