Exemple #1
0
async def transform(collector, target, reference, **kwargs):
    """
    Do a http-api like transformation over whatever target you specify

    ``target:transform d073d5000000 -- '{"color": "red", "effect": "pulse"}'``

    It takes in ``color``, ``effect``, ``power`` and valid options for a
    ``SetWaveformOptional``.

    You may also specify ``transform_options`` that change how the transform works.

    keep_brightness
        Ignore brightness options in the request

    transition_color
        If the light is off and we power on, setting this to True will mean the
        color of the light is not set to the new color before we make it appear
        to be on. This defaults to False, which means it will appear to turn on
        with the new color
    """
    extra = collector.photons_app.extra_as_json
    extra = sb.dictionary_spec().normalise(Meta.empty(), extra)

    transform_options = sb.set_options(
        transform_options=sb.dictionary_spec()).normalise(
            Meta.empty(), extra)["transform_options"]

    msg = Transformer.using(extra, **transform_options)

    if not msg:
        raise PhotonsAppError(
            'Please specify valid options after --. For example ``transform -- \'{"power": "on", "color": "red"}\'``'
        )

    await target.send(msg, reference)
Exemple #2
0
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.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 def gen(reference, sender, **kwargs):
        ps = sender.make_plans("capability")
        async for serial, _, info in sender.gatherer.gather(
                ps, reference, **kwargs):
            if info["cap"].has_matrix:
                for i, (user_x, user_y) in enumerate(positions):
                    yield TileMessages.SetUserPosition(
                        tile_index=i,
                        user_x=user_x,
                        user_y=user_y,
                        res_required=False,
                        target=serial,
                    )

    await target.send(FromGenerator(gen), reference)
Exemple #3
0
 async def animate(
     self, target, sender, final_future, reference, options, global_options=None, **kwargs
 ):
     options = self.optionskls.FieldSpec().normalise(Meta.empty(), options)
     return await self.animationkls(target, sender, options, global_options).animate(
         reference, final_future, **kwargs
     )
Exemple #4
0
 def __new__(*args, **kwargs):
     kls = type.__new__(*args, **kwargs)
     if kls.min_extended_fw is not None:
         spec = sb.tuple_spec(sb.integer_spec(), sb.integer_spec())
         kls.min_extended_fw = spec.normalise(Meta.empty(),
                                              kls.min_extended_fw)
     return kls
Exemple #5
0
    def __init__(self,
                 mn,
                 mx,
                 minimum_mn=None,
                 maximum_mx=None,
                 spec=None,
                 multiplier=None):
        self.mn = round(float(mn), 3)
        self.mx = round(float(mx), 3)
        self.spec = (spec or self.default_normaliser)()
        self.multiplier = multiplier if multiplier is not None else self.default_multiplier

        mmn = minimum_mn if minimum_mn is not None else self.default_minimum_min
        mmx = maximum_mx if maximum_mx is not None else self.default_maximum_max

        if mmn not in (None, False) and self.mn < mmn:
            self.mn = mmn
        if mmx not in (None, False) and self.mx > mmx:
            self.mx = mmx

        self.meta = Meta.empty()

        self.constant = None
        if self.mn == self.mx:
            self.constant = self.mn
Exemple #6
0
async def attr(collector, target, reference, artifact, **kwargs):
    """
    Send a message to your bulb and print out all the replies.

    This is the same as the get_attr and set_attr commands but doesn't prefix the wanted message with get or set

    ``target:attr d073d5000000 get_host_firmware``
    """
    protocol_register = collector.configuration["protocol_register"]

    if artifact in (None, "", sb.NotSpecified):
        raise BadOption(
            "Please specify what you want to get\nUsage: {0} <target>:get_attr <reference> <attr_to_get>"
            .format(sys.argv[0]))

    kls = find_packet(protocol_register, artifact, "")
    if kls is None:
        raise BadOption("Sorry, couldn't a class for this message",
                        prefix="",
                        want=artifact)

    photons_app = collector.photons_app

    extra = photons_app.extra_as_json

    if "extra_payload_kwargs" in kwargs:
        extra.update(kwargs["extra_payload_kwargs"])

    msg = kls.normalise(Meta.empty(), extra)
    async for pkt in target.send(msg, reference, **kwargs):
        print("{0}: {1}".format(pkt.serial, repr(pkt.payload)))
