Ejemplo n.º 1
0
class Wat(dictobj.Spec):
    one = dictobj.Field(format_into=sb.string_spec)
    two = dictobj.Field(format_into=sb.string_spec)

    @property
    def thing(self):
        return "{0}.{1}".format(self.one, self.two)
class Result(dictobj.Spec):
    specs = dictobj.Field(sb.dictof(spec_key_spec(), sb.has("normalise")))
    extra = dictobj.Field(
        no_such_key_spec("Use extras instead (notice the s!)"))
    extras = dictobj.Field(
        sb.listof(sb.tuple_spec(sb.string_spec(),
                                sb.tupleof(sb.string_spec()))))
Ejemplo n.º 3
0
class TilePacmanOptions(AnimationOptions):
    user_coords = dictobj.Field(sb.boolean, default=False)
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration
class Addon(dictobj.Spec):
    name = dictobj.Field(sb.string_spec)
    extras = dictobj.Field(
        sb.listof(sb.tuple_spec(sb.string_spec(), sb.string_spec())))
    resolver = dictobj.Field(sb.any_spec)
    namespace = dictobj.Field(sb.string_spec)

    class BadHook(DelfickError):
        desc = "Bad Hook"

    @property
    def resolved(self):
        errors = []
        if getattr(self, "_resolved", None) is None:
            try:
                self._resolved = list(self.resolver())
            except Exception as error:
                errors.append(
                    self.BadHook("Failed to resolve a hook",
                                 name=self.name,
                                 namespace=self.namespace,
                                 error=str(error)))

        if errors:
            raise self.BadHook(_errors=errors)

        return self._resolved

    def process(self, collector):
        for result in self.resolved:
            if collector is not None:
                collector.register_converters(result.get("specs", {}), Meta,
                                              collector.configuration,
                                              sb.NotSpecified)

    def post_register(self, **kwargs):
        list(self.resolver(post_register=True, **kwargs))

    def unresolved_dependencies(self):
        for namespace, name in self.extras:
            yield (namespace, name)

    def resolved_dependencies(self):
        for result in self.resolved:
            for namespace, names in result.get("extras", []):
                if not isinstance(names, (tuple, list)):
                    names = (names, )
                for name in names:
                    yield (namespace, name)

    def dependencies(self, all_deps):
        for dep in self.unresolved_dependencies():
            yield dep
        if hasattr(self, "_resolved"):
            for dep in self.resolved_dependencies():
                yield dep
Ejemplo n.º 5
0
    class C(dictobj.Spec):
        hue = dictobj.Field(sb.float_spec, default=h)
        saturation = dictobj.Field(sb.float_spec, default=s)
        brightness = dictobj.Field(sb.float_spec, default=b)
        kelvin = dictobj.Field(sb.integer_spec, default=k)

        @property
        def color(self):
            return Color(self.hue, self.saturation, self.brightness,
                         self.kelvin)
Ejemplo n.º 6
0
class TileTwinklesOptions(AnimationOptions):
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)

    palette = dictobj.Field(choose_palette())
    num_twinkles = dictobj.Field(sb.integer_spec, default=20)
    fade_in_speed = dictobj.Field(sb.float_spec, default=0.125)
    fade_out_speed = dictobj.Field(sb.float_spec, default=0.078)

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration
Ejemplo n.º 7
0
class BackgroundOption(dictobj.Spec):
    type = dictobj.Field(sb.string_choice_spec(["specified", "current"]),
                         default="specified")

    hue = dictobj.Field(sb.float_spec, default=0)
    saturation = dictobj.Field(sb.float_spec, default=0)
    brightness = dictobj.Field(sb.float_spec, default=0)
    kelvin = dictobj.Field(sb.float_spec, default=3500)

    @property
    def default_color(self):
        return Color(self.hue, self.saturation, self.brightness, self.kelvin)
