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}"')
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)
def make_command(self, meta, val, existing): v = sb.set_options(path=sb.required(sb.string_spec()), allow_ws_only=sb.defaulted(sb.boolean(), False)).normalise( meta, val) path = v["path"] allow_ws_only = v["allow_ws_only"] if path not in self.paths: raise NoSuchPath(path, sorted(self.paths)) val = sb.set_options(body=sb.required( sb.set_options(args=sb.dictionary_spec(), command=sb.required(sb.string_spec())))).normalise( meta, val) args = val["body"]["args"] name = val["body"]["command"] if existing: name = val["body"]["command"] = f"{existing['path']}:{name}" extra_context = {} if existing: extra_context["_parent_command"] = existing["command"] everything = meta.everything if isinstance(meta.everything, MergedOptions): everything = meta.everything.wrapped() everything.update(extra_context) meta = Meta(everything, []).at("body") available_commands = self.paths[path] if name not in available_commands: raise BadSpecValue( "Unknown command", wanted=name, available=self.available(available_commands, allow_ws_only=allow_ws_only), meta=meta.at("command"), ) command = available_commands[name]["spec"].normalise( meta.at("args"), args) if not allow_ws_only and command.__whirlwind_ws_only__: raise BadSpecValue( "Command is for websockets only", wanted=name, available=self.available(available_commands, allow_ws_only=allow_ws_only), meta=meta.at("command"), ) return command, name
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]
def normalise_filled(self, meta, val): if isinstance(val, int): return False if val == 0 else True elif isinstance(val, str): return False if val.lower() in ("no", "false") else True elif isinstance(val, list): if len(val) != 1: raise BadSpecValue( "Lists can only be turned into a boolean if they have only one item", got=len(val), meta=meta, ) return boolean().normalise(meta.indexed_at(0), val[0]) return sb.boolean().normalise(meta, val)
def normalise_filled(self, meta, val): options_spec = sb.set_options( filename=sb.required(sb.string_spec()), optional=sb.defaulted(sb.boolean(), False) ) lst_spec = sb.listof(sb.or_spec(sb.string_spec(), options_spec)) if isinstance(val, list): val = {"after": lst_spec.normalise(meta, val)} val = sb.set_options(after=lst_spec, before=lst_spec).normalise(meta, val) formatted = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) for key in ("after", "before"): result = [] for i, thing in enumerate(val[key]): filename = thing optional = False if isinstance(thing, dict): filename = thing["filename"] optional = thing["optional"] filename = formatted.normalise(meta.at(key).indexed_at(i), filename) if optional and not os.path.exists(filename): log.warning(hp.lc("Ignoring optional configuration", filename=filename)) continue if not os.path.exists(filename): raise BadConfiguration( "Specified extra file doesn't exist", source=self.source, filename=filename, meta=meta, ) result.append(filename) val[key] = result return self.extras_spec.normalise(meta, val)
describe "signature": def assertSignature(self, spec, want): assert " ".join(signature(spec)) == want it "knows about integer_spec": self.assertSignature(sb.integer_spec(), "integer") it "knows about float_spec": self.assertSignature(sb.float_spec(), "float") it "knows about string_spec": self.assertSignature(sb.string_spec(), "string") it "knows about boolean": self.assertSignature(sb.boolean(), "boolean") it "knows about dictionary_spec": self.assertSignature(sb.dictionary_spec(), "dictionary") it "knows about string_choice_spec": self.assertSignature(sb.string_choice_spec(["one", "two"]), "choice of (one | two)") it "knows about optional_spec": self.assertSignature(sb.optional_spec(sb.integer_spec()), "integer (optional)") self.assertSignature(sb.optional_spec(sb.any_spec()), "(optional)") it "knows about defaulted": self.assertSignature(sb.defaulted(sb.integer_spec(), 20), "integer (default 20)") self.assertSignature(sb.defaulted(sb.any_spec(), True), "(default True)")
def normalise(self, meta, val): if "NOISY_NETWORK" in os.environ: return True if val is sb.NotSpecified: val = False return sb.boolean().normalise(meta, val)