Exemple #7
0
def fields_description(kls):
    """
    yield (name, type_info, help) for all the fields on our kls

    Where type_info looks something like `integer (required)` or `string (default "blah")`

    and fields that have no help are skipped
    """
    final_specs = (
        kls.FieldSpec(formatter=MergedOptionStringFormatter).make_spec(Meta.empty()).kwargs
    )

    for name, field in kls.fields.items():
        hlp = ""
        if type(field) is tuple:
            hlp, field = field
        else:
            hlp = field.help

        spec = final_specs[name]
        if isinstance(field, dictobj.NullableField):
            spec = spec.spec.specs[1]

        if hlp:
            yield name, " ".join(signature(spec)), dedent(hlp).strip()
Exemple #8
0
    async def execute_task(self, **kwargs):
        extra = self.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 def gen(reference, sender, **kwargs):
            ps = sender.make_plans("capability")
            async for serial, _, info in sender.gatherer.gather(
                    ps, reference, **kwargs):
                if info["cap"].has_matrix:
                    for i, (user_x, user_y) in enumerate(positions):
                        yield TileMessages.SetUserPosition(
                            tile_index=i,
                            user_x=user_x,
                            user_y=user_y,
                            res_required=False,
                            target=serial,
                        )

        await self.target.send(FromGenerator(gen), self.reference)
Exemple #9
0
    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)
Exemple #10
0
    async def execute_task(self, **kwargs):
        options = sb.set_options(
            indication=sb.required(sb.boolean()),
            duration_s=sb.required(sb.integer_spec()),
        ).normalise(Meta.empty(), self.photons_app.extra_as_json)

        await self.target.send(SetCleanConfig(**options), self.reference,
                               **kwargs)
Exemple #11
0
 def make_animation(final_future, pauser=None):
     options = self.animator.Options.FieldSpec().normalise(Meta.empty(), self.options)
     animation = self.animator.Animation(final_future, options, pauser=pauser)
     if self.options is not sb.NotSpecified:
         for attr in animation.overridable:
             if attr in self.options:
                 setattr(animation, attr, self.options[attr])
     return animation
Exemple #12
0
    def assertProperties(self, thing, checker):
        checker(thing)

        bts = thing.pack()
        thing2 = type(thing).unpack(bts)
        checker(thing2)

        thing3 = type(thing).normalise(Meta.empty(), json.loads(repr(thing)))
        checker(thing3)
Exemple #13
0
async def make_crontab(collector, target, reference, artifact, **kwargs):
    """
    Make a crontab file executing our day dusk options.

    Usage is::

        ./generate-crontab.py
    """
    collector.register_converters(
        {"daydusk": DayDusk.FieldSpec(formatter=MergedOptionStringFormatter)})
    daydusk = collector.configuration["daydusk"]

    spec = sb.set_options(path=sb.defaulted(sb.string_spec(),
                                            '/config/daydusk.crontab'),
                          lifx_script=sb.defaulted(sb.string_spec(),
                                                   '/usr/local/bin/lifx'))
    extra = collector.configuration["photons_app"].extra_as_json
    kwargs = {
        k: v
        for k, v in spec.normalise(Meta.empty(), extra).items()
        if v is not sb.NotSpecified
    }

    cronfile = kwargs['path']
    lifx_script = kwargs['lifx_script']

    if not daydusk.schedules:
        raise NoSchedules()

    cron = CronTab()

    extra_script_args = ["--silent"]

    for name, options in daydusk.schedules.items():
        script_args = {**options.hsbk, **options.extra}
        command = [
            lifx_script,
            options.task,
            options.reference,
            *extra_script_args,
            "--",
            json.dumps(script_args),
        ]

        command = str(" ".join([shlex.quote(part) for part in command]))

        job = cron.new(command=command)
        job.dow.on(*options.dow)
        job.minute.on(options.minute)
        job.hour.on(options.hour)

    if os.path.exists(cronfile):
        os.remove(cronfile)

    cron.write(cronfile)
    print(f"Generated crontab at {cronfile}")
Exemple #14
0
def make_message(protocol_register, pkt_type, pkt_args):
    """
    Find the packet class for this ``pkt_type`` and instantiate it with
    the provided ``pkt_args``.
    """
    kls = find_packet(protocol_register, pkt_type)
    if pkt_args is not None:
        return kls.normalise(Meta.empty(), pkt_args)
    else:
        return kls()