Ejemplo n.º 8
0
class TileFallingOptions(AnimationOptions):
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)
    random_orientations = dictobj.Field(sb.boolean, default=False)

    hue_ranges = dictobj.NullableField(split_by_comma(hue_range_spec()),
                                       default=[])
    line_tip_hue = dictobj.NullableField(hue_range_spec(),
                                         default=HueRange(60, 60))
    blinking_pixels = dictobj.Field(sb.boolean, default=True)

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration
class Capability(dictobj.Spec):
    """Represents the capability for a device"""
    name = dictobj.Field(sb.string_spec, wrapper=sb.required)
    company = dictobj.Field(sb.string_spec, wrapper=sb.required)
    identifier = dictobj.Field(sb.string_spec, wrapper=sb.required)

    has_color = dictobj.Field(sb.boolean, default=True)
    has_ir = dictobj.Field(sb.boolean, default=False)
    has_multizone = dictobj.Field(sb.boolean, default=False)
    has_variable_color_temp = dictobj.Field(sb.boolean, default=True)
    has_chain = dictobj.Field(sb.boolean, default=False)

    min_kelvin = dictobj.Field(sb.integer_spec, default=2500)
    max_kelvin = dictobj.Field(sb.integer_spec, default=9000)
Ejemplo n.º 10
0
class TileMarqueeOptions(AnimationOptions):
    text_color = dictobj.Field(ColorOption(200, 0.24, 0.5, 3500))
    text = dictobj.Field(sb.string_spec, default="LIFX is awesome!")
    user_coords = dictobj.Field(sb.boolean, default=False)
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)
    direction = dictobj.Field(enum_spec(None, MarqueeDirection, unpacking=True), default=MarqueeDirection.LEFT)

    @property
    def text_width(self):
        return len(self.text) * 8

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration
Ejemplo n.º 11
0
class TileGameOfLifeOptions(AnimationOptions):
    user_coords = dictobj.Field(sb.boolean, default=True)
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)
    new_color_style = dictobj.Field(sb.string_choice_spec(["random", "average"]), default="average")
    iteration_delay = dictobj.Field(sb.float_spec, default=0.1)

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration

    def make_new_color(self, surrounding):
        if self.new_color_style == "random":
            return Color(random.randrange(0, 360), 1, 1, 3500)
        else:
            return Color.average(surrounding)
Ejemplo n.º 12
0
class Options(dictobj.Spec):
    colors = dictobj.Field(sb.listof(Color.FieldSpec()), wrapper=sb.required)
    theme = dictobj.Field(sb.string_choice_spec(appliers.keys()),
                          default="SPLOTCH")
    duration = dictobj.Field(sb.float_spec(), default=1)
    hue = dictobj.NullableField(sb.float_spec())
    saturation = dictobj.NullableField(sb.float_spec())
    brightness = dictobj.NullableField(sb.float_spec())
    kelvin = dictobj.NullableField(sb.float_spec())

    @property
    def overrides(self):
        o = {}
        for key in ("duration", "hue", "saturation", "brightness", "kelvin"):
            if self[key] is not None:
                o[key] = self[key]
        return o
Ejemplo n.º 13
0
class SocketTarget(TransportTarget):
    """
    A subclass of ``photons_protocol.target.traget.TransportTarget``
    using our ``SocketItem`` and ``SocketBridge``
    """
    item_kls = lambda s: SocketItem
    bridge_kls = lambda s: SocketBridge
    description = dictobj.Field(
        sb.string_spec,
        default="Understands how to talk to a device over a TCP socket")
Ejemplo n.º 14
0
class Collection(dictobj.Spec):
    """
    Represents either a group or a location. It understands the relationship
    between label and updated_at such that the collections' name corresponds
    to the name with the newest updated_at property
    """
    typ = dictobj.Field(sb.string_spec, wrapper=sb.required)
    uuid = dictobj.Field(sb.string_spec, wrapper=sb.required)
    name = dictobj.Field(sb.string_spec, default="")

    def setup(self, *args, **kwargs):
        super(Collection, self).setup(*args, **kwargs)
        self.newest_timestamp = None

    def add_name(self, timestamp, name):
        if self.newest_timestamp is None or self.newest_timestamp < timestamp:
            self.name = name
            self.newest_timestamp = timestamp

    def __eq__(self, other):
        return isinstance(
            other,
            Collection) and self.typ == other.typ and self.uuid == other.uuid
