class StateExtendedColorZones(packet.LifxMessage): registers: packet.REGISTER_T = [ ("count", packet.LifxType.u16, 1), ("index", packet.LifxType.u16, 1), ("colors_count", packet.LifxType.u8, 1), ("colors", packet.Hsbk(), 82), ]
class SetTileEffect(packet.LifxMessage): registers: packet.REGISTER_T = [ ("reserved_1", packet.LifxType.u8, 1), ("reserved_2", packet.LifxType.u8, 1), ("instanceid", packet.LifxType.u32, 1), ("type", packet.LifxType.u8, 1), ("speed", packet.LifxType.u32, 1), ("duration", packet.LifxType.u64, 1), ("reserved_3", packet.LifxType.u32, 1), ("reserved_4", packet.LifxType.u32, 1), ("parameters", packet.LifxType.u32, 8), ("palette_count", packet.LifxType.u8, 1), ("palette", packet.Hsbk(), 16), ] def set_value(self, name: str, value: Any): """The apply register must be an ApplicationRequest type""" if name.lower() == "type": if isinstance(value, str): value = TileEffectType[value].value elif isinstance(value, TileEffectType): value = value.value elif isinstance(value, int): if value not in [ar.value for ar in TileEffectType]: raise ValueError(f"Invalid TileType: {value}") super().set_value(name, value)
class State(packet.LifxMessage): registers: packet.REGISTER_T = [ ("color", packet.Hsbk(), 1), ("reserved_1", packet.LifxType.s16, 1), ("power", packet.LifxType.u16, 1), ("label", packet.LifxType.char, 32), ("reserved_2", packet.LifxType.u64, 1), ]
class StateTileState64(packet.LifxMessage): registers: packet.REGISTER_T = [ ("tile_index", packet.LifxType.u8, 1), ("reserved", packet.LifxType.u8, 1), ("x", packet.LifxType.u8, 1), ("y", packet.LifxType.u8, 1), ("width", packet.LifxType.u8, 1), ("colors", packet.Hsbk(), 64), ]
def test_state_message(self): """Mixture of ints and bytes""" hsbk = packet.Hsbk() hsbk["hue"] = 21845 # Green hsbk["saturation"] = 65535 hsbk["brightness"] = 65535 hsbk["kelvin"] = 3500 state = light_messages.State(color=hsbk, label="example", power=True) state_bytes = state.to_bytes() self.assertEqual(light_messages.State.from_bytes(state_bytes), state)
class SetWaveform(packet.LifxMessage): registers: packet.REGISTER_T = [ ("reserved", packet.LifxType.u8, 1), ("transient", packet.LifxType.u8, 1), ("color", packet.Hsbk(), 1), ("period", packet.LifxType.u32, 1), ("cycles", packet.LifxType.f32, 1), ("skew_ratio", packet.LifxType.s16, 1), ("waveform", packet.LifxType.u8, 1), ]
def test_set_color(self): # Green according to the LIFX packet tutorial: # https://lan.developer.lifx.com/docs/building-a-lifx-packet hsbk = packet.Hsbk() hsbk["hue"] = 21845 # Green hsbk["saturation"] = 65535 hsbk["brightness"] = 65535 hsbk["kelvin"] = 3500 hsbk_bytes = hsbk.to_bytes() bytes_hex = [hex(bb) for bb in hsbk_bytes] bytes_int = [int(bb) for bb in hsbk_bytes] logging.info("HSBK green:") logging.info(hsbk_bytes) logging.info(bytes_hex) logging.info(bytes_int) color = light_messages.SetColor() color["color"] = hsbk color["duration"] = 1024 color_bytes = color.to_bytes() color_hex = [hex(bb) for bb in color_bytes] color_int = [int(bb) for bb in color_bytes] logging.info("SetColor payload green:") logging.info(color_bytes) logging.info(color_hex) logging.info(color_int) nominal_payload = [ 0x00, 0x55, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xAC, 0x0D, 0x00, 0x04, 0x00, 0x00, ] self.assertEqual(color_int, nominal_payload) color_from_bytes = light_messages.SetColor.from_bytes(color_bytes) self.assertEqual(color_from_bytes["color"]["hue"], hsbk["hue"]) self.assertEqual(color_from_bytes["color"]["saturation"], hsbk["saturation"]) self.assertEqual(color_from_bytes["color"]["brightness"], hsbk["brightness"]) self.assertEqual(color_from_bytes["color"]["kelvin"], hsbk["kelvin"]) self.assertEqual(color_from_bytes["duration"], color["duration"]) self.assertEqual(color_from_bytes.type, color.type)
class StateTileEffect(packet.LifxMessage): registers: packet.REGISTER_T = [ ("reserved_1", packet.LifxType.u8, 1), ("instanceid", packet.LifxType.u32, 1), ("type", packet.LifxType.u8, 1), ("speed", packet.LifxType.u32, 1), ("duration", packet.LifxType.u64, 1), ("reserved_2", packet.LifxType.u32, 1), ("reserved_3", packet.LifxType.u32, 1), ("parameters", packet.LifxType.u32, 8), ("palette_count", packet.LifxType.u8, 1), ("palette", packet.Hsbk(), 16), ]
def to_packet(self) -> packet.Hsbk: """Create a message packet from an HSBK tuple""" hsbk = packet.Hsbk() max_hue = hsbk.get_max("hue") + 1 max_saturation = hsbk.get_max("saturation") max_brightness = hsbk.get_max("brightness") hsbk["hue"] = int(self.hue * max_hue / 360) % max_hue hsbk["saturation"] = min(int(self.saturation * max_saturation), max_saturation) hsbk["brightness"] = min(int(self.brightness * max_brightness), max_brightness) hsbk["kelvin"] = int(self.kelvin) return hsbk
def test_hsbk(self): hsbk = packet.Hsbk() hsbk["hue"] = 0 hsbk["saturation"] = 65535 hsbk["brightness"] = 65535 hsbk["kelvin"] = 5500 hsbk_bytes = hsbk.to_bytes() bytes_ints = [int(b) for b in hsbk_bytes] self.assertEqual(bytes_ints, [0, 0, 255, 255, 255, 255, 124, 21]) hsbk_from_bytes = packet.Hsbk.from_bytes(hsbk_bytes) self.assertEqual(hsbk_from_bytes["hue"], hsbk["hue"]) self.assertEqual(hsbk_from_bytes["saturation"], hsbk["saturation"]) self.assertEqual(hsbk_from_bytes["brightness"], hsbk["brightness"]) self.assertEqual(hsbk_from_bytes["kelvin"], hsbk["kelvin"])
def test_set_waveform(self): """contains a float""" hsbk = packet.Hsbk() hsbk["hue"] = 21845 # Green hsbk["saturation"] = 65535 hsbk["brightness"] = 65535 hsbk["kelvin"] = 3500 waveform = light_messages.SetWaveform(transient=1, color=hsbk, period=1000, cycles=3.14, skew_ratio=10) waveform_bytes = waveform.to_bytes() recovered = light_messages.SetWaveform.from_bytes(waveform_bytes) for (name, _, _) in waveform.registers: if name == "cycles": self.assertAlmostEqual(waveform[name], recovered[name], 3) else: self.assertEqual(waveform[name], recovered[name])
class SetExtendedColorZones(packet.LifxMessage): registers: packet.REGISTER_T = [ ("duration", packet.LifxType.u32, 1), ("apply", packet.LifxType.u8, 1), ("index", packet.LifxType.u16, 1), ("colors_count", packet.LifxType.u8, 1), ("colors", packet.Hsbk(), 82), ] def set_value(self, name: str, value: Any, index: Optional[int] = None): """The apply register must be an ApplicationRequest type""" if name.lower() == "apply": if isinstance(value, str): value = ApplicationRequest[value].value elif isinstance(value, ApplicationRequest): value = value.value elif isinstance(value, int): if value not in [ar.value for ar in ApplicationRequest]: raise ValueError(f"Invalid application request: {value}") super().set_value(name, value, index)
def test_packet(self): hsbk = packet.Hsbk(hue=21845, saturation=65535, brightness=65535, kelvin=3500) green = light_messages.SetColor(color=hsbk, duration=1024) comm = packet.UdpSender( ip="127.0.0.1", port=56700, comm=test_utils.MockSocket(), nonblock_delay=0 ) packet_comm = packet.PacketComm(comm) payload_bytes, _ = packet_comm.get_bytes_and_source( payload=green, mac_addr="00:00:00:00:00:00", res_required=False, ack_required=False, sequence=0, source=0, ) # Taken from the green light example lifx_ref = [ 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xAC, 0x0D, 0x00, 0x04, 0x00, 0x00, ] payload_bytes_array = [int(bb) for bb in payload_bytes] self.assertEqual(payload_bytes_array, lifx_ref) # Test the full encode-send-receive-decode chain responses = packet_comm.send_recv( payload=green, mac_addr="00:00:00:00:00:00", res_required=True, ack_required=False, sequence=123, source=1234, verbose=True, ) self.assertEqual(len(responses), 1) response = responses.pop() self.assertEqual(response.addr, ("127.0.0.1", packet.LIFX_PORT)) self.assertEqual(response.frame["source"], 1234) self.assertEqual(response.frame_address["sequence"], 123) self.assertEqual(response.protocol_header["type"], light_messages.State.type) self.assertEqual(response.payload["color"], hsbk)
elif self._timeout: raise socket.timeout else: raise BlockingIOError if __name__ == "__main__": import coloredlogs import logging coloredlogs.install(level=logging.INFO) udp_sender = packet.UdpSender(ip="127.0.0.1", comm=MockSocket()) packet_comm = packet.PacketComm(udp_sender, verbose=True) set_color = light_messages.SetColor(color=packet.Hsbk( hue=16384, saturation=65535, brightness=65535, kelvin=5500, )) logging.info( packet_comm.send_recv(payload=light_messages.SetPower(level=65535), res_required=True)) logging.info(packet_comm.send_recv(payload=set_color, res_required=True)) logging.info( packet_comm.send_recv(payload=light_messages.SetPower(), ack_required=True)) logging.info( packet_comm.send_recv(payload=light_messages.Get(), res_required=True))
class SetColor(packet.LifxMessage): registers: packet.REGISTER_T = [ ("reserved", packet.LifxType.u8, 1), ("color", packet.Hsbk(), 1), ("duration", packet.LifxType.u32, 1), ]