Exemple #15
0
 def pack_payload(kls, pkt_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(pkt_type) in k.by_type:
             return k.by_type[int(pkt_type)].Payload.normalise(
                 Meta.empty(), data).pack()
     raise BadConversion("Unknown message type!", pkt_type=pkt_type)
Exemple #16
0
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.
    """  # noqa
    options = collector.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.Set64.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.Set64.empty_normalise(**options)
    await target.send(msg, reference)
Exemple #17
0
    async def execute_task(self, **kwargs):
        broadcast = True
        if self.artifact is not sb.NotSpecified:
            broadcast = self.artifact

        options = sb.set_options(
            cli_output=sb.defaulted(sb.boolean(), None),
            env_output=sb.defaulted(sb.boolean(), False),
            settings_output=sb.defaulted(sb.boolean(), False),
        ).normalise(Meta.empty(), self.collector.photons_app.extra_as_json)

        if options["env_output"] is False and options[
                "settings_output"] is False:
            if options["cli_output"] is None:
                options["cli_output"] = True

        env_output = options["env_output"]
        cli_output = options["cli_output"]
        settings_output = options["settings_output"]

        ips = {}

        async with self.target.session() as sender:
            found, serials = await self.reference.find(sender,
                                                       timeout=20,
                                                       broadcast=broadcast)
            for serial in serials:
                services = found[binascii.unhexlify(serial)]
                if Services.UDP in services:
                    ip = services[Services.UDP].host
                    ips[serial] = ip

        sorted_ips = sorted(ips.items(),
                            key=lambda item: ipaddress.ip_address(item[1]))

        if cli_output:
            for serial, ip in sorted_ips:
                print(f"{serial}: {ip}")

        if cli_output and (env_output or settings_output):
            print()

        if env_output:
            print(f"export HARDCODED_DISCOVERY='{json.dumps(sorted_ips)}'")
            if settings_output:
                print()

        if settings_output:
            print("discovery_options:")
            print("  hardcoded_discovery:")

            for serial, ip in sorted_ips:
                print(f'    {serial}: "{ip}"')
Exemple #18
0
async def help(collector, tasks, reference, target, **kwargs):
    """
    Display more help information for specified target:task

    This task takes an extra argument that can be:

    <target>
        A specific target, will show associated tasks for that target

    <target type>
        Will show what targets are available for this type and their
        associated tasks

    <task>
        Will show expanded help information for this task

    You can also be tricky and do something like ``<target>:help`` instead
    of ``help <target>``
    """
    if reference in ("", sb.NotSpecified, None):
        task_name = "help"
        target_name = sb.NotSpecified
    else:
        target_name, task_name = task_specifier_spec().normalise(Meta.empty(), reference)

    target_register = collector.configuration["target_register"]

    if task_name == "help":
        task_name = sb.NotSpecified

    if task_name in target_register.targets or task_name in target_register.types:
        target_name = task_name
        task_name = sb.NotSpecified

    if target_name is not sb.NotSpecified:
        if target_name in target_register.targets or target_name in target_register.types:
            kwargs["specific_target"] = target_name

        if target_name not in target_register.targets and target_name not in target_register.types:
            raise PhotonsAppError(
                "Sorry, cannot find help for non existing target", wanted=target_name
            )

    if task_name is not sb.NotSpecified:
        kwargs["specific_task"] = task_name
        if task_name not in tasks:
            raise PhotonsAppError("Sorry, cannot find help for non existing task", wanted=task_name)

    await list_tasks(collector, tasks, **kwargs)
Exemple #19
0
    def create(kls, *args, **kwargs):
        """Create an instance of this class from either a single position argument or keyword arguments"""
        if (args and kwargs) or (args and len(args) != 1):
            raise PhotonsAppError(
                "Creating a packet must be done with one positional argument, or just keyword arguments",
                args=args,
                kwargs=kwargs,
            )

        val = kwargs or sb.NotSpecified
        if args:
            val = args[0]

        if isinstance(val, (bitarray, bytes)):
            return PacketPacking.unpack(kls, val)

        return kls.spec().normalise(Meta.empty(), val)
Exemple #20
0
    async def execute_task(self, **kwargs):
        task_name = sb.NotSpecified
        target_name = self.target

        if self.reference is not sb.NotSpecified:
            if ":" in self.reference:
                target_name, task_name = task_specifier_spec().normalise(
                    Meta.empty(), self.reference)
            else:
                task_name = self.reference

        target_register = self.collector.configuration["target_register"]

        if task_name in target_register.registered or task_name in target_register.types:
            target_name = task_name
            task_name = sb.NotSpecified

        for name, target in target_register.created.items():
            if target is target_name:
                target_name = name
                break

        if target_name is not sb.NotSpecified:
            if target_name in target_register.registered or target_name in target_register.types:
                kwargs["specific_target"] = target_name

            if (target_name not in target_register.registered
                    and target_name not in target_register.types):
                raise PhotonsAppError(
                    "Sorry, cannot find help for non existing target",
                    wanted=target_name)

        if task_name is not sb.NotSpecified:
            kwargs["specific_task"] = task_name
            if task_name not in task_register:
                raise PhotonsAppError(
                    "Sorry, cannot find help for non existing task",
                    wanted=task_name,
                    available=task_register.names,
                )

        await task_register.fill_task(
            self.collector,
            list_tasks,
            specific_task_groups=self.specific_task_groups,
            **kwargs).run()
Exemple #21
0
    def getitem_spec(self, typ, key, actual, parent, serial, do_transform,
                     allow_bitarray, unpacking):
        """
        Used by __getitem__ to use the spec on the type to transform the ``actual`` value
        """
        if typ._allow_callable:
            if callable(actual):
                actual = actual(parent or self, serial)

        spec = typ.spec(self, unpacking, transform=False)
        res = spec.normalise(Meta.empty().at(key), actual)

        if do_transform and unpacking and res is not sb.NotSpecified and res is not Optional:
            res = typ.untransform(self, res)

        if type(res) is bitarray and not allow_bitarray:
            return res.tobytes()
        else:
            return res
Exemple #22
0
async def attr_actual(collector, target, reference, artifact, **kwargs):
    """
    Same as the attr command but prints out the actual values on the replies rather than transformed values
    """
    protocol_register = collector.configuration["protocol_register"]

    if artifact in (None, "", sb.NotSpecified):
        raise BadOption(
            "Please specify what you want to get\nUsage: {0} <target>:get_attr <reference> <attr_to_get>"
            .format(sys.argv[0]))

    kls = find_packet(protocol_register, artifact, "")
    if kls is None:
        raise BadOption("Sorry, couldn't a class for this message",
                        prefix="",
                        want=artifact)

    extra = collector.photons_app.extra_as_json

    if "extra_payload_kwargs" in kwargs:
        extra.update(kwargs["extra_payload_kwargs"])

    def lines(pkt, indent="    "):
        for field in pkt.Meta.all_names:
            val = pkt[field]
            if isinstance(val, list):
                yield f"{indent}{field}:"
                for item in val:
                    ind = f"{indent}    "
                    ls = list(lines(item, ind))
                    first = list(ls[0])
                    first[len(indent) + 2] = "*"
                    ls[0] = "".join(first)
                    yield from ls
            else:
                yield f"{indent}{field}: {pkt.actual(field)}"

    msg = kls.normalise(Meta.empty(), extra)
    async for pkt in target.send(msg, reference, **kwargs):
        print()
        print(f"""{"=" * 10}: {pkt.serial}""")
        for line in lines(pkt):
            print(line)
Exemple #23
0
    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.
        """
        protocol, pkt_type, Packet, PacketKls, data = kls.get_packet_type(
            data, protocol_register)

        if PacketKls is None:
            if not unknown_ok:
                raise BadConversion("Unknown message type!",
                                    protocol=protocol,
                                    pkt_type=pkt_type)
            PacketKls = Packet

        return PacketKls.normalise(Meta.empty(), data).pack()
Exemple #24
0
    def msg(kls, components, overrides=None):
        """
        Create a :ref:`SetWaveformOptional <LightMessages.SetWaveformOptional>`
        message that may be used to change the state of a device to what has
        been specified.

        .. code-block:: python

            from photons_control.colour import ColourParser


            async def my_action(target, reference):
                msg = ColourParser.msg("green")
                await target.send(msg, reference)
        """
        h, s, b, k = kls.hsbk(components, overrides)

        colour = dict(
            hue=0 if h is None else h,
            set_hue=h is not None,
            saturation=0 if s is None else s,
            set_saturation=s is not None,
            brightness=0 if b is None else b,
            set_brightness=b is not None,
            kelvin=0 if k is None else int(k),
            set_kelvin=k is not None,
        )

        other = dict(
            transient=0,
            cycles=1,
            skew_ratio=0,
            waveform=Waveform.SAW,
            period=0 if not overrides else overrides.get("duration", 0),
        )

        other_override = Effects.make(**(overrides or {}))
        options = MergedOptions.using(other, other_override, overrides or {},
                                      colour)
        return LightMessages.SetWaveformOptional.normalise(
            Meta.empty(), options)
Exemple #25
0
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, "", sb.NotSpecified):
        raise BadOption(
            "Please specify what you want to get\nUsage: {0} <target>:set_attr <reference> <attr_to_get> -- '{{<options>}}'"
            .format(sys.argv[0]))

    setter = find_packet(protocol_register, artifact, "Set")
    if setter is None:
        raise BadOption("Sorry, couldn't a class for this message",
                        prefix="set",
                        want=artifact)

    extra = collector.photons_app.extra_as_json

    if "extra_payload_kwargs" in kwargs:
        extra.update(kwargs["extra_payload_kwargs"])

    msg = setter.normalise(Meta.empty(), extra)
    async for pkt in target.send(msg, reference, broadcast=broadcast):
        print("{0}: {1}".format(pkt.serial, repr(pkt.payload)))