Ejemplo n.º 15
0
class TileNyanOptions(AnimationOptions):
    user_coords = dictobj.Field(sb.boolean, default=False)
    num_iterations = dictobj.Field(sb.integer_spec, default=-1)
    random_orientations = dictobj.Field(sb.boolean, default=False)

    @property
    def direction(self):
        return MarqueeDirection.RIGHT

    @property
    def text_width(self):
        return 11

    @property
    def text_color(self):
        class Color:
            color = None

        return Color

    def final_iteration(self, iteration):
        if self.num_iterations == -1:
            return False
        return self.num_iterations <= iteration
Ejemplo n.º 16
0
class DocOptions(dictobj.Spec):
    out = dictobj.Field(wrapper=sb.required, format_into=sb.string_spec)
    src = dictobj.Field(wrapper=sb.required, format_into=sb.directory_spec)
Ejemplo n.º 17
0
class TileDiceRollOptions(AnimationOptions):
    num_iterations = dictobj.Field(sb.integer_spec, default=1)

    roll_time = dictobj.Field(sb.float_spec, default=2)
    dice_color = dictobj.Field(ColorOption(200, 1, 1, 3500))
Ejemplo n.º 18
0
class Filter(dictobj.Spec):
    """
    The options for a filter. Usage looks like:

    .. code-block:: python

        filtr = Filter.FieldSpec().empty_normalise(force_refresh=True, firmware_version=1.22)

        # or
        filtr = Filter.from_json_str('{"force_refresh": true, "firmware_version": 1.22}')

        # or
        filtr = Filter.from_options({"force_refresh": True, "firmware_version": 1.22})

        # or
        filtr = Filter.from_kwargs(force_refresh=True, firmware_version=1.22)

        # or
        filtr = Filter.from_key_value_str("force_refresh=true firmware_version=1.22")

        # or
        filtr = Filter.from_url_str("force_refresh=true&firmware_version=1.22")

    .. automethod:: photons_device_finder.Filter.from_options

    .. automethod:: photons_device_finder.Filter.from_kwargs

    .. automethod:: photons_device_finder.Filter.empty

    .. automethod:: photons_device_finder.Filter.from_json_str

    .. automethod:: photons_device_finder.Filter.from_key_value_str

    .. automethod:: photons_device_finder.Filter.from_url_str

    .. autoattribute:: photons_device_finder.Filter.matches_all

    .. automethod:: photons_device_finder.Filter.matches

    .. automethod:: photons_device_finder.Filter.has

    Finally, we have ``has`` which takes in a ``field_name`` and says whether
    """
    force_refresh = dictobj.Field(boolean, default=False)

    serial = dictobj.Field(sb.listof(sb.string_spec()),
                           wrapper=sb.optional_spec)

    label = dictobj.Field(sb.listof(sb.string_spec()),
                          wrapper=sb.optional_spec)
    power = dictobj.Field(sb.listof(sb.string_spec()),
                          wrapper=sb.optional_spec)

    group_id = dictobj.Field(sb.listof(sb.string_spec()),
                             wrapper=sb.optional_spec)
    group_name = dictobj.Field(sb.listof(sb.string_spec()),
                               wrapper=sb.optional_spec)

    location_id = dictobj.Field(sb.listof(sb.string_spec()),
                                wrapper=sb.optional_spec)
    location_name = dictobj.Field(sb.listof(sb.string_spec()),
                                  wrapper=sb.optional_spec)

    hue = dictobj.Field(str_ranges, wrapper=sb.optional_spec)
    saturation = dictobj.Field(str_ranges, wrapper=sb.optional_spec)
    brightness = dictobj.Field(str_ranges, wrapper=sb.optional_spec)
    kelvin = dictobj.Field(str_ranges, wrapper=sb.optional_spec)

    firmware_version = dictobj.Field(sb.listof(sb.string_spec()),
                                     wrapper=sb.optional_spec)

    product_id = dictobj.Field(sb.listof(sb.integer_spec()),
                               wrapper=sb.optional_spec)
    product_identifier = dictobj.Field(sb.listof(sb.string_spec()),
                                       wrapper=sb.optional_spec)

    cap = dictobj.Field(sb.listof(sb.string_spec()), wrapper=sb.optional_spec)

    @classmethod
    def from_json_str(kls, s):
        """
        Interpret s as a json string and use it to create a Filter using from_options
        """
        try:
            options = json.loads(s)
        except (TypeError, ValueError) as error:
            raise InvalidJson(error=error)
        else:
            if type(options) is not dict:
                raise InvalidJson("Expected a dictionary", got=type(options))
            return kls.from_options(options)

    @classmethod
    def from_key_value_str(kls, s):
        """
        Create a Filter based on the ``key=value key2=value2`` string provided.

        Each key=value pair is separated by a space and arrays are formed by
        separating values by a comma.

        Note that values may not have spaces in them because of how we split
        the key=value pairs. If you need values to have spaces use from_json_str
        or from_options.
        """
        options = {}

        for part in s.split(" "):
            m = regexes["key_value"].match(part)
            if m:
                groups = m.groupdict()
                if groups["key"] not in ("hue", "saturation", "brightness",
                                         "kelvin", "force_refresh"):
                    options[groups["key"]] = groups["value"].split(',')
                else:
                    options[groups["key"]] = groups["value"]

        return kls.from_options(options)

    @classmethod
    def from_url_str(kls, s):
        """
        Create a Filter based on ``key=value&otherkey=value2`` string provided

        Where the string is url encoded.
        """
        return kls.from_options(parse_qs(s))

    @classmethod
    def from_kwargs(kls, **kwargs):
        """Create a Filter based on the provided kwarg arguments"""
        return kls.from_options(kwargs)

    @classmethod
    def empty(kls, force_refresh=False):
        """Create an empty filter"""
        return kls.from_options({"force_refresh": force_refresh})

    @classmethod
    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)

    def has(self, field):
        """Say whether the filter has an opinion on this field"""
        return field in self.fields and self[field] != sb.NotSpecified

    def matches(self, field_name, val):
        """
        Says whether this filter matches against provided filed_name/val pair

        * Always say False for ``force_refresh``
        * Say False if the value on the filter for field_name is NotSpecified
        * Say True if a hsbk value and we are within the range specified in val
        * Say True if value on the filter is a list, and val exists in that list
        * Say True if value on the filter is not a list and matches val
        """
        if field_name == "force_refresh":
            return False

        if field_name in self.fields:
            f = self[field_name]
            if f is not sb.NotSpecified:
                if field_name in ("hue", "saturation", "brightness", "kelvin"):
                    return any(val >= pair[0] and val <= pair[1] for pair in f)

                if field_name in self.label_fields and type(val) is str:
                    if type(f) is list:
                        return any(fnmatch.fnmatch(val, pat) for pat in f)
                    else:
                        return fnmatch.fnmatch(val, f)

                if type(f) is list:
                    if type(val) is list:
                        return any(v in val for v in f)
                    else:
                        return val in f
                else:
                    return val == f

        return False

    @property
    def matches_all(self):
        """True if this Filter matches against any device"""
        for field in self.fields:
            if field != "force_refresh":
                if self[field] != sb.NotSpecified:
                    return False
        return True

    @property
    def points(self, for_info=False):
        """Provide InfoPoints enums that match the keys on this filter with values"""
        for e in InfoPoints:
            for key in e.value.keys:
                if self[key] != sb.NotSpecified:
                    yield e

    @property
    def label_fields(self):
        return ("product_identifier", "label", "location_name", "group_name")
