class Options(dictobj.Spec): clean_indication = dictobj.Field(sb.boolean, default=False) clean_last_result = dictobj.Field( enum_spec(None, LightLastHevCycleResult, unpacking=True), default=LightLastHevCycleResult.NONE, ) clean_default_duration_s = dictobj.Field(sb.float_spec, default=7200)
class Options(dictobj.Spec): zones = dictobj.Field(sb.listof(color_spec())) zones_count = dictobj.NullableField(sb.integer_spec) zones_effect = dictobj.Field(enum_spec(None, MultiZoneEffectType, unpacking=True), default=MultiZoneEffectType.OFF)
class Schedule(dictobj.Spec): days = dictobj.NullableField( sb.listof(enum_spec(None, Days, unpacking=True))) hour = dictobj.Field(range_spec(sb.integer_spec(), 0, 23), wrapper=sb.required) minute = dictobj.Field(range_spec(sb.integer_spec(), 0, 59), wrapper=sb.required) task = dictobj.Field(task_spec) hue = dictobj.Field(range_spec(sb.float_spec(), 0, 360), default=0) saturation = dictobj.Field(range_spec(sb.float_spec(), 0, 1), default=0) brightness = dictobj.Field(range_spec(sb.float_spec(), 0, 1), default=1) kelvin = dictobj.Field(range_spec(sb.integer_spec(), 1500, 9000), default=3500) transform_options = dictobj.NullableField( sb.dictof(sb.string_spec(), sb.boolean())) duration = dictobj.NullableField(sb.float_spec) power = dictobj.NullableField(power_spec) colors = dictobj.NullableField(colors_spec) override = dictobj.NullableField( sb.dictof(sb.string_spec(), range_spec(sb.float_spec(), 0, 1))) reference = dictobj.Field(reference_spec) @property def hsbk(self): if self.task == 'lan:transform': keys = ["hue", "saturation", "brightness", "kelvin"] options = {k: v for k, v in self.as_dict().items() if k in keys} return {k: v for k, v in options.items() if v is not None} else: return {} @property def extra(self): keys_except = [ "days", "hour", "minute", "reference", "task", "hue", "saturation", "brightness", "kelvin" ] options = { k: v for k, v in self.as_dict().items() if k not in keys_except } return {k: v for k, v in options.items() if v is not None} @property def dow(self): days = self.days if not self.days: days = list(Days) return [day.value for day in days]
class Options(dictobj.Spec): chain = dictobj.Field(sb.listof(TileChild.FieldSpec())) chain_length = dictobj.NullableField(sb.integer_spec) palette = dictobj.Field(sb.listof(color_spec())) palette_count = dictobj.NullableField(sb.integer_spec) matrix_effect = dictobj.Field(enum_spec(None, TileEffectType, unpacking=True), default=TileEffectType.OFF)
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
def normalise_filled(self, meta, val): return enum_spec(None, MarqueeDirection, unpacking=True).normalise(meta, val)
class RunEffectCommand(EffectCommand): """ Start or stop a firmware animation on devices that support them """ matrix_animation = dictobj.NullableField( enum_spec(None, TileEffectType, unpacking=True), help=""" The animation to run for matrix devices. This can be FLAME, MORPH or OFF. If you don't supply this these devices will not run any animation" """, ) matrix_options = dictobj.Field( sb.dictionary_spec, help=""" Any options to give to the matrix animation. For example duration """, ) linear_animation = dictobj.NullableField( enum_spec(None, MultiZoneEffectType, unpacking=True), help=""" The animation to run for linear devices. Currently only MOVE or OFF are supported If you don't supply this these devices will not run any animation" """, ) linear_options = dictobj.Field( sb.dictionary_spec, help=""" Options for the linear firmware effect: - speed: duration in seconds to complete one cycle of the effect - duration: time in seconds the effect will run. - direction: either "left" or "right" (default: "right") If duration is not specified or set to 0, the effect will run until it is manually stopped. """, ) async def execute(self): async def gen(reference, afr, **kwargs): if self.apply_theme: yield self.theme_msg() if self.matrix_animation: yield SetTileEffect(self.matrix_animation, **self.matrix_options) if self.linear_animation: yield SetZonesEffect(self.linear_animation, **self.linear_options) return await self.send(FromGeneratorPerSerial(gen), add_replies=False)
class RunEffectCommand(EffectCommand): """ Start or stop a firmware animation on devices that support them """ matrix_animation = dictobj.NullableField( enum_spec(None, TileEffectType, unpacking=True), help=""" The animation to run for matrix devices. This can be FLAME, MORPH or OFF. If you don't supply this these devices will not run any animation" """, ) matrix_options = dictobj.Field( sb.dictionary_spec, help=""" Any options to give to the matrix animation. For example duration """, ) linear_animation = dictobj.NullableField( enum_spec(None, MultiZoneEffectType, unpacking=True), help=""" The animation to run for linear devices. Currently only MOVE or OFF are supported If you don't supply this these devices will not run any animation" """, ) linear_options = dictobj.Field( sb.dictionary_spec, help=""" Any options to give to the linear animation. For example duration """, ) async def execute(self): fltr = chp.filter_from_matcher(self.matcher) if self.refresh is not None: fltr.force_refresh = self.refresh gatherer = Gatherer(self.target) theme_msg = self.theme_msg(gatherer) async def gen(reference, afr, **kwargs): if self.apply_theme: yield theme_msg if self.matrix_animation: yield SetTileEffect(self.matrix_animation, gatherer=gatherer, **self.matrix_options) if self.linear_animation: yield SetZonesEffect(self.linear_animation, gatherer=gatherer, **self.linear_options) script = self.target.script(FromGeneratorPerSerial(gen)) return await chp.run( script, fltr, self.finder, message_timeout=self.timeout, add_replies=False, )
def subject_with_unknown(self, pkt, enum): return types.enum_spec(pkt, enum, unpacking=True, allow_unknown=True)
def subject(self, pkt, enum): return types.enum_spec(pkt, enum, unpacking=True)
it "complains if it can't find a string value in the mask", meta, subject: with assertRaises(BadConversion, "Can't convert value into value from mask"): subject.normalise(meta, "SEVEN") it "does not complain if it can't find an integer value in the mask", meta, subject: assert subject.normalise(meta, (1 << 24)) == set() describe "enum_spec": it "takes in some things": pkt = mock.Mock(name="pkt") em = mock.Mock(name="enum") unpacking = mock.Mock(name="unpacking") allow_unknown = mock.Mock(name="allow_unknown") spec = types.enum_spec(pkt, em, unpacking=unpacking, allow_unknown=allow_unknown) assert spec.pkt is pkt assert spec.enum is em assert spec.unpacking is unpacking assert spec.allow_unknown is allow_unknown describe "normalisation": @pytest.fixture() def enum(self): class Vals(Enum): ONE = 1 TWO = 2 THREE = 3 FOUR = 4