Exemple #26
0
    def __setitem__(self, key, val):
        """
        Set values on the object

        This will unpack dictionaries if we are setting a dictionary for a group
        field.

        We also see if a field has a transform option and use it if it's there
        """
        if key in self.Meta.groups:
            if val is Initial:
                # Special case because of the logic in dictobj that sets default values on initialization
                # Should only happen for group fields where it makes no sense
                return

            self._set_group_item(key, val)
            return

        # If our type has a transformation, apply it to our value
        # The assumption is that if there is a transformation,
        # The value will always be given with the transformation in mind
        # untransform will be used when extracting the value
        typ = self.Meta.all_field_types_dict.get(key)
        if typ and typ._transform is not sb.NotSpecified and val not in (
                sb.NotSpecified, Optional):
            val = typ.do_transform(self, val)

        # If we have multiple, then we want to make sure we create actual objects
        # So that if we modify an object in place, it's updated on the packet
        if typ and hasattr(
                typ,
                "_multiple") and typ._multiple and val is not sb.NotSpecified:
            val = typ.spec(self,
                           unpacking=True).normalise(Meta.empty().at(key), val)

        # Otherwise we set directly on the packet
        dictobj.__setitem__(self, key, val)
Exemple #27
0
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, "", sb.NotSpecified):
        raise BadOption(
            "Please specify what you want to get\nUsage: {0} <target>:get_attr <reference> <attr_to_get>"
            .format(sys.argv[0]))

    getter = find_packet(protocol_register, artifact, "Get")
    if getter is None:
        raise BadOption("Sorry, couldn't a class for this message",
                        prefix="get",
                        want=artifact)

    extra = collector.photons_app.extra_as_json

    if "extra_payload_kwargs" in kwargs:
        extra.update(kwargs["extra_payload_kwargs"])

    msg = getter.normalise(Meta.empty(), extra)
    async for pkt in target.send(msg, reference, **kwargs):
        print("{0}: {1}".format(pkt.serial, repr(pkt.payload)))