Ejemplo n.º 19
0
class Device(dictobj.Spec):
    """
    An object representing a single device.

    Users shouldn't have to interact with these directly
    """
    serial = dictobj.Field(sb.string_spec, wrapper=sb.required)

    label = dictobj.Field(sb.string_spec, wrapper=sb.optional_spec)
    power = dictobj.Field(sb.string_spec, wrapper=sb.optional_spec)

    group = dictobj.Field(sb.any_spec, wrapper=sb.optional_spec)
    location = dictobj.Field(sb.any_spec, wrapper=sb.optional_spec)

    hue = dictobj.Field(sb.integer_spec, wrapper=sb.optional_spec)
    saturation = dictobj.Field(sb.float_spec, wrapper=sb.optional_spec)
    brightness = dictobj.Field(sb.float_spec, wrapper=sb.optional_spec)
    kelvin = dictobj.Field(sb.integer_spec, wrapper=sb.optional_spec)

    firmware_version = dictobj.Field(sb.string_spec, wrapper=sb.optional_spec)

    product_id = dictobj.Field(sb.integer_spec, wrapper=sb.optional_spec)
    product_identifier = dictobj.Field(sb.string_spec,
                                       wrapper=sb.optional_spec)

    cap = dictobj.Field(sb.listof(sb.string_spec()), wrapper=sb.optional_spec)

    @property
    def property_fields(self):
        return ["group_id", "group_name", "location_name", "location_id"]

    @property
    def group_id(self):
        if self.group is sb.NotSpecified:
            return sb.NotSpecified
        return self.group.uuid

    @property
    def group_name(self):
        if self.group is sb.NotSpecified:
            return sb.NotSpecified
        return self.group.name

    @property
    def location_name(self):
        if self.location is sb.NotSpecified:
            return sb.NotSpecified
        return self.location.name

    @property
    def location_id(self):
        if self.location is sb.NotSpecified:
            return sb.NotSpecified
        return self.location.uuid

    def as_dict(self):
        actual = super(Device, self).as_dict()
        del actual["group"]
        del actual["location"]
        for key in self.property_fields:
            actual[key] = self[key]
        return actual

    def matches(self, filtr):
        """
        Say whether we match against the provided filter
        """
        if filtr.matches_all:
            return True

        fields = list(self.fields) + self.property_fields
        has_atleast_one_field = False

        for field in fields:
            val = self[field]
            if val is not sb.NotSpecified:
                has_field = filtr.has(field)
                if has_field:
                    has_atleast_one_field = True
                if has_field and not filtr.matches(field, val):
                    return False

        return has_atleast_one_field

    def set_from_pkt(self, pkt, collections):
        """
        Set information from the provided pkt.

        collections is used for determining the group/location based on the pkt.

        We return a InfoPoints enum representing what type of information was set.
        """
        if pkt | LightMessages.LightState:
            self.label = pkt.label
            self.power = "off" if pkt.power is 0 else "on"
            self.hue = pkt.hue
            self.saturation = pkt.saturation
            self.brightness = pkt.brightness
            self.kelvin = pkt.kelvin
            return InfoPoints.LIGHT_STATE

        elif pkt | DeviceMessages.StateGroup:
            uuid = binascii.hexlify(pkt.group).decode()
            self.group = collections.add_group(uuid, pkt.updated_at, pkt.label)
            return InfoPoints.GROUP

        elif pkt | DeviceMessages.StateLocation:
            uuid = binascii.hexlify(pkt.location).decode()
            self.location = collections.add_location(uuid, pkt.updated_at,
                                                     pkt.label)
            return InfoPoints.LOCATION

        elif pkt | DeviceMessages.StateHostFirmware:
            self.firmware_version = pkt.version
            return InfoPoints.FIRMWARE

        elif pkt | DeviceMessages.StateVersion:
            self.product_id = pkt.product
            capability = capability_for_ids(pkt.product, pkt.vendor)
            self.product_identifier = capability.identifier
            cap = []
            for prop in ("has_color", "has_ir", "has_multizone", "has_chain",
                         "has_variable_color_temp"):
                if getattr(capability, prop):
                    cap.append(prop[4:])
                else:
                    cap.append("not_{}".format(prop[4:]))
            self.cap = sorted(cap)
            return InfoPoints.VERSION
