def pack_payload(kls, message_type, data, messages_register=None): """ Given some payload data as a dictionary and it's ``pkt_type``, return a hexlified string of the payload. """ for k in (messages_register or [kls]): if int(message_type) in k.by_type: return k.by_type[int(message_type)].Payload.normalise( Meta.empty(), data).pack() raise BadConversion("Unknown message type!", pkt_type=message_type)
def from_options(kls, options): """Create a Filter based on the provided dictionary""" if isinstance(options, dict): for option in options: if option not in kls.fields: log.warning( hp.lc("Unknown option provided for filter", wanted=option)) return kls.FieldSpec().normalise(Meta.empty(), options)
async def set_chain_state(collector, target, reference, artifact, **kwargs): """ Set the state of colors on your tile ``lan:set_chain_state d073d5f09124 -- '{"colors": [[[0, 0, 0, 3500], [0, 0, 0, 3500], ...], [[0, 0, 1, 3500], ...], ...], "tile_index": 1, "length": 1, "x": 0, "y": 0, "width": 8}'`` Where the colors is a grid of 8 rows of 8 ``[h, s, b, k]`` values. """ options = collector.configuration["photons_app"].extra_as_json if "colors" in options: spec = sb.listof( sb.listof( list_spec(sb.integer_spec(), sb.float_spec(), sb.float_spec(), sb.integer_spec()))) colors = spec.normalise(Meta.empty().at("colors"), options["colors"]) row_lengths = [len(row) for row in colors] if len(set(row_lengths)) != 1: raise PhotonsAppError( "Please specify colors as a grid with the same length rows", got=row_lengths) num_cells = sum(len(row) for row in colors) if num_cells != 64: raise PhotonsAppError("Please specify 64 colors", got=num_cells) cells = [] for row in colors: for col in row: cells.append({ "hue": col[0], "saturation": col[1], "brightness": col[2], "kelvin": col[3] }) options["colors"] = cells else: raise PhotonsAppError( "Please specify colors in options after -- as a grid of [h, s, b, k]" ) missing = [] for field in TileMessages.SetState64.Payload.Meta.all_names: if field not in options and field not in ("duration", "reserved6"): missing.append(field) if missing: raise PhotonsAppError("Missing options for the SetTileState message", missing=missing) options["res_required"] = False msg = TileMessages.SetState64.empty_normalise(**options) await target.script(msg).run_with_all(reference)
def pack(kls, data, protocol_register, unknown_ok=False): """ Return a hexlified string of the data. This uses ``pkt_type`` and ``protocol`` in the data, along with the protocol_register to find the appropriate class to use to perform the packing. """ if "pkt_type" in data: message_type = data["pkt_type"] elif "pkt_type" in data.get("protocol_header", {}): message_type = data["protocol_header"]["pkt_type"] else: raise BadConversion( "Don't know how to pack this dictionary, it doesn't specify a pkt_type!" ) if "protocol" in data: protocol = data["protocol"] elif "frame_header" in data and "protocol" in data["frame_header"]: protocol = data["frame_header"]["protocol"] else: raise BadConversion( "Don't know how to pack this dictionary, it doesn't specify a protocol!" ) prot = protocol_register.get(protocol) if prot is None: raise BadConversion("Unknown packet protocol", wanted=protocol, available=list(protocol_register)) Packet, messages_register = prot for k in (messages_register or [kls]): if message_type in k.by_type: return k.by_type[message_type].normalise(Meta.empty(), data).pack() if unknown_ok: return Packet.normalise(Meta.empty(), data).pack() raise BadConversion("Unknown message type!", pkt_type=message_type)
async def set_attr(collector, target, reference, artifact, broadcast=False, **kwargs): """ Set attributes on your globes ``target:set_attr d073d5000000 color -- '{"hue": 360, "saturation": 1, "brightness": 1}'`` This does the same thing as ``get_attr`` but will look for ``Set<Attr>`` message and initiates it with the options found after the ``--``. So in this case it will create ``SetColor(hue=360, saturation=1, brightness=1)`` and send that to the device. """ protocol_register = collector.configuration["protocol_register"] if artifact in (None, "", NotSpecified): raise BadOption( "Please specify what you want to get\nUsage: {0} <target>:set_attr <reference> <attr_to_get> -- '{{<options>}}'" .format(sys.argv[0])) kls_name = "Set{0}".format("".join(part.capitalize() for part in artifact.split("_"))) setter = None for messages in protocol_register.message_register(1024): for kls in messages.by_type.values(): if kls.__name__ == kls_name: setter = kls break if setter is None: raise BadOption( "Sorry, couldn't find the message type {0}".format(kls_name)) photons_app = collector.configuration["photons_app"] extra = photons_app.extra_as_json if "extra_payload_kwargs" in kwargs: extra.update(kwargs["extra_payload_kwargs"]) script = target.script(setter.normalise(Meta.empty(), extra)) async for pkt, _, _ in script.run_with(reference, broadcast=broadcast): print("{0}: {1}".format(pkt.serial, repr(pkt.payload)))
async def get_attr(collector, target, reference, artifact, **kwargs): """ Get attributes from your globes ``target:get_attr d073d5000000 color`` Where ``d073d5000000`` is replaced with the serial of the device you are addressing and ``color`` is replaced with the attribute you want. This task works by looking at all the loaded LIFX binary protocol messages defined for the 1024 protocol and looks for ``Get<Attr>``. So if you want the ``color`` attribute, it will look for the ``GetColor`` message and send that to the device and print out the reply packet we get back. """ protocol_register = collector.configuration["protocol_register"] if artifact in (None, "", NotSpecified): raise BadOption( "Please specify what you want to get\nUsage: {0} <target>:get_attr <reference> <attr_to_get>" .format(sys.argv[0])) kls_name = "Get{0}".format("".join(part.capitalize() for part in artifact.split("_"))) getter = None for messages in protocol_register.message_register(1024): for kls in messages.by_type.values(): if kls.__name__ == kls_name: getter = kls break if getter is None: raise BadOption( "Sorry, couldn't find the message type {0}".format(kls_name)) photons_app = collector.configuration["photons_app"] extra = photons_app.extra_as_json if "extra_payload_kwargs" in kwargs: extra.update(kwargs["extra_payload_kwargs"]) script = target.script(getter.normalise(Meta.empty(), extra)) async for pkt, _, _ in script.run_with(reference, **kwargs): print("{0}: {1}".format(pkt.serial, repr(pkt.payload)))
async def apply_theme(collector, target, reference, artifact, **kwargs): """ Apply a theme to specified device ``lan:apply_theme d073d5000001 -- `{"colors": <colors>, "theme": "SPLOTCH"}'`` If you don't specify serials, then the theme will apply to all devices found on the network. colors must be an array of ``{"hue": <hue>, "saturation": <saturation>, "brightness": <brightness>, "kelvin": <kelvin>}`` theme must be a valid theme type and defaults to SPLOTCH You may also specify ``duration`` which is how long to take to apply in seconds. And you may also supply ``hue``, ``saturation``, ``brightness`` and ``kelvin`` to override the specified colors. """ options = Options.FieldSpec().normalise( Meta.empty(), collector.configuration["photons_app"].extra_as_json) async with target.session() as afr: await do_apply_theme(target, reference, afr, options)
async def set_tile_positions(collector, target, reference, **kwargs): """ Set the positions of the tiles in your chain. ``lan:set_tile_positions d073d5f09124 -- '[[0, 0], [-1, 0], [-1, 1]]'`` """ extra = collector.configuration["photons_app"].extra_as_json positions = sb.listof(sb.listof(sb.float_spec())).normalise( Meta.empty(), extra) if any(len(position) != 2 for position in positions): raise PhotonsAppError( "Please enter positions as a list of two item lists of user_x, user_y" ) async with target.session() as afr: for i, (user_x, user_y) in enumerate(positions): msg = TileMessages.SetUserPosition(tile_index=i, user_x=user_x, user_y=user_y, res_required=False) await target.script(msg).run_with_all(reference, afr)
def empty_normalise(self, **kwargs): """Normalise val with the spec from self.make_spec""" return self.normalise(Meta.empty(), kwargs)
return pkt.getitem_spec(self.typ, self.key, actual, self.parent, self.serial , do_transform = do_transform , allow_bitarray = allow_bitarray , unpacking = self.unpacking ) it "calls the value if it's allowed to be callable and is callable": self.typ._allow_callable = True cald = mock.Mock(name="actual return value") actual = mock.Mock(name='actual', return_value=cald) class P(PacketSpecMixin): pass p = P() meta = Meta.empty() self.assertIs(self.getitem_spec(p, actual, do_transform=True, allow_bitarray=True), self.untransformed) actual.assert_called_with(self.parent, self.serial) self.typ.spec.assert_called_once_with(p, self.unpacking, transform=False) self.initd_spec.normalise.assert_called_with(meta.at(self.key), cald) self.untransform.assert_called_with(self.normalised) it "does not call the value if it's not allowed to be callable and is callable": actual = mock.Mock(name='actual') class P(PacketSpecMixin): pass p = P() meta = Meta.empty()
describe "from_options": it "normalises the options": normalised = mock.Mock(name="normalised") spec = mock.Mock(name="spec") spec.normalise.return_value = normalised FieldSpec = mock.Mock(name='FieldSpec', return_value=spec) options = mock.Mock(name="options") with mock.patch.object(Filter, "FieldSpec", FieldSpec): self.assertIs(Filter.from_options(options), normalised) FieldSpec.assert_called_once_with() spec.normalise.assert_called_once_with(Meta.empty(), options) it "works": want = {"label": ["bathroom", "hallway"], "location_id": ["identifier1"], "saturation": [(0.7, 0.7), (0.8, 1.0)]} filtr = Filter.from_options(want) self.assertFiltrMatches(filtr, want) describe "empty": it "uses from_options with just force_refresh": filtr = mock.Mock(name="filtr") from_options = mock.Mock(name="from_options", return_value=filtr) with mock.patch.object(Filter, "from_options", from_options): self.assertIs(Filter.empty(), filtr) from_options.assert_called_once_with({"force_refresh": False})
assert not hasattr(changed, "three") assert not hasattr(changed, "four") self.assertEqual(changed.one, sb.NotSpecified) self.assertEqual(changed.two, sb.NotSpecified) it "allows setting all values to be required": class Original(dictobj.Spec): one = dictobj.Field(sb.string_spec) two = dictobj.Field(sb.integer_spec) three = dictobj.Field(sb.string_spec) four = dictobj.Field(sb.any_spec) Changed = Original.selection("Changed", ["one", "two"], all_required=True) m = Meta.empty() error1 = BadSpecValue("Expected a value but got none", meta=m.at("two")) error2 = BadSpecValue("Expected a value but got none", meta=m.at("one")) with self.fuzzyAssertRaisesError(BadSpecValue, _errors=[error1, error2]): changed = Changed.FieldSpec().normalise(m, {}) it "can override all_required with optional": class Original(dictobj.Spec): one = dictobj.Field(sb.string_spec) two = dictobj.Field(sb.integer_spec) three = dictobj.Field(sb.string_spec) four = dictobj.Field(sb.any_spec) Changed = Original.selection("Changed", ["one", "two"], all_required=True, optional=["two"])
from input_algorithms.meta import Meta from bitarray import bitarray from textwrap import dedent import binascii import mock describe TestCase, "FrameHeader": before_each: self.frame_header = frame.FrameHeader() it "defaults size to size_bits on the pkt divided by 8": pkt = mock.Mock(name="pkt") pkt.size_bits.return_value = 16 spec = self.frame_header.Meta.field_types_dict["size"].spec(pkt) val = spec.normalise(Meta.empty(), sb.NotSpecified) self.assertEqual(val, 2) it "defaults protocol to 1024": pkt = mock.Mock(name="pkt") spec = self.frame_header.Meta.field_types_dict["protocol"].spec(pkt) val = spec.normalise(Meta.empty(), sb.NotSpecified) self.assertEqual(val, 1024) it "defaults addressable to False if we have no target": pkt = mock.Mock(name="pkt", target=None) spec = self.frame_header.Meta.field_types_dict["addressable"].spec(pkt) val = spec.normalise(Meta.empty(), sb.NotSpecified) self.assertEqual(val, False) it "defaults addressable to True if we do have a target":
async def animate(self, target, afr, final_future, reference, options, **kwargs): options = self.optionskls.FieldSpec().normalise(Meta.empty(), options) return await self.animationkls(target, afr, options).animate( reference, final_future, **kwargs)
# coding: spec from photons_device_finder import boolean, str_ranges from photons_app.test_helpers import TestCase from input_algorithms.errors import BadSpecValue from input_algorithms.meta import Meta describe TestCase, "boolean": it "transforms int into a boolean": self.assertEqual(boolean().normalise(Meta.empty(), 0), False) for i in (1, 20, 100): self.assertEqual(boolean().normalise(Meta.empty(), i), True) it "transforms a str into boolean": for s in ("no", "false", "No", "NO", "False", "FALSE"): self.assertEqual(boolean().normalise(Meta.empty(), s), False) for s in ("yes", "true", "True", "TRUE", "YES"): self.assertEqual(boolean().normalise(Meta.empty(), s), True) it "passes through booleans": self.assertEqual(boolean().normalise(Meta.empty(), False), False) self.assertEqual(boolean().normalise(Meta.empty(), True), True) it "complains about anything else": class Wat: pass for thing in ([], [1], {}, {1: 2}, lambda: 1, Wat, Wat()):