Exemple #28
0
        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):
                    assert Filter.from_options(options) is 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.assertFltrMatches(filtr, want)

        describe "empty":
            it "gives back a filter with just refresh options":
                self.assertFltrMatches(
                    Filter.empty(), {"refresh_info": False, "refresh_discovery": False}
                )
Exemple #29
0
                    ]

                @hp.memoized_property
                def Together(s):
                    class Together(dictobj.PacketSpec):
                        parent_packet = True
                        fields = [("g1", s.Group1), ("g2", s.Group2), ("three", T.String)]

                    return Together

            return V()

        it "works", V:
            spec = V.Together.spec()
            t = spec.normalise(
                Meta.empty(),
                {"one": False, "two": 1.2, "three": b"whatever\x00\x00", "sb": 24, "bts": b"\x01"},
            )
            assert t.one is False
            assert t.two == 1.2
            assert t.mod == "1.2.modified"
            assert t.three == "whatever"
            assert t.sb == 24
            assert t.bts == b"\x01\x00\x00"

            assert t.actual("two") == 65538
            assert t.actual("three").tobytes() == b"whatever\x00\x00"

        it "works when value given as groups", V:
            spec = V.Together.spec()
            t = spec.normalise(
 def make_photons_app(self, **kwargs):
     meta = Meta.empty().at("photons_app")
     return PhotonsApp.FieldSpec(formatter=MergedOptionStringFormatter).normalise(meta, kwargs)