Ejemplo n.º 20
0
class PhotonsApp(dictobj.Spec):
    """
    The main photons_app object.

    .. dictobj_params::
    """
    config = dictobj.Field(sb.file_spec,
                           wrapper=sb.optional_spec,
                           help="The root configuration file")
    extra = dictobj.Field(
        sb.string_spec,
        default="",
        help="The arguments after the ``--`` in the commandline")
    debug = dictobj.Field(sb.boolean,
                          default=False,
                          help="Whether we are in debug mode or not")
    target = dictobj.Field(wrapper=sb.optional_spec,
                           format_into=sb.string_spec,
                           help="The target to use when executing the task")
    artifact = dictobj.Field(default="",
                             format_into=sb.string_spec,
                             help="The artifact string from the commandline")
    reference = dictobj.Field(default="",
                              format_into=sb.string_spec,
                              help="The device(s) to send commands to")
    extra_files = dictobj.Field(sb.string_spec,
                                wrapper=sb.listof,
                                help="Extra files to load")
    chosen_task = dictobj.Field(default="list_tasks",
                                format_into=sb.string_spec,
                                help="The task that is being executed")
    cleaners = dictobj.Field(
        lambda: sb.overridden([]),
        help=
        "A list of functions to call when cleaning up at the end of the program"
    )
    final_future = dictobj.Field(
        sb.overridden("{final_future}"),
        formatted=True,
        help="A future representing the end of the program")
    default_activate_all_modules = dictobj.Field(
        sb.boolean,
        default=False,
        help=
        "The collector looks at this to determine if we should default to activating all photons modules"
    )

    @memoized_property
    def loop(self):
        loop = asyncio.get_event_loop()
        if self.debug:
            loop.set_debug(True)
        return loop

    @memoized_property
    def extra_as_json(self):
        options = "{}" if self.extra in (None, "",
                                         sb.NotSpecified) else self.extra
        try:
            return json.loads(options)
        except (TypeError, ValueError) as error:
            raise BadOption("The options after -- wasn't valid json",
                            error=error)

    async def cleanup(self, targets):
        for cleaner in self.cleaners:
            try:
                await cleaner()
            except asyncio.CancelledError:
                break
            except KeyboardInterrupt:
                break
            except (RuntimeWarning, Exception):
                exc_info = sys.exc_info()
                log.error(exc_info[1], exc_info=exc_info)

        for target in targets:
            try:
                if hasattr(target, "finish"):
                    await target.finish()
            except asyncio.CancelledError:
                break
            except KeyboardInterrupt:
                break
            except (RuntimeWarning, Exception):
                exc_info = sys.exc_info()
                log.error(exc_info[1], exc_info=exc_info)
Ejemplo n.º 21
0
 class Original(dictobj.Spec):
     one = dictobj.Field(sb.string_spec)
     two = dictobj.Field(sb.integer_spec, default=3)
     three = dictobj.Field(sb.string_spec, help="three")
     four = dictobj.Field(sb.any_spec)
Ejemplo n.º 22
0
 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)
Ejemplo n.º 23
0
class TransportTarget(dictobj.Spec):
    """
    This is responsible for bringing together the TransportBridge and the TransportItems

    It implements the ability to create and destroy args_for_run (the bridge), as well as
    creating a `script` that may be run with `script.run_with`.

    We also have higher order functions for finding and forgetting devices.

    When creating your own target do something like:

    .. code-block:: python

        class SocketTarget(TransportTarget):
            item_kls = lambda s: SocketItem
            bridge_kls = lambda s: SocketBridge
            description = dictobj.Field(sb.string_spec, default="Understands how to talk to a device over a TCP socket")

    ``protocol_register`` and ``final_future`` are retrieved automatically from
    ``Meta`` if we create the transport by doing
    ``TransportTarget.normalise(meta, **kwargs)``

    Note that the path on the meta cannot be root. So make you meta like:

    .. code-block:: python

        from input_algorithms.meta import Meta
        from option_merge import MergedOptions

        configuration = MergedOptions.using({"protocol_register": ..., "final_future": asyncio.Future()})

        # By saying `at("options")` on the meta we are putting it not at root
        # So when we resolve final_future we don't get recursive option errors
        meta = Meta(configuration, []).at("options")

    Generally you'll be passed in a transport via the ``tasks`` mechanism and
    you won't have to instantiate it yourself.
    """
    protocol_register = dictobj.Field(sb.overridden("{protocol_register}"), formatted=True)
    final_future = dictobj.Field(sb.overridden("{final_future}"), formatted=True)
    default_broadcast = dictobj.Field(sb.defaulted(sb.string_spec(), "255.255.255.255"))
    item_kls = lambda s: TransportItem
    bridge_kls = lambda s: TransportBridge
    description = dictobj.Field(sb.string_spec, default="Base transport functionality")

    @classmethod
    def create(kls, configuration, options=None):
        options = options if options is not None else configuration
        meta = Meta(configuration, []).at("options")
        return kls.FieldSpec(formatter=MergedOptionStringFormatter).normalise(meta, options)

    def script(self, raw):
        """Return us a ScriptRunnerIterator for the given `raw` against this `target`"""
        items = list(self.simplify(raw))
        if len(items) > 1:
            items = Pipeline(*items)
        else:
            items = items[0]
        return ScriptRunnerIterator(items, target=self)

    def session(self):
        info = {}

        class Session:
            async def __aenter__(s):
                afr = info["afr"] = await self.args_for_run()
                return afr

            async def __aexit__(s, exc_type, exc, tb):
                if "afr" in info:
                    await self.close_args_for_run(info["afr"])

        return Session()

    async def args_for_run(self):
        """Create an instance of args_for_run. This is designed to be shared amongst many `script`"""
        afr = self.bridge_kls()(self.final_future, self
            , protocol_register=self.protocol_register
            , default_broadcast=self.default_broadcast
            )
        await afr.start()
        return afr

    async def close_args_for_run(self, args_for_run):
        """Close an args_for_run"""
        args_for_run.finish()

    async def get_list(self, args_for_run, broadcast=sb.NotSpecified, **kwargs):
        """Return us the targets that we can find from this bridge"""
        addr = broadcast if broadcast is not sb.NotSpecified else self.default_broadcast
        found = await args_for_run.find_devices(addr, **kwargs)
        return sorted([binascii.hexlify(target[:6]).decode() for target in found])

    def device_forgetter(self, args_for_run):
        """Return a function that may be used to forget a device on this args_for_run"""
        return args_for_run.forget

    def find(self, args_for_run):
        """Return a function that may be used to find a device on this args_for_run"""
        return args_for_run.find

    def simplify(self, script_part, chain=None):
        """
        Used by ``self.script`` to convert ``raw`` into TransportItems

        For each leaf child that is found, we gather messages into groups of
        messages with a ``pack`` method and yield ``self.item_kls()(group)`` with
        messages that don't have a ``pack`` method yield as is.

        For example, let's say we have ``[p1, p2, m1, p3]`` where ``m1`` does
        not have a ``pack`` method and the others do, we'll yield:

        * ``self.item_kls()([p1, p2])``
        * ``m1``
        * ``self.item_kls()([p3])``
        """
        chain = [] if chain is None else chain
        if type(script_part) is not list:
            script_part = [script_part]

        final = []
        errors = []
        for p in script_part:
            if getattr(p, "has_children", False):
                final.append(p.simplified(self.simplify, chain + [p.name]))
                continue
            else:
                if not hasattr(p, "pack"):
                    errors.append(p)
                else:
                    final.append(p)

        if errors:
            raise InvalidScript("Script part has no pack method!", parts=errors, chain=chain)

        buf = []
        for p in final:
            if hasattr(p, "pack"):
                buf.append(p)
            else:
                if buf:
                    yield self.item_kls()(buf)
                    buf = []
                yield p
        if buf:
            yield self.item_kls()(buf)
Ejemplo n.º 24
0
class TileTimeOptions(AnimationOptions):
    hour24 = dictobj.Field(sb.boolean, default=True)
    number_color = dictobj.Field(ColorOption(200, 0.24, 0.5, 3500))
    progress_bar_color = dictobj.Field(ColorOption(0, 1, 0.4, 3500))
    full_height_progress = dictobj.Field(sb.boolean, default=False)
Ejemplo n.º 25
0
 class T(TransportTarget):
     one = dictobj.Field(sb.integer_spec)
Ejemplo n.º 26
0
class AnimationOptions(dictobj.Spec):
    background = dictobj.Field(BackgroundOption.FieldSpec())
    combine_tiles = dictobj.Field(sb.boolean, default=False)
Ejemplo n.º 27
0
class Color(dictobj.Spec):
    hue = dictobj.Field(sb.integer_spec(), wrapper=sb.required)
    saturation = dictobj.Field(sb.float_spec(), wrapper=sb.required)
    brightness = dictobj.Field(sb.float_spec(), wrapper=sb.required)
    kelvin = dictobj.Field(sb.integer_spec(), wrapper=sb.required)